Skip to content

feat(signing): NEAR redirect provider (Reborn-only, replaces #3993) - #6576

Closed
zmanian wants to merge 1 commit into
attested-signing-07-injected-providerfrom
attested-signing-08-near-redirect
Closed

zmanian wants to merge 1 commit into
attested-signing-07-injected-providerfrom
attested-signing-08-near-redirect

Conversation

@zmanian

@zmanian zmanian commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Replaces #3993, which was closed on 2026-07-20 because it modified the legacy top-level src/ web ingress. src/ has since been deleted entirely (b6da0272a, #6375 — "delete v1 legacy monolith (src/) and cut deploy over to Reborn"), so this branch has been rebased onto current main with all src/ hunks excised. The crate-side work is unchanged.

Part of the attested-signing revival — see the tracking issue #6532 for the full plan.

What this PR is

NEAR browser-wallet redirect signing provider (ironclaw_wallet_external::near_redirect): the user is redirected to a NEAR wallet that signs the bound ApprovedTxHash with the account's ed25519 access key and redirects back with the signature. The wallet holds the keys and renders/signs natively (wallet-side WYSIWYS); IronClaw never has custody.

Cascade changes (vs the closed #3993)

  • Rebased onto current main (the stack was 1184 commits behind; merge base was 2026-05-24).
  • src/ excised: dropped the legacy src/channels/web/features/chat/attested.rs proof-ingress hunks (+696 lines across 3 files). The Reborn-side replacement lands in the gate/resolve ingress PR (feat(signing): reborn webui attested gate/resolve ingress (attested-signing PR11/12) #3995).
  • Adopted the hardened round-2 attestation API (these are the security fixes the lower PRs gained after this branch forked, and they must not regress):
    • AttestedSigningGrant::seal → fallible ::new (timestamp/expiry validation)
    • map_grant_error now maps GrantError::InvalidTimestamp/InvalidExpiry fail-closed to GrantClaimFailed

Verification

cargo build/cargo test -p ironclaw_wallet_external green (19 + 12 + 5 + 20 tests); attested_signing_boundaries boundary test green (incl. workspace_graph_is_openssl_free); clippy clean.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@ironloopai

ironloopai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

🔎 IronLoop Review Status

Head: f3afe3215ddee724e28688ddfe586d2210e2ee73
Result: One or more review results were superseded by a newer PR head.
Next: Run @ironloopai review on the latest PR head.
Updated: 2026-07-23T15:37:22.877Z

Current reviewers:

Reviewer State Verdict Findings Last update
ironloop/common-reviewer (reviewer) Superseded N/A N/A 2026-07-23T15:37:22.860Z
Reviewer summaries
Reviewer Detail
ironloop/common-reviewer (reviewer) Superseded by a newer PR head. New head: f3afe32. Previous verdict: Changes requested.
Recent activity
Time Reviewer State Detail
2026-07-23T14:09:59.915Z ironloop/common-reviewer (reviewer) Queued Accepted review request for head a95f8eb.
2026-07-23T14:09:59.915Z ironloop/common-reviewer (reviewer) Queued Waiting for this reviewer lane to become available.
2026-07-23T14:10:00.401Z ironloop/common-reviewer (reviewer) Started Reviewer worker started.
2026-07-23T14:10:03.718Z ironloop/common-reviewer (reviewer) Workspace ready Prepared isolated checkout (merge_ref) at 9b23130.
2026-07-23T14:14:32.780Z ironloop/common-reviewer (reviewer) Result captured Changes requested; 2 blocking findings.
2026-07-23T14:14:32.780Z ironloop/common-reviewer (reviewer) Completed Review completed and terminal status was persisted.
2026-07-23T15:37:22.860Z ironloop/common-reviewer (reviewer) Superseded A newer PR head replaced this review (f3afe32).
Available commands
  • @ironloopai help
  • @ironloopai agents
  • @ironloopai review
  • @ironloopai review --agent <agent>
Run metadata

Admission: webhook accepted the request and IronLoop persisted reviewer state before this projection.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • staging
  • reborn-integration

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b66f4fd7-2fa0-45b5-89f1-bbed4c5c0c10

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the scope: dependencies Dependency updates label Jul 23, 2026
@github-actions github-actions Bot added size: XL 500+ changed lines risk: low Changes to docs, tests, or low-risk modules contributor: core 20+ merged PRs labels Jul 23, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a95f8ebdd1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// bound operation. A function-call key restricted to a different
// receiver / method cannot authorize this transaction.
let bound_op = decode_bound_operation(context, approved_tx_hash);
validate_access_key_scope(&payload.access_key_scope, &bound_op)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Bind access-key scope before trusting FullAccess

When the redirect callback is for a NEAR function-call access key, payload.access_key_scope is entirely callback-controlled while the ed25519 signature verified above covers only approved_tx_hash.as_bytes(), so a caller can serialize FullAccess here and bypass the receiver/method checks. That returns VerifiedProof and consumes the grant for an operation the bound key scope may not cover; the scope needs to come from trusted gate state/chain lookup or be included in the signed/bound material, not from the proof payload.

AGENTS.md reference: AGENTS.md:L127-L134

Useful? React with 👍 / 👎.

@ironloopai ironloopai 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.

❌ IronLoop Review: reviewer

Review at a glance

Verdict Blocking Notes Inline Head
❌ Changes requested 2 0 2 a95f8ebdd115

Head: a95f8ebdd1159192b6f1c219e080bc12594657c7
Next: Fix the blocking findings, push the PR branch, then re-run this reviewer.

Run details

Status: Current
Needs human: no
Needs validation: no

Summary

The provider’s synthetic redirect/proof format is incompatible with NEAR wallet signing, and its access-key scope is attacker-controlled.

Findings

Blocking: 2 / Notes: 0

Blocking findings

1. ❌ [HIGH] Implement an actual NEAR wallet signing protocol

Location: crates/ironclaw_wallet_external/src/near_redirect/mod.rs:189-202
This base64-encodes arbitrary opaque bytes into transactions, while verify_resume later requires a raw ed25519 signature over ApprovedTxHash. NEAR Wallet Selector’s callback-capable flow signs a NEP-413 message payload; its transaction API accepts structured actions and signs-and-sends the transaction. A real wallet therefore cannot produce the synthetic NearRedirectProofPayload this verifier accepts, so the redirect cannot complete. Implement and verify a supported NEAR wire protocol, with an adapter-level integration fixture rather than tests that manufacture key.sign(hash) directly.

2. ❌ [HIGH] Do not trust the callback’s claimed access-key scope

Location: crates/ironclaw_wallet_external/src/near_redirect/mod.rs:396
access_key_scope is unsigned callback JSON, but this branch accepts its FullAccess claim without consulting NEAR state. A holder of the gate-bound function-call key can sign the raw hash directly, claim FullAccess, and bypass the receiver/method restrictions that threat #22 is meant to enforce. Obtain the permission for (account, public_key) from an authoritative source and validate the actual signed transaction, or reject non-verified full-access keys. Add a regression test for a function-call key claiming FullAccess.

Developer follow-up

After fixing this feedback:

  1. Push the fix to this PR branch.
  2. Re-run this reviewer with @ironloopai review --agent reviewer if you only changed this reviewer's findings.
  3. Re-run all reviewers with @ironloopai review when the fix may affect multiple areas.

approved_tx_hash: &ApprovedTxHash,
) -> String {
use base64::Engine as _;
let tx_b64 = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(decoded.as_opaque());

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 emits arbitrary opaque bytes as transactions but verification expects a synthetic raw-hash signature. NEAR wallet flows use structured transaction actions or NEP-413 message signing, so a real wallet cannot return the proof shape accepted here. Please implement and test a supported wallet wire protocol.

) -> Result<(), SigningProviderError> {
match scope {
// A full-access key authorizes any action on the account.
NearAccessKeyScope::FullAccess => Ok(()),

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.

access_key_scope comes from unsigned callback JSON. A function-call key can claim FullAccess and bypass the intended scope validation. Resolve the bound key’s real permission authoritatively and validate the actual signed operation (or reject unverified non-full-access keys).

@zmanian

zmanian commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #6755, part of consolidating the 20-PR attested-signing-* stack into 8 PRs against current main.

The content of this PR is carried forward in full, re-based on current main rather than on the old stack. That re-basing also fixed the CI failures that had been red here — including the missing [package.metadata.ironclaw] layer declarations, which were blocking the bottom of the stack and therefore every PR above it.

Closing to keep the queue honest. The review history stays on this PR and remains readable; reopen if the consolidation is rejected.

@zmanian zmanian closed this Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: low Changes to docs, tests, or low-risk modules scope: dependencies Dependency updates size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant