mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
JavaScript: Add support for sanitising dynamic property accesses.
This generalises our previous handling of sanitisers operating on property accesses to support dynamic property accesses where the property name is an SSA variable by representing them as access paths.
This commit is contained in:
@@ -70,6 +70,7 @@
|
||||
|
||||
import javascript
|
||||
private import internal.FlowSteps
|
||||
private import internal.AccessPaths
|
||||
|
||||
/**
|
||||
* A data flow tracking configuration for finding inter-procedural paths from
|
||||
@@ -291,26 +292,12 @@ abstract class BarrierGuardNode extends DataFlow::Node {
|
||||
)
|
||||
)
|
||||
or
|
||||
// 2) `nd` is a use of an SSA variable `ssa`, and dominated by a barrier for `ssa`
|
||||
exists (SsaVariable ssa, BasicBlock bb |
|
||||
nd = DataFlow::valueNode(ssa.getAUseIn(bb)) and
|
||||
exists (ConditionGuardNode cond |
|
||||
asExpr() = cond.getTest() and
|
||||
blocks(cond.getOutcome(), ssa.getAUse()) and
|
||||
cond.dominates(bb)
|
||||
)
|
||||
)
|
||||
or
|
||||
// 3) `nd` is a property access `ssa.p.q` on an SSA variable `ssa`, and dominated by
|
||||
// a barrier for `ssa.p.q`
|
||||
exists (SsaVariable ssa, string props, BasicBlock bb |
|
||||
nd = DataFlow::valueNode(nestedPropAccessOnSsaVar(ssa, props)) and
|
||||
bb = nd.getBasicBlock() |
|
||||
exists (ConditionGuardNode cond |
|
||||
asExpr() = cond.getTest() and
|
||||
blocks(cond.getOutcome(), nestedPropAccessOnSsaVar(ssa, props)) and
|
||||
cond.dominates(bb)
|
||||
)
|
||||
// 2) `nd` is an instance of an access path `p`, and dominated by a barrier for `p`
|
||||
exists (AccessPath p, BasicBlock bb, ConditionGuardNode cond |
|
||||
nd = DataFlow::valueNode(p.getAnInstanceIn(bb)) and
|
||||
asExpr() = cond.getTest() and
|
||||
blocks(cond.getOutcome(), p.getAnInstance()) and
|
||||
cond.dominates(bb)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -330,20 +317,6 @@ abstract class LabeledBarrierGuardNode extends BarrierGuardNode {
|
||||
abstract FlowLabel getALabel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `props` is a string of the form `p.q.r`, and the result is a property access
|
||||
* of the form `v.p.q.r`.
|
||||
*/
|
||||
private DotExpr nestedPropAccessOnSsaVar(SsaVariable v, string props) {
|
||||
exists (Expr base, string prop | result.accesses(base, prop) |
|
||||
base = v.getAUse() and props = prop
|
||||
or
|
||||
exists (string prevProps |
|
||||
base = nestedPropAccessOnSsaVar(v, prevProps) and
|
||||
props = prevProps + "." + prop
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow edge that should be added to all data flow configurations in
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Provides classes for reasoning about certain property accesses as access paths.
|
||||
*
|
||||
* Access paths represent SSA variables with zero or more property accesses applied to them.
|
||||
* Each property name in the access must either be constant or itself be a use of an SSA
|
||||
* variable.
|
||||
*
|
||||
* If `x` and `y` are SSA variables, then `x`, `x.p`, `x.p["q"]`, `x[y]` and `x[y].q` are
|
||||
* access paths, but `x[x.y]` is not an access path.
|
||||
*
|
||||
* Access paths can be used to identify expressions that have the same value, disregarding
|
||||
* any heap modifications. In general, expressions that are instances of the same access
|
||||
* path are not guaranteed to evaluate to the same value nor do all expressions that evaluate
|
||||
* to the same value have the same access paths, so access paths are neither sound nor
|
||||
* complete as an approximation of expression semantics.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A representation of a property name that is either statically known or is
|
||||
* the value of an SSA variable.
|
||||
*/
|
||||
private newtype PropertyName =
|
||||
StaticPropertyName(string name) {
|
||||
exists (PropAccess pa | name = pa.getPropertyName())
|
||||
}
|
||||
or
|
||||
DynamicPropertyName(SsaVariable var) {
|
||||
exists (PropAccess pa | pa.getPropertyNameExpr() = var.getAUse())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the representation of the property name of `pacc`, if any.
|
||||
*/
|
||||
private PropertyName getPropertyName(PropAccess pacc) {
|
||||
result = StaticPropertyName(pacc.getPropertyName())
|
||||
or
|
||||
exists (SsaVariable var |
|
||||
pacc.getPropertyNameExpr() = var.getAUse() and
|
||||
result = DynamicPropertyName(var)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of a (nested) property access on an SSA variable
|
||||
* where each property name is either constant or itself an SSA variable.
|
||||
*/
|
||||
private newtype TAccessPath =
|
||||
MkRoot(SsaVariable var)
|
||||
or
|
||||
MkAccessStep(AccessPath base, PropertyName name) {
|
||||
exists (PropAccess pacc |
|
||||
pacc.getBase() = base.getAnInstance() and
|
||||
getPropertyName(pacc) = name
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of a (nested) property access on an SSA variable
|
||||
* where each property name is either constant or itself an SSA variable.
|
||||
*/
|
||||
class AccessPath extends TAccessPath {
|
||||
/**
|
||||
* Gets an expression in `bb` represented by this access path.
|
||||
*/
|
||||
Expr getAnInstanceIn(BasicBlock bb) {
|
||||
exists (SsaVariable var |
|
||||
this = MkRoot(var) and
|
||||
result = var.getAUseIn(bb)
|
||||
)
|
||||
or
|
||||
exists (PropertyName name |
|
||||
result = getABaseInstanceIn(bb, name) and
|
||||
getPropertyName(result) = name
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property access in `bb` whose base is represented by the
|
||||
* base of this access path, and where `name` is bound to the last
|
||||
* component of this access path.
|
||||
*
|
||||
* This is an auxiliary predicate that's needed to enforce a better
|
||||
* join order in `getAnInstanceIn` above.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private PropAccess getABaseInstanceIn(BasicBlock bb, PropertyName name) {
|
||||
exists (AccessPath base | this = MkAccessStep(base, name) |
|
||||
result.getBase() = base.getAnInstanceIn(bb)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression represented by this access path.
|
||||
*/
|
||||
Expr getAnInstance() {
|
||||
result = getAnInstanceIn(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this access path.
|
||||
*/
|
||||
string toString() {
|
||||
exists (SsaVariable var | this = MkRoot(var) |
|
||||
result = var.getSourceVariable().getName()
|
||||
)
|
||||
or
|
||||
exists (AccessPath base, PropertyName name, string rest |
|
||||
rest = "." + any(string s | name = StaticPropertyName(s))
|
||||
or
|
||||
rest = "[" + any(SsaVariable var | name = DynamicPropertyName(var)).getSourceVariable().getName() + "]" |
|
||||
result = base.toString() + rest and
|
||||
this = MkAccessStep(base, name)
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user