Merge branch 'main' into ldap

This commit is contained in:
Erik Krogh Kristensen
2021-10-27 19:06:06 +02:00
1825 changed files with 195123 additions and 20595 deletions

View File

@@ -71,13 +71,7 @@ predicate isCompatibleRequestedService(InjectableFunctionServiceRequest request,
isRunMethod(request) or
isControllerFunction(request)
) and
(
kind = "value" or
kind = "service" or
kind = "factory" or
kind = "constant" or
kind = "provider-value"
)
kind = ["value", "service", "factory", "constant", "provider-value"]
or
isControllerFunction(request) and
kind = "controller-only"

View File

@@ -10,7 +10,7 @@
import javascript
import semmle.javascript.dataflow.LocalObjects
import UnusedVariable
import Declarations.UnusedVariable
import UnusedParameter
import Expressions.ExprHasNoEffect

View File

@@ -10,7 +10,7 @@
*/
import javascript
import UnusedVariable
import Declarations.UnusedVariable
/**
* Holds if `v` is mentioned in a JSDoc comment in the same file, and that file

View File

@@ -1,25 +0,0 @@
/**
* Provides classes and predicates for the 'js/unused-local-variable' query.
*/
import javascript
import LanguageFeatures.UnusedIndexVariable
/**
* A local variable that is neither used nor exported, and is not a parameter
* or a function name.
*/
class UnusedLocal extends LocalVariable {
UnusedLocal() {
not exists(getAnAccess()) and
not exists(Parameter p | this = p.getAVariable()) and
not exists(FunctionExpr fe | this = fe.getVariable()) and
not exists(ClassExpr ce | this = ce.getVariable()) and
not exists(ExportDeclaration ed | ed.exportsAs(this, _)) and
not exists(LocalVarTypeAccess type | type.getVariable() = this) and
// avoid double reporting
not unusedIndexVariable(_, this, _) and
// common convention: variables with leading underscore are intentionally unused
getName().charAt(0) != "_"
}
}

View File

@@ -1,18 +0,0 @@
/**
* Provides predicates for working with the DOM type hierarchy.
*/
import semmle.javascript.Externs
/** Holds if `et` is a root interface of the DOM type hierarchy. */
predicate isDOMRootType(ExternalType et) {
exists(string n | n = et.getName() | n = "EventTarget" or n = "StyleSheet")
}
/** Holds if `p` is declared as a property of a DOM class or interface. */
pragma[nomagic]
predicate isDOMProperty(string p) {
exists(ExternalMemberDecl emd | emd.getName() = p |
isDOMRootType(emd.getDeclaringType().getASupertype*())
)
}

View File

@@ -13,7 +13,7 @@
*/
import javascript
import ExprHasNoEffect
import Expressions.ExprHasNoEffect
import semmle.javascript.RestrictedLocations
from Expr e

View File

