T09 · Insecure Skill Coding Practices
- Location
src/policy.mjs:1- Finding
Unvalidated Vote Weights and Thresholds Can Manipulate Consensus Decisions
- Content
View full analysis
0) { yes/=total; no/=total; rewrite/=total; } const threshold = Number(policy.approve_threshold ?? 0.7); let final_decision = 'BLOCK'; const rationale = []; if (hard_block) { final_decision='BLOCK'; rationale.push('hard_block detected'); } else if (yes>=threshold) { final_decision='APPROVE'; rationale.push('approval threshold met'); } else if (rewrite>0) { final_decision='REWRITE'; rationale.push('fixable issues exist'); } else { final_decision='BLOCK'; rationale.push('insufficient approval'); } return { method: policy.method || 'WEIGHTED_APPROVAL_VOTE', weighted_yes:+yes.toFixed(6), weighted_no:+no.toFixed(6), weighted_rewrite:+rewrite.toFixed(6), hard_block, rationale, final_decision }; } ``` ### Technical Analysis `aggregateVotes()` converts caller-supplied values with `Number()` but does not verify that they are finite, non-negative, or within the expected range. The function also does not validate the vote array, supported vote values, duplicate persona votes, or the approval threshold. Consequently, inputs such as negative weights, `Infinity`, `NaN`, or thresholds outside the `[0,1]` interval can invalidate the aggregation model. A particularly direct bypass is an approval threshold below zero: with no affirmative voting weight, `yes` remains zero, and `0 >= -1` evaluates to true. The package's `rejectUnknown()` helper only rejects unknown property names and is not in ...[truncated 1378 chars]- Remediation
View remediation
1) { throw new RangeError('approve_threshold must be a finite number in [0,1]'); } for (const vote of votes) { if (!['YES', 'NO', 'REWRITE'].includes(vote.vote)) { throw new TypeError('unsupported vote value'); } const weight = Number(vote.reputation_before); if (!Number.isFinite(weight) || weight < 0 || weight > 1) { throw new RangeError('reputation_before must be a finite number in [0,1]'); } } ``` ]]>
