SECURITY: make EPT routing independent of signature well-formedness
All checks were successful
check / gate (push) Successful in 20s
All checks were successful
check / gate (push) Successful in 20s
looks_like_ept used jwt.get_unverified_header, which validates the WHOLE token and therefore rejects anything with a malformed signature segment. Routing consequently depended on signature well-formedness: an EPT-shaped token with a bad signature fell through to the HUMAN path, where it was refused for the wrong reason and — with require_verified_jwt off (dev) — could have been read as a human identity via its `sub` claim. Now the header segment is decoded directly, so routing depends only on what the token CLAIMS to be; whether it is authentic remains verify_ept's job. Also routes alg:none to the EPT verifier regardless of typ, since a `none` token is never valid for any caller. Both forged shapes now return 401 ept_invalid — the honest code — instead of 503 "feature not ready". Found by noticing a forged EPT returned 503 where the verifier should have answered 401, rather than accepting "it was refused, close enough". 80 tests green. Co-Authored-By: Claude (Fable 5) <noreply@anthropic.com>
This commit is contained in:
@@ -670,7 +670,10 @@ async def test_security_forged_agent_token_is_refused_in_production():
|
||||
eternitas_platform_api_key="x")
|
||||
req = _fake_request(settings)
|
||||
|
||||
for typ, expected in (("JWT", "human_signin_not_ready"), ("EPT", "ept_invalid")):
|
||||
# Both shapes now route to the EPT verifier, because alg:none is never
|
||||
# valid for ANY caller — so 401 "your token is bad" is the honest answer,
|
||||
# not 503 "that feature isn't ready".
|
||||
for typ, expected in (("JWT", "ept_invalid"), ("EPT", "ept_invalid")):
|
||||
def seg(d):
|
||||
return _b64.urlsafe_b64encode(_json.dumps(d).encode()).rstrip(b"=").decode()
|
||||
forged = (f"{seg({'alg':'none','typ':typ})}"
|
||||
|
||||
Reference in New Issue
Block a user