@@ -1,170 +0,0 @@
/**
* Provides classes and predicates for the 'js/useless-expression' query.
*/
import javascript
import DOMProperties
import semmle.javascript.frameworks.xUnit
/**
* Holds if `e` appears in a syntactic context where its value is discarded.
*/
predicate inVoidContext(Expr e) {
exists(ExprStmt parent |
// e is a toplevel expression in an expression statement
parent = e.getParent() and
// but it isn't an HTML attribute or a configuration object
not exists(TopLevel tl | tl = parent.getParent() |
tl instanceof CodeInAttribute
or
// if the toplevel in its entirety is of the form `({ ... })`,
// it is probably a configuration object (e.g., a require.js build configuration)
tl.getNumChildStmt() = 1 and e.stripParens() instanceof ObjectExpr
)
)
or
exists(SeqExpr seq, int i, int n |
e = seq.getOperand(i) and
n = seq.getNumOperands()
|
i < n - 1 or inVoidContext(seq)
)
or
exists(ForStmt stmt | e = stmt.getUpdate())
or
exists(ForStmt stmt | e = stmt.getInit() |
// Allow the pattern `for(i; i < 10; i++)`
not e instanceof VarAccess
)
or
exists(LogicalBinaryExpr logical | e = logical.getRightOperand() and inVoidContext(logical))
}
/**
* Holds if `e` is of the form `x;` or `e.p;` and has a JSDoc comment containing a tag.
* In that case, it is probably meant as a declaration and shouldn't be flagged by this query.
*
* This will still flag cases where the JSDoc comment contains no tag at all (and hence carries
* no semantic information), and expression statements with an ordinary (non-JSDoc) comment
* attached to them.
*/
predicate isDeclaration(Expr e) {
(e instanceof VarAccess or e instanceof PropAccess) and
exists(e.getParent().(ExprStmt).getDocumentation().getATag())
}
/**
* Holds if there exists a getter for a property called `name` anywhere in the program.
*/
predicate isGetterProperty(string name) {
// there is a call of the form `Object.defineProperty(..., name, descriptor)` ...
exists(CallToObjectDefineProperty defProp | name = defProp.getPropertyName() |
// ... where `descriptor` defines a getter
defProp.hasPropertyAttributeWrite("get", _)
or
// ... where `descriptor` may define a getter
exists(DataFlow::SourceNode descriptor | descriptor.flowsTo(defProp.getPropertyDescriptor()) |
descriptor.isIncomplete(_)
or
// minimal escape analysis for the descriptor
exists(DataFlow::InvokeNode invk |
not invk = defProp and
descriptor.flowsTo(invk.getAnArgument())
)
)
)
or
// there is an object expression with a getter property `name`
exists(ObjectExpr obj | obj.getPropertyByName(name) instanceof PropertyGetter)
}
/**
* A property access that may invoke a getter.
*/
class GetterPropertyAccess extends PropAccess {
override predicate isImpure() { isGetterProperty(getPropertyName()) }
}
/**
* Holds if `c` is an indirect eval call of the form `(dummy, eval)(...)`, where
* `dummy` is some expression whose value is discarded, and which simply
* exists to prevent the call from being interpreted as a direct eval.
*/
predicate isIndirectEval(CallExpr c, Expr dummy) {
exists(SeqExpr seq | seq = c.getCallee().stripParens() |
dummy = seq.getOperand(0) and
seq.getOperand(1).(GlobalVarAccess).getName() = "eval" and
seq.getNumOperands() = 2
)
}
/**
* Holds if `c` is a call of the form `(dummy, e[p])(...)`, where `dummy` is
* some expression whose value is discarded, and which simply exists
* to prevent the call from being interpreted as a method call.
*/
predicate isReceiverSuppressingCall(CallExpr c, Expr dummy, PropAccess callee) {
exists(SeqExpr seq | seq = c.getCallee().stripParens() |
dummy = seq.getOperand(0) and
seq.getOperand(1) = callee and
seq.getNumOperands() = 2
)
}
/**
* Holds if evaluating `e` has no side effects (except potentially allocating
* and initializing a new object).
*
* For calls, we do not check whether their arguments have any side effects:
* even if they do, the call itself is useless and should be flagged by this
* query.
*/
predicate noSideEffects(Expr e) {
e.isPure()
or
// `new Error(...)`, `new SyntaxError(...)`, etc.
forex(Function f | f = e.flow().(DataFlow::NewNode).getACallee() |
f.(ExternalType).getASupertype*().getName() = "Error"
)
}
/**
* Holds if the expression `e` should be reported as having no effect.
*/
predicate hasNoEffect(Expr e) {
noSideEffects(e) and
inVoidContext(e) and
// disregard pure expressions wrapped in a void(...)
not e instanceof VoidExpr and
// filter out directives (unknown directives are handled by UnknownDirective.ql)
not exists(Directive d | e = d.getExpr()) and
// or about externs
not e.inExternsFile() and
// don't complain about declarations
not isDeclaration(e) and
// exclude DOM properties, which sometimes have magical auto-update properties
not isDOMProperty(e.(PropAccess).getPropertyName()) and
// exclude xUnit.js annotations
not e instanceof XUnitAnnotation and
// exclude common patterns that are most likely intentional
not isIndirectEval(_, e) and
not isReceiverSuppressingCall(_, e, _) and
// exclude anonymous function expressions as statements; these can only arise
// from a syntax error we already flag
not exists(FunctionExpr fe, ExprStmt es | fe = e |
fe = es.getExpr() and
not exists(fe.getName())
) and
// exclude block-level flow type annotations. For example: `(name: empty)`.
not e.(ParExpr).getExpression().getLastToken().getNextToken().getValue() = ":" and
// exclude the first statement of a try block
not e = any(TryStmt stmt).getBody().getStmt(0).(ExprStmt).getExpr() and
// exclude expressions that are alone in a file, and file doesn't contain a function.
not exists(TopLevel top |
top = e.getParent().(ExprStmt).getParent() and
top.getNumChild() = 1 and
not exists(Function fun | fun.getEnclosingContainer() = top)
) and
// ignore Angular templates
not e.getTopLevel() instanceof Angular2::TemplateTopLevel
}

