Skip to content

Commit 54fba2d

Browse files
authored
Merge pull request #6781 from erik-krogh/ldap
JS: Move LDAP injection out of experimental
2 parents 7d0152f + 7a96b8e commit 54fba2d

File tree

17 files changed

+251
-353
lines changed

17 files changed

+251
-353
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
lgtm,codescanning
2+
* The `js/sql-injection` now recognizes the `ldapjs` library as a sink.
3+
Affected packages are
4+
[ldapjs](https://www.npmjs.com/package/ldapjs)

javascript/ql/lib/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ import semmle.javascript.frameworks.History
9999
import semmle.javascript.frameworks.Immutable
100100
import semmle.javascript.frameworks.Knex
101101
import semmle.javascript.frameworks.LazyCache
102+
import semmle.javascript.frameworks.LdapJS
102103
import semmle.javascript.frameworks.LodashUnderscore
103104
import semmle.javascript.frameworks.Logging
104105
import semmle.javascript.frameworks.HttpFrameworks
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* Provides classes for working with [LDAPjs](https://www.npmjs.com/package/ldapjs)
3+
*/
4+
5+
import javascript
6+
7+
/**
8+
* A module providing sinks and sanitizers for LDAP injection.
9+
*/
10+
module LdapJS {
11+
/** Gets a reference to the ldapjs library. */
12+
API::Node ldapjs() { result = API::moduleImport("ldapjs") }
13+
14+
/** Gets an LDAPjs client. */
15+
private API::Node ldapClient() { result = ldapjs().getMember("createClient").getReturn() }
16+
17+
/** A call to a LDAPjs Client API method. */
18+
class ClientCall extends API::CallNode {
19+
string methodName;
20+
21+
ClientCall() {
22+
methodName = ["add", "bind", "compare", "del", "modify", "modifyDN", "search"] and
23+
this = ldapClient().getMember(methodName).getACall()
24+
}
25+
26+
/** Gets the name of the LDAPjs Client API method. */
27+
string getMethodName() { result = methodName }
28+
}
29+
30+
/** A reference to a LDAPjs client `search` options. */
31+
class SearchOptions extends API::Node {
32+
ClientCall call;
33+
34+
SearchOptions() { call.getMethodName() = "search" and this = call.getParameter(1) }
35+
}
36+
37+
/** A creation of an LDAPjs filter, or object containing a filter, that doesn't sanitizes the input. */
38+
abstract class TaintPreservingLdapFilterStep extends DataFlow::Node {
39+
/** The input that creates (part of) an LDAPjs filter. */
40+
abstract DataFlow::Node getInput();
41+
42+
/** The resulting LDAPjs filter. */
43+
abstract DataFlow::Node getOutput();
44+
}
45+
46+
/** A call to the LDAPjs utility method "parseFilter". */
47+
private class ParseFilter extends TaintPreservingLdapFilterStep, API::CallNode {
48+
ParseFilter() { this = ldapjs().getMember("parseFilter").getACall() }
49+
50+
override DataFlow::Node getInput() { result = this.getArgument(0) }
51+
52+
override DataFlow::Node getOutput() { result = this }
53+
}
54+
55+
/**
56+
* A filter used in call to "search" on an LDAPjs client.
57+
* We model that as a step from the ".filter" write to the options object itself.
58+
*/
59+
private class SearchFilter extends TaintPreservingLdapFilterStep {
60+
SearchOptions options;
61+
62+
SearchFilter() {
63+
options = ldapClient().getMember("search").getACall().getParameter(1) and
64+
this = options.getARhs()
65+
}
66+
67+
override DataFlow::Node getInput() { result = options.getMember("filter").getARhs() }
68+
69+
override DataFlow::Node getOutput() { result = this }
70+
}
71+
}

javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionCustomizations.qll

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,35 @@ module SqlInjection {
4141
class GraphqlInjectionSink extends Sink {
4242
GraphqlInjectionSink() { this instanceof GraphQL::GraphQLString }
4343
}
44+
45+
/**
46+
* An LDAPjs sink.
47+
*/
48+
class LdapJSSink extends Sink {
49+
LdapJSSink() {
50+
// A distinguished name (DN) used in a call to the client API.
51+
this = any(LdapJS::ClientCall call).getArgument(0)
52+
or
53+
// A search options object, which contains a filter and a baseDN.
54+
this = any(LdapJS::SearchOptions opt).getARhs()
55+
or
56+
// A call to "parseDN", which parses a DN from a string.
57+
this = LdapJS::ldapjs().getMember("parseDN").getACall().getArgument(0)
58+
}
59+
}
60+
61+
import semmle.javascript.security.IncompleteBlacklistSanitizer as IncompleteBlacklistSanitizer
62+
63+
/**
64+
* A chain of replace calls that replaces all unsafe chars for ldap injection.
65+
* For simplicity it's used as a sanitizer for all of `js/sql-injection`.
66+
*/
67+
class LdapStringSanitizer extends Sanitizer,
68+
IncompleteBlacklistSanitizer::StringReplaceCallSequence {
69+
LdapStringSanitizer() {
70+
forall(string char | char = ["*", "(", ")", "\\", "/"] |
71+
this.getAMember().getAReplacedString() = char
72+
)
73+
}
74+
}
4475
}

javascript/ql/lib/semmle/javascript/security/dataflow/SqlInjectionQuery.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,11 @@ class Configuration extends TaintTracking::Configuration {
2424
super.isSanitizer(node) or
2525
node instanceof Sanitizer
2626
}
27+
28+
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
29+
exists(LdapJS::TaintPreservingLdapFilterStep filter |
30+
pred = filter.getInput() and
31+
succ = filter.getOutput()
32+
)
33+
}
2734
}

javascript/ql/src/Security/CWE-089/SqlInjection.ql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
* @id js/sql-injection
1010
* @tags security
1111
* external/cwe/cwe-089
12+
* external/cwe/cwe-090
13+
* external/cwe/cwe-943
1214
*/
1315

1416
import javascript

javascript/ql/src/experimental/Security/CWE-090/LdapInjection.qhelp

Lines changed: 0 additions & 50 deletions
This file was deleted.

javascript/ql/src/experimental/Security/CWE-090/LdapInjection.ql

Lines changed: 0 additions & 20 deletions
This file was deleted.

javascript/ql/src/experimental/Security/CWE-090/LdapInjection.qll

Lines changed: 0 additions & 25 deletions
This file was deleted.

javascript/ql/src/experimental/Security/CWE-090/LdapInjectionCustomizations.qll

Lines changed: 0 additions & 69 deletions
This file was deleted.

0 commit comments

Comments
 (0)