Skip to content

fix(errors): 403 auth rejections no longer read as policy blocks#197

Open
saurabhjain1592 wants to merge 1 commit into
mainfrom
fix/2861-403-auth-misclassification
Open

fix(errors): 403 auth rejections no longer read as policy blocks#197
saurabhjain1592 wants to merge 1 commit into
mainfrom
fix/2861-403-auth-misclassification

Conversation

@saurabhjain1592

Copy link
Copy Markdown
Member

Release-blocking library fix from the getaxonflow/axonflow-enterprise#2861 release-readiness gate. Makes 8.5.2 cut-ready (operator cuts the tag).

Bug

Every agent error envelope carries a literal "blocked" JSON key — a tenant-mismatch rejection is:

{"success":false,"error":"Tenant mismatch","blocked":false}   // HTTP 403

so handleErrorResponse's substring heuristic

case 403:
  if (body.contains("policy") || body.contains("blocked")) {
    throw new PolicyViolationException(errorMessage);
  }

classified every 403 authorization rejection as PolicyViolationException. Callers following the documented pattern (policy block = expected, non-fatal outcome) silently swallowed real auth failures with exit 0 — exactly how examples/basic passed the smoke with a wrong client-id/user-token tenant pairing.

Fix

The 403 branch parses the JSON body (Jackson, already in use) and treats a present blocked boolean as authoritative:

  • "blocked":truePolicyViolationException
  • "blocked":falseAuthenticationException (HTTP 403)
  • unparseable body, or no blocked boolean → legacy policy-phrase fallback (policy / block_reason; the bare blocked substring no longer counts)

Audited the rest of the method: this was the only substring-classification site — 401 always maps to AuthenticationException; 402/409/429/408/504 key on status code alone.

Tests

  • Unit (WireMock): exact live tenant-mismatch envelope → AuthenticationException with message intact; blocked:true envelope → PolicyViolationException; unparseable-body fallback in both directions. Full suite: 1316 tests, 0 failures.
  • New live-stack leg runtime-e2e/error_classification_403/ (no skip hatch) asserting both directions through proxyLLMCall.

Live runtime-e2e evidence (enterprise stack, platform v9.6.1, agent :8080)

Against this build (8.5.2 jar):

PASS [tenant-mismatch-403] AuthenticationException: Tenant mismatch
PASS [policy-block-403] PolicyViolationException: Request blocked by policy: Detects stacked DROP TABLE/DATABASE statement
RESULT: PASS (2/2)
EXIT=0

Same leg against the released 8.5.1 jar (proves the bug + the leg's sensitivity):

FAIL: REGRESSION: tenant-mismatch 403 classified as PolicyViolationException (Request blocked by policy: Tenant mismatch) — the "blocked":false envelope must map to AuthenticationException
EXIT=1

Release prep (same PR, per the version-alignment gate)

Refs getaxonflow/axonflow-enterprise#2861.

Release-gate smoke for getaxonflow/axonflow-enterprise#2861 found that
every agent error envelope carries a literal "blocked" JSON key (a
tenant-mismatch rejection is
{"success":false,"error":"Tenant mismatch","blocked":false}), so
handleErrorResponse's body.contains("policy") || body.contains("blocked")
substring heuristic classified EVERY 403 authorization rejection as
PolicyViolationException. Callers following the documented pattern of
treating policy blocks as an expected, non-fatal outcome silently
swallowed real auth failures (wrong client-id/user-token tenant pairing)
with exit 0.

The 403 branch now parses the JSON body and treats a present "blocked"
boolean as authoritative: true -> PolicyViolationException, false ->
AuthenticationException. Only unparseable or blocked-less bodies fall
back to the policy-phrase heuristic (policy / block_reason; the bare
"blocked" substring no longer counts). This is the only substring
classification site in the method — 401 always maps to
AuthenticationException and the remaining branches key on status code
alone.

Tests: exact live tenant-mismatch envelope -> AuthenticationException;
blocked:true envelope -> PolicyViolationException; unparseable-body
fallback both ways (1316 unit tests green). New live-stack leg
runtime-e2e/error_classification_403/ asserts both directions through
proxyLLMCall against a real enterprise agent; it fails against the
8.5.1 jar and passes against this build.

Release prep for 8.5.2: pom.xml bumped, CHANGELOG [Unreleased] renamed
to [8.5.2] - 2026-07-10 with the fix noted, example poms pinned to
8.5.2 per the 8.5.1 release convention (CI re-syncs them to the parent
version at run time).

Signed-off-by: Saurabh Jain <saurabh.jain@getaxonflow.com>
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.

1 participant