View File

@@ -12,7 +12,7 @@
*/
import Clones
import DOMProperties
import Expressions.DOMProperties
/**
* Gets a description of expression `e`, which is assumed to be the left-hand

View File

@@ -10,7 +10,7 @@
*/
import javascript
import UnusedIndexVariable
import LanguageFeatures.UnusedIndexVariable
from RelationalComparison rel, Variable idx, Variable v
where unusedIndexVariable(rel, idx, v)

View File

@@ -1,40 +0,0 @@
/**
* Provides a predicate for identifying unused index variables in loops.
*/
import javascript
/**
* Holds if `arr` is of the form `base[idx]` and is nested inside loop `fs`.
*/
private predicate arrayIndexInLoop(IndexExpr arr, Variable base, Expr idx, ForStmt fs) {
arr.getEnclosingStmt().getParentStmt*() = fs.getBody() and
arr.getBase() = base.getAnAccess() and
arr.getIndex() = idx
}
/**
* Gets `e` or a sub-expression `s` resulting from `e` by peeling off unary and binary
* operators, increments, decrements, type assertions, parentheses, sequence expressions,
* and assignments.
*/
private Expr unwrap(Expr e) {
result = e or
result = unwrap(e.(UpdateExpr).getOperand()) or
result = unwrap(e.(UnaryExpr).getOperand()) or
result = unwrap(e.(BinaryExpr).getAnOperand()) or
result = unwrap(e.getUnderlyingValue())
}
/**
* Holds if `rel` is a for-loop condition of the form `idx <= v.length`, but all array
* indices `v[c]` inside the loop body (of which there must be at least one) use a constant
* index `c` instead of an index based on `idx`.
*/
predicate unusedIndexVariable(RelationalComparison rel, Variable idx, Variable v) {
exists(ForStmt fs | fs.getTest() = rel |
unwrap(rel.getLesserOperand()) = idx.getAnAccess() and
rel.getGreaterOperand().(PropAccess).accesses(v.getAnAccess(), "length") and
forex(IndexExpr arr, Expr e | arrayIndexInLoop(arr, v, e, fs) | exists(e.getIntValue()))
)
}

View File

