feat: reject update calls when sender_info restricts permissions to queries#10447
feat: reject update calls when sender_info restricts permissions to queries#10447aterga wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
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.infoinic-validatorto extractimplicit:permissionsand reject update calls when it equals"queries". - Introduce a new validation error
UpdateCallNotPermittedBySenderInfoand plumb it throughic-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.
| "//rs/crypto/tree_hash", | ||
| "//rs/limits", | ||
| "//rs/types/types", | ||
| "@crate_index//:candid", |
There was a problem hiding this comment.
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.
…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
535eb25 to
0c5ce91
Compare
|
Closing in favor of the more lightweight approach in #10449 (delegations carry the Generated by Claude Code |
Queries-only permission for
sender_info— combined read-only-session designStacked 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 tomasterautomatically when #10452 merges.The combined design
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 asender_infoare rejected (SenderInfoRequiredByDelegation).sender_infowhoseinfoblob is a Candid-encoded ICRC-3 map (the format II certifies under theic-sender-infodomain separator) with the attributeimplicit: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_sessionse2e test module inrs/validator/ingress_message/tests/validate_request.rsdemonstrates 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)
HttpRequestVerifier<SignedIngressContent>::validate_request— the same layer that enforces delegationtargets; the ingress manager uses the same verifier for payload validation, so the restriction also holds during block validation.implicit:permissions(single colon), following II's existingimplicit:nonce/implicit:originconvention. The attribute parsing only runs after the canister-signature verification of the blob succeeded, with bounded Candid decoding quotas."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./callare also rejected (the validator cannot know method kinds); restricted callers use the query path.Valuetype is mirrored minimally insideic-validatorto avoid pulling ledger packages into the consensus-critical validator.Tests
sender_info, plus the combinedread_only_sessionsmodule described above.requests_with_sender_info_permissionsinrs/tests/crypto/ingress_verification_test.rs) demonstrating attribute enforcement against a real replica across all/callAPI versions.Related
read_only→ attribute issuance / delegation bit) are prepared as patches pending repo access.cargo test -p ic-validator -p ic-validator-ingress-message -p ic-typespasses 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