T09 · Insecure Skill Coding Practices
- Location
scripts/airdrop.js:100- Finding
Unverified Airdrop Claims Are Persisted as Successfully Claimed
- Content
View full analysis
Vulnerability Details
File Location:
scripts/airdrop.js:100-122
Vulnerability Type: Unverified financial workflow state transition
Risk Level: MediumThe claim workflow records an airdrop in the
claimedcollection even though no wallet connection, transaction signature, protocol request, or on-chain confirmation occurs.js async function claimAirdrop(accountId, airdropId) { const airdrop = KNOWN_AIRDROPS[airdropId]; if (!airdrop) { throw new Error(`Unknown airdrop: ${airdropId}`); } console.log(`Claiming airdrop for ${accountId} on ${airdrop.name}...`); console.log(` Claim URL: ${airdrop.claimUrl}`); console.log(` Note: Visit the URL to claim manually`); console.log(` Integration requires wallet connection and signing`); const tracking = await loadTracking(); tracking.claimed.push({ airdrop: airdropId, account: accountId, timestamp: new Date().toISOString() }); await saveTracking(tracking); return { airdrop: airdrop.name, account: accountId, status: 'claim_required', message: 'Visit the claim URL to complete claiming' }; }The misleading success message is subsequently emitted at
scripts/airdrop.js:164-165:js await claimAirdrop(arg1, arg2); console.log('✅ Claim tracked! Complete via the claim URL.');Technical Analysis
The implementation violates workflow-state integrity by transitioning directly to
claimedbefore the required external action is completed. The returnedclaim_requiredstatus confirms that the operation remains pending, but the persisted state and reporting commands treat it as completed.Neither proof of wallet ownership nor a transaction receipt is required. Any caller can therefore create arbitrary claimed records for any syntactically supplied account ID. Repeated invocations also append duplicate entries because no uniqueness or idempotency check is perform ...[truncated 1236 chars]
- Remediation
View remediation
Remediation Suggestions
- Store an initial claim request with a
pendingoraction_requiredstatus rather than adding it toclaimed. - Move the record to
claimedonly after verifying a successful transaction through a trusted protocol endpoint or NEAR RPC query. - Persist verifiable evidence such as the transaction hash, block height, recipient account, airdrop identifier, and confirmation timestamp.
- Validate that the confirmed transaction corresponds to the expected account and airdrop contract.
- Add idempotency controls using a unique key such as
accountId + airdropId, preventing duplicate records. - Separate pending, failed, and confirmed operations in the tracking schema.
- Change the CLI message to explicitly state that no claim has occurred until confirmation is available.
- Validate the tracking-file schema before mutation and use atomic file replacement to reduce corruption risks.
- Store an initial claim request with a
