Skip to content

fix(intent-bridge): validate signed decision enum - #322

Merged
imran-siddique merged 1 commit into
agentrust-io:mainfrom
altrudev:fix/intent-bridge-decision-enum-319
Sep 11, 2026
Merged

fix(intent-bridge): validate signed decision enum#322
imran-siddique merged 1 commit into
agentrust-io:mainfrom
altrudev:fix/intent-bridge-decision-enum-319

Conversation

@altrudev

Copy link
Copy Markdown
Contributor

Closes #319.

What

verify_bridge() now validates authorization.decision against the bridge's closed allow|deny contract before applying authorization semantics.

Previously the runtime used decision != "allow" as the denial branch, so a correctly re-signed bridge carrying malformed values such as true, 1, null, "", or "reject" was reported as AuthorizationDenied. That conflated malformed producer output with a legitimate signed denial.

The fix adds an explicit decision validator alongside the module's existing typed field checks. Only the literal valid "deny" reaches AuthorizationDenied; malformed values raise IntentBridgeError. The AuthorizationDenied docstring now names that literal-deny meaning.

Regression coverage

The tests re-sign each malformed authorization so the enum check, rather than signature failure, is load-bearing. They cover true, 1, null, "", and "reject", and assert that the resulting exception is IntentBridgeError but not AuthorizationDenied. The existing valid-deny test remains unchanged and continues to hold the policy-denial path.

Scope

No wire-format, signature, trust-key, scope, digest, transcript, or authorization-policy change. This aligns the runtime verifier with the published bridge schema and documented error boundary.

CHANGELOG.md is updated under Unreleased / Fixed.

Branch base: upstream a247244fb0bb97aea4e60d655f36d445cca18cd5.

DCO sign-off is present. AI-assistance disclosure: ChatGPT assisted with source review, duplicate search, implementation drafting, and regression construction. altrudev reviewed the bounded change and remains responsible for the contribution.

@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

🔴 Contributor Check: HIGH

Check Result
Profile HIGH
Credential LOW
Overall HIGH

Automated check by AgenTrust Contributor Check.

@github-actions github-actions Bot added the needs-review:HIGH Contributor check flagged HIGH risk label Sep 10, 2026
@altrudev
altrudev marked this pull request as ready for review September 10, 2026 18:47
@altrudev
altrudev requested review from a team and lywinged as code owners September 10, 2026 18:47
lywinged
lywinged previously approved these changes Sep 10, 2026

@lywinged lywinged left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

head daeec5c: Approve

Verified on daeec5c. Each of the five values from #319, true, 1, null, the empty string and "reject", re-signed with sign_bridge and verified at a fixed time inside the window, now raises IntentBridgeError with the message naming the field, and none of them is an AuthorizationDenied. The literal "deny" still is, "allow" still verifies, and a capitalised Allow, which I added, is refused as malformed rather than read as a denial.

The check sits with the module's other typed field checks, before the signature is verified, which is where #319 asked for it. So a malformed decision under a bad signature reports the decision, and a signed "deny" under a bad signature reports the signature. That is the order the other fields already had. The three things that went with the change are all here: the AuthorizationDenied docstring names the literal deny, the regression tests re-sign each value so the enum check and not the signature is what fails, and the CHANGELOG.md entry is under Unreleased, Fixed.

The four CI steps with the hash-pinned install, on 3.11 and on 3.12: green, 1407 passed, 1 skipped. With src/ reverted to a247244 and the new tests kept, exactly the five parametrized cases fail and nothing else, so the tests hold the change and no more.

For sequencing: the intent_bridge.py hunk here is not the one #325 touches, and CHANGELOG.md is the only overlap between the two.

@imran-siddique

Copy link
Copy Markdown
Member

Conflicted by my own merge, and it is mechanical. Apologies for the round trip.

I merged #315 a few minutes ago, which added a CHANGELOG.md entry at the same place yours sit. That conflicts #318, #322 and #323, all three on CHANGELOG.md and nothing else.

Rebase onto df0120b9b7d435d4217ffe34020461c6dd7bd0bf.

I trial-merged each of the three against current main before writing this. In every case the single conflicted file is CHANGELOG.md, both entries are kept, and no source file conflicts: intent_bridge.py in #318 and #322 and provenance.py in #323 all merge clean. I also checked the three against each other and against @lywinged's #325, which touches the same two modules: zero shared added lines in any pairing, so these are independent fixes in different places rather than competing ones.

On #322 itself: approved by @lywinged, all five checks green, and I was merging it when the conflict appeared. It goes in as soon as it applies.

The changelog collision is our problem rather than yours. Every open PR in this repo appends to one block, so each merge conflicts the rest. I am looking at whether a fragment directory fixes it properly.

Rebased onto upstream 760cc3f after the CHANGELOG-only conflict identified by maintainers.

Signed-off-by: Altru.dev <altrudevelop@gmail.com>

@imran-siddique imran-siddique left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approving and merging. Thank you for the fast rebase.

On the review state: @lywinged approved this at daeec5c and that approval was dismissed automatically when you rebased, not withdrawn. The rebase existed because I merged #315 into the same CHANGELOG.md block, which was my sequencing error rather than anything about this PR.

Verified the rebase preserved what that approval covered rather than assuming it. At 202b7b71: _decision still refuses anything that is not the literal "allow" or "deny", AuthorizationDenied still carries the "literal valid deny" wording, and the parametrised test still covers True, 1, None, "" and "reject". Same +33/-1 across the same three files.

The property this protects is worth restating since it is the reason it merges rather than the diff size: a verifier that reports a malformed value as AuthorizationDenied is telling a relying party that the producer said no, when what actually happened is that the producer said something unreadable. Those are different facts and only one of them is a policy decision.

@imran-siddique
imran-siddique merged commit 10fcba4 into agentrust-io:main Sep 11, 2026
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-review:HIGH Contributor check flagged HIGH risk

Projects

None yet

Development

Successfully merging this pull request may close these issues.

intent_bridge classifies malformed decision values as valid denial

3 participants