Secret Vault on Aptos

First Flight #46
Beginner FriendlyWallet
100 EXP
View results
Submission Details
Impact: medium
Likelihood: high
Invalid

Missing Resource Existence Checks Causing Uncontrolled Aborts

Root + Impact

Description

Normal Behavior

The get_secret function should gracefully handle cases where no vault exists for the requested address, providing clear error messages or appropriate default behavior instead of crashing with cryptic technical errors.

Issue

The get_secret function calls borrow_global<Vault>(@owner) without first checking if the resource exists, causing uncontrolled aborts with confusing error messages when users attempt to read secrets before any vault has been created. This results in poor user experience where users receive technical error codes (MISSING_DATA) instead of meaningful feedback about the system state.

#[view]
public fun get_secret(caller: address): String acquires Vault {
assert!(caller == @owner, NOT_OWNER);
let vault = borrow_global<Vault>(@owner); // @> Aborts with MISSING_DATA if no vault exists
vault.secret
}

Risk

Likelihood:

  • Any call to get_secret before set_secret triggers the error

  • New users will encounter this immediately when checking for existing secrets

  • The vulnerability affects 100% of first-time users

  • No mechanism exists for callers to safely check vault existence beforehand

Impact:

  • Poor user experience: Users receive cryptic MISSING_DATA (code 4008) errors with technical messages like "Failed to borrow global resource"

  • Unpredictable behavior: Function success depends entirely on hidden state and calling order

  • Development friction: No way for developers to handle the "no data exists" case gracefully

  • Support burden: Users require assistance interpreting technical error codes

  • API inconsistency: Cannot distinguish between "no vault exists" and other system errors

Proof of Concept

The following test demonstrates the missing existence checks vulnerability:

#[test(owner = @0xcc)]
#[expected_failure(abort_code = 0x60001)] // RESOURCE_NOT_FOUND/MISSING_DATA
fun test_get_secret_aborts_without_vault(owner: &signer) acquires Vault {
account::create_account_for_test(signer::address_of(owner));
let owner_addr = signer::address_of(owner);
// Verify no vault exists initially
assert!(!exists<Vault>(owner_addr), 1000);
// This call aborts with cryptic MISSING_DATA error
let _secret = get_secret(owner_addr); // ❌ ABORTS with technical error!
}

Recommended Mitigation

Add existence check before attempting to borrow the resource:

+ const NO_VAULT_EXISTS: u64 = 2;
#[view]
public fun get_secret(caller: address): String acquires Vault {
assert!(caller == @owner, NOT_OWNER);
+ assert!(exists<Vault>(@owner), NO_VAULT_EXISTS);
let vault = borrow_global<Vault>(@owner);
vault.secret
}
Updates

Lead Judging Commences

bube Lead Judge 15 days ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Lack of `Vault` existence check in `get_secret`

There is no security impact on the protocol, therefore this is an Informational finding. Also, it is a user mistake, if the user calls `get_secret` without first calling `set_secret`.

Support

FAQs

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