Root + Impact
set_secret unconditionally does move_to even if Vault already exists. A second call aborts with RESOURCE_ALREADY_EXISTS, preventing update of the secret.
Description
The owner should be able to rotate or update their secret. A second set_secret to the same address aborts and there is no function that specifically updates the secret in case the Vault already exists.
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
Owner cannot update the secret and there is no function that updates a Vault if it already exists.
Proof of Concept
Consider running the following test :
    #[test(owner = @0xcc)]
    #[expected_failure(major_status = 4004, location = Self)]
    fun test_set_secret_again(owner: &signer) {
        use aptos_framework::account;
        
        account::create_account_for_test(signer::address_of(owner));
        let bytes = b"i'm a secret";
        set_secret(owner, bytes);
        set_secret(owner, bytes);
    }
Recommended Mitigation
- 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 entry fun set_secret(caller: &signer, secret: vector<u8>) acquires Vault {
+     let addr = signer::address_of(caller);
+     if (!exists<Vault>(addr)) {
+         move_to(caller, Vault { secret: string::utf8(secret) });
+     } else {
+         let v = borrow_global_mut<Vault>(addr);
+         v.secret = string::utf8(secret);
+     };
+     event::emit(SetNewSecret {});
+ }