Secret Vault on Aptos

First Flight #46
Beginner FriendlyWallet
100 EXP
View results
Submission Details
Severity: medium
Valid

Undefined @owner and broken authorization in `get_secret`

Root + Impact

Description

  • The get_secret function uses @owner in an equality check and in borrow_global````(@owner), but @owner is not defined anywhere. That makes the authorization check invalid and the code either won’t compile or — if modified incorrectly — will allow unauthorized reads of secrets. More importantly, the intended access control (“only the owner can read their secret”) is not implemented correctly

  • The bug is located in the get_secret view function:

// Root cause in the codebase with @> marks to highlight the relevant section
#[view]
public fun get_secret (caller: address):String acquires Vault{
assert! (caller == @owner,NOT_OWNER);
let vault = borrow_global<Vault >(@owner);
vault.secret
}

https://github.com/CodeHawks-Contests/2025-07-secret-vault/blob/c04b124c27f7e56ab553ab830870ba6085d31a1f/sources/secret_vault.move#L27C1-L33C6

In Move, @ is an operator, not a variable. The syntax @

is a shorthand for writing an address literal.owner is not a named address. The code uses @owner as if owner were a named address defined in the project's configuration. However, there is no indication that a named address called owner exists. Without this definition, the compiler has no idea what value @owner should resolve to.

Risk

Likelihood:

  • This is not a runtime issue that might occur under specific conditions; it is a guaranteed failure at the compilation stage.

Impact:

  • The module is completely unusable. It cannot be compiled or deployed.

Recommended Mitigation

  • Use owner: &signer so the runtime verifies the caller genuinely controls that account.

  • Return vector<u8> if you want binary-safe storage; otherwise return string::String.

  • exists<Vault>(addr) prevents panic when vault doesn't exist and provides a clear abort code.

- public fun get_secret (caller: address):String acquires Vault{
- assert! (caller == @owner,NOT_OWNER);
- let vault = borrow_global<Vault >(@owner);
- vault.secret
}
+ public fun get_secret(owner: &signer): string::String acquires Vault {
+ let addr = signer::address_of(owner);
+ assert!(exists<Vault>(addr), NOT_OWNER);
+ let vault_ref = borrow_global<Vault>(addr);
+ vault_ref.secret
}
Updates

Lead Judging Commences

bube Lead Judge 17 days ago
Submission Judgement Published
Validated
Assigned finding tags:

The protocol doesn't work as intended

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.