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 events → no record of any read attempt, authorized or not.
#[event]
struct SetNewSecret has drop, store {
}
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 {});
}
public fun get_secret(caller: address):String acquires Vault{
assert!(caller == @owner,NOT_OWNER);
let vault = borrow_global<Vault>(@owner);
vault.secret
}
Risk
Likelihood:
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);
set_secret(&owner_signer, b"my secret");
debug::print(&b"Empty SetNewSecret event emitted");
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
}