Skip to content

security(core): add tests for unauthorized-caller rejection paths #48

Description

@Demilade10

Description

This test-only security issue converts the authorization findings from #47 into regression coverage. Every merged auth-gated contract entry point must reject missing authorization and authorization from the wrong identity.

Blocked by #47: use its final entry-point and intended-authorizer table as the coverage checklist.

Requirements and Context

For every function classified by #47 as merchant-, subscriber-, or administrator-authorized, add:

  1. a no-auth test; and
  2. a wrong-authorizer test where the Soroban test framework can express the exact authorization tree.

At minimum, cover the merged versions of:

  • create_plan();
  • subscribe();
  • cancel();
  • upgrade and downgrade functions;
  • subscriber plan-version migration;
  • record_usage();
  • plan-version creation;
  • token/router/oracle allowlist or configuration;
  • trial/pricing configuration;
  • subscriber-controlled payment, slippage, cap, or migration settings;
  • every other mutation listed as auth-gated in security(core): audit require_auth() usage across all functions #47.

Do not assume functions from open issues exist. Test the exact merged contract.

Test methodology

A test using only env.mock_all_auths() cannot prove that the correct address was required.

Use:

  • env.set_auths(&[]) or the current SDK-equivalent for missing auth;
  • explicit MockAuth / MockAuthInvoke authorization trees for correct and wrong signers;
  • authorization assertions provided by soroban_sdk::testutils;
  • exact function names and arguments where require_auth_for_args() is used.

For each auth-gated function, prove:

  • no authorization fails;
  • an unrelated address's authorization fails;
  • the intended stored/supplied address succeeds;
  • authorization for different arguments cannot be replayed where argument-scoped auth applies;
  • failure occurs before any persistent mutation or external token/router/oracle call;
  • all relevant state and balances remain unchanged.

Pay special attention to functions that accept an Address parameter. Verify the function requires authorization from that exact identity rather than accepting any signature.

Where ownership should be derived from storage, verify that passing or referencing another user's subscription, plan group, or configuration does not redirect the auth check.

Expected permissionless/public behavior

Use the audit to add focused counter-tests confirming intentionally permissionless/public entry points remain callable without auth, including where merged:

  • charge(), while still enforcing state, schedule, and allowance rules;
  • get_plan();
  • get_subscription();
  • get_usage();
  • get_usage_report();
  • preview functions;
  • public configuration reads.

These tests prevent a future contributor from accidentally adding an authorization requirement that breaks keeper or client integrations.

Account and contract authorizers

Where the final design permits contract addresses as merchants, subscribers, or administrators, include appropriate tests or document why the scenario cannot be represented safely in the current test scope.

Do not assume account-address and contract-address authorization trees are identical.

Naming

Use consistent names such as:

test_<function>_requires_auth
test_<function>_rejects_wrong_authorizer
test_<function>_accepts_expected_authorizer
test_<function>_is_intentionally_permissionless

Security finding handling

If a test proves that unauthorized mutation is currently possible:

  • notify the maintainer;
  • open a separate high-priority linked security issue;
  • do not silently change production code in this test-only PR;
  • avoid publishing unnecessary exploit details before a fix is ready;
  • do not leave required CI failing;
  • retain a minimal reproducer as an explicitly ignored test linked to the security issue, or add it in the fix PR.

Suggested Execution

  • Branch name: security/unauthorized-caller-tests
  • Files to touch:
    • src/test.rs, or
    • focused Rust auth test modules if this keeps the suite maintainable
  • Example commit message: test(security): cover unauthorized callers for auth-gated functions

Test and Commit Steps

  1. Confirm security(core): audit require_auth() usage across all functions #47 is merged and copy its complete findings table into a local checklist.
  2. Map each merged entry point to no-auth, wrong-auth, correct-auth, and permissionless/public cases.
  3. Add explicit mock-auth trees without using mock_all_auths() as evidence.
  4. Snapshot relevant storage/balances before failures and assert zero mutation afterward.
  5. Run the complete native suite with crate-type = ["rlib"] on Windows if required:
cargo fmt --all -- --check
cargo test
cargo clippy --all-targets --all-features -- -D warnings
  1. Restore crate-type = ["cdylib", "rlib"] and build:
stellar contract build --target wasm32v1-none
  1. Commit the actual test paths:
git add src/test.rs
git commit -m "test(security): cover unauthorized callers for auth-gated functions"

If focused test modules are added, stage them as well.

Guidelines

  • Comment with confirmation that security(core): audit require_auth() usage across all functions #47 is merged and the proposed coverage matrix to request assignment.
  • Do not start or open a PR until assigned by a maintainer.
  • The PR description must include Closes #<issue-number>.
  • Keep the PR test-only.
  • Do not use mock_all_auths() as proof of correct authorization.
  • Test the exact expected identity, not merely the presence of any signature.
  • Treat every discovered unauthorized mutation as a security finding.
  • Preserve intentionally permissionless charge() and public reads.
  • Do not upgrade Soroban SDK from 27.0.3.
  • Follow Conventional Commits and repository branch conventions.

Complexity

High (200 pts)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions