Skip to content

fix(ctf): remove dead auth guard in challenge check & hint endpoints - #515

Open
prince-shakyaa wants to merge 2 commits into
GenAI-Security-Project:mainfrom
prince-shakyaa:fix/dead-auth-guard-challenge-endpoints
Open

prince-shakyaa wants to merge 2 commits into
GenAI-Security-Project:mainfrom
prince-shakyaa:fix/dead-auth-guard-challenge-endpoints

Conversation

@prince-shakyaa

@prince-shakyaa prince-shakyaa commented May 28, 2026 •

Copy link
Copy Markdown

Fixes #514

Summary

Removes a login check in the challenge check and hint endpoints that never actually runs. Players without an account can still use both endpoints, same as today.

What was wrong

check_challenge and use_hint in finbot/apps/ctf/routes/challenges.py both had this:

if not session_context:
    raise HTTPException(status_code=401, detail="Authentication required")

This never runs. get_session_context always returns a session, even for players who haven't signed up (temporary sessions), so session_context is never empty. The code looks like it blocks logged-out players, but it doesn't do anything. That's misleading for anyone reading or changing this code later.

What this PR changes

finbot/apps/ctf/routes/challenges.py

  • Removed the dead check from check_challenge and use_hint.
  • Kept get_session_context on both, so players without an account can still check progress and use hints.
  • Added a short note in each docstring saying this is on purpose.

tests/unit/apps/ctf/test_challenge_auth.py (new)

  • A temporary session can call POST /check and gets a 200 (and the attempt is recorded).
  • A temporary session can call POST /hint and gets a 200 with the hint text.
  • Both endpoints use get_session_context.
  • The dead if not session_context check is gone from both.

Behavior

Before After
Player without account calls /check or /hint Works Works (no change)
Logged-in player calls /check or /hint Works Works (no change)
Dead if not session_context check In code, never runs Removed

No change for players. This is a cleanup plus tests.

Why not require login?

The first version of this PR switched both endpoints to get_authenticated_session_context, which would block players without an account. Based on the comments here and on #514, the CTF is meant to be playable without signing up, so I took that out.

Follow-up (not in this PR)

  • If we want players without an account to get fewer hints (as @stealthwhizz suggested), I can add a limit in a separate PR once we agree on the number.
  • list_challenges and get_challenge have a similar if session_context: check that is always true. It's harmless there, so I left it alone to keep this PR small.

Testing

pytest tests/unit/apps/ctf/test_challenge_auth.py -v   # 6 passed
pytest tests/unit -q                                   # 313 passed, 26 skipped, 3 failed

The 3 failures are in tests/unit/agents/test_specialized_agents.py. They fail on main too and aren't related to this change.

Checklist

  • Dead check removed from both endpoints
  • Players without an account can still use /check and /hint
  • GET /challenges and GET /challenges/{id} unchanged
  • New tests added and passing
  • No new test failures

…ints

Fixes GenAI-Security-Project#514

## Problem

POST /api/v1/challenges/{id}/check and POST /api/v1/challenges/{id}/hint
both used get_session_context + a manual 'if not session_context: raise 401'
guard. Because get_session_context NEVER returns None (it always returns
request.state.session_context set by SessionMiddleware), the guard was dead
code — always False. Temporary/anonymous sessions passed through freely.

Impact:
- Any visitor could spam POST /check → inflated attempt counters in DB
- Any visitor could spam POST /hint  → full hint text revealed without auth

## Fix

- Add get_authenticated_session_context to the import in challenges.py
- Swap check_challenge and use_hint to Depends(get_authenticated_session_context)
- Remove the now-redundant dead 'if not session_context' guards
- get_authenticated_session_context raises HTTP 401 automatically when
  session_context.is_temporary is True (caller hasn't bound an email)

GET /api/v1/challenges and GET /api/v1/challenges/{id} are unchanged —
they intentionally remain publicly browsable.

## Tests

New file: tests/unit/apps/ctf/test_challenge_auth.py (9 tests, all passing)

BUG-514-001: temp session rejected on check_challenge          PASS
BUG-514-002: temp session rejected on use_hint                 PASS
BUG-514-003: auth session not blocked (happy path)             PASS
BUG-514-004: check_challenge dependency introspection          PASS
BUG-514-005: use_hint dependency introspection                 PASS
BUG-514-006: dead guard removed from check_challenge source    PASS
BUG-514-007: dead guard removed from use_hint source           PASS
BUG-514-008: end-to-end POST /check → HTTP 401                 PASS
BUG-514-009: end-to-end POST /hint  → HTTP 401                 PASS
@prince-shakyaa

prince-shakyaa commented May 28, 2026 •

Copy link
Copy Markdown
Author

Hey @saikishu @e2hln ,
I found a dead auth guard in the check and hint challenge endpoints where if not session_context could never fire, letting unauthenticated users freely access both endpoints and leak hint text. Fixed it by swapping to get_authenticated_session_context (same pattern as every other protected route) and added 9 unit tests to prevent regression.
Happy to make any changes based on your review!
Thank You.

@joshlochner

Copy link
Copy Markdown
Contributor

Sai, do we want unauthenticated users to be able to get hints?

@stealthwhizz

Copy link
Copy Markdown
Contributor

Sai, do we want unauthenticated users to be able to get hints?

Hey @joshlochner , i think there should be a difference between auth and unauth users..
People wont signup if its same for both Right...
and at EOD this is meant for learning
so i think providing unauth users with less hints would be a better option.

prince-shakyaa pushed a commit to prince-shakyaa/finbot-ctf that referenced this pull request Oct 17, 2026
Based on feedback on GenAI-Security-Project#514 and GenAI-Security-Project#515: the CTF is meant to be playable
without signing up, so /check and /hint should keep working for
temporary sessions. Go back to get_session_context, keep the dead
`if not session_context` check removed, and rewrite the tests to cover
the real routes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@prince-shakyaa prince-shakyaa changed the title fix(security): Remove Dead Auth Guard in Challenge Check & Hint Endpoints fix(ctf): remove dead auth guard in challenge check & hint endpoints Oct 17, 2026
@prince-shakyaa

Copy link
Copy Markdown
Author

Thanks @joshlochner and @stealthwhizz for the input, and @c-thiam for the comment on #514.

I've updated the PR based on that. It no longer requires login. Players without an account can still check progress and use hints, same as now. The PR now just removes the dead if not session_context check and adds tests, so it should be safe to merge either way.

@saikishu if you'd like players without an account to get fewer hints, I'm happy to do that in a follow-up PR. Just let me know what limit you'd want.

Based on feedback on GenAI-Security-Project#514 and GenAI-Security-Project#515: the CTF is meant to be playable
without signing up, so /check and /hint should keep working for
temporary sessions. Go back to get_session_context, keep the dead
`if not session_context` check removed, and rewrite the tests to cover
the real routes.
@prince-shakyaa
prince-shakyaa force-pushed the fix/dead-auth-guard-challenge-endpoints branch from be3ccbd to 785e0a6 Compare October 17, 2026 06:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug / Security] Dead Auth Guard in Challenge Check & Hint Endpoints: Anonymous Sessions Bypass Attempt Tracking and Hint Consumption

3 participants