Skip to content

fix(wrapper): block forwarding to the management canister - #229

Merged
AntonioVentilii merged 5 commits into
mainfrom
av/management-canister-bypass-e40509
Jul 15, 2026
Merged

AntonioVentilii merged 5 commits into
mainfrom
av/management-canister-bypass-e40509

Conversation

@AntonioVentilii

Copy link
Copy Markdown
Contributor

Summary

The bridge's bridge_call only rejected calls whose target was the canister itself:

if args.target == ic_cdk::api::canister_self() {
    return Err("Self-calls are not allowed through the bridge.".to_string());
}

It did not block the IC management canister (aaaaa-aa). Because call0/call_blob are public #[update] entrypoints that forward an arbitrary target/method/raw args, any caller could set target = aaaaa-aa and have the bridge forward a management-canister call (update_settings, install_code, uninstall_code, stop_canister, …).

On the IC, an inter-canister call is authorized as the calling canister's principal, and management-canister methods authorize on whether the caller is a controller of the canister_id in the payload. So a forwarded management call executes with the bridge's own authority — if the bridge is (or becomes) a controller of any canister, an attacker could change controllers, uninstall code, or stop that canister.

Fix

Reject the management canister principal explicitly, alongside the existing self-call guard, via a new BridgeError::ForbiddenTarget variant.

Notes on severity

This is primarily a hardening / defense-in-depth fix. The concrete impact is contingent on the bridge controlling some canister; under a default deployment where the bridge controls nothing, the management canister would reject the forwarded call anyway. Blocking aaaaa-aa at the bridge closes the gap regardless of controller configuration. A longer-term improvement would be moving from a target denylist to an allowlist of permitted (target, method) pairs (the MethodKey/MethodConfig types already anticipate this).

Testing

  • cargo build / cargo clippy -p ic-papi-wrapper — clean
  • New integration test bridge_call_fails_if_target_is_management_canister passes alongside the existing bridge tests (3 passed).

The bridge only rejected calls whose target was the canister itself, so
any caller could set target=aaaaa-aa and have the bridge forward a
management-canister call (update_settings, uninstall_code, stop_canister,
...). Such calls execute authorized as the bridge's own principal, which
would let an attacker perform lifecycle operations against any canister
the bridge controls.

Reject the management canister principal explicitly, alongside the
existing self-call guard, and cover it with an integration test.
@AntonioVentilii
AntonioVentilii requested a review from a team as a code owner July 15, 2026 07:27
@zeropath-ai

zeropath-ai Bot commented Jul 15, 2026 •

Copy link
Copy Markdown

✅ No security or compliance issues detected. Reviewed everything up to a317ec6.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► src/wrapper/src/api/call.rs
    Add MANAGEMENT_CANISTER_ID constant and guard logic to block management canister access through the bridge
► src/wrapper/src/domain/errors.rs
    Add ForbiddenTarget error variant and Display handling
► src/wrapper/tests/it/bridge_tests.rs
    Add test bridge_call_fails_if_target_is_management_canister to verify protection against management canister access

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

Pull request overview

This PR hardens the ic-papi-wrapper bridge entrypoints (call0/call_blob) by preventing them from being used as a proxy to the IC management canister (aaaaa-aa), which would otherwise allow forwarded management-canister calls to execute with the bridge canister’s own authority.

Changes:

  • Introduces a BridgeError::ForbiddenTarget error variant for disallowed bridge targets.
  • Adds an explicit guard in bridge_call rejecting Principal::management_canister().
  • Adds an integration test ensuring bridge calls fail when the target is the management canister.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/wrapper/src/api/call.rs Adds an explicit management-canister target block inside bridge_call.
src/wrapper/src/domain/errors.rs Adds ForbiddenTarget variant and Display formatting.
src/wrapper/tests/it/bridge_tests.rs Adds integration test for rejecting management-canister targets.

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

@AntonioVentilii
AntonioVentilii enabled auto-merge (squash) July 15, 2026 07:35

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

Pull request overview

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

Comment thread src/wrapper/src/api/call.rs Outdated
@AntonioVentilii
AntonioVentilii merged commit aad4592 into main Jul 15, 2026
13 checks passed
@AntonioVentilii
AntonioVentilii deleted the av/management-canister-bypass-e40509 branch July 15, 2026 11:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants