Secret Vault on Aptos

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

Resource Overwrite / Inability to Update Secret

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

    Answer: The set_secret function should allow the vault owner to create or update their stored secret multiple times without failure, ensuring the secret remains under their control.


  • Explain the specific issue or problem in one or more sentences

    The function always uses move_to to create a new Vault resource. In Move, move_to aborts if the resource already exists under that account, which means the owner can only set their secret once. Any attempt to update it later will fail with a RESOURCE_ALREADY_EXISTS error.

public entry fun set_secret(caller:&signer, secret:vector<u8>) {
let secret_vault = Vault{secret: string::utf8(secret)};
@> move_to(caller, secret_vault);
event::emit(SetNewSecret {});
}

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

    This occurs whenever the same account tries to call set_secret more than once, since the first call already placed a Vault resource at their address.


  • Reason 2

    This occurs because there is no logic to check for the existence of the Vault resource and mutate it; instead, the code blindly tries to create a new one every time.

Impact:

  • Impact 1

    The contract fails to support secret updates, locking users into their first stored value permanently.


  • Impact 2

    This undermines usability and can cause users to lose control if they mistakenly set the wrong secret, as they cannot overwrite it without deleting their account’s state.

Proof of Concept

The first call creates a Vault resource at the owner’s address.

The second call attempts to use move_to again, but since the Vault already exists there, Move aborts with a RESOURCE_ALREADY_EXISTS error.

This demonstrates that the vault cannot be updated once initialized.

#[test(owner = @0xcc)]
fun test_overwrite_failure(owner: &signer) acquires Vault {
let secret1 = b"first_secret";
vault::set_secret(owner, secret1); // works
let secret2 = b"second_secret";
// This will abort with RESOURCE_ALREADY_EXISTS
vault::set_secret(owner, secret2);
}

Recommended Mitigation

Before creating a new Vault, check whether the resource already exists at the signer’s address.

If it does, borrow it mutably and update the secret field instead of overwriting.

If it does not exist, create it normally with move_to.

This ensures the vault can be created once and updated safely multiple times, meeting expected behavior.

- remove this code
- let secret_vault = Vault{secret: string::utf8(secret)};
- move_to(caller, secret_vault);
+ add this code
+ if (exists<Vault>(signer::address_of(caller))) {
+ let vault = borrow_global_mut<Vault>(signer::address_of(caller));
+ vault.secret = string::utf8(secret);
+ } else {
+ move_to(caller, Vault { secret: string::utf8(secret) });
+ }
Updates

Lead Judging Commences

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

The `secret` can not be updated

Support

FAQs

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