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 4 months 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.

Give us feedback!