Skip to content

fix(security): bind verify_brand_claim response verifier to brand.json authorization - #5988

Open
garvitkaushik-123 wants to merge 1 commit into
adcontextprotocol:mainfrom
garvitkaushik-123:fix/brand-claim-verifier-authorization-binding
Open

fix(security): bind verify_brand_claim response verifier to brand.json authorization#5988
garvitkaushik-123 wants to merge 1 commit into
adcontextprotocol:mainfrom
garvitkaushik-123:fix/brand-claim-verifier-authorization-binding

Conversation

@garvitkaushik-123

Copy link
Copy Markdown
Contributor

Summary

Closes #5826 (security audit NS-SIG-002, P0 — brand-spoofing).

The designated-task payload-envelope response-signing profile required a verify_brand_claim / verify_brand_claims verifier to check the JWS signature, adcp_use, freshness, and request_hash — but no step bound the signing key to the asserted brand_domain. Since brand_domain and agent_url are attacker-choosable fields inside the payload the same key signs, an attacker running a conformant brand-agent for a domain it controls can emit a cryptographically valid owned envelope for brand_domain: "victim.example". The signature attests authorship, not brand authority.

Fix

Adds a normative Brand-authorization cross-check verifier step, mirroring the two anti-spoofing controls the spec already relies on — the governance token's step-13 iss→brand.json binding and the request-signing agent-URL match. Before trusting a signed envelope as evidence about brand_domain, an online verifier MUST:

  1. Resolve {brand_domain}/.well-known/brand.json (SSRF-safe, one redirect).
  2. Match a type: "brand" agents[] entry whose url byte-equals the envelope's agent_url — reject response_signature_agent_not_in_brand_json (none) / response_signature_brand_json_ambiguous (>1).
  3. Resolve the kid only within that entry's jwks_uri.

Until it passes, brand_domain / agent_url are untrusted. The binding applies to both assertion and rejection directions (an unbound signer could forge either).

Files

  • docs/building/by-layer/L1/security.mdx — the normative step (with reject reasons + vector citation).
  • docs/brand-protocol/tasks/verify_brand_claim.mdx / verify_brand_claims.mdx — trust-model cross-references (single + batch envelope).
  • static/compliance/source/test-vectors/brand-response-signing/brand-authorization-cross-check.json — 6 crypto-independent authorization-layer vectors.

Notes / scope

Test plan

  • Vector JSON valid (6 cases: forged-owned, wrong-type, trailing-slash, ambiguous, forged-rejection, authorized-accept)
  • lint-compliance-packaged-refs passes (no unresolvable refs)
  • All four referenced doc anchors resolve
  • Changeset included (patch; static/compliance/source/ is protocol-scoped)
  • Mintlify broken-links CI

…n authorization

The designated-task response-signing verifier checked signature, adcp_use,
freshness, and request_hash, but no step confirmed the signing key was
authorized for the asserted brand_domain. Since brand_domain and agent_url are
attacker-choosable fields inside the signed payload, an attacker running a
conformant brand-agent for a domain it controls could emit a cryptographically
valid `owned` envelope for any brand_domain (NS-SIG-002) — the signature
attested authorship, not brand authority.

Adds a normative Brand-authorization cross-check step mirroring governance
step 13: online verifiers MUST resolve {brand_domain}'s brand.json, match a
type:brand agents[] entry whose url byte-equals agent_url
(response_signature_agent_not_in_brand_json / _brand_json_ambiguous), and
resolve kid only within that entry's jwks_uri, before trusting either an
assertion or a rejection. Cross-referenced from both brand-claim trust models;
crypto-independent conformance vectors added.

Closes adcontextprotocol#5826

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Argus review could not complete

The automated review encountered an issue (possibly reached max turns, timed out, or failed to post the final gh pr review). A human reviewer should take this PR.

View workflow run

This is an automated message from the Argus AI review workflow.

@bokelley bokelley left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes verifier behavior too strongly for a 3.1 patch and conflicts with the recorded rollout: 3.2 should treat unbound evidence as advisory/untrusted, while mandatory hard-fail brand authorization is a 4.0 contract. Please rework this around the 3.2 trust annotation/advisory behavior and keep the eventual mandatory rejection semantics for the 4.0 design.

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.

2 participants