# Resource Existence Collision DoS on Secret Updates
## Description
* The `set_secret()` function is intended to allow owners to set and update their vault secrets
* The function uses `move_to()` unconditionally, which will abort if a `Vault` resource already exists at the caller's address. After the first successful call to `set_secret()`, all subsequent calls will fail with a runtime error, permanently preventing secret updates or rotation
```java
public entry fun set_secret(caller: &signer, secret: vector<u8>) {
let secret_vault = Vault { secret: string::utf8(secret) };
move_to(caller, secret_vault); // @> This aborts if Vault already exists
event::emit(SetNewSecret {});
}
```
## Risk
**Likelihood**:
* Any second call to `set_secret()` by the same user will trigger an abort
* The issue occurs immediately after the first successful secret creation
* No workaround exists within the current contract design
* The problem is deterministic and affects all users who attempt to update their secrets
**Impact**:
* Complete denial of service for secret rotation functionality after first use
* Users cannot update compromised or outdated secrets
* Poor user experience with cryptic runtime errors on legitimate operations
* Security degradation as users cannot respond to potential secret compromises
* Business logic failure preventing normal application workflows
## Proof of Concept
* Add this test to `secret_vault.move` file and see the results
```java
#[test(owner = @0xcc)]
fun test_set_secret_dos(owner: &signer) {
use aptos_framework::account;
account::create_account_for_test(signer::address_of(owner));
let secret1 = b"first-secret";
let secret2 = b"rotated-secret";
// First set_secret call should succeed
set_secret(owner, secret1);
// This proves the DoS bug - second call will abort
set_secret(owner, secret2); // ABORT: Resource already exists
}
```
## Recommended Mitigation
* Here is the recommended mitigation
```diff
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 {});
+ let caller_address = signer::address_of(caller);
+ let secret_vault = Vault { secret: string::utf8(secret) };
+
+ // Check if vault already exists and handle accordingly
+ if (exists<Vault>(caller_address)) {
+ // Update existing vault
+ let vault_ref = borrow_global_mut<Vault>(caller_address);
+ vault_ref.secret = string::utf8(secret);
+ } else {
+ // Create new vault
+ move_to(caller, secret_vault);
+ };
+
+ event::emit(SetNewSecret {});
}
```
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.