Files
codeql/javascript/ql/lib/semmle/javascript/frameworks/LdapJS.qll

72 lines
2.3 KiB
Plaintext

/**
* Provides classes for working with [LDAPjs](https://www.npmjs.com/package/ldapjs)
*/
import javascript
/**
* A module providing sinks and sanitizers for LDAP injection.
*/
module LdapJS {
/** Gets a reference to the ldapjs library. */
API::Node ldapjs() { result = API::moduleImport("ldapjs") }
/** Gets an LDAPjs client. */
private API::Node ldapClient() { result = ldapjs().getMember("createClient").getReturn() }
/** A call to a LDAPjs Client API method. */
class ClientCall extends API::CallNode {
string methodName;
ClientCall() {
methodName = ["add", "bind", "compare", "del", "modify", "modifyDN", "search"] and
this = ldapClient().getMember(methodName).getACall()
}
/** Gets the name of the LDAPjs Client API method. */
string getMethodName() { result = methodName }
}
/** A reference to a LDAPjs client `search` options. */
class SearchOptions extends API::Node {
SearchOptions() {
exists(ClientCall call | call.getMethodName() = "search" and this = call.getParameter(1))
}
}
/** A creation of an LDAPjs filter, or object containing a filter, that doesn't sanitizes the input. */
abstract class TaintPreservingLdapFilterStep extends DataFlow::Node {
/** Gets the input that creates (part of) an LDAPjs filter. */
abstract DataFlow::Node getInput();
/** Gets the resulting LDAPjs filter. */
abstract DataFlow::Node getOutput();
}
/** A call to the LDAPjs utility method "parseFilter". */
private class ParseFilter extends TaintPreservingLdapFilterStep, API::CallNode {
ParseFilter() { this = ldapjs().getMember("parseFilter").getACall() }
override DataFlow::Node getInput() { result = this.getArgument(0) }
override DataFlow::Node getOutput() { result = this }
}
/**
* A filter used in call to "search" on an LDAPjs client.
* We model that as a step from the ".filter" write to the options object itself.
*/
private class SearchFilter extends TaintPreservingLdapFilterStep {
SearchOptions options;
SearchFilter() {
options = ldapClient().getMember("search").getACall().getParameter(1) and
this = options.asSink()
}
override DataFlow::Node getInput() { result = options.getMember("filter").asSink() }
override DataFlow::Node getOutput() { result = this }
}
}