Skip to content

feat: reject update calls when sender_info restricts permissions to queries#10447

Closed
aterga wants to merge 4 commits into
claude/fervent-mccarthy-tmfzpw-sender-info-requiredfrom
claude/fervent-mccarthy-tmfzpw
Closed

feat: reject update calls when sender_info restricts permissions to queries#10447
aterga wants to merge 4 commits into
claude/fervent-mccarthy-tmfzpw-sender-info-requiredfrom
claude/fervent-mccarthy-tmfzpw

Conversation

@aterga

@aterga aterga commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Queries-only permission for sender_info — combined read-only-session design

Stacked on #10452 (delegations can require a signed sender_info): this PR is now rebased on top of that branch, and together they form the complete, protocol-enforced read-only-session design. The base of this PR is set to the #10452 branch so the diff shows only the attribute-checking part; GitHub will retarget it to master automatically when #10452 merges.

The combined design

  1. feat: delegations can require calls to carry a signed sender_info #10452 — the delegation bit: the issuer (Internet Identity) signs the session delegation with sender_info_required = true. The field is covered by the delegation signature, so the dapp holding the session key cannot strip it; update calls without a sender_info are rejected (SenderInfoRequiredByDelegation).
  2. This PR — the attribute semantics: when an update call carries a sender_info whose info blob is a Candid-encoded ICRC-3 map (the format II certifies under the ic-sender-info domain separator) with the attribute implicit:permissions = "queries", the call is rejected (UpdateCallNotPermittedBySenderInfo). Query calls are unaffected.

Together: a read-only session must present its certified attributes on every update call (1), and those attributes forbid update calls (2) — so the restriction holds regardless of client cooperation, while all permission semantics stay in the extensible attribute map rather than the delegation format. The new read_only_sessions e2e test module in rs/validator/ingress_message/tests/validate_request.rs demonstrates the full story: update with queries-only attributes → rejected; update omitting attributes → rejected; queries → permitted; update with "updates" attributes → permitted.

Design notes (attribute checking, this PR)

  • Enforcement point: HttpRequestVerifier<SignedIngressContent>::validate_request — the same layer that enforces delegation targets; the ingress manager uses the same verifier for payload validation, so the restriction also holds during block validation.
  • Attribute key: implicit:permissions (single colon), following II's existing implicit:nonce / implicit:origin convention. The attribute parsing only runs after the canister-signature verification of the blob succeeded, with bounded Candid decoding quotas.
  • Fail-open for unknown values (legacy-blob compatibility): only the exact value "queries" restricts. Note the presence requirement of feat: delegations can require calls to carry a signed sender_info #10452 and the delegation field itself are fail-closed.
  • Update-call path, not update methods: replicated queries via /call are also rejected (the validator cannot know method kinds); restricted callers use the query path.
  • The ICRC-3 Value type is mirrored minimally inside ic-validator to avoid pulling ledger packages into the consensus-critical validator.

Tests

  • Unit tests for permission extraction/decision logic.
  • End-to-end tests with validly canister-signed sender_info, plus the combined read_only_sessions module described above.
  • System test (requests_with_sender_info_permissions in rs/tests/crypto/ingress_verification_test.rs) demonstrating attribute enforcement against a real replica across all /call API versions.

Related

cargo test -p ic-validator -p ic-validator-ingress-message -p ic-types passes on the stack, clippy is clean, and the system test binary compiles. Bazel was not available in the development container, so CI should confirm the Bazel build.

https://claude.ai/code/session_01BQNgPJKgxWohrJ6owwJBzz

Comment thread rs/validator/ingress_message/tests/validate_request.rs Outdated

Copilot AI 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.

Pull request overview

This PR introduces a protocol-level restriction on sender_info for update calls: if a canister-signed sender_info.info decodes as an ICRC-3 map with implicit:permissions = "queries", the replica rejects /call requests during ingress validation (while leaving query/read_state validation behavior unchanged).

Changes:

  • Add Candid-decoding of sender_info.info in ic-validator to extract implicit:permissions and reject update calls when it equals "queries".
  • Introduce a new validation error UpdateCallNotPermittedBySenderInfo and plumb it through ic-validator-ingress-message.
  • Add unit + integration tests and update Cargo/Bazel dependencies needed for Candid decoding in the validator and for encoding test payloads.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
