From c518b4b1bff73c440b01903345c722e37717a697 Mon Sep 17 00:00:00 2001 From: Yoshifumi Nakamura Date: Mon, 25 May 2026 22:48:07 +0900 Subject: [PATCH] Fix auth HEAD handling and shell lint Signed-off-by: Yoshifumi Nakamura --- programs/LQCD_dw_solver/build.sh | 2 +- programs/MHDTurbulence/build.sh | 10 ++-- programs/genesis/build.sh | 2 +- result_server/routes/auth.py | 5 +- result_server/tests/test_totp_security.py | 64 +++++++++++++++++++++++ 5 files changed, 75 insertions(+), 8 deletions(-) diff --git a/programs/LQCD_dw_solver/build.sh b/programs/LQCD_dw_solver/build.sh index 6835e2f..620b1bf 100644 --- a/programs/LQCD_dw_solver/build.sh +++ b/programs/LQCD_dw_solver/build.sh @@ -74,7 +74,7 @@ case "$system" in cd - cp $BIN ../artifacts ;; - MiyabiG) + MiyabiG) # OpenACC cp Makefile_openacc Makefile make -j 8 lib diff --git a/programs/MHDTurbulence/build.sh b/programs/MHDTurbulence/build.sh index a17002d..fcecfa4 100644 --- a/programs/MHDTurbulence/build.sh +++ b/programs/MHDTurbulence/build.sh @@ -25,22 +25,22 @@ case "$system" in cd src_f90_omp_host echo "Compile cods in "`pwd` make - echo "Executable is "${BIN}" and copied to "${artdir} + echo "Executable is "${BIN}" and copied to "${artdir} cp ../exe/$BIN ../../${artdir} ;; - MiyabiG) + MiyabiG) cd src_f90_acc_device echo "Compile cods in "`pwd` make - echo "Executable is "${BIN}" and copied to "${artdir} + echo "Executable is "${BIN}" and copied to "${artdir} cp ../exe/$BIN ../../${artdir} ;; # in the future, we may add this -# MiyabiG/OpenMP) +# MiyabiG/OpenMP) # cd src_f90_omp_device # echo "Compile cods in "`pwd` # make -# echo "Executable is "${BIN}" and copied to "${artdir} +# echo "Executable is "${BIN}" and copied to "${artdir} # cp ../exe/$BIN ../../${artdir} # ;; *) diff --git a/programs/genesis/build.sh b/programs/genesis/build.sh index b398880..ca7bebe 100644 --- a/programs/genesis/build.sh +++ b/programs/genesis/build.sh @@ -293,7 +293,7 @@ echo "FC=$FC" echo "CC=$CC" echo "CXX=${CXX:-}" echo "F77=${F77:-}" -echo "configure args: ${CONFIG_ARGS[@]}" +echo "configure args: ${CONFIG_ARGS[*]}" bootstrap_genesis configure_env=(CC="$CC" FC="$FC") diff --git a/result_server/routes/auth.py b/result_server/routes/auth.py index 98df5c8..b25a0f4 100644 --- a/result_server/routes/auth.py +++ b/result_server/routes/auth.py @@ -125,7 +125,7 @@ def _login_rate_key(): @auth_bp.route("/login", methods=["GET", "POST"]) def login(): """Render the login flow and validate submitted TOTP codes.""" - if request.method == "GET": + if request.method in ("GET", "HEAD"): return render_template("auth_login.html", step="email") email = request.form.get("email", "").strip() @@ -221,6 +221,9 @@ def setup(token): email = invitation["email"] affiliations = invitation["affiliations"] + if request.method == "HEAD": + return _add_no_store_headers(make_response("", 200)) + if request.method == "GET": secret = generate_secret() session["_setup_secret"] = secret diff --git a/result_server/tests/test_totp_security.py b/result_server/tests/test_totp_security.py index 0b7a1ac..63fad06 100644 --- a/result_server/tests/test_totp_security.py +++ b/result_server/tests/test_totp_security.py @@ -127,6 +127,7 @@ class _StubUserStore: def __init__(self): self._users = {} + self._invitations = {} def create_user(self, email, totp_secret, affiliations): self._users[email] = { @@ -161,6 +162,19 @@ def has_totp_secret(self, email): user = self._users.get(email) return bool(user and user.get("totp_secret")) + def create_invitation(self, email, affiliations, token="token-1"): + self._invitations[token] = { + "email": email, + "affiliations": list(affiliations), + } + return token + + def get_invitation(self, token): + return self._invitations.get(token) + + def delete_invitation(self, token): + self._invitations.pop(token, None) + class _BrokenRedis: def ping(self): @@ -278,6 +292,56 @@ def test_dev_mode_without_redis_continues_login_flow(self, auth_app): assert b"Step 2 of 2" in resp.data +class TestAuthHeadRequests: + """Tests safe HEAD behavior for public auth routes.""" + + def test_head_login_returns_200_without_redis(self, auth_app): + auth_app.config["AUTH_REQUIRES_REDIS"] = True + auth_app.config["REDIS_CONN"] = None + + with auth_app.test_client() as client: + resp = client.head("/auth/login", follow_redirects=False) + + assert resp.status_code == 200 + assert resp.location is None + assert resp.data == b"" + + def test_get_login_still_renders_form(self, auth_app): + auth_app.config["AUTH_REQUIRES_REDIS"] = True + auth_app.config["REDIS_CONN"] = None + + with auth_app.test_client() as client: + resp = client.get("/auth/login") + + assert resp.status_code == 200 + assert b"Email address" in resp.data + + def test_head_setup_valid_token_does_not_create_secret(self, auth_app): + token = auth_app.config["USER_STORE"].create_invitation( + "user@test.com", + ["dev"], + token="valid-token", + ) + + with auth_app.test_client() as client: + resp = client.head(f"/auth/setup/{token}", follow_redirects=False) + with client.session_transaction() as sess: + setup_secret = sess.get("_setup_secret") + + assert resp.status_code == 200 + assert resp.location is None + assert resp.data == b"" + assert setup_secret is None + + def test_head_setup_invalid_token_returns_200_without_redirect(self, auth_app): + with auth_app.test_client() as client: + resp = client.head("/auth/setup/bad-token", follow_redirects=False) + + assert resp.status_code == 200 + assert resp.location is None + assert resp.data == b"" + + class TestLoginRateLimiting: """Tests source-scoped login rate limiting without account lockout."""