Rust Fund

AI First Flight #9
Beginner FriendlyRust
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

[M-03] Contributions Accepted Indefinitely on Deadline-less Funds

[M-03] Contributions Accepted Indefinitely on Deadline-less Funds

Description

  • The contribute() function has a deadline check that blocks contributions after the deadline has passed. This is intended to close the campaign to new deposits once the deadline expires.

  • The same deadline != 0 && short-circuit means contributions are always accepted on funds with no deadline. Combined with H-03 (withdraw without guards), the creator can collect unlimited SOL and withdraw at any time.

@> if fund.deadline != 0 && fund.deadline < Clock::get().unwrap().unix_timestamp.try_into().unwrap() {
return Err(ErrorCode::DeadlineReached.into());
}

Risk

Likelihood: Medium

  • Any fund created without calling set_deadline() accepts contributions forever. There is no mechanism to close or finalize the campaign.

Impact: Medium

  • Contributors keep sending SOL to a fund with no end date. The creator can withdraw at any time (H-03), creating an unlimited extraction vector.

Severity: Medium

Proof of Concept

A fund is created without setting a deadline. Days, weeks, or months later, new contributions are still accepted with no expiry. Combined with H-03, the creator can withdraw at any time.

it("Proves contributions accepted forever on deadline-less fund", async () => {
await program.methods.fundCreate("poc-m003", "no deadline", goal).rpc();
// Note: set_deadline() is never called — deadline = 0
// Contribute now
await program.methods.contribute(new anchor.BN(1 * LAMPORTS_PER_SOL)).signers([c1]).rpc();
// Simulate time passing — contribute again, still works
await program.methods.contribute(new anchor.BN(1 * LAMPORTS_PER_SOL)).signers([c2]).rpc();
await program.methods.contribute(new anchor.BN(1 * LAMPORTS_PER_SOL)).signers([c3]).rpc();
// The deadline check: deadline != 0 && deadline < now
// When deadline = 0: false && (...) → short-circuits → guard never triggers
let fund = await program.account.fund.fetch(fundPDA);
expect(fund.amountRaised.toNumber()).to.equal(3 * LAMPORTS_PER_SOL);
expect(fund.deadline.toNumber()).to.equal(0); // still no deadline
// Creator can withdraw at any point (H-03) — unlimited collection vector
});
## Recommended Mitigation
Require a deadline to be set before accepting contributions. This ensures every fund has a finite campaign window, preventing the open-ended collection scenario. The restored invariant is: contributions are only accepted between fund creation and deadline expiry, and every fund must have an explicit end date.
```diff
+ require!(fund.deadline != 0, ErrorCode::DeadlineNotSet);
if fund.deadline < Clock::get().unwrap().unix_timestamp.try_into().unwrap() {
return Err(ErrorCode::DeadlineReached.into());
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 3 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!