upgrade query to detect redash CVE too

This commit is contained in:
amammad
2023-06-30 22:14:50 +10:00
parent 7a17b99c17
commit 816799c4ba
436 changed files with 13346 additions and 1089 deletions

View File

@@ -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

View 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.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.7.2
lastReleaseVersion: 0.7.3

View File

@@ -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

View File

@@ -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

View File

@@ -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()
)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
)
}

View File

@@ -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)

View File

@@ -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()

View File

@@ -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)
}

View File

@@ -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.

View File

@@ -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

View 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.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.2
lastReleaseVersion: 0.6.3

View File

@@ -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

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.6.3-dev
version: 0.6.4-dev
groups:
- cpp
- queries

View File

@@ -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 |

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -0,0 +1,2 @@
failures
testFailures

View File

@@ -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>

View File

@@ -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 |

View File

@@ -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 | |

View File

@@ -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
}

View File

@@ -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:
}
}
}

View File

@@ -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. |

View File

@@ -1,3 +1,7 @@
## 1.5.3
No user-facing changes.
## 1.5.2
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.5.3
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.2
lastReleaseVersion: 1.5.3

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
version: 1.5.3-dev
version: 1.5.4-dev
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,7 @@
## 1.5.3
No user-facing changes.
## 1.5.2
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.5.3
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.2
lastReleaseVersion: 1.5.3

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
version: 1.5.3-dev
version: 1.5.4-dev
groups:
- csharp
- solorigate

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View 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.

View 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`

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.2
lastReleaseVersion: 0.6.3

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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.

View File

@@ -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.
*/

View File

@@ -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.

View File

@@ -1,3 +1,7 @@
## 0.6.3
No user-facing changes.
## 0.6.2
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.6.3
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.2
lastReleaseVersion: 0.6.3

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries
version: 0.6.3-dev
version: 0.6.4-dev
groups:
- csharp
- queries

View 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

View 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

View 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

View 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

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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>
{

View File

@@ -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 |

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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

View 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

View File

@@ -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)

View File

@@ -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. |

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -1,3 +1,7 @@
## 0.5.3
No user-facing changes.
## 0.5.2
### Minor Analysis Improvements

View File

@@ -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.

View File

@@ -0,0 +1,3 @@
## 0.5.3
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.5.2
lastReleaseVersion: 0.5.3

View File

@@ -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

View File

@@ -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;

View File

@@ -1,3 +1,7 @@
## 0.5.3
No user-facing changes.
## 0.5.2
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.5.3
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.5.2
lastReleaseVersion: 0.5.3

View File

@@ -1,5 +1,5 @@
name: codeql/go-queries
version: 0.5.3-dev
version: 0.5.4-dev
groups:
- go
- queries

View File

@@ -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

View File

@@ -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

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -0,0 +1,2 @@
failures
testFailures

View File

@@ -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>>

View File

@@ -0,0 +1,2 @@
failures
testFailures

View File

@@ -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>>

View File

@@ -0,0 +1,2 @@
failures
testFailures

View File

@@ -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>>

View File

@@ -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