mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge master into next.
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
* same id attribute, it may be interpreted differently
|
||||
* by different browsers.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @problem.severity warning
|
||||
* @id js/duplicate-html-id
|
||||
* @tags maintainability
|
||||
* correctness
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description If an HTML element has two attributes with the same name
|
||||
* but different values, its behavior may be browser-dependent.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @problem.severity warning
|
||||
* @id js/conflicting-html-attribute
|
||||
* @tags maintainability
|
||||
* correctness
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.frameworks.Templating
|
||||
import semmle.javascript.RestrictedLocations
|
||||
|
||||
/**
|
||||
* Holds if the href attribute contains a host that we cannot determine statically.
|
||||
@@ -53,4 +54,4 @@ where // `e` is a link that opens in a new browsing context (that is, it has `ta
|
||||
not exists (DOM::AttributeDefinition attr | attr = e.getAnAttribute() |
|
||||
not exists(attr.getName())
|
||||
)
|
||||
select e, "External links without noopener/noreferrer are a potential security risk."
|
||||
select (FirstLineOf)e, "External links without noopener/noreferrer are a potential security risk."
|
||||
|
||||
@@ -50,5 +50,14 @@ where maybeMissingThis(call, intendedTarget, gv)
|
||||
decl.isNamespaceExport() and
|
||||
call.getContainer().getEnclosingContainer*() instanceof NamespaceDeclaration
|
||||
)
|
||||
or
|
||||
// call to global function with additional arguments
|
||||
exists (Function self |
|
||||
intendedTarget.getBody() = self and
|
||||
call.getEnclosingFunction() = self and
|
||||
call.flow().(DataFlow::CallNode).getNumArgument() > self.getNumParameter() and
|
||||
not self.hasRestParameter() and
|
||||
not self.usesArgumentsObject()
|
||||
)
|
||||
)
|
||||
select call, "This call refers to a global function, and not the local method $@.", intendedTarget, intendedTarget.getName()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description If a variable is not declared as a local variable, it becomes a global variable
|
||||
* by default, which may be unintentional and could lead to unexpected behavior.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @problem.severity warning
|
||||
* @id js/missing-variable-declaration
|
||||
* @tags reliability
|
||||
* maintainability
|
||||
|
||||
@@ -10,15 +10,19 @@
|
||||
*/
|
||||
import javascript
|
||||
|
||||
/** Holds if `base` declares or inherits method `m` with the given `name`. */
|
||||
predicate hasMethod(ClassDefinition base, string name, MethodDefinition m) {
|
||||
m = base.getMethod(name) or
|
||||
hasMethod(base.getSuperClassDefinition(), name, m)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `access` is in`fromMethod`, and it references `toMethod` through `this`.
|
||||
*/
|
||||
predicate isLocalMethodAccess(PropAccess access, MethodDefinition fromMethod, MethodDefinition toMethod) {
|
||||
fromMethod.getDeclaringClass() = toMethod.getDeclaringClass() and
|
||||
hasMethod(fromMethod.getDeclaringClass(), access.getPropertyName(), toMethod) and
|
||||
access.getEnclosingFunction() = fromMethod.getBody() and
|
||||
access.getBase() instanceof ThisExpr and
|
||||
access.getPropertyName() = toMethod.getName()
|
||||
access.getBase() instanceof ThisExpr
|
||||
}
|
||||
|
||||
string getKind(MethodDefinition m) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description If two conditions in an 'if'-'else if' chain are identical, the
|
||||
* second condition will never hold.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @problem.severity warning
|
||||
* @id js/duplicate-condition
|
||||
* @tags maintainability
|
||||
* correctness
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description If two cases in a switch statement have the same label, the second case
|
||||
* will never be executed.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @problem.severity warning
|
||||
* @id js/duplicate-switch-case
|
||||
* @tags maintainability
|
||||
* correctness
|
||||
|
||||
@@ -216,6 +216,13 @@ class ClassDefinition extends @classdefinition, ClassOrInterface, AST::ValueNode
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the definition of the super class of this class, if it can be determined.
|
||||
*/
|
||||
ClassDefinition getSuperClassDefinition() {
|
||||
result = getSuperClass().analyze().getAValue().(AbstractClass).getClass()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -224,6 +224,12 @@ class NgInjectDirective extends KnownDirective {
|
||||
NgInjectDirective() { getDirectiveText().regexpMatch("ng(No)?Inject") }
|
||||
}
|
||||
|
||||
/** A YUI compressor directive. */
|
||||
class YuiDirective extends KnownDirective {
|
||||
YuiDirective() {
|
||||
getDirectiveText().regexpMatch("([a-z0-9_]+:nomunge, ?)*([a-z0-9_]+:nomunge)")
|
||||
}
|
||||
}
|
||||
|
||||
/** A SystemJS `deps` directive. */
|
||||
class SystemJSDepsDirective extends KnownDirective {
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Provides classes for working with analysis-specific abstract values.
|
||||
*
|
||||
* Implement a subclass of `CustomAbstractValueDefinition` when the builtin
|
||||
* abstract values of `AbstractValues.qll` are not expressive enough.
|
||||
*
|
||||
* For performance reasons, all subclasses of `CustomAbstractValueDefinition`
|
||||
* should be part of the standard library.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
private import internal.AbstractValuesImpl
|
||||
private import InferredTypes
|
||||
|
||||
/**
|
||||
* An abstract representation of an analysis-specific value.
|
||||
*
|
||||
* Wraps a `CustomAbstractValueDefinition`.
|
||||
*/
|
||||
class CustomAbstractValueFromDefinition extends AbstractValue, TCustomAbstractValueFromDefinition {
|
||||
|
||||
CustomAbstractValueDefinition def;
|
||||
|
||||
CustomAbstractValueFromDefinition() {
|
||||
this = TCustomAbstractValueFromDefinition(def)
|
||||
}
|
||||
|
||||
override InferredType getType() {
|
||||
result = def.getType()
|
||||
}
|
||||
|
||||
override boolean getBooleanValue() {
|
||||
result = def.getBooleanValue()
|
||||
}
|
||||
|
||||
override PrimitiveAbstractValue toPrimitive() {
|
||||
result = def.toPrimitive()
|
||||
}
|
||||
|
||||
override predicate isCoercibleToNumber() {
|
||||
def.isCoercibleToNumber()
|
||||
}
|
||||
|
||||
override predicate isIndefinite(DataFlow::Incompleteness cause) {
|
||||
def.isIndefinite(cause)
|
||||
}
|
||||
|
||||
override DefiniteAbstractValue getAPrototype() {
|
||||
result = def.getAPrototype()
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(string f, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
def.getLocation().hasLocationInfo(f, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = def.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the definition that induces this value.
|
||||
*/
|
||||
CustomAbstractValueDefinition getDefinition() {
|
||||
result = def
|
||||
}
|
||||
|
||||
/** Holds if this is a value whose properties the type inference tracks. */
|
||||
predicate shouldTrackProperties() {
|
||||
def.shouldTrackProperties()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that induces an analysis-specific abstract value.
|
||||
*
|
||||
* Enables modular extensions of `AbstractValue`.
|
||||
*
|
||||
* For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library.
|
||||
*/
|
||||
abstract class CustomAbstractValueDefinition extends Locatable {
|
||||
|
||||
/**
|
||||
* Gets the type of some concrete value represented by the induced
|
||||
* abstract value.
|
||||
*/
|
||||
abstract InferredType getType();
|
||||
|
||||
/**
|
||||
* Gets the Boolean value that some concrete value represented by the
|
||||
* induced abstract value coerces to.
|
||||
*/
|
||||
abstract boolean getBooleanValue();
|
||||
|
||||
/**
|
||||
* Gets an abstract primitive value the induced abstract value coerces
|
||||
* to.
|
||||
*
|
||||
* This abstractly models the `ToPrimitive` coercion described in the
|
||||
* ECMAScript language specification.
|
||||
*/
|
||||
abstract PrimitiveAbstractValue toPrimitive();
|
||||
|
||||
/**
|
||||
* Holds if the induced abstract value is coercible to a number, that
|
||||
* is, it represents at least one concrete value for which the
|
||||
* `ToNumber` conversion does not yield `NaN`.
|
||||
*/
|
||||
abstract predicate isCoercibleToNumber();
|
||||
|
||||
/**
|
||||
* Holds if the induced abstract value is an indefinite value arising
|
||||
* from the incompleteness `cause`.
|
||||
*/
|
||||
predicate isIndefinite(DataFlow::Incompleteness cause) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an abstract value that represents a prototype object of the
|
||||
* induced abstract value.
|
||||
*/
|
||||
AbstractValue getAPrototype() {
|
||||
exists (AbstractProtoProperty proto |
|
||||
proto.getBase() = getAbstractValue() and
|
||||
result = proto.getAValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the induced abstract value.
|
||||
*/
|
||||
AbstractValue getAbstractValue() {
|
||||
result.(CustomAbstractValueFromDefinition).getDefinition() = this
|
||||
}
|
||||
|
||||
/** Holds if this is a value whose properties the type inference tracks. */
|
||||
abstract predicate shouldTrackProperties();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Flow analysis for custom abstract values.
|
||||
*/
|
||||
class CustomAbstractValueFromDefinitionNode extends DataFlow::AnalyzedNode, DataFlow::ValueNode {
|
||||
|
||||
CustomAbstractValueFromDefinition val;
|
||||
|
||||
CustomAbstractValueFromDefinitionNode() {
|
||||
val = TCustomAbstractValueFromDefinition(this.getAstNode())
|
||||
}
|
||||
|
||||
override AbstractValue getALocalValue() {
|
||||
result = val
|
||||
}
|
||||
|
||||
}
|
||||
@@ -77,5 +77,6 @@ predicate shouldAlwaysTrackProperties(AbstractValue baseVal) {
|
||||
predicate shouldTrackProperties(AbstractValue baseVal) {
|
||||
shouldAlwaysTrackProperties(baseVal) or
|
||||
baseVal instanceof AbstractObjectLiteral or
|
||||
baseVal instanceof AbstractInstance
|
||||
baseVal instanceof AbstractInstance or
|
||||
baseVal.(CustomAbstractValueFromDefinition).shouldTrackProperties()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import semmle.javascript.dataflow.AbstractValues
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
import semmle.javascript.dataflow.CustomAbstractValueDefinitions
|
||||
|
||||
/** An abstract value inferred by the flow analysis. */
|
||||
cached newtype TAbstractValue =
|
||||
@@ -101,6 +102,9 @@ cached newtype TAbstractValue =
|
||||
or
|
||||
/** A custom abstract value induced by `tag`. */
|
||||
TCustomAbstractValue(CustomAbstractValueTag tag)
|
||||
or
|
||||
/** A custom abstract value induced by `def`. */
|
||||
TCustomAbstractValueFromDefinition(CustomAbstractValueDefinition def)
|
||||
|
||||
/**
|
||||
* Gets a definite abstract value with the given type.
|
||||
|
||||
@@ -9,6 +9,11 @@ private string getDirectiveName(SlashStarComment c) {
|
||||
result = c.getText().regexpCapture("(?s)\\s*(\\w+)\\b.*", 1)
|
||||
}
|
||||
|
||||
/** Gets a function at the specified location. */
|
||||
private Function getFunctionAt(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** A JSLint directive. */
|
||||
abstract class JSLintDirective extends SlashStarComment {
|
||||
/**
|
||||
@@ -36,7 +41,7 @@ abstract class JSLintDirective extends SlashStarComment {
|
||||
private Function getASurroundingFunction() {
|
||||
exists (string path, int fsl, int fsc, int fel, int fec,
|
||||
int dsl, int dsc, int del, int dec |
|
||||
result.getLocation().hasLocationInfo(path, fsl, fsc, fel, fec) and
|
||||
result = getFunctionAt(path, fsl, fsc, fel, fec) and
|
||||
this.getLocation().hasLocationInfo(path, dsl, dsc, del, dec) |
|
||||
// the function starts before this directive
|
||||
(fsl < dsl or (fsl = dsl and fsc <= dsc))
|
||||
@@ -96,6 +101,11 @@ abstract class JSLintGlobal extends Linting::GlobalDeclaration, JSLintDirective
|
||||
override predicate appliesTo(ExprOrStmt s) {
|
||||
JSLintDirective.super.appliesTo(s)
|
||||
}
|
||||
|
||||
override predicate declaresGlobalForAccess(GlobalVarAccess gva) {
|
||||
declaresGlobal(gva.getName(), _) and
|
||||
getScope() = gva.getContainer().getEnclosingContainer*()
|
||||
}
|
||||
}
|
||||
|
||||
/** A JSLint `global` directive. */
|
||||
|
||||
@@ -52,7 +52,7 @@ module CleartextLogging {
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node trg) {
|
||||
any (TaintTracking::StringConcatenationTaintStep s).step(src, trg)
|
||||
StringConcatenation::taintStep(src, trg)
|
||||
or
|
||||
exists (string name | name = "toString" or name = "valueOf" |
|
||||
src.(DataFlow::SourceNode).getAMethodCall(name) = trg
|
||||
|
||||
Reference in New Issue
Block a user