Secret Vault on Aptos

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

Inadequate Event Emission Prevents Monitoring, Debugging & Audit Trails

Root + Impact

Description

  • A production-grade secret-vault contract should emit rich events (who, when, what, success/failure) so that

    • off-chain indexers can reconstruct state,

    • security teams can build audit trails, and

    • front-ends can give real-time feedback.

Specific issue

  • set_secret emits an empty event (SetNewSecret {}) → no user address, no timestamp, no metadata.

  • get_secret emits zero eventsno record of any read attempt, authorized or not.

// Root cause in the codebase with @> marks to highlight the relevant section
// 1. Storage emits an empty shell
#[event]
struct SetNewSecret has drop, store { // @> empty struct
}
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 {}); // @> no context at all
}
// 2. Retrieval is a black box
public fun get_secret(caller: address):String acquires Vault{
assert!(caller == @owner,NOT_OWNER);
let vault = borrow_global<Vault>(@owner);
// @> zero events emitted
vault.secret
}

Risk

Likelihood:

  • Every legitimate user action triggers these code paths.

  • Security tooling expects actionable events; they are absent by design.


Impact:

  • No audit trail – impossible to answer “Who stored/retrieved what and when?”

  • Frontend dead-end – dApps cannot show status updates (no user address, no timestamp).

  • Incident response blind spot – brute-force or unauthorized retrieval attempts leave zero on-chain trace.


Proof of Concept

#[test(owner = @0xcc)]
fun test_event_emission_gaps() acquires Vault {
use aptos_framework::account;
account::create_account_for_test(@0xcc);
let owner_signer = account::create_signer_for_test(@0xcc);
// 1) Store secret → emits empty SetNewSecret
set_secret(&owner_signer, b"my secret");
debug::print(&b"Empty SetNewSecret event emitted");
// 2) Retrieve secret → emits nothing
let _secret = get_secret(@0xcc);
debug::print(&b"No event emitted on retrieval");
}
CLI output:
Copy
[debug] 0x456d707479205365744e6577536563726574206576656e7420656d6974746564
[debug] 0x4e6f206576656e7420656d6974746564206f6e2072657472696576616c
[ PASS ] 0x234::vault::test_event_emission_gaps

Recommended Mitigation

- remove this code
+ add this code
-#[event]
-struct SetNewSecret has drop, store {}
+#[event]
+struct SecretStored has drop, store {
+ user: address,
+ timestamp: u64,
+ secret_length: u64,
+}
+
+#[event]
+struct SecretRetrieved has drop, store {
+ user: address,
+ timestamp: u64,
+}
public entry fun set_secret(caller: &signer, secret: vector<u8>) {
+ let caller_addr = signer::address_of(caller);
let secret_vault = Vault{secret: string::utf8(secret)};
move_to(caller, secret_vault);
- event::emit(SetNewSecret {});
+ event::emit(SecretStored {
+ user: caller_addr,
+ timestamp: aptos_framework::timestamp::now_seconds(),
+ secret_length: vector::length(&secret),
+ });
}
public fun get_secret(caller: address): String acquires Vault {
assert!(caller == @owner, NOT_OWNER);
+ event::emit(SecretRetrieved {
+ user: caller,
+ timestamp: aptos_framework::timestamp::now_seconds(),
+ });
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:

Insufficient Data in `SetNewSecret` event

This is an Informational finding. It has no impact on the security of the protocol.

Support

FAQs

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