VNX-1039: reCAPTCHA v3 verdict accepted without checking the score
Reports Python and JavaScript/TypeScript handlers in reCAPTCHA v3 projects that accept the siteverify response on success alone, and score checks against 0, so the bot detector never rejects a bot.
| Field | Value |
|---|---|
| Rule ID | VNX-1039 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | JavaScript / TypeScript, Python |
| CWE | CWE-1039 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | captcha recaptcha bot-detection cwe-1039 |
| Source | internal/sast/rules/vnx-1039.rego |
Overview
VNX-1039 maps to CWE-1039: Automated Recognition Mechanism with Inadequate Detection or Handling of Adversarial Input Perturbations. reCAPTCHA v3 is an automated recognition mechanism: siteverify answers success: true for every well-formed token, bots included, and the bot/human judgement is only in the score (0.0 to 1.0). The rule reports:
- in a project that uses reCAPTCHA v3 (
grecaptcha.execute(...),api.js?render=<site key>, react-google-recaptcha-v3, aRECAPTCHA_V3name), a file that handles the siteverify response and readssuccessbut never mentions the score; - a score compared against 0 (
score > 0,score >= 0.0), which accepts every request.
Not reported: reCAPTCHA v2, hCaptcha and Turnstile projects (no score; success is the verdict), handlers that read the score, comments and test files. CWE-1039 in general (machine-learning classifiers that adversarial input can fool) has no static signature; this rule covers the concrete case of a bot-detection score being ignored.
Why This Matters
A v3 integration that checks only success blocks nothing: an automated client that obtains any token passes, so sign-up, login and contact forms are open to credential stuffing and spam while the team believes they are protected.
What Gets Flagged
result = r.json()
if not result["success"]:
abort(403)
const { success } = await resp.json();
return data.get("success") and data.get("score", 0) >= 0.0
Remediation
- Reject requests whose
scoreis below a threshold suited to the action (Google suggests starting at 0.5). - Check that
actionmatches the action the page executed, and thathostnameis yours.
References
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-1039
// nosec vnx-1039
Or record an ignore rule: vulnetix ignore add --rule vnx-1039 --reason "...". See Suppressing findings.