Merge branch 'main' into extractBigReg

This commit is contained in:
Erik Krogh Kristensen
2021-11-03 13:08:51 +01:00
313 changed files with 7234 additions and 3217 deletions

View File

@@ -63,29 +63,25 @@ module DOM {
/**
* An HTML element, viewed as an `ElementDefinition`.
*/
private class HtmlElementDefinition extends ElementDefinition, @xmlelement {
HtmlElementDefinition() { this instanceof HTML::Element }
override string getName() { result = this.(HTML::Element).getName() }
private class HtmlElementDefinition extends ElementDefinition, @xmlelement instanceof HTML::Element {
override string getName() { result = HTML::Element.super.getName() }
override AttributeDefinition getAttribute(int i) {
result = this.(HTML::Element).getAttribute(i)
result = HTML::Element.super.getAttribute(i)
}
override ElementDefinition getParent() { result = this.(HTML::Element).getParent() }
override ElementDefinition getParent() { result = HTML::Element.super.getParent() }
}
/**
* A JSX element, viewed as an `ElementDefinition`.
*/
private class JsxElementDefinition extends ElementDefinition, @jsx_element {
JsxElementDefinition() { this instanceof JSXElement }
private class JsxElementDefinition extends ElementDefinition, @jsx_element instanceof JSXElement {
override string getName() { result = JSXElement.super.getName() }
override string getName() { result = this.(JSXElement).getName() }
override AttributeDefinition getAttribute(int i) { result = JSXElement.super.getAttribute(i) }
override AttributeDefinition getAttribute(int i) { result = this.(JSXElement).getAttribute(i) }
override ElementDefinition getParent() { result = this.(JSXElement).getJsxParent() }
override ElementDefinition getParent() { result = super.getJsxParent() }
}
/**
@@ -131,14 +127,12 @@ module DOM {
/**
* An HTML attribute, viewed as an `AttributeDefinition`.
*/
private class HtmlAttributeDefinition extends AttributeDefinition, @xmlattribute {
HtmlAttributeDefinition() { this instanceof HTML::Attribute }
private class HtmlAttributeDefinition extends AttributeDefinition, @xmlattribute instanceof HTML::Attribute {
override string getName() { result = HTML::Attribute.super.getName() }
override string getName() { result = this.(HTML::Attribute).getName() }
override string getStringValue() { result = super.getValue() }
override string getStringValue() { result = this.(HTML::Attribute).getValue() }
override ElementDefinition getElement() { result = this.(HTML::Attribute).getElement() }
override ElementDefinition getElement() { result = HTML::Attribute.super.getElement() }
}
/**

View File

@@ -658,7 +658,7 @@ abstract class ReExportDeclaration extends ExportDeclaration {
cached
Module getReExportedModule() {
Stages::Imports::ref() and
result.getFile() = getEnclosingModule().resolve(getImportedPath().(PathExpr))
result.getFile() = getEnclosingModule().resolve(getImportedPath())
or
result = resolveFromTypeRoot()
}

View File

@@ -699,7 +699,7 @@ module PrintHTML {
childIndex = -1 and result.(HTMLAttributesNodes).getElement() = element
or
exists(HTML::Element child | result.(HTMLElementNode).getElement() = child |
child = element.(HTML::Element).getChild(childIndex)
child = element.getChild(childIndex)
)
}
}

View File

@@ -61,17 +61,15 @@ class ParameterNode extends DataFlow::SourceNode {
* new Array(16)
* ```
*/
class InvokeNode extends DataFlow::SourceNode {
InvokeNode() { this instanceof DataFlow::Impl::InvokeNodeDef }
class InvokeNode extends DataFlow::SourceNode instanceof DataFlow::Impl::InvokeNodeDef {
/** Gets the syntactic invoke expression underlying this function invocation. */
InvokeExpr getInvokeExpr() { result = this.(DataFlow::Impl::InvokeNodeDef).getInvokeExpr() }
InvokeExpr getInvokeExpr() { result = super.getInvokeExpr() }
/** Gets the name of the function or method being invoked, if it can be determined. */
string getCalleeName() { result = this.(DataFlow::Impl::InvokeNodeDef).getCalleeName() }
string getCalleeName() { result = super.getCalleeName() }
/** Gets the data flow node specifying the function to be called. */
DataFlow::Node getCalleeNode() { result = this.(DataFlow::Impl::InvokeNodeDef).getCalleeNode() }
DataFlow::Node getCalleeNode() { result = super.getCalleeNode() }
/**
* Gets the data flow node corresponding to the `i`th argument of this invocation.
@@ -92,10 +90,10 @@ class InvokeNode extends DataFlow::SourceNode {
* but the position of `z` cannot be determined, hence there are no first and second
* argument nodes.
*/
DataFlow::Node getArgument(int i) { result = this.(DataFlow::Impl::InvokeNodeDef).getArgument(i) }
DataFlow::Node getArgument(int i) { result = super.getArgument(i) }
/** Gets the data flow node corresponding to an argument of this invocation. */
DataFlow::Node getAnArgument() { result = this.(DataFlow::Impl::InvokeNodeDef).getAnArgument() }
DataFlow::Node getAnArgument() { result = super.getAnArgument() }
/** Gets the data flow node corresponding to the last argument of this invocation. */
DataFlow::Node getLastArgument() { result = getArgument(getNumArgument() - 1) }
@@ -112,12 +110,10 @@ class InvokeNode extends DataFlow::SourceNode {
* ```
* .
*/
DataFlow::Node getASpreadArgument() {
result = this.(DataFlow::Impl::InvokeNodeDef).getASpreadArgument()
}
DataFlow::Node getASpreadArgument() { result = super.getASpreadArgument() }
/** Gets the number of arguments of this invocation, if it can be determined. */
int getNumArgument() { result = this.(DataFlow::Impl::InvokeNodeDef).getNumArgument() }
int getNumArgument() { result = super.getNumArgument() }
Function getEnclosingFunction() { result = getBasicBlock().getContainer() }
@@ -258,15 +254,13 @@ class InvokeNode extends DataFlow::SourceNode {
* Math.abs(x)
* ```
*/
class CallNode extends InvokeNode {
CallNode() { this instanceof DataFlow::Impl::CallNodeDef }
class CallNode extends InvokeNode instanceof DataFlow::Impl::CallNodeDef {
/**
* Gets the data flow node corresponding to the receiver expression of this method call.
*
* For example, the receiver of `x.m()` is `x`.
*/
DataFlow::Node getReceiver() { result = this.(DataFlow::Impl::CallNodeDef).getReceiver() }
DataFlow::Node getReceiver() { result = super.getReceiver() }
}
/**
@@ -279,11 +273,9 @@ class CallNode extends InvokeNode {
* Math.abs(x)
* ```
*/
class MethodCallNode extends CallNode {
MethodCallNode() { this instanceof DataFlow::Impl::MethodCallNodeDef }
class MethodCallNode extends CallNode instanceof DataFlow::Impl::MethodCallNodeDef {
/** Gets the name of the invoked method, if it can be determined. */
string getMethodName() { result = this.(DataFlow::Impl::MethodCallNodeDef).getMethodName() }
string getMethodName() { result = super.getMethodName() }
/**
* Holds if this data flow node calls method `methodName` on receiver node `receiver`.

View File

@@ -53,21 +53,18 @@ abstract class RefinementCandidate extends Expr {
* A refinement candidate that references at most one variable, and hence
* can be used to refine the abstract values inferred for that variable.
*/
class Refinement extends Expr {
Refinement() {
this instanceof RefinementCandidate and
count(this.(RefinementCandidate).getARefinedVar()) <= 1
}
class Refinement extends Expr instanceof RefinementCandidate {
Refinement() { count(this.(RefinementCandidate).getARefinedVar()) <= 1 }
/**
* Gets the variable refined by this expression, if any.
*/
SsaSourceVariable getRefinedVar() { result = this.(RefinementCandidate).getARefinedVar() }
SsaSourceVariable getRefinedVar() { result = super.getARefinedVar() }
/**
* Gets a refinement value inferred for this expression in context `ctxt`.
*/
RefinementValue eval(RefinementContext ctxt) { result = this.(RefinementCandidate).eval(ctxt) }
RefinementValue eval(RefinementContext ctxt) { result = super.eval(ctxt) }
}
/** A literal, viewed as a refinement expression. */

View File

@@ -8,7 +8,7 @@ pragma[nomagic]
predicate isAnalyzedParameter(Parameter p) {
exists(FunctionWithAnalyzedParameters f, int parmIdx | p = f.getParameter(parmIdx) |
// we cannot track flow into rest parameters
not p.(Parameter).isRestParameter()
not p.isRestParameter()
)
}

View File

@@ -199,7 +199,7 @@ private class AnalyzedNewExpr extends DataFlow::AnalyzedValueNode {
*/
private predicate isIndefinite() {
exists(DataFlow::AnalyzedNode callee, AbstractValue calleeVal |
callee = astNode.(NewExpr).getCallee().analyze() and
callee = astNode.getCallee().analyze() and
calleeVal = callee.getALocalValue()
|
calleeVal.isIndefinite(_) or
@@ -217,7 +217,7 @@ private class NewInstance extends DataFlow::AnalyzedValueNode {
override AbstractValue getALocalValue() {
exists(DataFlow::AnalyzedNode callee |
callee = astNode.(NewExpr).getCallee().analyze() and
callee = astNode.getCallee().analyze() and
result = TAbstractInstance(callee.getALocalValue())
)
}

View File

@@ -4,6 +4,97 @@
import javascript
/**
* Classes and predicates for reasoning about writes to cookies.
*/
module CookieWrites {
/**
* A write to a cookie.
*/
abstract class CookieWrite extends DataFlow::Node {
/**
* Holds if this cookie is secure, i.e. only transmitted over SSL.
*/
abstract predicate isSecure();
/**
* Holds if this cookie is HttpOnly, i.e. not accessible by JavaScript.
*/
abstract predicate isHttpOnly();
/**
* Holds if the cookie likely is an authentication cookie or otherwise sensitive.
*/
abstract predicate isSensitive();
/**
* Holds if the cookie write happens on a server, i.e. the `httpOnly` flag is relevant.
*/
predicate isServerSide() {
any() // holds by default. Client-side cookie writes should extend ClientSideCookieWrite.
}
}
/**
* A client-side write to a cookie.
*/
abstract class ClientSideCookieWrite extends CookieWrite {
final override predicate isHttpOnly() { none() }
final override predicate isServerSide() { none() }
}
/**
* The flag that indicates that a cookie is secure.
*/
string secure() { result = "secure" }
/**
* The flag that indicates that a cookie is HttpOnly.
*/
string httpOnly() { result = "httpOnly" }
}
/**
* Holds if `node` looks like it can contain a sensitive cookie.
*
* Heuristics:
* - `node` contains a string value that looks like a sensitive cookie name
* - `node` is a sensitive expression
*/
private predicate canHaveSensitiveCookie(DataFlow::Node node) {
exists(string s |
node.mayHaveStringValue(s) or
s = node.(StringOps::ConcatenationRoot).getConstantStringParts()
|
HeuristicNames::nameIndicatesSensitiveData([s, getCookieName(s)], _)
)
or
node.asExpr() instanceof SensitiveExpr
}
/**
* Gets the cookie name of a `Set-Cookie` header value.
* The header value always starts with `<cookie-name>=<cookie-value>` optionally followed by attributes:
* `<cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly`
*/
bindingset[s]
private string getCookieName(string s) { result = s.regexpCapture("([^=]*)=.*", 1).trim() }
/**
* Holds if the `Set-Cookie` header value contains the specified attribute
* 1. The attribute is case insensitive
* 2. It always starts with a pair `<cookie-name>=<cookie-value>`.
* If the attribute is present there must be `;` after the pair.
* Other attributes like `Domain=`, `Path=`, etc. may come after the pair:
* `<cookie-name>=<cookie-value>; Domain=<domain-value>; Secure; HttpOnly`
* See `https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie`
*/
bindingset[s, attribute]
private predicate hasCookieAttribute(string s, string attribute) {
s.regexpMatch("(?i).*;\\s*" + attribute + "\\b\\s*;?.*$")
}
/**
* A model of the `js-cookie` library (https://github.com/js-cookie/js-cookie).
*/
@@ -25,12 +116,22 @@ private module JsCookie {
}
}
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode,
CookieWrites::ClientSideCookieWrite {
WriteAccess() { this = libMemberCall("set") }
string getKey() { getArgument(0).mayHaveStringValue(result) }
override DataFlow::Node getValue() { result = getArgument(1) }
override predicate isSecure() {
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::secure()) |
not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
)
}
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
}
}
@@ -53,12 +154,25 @@ private module BrowserCookies {
}
}
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode,
CookieWrites::ClientSideCookieWrite {
WriteAccess() { this = libMemberCall("set") }
string getKey() { getArgument(0).mayHaveStringValue(result) }
override DataFlow::Node getValue() { result = getArgument(1) }
override predicate isSecure() {
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::secure()) |
not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
)
or
// or, an explicit default has been set
exists(DataFlow::moduleMember("browser-cookies", "defaults").getAPropertyWrite("secure"))
}
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
}
}
@@ -81,11 +195,174 @@ private module LibCookie {
override PersistentWriteAccess getAWrite() { key = result.(WriteAccess).getKey() }
}
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode,
CookieWrites::ClientSideCookieWrite {
WriteAccess() { this = libMemberCall("serialize") }
string getKey() { getArgument(0).mayHaveStringValue(result) }
override DataFlow::Node getValue() { result = getArgument(1) }
override predicate isSecure() {
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::secure()) |
not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
)
}
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
}
}
/**
* A model of cookies in an express application.
*/
private module ExpressCookies {
/**
* A cookie set using `response.cookie` from `express` module (https://expressjs.com/en/api.html#res.cookie).
*/
private class InsecureExpressCookieResponse extends CookieWrites::CookieWrite,
DataFlow::MethodCallNode {
InsecureExpressCookieResponse() { this.asExpr() instanceof Express::SetCookie }
override predicate isSecure() {
// A cookie is secure if there are cookie options with the `secure` flag set to `true`.
// The default is `false`.
exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::secure()) |
not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
)
}
override predicate isSensitive() { canHaveSensitiveCookie(this.getArgument(0)) }
override predicate isHttpOnly() {
// A cookie is httpOnly if there are cookie options with the `httpOnly` flag set to `true`.
// The default is `false`.
exists(DataFlow::Node value | value = this.getOptionArgument(2, CookieWrites::httpOnly()) |
not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
)
}
}
/**
* A cookie set using the `express` module `cookie-session` (https://github.com/expressjs/cookie-session).
*/
class InsecureCookieSession extends ExpressLibraries::CookieSession::MiddlewareInstance,
CookieWrites::CookieWrite {
private DataFlow::Node getCookieFlagValue(string flag) {
result = this.getOptionArgument(0, flag)
}
override predicate isSecure() {
// The flag `secure` is set to `false` by default for HTTP, `true` by default for HTTPS (https://github.com/expressjs/cookie-session#cookie-options).
// A cookie is secure if the `secure` flag is not explicitly set to `false`.
not getCookieFlagValue(CookieWrites::secure()).mayHaveBooleanValue(false)
}
override predicate isSensitive() {
any() // It is a session cookie, likely auth sensitive
}
override predicate isHttpOnly() {
// The flag `httpOnly` is set to `true` by default (https://github.com/expressjs/cookie-session#cookie-options).
// A cookie is httpOnly if the `httpOnly` flag is not explicitly set to `false`.
not getCookieFlagValue(CookieWrites::httpOnly()).mayHaveBooleanValue(false)
}
}
/**
* A cookie set using the `express` module `express-session` (https://github.com/expressjs/session).
*/
class InsecureExpressSessionCookie extends ExpressLibraries::ExpressSession::MiddlewareInstance,
CookieWrites::CookieWrite {
private DataFlow::Node getCookieFlagValue(string flag) {
result = this.getOption("cookie").getALocalSource().getAPropertyWrite(flag).getRhs()
}
override predicate isSecure() {
// The flag `secure` is not set by default (https://github.com/expressjs/session#Cookiesecure).
// The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
exists(DataFlow::Node value | value = getCookieFlagValue(CookieWrites::secure()) |
not value.mayHaveBooleanValue(false) // anything but `false` is accepted as being maybe true
)
}
override predicate isSensitive() {
any() // It is a session cookie, likely auth sensitive
}
override predicate isHttpOnly() {
// The flag `httpOnly` is set by default (https://github.com/expressjs/session#Cookiesecure).
// The default value for cookie options is { path: '/', httpOnly: true, secure: false, maxAge: null }.
// A cookie is httpOnly if the `httpOnly` flag is not explicitly set to `false`.
not getCookieFlagValue(CookieWrites::httpOnly()).mayHaveBooleanValue(false)
}
}
}
/**
* A cookie set using `Set-Cookie` header of an `HTTP` response, where a raw header is used.
* (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie).
* This class does not model the Express implementation of `HTTP::CookieDefintion`
* as the express implementation does not use raw headers.
*
* In case an array is passed `setHeader("Set-Cookie", [...]` it sets multiple cookies.
* We model a `CookieWrite` for each array element.
*/
private class HTTPCookieWrite extends CookieWrites::CookieWrite {
string header;
HTTPCookieWrite() {
exists(HTTP::CookieDefinition setCookie |
this.asExpr() = setCookie.getHeaderArgument() and
not this instanceof DataFlow::ArrayCreationNode
or
this = setCookie.getHeaderArgument().flow().(DataFlow::ArrayCreationNode).getAnElement()
) and
header =
[
any(string s | this.mayHaveStringValue(s)),
this.(StringOps::ConcatenationRoot).getConstantStringParts()
]
}
override predicate isSecure() {
// A cookie is secure if the `secure` flag is specified in the cookie definition.
// The default is `false`.
hasCookieAttribute(header, CookieWrites::secure())
}
override predicate isHttpOnly() {
// A cookie is httpOnly if the `httpOnly` flag is specified in the cookie definition.
// The default is `false`.
hasCookieAttribute(header, CookieWrites::httpOnly())
}
override predicate isSensitive() { canHaveSensitiveCookie(this) }
}
/**
* A write to `document.cookie`.
*/
private class DocumentCookieWrite extends CookieWrites::ClientSideCookieWrite {
string cookie;
DataFlow::PropWrite write;
DocumentCookieWrite() {
this = write and
write = DOM::documentRef().getAPropertyWrite("cookie") and
cookie =
[
any(string s | write.getRhs().mayHaveStringValue(s)),
write.getRhs().(StringOps::ConcatenationRoot).getConstantStringParts()
]
}
override predicate isSecure() {
// A cookie is secure if the `secure` flag is specified in the cookie definition.
// The default is `false`.
hasCookieAttribute(cookie, CookieWrites::secure())
}
override predicate isSensitive() { canHaveSensitiveCookie(write.getRhs()) }
}

View File

@@ -0,0 +1,71 @@
/**
* 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 {
ClientCall call;
SearchOptions() { 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 {
/** The input that creates (part of) an LDAPjs filter. */
abstract DataFlow::Node getInput();
/** 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.getARhs()
}
override DataFlow::Node getInput() { result = options.getMember("filter").getARhs() }
override DataFlow::Node getOutput() { result = this }
}
}

View File

@@ -139,7 +139,7 @@ private class JQueryDomElementDefinition extends DOM::ElementDefinition, @call_e
JQueryDomElementDefinition() {
this = call and
call = jquery().getACall().asExpr() and
exists(string s | s = call.getArgument(0).(Expr).getStringValue() |
exists(string s | s = call.getArgument(0).getStringValue() |
// match an opening angle bracket followed by a tag name, followed by arbitrary
// text and a closing angle bracket, potentially with whitespace in between
tagName = s.regexpCapture("\\s*<\\s*(\\w+)\\b[^>]*>\\s*", 1).toLowerCase()

View File

@@ -14,11 +14,14 @@ import semmle.javascript.security.internal.SensitiveDataHeuristics
private import HeuristicNames
/** An expression that might contain sensitive data. */
cached
abstract class SensitiveExpr extends Expr {
/** Gets a human-readable description of this expression for use in alert messages. */
cached
abstract string describe();
/** Gets a classification of the kind of sensitive data this expression might contain. */
cached
abstract SensitiveDataClassification getClassification();
}

View File

@@ -317,7 +317,7 @@ module PrettyPrintCatCall {
*/
string createFileThatIsReadFromCommandList(CommandCall call) {
exists(DataFlow::ArrayCreationNode array, DataFlow::Node element |
array = call.getArgumentList().(DataFlow::ArrayCreationNode) and
array = call.getArgumentList() and
array.getSize() = 1 and
element = array.getElement(0)
|

View File

@@ -41,4 +41,35 @@ module SqlInjection {
class GraphqlInjectionSink extends Sink {
GraphqlInjectionSink() { this instanceof GraphQL::GraphQLString }
}
/**
* An LDAPjs sink.
*/
class LdapJSSink extends Sink {
LdapJSSink() {
// A distinguished name (DN) used in a call to the client API.
this = any(LdapJS::ClientCall call).getArgument(0)
or
// A search options object, which contains a filter and a baseDN.
this = any(LdapJS::SearchOptions opt).getARhs()
or
// A call to "parseDN", which parses a DN from a string.
this = LdapJS::ldapjs().getMember("parseDN").getACall().getArgument(0)
}
}
import semmle.javascript.security.IncompleteBlacklistSanitizer as IncompleteBlacklistSanitizer
/**
* A chain of replace calls that replaces all unsafe chars for ldap injection.
* For simplicity it's used as a sanitizer for all of `js/sql-injection`.
*/
class LdapStringSanitizer extends Sanitizer,
IncompleteBlacklistSanitizer::StringReplaceCallSequence {
LdapStringSanitizer() {
forall(string char | char = ["*", "(", ")", "\\", "/"] |
this.getAMember().getAReplacedString() = char
)
}
}
}

View File

@@ -24,4 +24,11 @@ class Configuration extends TaintTracking::Configuration {
super.isSanitizer(node) or
node instanceof Sanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(LdapJS::TaintPreservingLdapFilterStep filter |
pred = filter.getInput() and
succ = filter.getOutput()
)
}
}

View File

@@ -526,9 +526,7 @@ module ReflectedXss {
* ```
*/
predicate isLocalHeaderDefinition(HTTP::HeaderDefinition header) {
exists(ReachableBasicBlock headerBlock |
headerBlock = header.getBasicBlock().(ReachableBasicBlock)
|
exists(ReachableBasicBlock headerBlock | headerBlock = header.getBasicBlock() |
1 =
strictcount(HTTP::ResponseSendArgument sender |
sender.getRouteHandler() = header.getRouteHandler() and

View File

@@ -58,7 +58,7 @@ module HeuristicNames {
*/
string maybeAccountInfo() {
result = "(?is).*acc(ou)?nt.*" or
result = "(?is).*(puid|username|userid).*" or
result = "(?is).*(puid|username|userid|session(id|key)).*" or
result = "(?s).*([uU]|^|_|[a-z](?=U))([uU][iI][dD]).*"
}

View File

@@ -47,10 +47,8 @@ module PolynomialReDoS {
* A remote input to a server, seen as a source for polynomial
* regular expression denial-of-service vulnerabilities.
*/
class RequestInputAccessAsSource extends Source {
RequestInputAccessAsSource() { this instanceof HTTP::RequestInputAccess }
override string getKind() { result = this.(HTTP::RequestInputAccess).getKind() }
class RequestInputAccessAsSource extends Source instanceof HTTP::RequestInputAccess {
override string getKind() { result = HTTP::RequestInputAccess.super.getKind() }
}
/**