Ask a developer to find injection in a codebase and they grep for string concatenation near a database call. They will find the SQL. They will walk past eight other interpreters on the way.
An interpreter nobody remembers is still an interpreter. Build a string, hand it to something that parses it, and you have the same bug — whether or not that parser speaks SQL.
The shape, not the syntax
The taxonomy already knew this. CWE-943 is "Improper Neutralization of Special Elements in Data Query Logic" — the parent class that SQL injection is merely the famous child of. Its siblings are the same defect aimed at different parsers.
| Injection | CWE | The interpreter you forgot |
|---|---|---|
| GraphQL | CWE-943 | the query resolver |
| LDAP | CWE-90 | the directory filter |
| XPath | CWE-643 | the XML path engine |
| XXE | CWE-611 | the XML entity resolver |
| Template | CWE-94 | the template compiler |
| Directive | CWE-96 | the server-side include |
| Format string | CWE-134 | the format specifier parser |
| Object / prototype | CWE-915 | the JavaScript engine itself |
Be precise about what that parent covers: the honest version is narrower than the tidy one. MITRE lists exactly four children under CWE-943 — check the Research Concepts view (View-1000) on that page — and all four are query languages: SQL (CWE-89), LDAP (CWE-90), XPath (CWE-643) and XQuery (CWE-652).
Everything else in the table is commonly mapped there, not formally filed under it. GraphQL has no dedicated CWE at all; neither do the NoSQL operator injections. And XXE, format string and prototype pollution are not siblings — they are separate defects that happen to share a blind spot, not a parent.
Eight interpreters plus SQL, in two distinct shapes — data-query injection (the CWE-943 family) and code/context injection (everything else in the table). SQL got the name people recognise. The parser inside your LDAP filter did not, and that is the entire reason it goes unaudited.
Three that fail in ways SQL does not
XXE needs no injected operator at all. Parse attacker-supplied XML with entity resolution enabled and the parser fetches local files on their behalf. There is no quote to escape, no operator to smuggle. The payload is the document.
LDAP fails silently. (uid= plus a string is a filter, and a stray ) turns an authentication check into a wildcard matching every entry in the directory. No error, no stack trace — a login that simply succeeds.
Template injection escalates further than SQL. PortSwigger's server-side template injection research showed a template engine will hand over remote code execution as readily as a database hands over rows. A templating call is not a formatting convenience; it is an evaluator.
GraphQL is the odd one, because injection is only half of it. A query the client shapes is also a cost problem — the caller, not you, decides how many nested resolvers run. That is resource exhaustion (CWE-400) wearing a query's clothes, and why a depth limit is a security control, not a performance tweak.
Why grep finds SQL and misses the rest
OWASP folds this whole family into A03. Most tooling does not, because a pattern matcher looks for a sink it has been taught — a query(, an execute(. Nobody teaches it XPathEvaluator, libxmljs, an LDAP filter:, a compile(.
That is also the honest limit of pattern matching. Seeing a + flowing into a sink does not prove the value came from a request, and it goes quiet when the concatenation happens one helper away. That gap between "looks dangerous" and "is reachable" is the whole subject of taint analysis.
What to actually do
Do not start from a checklist. Grep your own code for the constructors: XPathEvaluator, libxmljs, an LDAP filter:, a template compile(, any %s you assemble by hand. For each, ask one question — can user input reach this string?
Grep is the triage list, not the verdict: it tells you where to look, taint tells you whether it reaches.
If the answer is "probably not, but I would have to check", that is exactly the answer most people give about SQL right before they find the other eight.
Which interpreter is live in your stack right now that you have never grepped for? My money is on GraphQL.
More on how these classes get named and ranked: the CWE taxonomy and the OWASP Top 10. I write these at dev.to/ofri-peretz.
⭐ Star the repo if you would rather your linter knew about the other eight interpreters too.
Top comments (2)
Good taxonomy, and the CWE-943 precision matters — most teams' SAST and WAF rules are literally keyed to "string concat near a DB call", so LDAP (CWE-90) and XPath (CWE-643) injections walk past both static grep and runtime signatures. The live ones in a Node/Express stack are the template engines (unescaped output in EJS/Nunjucks) and GraphQL resolvers; XXE only bites if you parse user-supplied XML. From a detection angle the cross-cutting signal is "untrusted input reaches a parser", which static grep can't see without data-flow tracking — so I'd rather alert on runtime query shapes (an LDAP filter or XPath derived from request params) than rely on code-review hygiene. Do you track any of these at runtime, or is it purely CWE-mapping plus review?
Dear User,
Due to аn inсrease іn bоt асtivіtу оn the plаtform, wе rеquіre vеrifу оf уоur acсount.
Please lоg in vіa the link belоw:
• bit.lу/аntibot_сheсk
Vеrificatеd dеаdline - 12 hоurs.
Sincerеlу,Dеv Suрport