rs/validator/src/ingress_validation.rs Add permission extraction/decision logic and new validation error for update calls.
rs/validator/src/ingress_validation/tests.rs Add unit tests covering permission extraction and fail-open behavior.
rs/validator/ingress_message/tests/validate_request.rs Add end-to-end tests verifying update rejection vs query acceptance with permissions.
rs/validator/ingress_message/src/lib.rs Add new public error variant and Display message in the ingress-message wrapper crate.
rs/validator/ingress_message/src/internal/mod.rs Map the new ic_validator error into the wrapper crate error.
rs/validator/ingress_message/Cargo.toml Add candid dev-dependency for encoding test payloads.
rs/validator/ingress_message/BUILD.bazel Add Bazel test dependency on candid.
rs/validator/Cargo.toml Add candid, serde, and serde_bytes dependencies needed for decoding in validator.
rs/validator/BUILD.bazel Add Bazel deps for candid, serde, and serde_bytes to the validator library.
Cargo.lock Record new dependency edges for candid/serde_bytes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rs/validator/src/ingress_validation.rs Outdated
Comment thread rs/validator/BUILD.bazel
"//rs/crypto/tree_hash",
"//rs/limits",
"//rs/types/types",
"@crate_index//:candid",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Note to reviewer.

If you dislike adding candid as a dependency here, we can provide a non-candid endpoint in II to serve a different encoding of sender_info - please indicate your preferred encoding to keep this thread actionable.

claude added 4 commits June 12, 2026 10:06
…ueries

When an update call carries a sender_info whose info blob is a
Candid-encoded ICRC-3 map (the format used by Internet Identity to
certify request attributes, signed under the "ic-sender-info" domain
separator) and the map contains the "implicit:permissions" attribute
with the text value "queries", the ingress validator now rejects the
call with the new UpdateCallNotPermittedBySenderInfo error. Query calls
and all other cases (no sender info, unparseable info blob, absent
attribute, or any other attribute value) remain permitted.

This enables Internet Identity users to sign limited delegations that
can only read on their behalf (call query methods) but cannot change
state (call update methods), with the restriction enforced by the
protocol rather than by each individual canister.

https://claude.ai/code/session_01BQNgPJKgxWohrJ6owwJBzz
Import BTreeMap and CandidType instead of qualifying them in the
sender_info integration tests, and avoid cloning the permissions string
in sender_info_permissions by removing the entry from the owned map.

https://claude.ai/code/session_01BQNgPJKgxWohrJ6owwJBzz
Extends the ingress verification system test suite with a test that
demonstrates the implicit:permissions sender_info attribute end-to-end:
a signer canister certifies an ICRC-3 attributes map restricting the
sender to "queries", after which the protocol rejects the sender's
update calls during ingress validation while query calls remain
permitted (and the receiving canister can read the certified attributes
via the msg_caller_info system API). Attribute maps with permissions
"updates", or without the permissions attribute, leave update calls
permitted.

https://claude.ai/code/session_01BQNgPJKgxWohrJ6owwJBzz
Adds e2e tests for the combined design: a delegation with
sender_info_required = true forces update calls to carry the certified
session attributes, whose implicit:permissions attribute then determines
whether update calls are permitted. Covers rejection with queries-only
attributes, rejection when omitting the attributes, and acceptance of
queries and of update calls with updates-permissions.

https://claude.ai/code/session_01BQNgPJKgxWohrJ6owwJBzz
@aterga aterga force-pushed the claude/fervent-mccarthy-tmfzpw branch from 535eb25 to 0c5ce91 Compare June 12, 2026 10:10
@aterga aterga changed the base branch from master to claude/fervent-mccarthy-tmfzpw-sender-info-required June 12, 2026 10:10

aterga commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Closing in favor of the more lightweight approach in #10449 (delegations carry the queries/all permissions directly), which is fully green and consolidates this POC. The sender_info-attribute checking explored here remains documented in this PR's history should the certified-attributes route be revisited.


Generated by Claude Code

@aterga aterga closed this Jun 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants