no-ldap-injection
Detects LDAP injection vulnerabilities
CWE: CWE-74
OWASP Mobile: OWASP Mobile Top 10
Detects LDAP injection vulnerabilities. This rule is part of eslint-plugin-secure-coding.
💼 This rule is set to error in the recommended config.
Quick Summary
| Aspect | Details |
|---|---|
| CWE Reference | CWE-90 (LDAP Injection) |
| Severity | Critical (CVSS 9.8) |
| Auto-Fix | 💡 Suggestions available |
| Category | Security |
Vulnerability and Risk
Vulnerability: LDAP Injection allows attackers to modify LDAP statements by supplying malicious input that is not properly sanitized or escaped.
Risk: Attackers can alter LDAP queries to bypass authentication (e.g., logging in as any user), leak sensitive directory information (like emails, phone numbers, or passwords), or in some cases, modify user attributes.
Rule Details
LDAP injection occurs when user input is improperly inserted into LDAP queries, allowing attackers to:
- Bypass authentication and authorization
- Extract sensitive directory information (users, groups, passwords)
- Perform unauthorized LDAP operations
- Enumerate users through blind injection techniques
Why This Matters
| Issue | Impact | Solution |
|---|---|---|
| 🔓 Auth Bypass | Unauthorized access | Escape LDAP filter values |
| 📤 Data Theft | Directory data exposure | Validate and sanitize input |
| 👥 Enumeration | User discovery | Use parameterized queries |
Examples
❌ Incorrect
// String interpolation in LDAP filter
const filter = `(uid=${username})`;
ldapClient.search('ou=users,dc=example,dc=com', { filter });
// String concatenation
const searchFilter = '(cn=' + userInput + ')';
// Template literal with untrusted input
const ldapFilter = `(&(objectClass=user)(mail=${email}))`;✅ Correct
const filter = `(uid=${ldap.escape.filterValue(userId)})`;What the rule judges
Nothing is examined unless the file loads an LDAP client (ldapPackages /
additionalLdapPackages). Within such a file:
- A built filter — a template literal, a
+concatenation (including a conditional choosing between two), or a binding written once with one — is reported (unsafeLdapFilter) when its static text is filter grammar ((attr=,(attr~=,(attr>=, …) and a part of it is neither a constant this file wrote nor passed through a configured escape or validation function. - Request data handed over whole —
req.query.f, a name bound to it, an{ filter: … }option carrying it (the key may befilter,'filter',['filter']or a constant bound to'filter'), or a helper given it in any argument, spread included — is reported (unescapedLdapInput). - A distinguished name built from a dynamic part, or taken from the
request, is reported (
ldapInjection); a DN concatenated from constants is not. - A hardcoded match-everything filter — one ending in a wildcard (
(uid=*)), the absolute-true(&),*|*,*&*— is reported (dangerousLdapOperation). The absolute-false(|)matches nothing and is not; a!inside an assertion value is a literal character; a number concatenated into the filter is part of its text. - A receiver that is provably not an LDAP client — every write to its
binding (or its class field's initialiser) is a collection, a literal, or a
call/
newnot made from an LDAP import — is skipped, soset.add(…)is not an LDAPadd. A receiver bound to an unknown name is not provably anything and stays in view.
Configuration
{
rules: {
'secure-coding/no-ldap-injection': ['error', {
ldapFunctions: ['search', 'bind', 'modify', 'add', 'delete'],
ldapEscapeFunctions: ['escape.filterValue', 'escape.dnValue'],
ldapValidationFunctions: ['validateLdapInput', 'sanitizeLdapFilter']
}]
}
}Options
| Option | Type | Default | Description |
|---|---|---|---|
ldapFunctions | string[] | ["search","searchAsync","searchPaginated","bind","modify","modifyDN","add","del","delete","compare"] | LDAP client methods treated as query sinks |
ldapEscapeFunctions | string[] | ["escape.filterValue","escape.dnValue","filterValue","dnValue","filterEscape","dnEscape"] | Function names that escape LDAP filter or DN values |
ldapValidationFunctions | string[] | ["validateLdapInput","sanitizeLdapFilter","cleanLdapValue","checkLdapFilter"] | Function names that count as LDAP input validation |
trustedSanitizers | string[] | [] | Additional function names to consider as LDAP sanitizers |
trustedAnnotations | string[] | [] | Additional JSDoc annotations to consider as safe markers |
strictMode | boolean | false | Disable all false positive detection (strict mode) |
ldapPackages | string[] | ["ldapjs","ldapts","ldapauth-fork","passport-ldapauth","ldap-authentication","ldap-escape","ldap-filter","activedirectory","activedirectory2","node-ldap"] | Package specifiers whose import opens the file gate. Nothing in a file is examined unless one of these is loaded. Replaces the built-in list. |
additionalLdapPackages | string[] | [] | Extra LDAP client packages, on top of ldapPackages — a house wrapper around ldapjs belongs here. |
requestRoots | string[] | ["req","request","ctx","httpRequest"] | Identifiers naming a framework request object, matched as the exact ROOT of a member chain — never as a substring of the printed expression. Replaces the built-in list. |
additionalRequestRoots | string[] | [] | Extra request-object root names, on top of requestRoots. |
Error Message Format
The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:
🔒 CWE-90 OWASP:A05 CVSS:9.8 | LDAP Injection detected | CRITICAL
Fix: Review and apply the recommended fix | https://owasp.org/Top10/A05_2021/Message Components
| Component | Purpose | Example |
|---|---|---|
| Risk Standards | Security benchmarks | CWE-90 OWASP:A05 CVSS:9.8 |
| Issue Description | Specific vulnerability | LDAP Injection detected |
| Severity & Compliance | Impact assessment | CRITICAL |
| Fix Instruction | Actionable remediation | Follow the remediation steps below |
| Technical Truth | Official reference | OWASP Top 10 |
Known False Negatives
The following patterns are not detected due to static analysis limitations:
Query from Variable
Why: Query strings from variables not traced.
// ❌ NOT DETECTED - Query from variable
const query = `SELECT * FROM users WHERE id = ${userId}`;
db.execute(query);Mitigation: Always use parameterized queries.
Custom Query Builders
Why: Custom ORM/query builders not recognized.
// ❌ NOT DETECTED - Custom builder
customQuery.where(userInput).execute();Mitigation: Review all query builder patterns.
Template Engines
Why: Template-based queries not analyzed.
// ❌ NOT DETECTED - Template
executeTemplate('query.sql', { userId });Mitigation: Validate all template variables.
Further Reading
- OWASP LDAP Injection - Attack documentation
- CWE-90 - Official CWE entry
- ldapjs Security - Safe LDAP filter construction
Related Rules
no-sql-injection- SQL injection preventionno-xpath-injection- XPath injection prevention
Use this rule
npm install -D oxlint-plugin-secure-coding{ "jsPlugins": ["oxlint-plugin-secure-coding"], "rules": { "secure-coding/no-ldap-injection": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-secure-coding'], rules: { 'secure-coding/no-ldap-injection': 'error', }, },});oxlint-plugin-secure-coding re-exports eslint-plugin-secure-coding/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.