An injection flaw is input that changes the meaning of a command, query or document sent to another component. MITRE's CWE-74 describes it as building a command or data structure from externally influenced input without neutralizing special elements. In review, look for text assembled from variables and handed to an interpreter.
What is the general pattern?
CWE-74 says the product constructs all or part of a command, data structure or record using input from an upstream component, but does not neutralize, or incorrectly neutralizes, special elements that could modify how it is parsed or interpreted when sent to a downstream component. Break that sentence into parts and you have a map for review.
- Upstream input. Something an outsider can influence: a form field, header, file name, message, or a record someone else wrote.
- A construction step. Your code joins that input with fixed text.
- A downstream interpreter. A database, shell, search service, directory server, template engine, or parser that gives special meaning to certain characters.
SQL and shell commands are the famous cases. The same shape appears wherever one component hands a string to another that reads it as instructions.
What does it look like in a constructed case?
Take a support tool with a ticket search. Under the hood it sends a filter expression to an internal search service that understands a small query language: status:open AND owner:alice. A developer builds it like this:
const filter = "status:open AND owner:" + req.query.owner;
const results = await searchService.query(filter);
The developer's intent: show open tickets for one owner. The query language treats AND and OR as operators and a leading minus as negation. Suppose a user supplies an owner value that includes OR status:closed with a space. The service now reads two conditions where the developer meant one, and the user sees tickets outside the intended scope. If the service also supports field names for restricted data, the same trick might reach those.
No quote character was involved, and no database. The special elements were whatever the search service treats as syntax. That is why checklists that only look for the word SELECT miss real bugs.
How do you fix it?
The OWASP Injection Prevention Cheat Sheet gives the order of preference. Use structured mechanisms that keep data and command separate, such as parameterized interfaces, where they exist. Where they do not, validate against an allowlist of what is permitted. For contexts that need escaping, such as LDAP, the cheat sheet notes that distinct rules apply to distinguished names and search filters (defined by RFC 4514 and RFC 4515) and says to avoid custom escaping code and use a library encoder for the correct context.
For the search filter, that gives:
- Prefer the search client's structured query builder, where conditions are objects and values are separate fields.
- If you must build text, accept the owner value only if it matches a strict pattern such as a username shape, and reject everything else rather than stripping characters.
- Check on the server which fields a user may filter by, and do not let the filter text name fields.
What do you check in a code review?
Read the diff as a data flow, not as a list of dangerous function names.
- Find every place a string is passed to something that parses it: query clients, shell and process calls, template renderers, directory lookups, XML and expression evaluators, log pipelines.
- Trace each argument back. Does any part come from the request, a file, a queue, or another user's record?
- Check whether the value is bound as a parameter, or concatenated, or formatted into the text.
- Ask what the downstream syntax is, and whether the validation matches that syntax. A filter that blocks quotes does nothing for a language that uses spaces and keywords.
- Look for second order paths, where data was stored earlier and used later without checking.
What to do
- Inventory the interpreters your application talks to, not just the database.
- For each one, find the structured or parameterized interface and use it.
- Where none exists, use strict allowlists and fail closed.
- Use library encoders for context-specific escaping.
- Add tests that send syntax characters and keywords for that interpreter and assert the scope does not change.
- Add a review checklist line: no string built from request data passed to a parser.
Safeguard's code scanning can follow data flows from input to sinks as one input to this review, but the interpreter inventory in step 1 needs someone who knows your system.
Quick check
Is injection only a database problem?
No. Any component that parses text as instructions can be affected, including shells, search services, directory servers and template engines.
Is stripping bad characters a fix?
It is fragile. Prefer separating data from the command, or an allowlist that rejects anything outside what is expected.
Sources
- MITRE CWE-74: Improper Neutralization of Special Elements in Output Used by a Downstream Component
- OWASP Injection Prevention Cheat Sheet
Last reviewed: 18 October 2026.