CWE-793 is narrow and easy to picture. The product filters a single instance of a special element before sending data to a downstream component, and leaves any others untouched.
MITRE notes that this can be location-dependent, such as only the first or only the last element being handled.
How it happens
Many string functions act on one match. Python's str.replace replaces all by default, but re.sub with a count of one, str.replace with a count argument, Java's replaceFirst, and JavaScript's replace with a plain string pattern all stop after the first.
import re
name = re.sub(r"<", "", user_input, count=1) # removes only the first angle bracket
Input <<script leaves a bracket behind. Another common form is trimming one trailing character, then assuming the rest is clean.
What an attacker gains
The record lists integrity and an unexpected state, with no observed CVEs. Whatever the removed character protected against can come back through the remaining instances.
How to find it
- Search for first-match replace functions in input handling and output code.
- Test with the special element repeated, in front, in the middle and at the end.
- Ask a reviewer to read the filter with a hostile example in mind.
How to fix it
- Replace all occurrences, then verify none remain.
- Prefer rejecting input with unexpected characters to silently cleaning it.
- Use encoding that fits the destination, such as HTML escaping for pages and bound parameters for queries.
- Add a regression test with several instances of every special element.
if "<" in user_input:
raise ValueError("invalid input")
Quick check
Is it enough to run the filter twice?
Not reliably. An attacker can send more instances than your passes. Reject or encode instead.