From 94bcf551ceac71a3d227d4e9f98d3a42522e5797 Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Wed, 9 Sep 2026 17:09:40 -0400 Subject: [PATCH] fix(roster): strip Secrets Manager token whitespace A file:// secret commonly includes a trailing newline, so compare_digest must strip the cached value the same way it strips the Bearer header. --- src/roster-api/app.py | 5 ++++- tests/roster_api/test_handler.py | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/roster-api/app.py b/src/roster-api/app.py index a416492..ba7502c 100644 --- a/src/roster-api/app.py +++ b/src/roster-api/app.py @@ -111,7 +111,10 @@ def _expected_token() -> str: except Exception: logger.exception("roster api token secret read failed") raise SecretUnavailable from None - if not isinstance(token, str) or not token: + if not isinstance(token, str): + raise SecretUnavailable + token = token.strip() + if not token: raise SecretUnavailable _cached_token = token return token diff --git a/tests/roster_api/test_handler.py b/tests/roster_api/test_handler.py index afa6117..ae67e25 100644 --- a/tests/roster_api/test_handler.py +++ b/tests/roster_api/test_handler.py @@ -145,6 +145,24 @@ def test_401_wrong_token(rosterapi_app, schedule, secrets): assert schedule.get_employee_by_extension("110") is None +def test_authorize_strips_secret_trailing_newline( + rosterapi_app, schedule, env, monkeypatch +): + monkeypatch.setattr(rosterapi_app, "get_secret", lambda _id: TOKEN + "\n") + rosterapi_app._cached_token = None + result = rosterapi_app.handler(_event(body=_put_body()), None) + assert result["statusCode"] == 200 + assert schedule.get_employee_by_extension("110")["slack_user_id"] == "U123ABCDE" + + +def test_503_when_secret_is_whitespace_only(rosterapi_app, schedule, env, monkeypatch): + monkeypatch.setattr(rosterapi_app, "get_secret", lambda _id: "\n") + rosterapi_app._cached_token = None + result = rosterapi_app.handler(_event(body=_put_body()), None) + assert result["statusCode"] == 503 + assert schedule.get_employee_by_extension("110") is None + + def test_503_when_secret_read_fails(rosterapi_app, schedule, env, monkeypatch): def boom(_id): raise RuntimeError("secrets down")