mirror of
https://github.com/github/codeql.git
synced 2026-04-27 17:55:19 +02:00
QL code and tests for C#/C++/JavaScript.
This commit is contained in:
16
javascript/ql/src/meta/SSA/DeadDef.ql
Normal file
16
javascript/ql/src/meta/SSA/DeadDef.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Dead SSA definition
|
||||
* @description Each SSA definition should have at least one use.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id js/sanity/dead-ssa-definition
|
||||
* @tags sanity
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
from SsaVariable d
|
||||
where not exists (d.getAUse()) and
|
||||
not d = any(SsaPseudoDefinition phi).getAnInput() and
|
||||
d.getSourceVariable() instanceof PurelyLocalVariable
|
||||
select d, "Dead SSA definition " + d + "."
|
||||
32
javascript/ql/src/meta/SSA/Dominance.ql
Normal file
32
javascript/ql/src/meta/SSA/Dominance.ql
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @name SSA definition does not dominate use
|
||||
* @description Every use of an SSA variable should be dominated by its
|
||||
* definition.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id js/sanity/non-dominating-ssa-definition
|
||||
* @tags sanity
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Holds if SSA definition `def` dominates `use`,
|
||||
* which is a use of the same variable.
|
||||
*/
|
||||
predicate dominates(SsaDefinition def, VarUse use) {
|
||||
exists (SsaSourceVariable v,
|
||||
ReachableBasicBlock defbb, int defidx,
|
||||
ReachableBasicBlock usebb, int useidx |
|
||||
def.definesAt(defbb, defidx, v) and usebb.useAt(useidx, v, use) |
|
||||
defbb = usebb and defidx <= useidx or
|
||||
defbb.strictlyDominates(usebb)
|
||||
)
|
||||
}
|
||||
|
||||
from VarUse u, SsaDefinition d
|
||||
where u.getVariable() instanceof SsaSourceVariable and
|
||||
exists (ReachableBasicBlock bb | u = bb.getANode()) and
|
||||
u = d.getVariable().getAUse() and
|
||||
not dominates(d, u)
|
||||
select u, "Variable use is not dominated by its definition $@.", d, d.toString()
|
||||
19
javascript/ql/src/meta/SSA/MultipleDefs.ql
Normal file
19
javascript/ql/src/meta/SSA/MultipleDefs.ql
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @name Variable use with more than one corresponding SSA variable
|
||||
* @description Every reachable use of an SSA-convertible variable should correspond to
|
||||
* exactly one SSA variable.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id js/sanity/ambiguous-ssa-definition
|
||||
* @tags sanity
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
from VarUse u, int n, SsaVariable v
|
||||
where u.getVariable() instanceof SsaSourceVariable and
|
||||
exists (ReachableBasicBlock bb | u = bb.getANode()) and
|
||||
n = count(u.getSsaVariable()) and
|
||||
n > 1 and
|
||||
v = u.getSsaVariable()
|
||||
select u, "Variable use has " + n + " corresponding SSA variables: $@.", v, v.toString()
|
||||
16
javascript/ql/src/meta/SSA/MultipleRefinementInputs.ql
Normal file
16
javascript/ql/src/meta/SSA/MultipleRefinementInputs.ql
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @name Refinement node with more than one input
|
||||
* @description Every SSA refinement node should have exactly one input.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id js/sanity/ambiguous-refinement-node
|
||||
* @tags sanity
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
from SsaRefinementNode ref, int n, SsaDefinition input
|
||||
where n = count(ref.getAnInput()) and
|
||||
n > 1 and
|
||||
input = ref.getAnInput()
|
||||
select ref, "Refinement node has " + n + " inputs: $@.", input, input.toString()
|
||||
17
javascript/ql/src/meta/SSA/NoDefs.ql
Normal file
17
javascript/ql/src/meta/SSA/NoDefs.ql
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Variable use with no corresponding SSA variable
|
||||
* @description Every reachable use of an SSA-convertible variable should correspond to
|
||||
* exactly one SSA variable.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id js/sanity/dead-ssa-use
|
||||
* @tags sanity
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
from VarUse u
|
||||
where u.getVariable() instanceof SsaSourceVariable and
|
||||
exists (ReachableBasicBlock bb | u = bb.getANode()) and
|
||||
not exists(u.getSsaVariable())
|
||||
select u, "Variable use has no corresponding SSA variable."
|
||||
14
javascript/ql/src/meta/SSA/NoPhiInputs.ql
Normal file
14
javascript/ql/src/meta/SSA/NoPhiInputs.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name Phi node without inputs
|
||||
* @description Every SSA phi node should have two or more inputs.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id js/sanity/dead-phi-node
|
||||
* @tags sanity
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
from SsaPhiNode phi
|
||||
where not exists(phi.getAnInput())
|
||||
select phi, "Phi node without inputs."
|
||||
14
javascript/ql/src/meta/SSA/NoRefinementInputs.ql
Normal file
14
javascript/ql/src/meta/SSA/NoRefinementInputs.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name Refinement node without inputs
|
||||
* @description Every SSA refinement node should have exactly one input.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id js/sanity/dead-refinement-node
|
||||
* @tags sanity
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
from SsaRefinementNode ref
|
||||
where not exists(ref.getAnInput())
|
||||
select ref, "Refinement node without inputs."
|
||||
14
javascript/ql/src/meta/SSA/SinglePhiInput.ql
Normal file
14
javascript/ql/src/meta/SSA/SinglePhiInput.ql
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @name Phi node with a single input
|
||||
* @description Every SSA phi node should have two or more inputs.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id js/sanity/trivial-phi-node
|
||||
* @tags sanity
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
from SsaPhiNode phi
|
||||
where count(phi.getAnInput()) = 1
|
||||
select phi, "Phi node with exactly one input $@.", phi.getAnInput(), phi.getAnInput().toString()
|
||||
168
javascript/ql/src/meta/Sanity.ql
Normal file
168
javascript/ql/src/meta/Sanity.ql
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* @name Violation of API contract
|
||||
* @description If the snapshot database or the QL library violates an API contract described
|
||||
* in the documentation, queries that rely on the contract may yield unexpected
|
||||
* results.
|
||||
* @kind table
|
||||
* @problem.severity error
|
||||
* @id js/sanity/api-contracts
|
||||
* @tags sanity
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Holds if `e` is an expression that need not have an enclosing statement.
|
||||
*/
|
||||
predicate exprWithoutEnclosingStmt(Expr e) {
|
||||
// Function names, parameters, default values and bodies do not have an enclosing statement.
|
||||
exists (Function f | e = f.getAChild()) or
|
||||
exists (Parameter p | e = p.getDefault()) or
|
||||
// Class members do not have enclosing statements.
|
||||
exists (MemberDefinition md | e = md.getAChild()) or
|
||||
// If an expression's parent has no enclosing statement, then neither does the expression itself.
|
||||
exprWithoutEnclosingStmt(e.getParent()) or
|
||||
// Some expressions have non-expression parents that we want to skip over.
|
||||
exprWithoutEnclosingStmt(e.getParent().(Property).getObjectExpr()) or
|
||||
exprWithoutEnclosingStmt(e.getParent().(PropertyPattern).getObjectPattern()) or
|
||||
exprWithoutEnclosingStmt(e.getParent().(JSXAttribute).getElement())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `problem` is a string describing the fact that method `what`, which
|
||||
* is expected to have precisely one result, has `number` results, where `number`
|
||||
* is either zero or a number between two and ten.
|
||||
*
|
||||
* For example, if `what` is `"toString"` and `number` is `0`, then `problem` is
|
||||
* `"no results for toString()"`; if `number` is `3`, then `problem` is
|
||||
* `"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
|
||||
(number = 0 and problem = "no results for " + what + "()"
|
||||
or
|
||||
number in [2 .. 10] and problem = number.toString() + " results for " + what + "()")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a contract involving the AST structure is violated, where `clsname`
|
||||
* is the QL class name of the entity violating the contract, `problem` describes
|
||||
* the violation, and `what` gives location information where possible.
|
||||
*/
|
||||
predicate ast_sanity(string clsname, string problem, string what) {
|
||||
exists (Locatable l | clsname = l.getAQlClass() |
|
||||
uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l.getLocation() or
|
||||
uniqueness_error(strictcount(l.getLocation()), "getLocation", problem) and what = l.getLocation().toString() or
|
||||
not exists(l.getLocation()) and problem = "no location" and what = l.toString()
|
||||
) or
|
||||
exists (ASTNode nd | clsname = nd.getAQlClass() |
|
||||
uniqueness_error(count(nd.getTopLevel()), "getTopLevel", problem) and what = "at " + nd.getLocation()
|
||||
) or
|
||||
exists (Expr e | clsname = e.getAQlClass() |
|
||||
uniqueness_error(count(e.getContainer()), "getContainer", problem) and what = "at " + e.getLocation() or
|
||||
(not exprWithoutEnclosingStmt(e) and
|
||||
uniqueness_error(count(e.getEnclosingStmt()), "getEnclosingStmt", problem) and what = "at " + e.getLocation())
|
||||
) or
|
||||
exists (Stmt s | clsname = s.getAQlClass() |
|
||||
uniqueness_error(count(s.getContainer()), "getContainer", problem) and what = "at " + s.getLocation()
|
||||
) or
|
||||
exists (StmtContainer cont | not cont instanceof TopLevel and clsname = cont.getAQlClass() |
|
||||
uniqueness_error(count(cont.getEnclosingContainer()), "getEnclosingContainer", problem) and what = "at " + cont.getLocation()
|
||||
) or
|
||||
exists (UnaryExpr ue | clsname = ue.getAQlClass() |
|
||||
uniqueness_error(count(ue.getOperator()), "getOperator", problem) and what = "at " + ue.getLocation()
|
||||
) or
|
||||
exists (UpdateExpr ue | clsname = ue.getAQlClass() |
|
||||
uniqueness_error(count(ue.getOperator()), "getOperator", problem) and what = "at " + ue.getLocation()
|
||||
) or
|
||||
exists (BinaryExpr be | clsname = be.getAQlClass() |
|
||||
uniqueness_error(count(be.getOperator()), "getOperator", problem) and what = "at " + be.getLocation()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a location entity of QL class `clsname` does not have a unique `toString`,
|
||||
* where `problem` describes the problem and `what` gives location information where possible.
|
||||
*/
|
||||
predicate location_sanity(string clsname, string problem, string what) {
|
||||
exists (Location l | clsname = l.getAQlClass() |
|
||||
uniqueness_error(count(l.toString()), "toString", problem) and what = "at " + l or
|
||||
not exists(l.toString()) and problem = "no toString" and what = "a location"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if function or toplevel `sc` is expected to have an associated control flow graph.
|
||||
*/
|
||||
predicate hasCFG(StmtContainer sc) {
|
||||
not exists (Error err | err.getFile() = sc.getFile())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a contract involving the CFG structure is violated, where `clsname`
|
||||
* is the QL class name of the entity violating the contract, `problem` describes
|
||||
* the violation, and `what` gives location information.
|
||||
*/
|
||||
predicate cfg_sanity(string clsname, string problem, string what) {
|
||||
exists (StmtContainer cont | clsname = cont.getAQlClass() and hasCFG(cont) |
|
||||
uniqueness_error(count(cont.getEntry()), "getEntry", problem) and what = "at " + cont.getLocation() or
|
||||
uniqueness_error(count(cont.getExit()), "getExit", problem) and what = "at " + cont.getLocation()
|
||||
)
|
||||
or
|
||||
exists (ASTNode nd | clsname = nd.getAQlClass() and hasCFG(nd.getTopLevel()) |
|
||||
uniqueness_error(count(nd.getFirstControlFlowNode()), "getFirstControlFlowNode", problem) and
|
||||
what = "at " + nd.getLocation()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a contract involving scoping and name lookup is violated, where `clsname`
|
||||
* is the QL class name of the entity violating the contract, `problem` describes
|
||||
* the violation, and `what` gives location information.
|
||||
*/
|
||||
predicate scope_sanity(string clsname, string problem, string what) {
|
||||
exists (Scope s | clsname = s.getAQlClass() |
|
||||
uniqueness_error(count(s.toString()), "toString", problem) and what = "a scope" or
|
||||
not s instanceof GlobalScope and
|
||||
(uniqueness_error(count(s.getOuterScope()), "getOuterScope", problem) and what = s.toString() or
|
||||
uniqueness_error(count(s.getScopeElement()), "getScopeElement", problem) and what = s.toString())
|
||||
) or
|
||||
exists (int n | n = count(GlobalScope g) and n != 1 |
|
||||
clsname = "GlobalScope" and what = "" and problem = n + " instances"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a JSDoc type expression of QL class `clsname` does not have a unique `toString`,
|
||||
* where `problem` describes the problem and `what` is the empty string.
|
||||
*/
|
||||
predicate jsdoc_sanity(string clsname, string problem, string what) {
|
||||
exists (JSDocTypeExprParent jsdtep | clsname = jsdtep.getAQlClass() |
|
||||
uniqueness_error(count(jsdtep.toString()), "toString", problem) and what = ""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a variable reference does not refer to a unique variable,
|
||||
* where `problem` describes the problem and `what` is the name of the variable.
|
||||
*/
|
||||
predicate varref_sanity(string clsname, string problem, string what) {
|
||||
exists (VarRef vr, int n | n = count(vr.getVariable()) and n != 1 |
|
||||
clsname = vr.getAQlClass() and
|
||||
what = vr.getName() and
|
||||
problem = n + " target variables instead of one"
|
||||
)
|
||||
}
|
||||
|
||||
from string clsname, string problem, string what
|
||||
where ast_sanity(clsname, problem, what) or
|
||||
location_sanity(clsname, problem, what) or
|
||||
scope_sanity(clsname, problem, what) or
|
||||
cfg_sanity(clsname, problem, what) or
|
||||
jsdoc_sanity(clsname, problem, what) or
|
||||
varref_sanity(clsname, problem, what)
|
||||
select clsname + " " + what + " has " + problem
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Unpromoted route handler candidate
|
||||
* @description If a function that looks like a route handler is not detected as such, this may indicate incomplete library modeling.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision low
|
||||
* @id js/unpromoted-route-handler-candidate
|
||||
* @tags analysis-quality
|
||||
*/
|
||||
import javascript
|
||||
|
||||
from HTTP::RouteHandlerCandidate rh
|
||||
where not rh instanceof HTTP::RouteHandler and
|
||||
not exists (HTTP::RouteSetupCandidate setup |
|
||||
rh.(DataFlow::TrackedNode).flowsTo(setup.getARouteHandlerArg())
|
||||
)
|
||||
select rh, "A `RouteHandlerCandidate` that did not get promoted to `RouteHandler`, and it is not used in a `RouteSetupCandidate`."
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* @name Unpromoted route setup candidate
|
||||
* @description If a call that looks like a route setup is not detected as such, this may indicate incomplete library modeling.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision low
|
||||
* @id js/unpromoted-route-setup-candidate
|
||||
* @tags analysis-quality
|
||||
*/
|
||||
import javascript
|
||||
|
||||
from HTTP::RouteSetupCandidate setup
|
||||
where not setup.asExpr() instanceof HTTP::RouteSetup and
|
||||
exists (HTTP::RouteHandlerCandidate rh |
|
||||
rh.(DataFlow::TrackedNode).flowsTo(setup.getARouteHandlerArg())
|
||||
)
|
||||
select setup, "A `RouteSetupCandidate` that did not get promoted to `RouteSetup`, and it is using at least one `RouteHandlerCandidate`."
|
||||
Reference in New Issue
Block a user