@@ -76,13 +76,11 @@ class StateUpdateVolatileMethod extends Function {
// - componentsWillMount
// - componentsDidMount
exists(ReactComponent c |
methodName = "componentDidUnmount" or
methodName = "componentDidUpdate" or
methodName = "componentWillUpdate" or
methodName = "getDefaultProps" or
methodName = "getInitialState" or
methodName = "render" or
methodName = "shouldComponentUpdate"
methodName =
[
"componentDidUnmount", "componentDidUpdate", "componentWillUpdate", "getDefaultProps",
"getInitialState", "render", "shouldComponentUpdate"
]
|
this = c.getInstanceMethod(methodName)
)

View File

@@ -5,7 +5,7 @@
* command-line injection vulnerabilities.
* @kind path-problem
* @problem.severity warning
* @security-severity 9.8
* @security-severity 6.3
* @precision medium
* @id js/indirect-command-line-injection
* @tags correctness

View File

@@ -4,7 +4,7 @@
* environment may cause subtle bugs or vulnerabilities.
* @kind path-problem
* @problem.severity warning
* @security-severity 9.8
* @security-severity 6.3
* @precision high
* @id js/shell-command-injection-from-environment
* @tags correctness

View File

@@ -4,7 +4,7 @@
* user to change the meaning of the command.
* @kind path-problem
* @problem.severity error
* @security-severity 9.8
* @security-severity 6.3
* @precision high
* @id js/shell-command-constructed-from-input
* @tags correctness

View File

@@ -3,7 +3,7 @@
* @description Using the `cat` process to read a file is unnecessarily complex, inefficient, unportable, and can lead to subtle bugs, or even security vulnerabilities.
* @kind problem
* @problem.severity error
* @security-severity 9.8
* @security-severity 6.3
* @precision high
* @id js/unnecessary-use-of-cat
* @tags correctness

View File

@@ -4,11 +4,12 @@
* code execution.
* @kind path-problem
* @problem.severity error
* @security-severity 6.1
* @security-severity 9.3
* @precision high
* @id js/code-injection
* @tags security
* external/cwe/cwe-094
* external/cwe/cwe-095
* external/cwe/cwe-079
* external/cwe/cwe-116
*/

View File

@@ -79,14 +79,11 @@ predicate allBackslashesEscaped(DataFlow::Node nd) {
or
// flow through string methods
exists(DataFlow::MethodCallNode mc, string m |
m = "replace" or
m = "replaceAll" or
m = "slice" or
m = "substr" or
m = "substring" or
m = "toLowerCase" or
m = "toUpperCase" or
m = "trim"
m =
[
"replace", "replaceAll", "slice", "substr", "substring", "toLowerCase", "toUpperCase",
"trim"
]
|
mc = nd and m = mc.getMethodName() and allBackslashesEscaped(mc.getReceiver())
)

View File

@@ -3,7 +3,7 @@
* @description Using external input in format strings can lead to garbled output.
* @kind path-problem
* @problem.severity warning
* @security-severity 9.3
* @security-severity 7.3
* @precision high
* @id js/tainted-format-string
* @tags security

View File

@@ -104,7 +104,7 @@ class AsyncSentinelCall extends DataFlow::CallNode {
exists(DataFlow::FunctionNode node | node.getAstNode() = asyncCallee |
// manual models
exists(string memberName |
not "Sync" = memberName.suffix(memberName.length() - 4) and
not memberName.matches("%Sync") and
this = NodeJSLib::FS::moduleMember(memberName).getACall() and
node = this.getCallback([1 .. 2])
)

View File

@@ -4,10 +4,11 @@
* property can cause indefinite looping.
* @kind path-problem
* @problem.severity warning
* @security-severity 6.5
* @security-severity 7.5
* @id js/loop-bound-injection
* @tags security
* external/cwe/cwe-834
* external/cwe/cwe-730
* @precision high
*/

View File

@@ -3,7 +3,7 @@
* @description Writing network data directly to the file system allows arbitrary file upload and might indicate a backdoor.
* @kind path-problem
* @problem.severity warning
* @security-severity 9.8
* @security-severity 6.3
* @precision medium
* @id js/http-to-file-access
* @tags security

View File

@@ -111,16 +111,11 @@ predicate callToVoidFunction(DataFlow::CallNode call, Function func) {
* and the callback is expected to return a value.
*/
predicate hasNonVoidCallbackMethod(string name) {
name = "every" or
name = "filter" or
name = "find" or
name = "findIndex" or
name = "flatMap" or
name = "map" or
name = "reduce" or
name = "reduceRight" or
name = "some" or
name = "sort"
name =
[
"every", "filter", "find", "findIndex", "flatMap", "map", "reduce", "reduceRight", "some",
"sort"
]
}
DataFlow::SourceNode array(DataFlow::TypeTracker t) {

View File

@@ -55,7 +55,7 @@
import javascript
private import DataFlow
private import filters.ClassifyFiles
private import semmle.javascript.filters.ClassifyFiles
private import semmle.javascript.RestrictedLocations
/**

View File

@@ -8,7 +8,7 @@
*/
import javascript
import ClassifyFiles
import semmle.javascript.filters.ClassifyFiles
from File f, string category
where classify(f, category)

View File

@@ -1,87 +0,0 @@
/**
* Provides classes and predicates for classifying files as containing
* generated code, test code, externs declarations, library code or
* template code.
*/
import semmle.javascript.GeneratedCode
import semmle.javascript.frameworks.Testing
import semmle.javascript.frameworks.Templating
import semmle.javascript.dependencies.FrameworkLibraries
/**
* Holds if `e` may be caused by parsing a template file as plain HTML or JavaScript.
*
* We use two heuristics: check for the presence of a known template delimiter preceding
* the error on the same line, and check whether the file name contains `template` or
* `templates`.
*/
predicate maybeCausedByTemplate(JSParseError e) {
exists(File f | f = e.getFile() |
e.getLine().indexOf(Templating::getADelimiter()) <= e.getLocation().getStartColumn()
or
f.getAbsolutePath().regexpMatch("(?i).*\\btemplates?\\b.*")
)
}
/**
* Holds if `e` is an expression in the form `o.p1.p2.p3....pn`.
*/
private predicate isNestedDotExpr(DotExpr e) {
e.getBase() instanceof VarAccess or
isNestedDotExpr(e.getBase())
}
/**
* Holds if `tl` only contains variable declarations and field reads.
*/
private predicate looksLikeExterns(TopLevel tl) {
forex(Stmt s | s.getParent() = tl |
exists(VarDeclStmt vds | vds = s |
forall(VariableDeclarator vd | vd = vds.getADecl() | not exists(vd.getInit()))
)
or
isNestedDotExpr(s.(ExprStmt).getExpr())
)
}
/**
* Holds if `f` is classified as belonging to `category`.
*
* There are currently four categories:
* - `"generated"`: `f` contains generated or minified code;
* - `"test"`: `f` contains test code;
* - `"externs"`: `f` contains externs declarations;
* - `"library"`: `f` contains library code;
* - `"template"`: `f` contains template code.
*/
predicate classify(File f, string category) {
isGenerated(f.getATopLevel()) and category = "generated"
or
(
exists(Test t | t.getFile() = f)
or
exists(string stemExt | stemExt = "test" or stemExt = "spec" |
f = getTestFile(any(File orig), stemExt)
)
or
f.getAbsolutePath().regexpMatch(".*/__(mocks|tests)__/.*")
) and
category = "test"
or
(f.getATopLevel().isExterns() or looksLikeExterns(f.getATopLevel())) and
category = "externs"
or
f.getATopLevel() instanceof FrameworkLibraryInstance and category = "library"
or
exists(JSParseError err | maybeCausedByTemplate(err) |
f = err.getFile() and category = "template"
)
or
// Polymer templates
exists(HTML::Element elt | elt.getName() = "template" |
f = elt.getFile() and
category = "template" and
not f.getExtension() = "vue"
)
}

View File

@@ -37,22 +37,12 @@ predicate exprWithoutEnclosingStmt(Expr e) {
* `"3 results for toString()"`.
*/
predicate uniqueness_error(int number, string what, string problem) {
(
what = "toString" or
what = "getLocation" or
what = "getTopLevel" or
what = "getEnclosingStmt" or
what = "getContainer" or
what = "getEnclosingContainer" or
what = "getEntry" or
what = "getExit" or
what = "getFirstControlFlowNode" or
what = "getOuterScope" or
what = "getScopeElement" or
what = "getBaseName" or
what = "getOperator" or
what = "getTest"
) and
what =
[
"toString", "getLocation", "getTopLevel", "getEnclosingStmt", "getContainer",
"getEnclosingContainer", "getEntry", "getExit", "getFirstControlFlowNode", "getOuterScope",
"getScopeElement", "getBaseName", "getOperator", "getTest"
] and
(
number = 0 and problem = "no results for " + what + "()"
or

View File

@@ -2,6 +2,7 @@ name: codeql/javascript-queries
version: 0.0.3
suites: codeql-suites
extractor: javascript
defaultSuiteFile: codeql-suites/javascript-code-scanning.qls
dependencies:
codeql/javascript-all: "*"
codeql/suite-helpers: "*"