VNX-20: Input validation performed but its result ignored or bypassed
Input validation is wired up but never decides anything: an unread Spring BindingResult, discarded Bean Validation violations, express-validator chains with no validationResult(), WTForms data read without validate(), pydantic construct() on request data, and discarded gin bind or validator errors.
| Field | Value |
|---|---|
| Rule ID | VNX-20 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | Go, Java, JavaScript / TypeScript, Python |
| CWE | CWE-20 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:M/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C |
| Tags | input-validation cwe-20 |
| Source | internal/sast/rules/vnx-20.rego |
Overview
VNX-20 maps to CWE-20: Improper Input Validation. It reports the concrete, recognisable form of the weakness: the project has declared its validation rules, but the code that should act on the result never does, so invalid input is processed as if it had passed.
Why This Matters
Validation annotations, schemas and validator chains look like protection in review. When their result is dropped, nothing is rejected: a negative quantity, an oversized string or a missing required field reaches the business logic and the database. Every later control that assumed validated input is then wrong.
What Gets Flagged
// BindingResult declared, never read before the next method
public String register(@Valid @ModelAttribute("user") UserForm form, BindingResult result, Model model) {
validator.validate(order); // Bean Validation violations discarded
// no validationResult(req) anywhere in the project
router.post('/register', body('email').isEmail().normalizeEmail(), handler);
form = TransferForm(request.form) # form.amount.data read, no validate()
order = Order.model_construct(**payload) # payload = await request.json()
c.ShouldBindJSON(&req)
_ = validate.Struct(q)
Not flagged: a BindingResult that the method reads (hasErrors(), or passed to a helper), Spring’s validator.validate(target, errors) which records into errors, violations that are assigned or chained, a validationResult call anywhere in the project’s JavaScript (shared middleware), a form validated in the same function, construct() on data that was validated first or did not come from the request, a checked bind error, and test code. Request data reaching a dangerous sink with no validation at all is reported by the injection and traversal rules.
Remediation
- Spring: check
result.hasErrors()first and return the form view. - Bean Validation: assign the returned set and reject the input when it is not empty.
- express-validator: add a middleware that returns 400/422 when
validationResult(req)is not empty, and mount it after the chains. - WTForms: use the data only inside
if form.validate_on_submit():. - pydantic: build models with
Model(**data)orModel.model_validate(data); keepmodel_constructfor trusted data. - gin/validator: check the returned error and stop with 400.
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-20
// nosec vnx-20
Or record an ignore rule: vulnetix ignore add --rule vnx-20 --reason "...". See Suppressing findings.