fix(targeting): sanitize context-agent validation error responses - #474
Conversation
The context-agent HTTP handler (targeting/contextagent/handler.go)
echoed tmproto.ValidateContextRequest's raw err.Error() text straight
into the HTTP error response body, e.g. "property_id contains invalid
characters" or "seller_agent_url exceeds maximum length of 2048". This
violates AGENTS.md's generic-error-message invariant ("Never echo
err.Error() in HTTP responses ... details go to structured logs
(slog) inside the service boundary") and, unlike every other place
that runs the same ValidateContextRequest/ValidateIdentityRequest
check, never logged the failure server-side at all — so an operator
had zero record of why a request was rejected.
This exact pattern was swept and fixed elsewhere in this repo by
adcontextprotocol#190/adcontextprotocol#201 (landed in PR adcontextprotocol#210): router.HandleContextMatch,
router.HandleIdentityMatch, and identityagent's ServeHTTP were all
changed to log the validation error server-side via a
logValidationFailure helper and return a generic "invalid request"
message to the caller. That sweep did not touch
targeting/contextagent/handler.go, which is the handler actually
wired into the cmd/context-agent binary, so it kept the pre-adcontextprotocol#210
behavior.
Fix: add a logValidationFailure helper to the context-agent handler,
mirroring identityagent's implementation exactly (method, path, error,
and either request_id or request_id_valid=false, logged via
slog.Warn), and replace the err.Error()-in-response call with the
generic "invalid request" message.
Verification: confirmed via git blame that this line predates and was
untouched by the adcontextprotocol#210 sweep, and confirmed no open issue or PR already
covers this file. Regression tests added in handler_test.go assert
the HTTP response never contains the validator's field-specific text
while the structured log does; both tests were checked to fail against
the pre-fix source (reverting the handler.go change while keeping the
tests) and pass after restoring it.
IPR Policy Agreement Required@sujanchalla0510 — thanks for the contribution. Before this PR can be merged, the AgenticAdvertising.Org IPR Policy requires your agreement. To agree, post a new comment on this PR with the exact phrase: Your signature is recorded once and covers all contributions to AAO repositories. See |
|
I have read the IPR Policy |
|
Thanks for the acknowledgement, @sujanchalla0510. Generated by Claude Code |
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean security fix, no blocking or medium findings.
Checked:
targeting/contextagent/handler.gonow returns a generic "invalid request" instead of echoingtmproto.ValidateContextRequest'serr.Error()into the 400 body, closing the last context-agent path missed by the #190/#201/#210 validator-error sweep.logValidationFailuremirrors the identity-agent helper field-for-field.- Two verified regression tests added in
handler_test.go. - No schema/generated-type changes, no TMP signing/verification changes, no wire-shape or HTTP-status contract change (400 preserved; only body text sanitized).
high_risk is true only because both files match targeting/** — one is a benign modification with no findings, the other is a newly added test file. No deletion, no medium-or-higher concern on a modified sensitive file, so the flag alone does not warrant escalation. gated_paths is false. No no-auto-approve team match. Falls through to row 9.
bokelley
left a comment
There was a problem hiding this comment.
Maintainer review: generic caller-facing validation error, structured internal logging, and regression coverage are the correct production-hardening shape. Approved pending CI.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve.
This PR hardens targeting/contextagent/handler.go by no longer echoing ValidateContextRequest's err.Error() into the HTTP response (which leaked request-shaped detail such as "property_id contains invalid characters"), instead returning a generic "invalid request" while logging detail server-side via a new logValidationFailure that mirrors identityagent's pattern. A new handler_test.go pins both the generic-message invariant and unsafe-request-id elision.
Checks:
- No wire-shape/contract break; no HTTP-status change on a router endpoint.
- No
adcp/schemas/**oradcp/types_gen.gosurface touched (schema↔generated-type coherence intact). - No TMP signing/verification, TEE boundary, or protocol-managed-skill surface touched.
- high_risk is true only because
targeting/**is a high-risk glob: one file (modified) with no medium-or-higher findings, one file (added) — normal scaffolding. Not escalation-worthy. - gated_paths: false; review_decision: APPROVED; no no-auto-approve team match.
Decision table: no critical/high (row 1 no), gated_paths false (row 2 no), no deletions (row 3 no), no medium findings (rows 4/5/8 no), prior decision was approve so row 6 does not apply, no team gate (row 7 no) → row 9 approve.
Problem
targeting/contextagent/handler.go'sServeHTTPechoes the rawerr.Error()fromtmproto.ValidateContextRequestdirectly into the HTTPerror response body:
A caller sending a malformed
context_match_requestgets backvalidator-internal text such as
"property_id contains invalid characters"or"seller_agent_url exceeds maximum length of 2048"instead of a generic message, and the failure is never logged
server-side (
h.loggeris otherwise used elsewhere in this file, justnot here). This is a direct violation of AGENTS.md's error-message
invariant:
Just as importantly, this exact pattern was already identified and
swept out of the rest of the codebase: #190 ("Sweep TMP validator
errors for user-controlled value echoes") and #201 ("Audit TMP
validation rejection logging after error sanitization") were fixed by
PR #210, which changed
router.HandleContextMatch,router.HandleIdentityMatch, andidentityagent'sServeHTTPto logthe validation failure server-side via a
logValidationFailurehelperand return a generic
"invalid request"to the caller. That PR's diffdoes not touch
targeting/contextagent/handler.go— the handleractually wired into the
cmd/context-agentbinary — so it silentlykept the pre-#210 behavior. This PR closes that gap.
Verification
git blameon the affected line shows it predates PR fix(tmproto): sanitize validation error responses #210 and wasuntouched by it.
targeting/contextagent/handler.go; found none (Sweep TMP validator errors for user-controlled value echoes #190/Audit TMP validation rejection logging after error sanitization #201/fix(tmproto): sanitize validation error responses #210 coverrouter.go,targeting/identityagent/handler.go, andtmproto/validate.goonly).tmproto/validate.go/validate_ladder.gomessages don't interpolate raw user-supplied values (they use
static field names, lengths, and counts), so this isn't a literal
secret leak today — but it still directly contradicts the written
invariant, is inconsistent with the identical scenario already fixed
in the sibling identity-agent handler, and drops all server-side
diagnostics for rejected requests on this path.
Fix
Add a
logValidationFailurehelper totargeting/contextagent/handler.go, mirroringidentityagent.identityHandler.logValidationFailurefield-for-field(method, path, error, and either
request_idorrequest_id_valid=falsedepending ontmproto.SafeRequestIDForEcho),logged via
h.logger.Warn. The HTTP response now always gets thegeneric
"invalid request"message on a validation failure, matchingidentityagent's androuter.go's behavior for the same check.Test proof
Added
targeting/contextagent/handler_test.gowith two regressiontests:
TestContextHandlerValidationErrorIsGenericAndLogged— sends arequest with an invalid
property_id, asserts the HTTP responsemessage is exactly
"invalid request"and never contains"property_id", and asserts the structured log does contain thedetailed validator message (
"property_id contains invalid characters") plus method/path/request_id.TestContextHandlerInvalidRequestIDIsNotEchoed— sends a requestwhose
request_iditself failsvalidateSafeID("bad/id"),asserts it's neither echoed in the response nor written verbatim to
the log (elided in favor of
request_id_valid=false).Confirmed both are real regression tests: reverted the
handler.gosource change (
git stashon that file only, keeping the new testfile), re-ran
go test ./targeting/contextagent/... -run TestContextHandler -vand both tests failed against the pre-fixcode with exactly the expected diffs (response message
"property_id contains invalid characters"/"request_id contains invalid characters"instead of"invalid request", and empty logs).Restored the fix and re-ran — both pass.
No schema changes, no new dependencies, no behavior change on the
happy path — only the shape of the 400 response and server-side
logging on the validation-failure path for
POST /context.