Skip to content

Verify auth entries can't be used outside of the contract transaction#2471

Open
mootz12 wants to merge 2 commits intomainfrom
verify-non-root-auth
Open

Verify auth entries can't be used outside of the contract transaction#2471
mootz12 wants to merge 2 commits intomainfrom
verify-non-root-auth

Conversation

@mootz12
Copy link
Copy Markdown
Contributor

@mootz12 mootz12 commented Apr 7, 2026

What

The CLI currently relies on the RPC to check that no non-root auth is included in the simulation results. This PR adds an explicit check to verify that all non-source account authorizations are strictly tied to the root invocation. The check ensures that any signed authorization entries cannot be submitted outside the context of the invocation.

If any auth entry is detected that could be submitted outside of the transaction, all flagged entries are logged as follows:

$ contract invoke --source samwise --id CBKTFN4ATKHJMMKQFYWEUIPVE2FSRCQINHQNTID477G3247XLLE3KJAJ -- diff_auth_sub_auth --addr alice --val "Test" --subcall CCU2ER5XESXLT34MJTGY6GYMXHHJ3UB6NMTMLHSELT5WILZ7DNHJKOPV
⚠️  Authorization entries detected that could be submitted outside the context of this transaction:
  Auth Entry:
    Signer: GBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7L
    Function #0:
      Contract: CBKTFN4ATKHJMMKQFYWEUIPVE2FSRCQINHQNTID477G3247XLLE3KJAJ
      Fn: diff_auth_sub_auth
      Args:
        "1"
        "2"
      Function #0:
        Contract: CCU2ER5XESXLT34MJTGY6GYMXHHJ3UB6NMTMLHSELT5WILZ7DNHJKOPV
        Fn: do_auth
        Args:
          "GBXFXNDLV4LSWA4VB7YIL5GBD7BVNR22SGBTDKMO2SBZZHDXSKZYCP7L"
          Test
 
❌ error: Signing authorization entries that could be submitted outside the context of the transaction is not supported in the CLI    

If an auth entry is detected where the source account is used directly instead of with Credentials::SourceAccount, the error text changes to:

~ entry
❌ error: Source account detected with Address credentials. This requires an extra signature and is not expected.

Why

The CLI eagerly signs authorization entries returned from the user specified RPC. If an unsafe auth entry was included, the user might unexpectedly sign for something they did not intend.

This adds a check to ensure that everything the CLI signs aligns with the transaction that got submitted.

Known limitations

require_auth_with_args for non-source accounts

The change also blocks contracts that use require_auth_with_args for non-source accounts. Consider a transaction where a user submits from a source account UserA, and also controls UserB custom_token.transfer(from: UserB, to: UserC, amount: 100). If a malicious RPC intercepts this request, the could inject a bad auth such that custom_token.transfer(from: UserC, to: UserB, amount: 100. This technically satisfies the root auth requirement as the auth entry exists for the root contract and function invocation, but the arguments are different.

No bypass flag

This check currently cannot be bypassed. Most Stellar devtools (like the CLI and Stellar Lab) simulate in recording mode, which blocks non-root authorizations by default. If we add a flag to bypass this, we should also make the change to simulate in AuthMode::RecordAllowNonRoot. However, we should keep this consistent between Lab and CLI to prevent confusion, so this change was omitted for now.

Copilot AI review requested due to automatic review settings April 7, 2026 14:51
@github-project-automation github-project-automation bot moved this to Backlog (Not Ready) in DevX Apr 7, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a local (CLI-side) validation step to ensure Soroban authorization entries returned from RPC simulation are “strict” (tied to the root invocation) before the CLI signs them, reducing risk of signing replayable/out-of-context auth.

Changes:

  • Call a new auth::check_auth validation step before signing auth entries in sim_sign_and_send_tx.
  • Add formatting/logging helpers to render auth entry trees when validation fails.
  • Add new integration fixture + tests around auth behaviors; add missing network passphrase verification in extend/restore.

Reviewed changes

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

Show a summary per file
File Description
cmd/soroban-cli/src/tx.rs Runs check_auth before signing Soroban auth entries.
cmd/soroban-cli/src/signer/mod.rs Adds new signer error variants for invalid/out-of-context auth.
cmd/soroban-cli/src/log/auth.rs Adds pretty-formatting for auth entries / invocation tree display.
cmd/soroban-cli/src/lib.rs Exposes new auth module.
cmd/soroban-cli/src/commands/contract/restore.rs Verifies RPC network passphrase before network operations.
cmd/soroban-cli/src/commands/contract/extend.rs Verifies RPC network passphrase before network operations.
cmd/soroban-cli/src/auth.rs New auth validation logic + unit tests.
cmd/crates/soroban-test/tests/it/integration/util.rs Adds AUTH wasm fixture constant.
cmd/crates/soroban-test/tests/it/integration/auth.rs New integration tests for auth scenarios.
cmd/crates/soroban-test/tests/it/integration.rs Registers new auth integration module.
cmd/crates/soroban-test/tests/fixtures/test-wasms/auth/src/lib.rs Adds a dedicated auth-focused test contract fixture.
cmd/crates/soroban-test/tests/fixtures/test-wasms/auth/Cargo.toml New fixture crate manifest for test_auth.
Cargo.lock Adds test_auth package entry.

};

let mut non_strict_entries: Vec<&SorobanAuthorizationEntry> = Vec::new();
for entry in invoke_host_op.auths() {
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

InvokeHostFunctionOp (from stellar_xdr) doesn't provide an auths() accessor in this repo (and there’s no extension trait implementing it), so this loop won’t compile. Iterate over the auth field instead (e.g., invoke_host_op.auth.as_slice() / .iter()).

Suggested change
for entry in invoke_host_op.auths() {
for entry in invoke_host_op.auth.as_slice() {

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +55
// Check if source account appears as Address credential
if let Some(auth_addr) = auth_address_bytes(&creds.address) {
if source_bytes == auth_addr {
print.warnln("Source account detected with Address credentials. This requires an extra signature and is not expected.");
print.warnln(format_auth_entry(entry));
return Err(signer::Error::InvalidAuthEntry);
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

The PR description says the error text should change when the source account is recorded with Address credentials, but this path returns signer::Error::InvalidAuthEntry (displayed as "Invalid Soroban authorization entry") and only emits the specific message via warnln. If the intent is for the CLI to surface the specific message as the returned error, add a dedicated signer::Error variant (or otherwise propagate this message) and return that instead of InvalidAuthEntry.

Copilot uses AI. Check for mistakes.
.arg(&format!("--subcall={id2}"))
.assert()
.failure()
.stderr(predicates::str::contains("Auth, InvalidAction"));
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

These failure expectations appear outdated with the new check_auth behavior. partial_auth_sub_auth uses require_auth_for_args, so simulation can succeed but the recorded auth root invocation args won’t match the tx invocation args; check_auth should reject it with the new out-of-context auth error (and/or the source-account-as-Address error), rather than surfacing a host error like "Auth, InvalidAction".

Suggested change
.stderr(predicates::str::contains("Auth, InvalidAction"));
.stderr(
predicates::str::is_match("out[- ]of[- ]context|source-account-as-Address")
.unwrap(),
);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog (Not Ready)

Development

Successfully merging this pull request may close these issues.

2 participants