Files
codeql/javascript/ql/src/Security/CWE-807/ConditionalBypass.ql
2018-10-16 08:39:59 +02:00

115 lines
3.5 KiB
Plaintext

/**
* @name User-controlled bypass of security check
* @description Conditions that the user controls are not suited for making security-related decisions.
* @kind problem
* @problem.severity error
* @precision medium
* @id js/user-controlled-bypass
* @tags security
* external/cwe/cwe-807
* external/cwe/cwe-290
*/
import javascript
import semmle.javascript.security.dataflow.ConditionalBypass::ConditionalBypass
/**
* Holds if the value of `nd` flows into `guard`.
*/
predicate flowsToGuardExpr(DataFlow::Node nd, SensitiveActionGuardConditional guard) {
nd = guard or
flowsToGuardExpr(nd.getASuccessor(), guard)
}
/**
* A comparison that guards a sensitive action, e.g. the comparison in:
* `var ok = x == y; if (ok) login()`.
*/
class SensitiveActionGuardComparison extends Comparison {
SensitiveActionGuardConditional guard;
SensitiveActionGuardComparison() {
flowsToGuardExpr(DataFlow::valueNode(this), guard)
}
/**
* Gets the guard that uses this comparison.
*/
SensitiveActionGuardConditional getGuard() {
result = guard
}
}
/**
* An intermediary sink to enable reuse of the taint configuration.
* This sink should not be presented to the client of this query.
*/
class SensitiveActionGuardComparisonOperand extends Sink {
SensitiveActionGuardComparison comparison;
SensitiveActionGuardComparisonOperand() {
asExpr() = comparison.getAnOperand()
}
override SensitiveAction getAction() {
result = comparison.getGuard().getAction()
}
}
/**
* Holds if `sink` guards `action`, and `source` taints `sink`.
*
* If flow from `source` taints `sink`, then an attacker can
* control if `action` should be executed or not.
*/
predicate isTaintedGuardForSensitiveAction(Sink sink, DataFlow::Node source, SensitiveAction action) {
action = sink.getAction() and
// exclude the intermediary sink
not sink instanceof SensitiveActionGuardComparisonOperand and
exists (Configuration cfg |
// ordinary taint tracking to a guard
cfg.hasFlow(source, sink) or
// taint tracking to both operands of a guard comparison
exists (SensitiveActionGuardComparison cmp, DataFlow::Node lSource, DataFlow::Node rSource |
sink = cmp.getGuard() and
cfg.hasFlow(lSource, DataFlow::valueNode(cmp.getLeftOperand())) and
cfg.hasFlow(rSource, DataFlow::valueNode(cmp.getRightOperand())) |
source = lSource or
source = rSource
)
)
}
/**
* Holds if `e` effectively guards access to `action` by returning or throwing early.
*
* Example: `if (e) return; action(x)`.
*/
predicate isEarlyAbortGuard(Sink e, SensitiveAction action) {
exists (IfStmt guard |
// `e` is in the condition of an if-statement ...
e.asExpr().getParentExpr*() = guard.getCondition() and
// ... where the then-branch always throws or returns
exists (Stmt abort |
abort instanceof ThrowStmt or
abort instanceof ReturnStmt |
abort.nestedIn(guard) and
abort.getBasicBlock().(ReachableBasicBlock).postDominates(guard.getThen().getBasicBlock() )
) and
// ... and the else-branch does not exist
not exists (guard.getElse()) |
// ... and `action` is outside the if-statement
not action.asExpr().getEnclosingStmt().nestedIn(guard)
)
}
from DataFlow::Node source, DataFlow::Node sink, SensitiveAction action
where isTaintedGuardForSensitiveAction(sink, source, action) and
not isEarlyAbortGuard(sink, action)
select sink, "This condition guards a sensitive $@, but $@ controls it.",
action, "action",
source, "a user-provided value"