mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
upgrade query to detect redash CVE too
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
## 0.7.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Deleted the deprecated `hasCopyConstructor` predicate from the `Class` class in `Class.qll`.
|
||||
* Deleted many deprecated predicates and classes with uppercase `AST`, `SSA`, `CFG`, `API`, etc. in their names. Use the PascalCased versions instead.
|
||||
* Deleted the deprecated `CodeDuplication.qll` file.
|
||||
|
||||
## 0.7.2
|
||||
|
||||
### New Features
|
||||
|
||||
7
cpp/ql/lib/change-notes/released/0.7.3.md
Normal file
7
cpp/ql/lib/change-notes/released/0.7.3.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## 0.7.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Deleted the deprecated `hasCopyConstructor` predicate from the `Class` class in `Class.qll`.
|
||||
* Deleted many deprecated predicates and classes with uppercase `AST`, `SSA`, `CFG`, `API`, etc. in their names. Use the PascalCased versions instead.
|
||||
* Deleted the deprecated `CodeDuplication.qll` file.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.7.2
|
||||
lastReleaseVersion: 0.7.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.7.3-dev
|
||||
version: 0.7.4-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -193,86 +193,89 @@ private class SingleUseOperandNode0 extends OperandNode0, TSingleUseOperandNode0
|
||||
SingleUseOperandNode0() { this = TSingleUseOperandNode0(op) }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an operand in the IR
|
||||
* after `index` number of loads.
|
||||
*
|
||||
* Note: Unlike `RawIndirectOperand`, a value of type `IndirectOperand` may
|
||||
* be an `OperandNode`.
|
||||
*/
|
||||
class IndirectOperand extends Node {
|
||||
Operand operand;
|
||||
int indirectionIndex;
|
||||
|
||||
IndirectOperand() {
|
||||
this.(RawIndirectOperand).getOperand() = operand and
|
||||
this.(RawIndirectOperand).getIndirectionIndex() = indirectionIndex
|
||||
or
|
||||
nodeHasOperand(this, Ssa::getIRRepresentationOfIndirectOperand(operand, indirectionIndex),
|
||||
indirectionIndex - 1)
|
||||
private module IndirectOperands {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an operand in the IR
|
||||
* after `index` number of loads.
|
||||
*
|
||||
* Note: Unlike `RawIndirectOperand`, a value of type `IndirectOperand` may
|
||||
* be an `OperandNode`.
|
||||
*/
|
||||
abstract class IndirectOperand extends Node {
|
||||
/** Gets the underlying operand and the underlying indirection index. */
|
||||
abstract predicate hasOperandAndIndirectionIndex(Operand operand, int indirectionIndex);
|
||||
}
|
||||
|
||||
/** Gets the underlying operand. */
|
||||
Operand getOperand() { result = operand }
|
||||
private class IndirectOperandFromRaw extends IndirectOperand instanceof RawIndirectOperand {
|
||||
override predicate hasOperandAndIndirectionIndex(Operand operand, int indirectionIndex) {
|
||||
operand = RawIndirectOperand.super.getOperand() and
|
||||
indirectionIndex = RawIndirectOperand.super.getIndirectionIndex()
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
private class IndirectOperandFromIRRepr extends IndirectOperand {
|
||||
Operand operand;
|
||||
int indirectionIndex;
|
||||
|
||||
/**
|
||||
* Holds if this `IndirectOperand` is represented directly in the IR instead of
|
||||
* a `RawIndirectionOperand` with operand `op` and indirection index `index`.
|
||||
*/
|
||||
predicate isIRRepresentationOf(Operand op, int index) {
|
||||
this instanceof OperandNode and
|
||||
(
|
||||
op = operand and
|
||||
index = indirectionIndex
|
||||
)
|
||||
IndirectOperandFromIRRepr() {
|
||||
exists(Operand repr |
|
||||
repr = Ssa::getIRRepresentationOfIndirectOperand(operand, indirectionIndex) and
|
||||
nodeHasOperand(this, repr, indirectionIndex - 1)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasOperandAndIndirectionIndex(Operand op, int index) {
|
||||
op = operand and index = indirectionIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an instruction in the IR
|
||||
* after `index` number of loads.
|
||||
*
|
||||
* Note: Unlike `RawIndirectInstruction`, a value of type `IndirectInstruction` may
|
||||
* be an `InstructionNode`.
|
||||
*/
|
||||
class IndirectInstruction extends Node {
|
||||
Instruction instr;
|
||||
int indirectionIndex;
|
||||
import IndirectOperands
|
||||
|
||||
IndirectInstruction() {
|
||||
this.(RawIndirectInstruction).getInstruction() = instr and
|
||||
this.(RawIndirectInstruction).getIndirectionIndex() = indirectionIndex
|
||||
or
|
||||
nodeHasInstruction(this, Ssa::getIRRepresentationOfIndirectInstruction(instr, indirectionIndex),
|
||||
indirectionIndex - 1)
|
||||
private module IndirectInstructions {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an instruction in the IR
|
||||
* after `index` number of loads.
|
||||
*
|
||||
* Note: Unlike `RawIndirectInstruction`, a value of type `IndirectInstruction` may
|
||||
* be an `InstructionNode`.
|
||||
*/
|
||||
abstract class IndirectInstruction extends Node {
|
||||
/** Gets the underlying operand and the underlying indirection index. */
|
||||
abstract predicate hasInstructionAndIndirectionIndex(Instruction instr, int index);
|
||||
}
|
||||
|
||||
/** Gets the underlying instruction. */
|
||||
Instruction getInstruction() { result = instr }
|
||||
private class IndirectInstructionFromRaw extends IndirectInstruction instanceof RawIndirectInstruction
|
||||
{
|
||||
override predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
|
||||
instr = RawIndirectInstruction.super.getInstruction() and
|
||||
index = RawIndirectInstruction.super.getIndirectionIndex()
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
private class IndirectInstructionFromIRRepr extends IndirectInstruction {
|
||||
Instruction instr;
|
||||
int indirectionIndex;
|
||||
|
||||
/**
|
||||
* Holds if this `IndirectInstruction` is represented directly in the IR instead of
|
||||
* a `RawIndirectionInstruction` with instruction `i` and indirection index `index`.
|
||||
*/
|
||||
predicate isIRRepresentationOf(Instruction i, int index) {
|
||||
this instanceof InstructionNode and
|
||||
(
|
||||
i = instr and
|
||||
index = indirectionIndex
|
||||
)
|
||||
IndirectInstructionFromIRRepr() {
|
||||
exists(Instruction repr |
|
||||
repr = Ssa::getIRRepresentationOfIndirectInstruction(instr, indirectionIndex) and
|
||||
nodeHasInstruction(this, repr, indirectionIndex - 1)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasInstructionAndIndirectionIndex(Instruction i, int index) {
|
||||
i = instr and index = indirectionIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import IndirectInstructions
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
|
||||
|
||||
@@ -320,7 +323,7 @@ private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode
|
||||
override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) {
|
||||
this.getCallInstruction() = dfCall and
|
||||
pos.(IndirectionPosition).getArgumentIndex() = this.getArgumentIndex() and
|
||||
pos.(IndirectionPosition).getIndirectionIndex() = super.getIndirectionIndex()
|
||||
super.hasAddressOperandAndIndirectionIndex(_, pos.(IndirectionPosition).getIndirectionIndex())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,7 +848,7 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
||||
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
|
||||
* by default as a heuristic.
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { p instanceof IndirectParameterNode }
|
||||
|
||||
private predicate fieldHasApproxName(Field f, string s) {
|
||||
s = f.getName().charAt(0) and
|
||||
|
||||
@@ -274,7 +274,7 @@ class Node extends TIRDataFlowNode {
|
||||
* represents the value of `**x` going into `f`.
|
||||
*/
|
||||
Expr asIndirectArgument(int index) {
|
||||
this.(SideEffectOperandNode).getIndirectionIndex() = index and
|
||||
this.(SideEffectOperandNode).hasAddressOperandAndIndirectionIndex(_, index) and
|
||||
result = this.(SideEffectOperandNode).getArgument()
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ class Node extends TIRDataFlowNode {
|
||||
index = 0 and
|
||||
result = this.(ExplicitParameterNode).getParameter()
|
||||
or
|
||||
this.(IndirectParameterNode).getIndirectionIndex() = index and
|
||||
this.(IndirectParameterNode).hasInstructionAndIndirectionIndex(_, index) and
|
||||
result = this.(IndirectParameterNode).getParameter()
|
||||
}
|
||||
|
||||
@@ -577,15 +577,20 @@ class SsaPhiNode extends Node, TSsaPhiNode {
|
||||
*
|
||||
* A node representing a value after leaving a function.
|
||||
*/
|
||||
class SideEffectOperandNode extends Node, IndirectOperand {
|
||||
class SideEffectOperandNode extends Node instanceof IndirectOperand {
|
||||
CallInstruction call;
|
||||
int argumentIndex;
|
||||
|
||||
SideEffectOperandNode() { operand = call.getArgumentOperand(argumentIndex) }
|
||||
SideEffectOperandNode() {
|
||||
IndirectOperand.super.hasOperandAndIndirectionIndex(call.getArgumentOperand(argumentIndex), _)
|
||||
}
|
||||
|
||||
CallInstruction getCallInstruction() { result = call }
|
||||
|
||||
Operand getAddressOperand() { result = operand }
|
||||
/** Gets the underlying operand and the underlying indirection index. */
|
||||
predicate hasAddressOperandAndIndirectionIndex(Operand operand, int indirectionIndex) {
|
||||
IndirectOperand.super.hasOperandAndIndirectionIndex(operand, indirectionIndex)
|
||||
}
|
||||
|
||||
int getArgumentIndex() { result = argumentIndex }
|
||||
|
||||
@@ -665,10 +670,10 @@ class InitialGlobalValue extends Node, TInitialGlobalValue {
|
||||
*
|
||||
* A node representing an indirection of a parameter.
|
||||
*/
|
||||
class IndirectParameterNode extends Node, IndirectInstruction {
|
||||
class IndirectParameterNode extends Node instanceof IndirectInstruction {
|
||||
InitializeParameterInstruction init;
|
||||
|
||||
IndirectParameterNode() { this.getInstruction() = init }
|
||||
IndirectParameterNode() { IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _) }
|
||||
|
||||
int getArgumentIndex() { init.hasIndex(result) }
|
||||
|
||||
@@ -677,7 +682,12 @@ class IndirectParameterNode extends Node, IndirectInstruction {
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
|
||||
override Declaration getFunction() { result = init.getEnclosingFunction() }
|
||||
|
||||
/** Gets the underlying operand and the underlying indirection index. */
|
||||
predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
|
||||
IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index)
|
||||
}
|
||||
|
||||
override Location getLocationImpl() { result = this.getParameter().getLocation() }
|
||||
|
||||
@@ -699,7 +709,8 @@ class IndirectReturnNode extends Node {
|
||||
IndirectReturnNode() {
|
||||
this instanceof FinalParameterNode
|
||||
or
|
||||
this.(IndirectOperand).getOperand() = any(ReturnValueInstruction ret).getReturnAddressOperand()
|
||||
this.(IndirectOperand)
|
||||
.hasOperandAndIndirectionIndex(any(ReturnValueInstruction ret).getReturnAddressOperand(), _)
|
||||
}
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
@@ -722,7 +733,7 @@ class IndirectReturnNode extends Node {
|
||||
int getIndirectionIndex() {
|
||||
result = this.(FinalParameterNode).getIndirectionIndex()
|
||||
or
|
||||
result = this.(IndirectOperand).getIndirectionIndex()
|
||||
this.(IndirectOperand).hasOperandAndIndirectionIndex(_, result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1106,7 +1117,8 @@ predicate exprNodeShouldBeInstruction(Node node, Expr e) {
|
||||
/** Holds if `node` should be an `IndirectInstruction` that maps `node.asIndirectExpr()` to `e`. */
|
||||
predicate indirectExprNodeShouldBeIndirectInstruction(IndirectInstruction node, Expr e) {
|
||||
exists(Instruction instr |
|
||||
instr = node.getInstruction() and not indirectExprNodeShouldBeIndirectOperand(_, e)
|
||||
node.hasInstructionAndIndirectionIndex(instr, _) and
|
||||
not indirectExprNodeShouldBeIndirectOperand(_, e)
|
||||
|
|
||||
e = instr.(VariableAddressInstruction).getAst().(Expr).getFullyConverted()
|
||||
or
|
||||
@@ -1307,8 +1319,8 @@ pragma[noinline]
|
||||
private predicate indirectParameterNodeHasArgumentIndexAndIndex(
|
||||
IndirectParameterNode node, int argumentIndex, int indirectionIndex
|
||||
) {
|
||||
node.getArgumentIndex() = argumentIndex and
|
||||
node.getIndirectionIndex() = indirectionIndex
|
||||
node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and
|
||||
node.getArgumentIndex() = argumentIndex
|
||||
}
|
||||
|
||||
/** A synthetic parameter to model the pointed-to object of a pointer parameter. */
|
||||
@@ -1479,18 +1491,14 @@ VariableNode variableNode(Variable v) {
|
||||
*/
|
||||
Node uninitializedNode(LocalVariable v) { none() }
|
||||
|
||||
pragma[noinline]
|
||||
predicate hasOperandAndIndex(IndirectOperand indirectOperand, Operand operand, int indirectionIndex) {
|
||||
indirectOperand.getOperand() = operand and
|
||||
indirectOperand.getIndirectionIndex() = indirectionIndex
|
||||
indirectOperand.hasOperandAndIndirectionIndex(operand, indirectionIndex)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate hasInstructionAndIndex(
|
||||
IndirectInstruction indirectInstr, Instruction instr, int indirectionIndex
|
||||
) {
|
||||
indirectInstr.getInstruction() = instr and
|
||||
indirectInstr.getIndirectionIndex() = indirectionIndex
|
||||
indirectInstr.hasInstructionAndIndirectionIndex(instr, indirectionIndex)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -1656,8 +1664,7 @@ module ExprFlowCached {
|
||||
private predicate isIndirectBaseOfArrayAccess(IndirectOperand n, Expr e) {
|
||||
exists(LoadInstruction load, PointerArithmeticInstruction pai |
|
||||
pai = load.getSourceAddress() and
|
||||
pai.getLeftOperand() = n.getOperand() and
|
||||
n.getIndirectionIndex() = 1 and
|
||||
n.hasOperandAndIndirectionIndex(pai.getLeftOperand(), 1) and
|
||||
e = load.getConvertedResultExpression()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class FieldFlowPropertyProvider extends IRPropertyProvider {
|
||||
override string getOperandProperty(Operand operand, string key) {
|
||||
exists(PostFieldUpdateNode pfun, Content content |
|
||||
key = "store " + content.toString() and
|
||||
operand = pfun.getPreUpdateNode().(IndirectOperand).getOperand() and
|
||||
pfun.getPreUpdateNode().(IndirectOperand).hasOperandAndIndirectionIndex(operand, _) and
|
||||
result =
|
||||
strictconcat(string element, Node node |
|
||||
storeStep(node, content, pfun) and
|
||||
@@ -25,7 +25,7 @@ class FieldFlowPropertyProvider extends IRPropertyProvider {
|
||||
or
|
||||
exists(Node node2, Content content |
|
||||
key = "read " + content.toString() and
|
||||
operand = node2.(IndirectOperand).getOperand() and
|
||||
node2.(IndirectOperand).hasOperandAndIndirectionIndex(operand, _) and
|
||||
result =
|
||||
strictconcat(string element, Node node1 |
|
||||
readStep(node1, content, node2) and
|
||||
|
||||
@@ -18,9 +18,12 @@ private string stars(int k) {
|
||||
}
|
||||
|
||||
string starsForNode(Node node) {
|
||||
result = stars(node.(IndirectInstruction).getIndirectionIndex())
|
||||
or
|
||||
result = stars(node.(IndirectOperand).getIndirectionIndex())
|
||||
exists(int indirectionIndex |
|
||||
node.(IndirectInstruction).hasInstructionAndIndirectionIndex(_, indirectionIndex) or
|
||||
node.(IndirectOperand).hasOperandAndIndirectionIndex(_, indirectionIndex)
|
||||
|
|
||||
result = stars(indirectionIndex)
|
||||
)
|
||||
or
|
||||
not node instanceof IndirectInstruction and
|
||||
not node instanceof IndirectOperand and
|
||||
|
||||
@@ -117,6 +117,16 @@ private int countIndirections(Type t) {
|
||||
else (
|
||||
result = any(Indirection ind | ind.getType() = t).getNumberOfIndirections()
|
||||
or
|
||||
// If there is an indirection for the type, but we cannot count the number of indirections
|
||||
// it means we couldn't reach a non-indirection type by stripping off indirections. This
|
||||
// can occur if an iterator specifies itself as the value type. In this case we default to
|
||||
// 1 indirection fore the type.
|
||||
exists(Indirection ind |
|
||||
ind.getType() = t and
|
||||
not exists(ind.getNumberOfIndirections()) and
|
||||
result = 1
|
||||
)
|
||||
or
|
||||
not exists(Indirection ind | ind.getType() = t) and
|
||||
result = 0
|
||||
)
|
||||
@@ -263,7 +273,7 @@ private module IteratorIndirections {
|
||||
// Taint through `operator+=` and `operator-=` on iterators.
|
||||
call.getStaticCallTarget() instanceof Iterator::IteratorAssignArithmeticOperator and
|
||||
node2.(IndirectArgumentOutNode).getPreUpdateNode() = node1 and
|
||||
node1.(IndirectOperand).getOperand() = call.getArgumentOperand(0) and
|
||||
node1.(IndirectOperand).hasOperandAndIndirectionIndex(call.getArgumentOperand(0), _) and
|
||||
node1.getType().getUnspecifiedType() = this
|
||||
)
|
||||
}
|
||||
@@ -796,7 +806,7 @@ private module Cached {
|
||||
address.getDef() = instr and
|
||||
isDereference(load, address) and
|
||||
isUseImpl(address, _, indirectionIndex - 1) and
|
||||
result = instr
|
||||
result = load
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
|
||||
FunctionInput modelIn, FunctionOutput modelOut
|
||||
|
|
||||
indirectArgument = callInput(call, modelIn) and
|
||||
indirectArgument.getAddressOperand() = nodeIn.asOperand() and
|
||||
indirectArgument.hasAddressOperandAndIndirectionIndex(nodeIn.asOperand(), _) and
|
||||
call.getStaticCallTarget() = func and
|
||||
(
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
|
||||
@@ -108,7 +108,7 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
// these may do only a partial copy of the input buffer to the output
|
||||
// buffer
|
||||
exists(this.getParamSize()) and
|
||||
input.isParameter(this.getParamSrc()) and
|
||||
input.isParameterDeref(this.getParamSrc()) and
|
||||
(
|
||||
output.isParameterDeref(this.getParamDest()) or
|
||||
output.isReturnValueDeref()
|
||||
|
||||
@@ -70,6 +70,27 @@ predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionP
|
||||
// Conservatively assume that every edge is a back edge if we don't have dominance information.
|
||||
(
|
||||
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
|
||||
irreducibleSccEdge(phi.getBasicBlock(), edge.getOrigBlock()) or
|
||||
not edge.getOrigBlock().hasDominanceInformation()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the edge from b1 to b2 is part of a multiple-entry cycle in an irreducible control flow
|
||||
* graph.
|
||||
*
|
||||
* An ireducible control flow graph is one where the usual dominance-based back edge detection does
|
||||
* not work, because there is a cycle with multiple entry points, meaning there are
|
||||
* mutually-reachable basic blocks where neither dominates the other. For such a graph, we first
|
||||
* remove all detectable back-edges using the normal condition that the predecessor block is
|
||||
* dominated by the successor block, then mark all edges in a cycle in the resulting graph as back
|
||||
* edges.
|
||||
*/
|
||||
private predicate irreducibleSccEdge(SemBasicBlock b1, SemBasicBlock b2) {
|
||||
trimmedEdge(b1, b2) and trimmedEdge+(b2, b1)
|
||||
}
|
||||
|
||||
private predicate trimmedEdge(SemBasicBlock pred, SemBasicBlock succ) {
|
||||
pred.getASuccessor() = succ and
|
||||
not succ.bbDominates(pred)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.6.3
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `cpp/overrun-write`, to detect buffer overflows in C-style functions that manipulate buffers.
|
||||
|
||||
## 0.6.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 9.3
|
||||
* @precision medium
|
||||
* @precision low
|
||||
* @id cpp/overrun-write
|
||||
* @tags reliability
|
||||
* security
|
||||
|
||||
5
cpp/ql/src/change-notes/released/0.6.3.md
Normal file
5
cpp/ql/src/change-notes/released/0.6.3.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.6.3
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `cpp/overrun-write`, to detect buffer overflows in C-style functions that manipulate buffers.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.2
|
||||
lastReleaseVersion: 0.6.3
|
||||
|
||||
@@ -14,7 +14,7 @@ import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysi
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import FieldAddressToDerefFlow::PathGraph
|
||||
import ArrayAddressToDerefFlow::PathGraph
|
||||
|
||||
pragma[nomagic]
|
||||
Instruction getABoundIn(SemBound b, IRFunction func) {
|
||||
@@ -78,28 +78,45 @@ predicate isInvalidPointerDerefSink2(DataFlow::Node sink, Instruction i, string
|
||||
)
|
||||
}
|
||||
|
||||
predicate arrayTypeCand(ArrayType arrayType) {
|
||||
any(Variable v).getUnspecifiedType() = arrayType and
|
||||
exists(arrayType.getArraySize())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate arrayTypeHasSizes(ArrayType arr, int baseTypeSize, int arraySize) {
|
||||
arrayTypeCand(arr) and
|
||||
arr.getBaseType().getSize() = baseTypeSize and
|
||||
arr.getArraySize() = arraySize
|
||||
}
|
||||
|
||||
predicate pointerArithOverflow0(
|
||||
PointerArithmeticInstruction pai, Field f, int size, int bound, int delta
|
||||
) {
|
||||
not f.getNamespace() instanceof StdNamespace and
|
||||
arrayTypeHasSizes(f.getUnspecifiedType(), pai.getElementSize(), size) and
|
||||
semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), bound, true, _) and
|
||||
delta = bound - size and
|
||||
delta >= 0 and
|
||||
size != 0 and
|
||||
size != 1
|
||||
bindingset[pai]
|
||||
pragma[inline_late]
|
||||
predicate constantUpperBounded(PointerArithmeticInstruction pai, int delta) {
|
||||
semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), delta, true, _)
|
||||
}
|
||||
|
||||
bindingset[pai, size]
|
||||
predicate pointerArithOverflow0Impl(PointerArithmeticInstruction pai, int size, int delta) {
|
||||
exists(int bound |
|
||||
constantUpperBounded(pai, bound) and
|
||||
delta = bound - size and
|
||||
delta >= 0 and
|
||||
size != 0 and
|
||||
size != 1
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate pointerArithOverflow0(PointerArithmeticInstruction pai, int delta) {
|
||||
exists(int size |
|
||||
arrayTypeHasSizes(_, pai.getElementSize(), size) and
|
||||
pointerArithOverflow0Impl(pai, size, delta)
|
||||
)
|
||||
}
|
||||
|
||||
module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
pointerArithOverflow0(source.asInstruction(), _, _, _, _)
|
||||
}
|
||||
predicate isSource(DataFlow::Node source) { pointerArithOverflow0(source.asInstruction(), _) }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
|
||||
|
||||
@@ -110,25 +127,38 @@ module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig {
|
||||
|
||||
module PointerArithmeticToDerefFlow = DataFlow::Global<PointerArithmeticToDerefConfig>;
|
||||
|
||||
predicate pointerArithOverflow(
|
||||
PointerArithmeticInstruction pai, Field f, int size, int bound, int delta
|
||||
) {
|
||||
pointerArithOverflow0(pai, f, size, bound, delta) and
|
||||
predicate pointerArithOverflow(PointerArithmeticInstruction pai, int delta) {
|
||||
pointerArithOverflow0(pai, delta) and
|
||||
PointerArithmeticToDerefFlow::flow(DataFlow::instructionNode(pai), _)
|
||||
}
|
||||
|
||||
module FieldAddressToDerefConfig implements DataFlow::StateConfigSig {
|
||||
bindingset[v]
|
||||
predicate finalPointerArithOverflow(Variable v, PointerArithmeticInstruction pai, int delta) {
|
||||
exists(int size |
|
||||
arrayTypeHasSizes(pragma[only_bind_out](v.getUnspecifiedType()), pai.getElementSize(), size) and
|
||||
pointerArithOverflow0Impl(pai, size, delta)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSourceImpl(DataFlow::Node source, Variable v) {
|
||||
(
|
||||
source.asInstruction().(FieldAddressInstruction).getField() = v
|
||||
or
|
||||
source.asInstruction().(VariableAddressInstruction).getAstVariable() = v
|
||||
) and
|
||||
arrayTypeCand(v.getUnspecifiedType())
|
||||
}
|
||||
|
||||
module ArrayAddressToDerefConfig implements DataFlow::StateConfigSig {
|
||||
newtype FlowState =
|
||||
additional TArray(Field f) { pointerArithOverflow(_, f, _, _, _) } or
|
||||
additional TArray() or
|
||||
additional TOverflowArithmetic(PointerArithmeticInstruction pai) {
|
||||
pointerArithOverflow(pai, _, _, _, _)
|
||||
pointerArithOverflow(pai, _)
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
exists(Field f |
|
||||
source.asInstruction().(FieldAddressInstruction).getField() = f and
|
||||
state = TArray(f)
|
||||
)
|
||||
isSourceImpl(source, _) and
|
||||
state = TArray()
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
@@ -147,27 +177,27 @@ module FieldAddressToDerefConfig implements DataFlow::StateConfigSig {
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
exists(PointerArithmeticInstruction pai, Field f |
|
||||
state1 = TArray(f) and
|
||||
exists(PointerArithmeticInstruction pai |
|
||||
state1 = TArray() and
|
||||
state2 = TOverflowArithmetic(pai) and
|
||||
pai.getLeft() = node1.asInstruction() and
|
||||
node2.asInstruction() = pai and
|
||||
pointerArithOverflow(pai, f, _, _, _)
|
||||
pointerArithOverflow(pai, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module FieldAddressToDerefFlow = DataFlow::GlobalWithState<FieldAddressToDerefConfig>;
|
||||
module ArrayAddressToDerefFlow = DataFlow::GlobalWithState<ArrayAddressToDerefConfig>;
|
||||
|
||||
from
|
||||
Field f, FieldAddressToDerefFlow::PathNode source, PointerArithmeticInstruction pai,
|
||||
FieldAddressToDerefFlow::PathNode sink, Instruction deref, string operation, int delta
|
||||
Variable v, ArrayAddressToDerefFlow::PathNode source, PointerArithmeticInstruction pai,
|
||||
ArrayAddressToDerefFlow::PathNode sink, Instruction deref, string operation, int delta
|
||||
where
|
||||
FieldAddressToDerefFlow::flowPath(source, sink) and
|
||||
ArrayAddressToDerefFlow::flowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
|
||||
isInvalidPointerDerefSink2(sink.getNode(), deref, operation) and
|
||||
source.getState() = FieldAddressToDerefConfig::TArray(f) and
|
||||
sink.getState() = FieldAddressToDerefConfig::TOverflowArithmetic(pai) and
|
||||
pointerArithOverflow(pai, f, _, _, delta)
|
||||
pragma[only_bind_out](sink.getState()) = ArrayAddressToDerefConfig::TOverflowArithmetic(pai) and
|
||||
isSourceImpl(source.getNode(), v) and
|
||||
finalPointerArithOverflow(v, pai, delta)
|
||||
select pai, source, sink,
|
||||
"This pointer arithmetic may have an off-by-" + (delta + 1) +
|
||||
" error allowing it to overrun $@ at this $@.", f, f.getName(), deref, operation
|
||||
" error allowing it to overrun $@ at this $@.", v, v.getName(), deref, operation
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.6.3-dev
|
||||
version: 0.6.4-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -11,6 +11,13 @@ edges
|
||||
| test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... |
|
||||
| test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p |
|
||||
| test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf |
|
||||
| test.cpp:85:34:85:36 | buf | test.cpp:87:5:87:31 | access to array |
|
||||
| test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | access to array |
|
||||
| test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array |
|
||||
| test.cpp:134:25:134:27 | arr | test.cpp:136:9:136:16 | ... += ... |
|
||||
| test.cpp:136:9:136:16 | ... += ... | test.cpp:138:13:138:15 | arr |
|
||||
| test.cpp:143:18:143:21 | asdf | test.cpp:134:25:134:27 | arr |
|
||||
| test.cpp:143:18:143:21 | asdf | test.cpp:143:18:143:21 | asdf |
|
||||
nodes
|
||||
| test.cpp:35:5:35:22 | access to array | semmle.label | access to array |
|
||||
| test.cpp:35:10:35:12 | buf | semmle.label | buf |
|
||||
@@ -33,6 +40,16 @@ nodes
|
||||
| test.cpp:77:32:77:34 | buf | semmle.label | buf |
|
||||
| test.cpp:79:27:79:34 | buf | semmle.label | buf |
|
||||
| test.cpp:79:32:79:34 | buf | semmle.label | buf |
|
||||
| test.cpp:85:34:85:36 | buf | semmle.label | buf |
|
||||
| test.cpp:87:5:87:31 | access to array | semmle.label | access to array |
|
||||
| test.cpp:88:5:88:27 | access to array | semmle.label | access to array |
|
||||
| test.cpp:128:9:128:11 | arr | semmle.label | arr |
|
||||
| test.cpp:128:9:128:14 | access to array | semmle.label | access to array |
|
||||
| test.cpp:134:25:134:27 | arr | semmle.label | arr |
|
||||
| test.cpp:136:9:136:16 | ... += ... | semmle.label | ... += ... |
|
||||
| test.cpp:138:13:138:15 | arr | semmle.label | arr |
|
||||
| test.cpp:143:18:143:21 | asdf | semmle.label | asdf |
|
||||
| test.cpp:143:18:143:21 | asdf | semmle.label | asdf |
|
||||
subpaths
|
||||
#select
|
||||
| test.cpp:35:5:35:22 | PointerAdd: access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write |
|
||||
@@ -44,3 +61,5 @@ subpaths
|
||||
| test.cpp:61:9:61:19 | PointerAdd: access to array | test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:61:9:61:23 | Store: ... = ... | write |
|
||||
| test.cpp:72:5:72:15 | PointerAdd: access to array | test.cpp:79:32:79:34 | buf | test.cpp:72:5:72:15 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:72:5:72:19 | Store: ... = ... | write |
|
||||
| test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write |
|
||||
| test.cpp:128:9:128:14 | PointerAdd: access to array | test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:125:11:125:13 | arr | arr | test.cpp:128:9:128:18 | Store: ... = ... | write |
|
||||
| test.cpp:136:9:136:16 | PointerAdd: ... += ... | test.cpp:143:18:143:21 | asdf | test.cpp:138:13:138:15 | arr | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:142:10:142:13 | asdf | asdf | test.cpp:138:12:138:15 | Load: * ... | read |
|
||||
|
||||
@@ -85,7 +85,7 @@ void testCharIndex(BigArray *arr) {
|
||||
char *charBuf = (char*) arr->buf;
|
||||
|
||||
charBuf[MAX_SIZE_BYTES - 1] = 0; // GOOD
|
||||
charBuf[MAX_SIZE_BYTES] = 0; // BAD [FALSE NEGATIVE]
|
||||
charBuf[MAX_SIZE_BYTES] = 0; // BAD
|
||||
}
|
||||
|
||||
void testEqRefinement() {
|
||||
@@ -120,3 +120,25 @@ void testEqRefinement2() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testStackAllocated() {
|
||||
char *arr[MAX_SIZE];
|
||||
|
||||
for(int i = 0; i <= MAX_SIZE; i++) {
|
||||
arr[i] = 0; // BAD
|
||||
}
|
||||
}
|
||||
|
||||
int strncmp(const char*, const char*, int);
|
||||
|
||||
char testStrncmp2(char *arr) {
|
||||
if(strncmp(arr, "<test>", 6) == 0) {
|
||||
arr += 6;
|
||||
}
|
||||
return *arr; // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
void testStrncmp1() {
|
||||
char asdf[5];
|
||||
testStrncmp2(asdf);
|
||||
}
|
||||
|
||||
@@ -67,6 +67,8 @@ postWithInFlow
|
||||
| ref.cpp:109:9:109:11 | val [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| ref.cpp:113:11:113:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| ref.cpp:115:11:115:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| self_parameter_flow.cpp:3:4:3:5 | ps [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| self_parameter_flow.cpp:8:9:8:9 | s [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:91:3:91:9 | source1 [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:115:3:115:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:115:4:115:6 | out [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
@@ -128,6 +130,10 @@ postWithInFlow
|
||||
| test.cpp:690:3:690:3 | s [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:694:4:694:6 | buf [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:704:23:704:25 | buf [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:715:25:715:25 | c [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:728:3:728:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:728:4:728:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:734:41:734:41 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#include "../../../include/iterator.h"
|
||||
int source();
|
||||
|
||||
template<typename T>
|
||||
void sink(T);
|
||||
|
||||
template<> struct std::iterator_traits<unsigned long>
|
||||
{ // get traits from integer type
|
||||
typedef std::input_iterator_tag iterator_category;
|
||||
typedef unsigned long value_type;
|
||||
typedef unsigned long difference_type;
|
||||
typedef unsigned long distance_type;
|
||||
typedef unsigned long * pointer;
|
||||
typedef unsigned long& reference;
|
||||
};
|
||||
|
||||
|
||||
int test() {
|
||||
unsigned long x = source();
|
||||
sink(x); // $ ast ir
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
void incr(unsigned char **ps) // $ ast-def=ps ir-def=*ps ir-def=**ps
|
||||
{
|
||||
*ps += 1;
|
||||
}
|
||||
|
||||
void callincr(unsigned char *s) // $ ast-def=s
|
||||
{
|
||||
incr(&s);
|
||||
}
|
||||
|
||||
void test(unsigned char *s) // $ ast-def=s
|
||||
{
|
||||
callincr(s); // $ flow
|
||||
}
|
||||
@@ -702,4 +702,35 @@ void call_increment_buf(int** buf) { // $ ast-def=buf
|
||||
void test_conflation_regression(int* source) { // $ ast-def=source
|
||||
int* buf = source;
|
||||
call_increment_buf(&buf);
|
||||
}
|
||||
}
|
||||
|
||||
void write_to_star_star_p(unsigned char **p) // $ ast-def=p ir-def=**p ir-def=*p
|
||||
{
|
||||
**p = 0;
|
||||
}
|
||||
|
||||
void write_to_star_buf(unsigned char *buf) // $ ast-def=buf
|
||||
{
|
||||
unsigned char *c = buf;
|
||||
write_to_star_star_p(&c);
|
||||
}
|
||||
|
||||
void test_write_to_star_buf(unsigned char *source) // $ ast-def=source
|
||||
{
|
||||
write_to_star_buf(source);
|
||||
sink(*source); // clean
|
||||
}
|
||||
|
||||
void does_not_write_source_to_dereference(int *p) // $ ast-def=p ir-def=*p
|
||||
{
|
||||
int x = source();
|
||||
p = &x;
|
||||
*p = 42;
|
||||
}
|
||||
|
||||
void test_does_not_write_source_to_dereference()
|
||||
{
|
||||
int x;
|
||||
does_not_write_source_to_dereference(&x);
|
||||
sink(x); // $ ast,ir=733:7 SPURIOUS: ast,ir=726:11
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
@@ -0,0 +1,34 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
module TestConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.getLocation().getFile().getBaseName() = "self_parameter_flow.cpp" and
|
||||
source.asIndirectArgument() =
|
||||
any(Call call | call.getTarget().hasName("callincr")).getAnArgument()
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asDefiningArgument() =
|
||||
any(Call call | call.getTarget().hasName("callincr")).getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
import DataFlow::Global<TestConfig>
|
||||
|
||||
module TestSelfParameterFlow implements TestSig {
|
||||
string getARelevantTag() { result = "flow" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(DataFlow::Node sink |
|
||||
flowTo(sink) and
|
||||
location = sink.getLocation() and
|
||||
element = sink.toString() and
|
||||
tag = "flow" and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<TestSelfParameterFlow>
|
||||
@@ -42,3 +42,5 @@
|
||||
| test.cpp:551:9:551:9 | y | test.cpp:552:28:552:28 | y |
|
||||
| test.cpp:595:8:595:9 | xs | test.cpp:596:3:596:4 | xs |
|
||||
| test.cpp:595:8:595:9 | xs | test.cpp:597:9:597:10 | xs |
|
||||
| test.cpp:733:7:733:7 | x | test.cpp:734:41:734:41 | x |
|
||||
| test.cpp:733:7:733:7 | x | test.cpp:735:8:735:8 | x |
|
||||
|
||||
@@ -6591,6 +6591,20 @@
|
||||
| taint.cpp:702:4:702:6 | ... ++ | taint.cpp:703:8:703:8 | p | TAINT |
|
||||
| taint.cpp:702:10:702:11 | * ... | taint.cpp:702:3:702:11 | ... = ... | |
|
||||
| taint.cpp:702:11:702:11 | s | taint.cpp:702:10:702:11 | * ... | TAINT |
|
||||
| taint.cpp:709:25:709:25 | d | taint.cpp:709:25:709:25 | d | |
|
||||
| taint.cpp:709:25:709:25 | d | taint.cpp:711:10:711:10 | d | |
|
||||
| taint.cpp:709:25:709:25 | d | taint.cpp:712:7:712:7 | d | |
|
||||
| taint.cpp:709:34:709:34 | s | taint.cpp:709:34:709:34 | s | |
|
||||
| taint.cpp:709:34:709:34 | s | taint.cpp:710:18:710:18 | s | |
|
||||
| taint.cpp:709:34:709:34 | s | taint.cpp:711:13:711:13 | s | |
|
||||
| taint.cpp:710:18:710:18 | ref arg s | taint.cpp:709:34:709:34 | s | |
|
||||
| taint.cpp:710:18:710:18 | ref arg s | taint.cpp:711:13:711:13 | s | |
|
||||
| taint.cpp:711:10:711:10 | d | taint.cpp:711:2:711:8 | call to strncpy | |
|
||||
| taint.cpp:711:10:711:10 | ref arg d | taint.cpp:709:25:709:25 | d | |
|
||||
| taint.cpp:711:10:711:10 | ref arg d | taint.cpp:712:7:712:7 | d | |
|
||||
| taint.cpp:711:13:711:13 | s | taint.cpp:711:2:711:8 | call to strncpy | TAINT |
|
||||
| taint.cpp:711:13:711:13 | s | taint.cpp:711:10:711:10 | ref arg d | TAINT |
|
||||
| taint.cpp:712:7:712:7 | ref arg d | taint.cpp:709:25:709:25 | d | |
|
||||
| vector.cpp:16:43:16:49 | source1 | vector.cpp:17:26:17:32 | source1 | |
|
||||
| vector.cpp:16:43:16:49 | source1 | vector.cpp:31:38:31:44 | source1 | |
|
||||
| vector.cpp:17:21:17:33 | call to vector | vector.cpp:19:14:19:14 | v | |
|
||||
|
||||
@@ -702,4 +702,12 @@ namespace strings {
|
||||
*p++ = *s;
|
||||
sink(p); // $ ast ir
|
||||
}
|
||||
}
|
||||
|
||||
char * strncpy (char *, const char *, unsigned long);
|
||||
|
||||
void test_strncpy(char* d, char* s) {
|
||||
argument_source(s);
|
||||
strncpy(d, s, 16);
|
||||
sink(d); // $ ast ir
|
||||
}
|
||||
@@ -70,3 +70,27 @@ int f4(int x) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No interesting ranges to check here - this irreducible CFG caused an infinite loop due to back edge detection
|
||||
void gotoLoop(bool b1, bool b2)
|
||||
{
|
||||
int j;
|
||||
|
||||
if (b1)
|
||||
return;
|
||||
|
||||
if (!b2)
|
||||
{
|
||||
for (j = 0; j < 10; ++j)
|
||||
{
|
||||
goto main_decode_loop;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (j = 0; j < 10; ++j)
|
||||
{
|
||||
main_decode_loop:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,19 @@ edges
|
||||
| overflowdestination.cpp:23:45:23:48 | argv indirection | overflowdestination.cpp:30:17:30:20 | arg1 indirection |
|
||||
| overflowdestination.cpp:23:45:23:48 | argv indirection | overflowdestination.cpp:30:17:30:20 | arg1 indirection |
|
||||
| overflowdestination.cpp:43:8:43:10 | fgets output argument | overflowdestination.cpp:46:15:46:17 | src indirection |
|
||||
| overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:53:9:53:12 | memcpy output argument |
|
||||
| overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:53:15:53:17 | src indirection |
|
||||
| overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:53:15:53:17 | src indirection |
|
||||
| overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:54:9:54:12 | memcpy output argument |
|
||||
| overflowdestination.cpp:53:9:53:12 | memcpy output argument | overflowdestination.cpp:54:9:54:12 | memcpy output argument |
|
||||
| overflowdestination.cpp:54:9:54:12 | memcpy output argument | overflowdestination.cpp:54:9:54:12 | memcpy output argument |
|
||||
| overflowdestination.cpp:57:52:57:54 | src indirection | overflowdestination.cpp:64:16:64:19 | src2 indirection |
|
||||
| overflowdestination.cpp:57:52:57:54 | src indirection | overflowdestination.cpp:64:16:64:19 | src2 indirection |
|
||||
| overflowdestination.cpp:73:8:73:10 | fgets output argument | overflowdestination.cpp:75:30:75:32 | src indirection |
|
||||
| overflowdestination.cpp:73:8:73:10 | fgets output argument | overflowdestination.cpp:76:30:76:32 | src indirection |
|
||||
| overflowdestination.cpp:75:30:75:32 | overflowdest_test2 output argument | overflowdestination.cpp:76:30:76:32 | src indirection |
|
||||
| overflowdestination.cpp:75:30:75:32 | src indirection | overflowdestination.cpp:50:52:50:54 | src indirection |
|
||||
| overflowdestination.cpp:75:30:75:32 | src indirection | overflowdestination.cpp:75:30:75:32 | overflowdest_test2 output argument |
|
||||
| overflowdestination.cpp:76:30:76:32 | src indirection | overflowdestination.cpp:57:52:57:54 | src indirection |
|
||||
nodes
|
||||
| main.cpp:6:27:6:30 | argv indirection | semmle.label | argv indirection |
|
||||
@@ -28,15 +34,20 @@ nodes
|
||||
| overflowdestination.cpp:43:8:43:10 | fgets output argument | semmle.label | fgets output argument |
|
||||
| overflowdestination.cpp:46:15:46:17 | src indirection | semmle.label | src indirection |
|
||||
| overflowdestination.cpp:50:52:50:54 | src indirection | semmle.label | src indirection |
|
||||
| overflowdestination.cpp:53:9:53:12 | memcpy output argument | semmle.label | memcpy output argument |
|
||||
| overflowdestination.cpp:53:15:53:17 | src indirection | semmle.label | src indirection |
|
||||
| overflowdestination.cpp:53:15:53:17 | src indirection | semmle.label | src indirection |
|
||||
| overflowdestination.cpp:54:9:54:12 | memcpy output argument | semmle.label | memcpy output argument |
|
||||
| overflowdestination.cpp:57:52:57:54 | src indirection | semmle.label | src indirection |
|
||||
| overflowdestination.cpp:64:16:64:19 | src2 indirection | semmle.label | src2 indirection |
|
||||
| overflowdestination.cpp:64:16:64:19 | src2 indirection | semmle.label | src2 indirection |
|
||||
| overflowdestination.cpp:73:8:73:10 | fgets output argument | semmle.label | fgets output argument |
|
||||
| overflowdestination.cpp:75:30:75:32 | overflowdest_test2 output argument | semmle.label | overflowdest_test2 output argument |
|
||||
| overflowdestination.cpp:75:30:75:32 | src indirection | semmle.label | src indirection |
|
||||
| overflowdestination.cpp:76:30:76:32 | src indirection | semmle.label | src indirection |
|
||||
subpaths
|
||||
| overflowdestination.cpp:75:30:75:32 | src indirection | overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:53:9:53:12 | memcpy output argument | overflowdestination.cpp:75:30:75:32 | overflowdest_test2 output argument |
|
||||
| overflowdestination.cpp:75:30:75:32 | src indirection | overflowdestination.cpp:50:52:50:54 | src indirection | overflowdestination.cpp:54:9:54:12 | memcpy output argument | overflowdestination.cpp:75:30:75:32 | overflowdest_test2 output argument |
|
||||
#select
|
||||
| overflowdestination.cpp:30:2:30:8 | call to strncpy | main.cpp:6:27:6:30 | argv indirection | overflowdestination.cpp:30:17:30:20 | arg1 indirection | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
|
||||
| overflowdestination.cpp:30:2:30:8 | call to strncpy | main.cpp:6:27:6:30 | argv indirection | overflowdestination.cpp:30:17:30:20 | arg1 indirection | To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size. |
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.5.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.5.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.5.3
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.2
|
||||
lastReleaseVersion: 1.5.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.5.3-dev
|
||||
version: 1.5.4-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.5.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.5.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.5.3
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.2
|
||||
lastReleaseVersion: 1.5.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.5.3-dev
|
||||
version: 1.5.4-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -17,3 +17,8 @@ query predicate missingLocation(Element e) {
|
||||
not exists(TupleType t | e = t or e = t.getAField()) and
|
||||
not exists(e.getLocation())
|
||||
}
|
||||
|
||||
query predicate multipleToString(Element e, string s) {
|
||||
s = strictconcat(e.toString(), ",") and
|
||||
strictcount(e.toString()) > 1
|
||||
}
|
||||
|
||||
@@ -62,3 +62,8 @@ query predicate preBasicBlockConsistency(ControlFlowElement cfe1, ControlFlowEle
|
||||
bbIntraSuccInconsistency(cfe1, cfe2) and
|
||||
s = "intra succ inconsistency"
|
||||
}
|
||||
|
||||
query predicate multipleToString(Node n, string s) {
|
||||
s = strictconcat(n.toString(), ",") and
|
||||
strictcount(n.toString()) > 1
|
||||
}
|
||||
|
||||
@@ -74,3 +74,8 @@ private class MyConsistencyConfiguration extends ConsistencyConfiguration {
|
||||
|
||||
override predicate identityLocalStepExclude(Node n) { none() }
|
||||
}
|
||||
|
||||
query predicate multipleToString(Node n, string s) {
|
||||
s = strictconcat(n.toString(), ",") and
|
||||
strictcount(n.toString()) > 1
|
||||
}
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
## 0.6.3
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* The extractor has been changed to run after the traced compiler call. This allows inspecting compiler generated files, such as the output of source generators. With this change, `.cshtml` files and their generated `.cshtml.g.cs` counterparts are extracted on dotnet 6 and above.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* C#: Analysis of the `dotnet test` command supplied with a `dll` or `exe` file as argument no longer fails due to the addition of an erroneous `-p:SharedCompilation=false` argument.
|
||||
* Deleted the deprecated `WebConfigXML`, `ConfigurationXMLElement`, `LocationXMLElement`, `SystemWebXMLElement`, `SystemWebServerXMLElement`, `CustomErrorsXMLElement`, and `HttpRuntimeXMLElement` classes from `WebConfig.qll`. The non-deprecated names with PascalCased Xml suffixes should be used instead.
|
||||
* Deleted the deprecated `Record` class from both `Types.qll` and `Type.qll`.
|
||||
* Deleted the deprecated `StructuralComparisonConfiguration` class from `StructuralComparison.qll`, use `sameGvn` instead.
|
||||
* Deleted the deprecated `isParameterOf` predicate from the `ParameterNode` class.
|
||||
* Deleted the deprecated `SafeExternalAPICallable`, `ExternalAPIDataNode`, `UntrustedDataToExternalAPIConfig`, `UntrustedExternalAPIDataNode`, and `ExternalAPIUsedWithUntrustedData` classes from `ExternalAPIsQuery.qll`. The non-deprecated names with PascalCased Api suffixes should be used instead.
|
||||
* Updated the following C# sink kind names. Any custom data extensions that use these sink kinds will need to be updated accordingly in order to continue working.
|
||||
* `code` to `code-injection`
|
||||
* `sql` to `sql-injection`
|
||||
* `html` to `html-injection`
|
||||
* `xss` to `js-injection`
|
||||
* `remote` to `file-content-store`
|
||||
|
||||
## 0.6.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
4
csharp/ql/lib/change-notes/2023-05-09-models-as-data.md
Normal file
4
csharp/ql/lib/change-notes/2023-05-09-models-as-data.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Additional support for `command-injection`, `ldap-injection`, `log-injection`, and `url-redirection` sink kinds for Models as Data.
|
||||
20
csharp/ql/lib/change-notes/released/0.6.3.md
Normal file
20
csharp/ql/lib/change-notes/released/0.6.3.md
Normal file
@@ -0,0 +1,20 @@
|
||||
## 0.6.3
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* The extractor has been changed to run after the traced compiler call. This allows inspecting compiler generated files, such as the output of source generators. With this change, `.cshtml` files and their generated `.cshtml.g.cs` counterparts are extracted on dotnet 6 and above.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* C#: Analysis of the `dotnet test` command supplied with a `dll` or `exe` file as argument no longer fails due to the addition of an erroneous `-p:SharedCompilation=false` argument.
|
||||
* Deleted the deprecated `WebConfigXML`, `ConfigurationXMLElement`, `LocationXMLElement`, `SystemWebXMLElement`, `SystemWebServerXMLElement`, `CustomErrorsXMLElement`, and `HttpRuntimeXMLElement` classes from `WebConfig.qll`. The non-deprecated names with PascalCased Xml suffixes should be used instead.
|
||||
* Deleted the deprecated `Record` class from both `Types.qll` and `Type.qll`.
|
||||
* Deleted the deprecated `StructuralComparisonConfiguration` class from `StructuralComparison.qll`, use `sameGvn` instead.
|
||||
* Deleted the deprecated `isParameterOf` predicate from the `ParameterNode` class.
|
||||
* Deleted the deprecated `SafeExternalAPICallable`, `ExternalAPIDataNode`, `UntrustedDataToExternalAPIConfig`, `UntrustedExternalAPIDataNode`, and `ExternalAPIUsedWithUntrustedData` classes from `ExternalAPIsQuery.qll`. The non-deprecated names with PascalCased Api suffixes should be used instead.
|
||||
* Updated the following C# sink kind names. Any custom data extensions that use these sink kinds will need to be updated accordingly in order to continue working.
|
||||
* `code` to `code-injection`
|
||||
* `sql` to `sql-injection`
|
||||
* `html` to `html-injection`
|
||||
* `xss` to `js-injection`
|
||||
* `remote` to `file-content-store`
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.2
|
||||
lastReleaseVersion: 0.6.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 0.6.3-dev
|
||||
version: 0.6.4-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -6,6 +6,7 @@ import csharp
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.system.Diagnostics
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A source specific to command injection vulnerabilities.
|
||||
@@ -66,6 +67,11 @@ module CommandInjection = TaintTracking::Global<CommandInjectionConfig>;
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
/** Command Injection sinks defined through Models as Data. */
|
||||
private class ExternalCommandInjectionExprSink extends Sink {
|
||||
ExternalCommandInjectionExprSink() { sinkNode(this, "command-injection") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink in `System.Diagnostic.Process` or its related classes.
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,7 @@ private import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
private import semmle.code.csharp.frameworks.system.DirectoryServices
|
||||
private import semmle.code.csharp.frameworks.system.directoryservices.Protocols
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A data flow source for unvalidated user input that is used to construct LDAP queries.
|
||||
@@ -68,6 +69,11 @@ module LdapInjection = TaintTracking::Global<LdapInjectionConfig>;
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
/** LDAP sinks defined through Models as Data. */
|
||||
private class ExternalLdapExprSink extends Sink {
|
||||
ExternalLdapExprSink() { sinkNode(this, "ldap-injection") }
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument that sets the `Path` property of a `DirectoryEntry` object that is a sink for LDAP
|
||||
* injection.
|
||||
|
||||
@@ -8,6 +8,7 @@ private import semmle.code.csharp.frameworks.System
|
||||
private import semmle.code.csharp.frameworks.system.text.RegularExpressions
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
private import semmle.code.csharp.security.dataflow.flowsinks.ExternalLocationSink
|
||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A data flow source for untrusted user input used in log entries.
|
||||
@@ -72,6 +73,11 @@ private class LogForgingLogMessageSink extends Sink, LogMessageSink { }
|
||||
*/
|
||||
private class LogForgingTraceMessageSink extends Sink, TraceMessageSink { }
|
||||
|
||||
/** Log Forging sinks defined through Models as Data. */
|
||||
private class ExternalLoggingExprSink extends Sink {
|
||||
ExternalLoggingExprSink() { sinkNode(this, "log-injection") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to String replace or remove that is considered to sanitize replaced string.
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,7 @@ private import semmle.code.csharp.frameworks.system.Web
|
||||
private import semmle.code.csharp.frameworks.system.web.Mvc
|
||||
private import semmle.code.csharp.security.Sanitizers
|
||||
private import semmle.code.csharp.frameworks.microsoft.AspNetCore
|
||||
private import semmle.code.csharp.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A data flow source for unvalidated URL redirect vulnerabilities.
|
||||
@@ -70,6 +71,11 @@ module UrlRedirect = TaintTracking::Global<UrlRedirectConfig>;
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
/** URL Redirection sinks defined through Models as Data. */
|
||||
private class ExternalUrlRedirectExprSink extends Sink {
|
||||
ExternalUrlRedirectExprSink() { sinkNode(this, "url-redirection") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A URL argument to a call to `HttpResponse.Redirect()` or `Controller.Redirect()`, that is a
|
||||
* sink for URL redirects.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.6.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
csharp/ql/src/change-notes/released/0.6.3.md
Normal file
3
csharp/ql/src/change-notes/released/0.6.3.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.3
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.2
|
||||
lastReleaseVersion: 0.6.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 0.6.3-dev
|
||||
version: 0.6.4-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -1,2 +1,2 @@
|
||||
semmle-extractor-options: /r:System.ComponentModel.Primitives.dll /r:${testdir}/../../../resources/assemblies/System.Data.dll /r:System.Data.Common.dll
|
||||
semmle-extractor-options: /langversion:8.0
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/System.Data.SqlClient/4.8.3/System.Data.SqlClient.csproj
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
semmle-extractor-options: /r:System.Runtime.Extensions.dll /r:System.Diagnostics.TraceSource.dll
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -1,15 +1,4 @@
|
||||
namespace System
|
||||
{
|
||||
public interface IComparable
|
||||
{
|
||||
int CompareTo(object obj); // GOOD: the very definition of IComparable.CompareTo()
|
||||
}
|
||||
|
||||
public interface IComparable<in T>
|
||||
{
|
||||
int CompareTo(T other); // GOOD: the very definition of IComparable<T>.CompareTo()
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
class C1<T>
|
||||
{
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| IncorrectCompareToSignature.cs:16:16:16:24 | CompareTo | The parameter of this 'CompareTo' method is of type $@, but $@ does not implement 'IComparable<$@>'. | IncorrectCompareToSignature.cs:14:10:14:10 | T | T | IncorrectCompareToSignature.cs:14:7:14:11 | C1<> | C1<> | IncorrectCompareToSignature.cs:14:10:14:10 | T | T |
|
||||
| IncorrectCompareToSignature.cs:5:16:5:24 | CompareTo | The parameter of this 'CompareTo' method is of type $@, but $@ does not implement 'IComparable<$@>'. | IncorrectCompareToSignature.cs:3:10:3:10 | T | T | IncorrectCompareToSignature.cs:3:7:3:11 | C1<> | C1<> | IncorrectCompareToSignature.cs:3:10:3:10 | T | T |
|
||||
| IncorrectCompareToSignatureBad.cs:5:16:5:24 | CompareTo | The parameter of this 'CompareTo' method is of type $@, but $@ does not implement 'IComparable<$@>'. | IncorrectCompareToSignatureBad.cs:3:7:3:9 | Bad | Bad | IncorrectCompareToSignatureBad.cs:3:7:3:9 | Bad | Bad | IncorrectCompareToSignatureBad.cs:3:7:3:9 | Bad | Bad |
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -1 +1,2 @@
|
||||
semmle-extractor-options: /r:System.ComponentModel.Primitives.dll
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
semmle-extractor-options: /r:System.ComponentModel.Primitives.dll
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
|
||||
@@ -1 +1 @@
|
||||
semmle-extractor-options: --cil /langversion:8.0 /r:System.Xml.dll /r:System.Xml.ReaderWriter.dll /r:System.Private.Xml.dll /r:System.ComponentModel.Primitives.dll /r:System.IO.Compression.dll /r:System.Runtime.Extensions.dll
|
||||
semmle-extractor-options: --cil /r:System.Private.Xml.dll /r:System.IO.Compression.dll
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -1,11 +1,6 @@
|
||||
using System;
|
||||
class HashSet<T>
|
||||
{
|
||||
public bool Add(T t)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
|
||||
class C1
|
||||
{
|
||||
@@ -30,11 +25,6 @@ class C1
|
||||
}
|
||||
}
|
||||
|
||||
class StringBuilder
|
||||
{
|
||||
public StringBuilder Append(string s) { return this; }
|
||||
}
|
||||
|
||||
class C2
|
||||
{
|
||||
static void Main(string[] args)
|
||||
@@ -59,20 +49,6 @@ class C2
|
||||
}
|
||||
}
|
||||
|
||||
namespace System.IO
|
||||
{
|
||||
public abstract class Stream
|
||||
{
|
||||
public abstract int Read(byte[] buffer, int offset, int count);
|
||||
public virtual int ReadByte() { return 0; }
|
||||
}
|
||||
|
||||
public class MemoryStream : Stream
|
||||
{
|
||||
public override int Read(byte[] buffer, int offset, int count) { return 0; }
|
||||
}
|
||||
}
|
||||
|
||||
class C3
|
||||
{
|
||||
static void Main(string[] args)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
| UncheckedReturnValue.cs:29:9:29:31 | call to method Add | Result of call to 'Add' is ignored, but 90% of calls to this method have their result used. |
|
||||
| UncheckedReturnValue.cs:91:9:91:26 | call to method Read | Result of call to 'Read' is ignored, but should always be checked. |
|
||||
| UncheckedReturnValue.cs:92:9:92:20 | call to method ReadByte | Result of call to 'ReadByte' is ignored, but should always be checked. |
|
||||
| UncheckedReturnValue.cs:109:9:109:17 | call to method M1<Int32> | Result of call to 'M1<Int32>' is ignored, but 90% of calls to this method have their result used. |
|
||||
| UncheckedReturnValue.cs:130:9:130:21 | call to method M2<Decimal> | Result of call to 'M2<Decimal>' is ignored, but 90% of calls to this method have their result used. |
|
||||
| UncheckedReturnValue.cs:142:9:142:20 | call to method M3<C6> | Result of call to 'M3<C6>' is ignored, but 90% of calls to this method have their result used. |
|
||||
| UncheckedReturnValue.cs:24:9:24:31 | call to method Add | Result of call to 'Add' is ignored, but 90% of calls to this method have their result used. |
|
||||
| UncheckedReturnValue.cs:67:9:67:26 | call to method Read | Result of call to 'Read' is ignored, but should always be checked. |
|
||||
| UncheckedReturnValue.cs:68:9:68:20 | call to method ReadByte | Result of call to 'ReadByte' is ignored, but should always be checked. |
|
||||
| UncheckedReturnValue.cs:85:9:85:17 | call to method M1<Int32> | Result of call to 'M1<Int32>' is ignored, but 90% of calls to this method have their result used. |
|
||||
| UncheckedReturnValue.cs:106:9:106:21 | call to method M2<Decimal> | Result of call to 'M2<Decimal>' is ignored, but 90% of calls to this method have their result used. |
|
||||
| UncheckedReturnValue.cs:118:9:118:20 | call to method M3<C6> | Result of call to 'M3<C6>' is ignored, but 90% of calls to this method have their result used. |
|
||||
| UncheckedReturnValueBad.cs:29:9:29:20 | call to method DoPrint | Result of call to 'DoPrint' is ignored, but 90% of calls to this method have their result used. |
|
||||
| UncheckedReturnValueBad.cs:36:13:36:40 | call to method Read | Result of call to 'Read' is ignored, but should always be checked. |
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
2
csharp/ql/test/query-tests/AlertSuppression/options
Normal file
2
csharp/ql/test/query-tests/AlertSuppression/options
Normal file
@@ -0,0 +1,2 @@
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
@@ -153,6 +153,36 @@ For example, if you want to continue analyzing a set of repositories that had re
|
||||
|
||||
You can then insert the ``new-repo-list`` of repositories into your list of custom repository lists for easy access in the Variant Analysis Repositories panel.
|
||||
|
||||
Using GitHub code search to add repositories to a custom list
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can use code search directly in the CodeQL extension to add a subset of repositories from GitHub.com to a custom list.
|
||||
|
||||
.. pull-quote::
|
||||
|
||||
Note
|
||||
|
||||
This feature uses the legacy code search via the code search API. For more information on the syntax to use, see "`Searching code (legacy) <https://docs.github.com/en/search-github/searching-on-github/searching-code>`__."
|
||||
|
||||
For example, to add all repositories in the ``rails`` organization on GitHub, you can search ``org:rails``.
|
||||
|
||||
You can add a maximum of 1000 repositories to a custom list per search.
|
||||
|
||||
#. In the Variant Analysis Repositories panel, choose the list that you want to add repositories to. You can create a new list or choose an existing list that already contains repositories.
|
||||
|
||||
#. Right-click on the list you have chosen and then click **Add repositories with GitHub Code Search**.
|
||||
|
||||
#. In the pop-up that appears at the top of the application, under the search bar, select a language for your search from the choices in the dropdown.
|
||||
|
||||
.. image:: ../images/codeql-for-visual-studio-code/variant-analysis-code-search-language.png
|
||||
:alt: Screenshot of the search bar for using code search to add repositories to a custom list. The search bar asks you to choose a language for your search and has a dropdown list of languages to choose from.
|
||||
|
||||
#. In the search bar, type the search query that you want to use and press **Enter**.
|
||||
|
||||
You can view the progress of your search in the bottom right corner of the application in a box with the text "Searching for repositories...". If you click **Cancel**, no repositories will be added to your list. Once complete, you will see the resulting repositories appear in the dropdown under your custom list in the Variant Analysis Repositories panel.
|
||||
|
||||
Some of the resulting repositories will not have CodeQL databases and some may not allow access by the CodeQL extension for Visual Studio Code. When you run an analysis on the list, the Variant Analysis Results view will show you which repositories were analyzed, which denied access, and which had no CodeQL database.
|
||||
|
||||
Troubleshooting variant analysis
|
||||
--------------------------------
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -164,6 +164,38 @@ If the call resolves to a predicate without result, then the call is a formula.
|
||||
It is also possible to call a predicate with result. This kind of call is an
|
||||
expression in QL, instead of a formula. For more information, see ":ref:`calls-with-result`."
|
||||
|
||||
Member predicates only apply to members of a particular class and calls to
|
||||
member predicates have a receiver of a matching type. Syntactically, if a call
|
||||
contains a dot, then the expression before the dot specifies the receiver of
|
||||
the call. For instance, ``x`` is the receiver for the call ``x.isEven()``.
|
||||
|
||||
For calls to member predicates of the enclosing class on the member itself
|
||||
(i.e., the value of ``this``), the receiver may be omitted syntactically. In
|
||||
this case we say the call has an implicit this receiver. For instance, in the
|
||||
following example the ``isEven()`` call in ``isOdd()`` is a member predicate
|
||||
call with an implicit this receiver and the call is equivalent to
|
||||
``this.isEven()``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class OneTwoThree extends int {
|
||||
OneTwoThree() { this = 1 or this = 2 or this = 3 }
|
||||
|
||||
predicate isEven() { this = 2 }
|
||||
|
||||
predicate isOdd() { not isEven() }
|
||||
}
|
||||
|
||||
Use of implicit this receivers can make it harder to spot predicates that introduce
|
||||
cartesian products by failing to relate the implicit ``this`` variable with
|
||||
other variables, which can negatively affect query performance. For more
|
||||
information on cartesian products, see ":ref:`Troubleshooting query performance
|
||||
<troubleshooting-query-performance>`".
|
||||
|
||||
It is possible to enable warnings about implicit this receivers for `CodeQL packs
|
||||
<https://docs.github.com/en/code-security/codeql-cli/codeql-cli-reference/about-codeql-packs#warnonimplicitthis>`__
|
||||
through the ``warnOnImplicitThis`` property.
|
||||
|
||||
.. _parenthesized-formulas:
|
||||
|
||||
Parenthesized formulas
|
||||
|
||||
@@ -1318,15 +1318,15 @@ The type environment for the arguments is the same as for the call.
|
||||
|
||||
A valid call with results *resolves* to a set of predicates. The ways a call can resolve are as follows:
|
||||
|
||||
- If the call has no receiver and the predicate name is a simple identifier, then the predicate is resolved by looking up its name and arity in the visible member-predicate environment of the enclosing class.
|
||||
- If the call has no receiver and the predicate reference is a simple identifier, then the call is resolved by looking up the predicate reference and arity in the visible predicate environment of the enclosing class.
|
||||
|
||||
- If the call has no receiver and the predicate name is a simple identifier, then the predicate is resolved by looking up its name and arity in the visible predicate environment of the enclosing module.
|
||||
- If the call has no receiver and the predicate reference is a simple identifier, then the call is resolved by looking up the predicate reference and arity in the visible predicate environment of the enclosing module.
|
||||
|
||||
- If the call has no receiver and the predicate name is a selection identifier, then the qualifier is resolved as a module (see "`Module resolution <#module-resolution>`__"). The identifier is then resolved in the exported predicate environment of the qualifier module.
|
||||
- If the call has no receiver and the predicate reference is a selection identifier, then the qualifier is resolved as a module (see "`Module resolution <#module-resolution>`__") and the call is resolved by looking up the identifier in the exported predicate environment of the qualifier module.
|
||||
|
||||
- If the type of the receiver is the same as the enclosing class, the predicate is resolved by looking up its name and arity in the visible predicate environment of the class.
|
||||
- If the the call has a receiver and the type of the receiver is the same as the enclosing class, the call is resolved by looking up the predicate name and arity in the visible predicate environment of the enclosing class.
|
||||
|
||||
- If the type of the receiver is not the same as the enclosing class, the predicate is resolved by looking up its name and arity in the exported predicate environment of the class or domain type.
|
||||
- If the the call has a receiver and the type of the receiver is not the same as the enclosing class, the call is resolved by looking up the predicate name and arity in the exported predicate environment of the type of the receiver.
|
||||
|
||||
If all the predicates that the call resolves to are declared on a primitive type, we then restrict to the set of predicates where each argument of the call is a subtype of the corresponding predicate argument type.
|
||||
Then we find all predicates ``p`` from this new set such that there is not another predicate ``p'`` where each argument of ``p'`` is a subtype of the corresponding argument in ``p``. We then say the call resolves to this set instead.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.5.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* When a result of path query flows through a function modeled using `DataFlow::FunctionModel` or `TaintTracking::FunctionModel`, the path now includes nodes corresponding to the input and output to the function. This brings it in line with functions modeled using Models-as-Data.
|
||||
3
go/ql/lib/change-notes/released/0.5.3.md
Normal file
3
go/ql/lib/change-notes/released/0.5.3.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.3
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.2
|
||||
lastReleaseVersion: 0.5.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-all
|
||||
version: 0.5.3-dev
|
||||
version: 0.5.4-dev
|
||||
groups: go
|
||||
dbscheme: go.dbscheme
|
||||
extractor: go
|
||||
|
||||
@@ -232,7 +232,11 @@ class CastNode extends ExprNode {
|
||||
* Holds if `n` should never be skipped over in the `PathGraph` and in path
|
||||
* explanations.
|
||||
*/
|
||||
predicate neverSkipInPathGraph(Node n) { none() }
|
||||
predicate neverSkipInPathGraph(Node n) {
|
||||
exists(DataFlow::FunctionModel fm | fm.getAnInputNode(_) = n or fm.getAnOutputNode(_) = n)
|
||||
or
|
||||
exists(TaintTracking::FunctionModel fm | fm.getAnInputNode(_) = n or fm.getAnOutputNode(_) = n)
|
||||
}
|
||||
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.5.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
go/ql/src/change-notes/released/0.5.3.md
Normal file
3
go/ql/src/change-notes/released/0.5.3.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.3
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.2
|
||||
lastReleaseVersion: 0.5.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-queries
|
||||
version: 0.5.3-dev
|
||||
version: 0.5.4-dev
|
||||
groups:
|
||||
- go
|
||||
- queries
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
edges
|
||||
| Dsn.go:47:10:47:30 | call to FormValue | Dsn.go:50:29:50:33 | dbDSN |
|
||||
| Dsn.go:47:10:47:30 | call to FormValue | Dsn.go:49:102:49:105 | name |
|
||||
| Dsn.go:49:11:49:106 | call to Sprintf | Dsn.go:50:29:50:33 | dbDSN |
|
||||
| Dsn.go:49:102:49:105 | name | Dsn.go:49:11:49:106 | call to Sprintf |
|
||||
nodes
|
||||
| Dsn.go:47:10:47:30 | call to FormValue | semmle.label | call to FormValue |
|
||||
| Dsn.go:49:11:49:106 | call to Sprintf | semmle.label | call to Sprintf |
|
||||
| Dsn.go:49:102:49:105 | name | semmle.label | name |
|
||||
| Dsn.go:50:29:50:33 | dbDSN | semmle.label | dbDSN |
|
||||
subpaths
|
||||
#select
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
edges
|
||||
| Dsn.go:26:11:26:17 | selection of Args | Dsn.go:29:29:29:33 | dbDSN |
|
||||
| Dsn.go:26:11:26:17 | selection of Args | Dsn.go:28:102:28:109 | index expression |
|
||||
| Dsn.go:28:11:28:110 | call to Sprintf | Dsn.go:29:29:29:33 | dbDSN |
|
||||
| Dsn.go:28:102:28:109 | index expression | Dsn.go:28:11:28:110 | call to Sprintf |
|
||||
| Dsn.go:62:2:62:4 | definition of cfg [pointer] | Dsn.go:63:9:63:11 | cfg [pointer] |
|
||||
| Dsn.go:62:2:62:4 | definition of cfg [pointer] | Dsn.go:67:102:67:104 | cfg [pointer] |
|
||||
| Dsn.go:63:9:63:11 | cfg [pointer] | Dsn.go:63:9:63:11 | implicit dereference |
|
||||
| Dsn.go:63:9:63:11 | implicit dereference | Dsn.go:62:2:62:4 | definition of cfg [pointer] |
|
||||
| Dsn.go:63:9:63:11 | implicit dereference | Dsn.go:63:9:63:11 | implicit dereference |
|
||||
| Dsn.go:63:9:63:11 | implicit dereference | Dsn.go:68:29:68:33 | dbDSN |
|
||||
| Dsn.go:63:19:63:25 | selection of Args | Dsn.go:63:9:63:11 | implicit dereference |
|
||||
| Dsn.go:63:19:63:25 | selection of Args | Dsn.go:68:29:68:33 | dbDSN |
|
||||
| Dsn.go:63:9:63:11 | implicit dereference | Dsn.go:67:102:67:108 | selection of dsn |
|
||||
| Dsn.go:63:19:63:25 | selection of Args | Dsn.go:63:19:63:29 | slice expression |
|
||||
| Dsn.go:63:19:63:29 | slice expression | Dsn.go:63:9:63:11 | implicit dereference |
|
||||
| Dsn.go:67:11:67:109 | call to Sprintf | Dsn.go:68:29:68:33 | dbDSN |
|
||||
| Dsn.go:67:102:67:104 | cfg [pointer] | Dsn.go:67:102:67:104 | implicit dereference |
|
||||
| Dsn.go:67:102:67:104 | implicit dereference | Dsn.go:63:9:63:11 | implicit dereference |
|
||||
| Dsn.go:67:102:67:104 | implicit dereference | Dsn.go:68:29:68:33 | dbDSN |
|
||||
| Dsn.go:67:102:67:104 | implicit dereference | Dsn.go:67:102:67:108 | selection of dsn |
|
||||
| Dsn.go:67:102:67:108 | selection of dsn | Dsn.go:67:11:67:109 | call to Sprintf |
|
||||
nodes
|
||||
| Dsn.go:26:11:26:17 | selection of Args | semmle.label | selection of Args |
|
||||
| Dsn.go:28:11:28:110 | call to Sprintf | semmle.label | call to Sprintf |
|
||||
| Dsn.go:28:102:28:109 | index expression | semmle.label | index expression |
|
||||
| Dsn.go:29:29:29:33 | dbDSN | semmle.label | dbDSN |
|
||||
| Dsn.go:62:2:62:4 | definition of cfg [pointer] | semmle.label | definition of cfg [pointer] |
|
||||
| Dsn.go:63:9:63:11 | cfg [pointer] | semmle.label | cfg [pointer] |
|
||||
| Dsn.go:63:9:63:11 | implicit dereference | semmle.label | implicit dereference |
|
||||
| Dsn.go:63:19:63:25 | selection of Args | semmle.label | selection of Args |
|
||||
| Dsn.go:63:19:63:29 | slice expression | semmle.label | slice expression |
|
||||
| Dsn.go:67:11:67:109 | call to Sprintf | semmle.label | call to Sprintf |
|
||||
| Dsn.go:67:102:67:104 | cfg [pointer] | semmle.label | cfg [pointer] |
|
||||
| Dsn.go:67:102:67:104 | implicit dereference | semmle.label | implicit dereference |
|
||||
| Dsn.go:67:102:67:108 | selection of dsn | semmle.label | selection of dsn |
|
||||
| Dsn.go:68:29:68:33 | dbDSN | semmle.label | dbDSN |
|
||||
subpaths
|
||||
#select
|
||||
|
||||
@@ -4,17 +4,23 @@ edges
|
||||
| builtin.go:97:21:97:31 | call to Referer | builtin.go:101:36:101:49 | untrustedInput |
|
||||
| builtin.go:111:21:111:31 | call to Referer | builtin.go:114:15:114:28 | untrustedInput |
|
||||
| builtin.go:129:21:129:31 | call to Referer | builtin.go:132:38:132:51 | untrustedInput |
|
||||
| new-tests.go:26:26:26:30 | &... | new-tests.go:31:11:31:57 | call to Sprintf |
|
||||
| new-tests.go:26:26:26:30 | &... | new-tests.go:32:11:32:57 | call to Sprintf |
|
||||
| new-tests.go:26:26:26:30 | &... | new-tests.go:35:12:35:58 | call to Sprintf |
|
||||
| new-tests.go:26:26:26:30 | &... | new-tests.go:31:48:31:56 | selection of word |
|
||||
| new-tests.go:26:26:26:30 | &... | new-tests.go:32:48:32:56 | selection of safe |
|
||||
| new-tests.go:26:26:26:30 | &... | new-tests.go:35:49:35:57 | selection of word |
|
||||
| new-tests.go:31:48:31:56 | selection of word | new-tests.go:31:11:31:57 | call to Sprintf |
|
||||
| new-tests.go:32:48:32:56 | selection of safe | new-tests.go:32:11:32:57 | call to Sprintf |
|
||||
| new-tests.go:35:49:35:57 | selection of word | new-tests.go:35:12:35:58 | call to Sprintf |
|
||||
| new-tests.go:39:18:39:30 | call to Param | new-tests.go:47:11:47:46 | ...+... |
|
||||
| new-tests.go:49:18:49:30 | call to Query | new-tests.go:50:11:50:46 | ...+... |
|
||||
| new-tests.go:62:2:62:39 | ... := ...[0] | new-tests.go:63:17:63:23 | reqBody |
|
||||
| new-tests.go:62:31:62:38 | selection of Body | new-tests.go:62:2:62:39 | ... := ...[0] |
|
||||
| new-tests.go:63:17:63:23 | reqBody | new-tests.go:63:26:63:30 | &... |
|
||||
| new-tests.go:63:26:63:30 | &... | new-tests.go:68:11:68:57 | call to Sprintf |
|
||||
| new-tests.go:63:26:63:30 | &... | new-tests.go:69:11:69:57 | call to Sprintf |
|
||||
| new-tests.go:63:26:63:30 | &... | new-tests.go:74:12:74:58 | call to Sprintf |
|
||||
| new-tests.go:63:26:63:30 | &... | new-tests.go:68:48:68:56 | selection of word |
|
||||
| new-tests.go:63:26:63:30 | &... | new-tests.go:69:48:69:56 | selection of safe |
|
||||
| new-tests.go:63:26:63:30 | &... | new-tests.go:74:49:74:57 | selection of word |
|
||||
| new-tests.go:68:48:68:56 | selection of word | new-tests.go:68:11:68:57 | call to Sprintf |
|
||||
| new-tests.go:69:48:69:56 | selection of safe | new-tests.go:69:11:69:57 | call to Sprintf |
|
||||
| new-tests.go:74:49:74:57 | selection of word | new-tests.go:74:12:74:58 | call to Sprintf |
|
||||
| new-tests.go:78:18:78:24 | selection of URL | new-tests.go:78:18:78:32 | call to Query |
|
||||
| new-tests.go:78:18:78:32 | call to Query | new-tests.go:78:18:78:46 | call to Get |
|
||||
| new-tests.go:78:18:78:46 | call to Get | new-tests.go:79:11:79:46 | ...+... |
|
||||
@@ -36,8 +42,11 @@ nodes
|
||||
| builtin.go:132:38:132:51 | untrustedInput | semmle.label | untrustedInput |
|
||||
| new-tests.go:26:26:26:30 | &... | semmle.label | &... |
|
||||
| new-tests.go:31:11:31:57 | call to Sprintf | semmle.label | call to Sprintf |
|
||||
| new-tests.go:31:48:31:56 | selection of word | semmle.label | selection of word |
|
||||
| new-tests.go:32:11:32:57 | call to Sprintf | semmle.label | call to Sprintf |
|
||||
| new-tests.go:32:48:32:56 | selection of safe | semmle.label | selection of safe |
|
||||
| new-tests.go:35:12:35:58 | call to Sprintf | semmle.label | call to Sprintf |
|
||||
| new-tests.go:35:49:35:57 | selection of word | semmle.label | selection of word |
|
||||
| new-tests.go:39:18:39:30 | call to Param | semmle.label | call to Param |
|
||||
| new-tests.go:47:11:47:46 | ...+... | semmle.label | ...+... |
|
||||
| new-tests.go:49:18:49:30 | call to Query | semmle.label | call to Query |
|
||||
@@ -47,8 +56,11 @@ nodes
|
||||
| new-tests.go:63:17:63:23 | reqBody | semmle.label | reqBody |
|
||||
| new-tests.go:63:26:63:30 | &... | semmle.label | &... |
|
||||
| new-tests.go:68:11:68:57 | call to Sprintf | semmle.label | call to Sprintf |
|
||||
| new-tests.go:68:48:68:56 | selection of word | semmle.label | selection of word |
|
||||
| new-tests.go:69:11:69:57 | call to Sprintf | semmle.label | call to Sprintf |
|
||||
| new-tests.go:69:48:69:56 | selection of safe | semmle.label | selection of safe |
|
||||
| new-tests.go:74:12:74:58 | call to Sprintf | semmle.label | call to Sprintf |
|
||||
| new-tests.go:74:49:74:57 | selection of word | semmle.label | selection of word |
|
||||
| new-tests.go:78:18:78:24 | selection of URL | semmle.label | selection of URL |
|
||||
| new-tests.go:78:18:78:32 | call to Query | semmle.label | call to Query |
|
||||
| new-tests.go:78:18:78:46 | call to Get | semmle.label | call to Get |
|
||||
|
||||
@@ -47,7 +47,7 @@ edges
|
||||
| test.go:240:15:240:36 | call to GetString | test.go:243:21:243:29 | untrusted |
|
||||
| test.go:253:23:253:44 | call to GetCookie | test.go:253:16:253:45 | type conversion |
|
||||
| test.go:264:62:264:83 | call to GetCookie | test.go:264:55:264:84 | type conversion |
|
||||
| test.go:269:2:269:40 | ... := ...[0] | test.go:277:21:277:61 | call to GetDisplayString |
|
||||
| test.go:269:2:269:40 | ... := ...[0] | test.go:277:44:277:60 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] | test.go:278:38:278:49 | genericFiles |
|
||||
| test.go:269:2:269:40 | ... := ...[0] | test.go:279:37:279:48 | genericFiles |
|
||||
| test.go:269:2:269:40 | ... := ...[0] | test.go:285:4:285:15 | genericFiles |
|
||||
@@ -61,6 +61,7 @@ edges
|
||||
| test.go:269:2:269:40 | ... := ...[0] | test.go:295:39:295:50 | genericFiles |
|
||||
| test.go:269:2:269:40 | ... := ...[0] | test.go:296:40:296:51 | genericFiles |
|
||||
| test.go:269:2:269:40 | ... := ...[0] | test.go:297:39:297:50 | genericFiles |
|
||||
| test.go:277:44:277:60 | selection of Filename | test.go:277:21:277:61 | call to GetDisplayString |
|
||||
| test.go:278:21:278:53 | call to SliceChunk | test.go:278:21:278:92 | selection of Filename |
|
||||
| test.go:278:38:278:49 | genericFiles | test.go:278:21:278:53 | call to SliceChunk |
|
||||
| test.go:279:21:279:60 | call to SliceDiff | test.go:279:21:279:96 | selection of Filename |
|
||||
@@ -177,6 +178,7 @@ nodes
|
||||
| test.go:264:62:264:83 | call to GetCookie | semmle.label | call to GetCookie |
|
||||
| test.go:269:2:269:40 | ... := ...[0] | semmle.label | ... := ...[0] |
|
||||
| test.go:277:21:277:61 | call to GetDisplayString | semmle.label | call to GetDisplayString |
|
||||
| test.go:277:44:277:60 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:278:21:278:53 | call to SliceChunk | semmle.label | call to SliceChunk |
|
||||
| test.go:278:21:278:92 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:278:38:278:49 | genericFiles | semmle.label | genericFiles |
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import go
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class UntrustedFlowSourceTest extends InlineExpectationsTest {
|
||||
UntrustedFlowSourceTest() { this = "untrustedflowsource" }
|
||||
module UntrustedFlowSourceTest implements TestSig {
|
||||
string getARelevantTag() { result = "untrustedflowsource" }
|
||||
|
||||
override string getARelevantTag() { result = "untrustedflowsource" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "untrustedflowsource" and
|
||||
value = element and
|
||||
exists(UntrustedFlowSource src | value = "\"" + src.toString() + "\"" |
|
||||
@@ -16,12 +14,10 @@ class UntrustedFlowSourceTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
class HeaderWriteTest extends InlineExpectationsTest {
|
||||
HeaderWriteTest() { this = "headerwrite" }
|
||||
module HeaderWriteTest implements TestSig {
|
||||
string getARelevantTag() { result = "headerwrite" }
|
||||
|
||||
override string getARelevantTag() { result = "headerwrite" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "headerwrite" and
|
||||
exists(Http::HeaderWrite hw, string name, string val | element = hw.toString() |
|
||||
hw.definesHeader(name, val) and
|
||||
@@ -32,12 +28,10 @@ class HeaderWriteTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
class LoggerTest extends InlineExpectationsTest {
|
||||
LoggerTest() { this = "LoggerTest" }
|
||||
module LoggerTest implements TestSig {
|
||||
string getARelevantTag() { result = "logger" }
|
||||
|
||||
override string getARelevantTag() { result = "logger" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(LoggerCall log |
|
||||
log.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
@@ -48,32 +42,32 @@ class LoggerTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
class Config extends TaintTracking::Configuration {
|
||||
Config() { this = "goproxy config" }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) {
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node n) {
|
||||
n = any(DataFlow::CallNode c | c.getCalleeName().matches("tainted%")).getResult()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node n) {
|
||||
predicate isSink(DataFlow::Node n) {
|
||||
n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
class TaintFlow extends InlineExpectationsTest {
|
||||
TaintFlow() { this = "goproxy flow" }
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
override string getARelevantTag() { result = "taintflow" }
|
||||
module TaintFlow implements TestSig {
|
||||
string getARelevantTag() { result = "taintflow" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "taintflow" and
|
||||
value = "" and
|
||||
element = "" and
|
||||
exists(Config c, DataFlow::Node toNode |
|
||||
exists(DataFlow::Node toNode |
|
||||
toNode
|
||||
.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
c.hasFlowTo(toNode)
|
||||
Flow::flowTo(toNode)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<MergeTests4<UntrustedFlowSourceTest, HeaderWriteTest, LoggerTest, TaintFlow>>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import go
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class SqlTest extends InlineExpectationsTest {
|
||||
SqlTest() { this = "SQLTest" }
|
||||
module SqlTest implements TestSig {
|
||||
string getARelevantTag() { result = "query" }
|
||||
|
||||
override string getARelevantTag() { result = "query" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "query" and
|
||||
exists(SQL::Query q, SQL::QueryString qs | qs = q.getAQueryString() |
|
||||
q.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
@@ -17,12 +15,10 @@ class SqlTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
class QueryString extends InlineExpectationsTest {
|
||||
QueryString() { this = "QueryString no Query" }
|
||||
module QueryString implements TestSig {
|
||||
string getARelevantTag() { result = "querystring" }
|
||||
|
||||
override string getARelevantTag() { result = "querystring" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "querystring" and
|
||||
element = "" and
|
||||
exists(SQL::QueryString qs | not exists(SQL::Query q | qs = q.getAQueryString()) |
|
||||
@@ -33,30 +29,30 @@ class QueryString extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
class Config extends TaintTracking::Configuration {
|
||||
Config() { this = "pg-orm config" }
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit }
|
||||
|
||||
override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLit }
|
||||
|
||||
override predicate isSink(DataFlow::Node n) {
|
||||
predicate isSink(DataFlow::Node n) {
|
||||
n = any(DataFlow::CallNode cn | cn.getTarget().getName() = "sink").getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
class TaintFlow extends InlineExpectationsTest {
|
||||
TaintFlow() { this = "pg-orm flow" }
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
override string getARelevantTag() { result = "flowfrom" }
|
||||
module TaintFlow implements TestSig {
|
||||
string getARelevantTag() { result = "flowfrom" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "flowfrom" and
|
||||
element = "" and
|
||||
exists(Config c, DataFlow::Node fromNode, DataFlow::Node toNode |
|
||||
exists(DataFlow::Node fromNode, DataFlow::Node toNode |
|
||||
toNode
|
||||
.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
c.hasFlow(fromNode, toNode) and
|
||||
Flow::flow(fromNode, toNode) and
|
||||
value = fromNode.asExpr().(StringLit).getValue()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<MergeTests3<SqlTest, QueryString, TaintFlow>>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
failures
|
||||
testFailures
|
||||
|
||||
@@ -11,32 +11,29 @@ DataFlow::CallNode getAYamlCall() {
|
||||
isYamlFunction(result.getACalleeIncludingExternals().asFunction())
|
||||
}
|
||||
|
||||
class TaintTransitsFunctionConfig extends TaintTracking::Configuration {
|
||||
TaintTransitsFunctionConfig() { this = "TaintTransitsFunctionConfig" }
|
||||
|
||||
predicate isSourceSinkPair(DataFlow::Node inNode, DataFlow::Node outNode) {
|
||||
exists(DataFlow::CallNode cn | cn = getAYamlCall() |
|
||||
inNode = [cn.getAnArgument(), cn.getReceiver()] and
|
||||
(
|
||||
outNode.(DataFlow::PostUpdateNode).getPreUpdateNode() =
|
||||
[cn.getAnArgument(), cn.getReceiver()]
|
||||
or
|
||||
outNode = cn.getAResult()
|
||||
)
|
||||
predicate isSourceSinkPair(DataFlow::Node inNode, DataFlow::Node outNode) {
|
||||
exists(DataFlow::CallNode cn | cn = getAYamlCall() |
|
||||
inNode = [cn.getAnArgument(), cn.getReceiver()] and
|
||||
(
|
||||
outNode.(DataFlow::PostUpdateNode).getPreUpdateNode() = [cn.getAnArgument(), cn.getReceiver()]
|
||||
or
|
||||
outNode = cn.getAResult()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node n) { this.isSourceSinkPair(n, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node n) { this.isSourceSinkPair(_, n) }
|
||||
)
|
||||
}
|
||||
|
||||
class TaintFunctionModelTest extends InlineExpectationsTest {
|
||||
TaintFunctionModelTest() { this = "TaintFunctionModelTest" }
|
||||
module TaintTransitsFunctionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node n) { isSourceSinkPair(n, _) }
|
||||
|
||||
override string getARelevantTag() { result = "ttfnmodelstep" }
|
||||
predicate isSink(DataFlow::Node n) { isSourceSinkPair(_, n) }
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
module TaintTransitsFunctionFlow = TaintTracking::Global<TaintTransitsFunctionConfig>;
|
||||
|
||||
module TaintFunctionModelTest implements TestSig {
|
||||
string getARelevantTag() { result = "ttfnmodelstep" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "ttfnmodelstep" and
|
||||
(
|
||||
exists(TaintTracking::FunctionModel model, DataFlow::CallNode call | call = model.getACall() |
|
||||
@@ -46,9 +43,9 @@ class TaintFunctionModelTest extends InlineExpectationsTest {
|
||||
value = "\"" + model.getAnInputNode(call) + " -> " + model.getAnOutputNode(call) + "\""
|
||||
)
|
||||
or
|
||||
exists(TaintTransitsFunctionConfig config, DataFlow::Node arg, DataFlow::Node output |
|
||||
config.hasFlow(arg, output) and
|
||||
config.isSourceSinkPair(arg, output) and
|
||||
exists(DataFlow::Node arg, DataFlow::Node output |
|
||||
TaintTransitsFunctionFlow::flow(arg, output) and
|
||||
isSourceSinkPair(arg, output) and
|
||||
arg.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = arg.toString() and
|
||||
@@ -58,12 +55,10 @@ class TaintFunctionModelTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
class MarshalerTest extends InlineExpectationsTest {
|
||||
MarshalerTest() { this = "MarshalerTest" }
|
||||
module MarshalerTest implements TestSig {
|
||||
string getARelevantTag() { result = "marshaler" }
|
||||
|
||||
override string getARelevantTag() { result = "marshaler" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "marshaler" and
|
||||
exists(MarshalingFunction m, DataFlow::CallNode call | call = m.getACall() |
|
||||
call.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
@@ -76,12 +71,10 @@ class MarshalerTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
class UnmarshalerTest extends InlineExpectationsTest {
|
||||
UnmarshalerTest() { this = "UnmarshalerTest" }
|
||||
module UnmarshalerTest implements TestSig {
|
||||
string getARelevantTag() { result = "unmarshaler" }
|
||||
|
||||
override string getARelevantTag() { result = "unmarshaler" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "unmarshaler" and
|
||||
exists(UnmarshalingFunction m, DataFlow::CallNode call | call = m.getACall() |
|
||||
call.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
@@ -93,3 +86,5 @@ class UnmarshalerTest extends InlineExpectationsTest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<MergeTests3<TaintFunctionModelTest, MarshalerTest, UnmarshalerTest>>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
edges
|
||||
| TaintedPath.go:13:18:13:22 | selection of URL | TaintedPath.go:13:18:13:30 | call to Query |
|
||||
| TaintedPath.go:13:18:13:30 | call to Query | TaintedPath.go:16:29:16:40 | tainted_path |
|
||||
| TaintedPath.go:13:18:13:30 | call to Query | TaintedPath.go:20:28:20:69 | call to Join |
|
||||
| TaintedPath.go:13:18:13:30 | call to Query | TaintedPath.go:20:57:20:68 | tainted_path |
|
||||
| TaintedPath.go:20:57:20:68 | tainted_path | TaintedPath.go:20:28:20:69 | call to Join |
|
||||
| tst.go:14:2:14:39 | ... := ...[1] | tst.go:17:41:17:56 | selection of Filename |
|
||||
nodes
|
||||
| TaintedPath.go:13:18:13:22 | selection of URL | semmle.label | selection of URL |
|
||||
| TaintedPath.go:13:18:13:30 | call to Query | semmle.label | call to Query |
|
||||
| TaintedPath.go:16:29:16:40 | tainted_path | semmle.label | tainted_path |
|
||||
| TaintedPath.go:20:28:20:69 | call to Join | semmle.label | call to Join |
|
||||
| TaintedPath.go:20:57:20:68 | tainted_path | semmle.label | tainted_path |
|
||||
| tst.go:14:2:14:39 | ... := ...[1] | semmle.label | ... := ...[1] |
|
||||
| tst.go:17:41:17:56 | selection of Filename | semmle.label | selection of Filename |
|
||||
subpaths
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user