-
Notifications
You must be signed in to change notification settings - Fork 2
feat(env-bridge): the machine verifies the owner's click itself — a passkey assertion bound to the frozen request (hardening B) #2599
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
5cd1f72
feat(env-bridge): derive the approval challenge from the frozen reque…
2witstudios 54a225e
feat(env-bridge): pin the owner's passkeys to the machine at enrolmen…
2witstudios 47fdaff
feat(env-bridge): carry the owner's assertion to the machine in the a…
2witstudios ab9a176
feat(env-bridge): the daemon verifies the owner's assertion itself (B4)
2witstudios 53ae331
feat(env-bridge): fail closed when no owner credential is pinned, and…
2witstudios e9a6710
test(db): allow the one credential-shaped column explicitly, and pin …
2witstudios 15ed48b
refactor(env-bridge): drop two exports nothing consumes
2witstudios 303abc7
fix(db): drop a duplicate readFileSync import in the schema test
2witstudios 45223f2
fix(env-bridge): the grant signer dropped the owner's assertion, so n…
2witstudios 395be78
fix(env-bridge): bind the owner's chosen SCOPE into the assertion (Co…
2witstudios 93b27d4
fix(env-bridge): verify every algorithm a passkey can be registered w…
2witstudios 7475c1f
fix(env-bridge): a refusal that is not the owner's decision must not …
2witstudios e7568c7
test(env-bridge): make the ReDoS guard test a real guard instead of a…
2witstudios ce1db9d
fix(env-bridge): keep hardening A's writeModes inside what the owner'…
2witstudios 1591f51
test(env-bridge): restructure the WebAuthn fixture so CodeQL's taint …
2witstudios f15f0be
test(env-bridge): suppress the WebAuthn hash alerts narrowly, with th…
2witstudios 1e9f8a2
test(env-bridge): break the CodeQL taint by deriving the challenge lo…
2witstudios f21574b
fix(cli): stop run.test.ts making real requests to pagespace.ai — the…
2witstudios File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the actual route → bridge-client → signer path, this assertion is dropped:
signGrantFramereconstructsapprovalIntentinapps/web/src/lib/env-bridge/grant-signer.tswith onlychallengeId,scope, andexpiresAt. Consequently every browser approval reaches the daemon without an assertion,verifyOwnerApprovalreturnsapproval_unproven, and the route has already consumed the pending challenge. Preserve the assertion when constructing the signed grant and test the full path through the signer.Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirmed, and this was the important one — the feature did not work at all. Fixed in
822015b92.signGrantFramerebuiltapprovalIntentfrom{challengeId, scope, expiresAt}, so every real browser approval reached the machine with no assertion, was refusedapproval_unproven, and the route had already spent the pending question — so the owner could not even retry. Your reading of why the tests missed it is exactly right: the route, the daemon and the codec each had coverage, and none crossed the hop between them.So the fix is one line and the test is the point.
apps/web/src/app/api/env-bridge/approvals/__tests__/click-end-to-end.test.tsdrives the whole path with nothing mocked between the hops — the card's POST body → the real route → the realsignGrantFrameunder a real Ed25519 keyring → the real canonical bytes, JSON round-tripped as a socket sends them →decodeFrame→verifyGrant→verifyOwnerApproval— and asserts the assertion survives each one and the daemon allows the run. The last three are the daemon's own gates; only the socket is stubbed. It also covers stripping or altering the assertion after signing (bad_signature, since it is under the server's signature) and a click the owner never signed (stillapproval_unproven— the route cannot invent a proof).Mutation: reintroducing the exact defect turns that row red; the identity control survived.
Separately, and prompted by your last paragraph: the route no longer burns the question on a refusal that is not the owner's decision (
1b4e77ebe).approval_unprovenand a transport failure now restore it — the daemon had not spent its challenge either, so the two sides were disagreeing until the TTL — while allow, deny andapproval_mismatchstill spend it.