mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Merge branch 'main' into fix/update-gson-model
This commit is contained in:
@@ -454,10 +454,6 @@
|
||||
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll",
|
||||
"swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll"
|
||||
],
|
||||
"SummaryTypeTracker": [
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll",
|
||||
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
|
||||
],
|
||||
"IncompleteUrlSubstringSanitization": [
|
||||
"javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll",
|
||||
"ruby/ql/src/queries/security/cwe-020/IncompleteUrlSubstringSanitization.qll"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
class AttributeArg extends @attribute_arg {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Attribute extends @attribute {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Location extends @location_default {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from AttributeArg arg, int kind, int kind_new, Attribute attr, int index, Location location
|
||||
where
|
||||
attribute_args(arg, kind, attr, index, location) and
|
||||
if arg instanceof @attribute_arg_expr then kind_new = 0 else kind_new = kind
|
||||
select arg, kind_new, attr, index, location
|
||||
2238
cpp/downgrades/d8149ca90e695fe26f9a0c5a7fa0edd6d4ea3f5d/old.dbscheme
Normal file
2238
cpp/downgrades/d8149ca90e695fe26f9a0c5a7fa0edd6d4ea3f5d/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
description: Support expression attribute arguments
|
||||
compatibility: partial
|
||||
attribute_arg_expr.rel: delete
|
||||
attribute_args.rel: run attribute_args.qlo
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.12.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### New Features
|
||||
|
||||
4
cpp/ql/lib/change-notes/2023-12-22-unique-function.md
Normal file
4
cpp/ql/lib/change-notes/2023-12-22-unique-function.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
* Under certain circumstances a function declaration that is not also a definition could be associated with a `Function` that did not have the definition as a `FunctionDeclarationEntry`. This is now fixed when only one definition exists, and a unique `Function` will exist that has both the declaration and the definition as a `FunctionDeclarationEntry`.
|
||||
3
cpp/ql/lib/change-notes/released/0.12.2.md
Normal file
3
cpp/ql/lib/change-notes/released/0.12.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.12.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.12.1
|
||||
lastReleaseVersion: 0.12.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.12.2-dev
|
||||
version: 0.12.3-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -7,6 +7,7 @@ import semmle.code.cpp.Location
|
||||
private import semmle.code.cpp.Enclosing
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
private import semmle.code.cpp.internal.ResolveGlobalVariable
|
||||
private import semmle.code.cpp.internal.ResolveFunction
|
||||
|
||||
/**
|
||||
* Get the `Element` that represents this `@element`.
|
||||
@@ -30,11 +31,14 @@ pragma[inline]
|
||||
@element unresolveElement(Element e) {
|
||||
not result instanceof @usertype and
|
||||
not result instanceof @variable and
|
||||
not result instanceof @function and
|
||||
result = e
|
||||
or
|
||||
e = resolveClass(result)
|
||||
or
|
||||
e = resolveGlobalVariable(result)
|
||||
or
|
||||
e = resolveFunction(result)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ import semmle.code.cpp.exprs.Call
|
||||
import semmle.code.cpp.metrics.MetricFunction
|
||||
import semmle.code.cpp.Linkage
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
private import semmle.code.cpp.internal.ResolveFunction
|
||||
|
||||
/**
|
||||
* A C/C++ function [N4140 8.3.5]. Both member functions and non-member
|
||||
@@ -25,6 +26,8 @@ private import semmle.code.cpp.internal.ResolveClass
|
||||
* in more detail in `Declaration.qll`.
|
||||
*/
|
||||
class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
Function() { isFunction(underlyingElement(this)) }
|
||||
|
||||
override string getName() { functions(underlyingElement(this), result, _) }
|
||||
|
||||
/**
|
||||
|
||||
@@ -281,6 +281,11 @@ class AttributeArgument extends Element, @attribute_arg {
|
||||
attribute_arg_constant(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of this argument, if its value is an expression.
|
||||
*/
|
||||
Expr getValueExpr() { attribute_arg_expr(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Gets the attribute to which this is an argument.
|
||||
*/
|
||||
@@ -308,6 +313,9 @@ class AttributeArgument extends Element, @attribute_arg {
|
||||
else
|
||||
if underlyingElement(this) instanceof @attribute_arg_constant_expr
|
||||
then tail = this.getValueConstant().toString()
|
||||
else
|
||||
if underlyingElement(this) instanceof @attribute_arg_expr
|
||||
then tail = this.getValueExpr().toString()
|
||||
else tail = this.getValueText()
|
||||
) and
|
||||
result = prefix + tail
|
||||
|
||||
@@ -110,8 +110,8 @@ private predicate loopConditionAlwaysUponEntry(ControlFlowNode loop, Expr condit
|
||||
* should be in this relation.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate isFunction(Element el) {
|
||||
el instanceof Function
|
||||
private predicate isFunction(@element el) {
|
||||
el instanceof @function
|
||||
or
|
||||
el.(Expr).getParent() = el
|
||||
}
|
||||
@@ -122,7 +122,7 @@ private predicate isFunction(Element el) {
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate callHasNoTarget(@funbindexpr fc) {
|
||||
exists(Function f |
|
||||
exists(@function f |
|
||||
funbind(fc, f) and
|
||||
not isFunction(f)
|
||||
)
|
||||
|
||||
57
cpp/ql/lib/semmle/code/cpp/internal/ResolveFunction.qll
Normal file
57
cpp/ql/lib/semmle/code/cpp/internal/ResolveFunction.qll
Normal file
@@ -0,0 +1,57 @@
|
||||
private predicate hasDefinition(@function f) {
|
||||
exists(@fun_decl fd | fun_decls(fd, f, _, _, _) | fun_def(fd))
|
||||
}
|
||||
|
||||
private predicate onlyOneCompleteFunctionExistsWithMangledName(@mangledname name) {
|
||||
strictcount(@function f | hasDefinition(f) and mangled_name(f, name)) = 1
|
||||
}
|
||||
|
||||
/** Holds if `f` is a unique function with a definition named `name`. */
|
||||
private predicate isFunctionWithMangledNameAndWithDefinition(@mangledname name, @function f) {
|
||||
hasDefinition(f) and
|
||||
mangled_name(f, name) and
|
||||
onlyOneCompleteFunctionExistsWithMangledName(name)
|
||||
}
|
||||
|
||||
/** Holds if `f` is a function without a definition named `name`. */
|
||||
private predicate isFunctionWithMangledNameAndWithoutDefinition(@mangledname name, @function f) {
|
||||
not hasDefinition(f) and
|
||||
mangled_name(f, name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `incomplete` is a function without a definition, and there exists
|
||||
* a unique function `complete` with the same name that does have a definition.
|
||||
*/
|
||||
private predicate hasTwinWithDefinition(@function incomplete, @function complete) {
|
||||
not function_instantiation(incomplete, complete) and
|
||||
(
|
||||
not compgenerated(incomplete) or
|
||||
not compgenerated(complete)
|
||||
) and
|
||||
exists(@mangledname name |
|
||||
isFunctionWithMangledNameAndWithoutDefinition(name, incomplete) and
|
||||
isFunctionWithMangledNameAndWithDefinition(name, complete)
|
||||
)
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* If `f` is a function without a definition, and there exists a unique
|
||||
* function with the same name that does have a definition, then the
|
||||
* result is that unique function. Otherwise, the result is `f`.
|
||||
*/
|
||||
cached
|
||||
@function resolveFunction(@function f) {
|
||||
hasTwinWithDefinition(f, result)
|
||||
or
|
||||
not hasTwinWithDefinition(f, _) and
|
||||
result = f
|
||||
}
|
||||
|
||||
cached
|
||||
predicate isFunction(@function f) { f = resolveFunction(_) }
|
||||
}
|
||||
@@ -24,8 +24,8 @@ private predicate isGlobalWithMangledNameAndWithoutDefinition(@mangledname name,
|
||||
* a unique global variable `complete` with the same name that does have a definition.
|
||||
*/
|
||||
private predicate hasTwinWithDefinition(@globalvariable incomplete, @globalvariable complete) {
|
||||
exists(@mangledname name |
|
||||
not variable_instantiation(incomplete, complete) and
|
||||
exists(@mangledname name |
|
||||
isGlobalWithMangledNameAndWithoutDefinition(name, incomplete) and
|
||||
isGlobalWithMangledNameAndWithDefinition(name, complete)
|
||||
)
|
||||
|
||||
@@ -59,6 +59,41 @@ private module Cached {
|
||||
import Cached
|
||||
private import Nodes0
|
||||
|
||||
/**
|
||||
* A module for calculating the number of stars (i.e., `*`s) needed for various
|
||||
* dataflow node `toString` predicates.
|
||||
*/
|
||||
module NodeStars {
|
||||
private int getNumberOfIndirections(Node n) {
|
||||
result = n.(RawIndirectOperand).getIndirectionIndex()
|
||||
or
|
||||
result = n.(RawIndirectInstruction).getIndirectionIndex()
|
||||
or
|
||||
result = n.(VariableNode).getIndirectionIndex()
|
||||
or
|
||||
result = n.(PostUpdateNodeImpl).getIndirectionIndex()
|
||||
or
|
||||
result = n.(FinalParameterNode).getIndirectionIndex()
|
||||
}
|
||||
|
||||
private int maxNumberOfIndirections() { result = max(getNumberOfIndirections(_)) }
|
||||
|
||||
private string repeatStars(int n) {
|
||||
n = 0 and result = ""
|
||||
or
|
||||
n = [1 .. maxNumberOfIndirections()] and
|
||||
result = "*" + repeatStars(n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
|
||||
* output for `n`.
|
||||
*/
|
||||
string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) }
|
||||
}
|
||||
|
||||
import NodeStars
|
||||
|
||||
class Node0Impl extends TIRDataFlowNode0 {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
|
||||
@@ -486,47 +486,6 @@ class Node extends TIRDataFlowNode {
|
||||
}
|
||||
}
|
||||
|
||||
private string toExprString(Node n) {
|
||||
not isDebugMode() and
|
||||
(
|
||||
result = n.asExpr(0).toString()
|
||||
or
|
||||
not exists(n.asExpr()) and
|
||||
result = stars(n) + n.asIndirectExpr(0, 1).toString()
|
||||
)
|
||||
}
|
||||
|
||||
private module NodeStars {
|
||||
private int getNumberOfIndirections(Node n) {
|
||||
result = n.(RawIndirectOperand).getIndirectionIndex()
|
||||
or
|
||||
result = n.(RawIndirectInstruction).getIndirectionIndex()
|
||||
or
|
||||
result = n.(VariableNode).getIndirectionIndex()
|
||||
or
|
||||
result = n.(PostUpdateNodeImpl).getIndirectionIndex()
|
||||
or
|
||||
result = n.(FinalParameterNode).getIndirectionIndex()
|
||||
}
|
||||
|
||||
private int maxNumberOfIndirections() { result = max(getNumberOfIndirections(_)) }
|
||||
|
||||
private string repeatStars(int n) {
|
||||
n = 0 and result = ""
|
||||
or
|
||||
n = [1 .. maxNumberOfIndirections()] and
|
||||
result = "*" + repeatStars(n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
|
||||
* output for `n`.
|
||||
*/
|
||||
string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) }
|
||||
}
|
||||
|
||||
private import NodeStars
|
||||
|
||||
/**
|
||||
* A class that lifts pre-SSA dataflow nodes to regular dataflow nodes.
|
||||
*/
|
||||
@@ -589,7 +548,10 @@ Type stripPointer(Type t) {
|
||||
result = t.(FunctionPointerIshType).getBaseType()
|
||||
}
|
||||
|
||||
private class PostUpdateNodeImpl extends PartialDefinitionNode, TPostUpdateNodeImpl {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
class PostUpdateNodeImpl extends PartialDefinitionNode, TPostUpdateNodeImpl {
|
||||
int indirectionIndex;
|
||||
Operand operand;
|
||||
|
||||
@@ -997,7 +959,8 @@ private Type getTypeImpl0(Type t, int indirectionIndex) {
|
||||
*
|
||||
* If `indirectionIndex` cannot be stripped off `t`, an `UnknownType` is returned.
|
||||
*/
|
||||
bindingset[indirectionIndex]
|
||||
bindingset[t, indirectionIndex]
|
||||
pragma[inline_late]
|
||||
Type getTypeImpl(Type t, int indirectionIndex) {
|
||||
result = getTypeImpl0(t, indirectionIndex)
|
||||
or
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
/**
|
||||
* This file activates debugging mode for dataflow node printing.
|
||||
* This file contains the class that implements the _debug_ version of
|
||||
* `toString` for `Instruction` and `Operand` dataflow nodes.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import codeql.util.Unit
|
||||
private import Node0ToString
|
||||
private import DataFlowUtil
|
||||
|
||||
private class DebugNode0ToString extends Node0ToString {
|
||||
final override predicate isDebugMode() { any() }
|
||||
DebugNode0ToString() {
|
||||
// Silence warning about `this` not being bound.
|
||||
exists(this)
|
||||
}
|
||||
|
||||
override string instructionToString(Instruction i) { result = i.getDumpString() }
|
||||
|
||||
override string operandToString(Operand op) {
|
||||
result = op.getDumpString() + " @ " + op.getUse().getResultId()
|
||||
}
|
||||
|
||||
override string toExprString(Node n) { none() }
|
||||
}
|
||||
|
||||
@@ -1,75 +1,53 @@
|
||||
/**
|
||||
* This file contains the abstract class that serves as the base class for
|
||||
* dataflow node printing.
|
||||
* This file imports the class that is used to construct the strings used by
|
||||
* `Node.ToString`.
|
||||
*
|
||||
* By default, a non-debug string is produced. However, a debug-friendly
|
||||
* string can be produced by importing `DebugPrinting.qll`.
|
||||
* Normally, this file should just import `NormalNode0ToString` to compute the
|
||||
* efficient `toString`, but for debugging purposes one can import
|
||||
* `DebugPrinting.qll` to better correlate the dataflow nodes with their
|
||||
* underlying instructions and operands.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import codeql.util.Unit
|
||||
private import DataFlowUtil
|
||||
import NormalNode0ToString // Change this import to control which version should be used.
|
||||
|
||||
/**
|
||||
* A class to control whether a debugging version of instructions and operands
|
||||
* should be printed as part of the `toString` output of dataflow nodes.
|
||||
*
|
||||
* To enable debug printing import the `DebugPrinting.ql` file. By default,
|
||||
* non-debug output will be used.
|
||||
/** An abstract class to control the behavior of `Node.toString`. */
|
||||
abstract class Node0ToString extends Unit {
|
||||
/**
|
||||
* Gets the string that should be used by `OperandNode.toString` to print the
|
||||
* dataflow node whose underlying operand is `op.`
|
||||
*/
|
||||
class Node0ToString extends Unit {
|
||||
abstract predicate isDebugMode();
|
||||
|
||||
private string normalInstructionToString(Instruction i) {
|
||||
not this.isDebugMode() and
|
||||
if i.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = i.getAst().toString()
|
||||
}
|
||||
|
||||
private string normalOperandToString(Operand op) {
|
||||
not this.isDebugMode() and
|
||||
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = op.getDef().getAst().toString()
|
||||
}
|
||||
abstract string operandToString(Operand op);
|
||||
|
||||
/**
|
||||
* Gets the string that should be used by `InstructionNode.toString`
|
||||
* Gets the string that should be used by `InstructionNode.toString` to print
|
||||
* the dataflow node whose underlying instruction is `instr`.
|
||||
*/
|
||||
string instructionToString(Instruction i) {
|
||||
if this.isDebugMode()
|
||||
then result = i.getDumpString()
|
||||
else result = this.normalInstructionToString(i)
|
||||
}
|
||||
abstract string instructionToString(Instruction i);
|
||||
|
||||
/**
|
||||
* Gets the string that should be used by `OperandNode.toString`.
|
||||
* Gets the string representation of the `Expr` associated with `n`, if any.
|
||||
*/
|
||||
string operandToString(Operand op) {
|
||||
if this.isDebugMode()
|
||||
then result = op.getDumpString() + " @ " + op.getUse().getResultId()
|
||||
else result = this.normalOperandToString(op)
|
||||
}
|
||||
}
|
||||
|
||||
private class NoDebugNode0ToString extends Node0ToString {
|
||||
final override predicate isDebugMode() { none() }
|
||||
abstract string toExprString(Node n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string that should be used by `OperandNode.toString`.
|
||||
* Gets the string that should be used by `OperandNode.toString` to print the
|
||||
* dataflow node whose underlying operand is `op.`
|
||||
*/
|
||||
string operandToString(Operand op) { result = any(Node0ToString nts).operandToString(op) }
|
||||
string operandToString(Operand op) { result = any(Node0ToString s).operandToString(op) }
|
||||
|
||||
/**
|
||||
* Gets the string that should be used by `InstructionNode.toString`
|
||||
* Gets the string that should be used by `InstructionNode.toString` to print
|
||||
* the dataflow node whose underlying instruction is `instr`.
|
||||
*/
|
||||
string instructionToString(Instruction i) { result = any(Node0ToString nts).instructionToString(i) }
|
||||
string instructionToString(Instruction instr) {
|
||||
result = any(Node0ToString s).instructionToString(instr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if debugging mode is enabled.
|
||||
*
|
||||
* In debug mode the `toString` on dataflow nodes is more expensive to compute,
|
||||
* but gives more precise information about the different dataflow nodes.
|
||||
* Gets the string representation of the `Expr` associated with `n`, if any.
|
||||
*/
|
||||
predicate isDebugMode() { any(Node0ToString nts).isDebugMode() }
|
||||
string toExprString(Node n) { result = any(Node0ToString s).toExprString(n) }
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* This file contains the class that implements the non-debug version of
|
||||
* `toString` for `Instruction` and `Operand` dataflow nodes.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import codeql.util.Unit
|
||||
private import Node0ToString
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
|
||||
private class NormalNode0ToString extends Node0ToString {
|
||||
NormalNode0ToString() {
|
||||
// Silence warning about `this` not being bound.
|
||||
exists(this)
|
||||
}
|
||||
|
||||
override string instructionToString(Instruction i) {
|
||||
if i.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = i.getAst().toString()
|
||||
}
|
||||
|
||||
override string operandToString(Operand op) {
|
||||
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = op.getDef().getAst().toString()
|
||||
}
|
||||
|
||||
override string toExprString(Node n) {
|
||||
result = n.asExpr(0).toString()
|
||||
or
|
||||
not exists(n.asExpr()) and
|
||||
result = stars(n) + n.asIndirectExpr(0, 1).toString()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import SsaInternals as Ssa
|
||||
private import PrintIRUtilities
|
||||
|
||||
@@ -33,9 +34,9 @@ private string getNodeProperty(Node node, string key) {
|
||||
key = "flow" and
|
||||
result =
|
||||
strictconcat(string flow, boolean to, int order1, int order2 |
|
||||
flow = getFromFlow(node, order1, order2) + "->" + starsForNode(node) + "@" and to = false
|
||||
flow = getFromFlow(node, order1, order2) + "->" + stars(node) + "@" and to = false
|
||||
or
|
||||
flow = starsForNode(node) + "@->" + getToFlow(node, order1, order2) and to = true
|
||||
flow = stars(node) + "@->" + getToFlow(node, order1, order2) and to = true
|
||||
|
|
||||
flow, ", " order by to, order1, order2, flow
|
||||
)
|
||||
|
||||
@@ -7,37 +7,14 @@ private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
|
||||
private string stars(int k) {
|
||||
k =
|
||||
[0 .. max([
|
||||
any(RawIndirectInstruction n).getIndirectionIndex(),
|
||||
any(RawIndirectOperand n).getIndirectionIndex()
|
||||
]
|
||||
)] and
|
||||
(if k = 0 then result = "" else result = "*" + stars(k - 1))
|
||||
}
|
||||
|
||||
string starsForNode(Node node) {
|
||||
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
|
||||
result = ""
|
||||
}
|
||||
|
||||
private Instruction getInstruction(Node n, string stars) {
|
||||
result = [n.asInstruction(), n.(RawIndirectInstruction).getInstruction()] and
|
||||
stars = starsForNode(n)
|
||||
stars = stars(n)
|
||||
}
|
||||
|
||||
private Operand getOperand(Node n, string stars) {
|
||||
result = [n.asOperand(), n.(RawIndirectOperand).getOperand()] and
|
||||
stars = starsForNode(n)
|
||||
stars = stars(n)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,15 @@ private module SourceVariables {
|
||||
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
|
||||
}
|
||||
|
||||
private int maxNumberOfIndirections() { result = max(SourceVariable sv | | sv.getIndirection()) }
|
||||
|
||||
private string repeatStars(int n) {
|
||||
n = 0 and result = ""
|
||||
or
|
||||
n = [1 .. maxNumberOfIndirections()] and
|
||||
result = "*" + repeatStars(n - 1)
|
||||
}
|
||||
|
||||
class SourceVariable extends TSourceVariable {
|
||||
SsaInternals0::SourceVariable base;
|
||||
int ind;
|
||||
@@ -32,13 +41,7 @@ private module SourceVariables {
|
||||
SsaInternals0::SourceVariable getBaseVariable() { result = base }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
ind = 0 and
|
||||
result = this.getBaseVariable().toString()
|
||||
or
|
||||
ind > 0 and
|
||||
result = this.getBaseVariable().toString() + " indirection"
|
||||
}
|
||||
string toString() { result = repeatStars(this.getIndirection()) + base.toString() }
|
||||
|
||||
/**
|
||||
* Gets the number of loads performed on the base source variable
|
||||
|
||||
@@ -418,6 +418,11 @@ class BaseCallVariable extends AbstractBaseSourceVariable, TBaseCallVariable {
|
||||
}
|
||||
|
||||
private module IsModifiableAtImpl {
|
||||
pragma[nomagic]
|
||||
private predicate isUnderlyingIndirectionType(Type t) {
|
||||
t = any(Indirection ind).getUnderlyingType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `indirectionIndex`'th dereference of a value of type
|
||||
* `cppType` is a type that can be modified (either by modifying the value
|
||||
@@ -445,10 +450,9 @@ private module IsModifiableAtImpl {
|
||||
bindingset[cppType, indirectionIndex]
|
||||
pragma[inline_late]
|
||||
private predicate impl(CppType cppType, int indirectionIndex) {
|
||||
exists(Type pointerType, Type base, Type t |
|
||||
pointerType = t.getUnderlyingType() and
|
||||
pointerType = any(Indirection ind).getUnderlyingType() and
|
||||
cppType.hasType(t, _) and
|
||||
exists(Type pointerType, Type base |
|
||||
isUnderlyingIndirectionType(pointerType) and
|
||||
cppType.hasUnderlyingType(pointerType, _) and
|
||||
base = getTypeImpl(pointerType, indirectionIndex)
|
||||
|
|
||||
// The value cannot be modified if it has a const specifier,
|
||||
|
||||
@@ -227,7 +227,7 @@ class CppType extends TCppType {
|
||||
predicate hasType(Type type, boolean isGLValue) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this type represents the C++ type `type`. If `isGLValue` is `true`, then this type
|
||||
* Holds if this type represents the C++ unspecified type `type`. If `isGLValue` is `true`, then this type
|
||||
* represents a glvalue of type `type`. Otherwise, it represents a prvalue of type `type`.
|
||||
*/
|
||||
final predicate hasUnspecifiedType(Type type, boolean isGLValue) {
|
||||
@@ -236,6 +236,18 @@ class CppType extends TCppType {
|
||||
type = specifiedType.getUnspecifiedType()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this type represents the C++ type `type` (after resolving
|
||||
* typedefs). If `isGLValue` is `true`, then this type represents a glvalue
|
||||
* of type `type`. Otherwise, it represents a prvalue of type `type`.
|
||||
*/
|
||||
final predicate hasUnderlyingType(Type type, boolean isGLValue) {
|
||||
exists(Type typedefType |
|
||||
this.hasType(typedefType, isGLValue) and
|
||||
type = typedefType.getUnderlyingType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
pragma[nomagic]
|
||||
private Type stripTopLevelSpecifiersOnly(Type t) {
|
||||
result = stripTopLevelSpecifiersOnly(t.(SpecifiedType).getBaseType())
|
||||
result = stripTopLevelSpecifiersOnly(pragma[only_bind_out](t.(SpecifiedType).getBaseType()))
|
||||
or
|
||||
result = t and
|
||||
not t instanceof SpecifiedType
|
||||
|
||||
@@ -937,6 +937,7 @@ case @attribute_arg.kind of
|
||||
| 2 = @attribute_arg_constant
|
||||
| 3 = @attribute_arg_type
|
||||
| 4 = @attribute_arg_constant_expr
|
||||
| 5 = @attribute_arg_expr
|
||||
;
|
||||
|
||||
attribute_arg_value(
|
||||
@@ -951,6 +952,10 @@ attribute_arg_constant(
|
||||
unique int arg: @attribute_arg ref,
|
||||
int constant: @expr ref
|
||||
)
|
||||
attribute_arg_expr(
|
||||
unique int arg: @attribute_arg ref,
|
||||
int expr: @expr ref
|
||||
)
|
||||
attribute_arg_name(
|
||||
unique int arg: @attribute_arg ref,
|
||||
string name: string ref
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Support expression attribute arguments
|
||||
compatibility: backwards
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.9.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
@@ -87,6 +87,8 @@ module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
|
||||
|
|
||||
e = any(StoreInstruction store).getDestinationAddress().getUnconvertedResultExpression()
|
||||
)
|
||||
or
|
||||
n.asExpr() instanceof ArrayExpr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,35 +101,43 @@ module ParameterSinks {
|
||||
)
|
||||
}
|
||||
|
||||
private CallInstruction getAnAlwaysReachedCallInstruction(IRFunction f) {
|
||||
result.getBlock().postDominates(f.getEntryBlock())
|
||||
private CallInstruction getAnAlwaysReachedCallInstruction() {
|
||||
exists(IRFunction f | result.getBlock().postDominates(f.getEntryBlock()))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callHasTargetAndArgument(Function f, int i, CallInstruction call, Instruction argument) {
|
||||
private predicate callHasTargetAndArgument(Function f, int i, Instruction argument) {
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() = f and
|
||||
call.getArgument(i) = argument
|
||||
call.getArgument(i) = argument and
|
||||
call = getAnAlwaysReachedCallInstruction()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate initializeParameterInFunction(Function f, int i, InitializeParameterInstruction init) {
|
||||
private predicate initializeParameterInFunction(Function f, int i) {
|
||||
exists(InitializeParameterInstruction init |
|
||||
pragma[only_bind_out](init.getEnclosingFunction()) = f and
|
||||
init.hasIndex(i)
|
||||
init.hasIndex(i) and
|
||||
init = getAnAlwaysDereferencedParameter()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate alwaysDereferencedArgumentHasValueNumber(ValueNumber vn) {
|
||||
exists(int i, Function f, Instruction argument |
|
||||
callHasTargetAndArgument(f, i, argument) and
|
||||
initializeParameterInFunction(pragma[only_bind_into](f), pragma[only_bind_into](i)) and
|
||||
vn.getAnInstruction() = argument
|
||||
)
|
||||
}
|
||||
|
||||
InitializeParameterInstruction getAnAlwaysDereferencedParameter() {
|
||||
result = getAnAlwaysDereferencedParameter0()
|
||||
or
|
||||
exists(
|
||||
CallInstruction call, int i, InitializeParameterInstruction p, Instruction argument,
|
||||
Function f
|
||||
|
|
||||
callHasTargetAndArgument(f, i, call, argument) and
|
||||
initializeParameterInFunction(f, i, p) and
|
||||
p = getAnAlwaysDereferencedParameter() and
|
||||
result =
|
||||
pragma[only_bind_out](pragma[only_bind_into](valueNumber(argument)).getAnInstruction()) and
|
||||
call = getAnAlwaysReachedCallInstruction(_)
|
||||
exists(ValueNumber vn |
|
||||
alwaysDereferencedArgumentHasValueNumber(vn) and
|
||||
vn.getAnInstruction() = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
98
cpp/ql/src/Security/CWE/CWE-416/Temporaries.qll
Normal file
98
cpp/ql/src/Security/CWE/CWE-416/Temporaries.qll
Normal file
@@ -0,0 +1,98 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.models.implementations.StdContainer
|
||||
|
||||
/**
|
||||
* Holds if `e` will be consumed by its parent as a glvalue and does not have
|
||||
* an lvalue-to-rvalue conversion. This means that it will be materialized into
|
||||
* a temporary object.
|
||||
*/
|
||||
predicate isTemporary(Expr e) {
|
||||
e instanceof TemporaryObjectExpr
|
||||
or
|
||||
e.isPRValueCategory() and
|
||||
e.getUnspecifiedType() instanceof Class and
|
||||
not e.hasLValueToRValueConversion()
|
||||
}
|
||||
|
||||
/** Holds if `e` is written to a container. */
|
||||
predicate isStoredInContainer(Expr e) {
|
||||
exists(StdSequenceContainerInsert insert, Call call, int index |
|
||||
call = insert.getACallToThisFunction() and
|
||||
index = insert.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceContainerPush push, Call call, int index |
|
||||
call = push.getACallToThisFunction() and
|
||||
index = push.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceEmplace emplace, Call call, int index |
|
||||
call = emplace.getACallToThisFunction() and
|
||||
index = emplace.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceEmplaceBack emplaceBack, Call call, int index |
|
||||
call = emplaceBack.getACallToThisFunction() and
|
||||
index = emplaceBack.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` or a conversion of `e` has an lvalue-to-rvalue conversion.
|
||||
*/
|
||||
private predicate hasLValueToRValueConversion(Expr e) {
|
||||
e.getConversion*().hasLValueToRValueConversion() and
|
||||
not e instanceof ConditionalExpr // ConditionalExpr may be spuriously reported as having an lvalue-to-rvalue conversion
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value of `e` outlives the enclosing full expression. For
|
||||
* example, because the value is stored in a local variable.
|
||||
*/
|
||||
predicate outlivesFullExpr(Expr e) {
|
||||
not hasLValueToRValueConversion(e) and
|
||||
(
|
||||
any(Assignment assign).getRValue() = e
|
||||
or
|
||||
any(Variable v).getInitializer().getExpr() = e
|
||||
or
|
||||
any(ReturnStmt ret).getExpr() = e
|
||||
or
|
||||
exists(ConditionalExpr cond |
|
||||
outlivesFullExpr(cond) and
|
||||
[cond.getThen(), cond.getElse()] = e
|
||||
)
|
||||
or
|
||||
exists(BinaryOperation bin |
|
||||
outlivesFullExpr(bin) and
|
||||
bin.getAnOperand() = e and
|
||||
not bin instanceof ComparisonOperation
|
||||
)
|
||||
or
|
||||
exists(PointerFieldAccess fa |
|
||||
outlivesFullExpr(fa) and
|
||||
fa.getQualifier() = e
|
||||
)
|
||||
or
|
||||
exists(AddressOfExpr ao |
|
||||
outlivesFullExpr(ao) and
|
||||
ao.getOperand() = e
|
||||
)
|
||||
or
|
||||
exists(ClassAggregateLiteral aggr |
|
||||
outlivesFullExpr(aggr) and
|
||||
aggr.getAFieldExpr(_) = e
|
||||
)
|
||||
or
|
||||
exists(ArrayAggregateLiteral aggr |
|
||||
outlivesFullExpr(aggr) and
|
||||
aggr.getAnElementExpr(_) = e
|
||||
)
|
||||
or
|
||||
isStoredInContainer(e)
|
||||
)
|
||||
}
|
||||
@@ -14,81 +14,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.implementations.StdString
|
||||
import semmle.code.cpp.models.implementations.StdContainer
|
||||
|
||||
/**
|
||||
* Holds if `e` will be consumed by its parent as a glvalue and does not have
|
||||
* an lvalue-to-rvalue conversion. This means that it will be materialized into
|
||||
* a temporary object.
|
||||
*/
|
||||
predicate isTemporary(Expr e) {
|
||||
e instanceof TemporaryObjectExpr
|
||||
or
|
||||
e.isPRValueCategory() and
|
||||
e.getUnspecifiedType() instanceof Class and
|
||||
not e.hasLValueToRValueConversion()
|
||||
}
|
||||
|
||||
/** Holds if `e` is written to a container. */
|
||||
predicate isStoredInContainer(Expr e) {
|
||||
exists(StdSequenceContainerInsert insert, Call call, int index |
|
||||
call = insert.getACallToThisFunction() and
|
||||
index = insert.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceContainerPush push, Call call, int index |
|
||||
call = push.getACallToThisFunction() and
|
||||
index = push.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceEmplace emplace, Call call, int index |
|
||||
call = emplace.getACallToThisFunction() and
|
||||
index = emplace.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceEmplaceBack emplaceBack, Call call, int index |
|
||||
call = emplaceBack.getACallToThisFunction() and
|
||||
index = emplaceBack.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value of `e` outlives the enclosing full expression. For
|
||||
* example, because the value is stored in a local variable.
|
||||
*/
|
||||
predicate outlivesFullExpr(Expr e) {
|
||||
any(Assignment assign).getRValue() = e
|
||||
or
|
||||
any(Variable v).getInitializer().getExpr() = e
|
||||
or
|
||||
any(ReturnStmt ret).getExpr() = e
|
||||
or
|
||||
exists(ConditionalExpr cond |
|
||||
outlivesFullExpr(cond) and
|
||||
[cond.getThen(), cond.getElse()] = e
|
||||
)
|
||||
or
|
||||
exists(BinaryOperation bin |
|
||||
outlivesFullExpr(bin) and
|
||||
bin.getAnOperand() = e
|
||||
)
|
||||
or
|
||||
exists(ClassAggregateLiteral aggr |
|
||||
outlivesFullExpr(aggr) and
|
||||
aggr.getAFieldExpr(_) = e
|
||||
)
|
||||
or
|
||||
exists(ArrayAggregateLiteral aggr |
|
||||
outlivesFullExpr(aggr) and
|
||||
aggr.getAnElementExpr(_) = e
|
||||
)
|
||||
or
|
||||
isStoredInContainer(e)
|
||||
}
|
||||
import Temporaries
|
||||
|
||||
from Call c
|
||||
where
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>Calling <code>get</code> on a <code>std::unique_ptr</code> object returns a pointer to the underlying allocations.
|
||||
When the <code>std::unique_ptr</code> object is destroyed, the pointer returned by <code>get</code> is no
|
||||
longer valid. If the pointer is used after the <code>std::unique_ptr</code> object is destroyed, then the behavior is undefined.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Ensure that the pointer returned by <code>get</code> does not outlive the underlying <code>std::unique_ptr</code> object.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example gets a <code>std::unique_ptr</code> object, and then converts the resulting unique pointer to a
|
||||
pointer using <code>get</code> so that it can be passed to the <code>work</code> function.
|
||||
|
||||
However, the <code>std::unique_ptr</code> object is destroyed as soon as the call
|
||||
to <code>get</code> returns. This means that <code>work</code> is given a pointer to invalid memory.
|
||||
</p>
|
||||
|
||||
<sample src="UseOfUniquePointerAfterLifetimeEndsBad.cpp" />
|
||||
|
||||
<p>
|
||||
The following example fixes the above code by ensuring that the pointer returned by the call to <code>get</code> does
|
||||
not outlive the underlying <code>std::unique_ptr</code> objects. This ensures that the pointer passed to <code>work</code>
|
||||
points to valid memory.
|
||||
</p>
|
||||
|
||||
<sample src="UseOfUniquePointerAfterLifetimeEndsGood.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM50-CPP.+Do+not+access+freed+memory">MEM50-CPP. Do not access freed memory</a>.</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @name Use of unique pointer after lifetime ends
|
||||
* @description Referencing the contents of a unique pointer after the underlying object has expired may lead to unexpected behavior.
|
||||
* @kind problem
|
||||
* @precision high
|
||||
* @id cpp/use-of-unique-pointer-after-lifetime-ends
|
||||
* @problem.severity warning
|
||||
* @security-severity 8.8
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-416
|
||||
* external/cwe/cwe-664
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
import Temporaries
|
||||
|
||||
predicate isUniquePointerDerefFunction(Function f) {
|
||||
exists(PointerWrapper wrapper |
|
||||
f = wrapper.getAnUnwrapperFunction() and
|
||||
// We only want unique pointers as the memory behind share pointers may still be
|
||||
// alive after the shared pointer is destroyed.
|
||||
wrapper.(Class).hasQualifiedName(["std", "bsl"], "unique_ptr")
|
||||
)
|
||||
}
|
||||
|
||||
from Call c
|
||||
where
|
||||
outlivesFullExpr(c) and
|
||||
not c.isFromUninstantiatedTemplate(_) and
|
||||
isUniquePointerDerefFunction(c.getTarget()) and
|
||||
isTemporary(c.getQualifier().getFullyConverted())
|
||||
select c,
|
||||
"The underlying unique pointer object is destroyed after the call to '" + c.getTarget() +
|
||||
"' returns."
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <memory>
|
||||
std::unique_ptr<T> getUniquePointer();
|
||||
void work(const T*);
|
||||
|
||||
// BAD: the unique pointer is deallocated when `get` returns. So `work`
|
||||
// is given a pointer to invalid memory.
|
||||
void work_with_unique_ptr_bad() {
|
||||
const T* combined_string = getUniquePointer().get();
|
||||
work(combined_string);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <memory>
|
||||
std::unique_ptr<T> getUniquePointer();
|
||||
void work(const T*);
|
||||
|
||||
// GOOD: the unique pointer outlives the call to `work`. So the pointer
|
||||
// obtainted from `get` is valid.
|
||||
void work_with_unique_ptr_good() {
|
||||
auto combined_string = getUniquePointer();
|
||||
work(combined_string.get());
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `cpp/use-of-unique-pointer-after-lifetime-ends`, to detect uses of the contents unique pointers that will be destroyed immediately.
|
||||
3
cpp/ql/src/change-notes/released/0.9.1.md
Normal file
3
cpp/ql/src/change-notes/released/0.9.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.0
|
||||
lastReleaseVersion: 0.9.1
|
||||
|
||||
@@ -32,18 +32,41 @@ predicate hasReferenceInitializer(EnumConstant c) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `rnk`'th (1-based) enumeration constant in `e` that does not have a
|
||||
* reference initializer (i.e., an initializer that refers to an enumeration
|
||||
* constant from the same enumeration).
|
||||
*/
|
||||
EnumConstant getNonReferenceInitializedEnumConstantByRank(Enum e, int rnk) {
|
||||
result =
|
||||
rank[rnk](EnumConstant cand, int pos, string filepath, int startline, int startcolumn |
|
||||
e.getEnumConstant(pos) = cand and
|
||||
not hasReferenceInitializer(cand) and
|
||||
cand.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _)
|
||||
|
|
||||
cand order by pos, filepath, startline, startcolumn
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ec` is not the last enumeration constant in `e` that has a non-
|
||||
* reference initializer.
|
||||
*/
|
||||
predicate hasNextWithoutReferenceInitializer(Enum e, EnumConstant ec) {
|
||||
exists(int rnk |
|
||||
ec = getNonReferenceInitializedEnumConstantByRank(e, rnk) and
|
||||
exists(getNonReferenceInitializedEnumConstantByRank(e, rnk + 1))
|
||||
)
|
||||
}
|
||||
|
||||
// There exists another constant whose value is implicit, but it's
|
||||
// not the last one: the last value is okay to use to get the highest
|
||||
// enum value automatically. It can be followed by aliases though.
|
||||
predicate enumThatHasConstantWithImplicitValue(Enum e) {
|
||||
exists(EnumConstant ec, int pos |
|
||||
ec = e.getEnumConstant(pos) and
|
||||
exists(EnumConstant ec |
|
||||
ec = e.getAnEnumConstant() and
|
||||
not hasInitializer(ec) and
|
||||
exists(EnumConstant ec2, int pos2 |
|
||||
ec2 = e.getEnumConstant(pos2) and
|
||||
pos2 > pos and
|
||||
not hasReferenceInitializer(ec2)
|
||||
)
|
||||
hasNextWithoutReferenceInitializer(e, ec)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.9.1-dev
|
||||
version: 0.9.2-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
| b.c:5:3:5:34 | return ... | 10 |
|
||||
| c.c:2:3:2:20 | return ... | 5 |
|
||||
| e.c:2:3:2:19 | return ... | 17 |
|
||||
| g.c:3:3:3:12 | return ... | 20 |
|
||||
| i.c:3:3:3:12 | return ... | 30 |
|
||||
| i.c:8:3:8:12 | return ... | 31 |
|
||||
| i.c:13:3:13:12 | return ... | 32 |
|
||||
|
||||
@@ -3,4 +3,4 @@ static int g() {
|
||||
return 20;
|
||||
}
|
||||
#endif
|
||||
// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/f.pch --expect_errors
|
||||
// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/f.pch
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#ifdef SEEN_H
|
||||
static int h() {
|
||||
return 30; // [FALSE POSITIVE] (#pragma hdrstop bug, SEEN_H should not be defined in the precompiled header)
|
||||
return 30;
|
||||
}
|
||||
#endif
|
||||
#ifdef H1
|
||||
@@ -10,7 +10,7 @@ static int h1() {
|
||||
#endif
|
||||
#ifdef H2
|
||||
static int h2() {
|
||||
return 32; // [FALSE POSITIVE] (#pragma hdrstop bug, H2 should not be defined in the precompiled header)
|
||||
return 32;
|
||||
}
|
||||
#endif
|
||||
// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/h.pch
|
||||
|
||||
@@ -9,7 +9,6 @@ edges
|
||||
| test_free.cpp:83:12:83:12 | pointer to operator delete output argument | test_free.cpp:85:12:85:12 | a |
|
||||
| test_free.cpp:101:10:101:10 | pointer to free output argument | test_free.cpp:103:10:103:10 | a |
|
||||
| test_free.cpp:128:10:128:11 | pointer to free output argument | test_free.cpp:129:10:129:11 | * ... |
|
||||
| test_free.cpp:131:10:131:13 | pointer to free output argument | test_free.cpp:132:10:132:13 | access to array |
|
||||
| test_free.cpp:152:27:152:27 | pointer to free output argument | test_free.cpp:154:10:154:10 | a |
|
||||
| test_free.cpp:207:10:207:10 | pointer to free output argument | test_free.cpp:209:10:209:10 | a |
|
||||
nodes
|
||||
@@ -33,8 +32,6 @@ nodes
|
||||
| test_free.cpp:103:10:103:10 | a | semmle.label | a |
|
||||
| test_free.cpp:128:10:128:11 | pointer to free output argument | semmle.label | pointer to free output argument |
|
||||
| test_free.cpp:129:10:129:11 | * ... | semmle.label | * ... |
|
||||
| test_free.cpp:131:10:131:13 | pointer to free output argument | semmle.label | pointer to free output argument |
|
||||
| test_free.cpp:132:10:132:13 | access to array | semmle.label | access to array |
|
||||
| test_free.cpp:152:27:152:27 | pointer to free output argument | semmle.label | pointer to free output argument |
|
||||
| test_free.cpp:154:10:154:10 | a | semmle.label | a |
|
||||
| test_free.cpp:207:10:207:10 | pointer to free output argument | semmle.label | pointer to free output argument |
|
||||
@@ -51,6 +48,5 @@ subpaths
|
||||
| test_free.cpp:85:12:85:12 | a | test_free.cpp:83:12:83:12 | pointer to operator delete output argument | test_free.cpp:85:12:85:12 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:83:5:83:13 | delete | delete |
|
||||
| test_free.cpp:103:10:103:10 | a | test_free.cpp:101:10:101:10 | pointer to free output argument | test_free.cpp:103:10:103:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:101:5:101:8 | call to free | call to free |
|
||||
| test_free.cpp:129:10:129:11 | * ... | test_free.cpp:128:10:128:11 | pointer to free output argument | test_free.cpp:129:10:129:11 | * ... | Memory pointed to by '* ...' may already have been freed by $@. | test_free.cpp:128:5:128:8 | call to free | call to free |
|
||||
| test_free.cpp:132:10:132:13 | access to array | test_free.cpp:131:10:131:13 | pointer to free output argument | test_free.cpp:132:10:132:13 | access to array | Memory pointed to by 'access to array' may already have been freed by $@. | test_free.cpp:131:5:131:8 | call to free | call to free |
|
||||
| test_free.cpp:154:10:154:10 | a | test_free.cpp:152:27:152:27 | pointer to free output argument | test_free.cpp:154:10:154:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:152:22:152:25 | call to free | call to free |
|
||||
| test_free.cpp:209:10:209:10 | a | test_free.cpp:207:10:207:10 | pointer to free output argument | test_free.cpp:209:10:209:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:207:5:207:8 | call to free | call to free |
|
||||
|
||||
@@ -129,7 +129,7 @@ void test_ptr_deref(void ** a) {
|
||||
free(*a); // BAD
|
||||
*a = malloc(10);
|
||||
free(a[0]); // GOOD
|
||||
free(a[1]); // GOOD [FALSE POSITIVE]
|
||||
free(a[1]); // GOOD
|
||||
}
|
||||
|
||||
struct list {
|
||||
|
||||
@@ -9,4 +9,5 @@
|
||||
| test.cpp:188:39:188:42 | call to data | The underlying string object is destroyed after the call to 'data' returns. |
|
||||
| test.cpp:189:44:189:47 | call to data | The underlying string object is destroyed after the call to 'data' returns. |
|
||||
| test.cpp:191:29:191:32 | call to data | The underlying string object is destroyed after the call to 'data' returns. |
|
||||
| test.cpp:193:31:193:35 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. |
|
||||
| test.cpp:193:47:193:51 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. |
|
||||
| test.cpp:195:31:195:35 | call to c_str | The underlying string object is destroyed after the call to 'c_str' returns. |
|
||||
|
||||
@@ -190,6 +190,8 @@ const char* test1(bool b1, bool b2) {
|
||||
char* s9;
|
||||
s9 = std::string("hello").data(); // BAD
|
||||
|
||||
const char* s13 = b1 ? std::string("hello").c_str() : s1; // BAD
|
||||
|
||||
return std::string("hello").c_str(); // BAD
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
| test.cpp:156:15:156:15 | call to operator* | The underlying unique pointer object is destroyed after the call to 'operator*' returns. |
|
||||
| test.cpp:157:31:157:33 | call to get | The underlying unique pointer object is destroyed after the call to 'get' returns. |
|
||||
| test.cpp:159:32:159:32 | call to operator-> | The underlying unique pointer object is destroyed after the call to 'operator->' returns. |
|
||||
| test.cpp:160:35:160:37 | call to get | The underlying unique pointer object is destroyed after the call to 'get' returns. |
|
||||
| test.cpp:161:44:161:46 | call to get | The underlying unique pointer object is destroyed after the call to 'get' returns. |
|
||||
| test.cpp:163:25:163:27 | call to get | The underlying unique pointer object is destroyed after the call to 'get' returns. |
|
||||
| test.cpp:172:33:172:35 | call to get | The underlying unique pointer object is destroyed after the call to 'get' returns. |
|
||||
| test.cpp:174:32:174:34 | call to get | The underlying unique pointer object is destroyed after the call to 'get' returns. |
|
||||
| test.cpp:177:16:177:16 | call to operator* | The underlying unique pointer object is destroyed after the call to 'operator*' returns. |
|
||||
| test.cpp:177:36:177:36 | call to operator* | The underlying unique pointer object is destroyed after the call to 'operator*' returns. |
|
||||
| test.cpp:179:11:179:11 | call to operator* | The underlying unique pointer object is destroyed after the call to 'operator*' returns. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-416/UseOfUniquePointerAfterLifetimeEnds.ql
|
||||
@@ -0,0 +1,206 @@
|
||||
typedef unsigned long size_t;
|
||||
|
||||
namespace std {
|
||||
template<class T> struct remove_reference { typedef T type; };
|
||||
|
||||
template<class T> struct remove_reference<T &> { typedef T type; };
|
||||
|
||||
template<class T> struct remove_reference<T &&> { typedef T type; };
|
||||
|
||||
template<class T> using remove_reference_t = typename remove_reference<T>::type;
|
||||
|
||||
template< class T > std::remove_reference_t<T>&& move( T&& t );
|
||||
|
||||
template< class T > struct default_delete;
|
||||
|
||||
template<class T> struct add_lvalue_reference { typedef T& type; };
|
||||
}
|
||||
|
||||
// --- iterator ---
|
||||
|
||||
namespace std {
|
||||
template<class T> struct remove_const { typedef T type; };
|
||||
|
||||
template<class T> struct remove_const<const T> { typedef T type; };
|
||||
|
||||
// `remove_const_t<T>` removes any `const` specifier from `T`
|
||||
template<class T> using remove_const_t = typename remove_const<T>::type;
|
||||
|
||||
struct ptrdiff_t;
|
||||
|
||||
template<class I> struct iterator_traits;
|
||||
|
||||
template <class Category,
|
||||
class value_type,
|
||||
class difference_type = ptrdiff_t,
|
||||
class pointer_type = value_type*,
|
||||
class reference_type = value_type&>
|
||||
struct iterator {
|
||||
typedef Category iterator_category;
|
||||
|
||||
iterator();
|
||||
iterator(iterator<Category, remove_const_t<value_type> > const &other); // non-const -> const conversion constructor
|
||||
|
||||
iterator &operator++();
|
||||
iterator operator++(int);
|
||||
iterator &operator--();
|
||||
iterator operator--(int);
|
||||
bool operator==(iterator other) const;
|
||||
bool operator!=(iterator other) const;
|
||||
reference_type operator*() const;
|
||||
pointer_type operator->() const;
|
||||
iterator operator+(int);
|
||||
iterator operator-(int);
|
||||
iterator &operator+=(int);
|
||||
iterator &operator-=(int);
|
||||
int operator-(iterator);
|
||||
reference_type operator[](int);
|
||||
};
|
||||
|
||||
struct input_iterator_tag {};
|
||||
struct forward_iterator_tag : public input_iterator_tag {};
|
||||
struct bidirectional_iterator_tag : public forward_iterator_tag {};
|
||||
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
|
||||
}
|
||||
|
||||
// --- string ---
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
using nullptr_t = decltype(nullptr);
|
||||
|
||||
template<class T, class Deleter = std::default_delete<T>> class unique_ptr {
|
||||
public:
|
||||
using pointer = T*;
|
||||
using element_type = T;
|
||||
using deleter_type = Deleter;
|
||||
|
||||
constexpr unique_ptr() noexcept;
|
||||
constexpr unique_ptr(nullptr_t) noexcept;
|
||||
explicit unique_ptr(pointer p) noexcept;
|
||||
unique_ptr(unique_ptr&& u) noexcept;
|
||||
template<class U, class E> unique_ptr(unique_ptr<U, E>&& u) noexcept;
|
||||
unique_ptr(const unique_ptr&) = delete;
|
||||
|
||||
unique_ptr& operator=(unique_ptr&& u) noexcept;
|
||||
unique_ptr& operator=(std::nullptr_t) noexcept;
|
||||
template<class U, class E> unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept;
|
||||
|
||||
~unique_ptr();
|
||||
|
||||
pointer get() const noexcept;
|
||||
deleter_type& get_deleter() noexcept;
|
||||
const deleter_type& get_deleter() const noexcept;
|
||||
explicit operator bool() const noexcept;
|
||||
typename std::add_lvalue_reference<T>::type operator*() const;
|
||||
pointer operator->() const noexcept;
|
||||
pointer release() noexcept;
|
||||
void reset(pointer p = pointer()) noexcept;
|
||||
void swap(unique_ptr& u) noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
// --- vector ---
|
||||
|
||||
namespace std {
|
||||
template <class T> class allocator {
|
||||
public:
|
||||
allocator() throw();
|
||||
typedef size_t size_type;
|
||||
};
|
||||
|
||||
template<class T, class Allocator = allocator<T>>
|
||||
class vector {
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using size_type = unsigned int;
|
||||
using iterator = std::iterator<random_access_iterator_tag, T>;
|
||||
using const_iterator = std::iterator<random_access_iterator_tag, const T>;
|
||||
|
||||
vector() noexcept(noexcept(Allocator()));
|
||||
explicit vector(const Allocator&) noexcept;
|
||||
explicit vector(size_type n, const Allocator& = Allocator());
|
||||
vector(size_type n, const T& value, const Allocator& = Allocator());
|
||||
template<class InputIterator, class IteratorCategory = typename InputIterator::iterator_category> vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
|
||||
~vector();
|
||||
|
||||
void push_back(const T& x);
|
||||
void push_back(T&& x);
|
||||
|
||||
iterator insert(const_iterator position, const T& x);
|
||||
iterator insert(const_iterator position, T&& x);
|
||||
iterator insert(const_iterator position, size_type n, const T& x);
|
||||
template<class InputIterator> iterator insert(const_iterator position, InputIterator first, InputIterator last);
|
||||
|
||||
template <class... Args> iterator emplace (const_iterator position, Args&&... args);
|
||||
template <class... Args> void emplace_back (Args&&... args);
|
||||
};
|
||||
}
|
||||
|
||||
struct S {
|
||||
const char* s;
|
||||
};
|
||||
|
||||
void call(S*);
|
||||
|
||||
void call_by_value(S);
|
||||
void call_by_ref(S&);
|
||||
|
||||
std::unique_ptr<S> get_unique_ptr();
|
||||
|
||||
const S* test1(bool b1, bool b2) {
|
||||
auto s1 = *get_unique_ptr(); // GOOD
|
||||
auto s1a = &*get_unique_ptr(); // BAD
|
||||
auto s1b = get_unique_ptr().get(); // BAD
|
||||
auto s1c = get_unique_ptr()->s; // GOOD
|
||||
auto s1d = &(get_unique_ptr()->s); // BAD
|
||||
auto s2 = b1 ? get_unique_ptr().get() : nullptr; // BAD
|
||||
auto s3 = b2 ? nullptr :get_unique_ptr().get(); // BAD
|
||||
const S* s4;
|
||||
s4 = get_unique_ptr().get(); // BAD
|
||||
|
||||
call(get_unique_ptr().get()); // GOOD
|
||||
call(b1 ? get_unique_ptr().get() : nullptr); // GOOD
|
||||
call(b1 ? (b2 ? nullptr : get_unique_ptr().get()) : nullptr); // GOOD
|
||||
call_by_value(*get_unique_ptr()); // GOOD
|
||||
call_by_ref(*get_unique_ptr()); // GOOD
|
||||
|
||||
std::vector<S*> v1;
|
||||
v1.push_back(get_unique_ptr().get()); // BAD
|
||||
|
||||
S* s5[] = { get_unique_ptr().get() }; // BAD
|
||||
|
||||
S s6 = b1 ? *get_unique_ptr() : *get_unique_ptr(); // GOOD
|
||||
S& s7 = b1 ? *get_unique_ptr() : *get_unique_ptr(); // BAD
|
||||
|
||||
return &*get_unique_ptr(); // BAD
|
||||
}
|
||||
|
||||
void test2(bool b1, bool b2) {
|
||||
|
||||
std::unique_ptr<S> s = get_unique_ptr();
|
||||
auto s1 = s.get(); // GOOD
|
||||
auto s2 = b1 ? s.get() : nullptr; // GOOD
|
||||
auto s3 = b2 ? nullptr : s.get(); // GOOD
|
||||
const S* s4;
|
||||
s4 = s.get(); // GOOD
|
||||
|
||||
std::unique_ptr<S>& sRef = s;
|
||||
|
||||
auto s5 = sRef.get(); // GOOD
|
||||
auto s6 = b1 ? sRef.get() : nullptr; // GOOD
|
||||
auto s7 = b2 ? nullptr : sRef.get(); // GOOD
|
||||
const S* s8;
|
||||
s8 = sRef.get(); // GOOD
|
||||
|
||||
std::unique_ptr<S>&& sRefRef = get_unique_ptr();
|
||||
|
||||
auto s9 = sRefRef.get(); // GOOD
|
||||
auto s10 = b1 ? sRefRef.get() : nullptr; // GOOD
|
||||
auto s11 = b2 ? nullptr : sRefRef.get(); // GOOD
|
||||
const S* s12;
|
||||
s12 = sRefRef.get(); // GOOD
|
||||
}
|
||||
@@ -936,9 +936,8 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
{
|
||||
actions.RunProcess["dotnet --list-sdks"] = 0;
|
||||
actions.RunProcessOut["dotnet --list-sdks"] = "2.1.2 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]";
|
||||
actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0;
|
||||
actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir scratch/.dotnet"] = 0;
|
||||
actions.RunProcess[@"rm dotnet-install.sh"] = 0;
|
||||
actions.RunProcess[@"chmod u+x scratch/.dotnet/dotnet-install.sh"] = 0;
|
||||
actions.RunProcess[@"scratch/.dotnet/dotnet-install.sh --channel release --version 2.1.3 --install-dir scratch/.dotnet"] = 0;
|
||||
actions.RunProcess[@"scratch/.dotnet/dotnet --info"] = 0;
|
||||
actions.RunProcess[@"scratch/.dotnet/dotnet clean C:\Project/test.csproj"] = 0;
|
||||
actions.RunProcess[@"scratch/.dotnet/dotnet restore C:\Project/test.csproj"] = 0;
|
||||
@@ -960,10 +959,11 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
|
||||
</Project>");
|
||||
actions.LoadXml[@"C:\Project/test.csproj"] = xml;
|
||||
actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh"));
|
||||
actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "scratch/.dotnet/dotnet-install.sh"));
|
||||
actions.CreateDirectories.Add(@"scratch/.dotnet");
|
||||
|
||||
var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3");
|
||||
TestAutobuilderScript(autobuilder, 0, 8);
|
||||
TestAutobuilderScript(autobuilder, 0, 7);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -972,9 +972,8 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
actions.RunProcess["dotnet --list-sdks"] = 0;
|
||||
actions.RunProcessOut["dotnet --list-sdks"] = @"2.1.3 [C:\Program Files\dotnet\sdks]
|
||||
2.1.4 [C:\Program Files\dotnet\sdks]";
|
||||
actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0;
|
||||
actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir scratch/.dotnet"] = 0;
|
||||
actions.RunProcess[@"rm dotnet-install.sh"] = 0;
|
||||
actions.RunProcess[@"chmod u+x scratch/.dotnet/dotnet-install.sh"] = 0;
|
||||
actions.RunProcess[@"scratch/.dotnet/dotnet-install.sh --channel release --version 2.1.3 --install-dir scratch/.dotnet"] = 0;
|
||||
actions.RunProcess[@"scratch/.dotnet/dotnet --info"] = 0;
|
||||
actions.RunProcess[@"scratch/.dotnet/dotnet clean C:\Project/test.csproj"] = 0;
|
||||
actions.RunProcess[@"scratch/.dotnet/dotnet restore C:\Project/test.csproj"] = 0;
|
||||
@@ -996,10 +995,11 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
|
||||
</Project>");
|
||||
actions.LoadXml[@"C:\Project/test.csproj"] = xml;
|
||||
actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "dotnet-install.sh"));
|
||||
actions.DownloadFiles.Add(("https://dot.net/v1/dotnet-install.sh", "scratch/.dotnet/dotnet-install.sh"));
|
||||
actions.CreateDirectories.Add(@"scratch/.dotnet");
|
||||
|
||||
var autobuilder = CreateAutoBuilder(false, dotnetVersion: "2.1.3");
|
||||
TestAutobuilderScript(autobuilder, 0, 8);
|
||||
TestAutobuilderScript(autobuilder, 0, 7);
|
||||
}
|
||||
|
||||
private void TestDotnetVersionWindows(Action action, int commandsRun)
|
||||
|
||||
@@ -190,18 +190,23 @@ namespace Semmle.Autobuild.CSharp
|
||||
}
|
||||
else
|
||||
{
|
||||
var dotnetInstallPath = builder.Actions.PathCombine(FileUtils.GetTemporaryWorkingDirectory(
|
||||
builder.Actions.GetEnvironmentVariable,
|
||||
builder.Options.Language.UpperCaseName,
|
||||
out var shouldCleanUp), ".dotnet", "dotnet-install.sh");
|
||||
|
||||
var downloadDotNetInstallSh = BuildScript.DownloadFile(
|
||||
"https://dot.net/v1/dotnet-install.sh",
|
||||
"dotnet-install.sh",
|
||||
dotnetInstallPath,
|
||||
e => builder.Log(Severity.Warning, $"Failed to download 'dotnet-install.sh': {e.Message}"));
|
||||
|
||||
var chmod = new CommandBuilder(builder.Actions).
|
||||
RunCommand("chmod").
|
||||
Argument("u+x").
|
||||
Argument("dotnet-install.sh");
|
||||
Argument(dotnetInstallPath);
|
||||
|
||||
var install = new CommandBuilder(builder.Actions).
|
||||
RunCommand("./dotnet-install.sh").
|
||||
RunCommand(dotnetInstallPath).
|
||||
Argument("--channel").
|
||||
Argument("release").
|
||||
Argument("--version").
|
||||
@@ -209,11 +214,17 @@ namespace Semmle.Autobuild.CSharp
|
||||
Argument("--install-dir").
|
||||
Argument(path);
|
||||
|
||||
var buildScript = downloadDotNetInstallSh & chmod.Script & install.Script;
|
||||
|
||||
if (shouldCleanUp)
|
||||
{
|
||||
var removeScript = new CommandBuilder(builder.Actions).
|
||||
RunCommand("rm").
|
||||
Argument("dotnet-install.sh");
|
||||
Argument(dotnetInstallPath);
|
||||
buildScript &= removeScript.Script;
|
||||
}
|
||||
|
||||
return downloadDotNetInstallSh & chmod.Script & install.Script & BuildScript.Try(removeScript.Script);
|
||||
return buildScript;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
this.progressMonitor = new ProgressMonitor(logger);
|
||||
this.sourceDir = new DirectoryInfo(srcDir);
|
||||
|
||||
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName));
|
||||
packageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName, "packages"));
|
||||
legacyPackageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName, "legacypackages"));
|
||||
missingPackageDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName, "missingpackages"));
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
this.progressMonitor.FindingFiles(srcDir);
|
||||
|
||||
|
||||
var allFiles = GetAllFiles();
|
||||
var allFiles = GetAllFiles().ToList();
|
||||
var binaryFileExtensions = new HashSet<string>(new[] { ".dll", ".exe" }); // TODO: add more binary file extensions.
|
||||
var allNonBinaryFiles = allFiles.Where(f => !binaryFileExtensions.Contains(f.Extension.ToLowerInvariant())).ToList();
|
||||
var smallNonBinaryFiles = allNonBinaryFiles.SelectSmallFiles(progressMonitor).SelectFileNames();
|
||||
@@ -439,6 +439,25 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
files = files.Where(f => !f.FullName.StartsWith(options.DotNetPath, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
files = files.Where(f =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (f.Exists)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
progressMonitor.Log(Severity.Warning, $"File {f.FullName} could not be processed.");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
progressMonitor.Log(Severity.Warning, $"File {f.FullName} could not be processed: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
files = new FilePathFilter(sourceDir, progressMonitor).Filter(files);
|
||||
return files;
|
||||
}
|
||||
@@ -448,7 +467,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
/// with this source tree. Use a SHA1 of the directory name.
|
||||
/// </summary>
|
||||
/// <returns>The full path of the temp directory.</returns>
|
||||
private static string ComputeTempDirectory(string srcDir, string packages = "packages")
|
||||
private static string ComputeTempDirectory(string srcDir, string subfolderName)
|
||||
{
|
||||
var bytes = Encoding.Unicode.GetBytes(srcDir);
|
||||
var sha = SHA1.HashData(bytes);
|
||||
@@ -456,7 +475,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
foreach (var b in sha.Take(8))
|
||||
sb.AppendFormat("{0:x2}", b);
|
||||
|
||||
return Path.Combine(FileUtils.GetTemporaryWorkingDirectory(out var _), "GitHub", packages, sb.ToString());
|
||||
return Path.Combine(FileUtils.GetTemporaryWorkingDirectory(out var _), sb.ToString(), subfolderName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -704,7 +723,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
Parallel.ForEach(notYetDownloadedPackages, new ParallelOptions { MaxDegreeOfParallelism = options.Threads }, package =>
|
||||
{
|
||||
progressMonitor.NugetInstall(package);
|
||||
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package));
|
||||
using var tempDir = new TemporaryDirectory(ComputeTempDirectory(package, "missingpackages_workingdir"));
|
||||
var success = dotnet.New(tempDir.DirInfo.FullName);
|
||||
if (!success)
|
||||
{
|
||||
|
||||
@@ -80,11 +80,11 @@ internal sealed class StubVisitor : SymbolVisitor
|
||||
stubWriter.Write(explicitInterfaceType.GetQualifiedName());
|
||||
stubWriter.Write('.');
|
||||
if (writeName)
|
||||
stubWriter.Write(explicitInterfaceSymbol.GetName());
|
||||
stubWriter.Write(EscapeIdentifier(explicitInterfaceSymbol.GetName()));
|
||||
}
|
||||
else if (writeName)
|
||||
{
|
||||
stubWriter.Write(symbol.GetName());
|
||||
stubWriter.Write(EscapeIdentifier(symbol.GetName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,6 +557,9 @@ internal sealed class StubVisitor : SymbolVisitor
|
||||
});
|
||||
}
|
||||
|
||||
private static bool ExcludeMethod(IMethodSymbol symbol) =>
|
||||
symbol.Name == "<Clone>$";
|
||||
|
||||
private void StubMethod(IMethodSymbol symbol, IMethodSymbol? explicitInterfaceSymbol, IMethodSymbol? baseCtor)
|
||||
{
|
||||
var methodKind = explicitInterfaceSymbol is null ? symbol.MethodKind : explicitInterfaceSymbol.MethodKind;
|
||||
@@ -568,7 +571,7 @@ internal sealed class StubVisitor : SymbolVisitor
|
||||
MethodKind.Ordinary
|
||||
};
|
||||
|
||||
if (!relevantMethods.Contains(methodKind))
|
||||
if (!relevantMethods.Contains(methodKind) || ExcludeMethod(symbol))
|
||||
return;
|
||||
|
||||
StubAttributes(symbol.GetAttributes());
|
||||
|
||||
@@ -76,6 +76,26 @@ public int M1(ref readonly Guid guid) => throw null;
|
||||
Assert.Equal(expected, stub);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StubGeneratorEscapeMethodName()
|
||||
{
|
||||
// Setup
|
||||
const string source = @"
|
||||
public class MyTest {
|
||||
public int @default() { return 0; }
|
||||
}";
|
||||
|
||||
// Execute
|
||||
var stub = GenerateStub(source);
|
||||
|
||||
// Verify
|
||||
const string expected = @"public class MyTest {
|
||||
public int @default() => throw null;
|
||||
}
|
||||
";
|
||||
Assert.Equal(expected, stub);
|
||||
}
|
||||
|
||||
private static string GenerateStub(string source)
|
||||
{
|
||||
var st = CSharpSyntaxTree.ParseText(source);
|
||||
|
||||
@@ -144,20 +144,26 @@ namespace Semmle.Util
|
||||
return nested;
|
||||
}
|
||||
|
||||
public static string GetTemporaryWorkingDirectory(Func<string, string?> getEnvironmentVariable, string lang, out bool shouldCleanUp)
|
||||
{
|
||||
shouldCleanUp = false;
|
||||
var tempFolder = getEnvironmentVariable($"CODEQL_EXTRACTOR_{lang}_SCRATCH_DIR");
|
||||
|
||||
if (string.IsNullOrEmpty(tempFolder))
|
||||
private static readonly Lazy<string> tempFolderPath = new Lazy<string>(() =>
|
||||
{
|
||||
var tempPath = Path.GetTempPath();
|
||||
var name = Guid.NewGuid().ToString("N").ToUpper();
|
||||
tempFolder = Path.Combine(tempPath, "GitHub", name);
|
||||
shouldCleanUp = true;
|
||||
var tempFolder = Path.Combine(tempPath, "GitHub", name);
|
||||
Directory.CreateDirectory(tempFolder);
|
||||
return tempFolder;
|
||||
});
|
||||
|
||||
public static string GetTemporaryWorkingDirectory(Func<string, string?> getEnvironmentVariable, string lang, out bool shouldCleanUp)
|
||||
{
|
||||
var tempFolder = getEnvironmentVariable($"CODEQL_EXTRACTOR_{lang}_SCRATCH_DIR");
|
||||
if (!string.IsNullOrEmpty(tempFolder))
|
||||
{
|
||||
shouldCleanUp = false;
|
||||
return tempFolder;
|
||||
}
|
||||
|
||||
return tempFolder;
|
||||
shouldCleanUp = true;
|
||||
return tempFolderPath.Value;
|
||||
}
|
||||
|
||||
public static string GetTemporaryWorkingDirectory(out bool shouldCleanUp) =>
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.7.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.7.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.7.5
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.7.4
|
||||
lastReleaseVersion: 1.7.5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.7.5-dev
|
||||
version: 1.7.6-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.7.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.7.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.7.5
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.7.4
|
||||
lastReleaseVersion: 1.7.5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.7.5-dev
|
||||
version: 1.7.6-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -4,10 +4,11 @@ private string getPath(Assembly a) {
|
||||
not a.getCompilation().getOutputAssembly() = a and
|
||||
exists(string s | s = a.getFile().getAbsolutePath() |
|
||||
result =
|
||||
s.substring(s.indexOf("GitHub/packages/") + "GitHub/packages/".length() + 16, s.length())
|
||||
s.substring(s.indexOf("test-db/working/") + "test-db/working/".length() + 16 +
|
||||
"/packages".length(), s.length())
|
||||
or
|
||||
result = s and
|
||||
not exists(s.indexOf("GitHub/packages/"))
|
||||
not exists(s.indexOf("test-db/working/"))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "8.0.100"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,8 @@
|
||||
from create_database_utils import *
|
||||
|
||||
path = b'\xd2abcd.cs'
|
||||
|
||||
with open(path, 'w') as file:
|
||||
file.write('class X { }\n')
|
||||
|
||||
run_codeql_database_create([], lang="csharp", extra_args=["--extractor-option=buildless=true", "--extractor-option=cil=false"])
|
||||
@@ -4,10 +4,11 @@ private string getPath(Assembly a) {
|
||||
not a.getCompilation().getOutputAssembly() = a and
|
||||
exists(string s | s = a.getFile().getAbsolutePath() |
|
||||
result =
|
||||
s.substring(s.indexOf("GitHub/packages/") + "GitHub/packages/".length() + 16, s.length())
|
||||
s.substring(s.indexOf("test-db/working/") + "test-db/working/".length() + 16 +
|
||||
"/packages".length(), s.length())
|
||||
or
|
||||
result = s and
|
||||
not exists(s.indexOf("GitHub/packages/"))
|
||||
not exists(s.indexOf("test-db/working/"))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@ private string getPath(Assembly a) {
|
||||
not a.getCompilation().getOutputAssembly() = a and
|
||||
exists(string s | s = a.getFile().getAbsolutePath() |
|
||||
result =
|
||||
s.substring(s.indexOf("GitHub/packages/") + "GitHub/packages/".length() + 16, s.length())
|
||||
s.substring(s.indexOf("test-db/working/") + "test-db/working/".length() + 16 +
|
||||
"/packages".length(), s.length())
|
||||
or
|
||||
result = s and
|
||||
not exists(s.indexOf("GitHub/packages/"))
|
||||
not exists(s.indexOf("test-db/working/"))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,19 +4,9 @@ private string getPath(Assembly a) {
|
||||
not a.getCompilation().getOutputAssembly() = a and
|
||||
exists(string s | s = a.getFile().getAbsolutePath() |
|
||||
result =
|
||||
s.substring(s.indexOf("GitHub/packages/") + "GitHub/packages/".length() + 16, s.length())
|
||||
or
|
||||
result =
|
||||
s.substring(s.indexOf("GitHub/legacypackages/") + "GitHub/legacypackages/".length() + 16,
|
||||
s.length())
|
||||
// TODO: excluding all other assemblies from the test result as mono installations seem problematic on ARM runners.
|
||||
// or
|
||||
// result = s.substring(s.indexOf("lib/mono/") + "lib/mono/".length(), s.length())
|
||||
// or
|
||||
// result = s and
|
||||
// not exists(s.indexOf("GitHub/packages/")) and
|
||||
// not exists(s.indexOf("GitHub/legacypackages/")) and
|
||||
// not exists(s.indexOf("lib/mono/"))
|
||||
s.substring(s.indexOf("test-db/working/") + "test-db/working/".length() + 16 +
|
||||
"/legacypackages".length(), s.length())
|
||||
// TODO: include all other assemblies from the test results. Initially disable because mono installations were problematic on ARM runners.
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,11 @@ private string getPath(Assembly a) {
|
||||
not a.getCompilation().getOutputAssembly() = a and
|
||||
exists(string s | s = a.getFile().getAbsolutePath() |
|
||||
result =
|
||||
s.substring(s.indexOf("GitHub/packages/") + "GitHub/packages/".length() + 16, s.length())
|
||||
s.substring(s.indexOf("test-db/working/") + "test-db/working/".length() + 16 +
|
||||
"/packages".length(), s.length())
|
||||
or
|
||||
result = s and
|
||||
not exists(s.indexOf("GitHub/packages/"))
|
||||
not exists(s.indexOf("test-db/working/"))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.8.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.8.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
csharp/ql/lib/change-notes/released/0.8.5.md
Normal file
3
csharp/ql/lib/change-notes/released/0.8.5.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.8.5
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.8.4
|
||||
lastReleaseVersion: 0.8.5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 0.8.5-dev
|
||||
version: 0.8.6-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.8.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.8.4
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -11,17 +11,24 @@ without properly sanitizing the input first, allows for a cross-site scripting v
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>To guard against cross-site scripting, consider using contextual output encoding/escaping before
|
||||
writing user input to the page, or one of the other solutions that are mentioned in the
|
||||
references.</p>
|
||||
<p>
|
||||
To guard against cross-site scripting, consider using a library that provides suitable encoding
|
||||
functionality, such as the <code>System.Net.WebUtility</code> class, to sanitize the untrusted input before writing it to the page.
|
||||
For other possible solutions, see the references.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>The following example shows the page parameter being written directly to the server error page,
|
||||
leaving the website vulnerable to cross-site scripting.</p>
|
||||
|
||||
<sample src="XSS.cs" />
|
||||
<p>
|
||||
The following example shows the page parameter being written directly to the server error page,
|
||||
leaving the website vulnerable to cross-site scripting.
|
||||
</p>
|
||||
<sample src="XSSBad.cs" />
|
||||
<p>
|
||||
Sanitizing the user-controlled data using the <code>WebUtility.HtmlEncode</code> method prevents the vulnerability:
|
||||
</p>
|
||||
<sample src="XSSGood.cs" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
@@ -36,6 +43,5 @@ OWASP:
|
||||
Wikipedia: <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">Cross-site scripting</a>.
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
13
csharp/ql/src/Security Features/CWE-079/XSSGood.cs
Normal file
13
csharp/ql/src/Security Features/CWE-079/XSSGood.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using System.Net;
|
||||
|
||||
public class XSSHandler : IHttpHandler
|
||||
{
|
||||
public void ProcessRequest(HttpContext ctx)
|
||||
{
|
||||
string page = WebUtility.HtmlEncode(ctx.Request.QueryString["page"]);
|
||||
ctx.Response.Write(
|
||||
"The page \"" + page + "\" was not found.");
|
||||
}
|
||||
}
|
||||
195
csharp/ql/src/Telemetry/ExtractorInformation.ql
Normal file
195
csharp/ql/src/Telemetry/ExtractorInformation.ql
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* @name C# extraction information
|
||||
* @description Information about the extraction for a C# database
|
||||
* @kind metric
|
||||
* @tags summary telemetry
|
||||
* @id cs/telemetry/extraction-information
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.commons.Diagnostics
|
||||
|
||||
predicate fileCount(string key, int value) {
|
||||
key = "Number of files" and
|
||||
value = strictcount(File f)
|
||||
}
|
||||
|
||||
predicate fileCountByExtension(string key, int value) {
|
||||
exists(string extension |
|
||||
key = "Number of files with extension " + extension and
|
||||
value = strictcount(File f | f.getExtension() = extension)
|
||||
)
|
||||
}
|
||||
|
||||
predicate totalNumberOfLines(string key, int value) {
|
||||
key = "Total number of lines" and
|
||||
value = strictsum(File f | any() | f.getNumberOfLines())
|
||||
}
|
||||
|
||||
predicate numberOfLinesOfCode(string key, int value) {
|
||||
key = "Number of lines of code" and
|
||||
value = strictsum(File f | any() | f.getNumberOfLinesOfCode())
|
||||
}
|
||||
|
||||
predicate totalNumberOfLinesByExtension(string key, int value) {
|
||||
exists(string extension |
|
||||
key = "Total number of lines with extension " + extension and
|
||||
value = strictsum(File f | f.getExtension() = extension | f.getNumberOfLines())
|
||||
)
|
||||
}
|
||||
|
||||
predicate numberOfLinesOfCodeByExtension(string key, int value) {
|
||||
exists(string extension |
|
||||
key = "Number of lines of code with extension " + extension and
|
||||
value = strictsum(File f | f.getExtension() = extension | f.getNumberOfLinesOfCode())
|
||||
)
|
||||
}
|
||||
|
||||
predicate extractorDiagnostics(string key, int value) {
|
||||
exists(int severity |
|
||||
key = "Number of diagnostics with severity " + severity.toString() and
|
||||
value = strictcount(Diagnostic d | d.getSeverity() = severity)
|
||||
)
|
||||
}
|
||||
|
||||
CompilerError getAmbiguityCompilerError() {
|
||||
result.getSeverity() >= 3 and
|
||||
result.getTag() = ["CS0101", "CS0104", "CS0111", "CS0121", "CS0229"]
|
||||
}
|
||||
|
||||
predicate numberOfAmbiguityCompilerErrors(string key, int value) {
|
||||
value = count(getAmbiguityCompilerError()) and
|
||||
key = "Number of compiler reported ambiguity errors"
|
||||
}
|
||||
|
||||
predicate numberOfDistinctAmbiguityCompilerErrorMessages(string key, int value) {
|
||||
value = count(getAmbiguityCompilerError().getFullMessage()) and
|
||||
key = "Number of compiler reported ambiguity error messages"
|
||||
}
|
||||
|
||||
predicate extractionIsStandalone(string key, int value) {
|
||||
(
|
||||
value = 1 and
|
||||
extractionIsStandalone()
|
||||
or
|
||||
value = 0 and
|
||||
not extractionIsStandalone()
|
||||
) and
|
||||
key = "Is buildless extraction"
|
||||
}
|
||||
|
||||
signature module StatsSig {
|
||||
int getNumberOfOk();
|
||||
|
||||
int getNumberOfNotOk();
|
||||
|
||||
string getOkText();
|
||||
|
||||
string getNotOkText();
|
||||
}
|
||||
|
||||
module ReportStats<StatsSig Stats> {
|
||||
predicate numberOfOk(string key, int value) {
|
||||
value = Stats::getNumberOfOk() and
|
||||
key = "Number of " + Stats::getOkText()
|
||||
}
|
||||
|
||||
predicate numberOfNotOk(string key, int value) {
|
||||
value = Stats::getNumberOfNotOk() and
|
||||
key = "Number of " + Stats::getNotOkText()
|
||||
}
|
||||
|
||||
predicate percentageOfOk(string key, float value) {
|
||||
value = Stats::getNumberOfOk() * 100.0 / (Stats::getNumberOfOk() + Stats::getNumberOfNotOk()) and
|
||||
key = "Percentage of " + Stats::getOkText()
|
||||
}
|
||||
}
|
||||
|
||||
module CallTargetStats implements StatsSig {
|
||||
int getNumberOfOk() { result = count(Call c | exists(c.getTarget())) }
|
||||
|
||||
int getNumberOfNotOk() { result = count(Call c | not exists(c.getTarget())) }
|
||||
|
||||
string getOkText() { result = "calls with call target" }
|
||||
|
||||
string getNotOkText() { result = "calls with missing call target" }
|
||||
}
|
||||
|
||||
module ExprTypeStats implements StatsSig {
|
||||
int getNumberOfOk() { result = count(Expr e | not e.getType() instanceof UnknownType) }
|
||||
|
||||
int getNumberOfNotOk() { result = count(Expr e | e.getType() instanceof UnknownType) }
|
||||
|
||||
string getOkText() { result = "expressions with known type" }
|
||||
|
||||
string getNotOkText() { result = "expressions with unknown type" }
|
||||
}
|
||||
|
||||
module TypeMentionTypeStats implements StatsSig {
|
||||
int getNumberOfOk() { result = count(TypeMention t | not t.getType() instanceof UnknownType) }
|
||||
|
||||
int getNumberOfNotOk() { result = count(TypeMention t | t.getType() instanceof UnknownType) }
|
||||
|
||||
string getOkText() { result = "type mentions with known type" }
|
||||
|
||||
string getNotOkText() { result = "type mentions with unknown type" }
|
||||
}
|
||||
|
||||
module AccessTargetStats implements StatsSig {
|
||||
int getNumberOfOk() { result = count(Access a | exists(a.getTarget())) }
|
||||
|
||||
int getNumberOfNotOk() { result = count(Access a | not exists(a.getTarget())) }
|
||||
|
||||
string getOkText() { result = "access with target" }
|
||||
|
||||
string getNotOkText() { result = "access with missing target" }
|
||||
}
|
||||
|
||||
module ExprStats implements StatsSig {
|
||||
int getNumberOfOk() { result = count(Expr e | not e instanceof @unknown_expr) }
|
||||
|
||||
int getNumberOfNotOk() { result = count(Expr e | e instanceof @unknown_expr) }
|
||||
|
||||
string getOkText() { result = "expressions with known kind" }
|
||||
|
||||
string getNotOkText() { result = "expressions with unknown kind" }
|
||||
}
|
||||
|
||||
module CallTargetStatsReport = ReportStats<CallTargetStats>;
|
||||
|
||||
module ExprTypeStatsReport = ReportStats<ExprTypeStats>;
|
||||
|
||||
module TypeMentionTypeStatsReport = ReportStats<TypeMentionTypeStats>;
|
||||
|
||||
module AccessTargetStatsReport = ReportStats<AccessTargetStats>;
|
||||
|
||||
module ExprStatsReport = ReportStats<ExprStats>;
|
||||
|
||||
from string key, float value
|
||||
where
|
||||
fileCount(key, value) or
|
||||
fileCountByExtension(key, value) or
|
||||
totalNumberOfLines(key, value) or
|
||||
numberOfLinesOfCode(key, value) or
|
||||
totalNumberOfLinesByExtension(key, value) or
|
||||
numberOfLinesOfCodeByExtension(key, value) or
|
||||
extractorDiagnostics(key, value) or
|
||||
numberOfAmbiguityCompilerErrors(key, value) or
|
||||
numberOfDistinctAmbiguityCompilerErrorMessages(key, value) or
|
||||
extractionIsStandalone(key, value) or
|
||||
CallTargetStatsReport::numberOfOk(key, value) or
|
||||
CallTargetStatsReport::numberOfNotOk(key, value) or
|
||||
CallTargetStatsReport::percentageOfOk(key, value) or
|
||||
ExprTypeStatsReport::numberOfOk(key, value) or
|
||||
ExprTypeStatsReport::numberOfNotOk(key, value) or
|
||||
ExprTypeStatsReport::percentageOfOk(key, value) or
|
||||
TypeMentionTypeStatsReport::numberOfOk(key, value) or
|
||||
TypeMentionTypeStatsReport::numberOfNotOk(key, value) or
|
||||
TypeMentionTypeStatsReport::percentageOfOk(key, value) or
|
||||
AccessTargetStatsReport::numberOfOk(key, value) or
|
||||
AccessTargetStatsReport::numberOfNotOk(key, value) or
|
||||
AccessTargetStatsReport::percentageOfOk(key, value) or
|
||||
ExprStatsReport::numberOfOk(key, value) or
|
||||
ExprStatsReport::numberOfNotOk(key, value) or
|
||||
ExprStatsReport::percentageOfOk(key, value)
|
||||
select key, value
|
||||
3
csharp/ql/src/change-notes/released/0.8.5.md
Normal file
3
csharp/ql/src/change-notes/released/0.8.5.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.8.5
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.8.4
|
||||
lastReleaseVersion: 0.8.5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 0.8.5-dev
|
||||
version: 0.8.6-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -7,7 +7,7 @@ private import ModelEditor
|
||||
* A class of effectively public callables from source code.
|
||||
*/
|
||||
class PublicEndpointFromSource extends Endpoint {
|
||||
PublicEndpointFromSource() { this.fromSource() and not this.getFile() instanceof TestFile }
|
||||
PublicEndpointFromSource() { this.fromSource() and not this.getFile() instanceof TestRelatedFile }
|
||||
|
||||
override predicate isSource() { this instanceof SourceCallable }
|
||||
|
||||
|
||||
@@ -110,9 +110,9 @@ string supportedType(Endpoint endpoint) {
|
||||
}
|
||||
|
||||
string methodClassification(Call method) {
|
||||
method.getFile() instanceof TestFile and result = "test"
|
||||
method.getFile() instanceof TestRelatedFile and result = "test"
|
||||
or
|
||||
not method.getFile() instanceof TestFile and
|
||||
not method.getFile() instanceof TestRelatedFile and
|
||||
result = "source"
|
||||
}
|
||||
|
||||
@@ -129,3 +129,13 @@ private string qualifiedTypeName(string namespace, Type t) {
|
||||
private string qualifiedCallableName(string namespace, string type, Callable c) {
|
||||
exists(string name | hasQualifiedMethodName(c, namespace, type, name) | result = name)
|
||||
}
|
||||
|
||||
/** A file that is either a test file or is only used in tests. */
|
||||
class TestRelatedFile extends File {
|
||||
TestRelatedFile() {
|
||||
this instanceof TestFile
|
||||
or
|
||||
this.getAbsolutePath().matches(["%/test/%", "%/tests/%"]) and
|
||||
not this.getAbsolutePath().matches("%/ql/test/%") // allows our test cases to work
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,5 +4,6 @@ semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resour
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/Dapper/2.1.24/Dapper.csproj
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/ServiceStack/8.0.0/ServiceStack.csproj
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/ServiceStack.OrmLite.SqlServer/8.0.0/ServiceStack.OrmLite.SqlServer.csproj
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/EntityFramework/6.4.4/EntityFramework.csproj
|
||||
semmle-extractor-options: ${testdir}/../../../resources/stubs/System.Web.cs
|
||||
semmle-extractor-options: ${testdir}/../../../resources/stubs/EntityFramework.cs
|
||||
semmle-extractor-options: ${testdir}/../../../resources/stubs/EntityFrameworkCore.cs
|
||||
|
||||
@@ -141,6 +141,35 @@ summary
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;();;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Id];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;();;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Name];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Person].Property[EFTests.Person.Name]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;();;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Name];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Name]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Addresses].Element.Property[EFTests.Address.Id];SyntheticGlobal[EFTests.MyContext.Addresses#ReturnValue.Element.Property[EFTests.Address.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Addresses].Element.Property[EFTests.Address.Id];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Addresses].Element.Property[EFTests.Address.Id];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Addresses].Element.Property[EFTests.Address.Street];SyntheticGlobal[EFTests.MyContext.Addresses#ReturnValue.Element.Property[EFTests.Address.Street]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Addresses].Element.Property[EFTests.Address.Street];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Street]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Addresses].Element.Property[EFTests.Address.Street];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Street]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.AddressId];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.AddressId]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Id];SyntheticGlobal[EFTests.MyContext.Addresses#ReturnValue.Element.Property[EFTests.Address.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Id];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Id];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Street];SyntheticGlobal[EFTests.MyContext.Addresses#ReturnValue.Element.Property[EFTests.Address.Street]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Street];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Street]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Street];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Street]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Id];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.PersonId];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.PersonId]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Person].Property[EFTests.Person.Id];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Person].Property[EFTests.Person.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Person].Property[EFTests.Person.Id];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Person].Property[EFTests.Person.Name];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Person].Property[EFTests.Person.Name]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.PersonAddresses].Element.Property[EFTests.PersonAddressMap.Person].Property[EFTests.Person.Name];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Name]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Id];SyntheticGlobal[EFTests.MyContext.Addresses#ReturnValue.Element.Property[EFTests.Address.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Id];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Id];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Street];SyntheticGlobal[EFTests.MyContext.Addresses#ReturnValue.Element.Property[EFTests.Address.Street]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Street];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Address].Property[EFTests.Address.Street]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Street];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Addresses].Element.Property[EFTests.Address.Street]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Id];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Person].Property[EFTests.Person.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Id];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Id]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Name];SyntheticGlobal[EFTests.MyContext.PersonAddresses#ReturnValue.Element.Property[EFTests.PersonAddressMap.Person].Property[EFTests.Person.Name]];value;manual |
|
||||
| System.Data.Entity;DbContext;false;SaveChangesAsync;(System.Threading.CancellationToken);;Argument[this].Property[EFTests.MyContext.Persons].Element.Property[EFTests.Person.Name];SyntheticGlobal[EFTests.MyContext.Persons#ReturnValue.Element.Property[EFTests.Person.Name]];value;manual |
|
||||
neutral
|
||||
sourceNode
|
||||
sinkNode
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
semmle-extractor-options: /r:System.Data.dll /r:System.ComponentModel.Primitives.dll /r:System.ComponentModel.TypeConverter.dll ${testdir}/../../../resources/stubs/EntityFramework.cs ${testdir}/../../../resources/stubs/System.Data.cs /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll /r:System.Linq.dll
|
||||
semmle-extractor-options: ${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/System.ComponentModel.Annotations.cs ${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/System.ComponentModel.cs
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/EntityFramework/6.4.4/EntityFramework.csproj
|
||||
semmle-extractor-options: ${testdir}/../../../resources/stubs/EntityFrameworkCore.cs
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
sqlExpressions
|
||||
| sql.cs:44:23:44:44 | object creation of type MySqlCommand | sql.cs:44:40:44:43 | access to parameter text |
|
||||
| sql.cs:45:13:45:38 | ... = ... | sql.cs:45:35:45:38 | access to parameter text |
|
||||
| sql.cs:46:13:46:34 | object creation of type MySqlCommand | sql.cs:46:30:46:33 | access to parameter text |
|
||||
| sql.cs:46:13:46:53 | ... = ... | sql.cs:46:50:46:53 | access to parameter text |
|
||||
| sql.cs:54:23:54:44 | object creation of type MySqlCommand | sql.cs:54:40:54:43 | access to parameter text |
|
||||
| sql.cs:55:13:55:38 | ... = ... | sql.cs:55:35:55:38 | access to parameter text |
|
||||
| sql.cs:56:13:56:34 | object creation of type MySqlCommand | sql.cs:56:30:56:33 | access to parameter text |
|
||||
| sql.cs:56:13:56:53 | ... = ... | sql.cs:56:50:56:53 | access to parameter text |
|
||||
sqlCsvSinks
|
||||
| sql.cs:43:46:43:65 | object creation of type SqlCommand | sql.cs:43:61:43:64 | access to parameter text |
|
||||
| sql.cs:47:13:47:42 | object creation of type SqlDataAdapter | sql.cs:47:32:47:35 | access to parameter text |
|
||||
| sql.cs:48:13:48:47 | call to method ExecuteScalar | sql.cs:48:43:48:46 | access to parameter text |
|
||||
| sql.cs:49:13:49:75 | call to method ExecuteScalar | sql.cs:49:71:49:74 | access to parameter text |
|
||||
| sql.cs:53:46:53:65 | object creation of type SqlCommand | sql.cs:53:61:53:64 | access to parameter text |
|
||||
| sql.cs:57:13:57:50 | object creation of type SqlDataAdapter | sql.cs:57:32:57:35 | access to parameter text |
|
||||
| sql.cs:58:13:58:47 | call to method ExecuteScalar | sql.cs:58:43:58:46 | access to parameter text |
|
||||
| sql.cs:59:13:59:75 | call to method ExecuteScalar | sql.cs:59:71:59:74 | access to parameter text |
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
semmle-extractor-options: ${testdir}/../../../resources/stubs/System.Data.cs
|
||||
semmle-extractor-options: ${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/System.ComponentModel.cs
|
||||
semmle-extractor-options: /r:System.ComponentModel.TypeConverter.dll /r:System.Data.Common.dll
|
||||
semmle-extractor-options: /nostdlib /noconfig
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
|
||||
semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/System.Data.SqlClient/4.8.5/System.Data.SqlClient.csproj
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user