Merge branch 'main' into MybatisSqli

This commit is contained in:
retanoj
2022-12-07 21:19:08 +08:00
committed by GitHub
323 changed files with 4062 additions and 1570 deletions

View File

@@ -0,0 +1,26 @@
name: Find Latest CodeQL Bundle
description: Finds the URL of the latest released version of the CodeQL bundle.
outputs:
url:
description: The download URL of the latest CodeQL bundle release
value: ${{ steps.find-latest.outputs.url }}
runs:
using: composite
steps:
- name: Find Latest Release
id: find-latest
shell: pwsh
run: |
$Latest = gh release list --repo github/codeql-action --exclude-drafts --limit 1000 |
ForEach-Object { $C = $_ -split "`t"; return @{ type = $C[1]; tag = $C[2]; } } |
Where-Object { $_.type -eq 'Latest' }
$Tag = $Latest.tag
if ($Tag -eq '') {
throw 'Failed to find latest bundle release.'
}
Write-Output "Latest bundle tag is '${Tag}'."
"url=https://github.com/github/codeql-action/releases/download/${Tag}/codeql-bundle-linux64.tar.gz" >> $env:GITHUB_OUTPUT
env:
GITHUB_TOKEN: ${{ github.token }}

View File

@@ -11,7 +11,7 @@ on:
branches:
- main
paths:
# - "java/ql/src/utils/model-generator/**/*.*"
- "java/ql/src/utils/model-generator/**/*.*"
- ".github/workflows/mad_modelDiff.yml"
permissions:
@@ -61,8 +61,8 @@ jobs:
DATABASE=$2
cd codeql-$QL_VARIANT
SHORTNAME=`basename $DATABASE`
python java/ql/src/utils/model-generator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE $MODELS/${SHORTNAME}.model.yml
mv $MODELS/${SHORTNAME}.model.yml $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.model.yml
python java/ql/src/utils/model-generator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE ${SHORTNAME}.temp.model.yml
mv java/ql/lib/ext/generated/${SHORTNAME}.temp.model.yml $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.model.yml
cd ..
}

View File

@@ -22,11 +22,15 @@ jobs:
steps:
### Build the queries ###
- uses: actions/checkout@v3
- name: Find latest bundle
id: find-latest-bundle
uses: ./.github/actions/find-latest-bundle
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
with:
languages: javascript # does not matter
tools: ${{ steps.find-latest-bundle.outputs.url }}
- name: Get CodeQL version
id: get-codeql-version
run: |
@@ -138,6 +142,7 @@ jobs:
languages: ql
db-location: ${{ runner.temp }}/db
config-file: ./ql-for-ql-config.yml
tools: ${{ steps.find-latest-bundle.outputs.url }}
- name: Move pack cache
run: |
cp -r ${PACK}/.cache ql/ql/src/.cache

View File

@@ -580,5 +580,9 @@
"IncompleteMultiCharacterSanitization JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",
"ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll"
],
"EncryptionKeySizes Python/Java": [
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
]
}

View File

@@ -0,0 +1,6 @@
---
category: deprecated
---
* Deprecated `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
* Deprecated `semmle.code.cpp.security.TaintTrackingImpl`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.

View File

@@ -1,642 +1,21 @@
/**
* DEPRECATED: Use `semmle.code.cpp.ir.dataflow.TaintTracking` as a replacement.
*
* An IR taint tracking library that uses an IR DataFlow configuration to track
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
*/
import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.DataFlow3
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.ResolveCall
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.ir.dataflow.TaintTracking
private import semmle.code.cpp.ir.dataflow.TaintTracking2
private import semmle.code.cpp.ir.dataflow.TaintTracking3
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
private import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl as DefaultTaintTrackingImpl
/**
* A predictable instruction is one where an external user can predict
* the value. For example, a literal in the source code is considered
* predictable.
*/
private predicate predictableInstruction(Instruction instr) {
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
or
// This could be a conversion on a string literal
predictableInstruction(instr.(UnaryInstruction).getUnary())
}
deprecated predicate predictableOnlyFlow = DefaultTaintTrackingImpl::predictableOnlyFlow/1;
/**
* Functions that we should only allow taint to flow through (to the return
* value) if all but the source argument are 'predictable'. This is done to
* emulate the old security library's implementation rather than due to any
* strong belief that this is the right approach.
*
* Note that the list itself is not very principled; it consists of all the
* functions listed in the old security library's [default] `isPureFunction`
* that have more than one argument, but are not in the old taint tracking
* library's `returnArgument` predicate.
*/
predicate predictableOnlyFlow(string name) {
name =
[
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
"strtoul"
]
}
deprecated predicate tainted = DefaultTaintTrackingImpl::tainted/2;
private DataFlow::Node getNodeForSource(Expr source) {
isUserInput(source, _) and
result = getNodeForExpr(source)
}
deprecated predicate taintedIncludingGlobalVars =
DefaultTaintTrackingImpl::taintedIncludingGlobalVars/3;
private DataFlow::Node getNodeForExpr(Expr node) {
result = DataFlow::exprNode(node)
or
// Some of the sources in `isUserInput` are intended to match the value of
// an expression, while others (those modeled below) are intended to match
// the taint that propagates out of an argument, like the `char *` argument
// to `gets`. It's impossible here to tell which is which, but the "access
// to argv" source is definitely not intended to match an output argument,
// and it causes false positives if we let it.
//
// This case goes together with the similar (but not identical) rule in
// `nodeIsBarrierIn`.
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
not argv(node.(VariableAccess).getTarget())
}
deprecated predicate globalVarFromId = DefaultTaintTrackingImpl::globalVarFromId/1;
private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
override predicate isSink(DataFlow::Node sink) {
sink.asVariable() instanceof GlobalOrNamespaceVariable
}
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
}
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private class FromGlobalVarTaintTrackingCfg extends TaintTracking2::Configuration {
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) {
// This set of sources should be reasonably small, which is good for
// performance since the set of sinks is very large.
exists(ToGlobalVarTaintTrackingCfg otherCfg | otherCfg.hasFlowTo(source))
}
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
// Additional step for flow out of variables. There is no flow _into_
// variables in this configuration, so this step only serves to take flow
// out of a variable that's a source.
readsVariable(n2.asInstruction(), n1.asVariable())
}
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private predicate readsVariable(LoadInstruction load, Variable var) {
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
}
private predicate writesVariable(StoreInstruction store, Variable var) {
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
}
/**
* A variable that has any kind of upper-bound check anywhere in the program. This is
* biased towards being inclusive because there are a lot of valid ways of doing an
* upper bounds checks if we don't consider where it occurs, for example:
* ```
* if (x < 10) { sink(x); }
*
* if (10 > y) { sink(y); }
*
* if (z > 10) { z = 10; }
* sink(z);
* ```
*/
// TODO: This coarse overapproximation, ported from the old taint tracking
// library, could be replaced with an actual semantic check that a particular
// variable _access_ is guarded by an upper-bound check. We probably don't want
// to do this right away since it could expose a lot of FPs that were
// previously suppressed by this predicate by coincidence.
private predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access |
oper.getAnOperand() = access and
access.getTarget() = var and
// Comparing to 0 is not an upper bound check
not oper.getAnOperand().getValue() = "0"
)
}
private predicate nodeIsBarrierEqualityCandidate(
DataFlow::Node node, Operand access, Variable checkedVar
) {
readsVariable(node.asInstruction(), checkedVar) and
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
}
cached
private module Cached {
cached
predicate nodeIsBarrier(DataFlow::Node node) {
exists(Variable checkedVar |
readsVariable(node.asInstruction(), checkedVar) and
hasUpperBoundsCheck(checkedVar)
)
or
exists(Variable checkedVar, Operand access |
/*
* This node is guarded by a condition that forces the accessed variable
* to equal something else. For example:
* ```
* x = taintsource()
* if (x == 10) {
* taintsink(x); // not considered tainted
* }
* ```
*/
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
readsVariable(access.getDef(), checkedVar)
)
}
cached
predicate nodeIsBarrierIn(DataFlow::Node node) {
// don't use dataflow into taint sources, as this leads to duplicate results.
exists(Expr source | isUserInput(source, _) |
node = DataFlow::exprNode(source)
or
// This case goes together with the similar (but not identical) rule in
// `getNodeForSource`.
node = DataFlow::definitionByReferenceNodeFromArgument(source)
)
or
// don't use dataflow into binary instructions if both operands are unpredictable
exists(BinaryInstruction iTo |
iTo = node.asInstruction() and
not predictableInstruction(iTo.getLeft()) and
not predictableInstruction(iTo.getRight()) and
// propagate taint from either the pointer or the offset, regardless of predictability
not iTo instanceof PointerArithmeticInstruction
)
or
// don't use dataflow through calls to pure functions if two or more operands
// are unpredictable
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
iTo = node.asInstruction() and
isPureFunction(iTo.getStaticCallTarget().getName()) and
iFrom1 = iTo.getAnArgument() and
iFrom2 = iTo.getAnArgument() and
not predictableInstruction(iFrom1) and
not predictableInstruction(iFrom2) and
iFrom1 != iFrom2
)
}
cached
Element adjustedSink(DataFlow::Node sink) {
// TODO: is it more appropriate to use asConvertedExpr here and avoid
// `getConversion*`? Or will that cause us to miss some cases where there's
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
// pretend there was flow to the converted `Expr` for the sake of
// compatibility.
sink.asExpr().getConversion*() = result
or
// For compatibility, send flow from arguments to parameters, even for
// functions with no body.
exists(FunctionCall call, int i |
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
)
or
// For compatibility, send flow into a `Variable` if there is flow to any
// Load or Store of that variable.
exists(CopyInstruction copy |
copy.getSourceValue() = sink.asInstruction() and
(
readsVariable(copy, result) or
writesVariable(copy, result)
) and
not hasUpperBoundsCheck(result)
)
or
// For compatibility, send flow into a `NotExpr` even if it's part of a
// short-circuiting condition and thus might get skipped.
result.(NotExpr).getOperand() = sink.asExpr()
or
// Taint postfix and prefix crement operations when their operand is tainted.
result.(CrementOperation).getAnOperand() = sink.asExpr()
or
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
result.(AssignOperation).getAnOperand() = sink.asExpr()
or
result =
sink.asOperand()
.(SideEffectOperand)
.getUse()
.(ReadSideEffectInstruction)
.getArgumentDef()
.getUnconvertedResultExpression()
}
/**
* Step to return value of a modeled function when an input taints the
* dereference of the return value.
*/
cached
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
n1.asOperand() = callInput(call, modelIn) and
(
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
or
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
) and
call.getStaticCallTarget() = func and
modelOut.isReturnValueDeref() and
call = n2.asInstruction()
)
}
}
private import Cached
/**
* Holds if `tainted` may contain taint from `source`.
*
* A tainted expression is either directly user input, or is
* computed from user input in a way that users can probably
* control the exact output of the computation.
*
* This doesn't include data flow through global variables.
* If you need that you must call `taintedIncludingGlobalVars`.
*/
cached
predicate tainted(Expr source, Element tainted) {
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
cfg.hasFlow(getNodeForSource(source), sink) and
tainted = adjustedSink(sink)
)
}
/**
* Holds if `tainted` may contain taint from `source`, where the taint passed
* through a global variable named `globalVar`.
*
* A tainted expression is either directly user input, or is
* computed from user input in a way that users can probably
* control the exact output of the computation.
*
* This version gives the same results as tainted but also includes
* data flow through global variables.
*
* The parameter `globalVar` is the qualified name of the last global variable
* used to move the value from source to tainted. If the taint did not pass
* through a global variable, then `globalVar = ""`.
*/
cached
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
tainted(source, tainted) and
globalVar = ""
or
exists(
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg,
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
global = variableNode.getVariable() and
toCfg.hasFlow(getNodeForSource(source), variableNode) and
fromCfg.hasFlow(variableNode, sink) and
tainted = adjustedSink(sink) and
global = globalVarFromId(globalVar)
)
}
/**
* Gets the global variable whose qualified name is `id`. Use this predicate
* together with `taintedIncludingGlobalVars`. Example:
*
* ```
* exists(string varName |
* taintedIncludingGlobalVars(source, tainted, varName) and
* var = globalVarFromId(varName)
* )
* ```
*/
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
/**
* Provides definitions for augmenting source/sink pairs with data-flow paths
* between them. From a `@kind path-problem` query, import this module in the
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
* in place of `tainted`.
*
* Importing this module will also import the query predicates that contain the
* taint paths.
*/
module TaintedWithPath {
private newtype TSingleton = MkSingleton()
/**
* A taint-tracking configuration that matches sources and sinks in the same
* way as the `tainted` predicate.
*
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
* a characteristic predicate.
*/
class TaintTrackingConfiguration extends TSingleton {
/** Override this to specify which elements are sources in this configuration. */
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
/** Override this to specify which elements are sinks in this configuration. */
abstract predicate isSink(Element e);
/** Override this to specify which expressions are barriers in this configuration. */
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
/**
* Override this predicate to `any()` to allow taint to flow through global
* variables.
*/
predicate taintThroughGlobals() { none() }
/** Gets a textual representation of this element. */
string toString() { result = "TaintTrackingConfiguration" }
}
private class AdjustedConfiguration extends TaintTracking3::Configuration {
AdjustedConfiguration() { this = "AdjustedConfiguration" }
override predicate isSource(DataFlow::Node source) {
exists(TaintTrackingConfiguration cfg, Expr e |
cfg.isSource(e) and source = getNodeForExpr(e)
)
}
override predicate isSink(DataFlow::Node sink) {
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
}
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
// Steps into and out of global variables
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
)
or
additionalTaintStep(n1, n2)
}
override predicate isSanitizer(DataFlow::Node node) {
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
}
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
/*
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
* conversions, and a `Variable` maps to all loads and stores from it. Because
* the path node is part of the tuple that constitutes the alert, this leads
* to duplicate alerts.
*
* To avoid showing duplicates, we edit the graph to replace the final node
* coming from the data-flow library with a node that matches exactly the
* `Element` sink that's requested.
*
* The same is done for sources.
*/
private newtype TPathNode =
TWrapPathNode(DataFlow3::PathNode n) or
// There's a single newtype constructor for both sources and sinks since
// that makes it easiest to deal with the case where source = sink.
TEndpointPathNode(Element e) {
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
cfg.hasFlow(sourceNode, sinkNode)
|
sourceNode = getNodeForExpr(e) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
or
e = adjustedSink(sinkNode) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
)
}
/** An opaque type used for the nodes of a data-flow path. */
class PathNode extends TPathNode {
/** Gets a textual representation of this element. */
string toString() { none() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
none()
}
}
/**
* INTERNAL: Do not use.
*/
module Private {
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
/** Gets the element that `pathNode` wraps, if any. */
Element getElementFromPathNode(PathNode pathNode) {
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
result = node.asInstruction().getAst()
or
result = node.asOperand().getDef().getAst()
)
or
result = pathNode.(EndpointPathNode).inner()
}
}
private class WrapPathNode extends PathNode, TWrapPathNode {
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
override string toString() { result = this.inner().toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private class EndpointPathNode extends PathNode, TEndpointPathNode {
Expr inner() { this = TEndpointPathNode(result) }
override string toString() { result = this.inner().toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.inner()
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/** A PathNode whose `Element` is a source. It may also be a sink. */
private class InitialPathNode extends EndpointPathNode {
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
}
/** A PathNode whose `Element` is a sink. It may also be a source. */
private class FinalPathNode extends EndpointPathNode {
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
}
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) {
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
or
// To avoid showing trivial-looking steps, we _replace_ the last node instead
// of adding an edge out of it.
exists(WrapPathNode sinkNode |
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
or
// Same for the first node
exists(WrapPathNode sourceNode |
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
}
/**
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
* from `par` to `ret` within it, in the graph of data flow path explanations.
*/
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
or
// To avoid showing trivial-looking steps, we _replace_ the last node instead
// of adding an edge out of it.
exists(WrapPathNode sinkNode |
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), sinkNode.inner()) and
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
or
// Same for the first node
exists(WrapPathNode sourceNode |
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
}
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
key = "semmle.label" and val = n.toString()
}
/**
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
* this predicate.
*
* A tainted expression is either directly user input, or is computed from
* user input in a way that users can probably control the exact output of
* the computation.
*/
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
source = sourceNode.(InitialPathNode).inner() and
flowSource = getNodeForExpr(source) and
cfg.hasFlow(flowSource, flowSink) and
tainted = adjustedSink(flowSink) and
tainted = sinkNode.(FinalPathNode).inner()
)
}
private predicate isGlobalVariablePathNode(WrapPathNode n) {
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
}
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
edges(a, b) and
not isGlobalVariablePathNode(a) and
not isGlobalVariablePathNode(b)
}
/**
* Holds if `tainted` can be reached from a taint source without passing
* through a global variable.
*/
predicate taintedWithoutGlobals(Element tainted) {
exists(AdjustedConfiguration cfg, PathNode sourceNode, FinalPathNode sinkNode |
cfg.isSource(sourceNode.(WrapPathNode).inner().getNode()) and
edgesWithoutGlobals+(sourceNode, sinkNode) and
tainted = sinkNode.inner()
)
}
}
deprecated module TaintedWithPath = DefaultTaintTrackingImpl::TaintedWithPath;

View File

@@ -0,0 +1,644 @@
/**
* INTERNAL: Do not use.
*
* An IR taint tracking library that uses an IR DataFlow configuration to track
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
*/
import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.DataFlow3
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.ResolveCall
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.ir.dataflow.TaintTracking
private import semmle.code.cpp.ir.dataflow.TaintTracking2
private import semmle.code.cpp.ir.dataflow.TaintTracking3
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
/**
* A predictable instruction is one where an external user can predict
* the value. For example, a literal in the source code is considered
* predictable.
*/
private predicate predictableInstruction(Instruction instr) {
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
or
// This could be a conversion on a string literal
predictableInstruction(instr.(UnaryInstruction).getUnary())
}
/**
* Functions that we should only allow taint to flow through (to the return
* value) if all but the source argument are 'predictable'. This is done to
* emulate the old security library's implementation rather than due to any
* strong belief that this is the right approach.
*
* Note that the list itself is not very principled; it consists of all the
* functions listed in the old security library's [default] `isPureFunction`
* that have more than one argument, but are not in the old taint tracking
* library's `returnArgument` predicate.
*/
predicate predictableOnlyFlow(string name) {
name =
[
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
"strtoul"
]
}
private DataFlow::Node getNodeForSource(Expr source) {
isUserInput(source, _) and
result = getNodeForExpr(source)
}
private DataFlow::Node getNodeForExpr(Expr node) {
result = DataFlow::exprNode(node)
or
// Some of the sources in `isUserInput` are intended to match the value of
// an expression, while others (those modeled below) are intended to match
// the taint that propagates out of an argument, like the `char *` argument
// to `gets`. It's impossible here to tell which is which, but the "access
// to argv" source is definitely not intended to match an output argument,
// and it causes false positives if we let it.
//
// This case goes together with the similar (but not identical) rule in
// `nodeIsBarrierIn`.
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
not argv(node.(VariableAccess).getTarget())
}
private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
override predicate isSink(DataFlow::Node sink) {
sink.asVariable() instanceof GlobalOrNamespaceVariable
}
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
}
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private class FromGlobalVarTaintTrackingCfg extends TaintTracking2::Configuration {
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) {
// This set of sources should be reasonably small, which is good for
// performance since the set of sinks is very large.
exists(ToGlobalVarTaintTrackingCfg otherCfg | otherCfg.hasFlowTo(source))
}
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
// Additional step for flow out of variables. There is no flow _into_
// variables in this configuration, so this step only serves to take flow
// out of a variable that's a source.
readsVariable(n2.asInstruction(), n1.asVariable())
}
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
private predicate readsVariable(LoadInstruction load, Variable var) {
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
}
private predicate writesVariable(StoreInstruction store, Variable var) {
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
}
/**
* A variable that has any kind of upper-bound check anywhere in the program. This is
* biased towards being inclusive because there are a lot of valid ways of doing an
* upper bounds checks if we don't consider where it occurs, for example:
* ```
* if (x < 10) { sink(x); }
*
* if (10 > y) { sink(y); }
*
* if (z > 10) { z = 10; }
* sink(z);
* ```
*/
// TODO: This coarse overapproximation, ported from the old taint tracking
// library, could be replaced with an actual semantic check that a particular
// variable _access_ is guarded by an upper-bound check. We probably don't want
// to do this right away since it could expose a lot of FPs that were
// previously suppressed by this predicate by coincidence.
private predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access |
oper.getAnOperand() = access and
access.getTarget() = var and
// Comparing to 0 is not an upper bound check
not oper.getAnOperand().getValue() = "0"
)
}
private predicate nodeIsBarrierEqualityCandidate(
DataFlow::Node node, Operand access, Variable checkedVar
) {
readsVariable(node.asInstruction(), checkedVar) and
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
}
cached
private module Cached {
cached
predicate nodeIsBarrier(DataFlow::Node node) {
exists(Variable checkedVar |
readsVariable(node.asInstruction(), checkedVar) and
hasUpperBoundsCheck(checkedVar)
)
or
exists(Variable checkedVar, Operand access |
/*
* This node is guarded by a condition that forces the accessed variable
* to equal something else. For example:
* ```
* x = taintsource()
* if (x == 10) {
* taintsink(x); // not considered tainted
* }
* ```
*/
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
readsVariable(access.getDef(), checkedVar)
)
}
cached
predicate nodeIsBarrierIn(DataFlow::Node node) {
// don't use dataflow into taint sources, as this leads to duplicate results.
exists(Expr source | isUserInput(source, _) |
node = DataFlow::exprNode(source)
or
// This case goes together with the similar (but not identical) rule in
// `getNodeForSource`.
node = DataFlow::definitionByReferenceNodeFromArgument(source)
)
or
// don't use dataflow into binary instructions if both operands are unpredictable
exists(BinaryInstruction iTo |
iTo = node.asInstruction() and
not predictableInstruction(iTo.getLeft()) and
not predictableInstruction(iTo.getRight()) and
// propagate taint from either the pointer or the offset, regardless of predictability
not iTo instanceof PointerArithmeticInstruction
)
or
// don't use dataflow through calls to pure functions if two or more operands
// are unpredictable
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
iTo = node.asInstruction() and
isPureFunction(iTo.getStaticCallTarget().getName()) and
iFrom1 = iTo.getAnArgument() and
iFrom2 = iTo.getAnArgument() and
not predictableInstruction(iFrom1) and
not predictableInstruction(iFrom2) and
iFrom1 != iFrom2
)
}
cached
Element adjustedSink(DataFlow::Node sink) {
// TODO: is it more appropriate to use asConvertedExpr here and avoid
// `getConversion*`? Or will that cause us to miss some cases where there's
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
// pretend there was flow to the converted `Expr` for the sake of
// compatibility.
sink.asExpr().getConversion*() = result
or
// For compatibility, send flow from arguments to parameters, even for
// functions with no body.
exists(FunctionCall call, int i |
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
)
or
// For compatibility, send flow into a `Variable` if there is flow to any
// Load or Store of that variable.
exists(CopyInstruction copy |
copy.getSourceValue() = sink.asInstruction() and
(
readsVariable(copy, result) or
writesVariable(copy, result)
) and
not hasUpperBoundsCheck(result)
)
or
// For compatibility, send flow into a `NotExpr` even if it's part of a
// short-circuiting condition and thus might get skipped.
result.(NotExpr).getOperand() = sink.asExpr()
or
// Taint postfix and prefix crement operations when their operand is tainted.
result.(CrementOperation).getAnOperand() = sink.asExpr()
or
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
result.(AssignOperation).getAnOperand() = sink.asExpr()
or
result =
sink.asOperand()
.(SideEffectOperand)
.getUse()
.(ReadSideEffectInstruction)
.getArgumentDef()
.getUnconvertedResultExpression()
}
/**
* Step to return value of a modeled function when an input taints the
* dereference of the return value.
*/
cached
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
n1.asOperand() = callInput(call, modelIn) and
(
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
or
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
) and
call.getStaticCallTarget() = func and
modelOut.isReturnValueDeref() and
call = n2.asInstruction()
)
}
}
private import Cached
/**
* Holds if `tainted` may contain taint from `source`.
*
* A tainted expression is either directly user input, or is
* computed from user input in a way that users can probably
* control the exact output of the computation.
*
* This doesn't include data flow through global variables.
* If you need that you must call `taintedIncludingGlobalVars`.
*/
cached
predicate tainted(Expr source, Element tainted) {
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
cfg.hasFlow(getNodeForSource(source), sink) and
tainted = adjustedSink(sink)
)
}
/**
* Holds if `tainted` may contain taint from `source`, where the taint passed
* through a global variable named `globalVar`.
*
* A tainted expression is either directly user input, or is
* computed from user input in a way that users can probably
* control the exact output of the computation.
*
* This version gives the same results as tainted but also includes
* data flow through global variables.
*
* The parameter `globalVar` is the qualified name of the last global variable
* used to move the value from source to tainted. If the taint did not pass
* through a global variable, then `globalVar = ""`.
*/
cached
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
tainted(source, tainted) and
globalVar = ""
or
exists(
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg,
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
global = variableNode.getVariable() and
toCfg.hasFlow(getNodeForSource(source), variableNode) and
fromCfg.hasFlow(variableNode, sink) and
tainted = adjustedSink(sink) and
global = globalVarFromId(globalVar)
)
}
/**
* Gets the global variable whose qualified name is `id`. Use this predicate
* together with `taintedIncludingGlobalVars`. Example:
*
* ```
* exists(string varName |
* taintedIncludingGlobalVars(source, tainted, varName) and
* var = globalVarFromId(varName)
* )
* ```
*/
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
/**
* Provides definitions for augmenting source/sink pairs with data-flow paths
* between them. From a `@kind path-problem` query, import this module in the
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
* in place of `tainted`.
*
* Importing this module will also import the query predicates that contain the
* taint paths.
*/
module TaintedWithPath {
private newtype TSingleton = MkSingleton()
/**
* A taint-tracking configuration that matches sources and sinks in the same
* way as the `tainted` predicate.
*
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
* a characteristic predicate.
*/
class TaintTrackingConfiguration extends TSingleton {
/** Override this to specify which elements are sources in this configuration. */
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
/** Override this to specify which elements are sinks in this configuration. */
abstract predicate isSink(Element e);
/** Override this to specify which expressions are barriers in this configuration. */
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
/**
* Override this predicate to `any()` to allow taint to flow through global
* variables.
*/
predicate taintThroughGlobals() { none() }
/** Gets a textual representation of this element. */
string toString() { result = "TaintTrackingConfiguration" }
}
private class AdjustedConfiguration extends TaintTracking3::Configuration {
AdjustedConfiguration() { this = "AdjustedConfiguration" }
override predicate isSource(DataFlow::Node source) {
exists(TaintTrackingConfiguration cfg, Expr e |
cfg.isSource(e) and source = getNodeForExpr(e)
)
}
override predicate isSink(DataFlow::Node sink) {
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
}
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
// Steps into and out of global variables
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
)
or
additionalTaintStep(n1, n2)
}
override predicate isSanitizer(DataFlow::Node node) {
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
}
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
}
/*
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
* conversions, and a `Variable` maps to all loads and stores from it. Because
* the path node is part of the tuple that constitutes the alert, this leads
* to duplicate alerts.
*
* To avoid showing duplicates, we edit the graph to replace the final node
* coming from the data-flow library with a node that matches exactly the
* `Element` sink that's requested.
*
* The same is done for sources.
*/
private newtype TPathNode =
TWrapPathNode(DataFlow3::PathNode n) or
// There's a single newtype constructor for both sources and sinks since
// that makes it easiest to deal with the case where source = sink.
TEndpointPathNode(Element e) {
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
cfg.hasFlow(sourceNode, sinkNode)
|
sourceNode = getNodeForExpr(e) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
or
e = adjustedSink(sinkNode) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
)
}
/** An opaque type used for the nodes of a data-flow path. */
class PathNode extends TPathNode {
/** Gets a textual representation of this element. */
string toString() { none() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
none()
}
}
/**
* INTERNAL: Do not use.
*/
module Private {
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
/** Gets the element that `pathNode` wraps, if any. */
Element getElementFromPathNode(PathNode pathNode) {
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
result = node.asInstruction().getAst()
or
result = node.asOperand().getDef().getAst()
)
or
result = pathNode.(EndpointPathNode).inner()
}
}
private class WrapPathNode extends PathNode, TWrapPathNode {
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
override string toString() { result = this.inner().toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
private class EndpointPathNode extends PathNode, TEndpointPathNode {
Expr inner() { this = TEndpointPathNode(result) }
override string toString() { result = this.inner().toString() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
this.inner()
.getLocation()
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
/** A PathNode whose `Element` is a source. It may also be a sink. */
private class InitialPathNode extends EndpointPathNode {
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
}
/** A PathNode whose `Element` is a sink. It may also be a source. */
private class FinalPathNode extends EndpointPathNode {
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
}
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
query predicate edges(PathNode a, PathNode b) {
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
or
// To avoid showing trivial-looking steps, we _replace_ the last node instead
// of adding an edge out of it.
exists(WrapPathNode sinkNode |
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
or
// Same for the first node
exists(WrapPathNode sourceNode |
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
}
/**
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
* from `par` to `ret` within it, in the graph of data flow path explanations.
*/
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
or
// To avoid showing trivial-looking steps, we _replace_ the last node instead
// of adding an edge out of it.
exists(WrapPathNode sinkNode |
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), sinkNode.inner()) and
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
or
// Same for the first node
exists(WrapPathNode sourceNode |
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
ret.(WrapPathNode).inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
}
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
key = "semmle.label" and val = n.toString()
}
/**
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
* this predicate.
*
* A tainted expression is either directly user input, or is computed from
* user input in a way that users can probably control the exact output of
* the computation.
*/
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
source = sourceNode.(InitialPathNode).inner() and
flowSource = getNodeForExpr(source) and
cfg.hasFlow(flowSource, flowSink) and
tainted = adjustedSink(flowSink) and
tainted = sinkNode.(FinalPathNode).inner()
)
}
private predicate isGlobalVariablePathNode(WrapPathNode n) {
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
}
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
edges(a, b) and
not isGlobalVariablePathNode(a) and
not isGlobalVariablePathNode(b)
}
/**
* Holds if `tainted` can be reached from a taint source without passing
* through a global variable.
*/
predicate taintedWithoutGlobals(Element tainted) {
exists(AdjustedConfiguration cfg, PathNode sourceNode, FinalPathNode sinkNode |
cfg.isSource(sourceNode.(WrapPathNode).inner().getNode()) and
edgesWithoutGlobals+(sourceNode, sinkNode) and
tainted = sinkNode.inner()
)
}
}

View File

@@ -121,7 +121,9 @@ private predicate moveToDependingOnSide(Expr src, Expr dest) {
* (this is done to avoid false positives). Because of this we need to track if the tainted element came from an argument
* or not, and for that we use destFromArg
*/
private predicate betweenFunctionsValueMoveTo(Element src, Element dest, boolean destFromArg) {
deprecated private predicate betweenFunctionsValueMoveTo(
Element src, Element dest, boolean destFromArg
) {
not unreachable(src) and
not unreachable(dest) and
(
@@ -162,13 +164,13 @@ private predicate betweenFunctionsValueMoveTo(Element src, Element dest, boolean
// predicate folding for proper join-order
// bad magic: pushes down predicate that ruins join-order
pragma[nomagic]
private predicate resolveCallWithParam(Call call, Function called, int i, Parameter p) {
deprecated private predicate resolveCallWithParam(Call call, Function called, int i, Parameter p) {
called = resolveCall(call) and
p = called.getParameter(i)
}
/** A variable for which flow through is allowed. */
library class FlowVariable extends Variable {
deprecated library class FlowVariable extends Variable {
FlowVariable() {
(
this instanceof LocalScopeVariable or
@@ -179,11 +181,11 @@ library class FlowVariable extends Variable {
}
/** A local scope variable for which flow through is allowed. */
library class FlowLocalScopeVariable extends Variable {
deprecated library class FlowLocalScopeVariable extends Variable {
FlowLocalScopeVariable() { this instanceof LocalScopeVariable }
}
private predicate insideFunctionValueMoveTo(Element src, Element dest) {
deprecated private predicate insideFunctionValueMoveTo(Element src, Element dest) {
not unreachable(src) and
not unreachable(dest) and
(
@@ -324,7 +326,7 @@ private predicate unionAccess(Variable v, Field f, FieldAccess a) {
a.getQualifier() = v.getAnAccess()
}
GlobalOrNamespaceVariable globalVarFromId(string id) {
deprecated GlobalOrNamespaceVariable globalVarFromId(string id) {
if result instanceof NamespaceVariable
then id = result.getNamespace() + "::" + result.getName()
else id = result.getName()
@@ -353,7 +355,7 @@ private predicate hasUpperBoundsCheck(Variable var) {
}
cached
private predicate taintedWithArgsAndGlobalVars(
deprecated private predicate taintedWithArgsAndGlobalVars(
Element src, Element dest, boolean destFromArg, string globalVar
) {
isUserInput(src, _) and
@@ -395,7 +397,7 @@ private predicate taintedWithArgsAndGlobalVars(
* This doesn't include data flow through global variables.
* If you need that you must call taintedIncludingGlobalVars.
*/
predicate tainted(Expr source, Element tainted) {
deprecated predicate tainted(Expr source, Element tainted) {
taintedWithArgsAndGlobalVars(source, tainted, _, "")
}
@@ -410,7 +412,7 @@ predicate tainted(Expr source, Element tainted) {
* The parameter `globalVar` is the name of the last global variable used to move the
* value from source to tainted.
*/
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
deprecated predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
taintedWithArgsAndGlobalVars(source, tainted, _, globalVar)
}
@@ -541,14 +543,14 @@ private predicate returnArgument(Function f, int sourceArg) {
* targets a virtual method, simple data flow analysis is performed
* in order to identify target(s).
*/
Function resolveCall(Call call) {
deprecated Function resolveCall(Call call) {
result = call.getTarget()
or
result = call.(DataSensitiveCallExpr).resolve()
}
/** A data sensitive call expression. */
abstract library class DataSensitiveCallExpr extends Expr {
abstract deprecated library class DataSensitiveCallExpr extends Expr {
DataSensitiveCallExpr() { not unreachable(this) }
abstract Expr getSrc();
@@ -579,7 +581,7 @@ abstract library class DataSensitiveCallExpr extends Expr {
}
/** Call through a function pointer. */
library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
deprecated library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
override Expr getSrc() { result = getExpr() }
override Function resolve() {
@@ -588,7 +590,8 @@ library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
}
/** Call to a virtual function. */
library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr, FunctionCall {
deprecated library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr,
FunctionCall {
DataSensitiveOverriddenFunctionCall() {
exists(getTarget().(VirtualFunction).getAnOverridingFunction())
}

View File

@@ -17,8 +17,9 @@
import cpp
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.TaintTracking
import DataFlow::PathGraph
/**
* A function for opening a file.
@@ -46,18 +47,91 @@ class FileFunction extends FunctionWithWrappers {
override predicate interestingArg(int arg) { arg = 0 }
}
class TaintedPathConfiguration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) {
exists(FileFunction fileFunction | fileFunction.outermostWrapperFunctionCall(tainted, _))
Expr asSourceExpr(DataFlow::Node node) {
result = node.asConvertedExpr()
or
result = node.asDefiningArgument()
}
Expr asSinkExpr(DataFlow::Node node) {
result =
node.asOperand()
.(SideEffectOperand)
.getUse()
.(ReadSideEffectInstruction)
.getArgumentDef()
.getUnconvertedResultExpression()
}
/**
* Holds for a variable that has any kind of upper-bound check anywhere in the program.
* This is biased towards being inclusive and being a coarse overapproximation because
* there are a lot of valid ways of doing an upper bounds checks if we don't consider
* where it occurs, for example:
* ```cpp
* if (x < 10) { sink(x); }
*
* if (10 > y) { sink(y); }
*
* if (z > 10) { z = 10; }
* sink(z);
* ```
*/
predicate hasUpperBoundsCheck(Variable var) {
exists(RelationalOperation oper, VariableAccess access |
oper.getAnOperand() = access and
access.getTarget() = var and
// Comparing to 0 is not an upper bound check
not oper.getAnOperand().getValue() = "0"
)
}
class TaintedPathConfiguration extends TaintTracking::Configuration {
TaintedPathConfiguration() { this = "TaintedPathConfiguration" }
override predicate isSource(DataFlow::Node node) { isUserInput(asSourceExpr(node), _) }
override predicate isSink(DataFlow::Node node) {
exists(FileFunction fileFunction |
fileFunction.outermostWrapperFunctionCall(asSinkExpr(node), _)
)
}
override predicate isSanitizerIn(DataFlow::Node node) { this.isSource(node) }
override predicate isSanitizer(DataFlow::Node node) {
node.asExpr().(Call).getTarget().getUnspecifiedType() instanceof ArithmeticType
or
exists(LoadInstruction load, Variable checkedVar |
load = node.asInstruction() and
checkedVar = load.getSourceAddress().(VariableAddressInstruction).getAstVariable() and
hasUpperBoundsCheck(checkedVar)
)
}
predicate hasFilteredFlowPath(DataFlow::PathNode source, DataFlow::PathNode sink) {
this.hasFlowPath(source, sink) and
// The use of `isUserInput` in `isSink` in combination with `asSourceExpr` causes
// duplicate results. Filter these duplicates. The proper solution is to switch to
// using `LocalFlowSource` and `RemoteFlowSource`, but this currently only supports
// a subset of the cases supported by `isUserInput`.
not exists(DataFlow::PathNode source2 |
this.hasFlowPath(source2, sink) and
asSourceExpr(source.getNode()) = asSourceExpr(source2.getNode())
|
not exists(source.getNode().asConvertedExpr()) and exists(source2.getNode().asConvertedExpr())
)
}
}
from
FileFunction fileFunction, Expr taintedArg, Expr taintSource, PathNode sourceNode,
PathNode sinkNode, string taintCause, string callChain
FileFunction fileFunction, Expr taintedArg, Expr taintSource, TaintedPathConfiguration cfg,
DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, string taintCause, string callChain
where
taintedArg = asSinkExpr(sinkNode.getNode()) and
fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and
taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
cfg.hasFilteredFlowPath(sourceNode, sinkNode) and
taintSource = asSourceExpr(sourceNode.getNode()) and
isUserInput(taintSource, taintCause)
select taintedArg, sourceNode, sinkNode,
"This argument to a file access function is derived from $@ and then passed to " + callChain + ".",

View File

@@ -13,7 +13,7 @@
import cpp
import semmle.code.cpp.commons.Environment
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
import TaintedWithPath
/** A call that prints its arguments to `stdout`. */

View File

@@ -15,7 +15,7 @@
import cpp
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
import TaintedWithPath
class SqlLikeFunction extends FunctionWithWrappers {

View File

@@ -14,7 +14,7 @@
import cpp
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
import TaintedWithPath
predicate isProcessOperationExplanation(Expr arg, string processOperation) {

View File

@@ -16,7 +16,7 @@
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
import TaintedWithPath
/*

View File

@@ -16,7 +16,7 @@
import cpp
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
import TaintedWithPath
class Configuration extends TaintTrackingConfiguration {

View File

@@ -16,7 +16,7 @@
import cpp
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
import TaintedWithPath
class Configuration extends TaintTrackingConfiguration {

View File

@@ -12,7 +12,7 @@
import cpp
import semmle.code.cpp.commons.NullTermination
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
/** A user-controlled expression that may not be null terminated. */
class TaintSource extends VariableAccess {

View File

@@ -15,7 +15,7 @@
import cpp
import semmle.code.cpp.security.Overflow
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
import TaintedWithPath
import Bounded

View File

@@ -17,7 +17,7 @@
import cpp
import semmle.code.cpp.security.Overflow
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
predicate isMaxValue(Expr mie) {
exists(MacroInvocation mi |

View File

@@ -15,7 +15,7 @@
import cpp
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
/** Holds if `expr` might overflow. */
predicate outOfBoundsExpr(Expr expr, string kind) {

View File

@@ -12,7 +12,7 @@
* external/cwe/cwe-290
*/
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
import TaintedWithPath
predicate hardCodedAddressOrIP(StringLiteral txt) {

View File

@@ -12,7 +12,7 @@
* external/cwe/cwe-807
*/
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
import TaintedWithPath
predicate sensitiveCondition(Expr condition, Expr raise) {

View File

@@ -0,0 +1,2 @@
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted.ql:9,8-47)
WARNING: Predicate tainted has been deprecated and may be removed in future (tainted.ql:20,49-74)

View File

@@ -0,0 +1,2 @@
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted.ql:10,8-47)
WARNING: Predicate tainted has been deprecated and may be removed in future (tainted.ql:21,3-28)

View File

@@ -0,0 +1,2 @@
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (global.ql:8,3-47)
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (global.ql:12,3-53)

View File

@@ -1,3 +1,4 @@
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted.ql:5,3-29)
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv | |

View File

@@ -1,3 +1,8 @@
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:5,35-54)
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:12,7-26)
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:16,3-22)
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted_diff.ql:11,3-34)
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted_diff.ql:17,7-38)
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | AST only |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |

View File

@@ -1,3 +1,5 @@
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_ir.ql:3,35-50)
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_ir.ql:9,3-18)
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:40 | (const char *)... |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:6:25:29 | ! ... |

View File

@@ -1,19 +1,8 @@
edges
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection |
subpaths
nodes
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | semmle.label | ... + ... |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | semmle.label | fgets output argument |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... | semmle.label | (const char *)... |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | semmle.label | data |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | semmle.label | data |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection | semmle.label | data indirection |
subpaths
#select
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | This argument to a file access function is derived from $@ and then passed to fopen(filename). | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | user input (fgets) |
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | user input (fgets) |

View File

@@ -1,19 +1,24 @@
edges
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
subpaths
| test.c:31:22:31:25 | argv | test.c:32:11:32:18 | fileName indirection |
| test.c:37:17:37:24 | fileName | test.c:38:11:38:18 | fileName indirection |
| test.c:37:17:37:24 | scanf output argument | test.c:38:11:38:18 | fileName indirection |
| test.c:43:17:43:24 | fileName | test.c:44:11:44:18 | fileName indirection |
| test.c:43:17:43:24 | scanf output argument | test.c:44:11:44:18 | fileName indirection |
nodes
| test.c:9:23:9:26 | argv | semmle.label | argv |
| test.c:9:23:9:26 | argv | semmle.label | argv |
| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
| test.c:17:11:17:18 | fileName | semmle.label | fileName |
| test.c:17:11:17:18 | fileName | semmle.label | fileName |
| test.c:17:11:17:18 | fileName indirection | semmle.label | fileName indirection |
| test.c:31:22:31:25 | argv | semmle.label | argv |
| test.c:32:11:32:18 | fileName indirection | semmle.label | fileName indirection |
| test.c:37:17:37:24 | fileName | semmle.label | fileName |
| test.c:37:17:37:24 | scanf output argument | semmle.label | scanf output argument |
| test.c:38:11:38:18 | fileName indirection | semmle.label | fileName indirection |
| test.c:43:17:43:24 | fileName | semmle.label | fileName |
| test.c:43:17:43:24 | scanf output argument | semmle.label | scanf output argument |
| test.c:44:11:44:18 | fileName indirection | semmle.label | fileName indirection |
subpaths
#select
| test.c:17:11:17:18 | fileName | test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:9:23:9:26 | argv | user input (argv) |
| test.c:17:11:17:18 | fileName | test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:9:23:9:26 | argv | user input (argv) |
| test.c:32:11:32:18 | fileName | test.c:31:22:31:25 | argv | test.c:32:11:32:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:31:22:31:25 | argv | user input (argv) |
| test.c:38:11:38:18 | fileName | test.c:37:17:37:24 | fileName | test.c:38:11:38:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:37:17:37:24 | fileName | user input (scanf) |
| test.c:44:11:44:18 | fileName | test.c:43:17:43:24 | fileName | test.c:44:11:44:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:43:17:43:24 | fileName | user input (scanf) |

View File

@@ -11,3 +11,7 @@ FILE *fopen(const char *filename, const char *mode);
int sprintf(char *s, const char *format, ...);
size_t strlen(const char *s);
char *strncat(char *s1, const char *s2, size_t n);
int scanf(const char *format, ...);
void *malloc(size_t size);
double strtod(const char *ptr, char **endptr);
char *getenv(const char *name);

View File

@@ -26,5 +26,38 @@ int main(int argc, char** argv) {
strncat(fileName+len, fixed, FILENAME_MAX-len-1);
fopen(fileName, "wb+");
}
{
char *fileName = argv[1];
fopen(fileName, "wb+"); // BAD
}
{
char fileName[20];
scanf("%s", fileName);
fopen(fileName, "wb+"); // BAD
}
{
char *fileName = (char*)malloc(20 * sizeof(char));
scanf("%s", fileName);
fopen(fileName, "wb+"); // BAD
}
{
char *aNumber = getenv("A_NUMBER");
double number = strtod(aNumber, 0);
char fileName[20];
sprintf(fileName, "/foo/%f", number);
fopen(fileName, "wb+"); // GOOD
}
{
void read(const char *fileName);
read(argv[1]); // BAD [NOT DETECTED]
}
}
void read(char *fileName) {
fopen(fileName, "wb+");
}

View File

@@ -10,5 +10,5 @@
import csharp
from CatchClause catch
where catch.getCaughtExceptionType().hasQualifiedName("System.IO.IOException")
where catch.getCaughtExceptionType().hasQualifiedName("System.IO", "IOException")
select catch

View File

@@ -10,5 +10,5 @@
import csharp
from ObjectCreation new
where new.getObjectType().hasQualifiedName("System.Exception")
where new.getObjectType().hasQualifiedName("System", "Exception")
select new

View File

@@ -13,5 +13,5 @@
import csharp
from RefType type
where type.getABaseType+().hasQualifiedName("System.Collections.IEnumerator")
where type.getABaseType+().hasQualifiedName("System.Collections", "IEnumerator")
select type

View File

@@ -11,6 +11,6 @@ import csharp
from Field f, FieldRead read
where
f.hasName("VirtualAddress") and
f.getDeclaringType().hasQualifiedName("Mono.Cecil.PE.Section") and
f.getDeclaringType().hasQualifiedName("Mono.Cecil.PE", "Section") and
f = read.getTarget()
select read

View File

@@ -12,5 +12,5 @@ from MethodCall call, Method method
where
call.getTarget() = method and
method.hasName("MethodName") and
method.getDeclaringType().hasQualifiedName("Company.Class")
method.getDeclaringType().hasQualifiedName("Company", "Class")
select call

View File

@@ -17,6 +17,6 @@ where
add.hasName("Add") and
add.getDeclaringType()
.getUnboundDeclaration()
.hasQualifiedName("System.Collections.Generic.ICollection<>") and
.hasQualifiedName("System.Collections.Generic", "ICollection<>") and
call.getAnArgument() instanceof NullLiteral
select call

View File

@@ -11,6 +11,6 @@ import csharp
from Method override, Method base
where
base.hasName("ToString") and
base.getDeclaringType().hasQualifiedName("System.Object") and
base.getDeclaringType().hasQualifiedName("System", "Object") and
base.getAnOverrider() = override
select override

View File

@@ -9,5 +9,5 @@
import csharp
from ThrowStmt throw
where throw.getThrownExceptionType().getBaseClass*().hasQualifiedName("System.IO.IOException")
where throw.getThrownExceptionType().getBaseClass*().hasQualifiedName("System.IO", "IOException")
select throw

View File

@@ -19,11 +19,14 @@ private int numStmts(ForeachStmt fes) {
}
/** Holds if the type's qualified name is "System.Linq.Enumerable" */
predicate isEnumerableType(ValueOrRefType t) { t.hasQualifiedName("System.Linq.Enumerable") }
predicate isEnumerableType(ValueOrRefType t) { t.hasQualifiedName("System.Linq", "Enumerable") }
/** Holds if the type's qualified name starts with "System.Collections.Generic.IEnumerable" */
predicate isIEnumerableType(ValueOrRefType t) {
t.getQualifiedName().matches("System.Collections.Generic.IEnumerable%")
exists(string type |
t.hasQualifiedName("System.Collections.Generic", type) and
type.matches("IEnumerable%")
)
}
/**

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* `Element::hasQualifiedName/1` has been deprecated. Use `hasQualifiedName/2` or `hasQualifiedName/3` instead.

View File

@@ -5,6 +5,7 @@
*/
import csharp
private import semmle.code.csharp.commons.QualifiedName
/**
* An ASP.NET program element. Either an attribute (`AspAttribute`), an open
@@ -40,7 +41,7 @@ class AspAttribute extends AspElement, @asp_attribute { }
*/
class AspOpenTag extends AspElement, @asp_open_tag {
/** Either `>` or `/>`, depending on whether it's an empty tag. */
private string closeAngle() { if isEmpty() then result = "/>" else result = ">" }
private string closeAngle() { if this.isEmpty() then result = "/>" else result = ">" }
/** Gets the `i`th attribute of this open tag. */
AspAttribute getAttribute(int i) { asp_tag_attribute(this, i, _, result) }
@@ -58,9 +59,9 @@ class AspOpenTag extends AspElement, @asp_open_tag {
predicate isEmpty() { asp_tag_isempty(this) }
override string toString() {
if hasAttribute()
then result = "<" + getName() + " ..." + closeAngle()
else result = "<" + getName() + closeAngle()
if this.hasAttribute()
then result = "<" + this.getName() + " ..." + this.closeAngle()
else result = "<" + this.getName() + this.closeAngle()
}
}
@@ -75,9 +76,9 @@ class AspOpenTag extends AspElement, @asp_open_tag {
*/
class AspCloseTag extends AspElement, @asp_close_tag {
/** Gets the name of this close tag. */
string getName() { result = getBody() }
string getName() { result = this.getBody() }
override string toString() { result = "</" + getName() + ">" }
override string toString() { result = "</" + this.getName() + ">" }
}
/**
@@ -146,44 +147,49 @@ class AspDirective extends AspElement, @asp_directive {
string getName() { asp_directive_name(this, result) }
/** Holds if this directive has an attribute. */
predicate hasAttribute() { exists(getAttribute(_)) }
predicate hasAttribute() { exists(this.getAttribute(_)) }
override string toString() {
if hasAttribute()
then result = "<%@" + getName() + " ...%>"
else result = "<%@" + getName() + "%>"
if this.hasAttribute()
then result = "<%@" + this.getName() + " ...%>"
else result = "<%@" + this.getName() + "%>"
}
}
/** A quoted string used as an attribute in a tag. */
class AspQuotedString extends AspAttribute, @asp_quoted_string {
override string toString() {
if exists(getBody().indexOf("\""))
then result = "'" + getBody() + "'"
else result = "\"" + getBody() + "\""
if exists(this.getBody().indexOf("\""))
then result = "'" + this.getBody() + "'"
else result = "\"" + this.getBody() + "\""
}
}
/** Arbitrary text. It will be inserted into the document as is. */
class AspText extends AspElement, @asp_text {
override string toString() { result = getBody() }
override string toString() { result = this.getBody() }
}
/** An XML directive, such as a `DOCTYPE` declaration. */
class AspXmlDirective extends AspElement, @asp_xml_directive {
override string toString() { result = getBody() }
override string toString() { result = this.getBody() }
}
/**
* A 'Page' ASP directive.
*/
class PageDirective extends AspDirective {
PageDirective() { getName() = "Page" }
PageDirective() { this.getName() = "Page" }
/**
* Gets the 'CodeBehind' class from which this page inherits.
*/
ValueOrRefType getInheritedType() { result.getQualifiedName() = getInheritedTypeQualifiedName() }
ValueOrRefType getInheritedType() {
exists(string qualifier, string type |
result.hasQualifiedName(qualifier, type) and
splitQualifiedName(this.getInheritedTypeQualifiedName(), qualifier, type)
)
}
private string getInheritedTypeQualifiedName() {
// Relevant attributes:
@@ -192,11 +198,11 @@ class PageDirective extends AspDirective {
// provide a fallback namespace if `Inherits` does not have one
// - `CodeBehindFile`/`CodeFile`: used by tooling, but not semantically
// relevant at runtime
exists(string inherits | inherits = getAttributeByName("Inherits").getBody() |
exists(string inherits | inherits = this.getAttributeByName("Inherits").getBody() |
if inherits.indexOf(".") != -1
then result = inherits
else
exists(string className | className = getAttributeByName("ClassName").getBody() |
exists(string className | className = this.getAttributeByName("ClassName").getBody() |
// take everything up to and including the last .
className.prefix(className.indexOf(".", count(className.indexOf(".")) - 1, 0) + 1) +
inherits = result
@@ -210,7 +216,7 @@ class PageDirective extends AspDirective {
*/
class CodeBehindFile extends File {
CodeBehindFile() {
getExtension() = "aspx" and
this.getExtension() = "aspx" and
exists(PageDirective pageDir | pageDir.getLocation().getFile() = this)
}
@@ -222,5 +228,5 @@ class CodeBehindFile extends File {
/**
* Gets the 'CodeBehind' class from which this page inherits.
*/
ValueOrRefType getInheritedType() { result = getPageDirective().getInheritedType() }
ValueOrRefType getInheritedType() { result = this.getPageDirective().getInheritedType() }
}

View File

@@ -4,6 +4,7 @@
private import CIL
private import csharp as CS
private import semmle.code.csharp.commons.QualifiedName
private newtype ConsistencyCheck =
MissingEntityCheck() or
@@ -484,9 +485,12 @@ class InvalidOverride extends MethodViolation {
}
override string getMessage() {
result =
"Overridden method from " + base.getDeclaringType().getQualifiedName() +
" is not in a base type"
exists(string qualifier, string type |
base.getDeclaringType().hasQualifiedName(qualifier, type)
|
result =
"Overridden method from " + getQualifiedName(qualifier, type) + " is not in a base type"
)
}
}

View File

@@ -146,7 +146,7 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
/** Holds if this method is a destructor/finalizer. */
predicate isFinalizer() {
this.getOverriddenMethod*().getQualifiedName() = "System.Object.Finalize"
this.getOverriddenMethod*().hasQualifiedName("System", "Object", "Finalize")
}
/** Holds if this method is an operator. */
@@ -258,7 +258,7 @@ class Setter extends Accessor {
/** Holds if this setter is an `init` accessor. */
predicate isInitOnly() {
exists(Type t | t.getQualifiedName() = "System.Runtime.CompilerServices.IsExternalInit" |
exists(Type t | t.hasQualifiedName("System.Runtime.CompilerServices", "IsExternalInit") |
this.hasRequiredCustomModifier(t)
)
}

View File

@@ -4,6 +4,7 @@
import CIL
private import dotnet
private import semmle.code.csharp.commons.QualifiedName
/**
* Something that contains other types.
@@ -19,7 +20,7 @@ class TypeContainer extends DotNet::NamedElement, @cil_type_container {
/** A namespace. */
class Namespace extends DotNet::Namespace, TypeContainer, @namespace {
override string toString() { result = this.getQualifiedName() }
override string toString() { result = this.getFullName() }
override Namespace getParent() { result = this.getParentNamespace() }
@@ -52,7 +53,9 @@ class Type extends DotNet::Type, Declaration, TypeContainer, @cil_type {
override predicate hasQualifiedName(string qualifier, string name) {
name = this.getName() and
qualifier = this.getParent().getQualifiedName()
exists(string pqualifier, string pname | this.getParent().hasQualifiedName(pqualifier, pname) |
qualifier = getQualifiedName(pqualifier, pname)
)
}
override Location getALocation() { cil_type_location(this.getUnboundDeclaration(), result) }
@@ -65,8 +68,7 @@ class Type extends DotNet::Type, Declaration, TypeContainer, @cil_type {
/**
* Holds if this type is a member of the `System` namespace and has the name
* `name`. This is the same as `getQualifiedName() = "System.<name>"`, but is
* faster to compute.
* `name`.
*/
predicate isSystemType(string name) {
exists(Namespace system | this.getParent() = system |

View File

@@ -7,6 +7,7 @@ import Member
import Stmt
import Type
import exprs.Call
private import commons.QualifiedName
private import dotnet
private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.metrics.Complexity
@@ -459,6 +460,11 @@ class Operator extends Callable, Member, Attributable, @operator {
super.hasQualifiedName(qualifier, _) and
name = this.getFunctionName()
}
override predicate hasQualifiedName(string namespace, string type, string name) {
super.hasQualifiedName(namespace, type, _) and
name = this.getFunctionName()
}
}
/** A clone method on a record. */
@@ -996,7 +1002,10 @@ class LocalFunction extends Callable, Modifiable, Attributable, @local_function
override Callable getEnclosingCallable() { result = this.getStatement().getEnclosingCallable() }
override predicate hasQualifiedName(string qualifier, string name) {
qualifier = this.getEnclosingCallable().getQualifiedName() and
exists(string cqualifier, string type |
this.getEnclosingCallable().hasQualifiedName(cqualifier, type) and
qualifier = getQualifiedName(cqualifier, type)
) and
name = this.getName()
}

View File

@@ -15,6 +15,7 @@
import Location
import Namespace
private import commons.QualifiedName
private import dotnet
private import TypeRef
@@ -97,11 +98,18 @@ private string getTypeArgumentsNames(ConstructedGeneric cg) {
result = strictconcat(Type t, int i | t = cg.getTypeArgument(i) | t.getName(), "," order by i)
}
/** Gets the concatenation of the `getQualifiedName()` of type arguments. */
bindingset[t]
private string getFullName(Type t) {
exists(string qualifier, string name |
t.hasQualifiedName(qualifier, name) and
result = getQualifiedName(qualifier, name)
)
}
/** Gets the concatenation of the `getFullName` of type arguments. */
language[monotonicAggregates]
private string getTypeArgumentsQualifiedNames(ConstructedGeneric cg) {
result =
strictconcat(Type t, int i | t = cg.getTypeArgument(i) | t.getQualifiedName(), "," order by i)
result = strictconcat(Type t, int i | t = cg.getTypeArgument(i) | getFullName(t), "," order by i)
}
/**
@@ -159,7 +167,7 @@ class UnboundGenericType extends ValueOrRefType, UnboundGeneric {
)
or
not exists(this.getDeclaringType()) and
qualifier = this.getNamespace().getQualifiedName() and
qualifier = this.getNamespace().getFullName() and
name0 = this.getUndecoratedName()
)
}
@@ -424,7 +432,7 @@ class ConstructedType extends ValueOrRefType, ConstructedGeneric {
)
or
not exists(this.getDeclaringType()) and
qualifier = this.getNamespace().getQualifiedName() and
qualifier = this.getNamespace().getFullName() and
name0 = this.getUndecoratedName()
)
}
@@ -594,8 +602,8 @@ class ConstructedMethod extends Method, ConstructedGeneric {
result = this.getUndecoratedName() + "<" + getTypeArgumentsNames(this) + ">"
}
override predicate hasQualifiedName(string qualifier, string name) {
qualifier = this.getDeclaringType().getQualifiedName() and
override predicate hasQualifiedName(string namespace, string type, string name) {
this.getDeclaringType().hasQualifiedName(namespace, type) and
name = this.getUndecoratedName() + "<" + getTypeArgumentsQualifiedNames(this) + ">"
}

View File

@@ -107,10 +107,10 @@ private ValueOrRefType getAnInterestingBaseType(ValueOrRefType type) {
private predicate isInterestingBaseType(ValueOrRefType type, ValueOrRefType base) {
not base instanceof ObjectType and
not base.getQualifiedName() = "System.ValueType" and
not base.getQualifiedName() = "System.Delegate" and
not base.getQualifiedName() = "System.MulticastDelegate" and
not base.getQualifiedName() = "System.Enum" and
not base.hasQualifiedName("System", "ValueType") and
not base.hasQualifiedName("System", "Delegate") and
not base.hasQualifiedName("System", "MulticastDelegate") and
not base.hasQualifiedName("System", "Enum") and
exists(TypeMention tm | tm.getTarget() = type and tm.getType() = base)
}

View File

@@ -75,7 +75,7 @@ class BlockStmt extends Stmt, @block_stmt {
/** Holds if this block is the container of the global statements. */
predicate isGlobalStatementContainer() {
this.getEnclosingCallable().hasQualifiedName("Program.<Main>$")
this.getEnclosingCallable().hasQualifiedName("Program", "<Main>$")
}
override Stmt stripSingletonBlocks() {

View File

@@ -69,7 +69,7 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
)
or
not exists(this.getDeclaringType()) and
qualifier = this.getNamespace().getQualifiedName() and
qualifier = this.getNamespace().getFullName() and
name = this.getUndecoratedName()
}
@@ -825,7 +825,7 @@ class AnonymousClass extends Class {
* The `object` type, `System.Object`.
*/
class ObjectType extends Class {
ObjectType() { this.hasQualifiedName("System.Object") }
ObjectType() { this.hasQualifiedName("System", "Object") }
override string toStringWithTypes() { result = "object" }
@@ -836,7 +836,7 @@ class ObjectType extends Class {
* The `string` type, `System.String`.
*/
class StringType extends Class {
StringType() { this.hasQualifiedName("System.String") }
StringType() { this.hasQualifiedName("System", "String") }
override string toStringWithTypes() { result = "string" }

View File

@@ -39,7 +39,7 @@ class GeneratedAttributeFile extends GeneratedCodeFile {
class GeneratedNamespaceFile extends GeneratedCodeFile {
GeneratedNamespaceFile() {
exists(Namespace n | n.getATypeDeclaration().getFile() = this |
n.getQualifiedName() = "Microsoft.Xml.Serialization.GeneratedAssembly"
n.getFullName() = "Microsoft.Xml.Serialization.GeneratedAssembly"
)
}
}

View File

@@ -0,0 +1,34 @@
/**
* Provides predicates related to C# qualified names.
*/
/**
* Returns the concatenation of `qualifier` and `name`, separated by a dot.
*/
bindingset[qualifier, name]
string getQualifiedName(string qualifier, string name) {
if qualifier = "" then result = name else result = qualifier + "." + name
}
/**
* Returns the concatenation of `namespace`, `type` and `name`, separated by a dot.
*/
bindingset[namespace, type, name]
string getQualifiedName(string namespace, string type, string name) {
result = getQualifiedName(namespace, type) + "." + name
}
/**
* Holds if `qualifiedName` is the concatenation of `qualifier` and `name`, separated by a dot.
*/
bindingset[qualifiedName]
predicate splitQualifiedName(string qualifiedName, string qualifier, string name) {
exists(string nameSplitter | nameSplitter = "(.*)\\.([^\\.]+)$" |
qualifier = qualifiedName.regexpCapture(nameSplitter, 1) and
name = qualifiedName.regexpCapture(nameSplitter, 2)
or
not qualifiedName.regexpMatch(nameSplitter) and
qualifier = "" and
name = qualifiedName
)
}

View File

@@ -14,7 +14,7 @@ class TargetFrameworkAttribute extends Attribute {
Assembly assembly;
TargetFrameworkAttribute() {
this.getType().getQualifiedName() = "System.Runtime.Versioning.TargetFrameworkAttribute" and
this.getType().hasQualifiedName("System.Runtime.Versioning", "TargetFrameworkAttribute") and
assembly = this.getTarget()
}

View File

@@ -8,7 +8,7 @@ class MainMethod extends Method {
(
this.hasName("Main")
or
this.hasQualifiedName("Program.<Main>$")
this.hasQualifiedName("Program", "<Main>$")
) and
this.isStatic() and
(this.getReturnType() instanceof VoidType or this.getReturnType() instanceof IntType) and

View File

@@ -768,7 +768,7 @@ module Expressions {
nc.getOuterCompletion()
.(ThrowCompletion)
.getExceptionClass()
.hasQualifiedName("System.InvalidOperationException")
.hasQualifiedName("System", "InvalidOperationException")
)
)
}

View File

@@ -51,7 +51,7 @@ private class ThrowingCall extends NonReturningCall {
this =
any(MethodCall mc |
mc.getTarget()
.hasQualifiedName("System.Runtime.ExceptionServices.ExceptionDispatchInfo", "Throw") and
.hasQualifiedName("System.Runtime.ExceptionServices", "ExceptionDispatchInfo", "Throw") and
(
mc.hasNoArguments() and
c.getExceptionClass() instanceof SystemExceptionClass
@@ -85,8 +85,8 @@ private class DirectlyExitingCallable extends ExitingCallable {
DirectlyExitingCallable() {
this =
any(Method m |
m.hasQualifiedName("System.Environment", "Exit") or
m.hasQualifiedName("System.Windows.Forms.Application", "Exit")
m.hasQualifiedName("System", "Environment", "Exit") or
m.hasQualifiedName("System.Windows.Forms", "Application", "Exit")
)
}
}

View File

@@ -13,7 +13,7 @@ private class ExprNode = ControlFlow::Nodes::ExprNode;
* Holds if `pa` is an access to the `Length` property of an array.
*/
predicate systemArrayLengthAccess(PropertyAccess pa) {
propertyOverrides(pa.getTarget(), "System.Array", "Length")
propertyOverrides(pa.getTarget(), "System", "Array", "Length")
}
/**

View File

@@ -150,9 +150,9 @@ private module Impl {
/**
* Holds if property `p` matches `property` in `baseClass` or any overrides.
*/
predicate propertyOverrides(Property p, string baseClass, string property) {
predicate propertyOverrides(Property p, string namespace, string baseClass, string property) {
exists(Property p2 |
p2.getUnboundDeclaration().getDeclaringType().hasQualifiedName(baseClass) and
p2.getUnboundDeclaration().getDeclaringType().hasQualifiedName(namespace, baseClass) and
p2.hasName(property)
|
p.overridesOrImplementsOrEquals(p2)

View File

@@ -83,10 +83,10 @@ private module Impl {
*/
predicate containerSizeAccess(ExprNode e) {
exists(Property p | p = e.getExpr().(PropertyAccess).getTarget() |
propertyOverrides(p, "System.Collections.Generic.IEnumerable<>", "Count") or
propertyOverrides(p, "System.Collections.ICollection", "Count") or
propertyOverrides(p, "System.String", "Length") or
propertyOverrides(p, "System.Array", "Length")
propertyOverrides(p, "System.Collections.Generic", "IEnumerable<>", "Count") or
propertyOverrides(p, "System.Collections", "ICollection", "Count") or
propertyOverrides(p, "System", "String", "Length") or
propertyOverrides(p, "System", "Array", "Length")
)
or
e.getExpr() instanceof CountCall

View File

@@ -9,7 +9,7 @@ private import semmle.code.csharp.frameworks.system.Data
module Dapper {
/** The namespace `Dapper`. */
class DapperNamespace extends Namespace {
DapperNamespace() { this.hasQualifiedName("Dapper") }
DapperNamespace() { this.getFullName() = "Dapper" }
}
/** A class in `Dapper`. */

View File

@@ -20,7 +20,7 @@ module DataAnnotations {
class NotMappedAttribute extends Attribute {
NotMappedAttribute() {
this.getType()
.hasQualifiedName("System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute")
.hasQualifiedName("System.ComponentModel.DataAnnotations.Schema", "NotMappedAttribute")
}
}
}
@@ -37,11 +37,7 @@ private predicate isNotMapped(Attributable a) {
module EntityFramework {
/** An EF6 or EFCore namespace. */
class EFNamespace extends Namespace {
EFNamespace() {
this.getQualifiedName() = "Microsoft.EntityFrameworkCore"
or
this.getQualifiedName() = "System.Data.Entity"
}
EFNamespace() { this.getFullName() = ["Microsoft.EntityFrameworkCore", "System.Data.Entity"] }
}
/** A taint source where the data has come from a mapped property stored in the database. */
@@ -162,7 +158,7 @@ module EntityFramework {
/** The struct `Microsoft.EntityFrameworkCore.RawSqlString`. */
private class RawSqlStringStruct extends Struct {
RawSqlStringStruct() { this.getQualifiedName() = "Microsoft.EntityFrameworkCore.RawSqlString" }
RawSqlStringStruct() { this.hasQualifiedName("Microsoft.EntityFrameworkCore", "RawSqlString") }
/** Gets a conversion operator from `string` to `RawSqlString`. */
ConversionOperator getAConversionTo() {

View File

@@ -27,15 +27,15 @@ class FormatMethod extends Method {
or
(this.hasName("Write") or this.hasName("WriteLine")) and
(
declType.hasQualifiedName("System.Console")
declType.hasQualifiedName("System", "Console")
or
declType.hasQualifiedName("System.IO.TextWriter")
declType.hasQualifiedName("System.IO", "TextWriter")
or
declType.hasQualifiedName("System.Diagnostics.Debug") and
declType.hasQualifiedName("System.Diagnostics", "Debug") and
this.getParameter(1).getType() instanceof ArrayType
)
or
declType.hasQualifiedName("System.Diagnostics.Trace") and
declType.hasQualifiedName("System.Diagnostics", "Trace") and
(
this.hasName("TraceError") or
this.hasName("TraceInformation") or
@@ -43,14 +43,14 @@ class FormatMethod extends Method {
)
or
this.hasName("TraceInformation") and
declType.hasQualifiedName("System.Diagnostics.TraceSource")
declType.hasQualifiedName("System.Diagnostics", "TraceSource")
or
this.hasName("Print") and
declType.hasQualifiedName("System.Diagnostics.Debug")
declType.hasQualifiedName("System.Diagnostics", "Debug")
)
or
this.hasName("Assert") and
declType.hasQualifiedName("System.Diagnostics.Debug") and
declType.hasQualifiedName("System.Diagnostics", "Debug") and
this.getNumberOfParameters() = 4
)
}
@@ -65,7 +65,7 @@ class FormatMethod extends Method {
else
if
this.hasName("Assert") and
this.getDeclaringType().hasQualifiedName("System.Diagnostics.Debug")
this.getDeclaringType().hasQualifiedName("System.Diagnostics", "Debug")
then result = 2
else result = 0
}

View File

@@ -8,7 +8,7 @@ import csharp
module JsonNET {
/** The namespace `Newtonsoft.Json`. */
class JsonNETNamespace extends Namespace {
JsonNETNamespace() { this.hasQualifiedName("Newtonsoft.Json") }
JsonNETNamespace() { this.getFullName() = "Newtonsoft.Json" }
}
/** A class in `Newtonsoft.Json`. */

View File

@@ -4,7 +4,7 @@ import csharp
/** The `Moq.Language` Namespace. */
class MoqLanguageNamespace extends Namespace {
MoqLanguageNamespace() { this.hasQualifiedName("Moq.Language") }
MoqLanguageNamespace() { this.getFullName() = "Moq.Language" }
}
/**

View File

@@ -14,7 +14,7 @@ module NHibernate {
/** The interface `NHibernamte.ISession`. */
class ISessionInterface extends Interface {
ISessionInterface() { this.hasQualifiedName("NHibernate.ISession") }
ISessionInterface() { this.hasQualifiedName("NHibernate", "ISession") }
/** Gets a parameter that uses a mapped object. */
Parameter getAMappedObjectParameter() {

View File

@@ -34,13 +34,14 @@ class IDbCommandConstructionSqlExpr extends SqlExpr, ObjectCreation {
exists(InstanceConstructor ic | ic = this.getTarget() |
ic.getDeclaringType().getABaseType*() instanceof SystemDataIDbCommandInterface and
ic.getParameter(0).getType() instanceof StringType and
not ic.getDeclaringType()
.hasQualifiedName([
// Known sealed classes:
"System.Data.SqlClient.SqlCommand", "System.Data.Odbc.OdbcCommand",
"System.Data.OleDb.OleDbCommand", "System.Data.EntityClient.EntityCommand",
"System.Data.SQLite.SQLiteCommand"
])
not exists(Type t | t = ic.getDeclaringType() |
// Known sealed classes:
t.hasQualifiedName("System.Data.SqlClient", "SqlCommand") or
t.hasQualifiedName("System.Data.Odbc", "OdbcCommand") or
t.hasQualifiedName("System.Data.OleDb", "OleDbCommand") or
t.hasQualifiedName("System.Data.EntityClient", "EntityCommand") or
t.hasQualifiedName("System.Data.SQLite", "SQLiteCommand")
)
)
}

View File

@@ -217,7 +217,7 @@ class MicrosoftAspNetCoreMvcController extends Class {
.getType()
.getABaseType*()
// ApiControllerAttribute is derived from ControllerAttribute
.hasQualifiedName("Microsoft.AspNetCore.Mvc.ControllerAttribute")
.hasQualifiedName("Microsoft.AspNetCore.Mvc", "ControllerAttribute")
) and
not this.getABaseType*().getAnAttribute() instanceof
MicrosoftAspNetCoreMvcNonControllerAttribute
@@ -288,7 +288,7 @@ class MicrosoftAspNetCoreHttpHttpResponse extends Class {
/** An interface that is a wrapper around the collection of cookies in the response. */
class MicrosoftAspNetCoreHttpResponseCookies extends Interface {
MicrosoftAspNetCoreHttpResponseCookies() {
this.hasQualifiedName("Microsoft.AspNetCore.Http.IResponseCookies")
this.hasQualifiedName("Microsoft.AspNetCore.Http", "IResponseCookies")
}
/** Gets the `Append` method. */

View File

@@ -118,10 +118,10 @@ class MicrosoftOwinIOwinRequestClass extends Class {
result.hasName("Scheme")
}
/** Gets the `URI` property. */
/** Gets the `Uri` property. */
Property getUriProperty() {
result = this.getAProperty() and
result.hasName("URI")
result.hasName("Uri")
}
/** DEPRECATED: Alias for getUriProperty */

View File

@@ -5,32 +5,33 @@
import csharp
/**
* Holds if the object creation `oc` is the creation of the reference type with the specified `qualifiedName`, or a class derived from
* the class with the specified `qualifiedName`.
* Holds if the object creation `oc` is the creation of the reference type with the specified `qualifier` and `type`, or a class derived from
* the class with the specified `qualifier` and `type`.
*/
private predicate isCreatingObject(ObjectCreation oc, string qualifiedName) {
exists(RefType t | t = oc.getType() | t.getBaseClass*().hasQualifiedName(qualifiedName))
private predicate isCreatingObject(ObjectCreation oc, string qualifier, string type) {
exists(RefType t | t = oc.getType() | t.getBaseClass*().hasQualifiedName(qualifier, type))
}
/**
* Holds if the method call `mc` is returning the reference type with the specified `qualifiedName`.
* Holds if the method call `mc` is returning the reference type with the specified `qualifier` and `type`.
* and the target of the method call is a library method.
*/
private predicate isReturningObject(MethodCall mc, string qualifiedName) {
private predicate isReturningObject(MethodCall mc, string qualifier, string type) {
mc.getTarget().fromLibrary() and
exists(RefType t | t = mc.getType() | t.hasQualifiedName(qualifiedName))
exists(RefType t | t = mc.getType() | t.hasQualifiedName(qualifier, type))
}
/**
* Holds if the method call `mc` is a call on the library method target with the specified `qualifiedName` and `methodName`, and an argument at
* Holds if the method call `mc` is a call on the library method target with the specified `namespace`, `type` and `methodName`, and an argument at
* index `argumentIndex` has the specified value `argumentValue` (case-insensitive).
*/
bindingset[argumentValue]
private predicate isMethodCalledWithArg(
MethodCall mc, string qualifiedName, string methodName, int argumentIndex, string argumentValue
MethodCall mc, string namespace, string type, string methodName, int argumentIndex,
string argumentValue
) {
mc.getTarget().fromLibrary() and
mc.getTarget().hasQualifiedName(qualifiedName, methodName) and
mc.getTarget().hasQualifiedName(namespace, type, methodName) and
mc.getArgument(argumentIndex).getValue().toUpperCase() = argumentValue.toUpperCase()
}
@@ -60,13 +61,14 @@ class SymmetricAlgorithm extends Class {
* Note: not all of the class names are supported on all platforms.
*/
predicate isCreatingDES(Expr e) {
isCreatingObject(e, "System.Security.Cryptography.DES") or
isReturningObject(e, "System.Security.Cryptography.DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, "DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0,
isCreatingObject(e, "System.Security.Cryptography", "DES") or
isReturningObject(e, "System.Security.Cryptography", "DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0, "DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0,
"System.Security.Cryptography.DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, "DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"System.Security.Cryptography.DES")
}
@@ -75,21 +77,22 @@ predicate isCreatingDES(Expr e) {
* Note: not all of the class names are supported on all platforms.
*/
predicate isCreatingTripleDES(Expr e) {
isCreatingObject(e, "System.Security.Cryptography.TripleDES") or
isReturningObject(e, "System.Security.Cryptography.TripleDES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0,
isCreatingObject(e, "System.Security.Cryptography", "TripleDES") or
isReturningObject(e, "System.Security.Cryptography", "TripleDES") or
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0,
"TripleDES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, "3DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0, "3DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0,
"Triple DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0,
"System.Security.Cryptography.TripleDES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"TripleDES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, "3DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"3DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"Triple DES") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"System.Security.Cryptography.TripleDES")
}
@@ -98,13 +101,14 @@ predicate isCreatingTripleDES(Expr e) {
* Note: not all of the class names are supported on all platforms.
*/
predicate isCreatingRC2(Expr e) {
isCreatingObject(e, "System.Security.Cryptography.RC2") or
isReturningObject(e, "System.Security.Cryptography.RC2") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, "RC2") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0,
isCreatingObject(e, "System.Security.Cryptography", "RC2") or
isReturningObject(e, "System.Security.Cryptography", "RC2") or
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0, "RC2") or
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0,
"System.Security.Cryptography.RC2") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, "RC2") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"RC2") or
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"System.Security.Cryptography.RC2")
}
@@ -112,26 +116,26 @@ predicate isCreatingRC2(Expr e) {
* Holds if the expression 'e' creates Rijndael symmetric algorithm.
*/
predicate isCreatingRijndael(Expr e) {
isCreatingObject(e, "System.Security.Cryptography.Rijndael") or
isReturningObject(e, "System.Security.Cryptography.Rijndael") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0,
isCreatingObject(e, "System.Security.Cryptography", "Rijndael") or
isReturningObject(e, "System.Security.Cryptography", "Rijndael") or
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0,
"Rijndael") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0,
"RijndaelManaged") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0,
"System.Security.Cryptography.Rijndael") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0,
"System.Security.Cryptography.RijndaelManaged") or
isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "SymmetricAlgorithm", "Create", 0,
"System.Security.Cryptography.SymmetricAlgorithm") or // this creates Rijndael
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"Rijndael") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"System.Security.Cryptography.Rijndael") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"RijndaelManaged") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"System.Security.Cryptography.RijndaelManaged") or
isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0,
isMethodCalledWithArg(e, "System.Security.Cryptography", "CryptoConfig", "CreateFromName", 0,
"System.Security.Cryptography.SymmetricAlgorithm") // this creates Rijndael
}

View File

@@ -36,7 +36,7 @@ class SystemTextRegularExpressionsRegexClass extends SystemTextRegularExpression
*/
class RegexGlobalTimeout extends MethodCall {
RegexGlobalTimeout() {
this.getTarget().hasQualifiedName("System.AppDomain.SetData") and
this.getTarget().hasQualifiedName("System.AppDomain", "SetData") and
this.getArgumentForName("name").getValue() = "REGEX_DEFAULT_MATCH_TIMEOUT"
}
}

View File

@@ -2,6 +2,7 @@
import csharp
import semmle.code.csharp.frameworks.Test
private import semmle.code.csharp.commons.QualifiedName
/** A class that is an NUnit test fixture */
class NUnitFixture extends TestClass {
@@ -38,7 +39,11 @@ class NUnitTestMethod extends TestMethod {
expected.getTarget() = this
|
if expected.getArgument(0).getType() instanceof StringType
then result.hasQualifiedName(expected.getArgument(0).getValue())
then
exists(string qualifier, string type |
result.hasQualifiedName(qualifier, type) and
splitQualifiedName(expected.getArgument(0).getValue(), qualifier, type)
)
else result = expected.getArgument(0).(TypeofExpr).getTypeAccess().getTarget()
)
}
@@ -56,11 +61,13 @@ class NUnitFile extends TestFile {
/** An attribute of type `NUnit.Framework.ValueSourceAttribute`. */
class ValueSourceAttribute extends Attribute {
ValueSourceAttribute() { this.getType().hasQualifiedName("NUnit.Framework.ValueSourceAttribute") }
ValueSourceAttribute() {
this.getType().hasQualifiedName("NUnit.Framework", "ValueSourceAttribute")
}
/** Holds if the first argument is the target type. */
private predicate typeSpecified() {
this.getArgument(0).getType().(Class).hasQualifiedName("System.Type") and
this.getArgument(0).getType().(Class).hasQualifiedName("System", "Type") and
this.getArgument(1).getType() instanceof StringType
}
@@ -88,12 +95,12 @@ class ValueSourceAttribute extends Attribute {
/** An attribute of type `NUnit.Framework.TestCaseSourceAttribute`. */
class TestCaseSourceAttribute extends Attribute {
TestCaseSourceAttribute() {
this.getType().hasQualifiedName("NUnit.Framework.TestCaseSourceAttribute")
this.getType().hasQualifiedName("NUnit.Framework", "TestCaseSourceAttribute")
}
/** Holds if the first argument is the target type. */
private predicate typeSpecified() {
this.getArgument(0).getType().(Class).hasQualifiedName("System.Type") and
this.getArgument(0).getType().(Class).hasQualifiedName("System", "Type") and
this.getArgument(1).getType() instanceof StringType
}
@@ -120,7 +127,7 @@ class TestCaseSourceAttribute extends Attribute {
/** The `NUnit.Framework.Assert` class. */
class NUnitAssertClass extends Class {
NUnitAssertClass() { this.hasQualifiedName("NUnit.Framework.Assert") }
NUnitAssertClass() { this.hasQualifiedName("NUnit.Framework", "Assert") }
/** Gets a `Null(object, ...)` method. */
Method getANullMethod() {
@@ -179,5 +186,5 @@ class NUnitAssertClass extends Class {
/** The `NUnit.Framework.AssertionException` class. */
class AssertionExceptionClass extends Class {
AssertionExceptionClass() { this.hasQualifiedName("NUnit.Framework.AssertionException") }
AssertionExceptionClass() { this.hasQualifiedName("NUnit.Framework", "AssertionException") }
}

View File

@@ -5,7 +5,7 @@ import semmle.code.csharp.frameworks.Test
/** The `Microsoft.VisualStudio.TestTools.UnitTesting` namespace. */
class VSTestNamespace extends Namespace {
VSTestNamespace() { this.hasQualifiedName("Microsoft.VisualStudio.TestTools.UnitTesting") }
VSTestNamespace() { this.getFullName() = "Microsoft.VisualStudio.TestTools.UnitTesting" }
}
/** A class that contains test methods. */

View File

@@ -5,7 +5,7 @@ import semmle.code.csharp.frameworks.Test
/** The `Xunit` namespace. */
class XUnitNamespace extends Namespace {
XUnitNamespace() { this.hasQualifiedName("Xunit") }
XUnitNamespace() { this.getFullName() = "Xunit" }
}
/** An xUnit test attribute. */

View File

@@ -4,6 +4,7 @@
*/
import csharp
private import semmle.code.csharp.commons.QualifiedName
private import semmle.code.csharp.dataflow.flowsources.Remote
private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.dataflow.FlowSummary
@@ -70,8 +71,21 @@ class ExternalApiDataNode extends DataFlow::Node {
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
int getIndex() { result = i }
/** Gets the description of the callable being called. */
string getCallableDescription() { result = this.getCallable().getQualifiedName() }
/** Holds if the callable being use has name `name` and has qualifier `qualifier`. */
predicate hasQualifiedName(string qualifier, string name) {
this.getCallable().hasQualifiedName(qualifier, name)
}
/**
* DEPRECATED: Use hasQualifiedName/2 instead.
*
* Gets the description of the callable being called.
*/
deprecated string getCallableDescription() {
exists(string qualifier, string name |
this.hasQualifiedName(qualifier, name) and result = getQualifiedName(qualifier, name)
)
}
}
/** DEPRECATED: Alias for ExternalApiDataNode */

View File

@@ -162,7 +162,7 @@ class TaintToObjectTypeTrackingConfig extends TaintTracking2::Configuration {
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodCall mc, Method m |
m = mc.getTarget() and
m.getDeclaringType().hasQualifiedName("System.Type") and
m.getDeclaringType().hasQualifiedName("System", "Type") and
m.hasName("GetType") and
m.isStatic() and
n1.asExpr() = mc.getArgument(0) and

View File

@@ -46,7 +46,7 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
class ArchiveFullNameSource extends Source {
ArchiveFullNameSource() {
exists(PropertyAccess pa | this.asExpr() = pa |
pa.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression.ZipArchiveEntry") and
pa.getTarget().getDeclaringType().hasQualifiedName("System.IO.Compression", "ZipArchiveEntry") and
pa.getTarget().getName() = "FullName"
)
}
@@ -56,7 +56,7 @@ class ArchiveFullNameSource extends Source {
class ExtractToFileArgSink extends Sink {
ExtractToFileArgSink() {
exists(MethodCall mc |
mc.getTarget().hasQualifiedName("System.IO.Compression.ZipFileExtensions", "ExtractToFile") and
mc.getTarget().hasQualifiedName("System.IO.Compression", "ZipFileExtensions", "ExtractToFile") and
this.asExpr() = mc.getArgumentForName("destinationFileName")
)
}
@@ -66,9 +66,9 @@ class ExtractToFileArgSink extends Sink {
class FileOpenArgSink extends Sink {
FileOpenArgSink() {
exists(MethodCall mc |
mc.getTarget().hasQualifiedName("System.IO.File", "Open") or
mc.getTarget().hasQualifiedName("System.IO.File", "OpenWrite") or
mc.getTarget().hasQualifiedName("System.IO.File", "Create")
mc.getTarget().hasQualifiedName("System.IO", "File", "Open") or
mc.getTarget().hasQualifiedName("System.IO", "File", "OpenWrite") or
mc.getTarget().hasQualifiedName("System.IO", "File", "Create")
|
this.asExpr() = mc.getArgumentForName("path")
)
@@ -79,7 +79,7 @@ class FileOpenArgSink extends Sink {
class FileStreamArgSink extends Sink {
FileStreamArgSink() {
exists(ObjectCreation oc |
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.FileStream")
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO", "FileStream")
|
this.asExpr() = oc.getArgumentForName("path")
)
@@ -94,7 +94,7 @@ class FileStreamArgSink extends Sink {
class FileInfoArgSink extends Sink {
FileInfoArgSink() {
exists(ObjectCreation oc |
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO.FileInfo")
oc.getTarget().getDeclaringType().hasQualifiedName("System.IO", "FileInfo")
|
this.asExpr() = oc.getArgumentForName("fileName")
)
@@ -108,7 +108,7 @@ class FileInfoArgSink extends Sink {
*/
class GetFileNameSanitizer extends Sanitizer {
GetFileNameSanitizer() {
exists(MethodCall mc | mc.getTarget().hasQualifiedName("System.IO.Path", "GetFileName") |
exists(MethodCall mc | mc.getTarget().hasQualifiedName("System.IO", "Path", "GetFileName") |
this.asExpr() = mc
)
}
@@ -122,19 +122,19 @@ class GetFileNameSanitizer extends Sanitizer {
*/
class SubstringSanitizer extends Sanitizer {
SubstringSanitizer() {
exists(MethodCall mc | mc.getTarget().hasQualifiedName("System.String", "Substring") |
exists(MethodCall mc | mc.getTarget().hasQualifiedName("System", "String", "Substring") |
this.asExpr() = mc
)
}
}
private predicate stringCheckGuard(Guard g, Expr e, AbstractValue v) {
g.(MethodCall).getTarget().hasQualifiedName("System.String", "StartsWith") and
g.(MethodCall).getTarget().hasQualifiedName("System", "String", "StartsWith") and
g.(MethodCall).getQualifier() = e and
// A StartsWith check against Path.Combine is not sufficient, because the ".." elements have
// not yet been resolved.
not exists(MethodCall combineCall |
combineCall.getTarget().hasQualifiedName("System.IO.Path", "Combine") and
combineCall.getTarget().hasQualifiedName("System.IO", "Path", "Combine") and
DataFlow::localExprFlow(combineCall, e)
) and
v.(AbstractValues::BooleanValue).getValue() = true

View File

@@ -47,7 +47,7 @@ abstract class InsecureXmlProcessing extends Call {
*/
private predicate isSafeXmlResolver(Expr e) {
e instanceof NullLiteral or
e.getType().(RefType).hasQualifiedName("System.Xml.XmlSecureResolver")
e.getType().(RefType).hasQualifiedName("System.Xml", "XmlSecureResolver")
}
/**
@@ -94,7 +94,7 @@ module XmlSettings {
* Holds if the given object creation constructs `XmlReaderSettings` with an insecure resolver.
*/
predicate insecureResolverSettings(ObjectCreation creation, Expr evidence, string reason) {
creation.getObjectType().getQualifiedName() = "System.Xml.XmlReaderSettings" and
creation.getObjectType().hasQualifiedName("System.Xml", "XmlReaderSettings") and
(
// one unsafe assignment to XmlResolver
exists(Expr xmlResolverVal | xmlResolverVal = getAValueForProp(creation, "XmlResolver") |
@@ -114,7 +114,7 @@ module XmlSettings {
* Holds if the given object creation constructs `XmlReaderSettings` with DTD processing enabled.
*/
predicate dtdEnabledSettings(ObjectCreation creation, Expr evidence, string reason) {
creation.getObjectType().getQualifiedName() = "System.Xml.XmlReaderSettings" and
creation.getObjectType().hasQualifiedName("System.Xml", "XmlReaderSettings") and
(
exists(Expr dtdVal | dtdVal = getAValueForProp(creation, "DtdProcessing") |
not isSafeDtdSetting(dtdVal) and evidence = dtdVal
@@ -145,14 +145,16 @@ module XmlReader {
private import semmle.code.csharp.dataflow.DataFlow2
private class InsecureXmlReaderCreate extends InsecureXmlProcessing, MethodCall {
InsecureXmlReaderCreate() { this.getTarget().hasQualifiedName("System.Xml.XmlReader.Create") }
InsecureXmlReaderCreate() {
this.getTarget().hasQualifiedName("System.Xml.XmlReader", "Create")
}
/**
* Gets the `XmlReaderSettings` argument to to this call, if any.
*/
Expr getSettings() {
result = this.getAnArgument() and
result.getType().(RefType).getABaseType*().hasQualifiedName("System.Xml.XmlReaderSettings")
result.getType().(RefType).getABaseType*().hasQualifiedName("System.Xml", "XmlReaderSettings")
}
override predicate isUnsafe(string reason) {
@@ -197,7 +199,7 @@ module XmlReader {
.getType()
.(RefType)
.getABaseType*()
.hasQualifiedName("System.Xml.XmlReaderSettings")
.hasQualifiedName("System.Xml", "XmlReaderSettings")
}
override predicate isSink(DataFlow::Node sink) {
@@ -209,7 +211,7 @@ module XmlReader {
/** Provides predicates related to `System.Xml.XmlTextReader`. */
module XmlTextReader {
private class InsecureXmlTextReader extends InsecureXmlProcessing, ObjectCreation {
InsecureXmlTextReader() { this.getObjectType().hasQualifiedName("System.Xml.XmlTextReader") }
InsecureXmlTextReader() { this.getObjectType().hasQualifiedName("System.Xml", "XmlTextReader") }
override predicate isUnsafe(string reason) {
not exists(Expr xmlResolverVal |
@@ -244,8 +246,8 @@ module XmlDocument {
*/
class InsecureXmlDocument extends InsecureXmlProcessing, MethodCall {
InsecureXmlDocument() {
this.getTarget().hasQualifiedName("System.Xml.XmlDocument.Load") or
this.getTarget().hasQualifiedName("System.Xml.XmlDocument.LoadXml")
this.getTarget().hasQualifiedName("System.Xml", "XmlDocument", "Load") or
this.getTarget().hasQualifiedName("System.Xml", "XmlDocument", "LoadXml")
}
override predicate isUnsafe(string reason) {

View File

@@ -90,7 +90,7 @@ private class WrapperDeserializer extends UnsafeDeserializer {
/** BinaryFormatter */
private class BinaryFormatterClass extends Class {
BinaryFormatterClass() {
this.hasQualifiedName("System.Runtime.Serialization.Formatters.Binary.BinaryFormatter")
this.hasQualifiedName("System.Runtime.Serialization.Formatters.Binary", "BinaryFormatter")
}
}
@@ -121,7 +121,7 @@ class BinaryFormatterUnsafeDeserializeMethodResponseMethod extends Method, Unsaf
/** SoapFormatter */
private class SoapFormatterClass extends Class {
SoapFormatterClass() {
this.hasQualifiedName("System.Runtime.Serialization.Formatters.Soap.SoapFormatter")
this.hasQualifiedName("System.Runtime.Serialization.Formatters.Soap", "SoapFormatter")
}
}
@@ -135,7 +135,7 @@ class SoapFormatterDeserializeMethod extends Method, UnsafeDeserializer {
/** ObjectStateFormatter */
private class ObjectStateFormatterClass extends Class {
ObjectStateFormatterClass() { this.hasQualifiedName("System.Web.UI.ObjectStateFormatter") }
ObjectStateFormatterClass() { this.hasQualifiedName("System.Web.UI", "ObjectStateFormatter") }
}
/** `System.Web.UI.ObjectStateFormatter.Deserialize` method */
@@ -149,7 +149,7 @@ class ObjectStateFormatterDeserializeMethod extends Method, UnsafeDeserializer {
/** NetDataContractSerializer */
class NetDataContractSerializerClass extends Class {
NetDataContractSerializerClass() {
this.hasQualifiedName("System.Runtime.Serialization.NetDataContractSerializer")
this.hasQualifiedName("System.Runtime.Serialization", "NetDataContractSerializer")
}
}
@@ -172,7 +172,7 @@ class NetDataContractSerializerReadObjectMethod extends Method, UnsafeDeserializ
/** DataContractJsonSerializer */
class DataContractJsonSerializerClass extends Class {
DataContractJsonSerializerClass() {
this.hasQualifiedName("System.Runtime.Serialization.Json.DataContractJsonSerializer")
this.hasQualifiedName("System.Runtime.Serialization.Json", "DataContractJsonSerializer")
}
}
@@ -187,7 +187,7 @@ class DataContractJsonSerializerReadObjectMethod extends Method, UnsafeDeseriali
/** JavaScriptSerializer */
class JavaScriptSerializerClass extends Class {
JavaScriptSerializerClass() {
this.hasQualifiedName("System.Web.Script.Serialization.JavaScriptSerializer")
this.hasQualifiedName("System.Web.Script.Serialization", "JavaScriptSerializer")
}
}
@@ -210,7 +210,7 @@ class JavaScriptSerializerClassDeserializeObjectMethod extends Method, UnsafeDes
/** XmlObjectSerializer */
class XmlObjectSerializerClass extends Class {
XmlObjectSerializerClass() {
this.hasQualifiedName("System.Runtime.Serialization.XmlObjectSerializer")
this.hasQualifiedName("System.Runtime.Serialization", "XmlObjectSerializer")
}
}
@@ -224,7 +224,7 @@ class XmlObjectSerializerReadObjectMethod extends Method, UnsafeDeserializer {
/** XmlSerializer */
class XmlSerializerClass extends Class {
XmlSerializerClass() { this.hasQualifiedName("System.Xml.Serialization.XmlSerializer") }
XmlSerializerClass() { this.hasQualifiedName("System.Xml.Serialization", "XmlSerializer") }
}
/** `System.Xml.Serialization.XmlSerializer.Deserialize` method */
@@ -238,7 +238,7 @@ class XmlSerializerDeserializeMethod extends Method, UnsafeDeserializer {
/** DataContractSerializer */
class DataContractSerializerClass extends Class {
DataContractSerializerClass() {
this.hasQualifiedName("System.Runtime.Serialization.DataContractSerializer")
this.hasQualifiedName("System.Runtime.Serialization", "DataContractSerializer")
}
}
@@ -252,7 +252,7 @@ class DataContractSerializerReadObjectMethod extends Method, UnsafeDeserializer
/** XmlMessageFormatter */
class XmlMessageFormatterClass extends Class {
XmlMessageFormatterClass() { this.hasQualifiedName("System.Messaging.XmlMessageFormatter") }
XmlMessageFormatterClass() { this.hasQualifiedName("System.Messaging", "XmlMessageFormatter") }
}
/** `System.Messaging.XmlMessageFormatter.Read` method */
@@ -265,7 +265,7 @@ class XmlMessageFormatterReadMethod extends Method, UnsafeDeserializer {
/** LosFormatter */
private class LosFormatterClass extends Class {
LosFormatterClass() { this.hasQualifiedName("System.Web.UI.LosFormatter") }
LosFormatterClass() { this.hasQualifiedName("System.Web.UI", "LosFormatter") }
}
/** `System.Web.UI.LosFormatter.Deserialize` method */
@@ -278,7 +278,7 @@ class LosFormatterDeserializeMethod extends Method, UnsafeDeserializer {
/** fastJSON */
private class FastJsonClass extends Class {
FastJsonClass() { this.hasQualifiedName("fastJSON.JSON") }
FastJsonClass() { this.hasQualifiedName("fastJSON", "JSON") }
}
/** `fastJSON.JSON.ToObject` method */
@@ -292,7 +292,7 @@ class FastJsonClassToObjectMethod extends Method, UnsafeDeserializer {
/** Activity */
private class ActivityClass extends Class {
ActivityClass() { this.hasQualifiedName("System.Workflow.ComponentModel.Activity") }
ActivityClass() { this.hasQualifiedName("System.Workflow.ComponentModel", "Activity") }
}
/** `System.Workflow.ComponentModel.Activity.Load` method */
@@ -305,7 +305,7 @@ class ActivityLoadMethod extends Method, UnsafeDeserializer {
/** ResourceReader */
private class ResourceReaderClass extends Class {
ResourceReaderClass() { this.hasQualifiedName("System.Resources.ResourceReader") }
ResourceReaderClass() { this.hasQualifiedName("System.Resources", "ResourceReader") }
}
/** `System.Resources.ResourceReader` constructor */
@@ -318,7 +318,9 @@ class ResourceReaderConstructor extends Constructor, UnsafeDeserializer {
/** BinaryMessageFormatter */
private class BinaryMessageFormatterClass extends Class {
BinaryMessageFormatterClass() { this.hasQualifiedName("System.Messaging.BinaryMessageFormatter") }
BinaryMessageFormatterClass() {
this.hasQualifiedName("System.Messaging", "BinaryMessageFormatter")
}
}
/** `System.Messaging.BinaryMessageFormatter.Read` method */
@@ -331,7 +333,7 @@ class BinaryMessageFormatterReadMethod extends Method, UnsafeDeserializer {
/** XamlReader */
private class XamlReaderClass extends Class {
XamlReaderClass() { this.hasQualifiedName("System.Windows.Markup.XamlReader") }
XamlReaderClass() { this.hasQualifiedName("System.Windows.Markup", "XamlReader") }
}
/** `System.Windows.Markup.XamlReader.Parse` method */
@@ -362,7 +364,7 @@ class XamlReaderLoadAsyncMethod extends Method, UnsafeDeserializer {
/** ProxyObject */
private class ProxyObjectClass extends Class {
ProxyObjectClass() { this.hasQualifiedName("Microsoft.Web.Design.Remote.ProxyObject") }
ProxyObjectClass() { this.hasQualifiedName("Microsoft.Web.Design.Remote", "ProxyObject") }
}
/** `Microsoft.Web.Design.Remote.ProxyObject.DecodeValue` method */
@@ -383,7 +385,7 @@ class ProxyObjectDecodeSerializedObjectMethod extends Method, UnsafeDeserializer
/** SweetJayson */
private class JaysonConverterClass extends Class {
JaysonConverterClass() { this.hasQualifiedName("Sweet.Jayson.JaysonConverter") }
JaysonConverterClass() { this.hasQualifiedName("Sweet.Jayson", "JaysonConverter") }
}
/** `Sweet.Jayson.JaysonConverter.ToObject` method */
@@ -398,7 +400,7 @@ class JaysonConverterToObjectMethod extends Method, UnsafeDeserializer {
/** ServiceStack.Text.JsonSerializer */
private class ServiceStackTextJsonSerializerClass extends Class {
ServiceStackTextJsonSerializerClass() {
this.hasQualifiedName("ServiceStack.Text.JsonSerializer")
this.hasQualifiedName("ServiceStack.Text", "JsonSerializer")
}
}
@@ -432,7 +434,7 @@ class ServiceStackTextJsonSerializerDeserializeFromStreamMethod extends Method,
/** ServiceStack.Text.TypeSerializer */
private class ServiceStackTextTypeSerializerClass extends Class {
ServiceStackTextTypeSerializerClass() {
this.hasQualifiedName("ServiceStack.Text.TypeSerializer")
this.hasQualifiedName("ServiceStack.Text", "TypeSerializer")
}
}
@@ -465,7 +467,9 @@ class ServiceStackTextTypeSerializerDeserializeFromStreamMethod extends Method,
/** ServiceStack.Text.CsvSerializer */
private class ServiceStackTextCsvSerializerClass extends Class {
ServiceStackTextCsvSerializerClass() { this.hasQualifiedName("ServiceStack.Text.CsvSerializer") }
ServiceStackTextCsvSerializerClass() {
this.hasQualifiedName("ServiceStack.Text", "CsvSerializer")
}
}
/** `ServiceStack.Text.CsvSerializer.DeserializeFromString` method */
@@ -497,7 +501,9 @@ class ServiceStackTextCsvSerializerDeserializeFromStreamMethod extends Method, U
/** ServiceStack.Text.XmlSerializer */
private class ServiceStackTextXmlSerializerClass extends Class {
ServiceStackTextXmlSerializerClass() { this.hasQualifiedName("ServiceStack.Text.XmlSerializer") }
ServiceStackTextXmlSerializerClass() {
this.hasQualifiedName("ServiceStack.Text", "XmlSerializer")
}
}
/** `ServiceStack.Text.XmlSerializer.DeserializeFromString` method */
@@ -529,7 +535,7 @@ class ServiceStackTextXmlSerializerDeserializeFromStreamMethod extends Method, U
/** MBrace.FsPickler.FsPicklerSerializer */
private class FsPicklerSerializerClass extends Class {
FsPicklerSerializerClass() { this.hasQualifiedName("MBrace.FsPickler.FsPicklerSerializer") }
FsPicklerSerializerClass() { this.hasQualifiedName("MBrace.FsPickler", "FsPicklerSerializer") }
}
/** `MBrace.FsPickler.FsPicklerSerializer.Deserialize` method */
@@ -598,7 +604,7 @@ class FsPicklerSerializerClassUnPickleUntypedMethod extends Method, UnsafeDeseri
/** MBrace.CsPickler.CsPicklerSerializer */
private class CsPicklerSerializerClass extends Class {
CsPicklerSerializerClass() { this.hasQualifiedName("MBrace.CsPickler.CsPicklerSerializer") }
CsPicklerSerializerClass() { this.hasQualifiedName("MBrace.CsPickler", "CsPicklerSerializer") }
}
/** `MBrace.FsPickler.CsPicklerSerializer.Deserialize` method */
@@ -620,7 +626,7 @@ class CsPicklerSerializerClassUnPickleMethod extends Method, UnsafeDeserializer
/** MBrace.CsPickler.CsPicklerTextSerializer */
private class CsPicklerTextSerializerClass extends Class {
CsPicklerTextSerializerClass() {
this.hasQualifiedName("MBrace.CsPickler.CsPicklerTextSerializer")
this.hasQualifiedName("MBrace.CsPickler", "CsPicklerTextSerializer")
}
}
@@ -634,7 +640,7 @@ class CsPicklerSerializerClassUnPickleOfStringMethod extends Method, UnsafeDeser
/** Polenter.Serialization.SharpSerializer */
private class SharpSerializerClass extends Class {
SharpSerializerClass() { this.hasQualifiedName("Polenter.Serialization.SharpSerializer") }
SharpSerializerClass() { this.hasQualifiedName("Polenter.Serialization", "SharpSerializer") }
}
/** `Polenter.Serialization.SharpSerializer.Deserialize` method */
@@ -647,7 +653,9 @@ class SharpSerializerClassDeserializeMethod extends Method, UnsafeDeserializer {
/** YamlDotNet.Serialization.Deserializer */
private class YamlDotNetDeserializerClass extends Class {
YamlDotNetDeserializerClass() { this.hasQualifiedName("YamlDotNet.Serialization.Deserializer") }
YamlDotNetDeserializerClass() {
this.hasQualifiedName("YamlDotNet.Serialization", "Deserializer")
}
}
/** `YamlDotNet.Serialization.Deserializer.Deserialize` method */

View File

@@ -4,11 +4,15 @@
import Element
import Type
private import semmle.code.csharp.commons.QualifiedName
/** A declaration. */
class Declaration extends NamedElement, @dotnet_declaration {
override predicate hasQualifiedName(string qualifier, string name) {
qualifier = this.getDeclaringType().getQualifiedName() and
exists(string dqualifier, string dname |
this.getDeclaringType().hasQualifiedName(dqualifier, dname) and
qualifier = getQualifiedName(dqualifier, dname)
) and
name = this.getName()
}
@@ -75,6 +79,16 @@ class Member extends Declaration, @dotnet_member {
/** Holds if this member is `static`. */
predicate isStatic() { none() }
/**
* Holds if this member has name `name` and is defined in type `type`
* with namespace `namespace`.
*/
cached
predicate hasQualifiedName(string namespace, string type, string name) {
this.getDeclaringType().hasQualifiedName(namespace, type) and
name = this.getName()
}
}
/** A property. */

View File

@@ -97,10 +97,13 @@ class NamedElement extends Element, @dotnet_named_element {
}
/**
* DEPRECATED: Use `hasQualifiedName/2` instead.
* Holds if this element has qualified name `qualifiedName`, for example
* `System.Console.WriteLine`.
*/
final predicate hasQualifiedName(string qualifiedName) { qualifiedName = this.getQualifiedName() }
deprecated final predicate hasQualifiedName(string qualifiedName) {
qualifiedName = this.getQualifiedName()
}
/** Holds if this element has the qualified name `qualifier`.`name`. */
cached

View File

@@ -3,6 +3,7 @@
*/
private import Declaration
private import semmle.code.csharp.commons.QualifiedName
/** A namespace. */
class Namespace extends Declaration, @namespace {
@@ -25,12 +26,15 @@ class Namespace extends Declaration, @namespace {
* `qualifier`=`System.Collections` and `name`=`Generic`.
*/
override predicate hasQualifiedName(string qualifier, string name) {
qualifier = this.getParentNamespace().getQualifiedName() and
exists(string pqualifier, string pname |
this.getParentNamespace().hasQualifiedName(pqualifier, pname) and
qualifier = getQualifiedName(pqualifier, pname)
) and
name = this.getName()
}
/** Gets a textual representation of this namespace. */
override string toString() { result = this.getQualifiedName() }
override string toString() { result = this.getFullName() }
/** Holds if this is the global namespace. */
final predicate isGlobalNamespace() { this.getName() = "" }
@@ -41,6 +45,16 @@ class Namespace extends Declaration, @namespace {
final override string getUndecoratedName() { namespaces(this, result) }
override string getAPrimaryQlClass() { result = "Namespace" }
/**
* Get the fully qualified name of this namespace.
*/
string getFullName() {
exists(string namespace, string name |
this.hasQualifiedName(namespace, name) and
result = getQualifiedName(namespace, name)
)
}
}
/** The global namespace. */

View File

@@ -28,7 +28,7 @@ class ValueOrRefType extends Type, @dotnet_valueorreftype {
or
if this.getDeclaringNamespace().isGlobalNamespace()
then result = ""
else result = this.getDeclaringNamespace().getQualifiedName() + "."
else result = this.getDeclaringNamespace().getFullName() + "."
}
pragma[noinline]

View File

@@ -16,5 +16,5 @@ where
c.getTarget() = gcCollect and
gcCollect.hasName("Collect") and
gcCollect.hasNoParameters() and
gcCollect.getDeclaringType().hasQualifiedName("System.GC")
gcCollect.getDeclaringType().hasQualifiedName("System", "GC")
select c, "Call to 'GC.Collect()'."

View File

@@ -11,6 +11,7 @@
*/
import csharp
import semmle.code.csharp.commons.QualifiedName
private predicate potentialOverride(Method vm, Method m) {
vm.getDeclaringType() = m.getDeclaringType().getBaseClass+()
@@ -36,9 +37,10 @@ predicate nonOverridingMethod(Method m, Method vm) {
m.getName().toLowerCase() = vm.getName().toLowerCase()
}
from Method m, Method vm
from Method m, Method vm, string namespace, string type, string name
where
m.fromSource() and
nonOverridingMethod(m, vm)
nonOverridingMethod(m, vm) and
vm.hasQualifiedName(namespace, type, name)
select m, "Method '" + m.getName() + "' looks like it should override $@ but does not do so.",
vm.getUnboundDeclaration(), vm.getQualifiedName()
vm.getUnboundDeclaration(), getQualifiedName(namespace, type, name)

View File

@@ -19,6 +19,6 @@ where
m.fromSource() and
exists(UsingNamespaceDirective u |
u.getFile() = m.getFile() and
u.getImportedNamespace().hasQualifiedName("System.Web")
u.getImportedNamespace().hasQualifiedName("System", "Web")
)
select m, "Remove debug code if your ASP.NET application is in production."

View File

@@ -11,90 +11,95 @@
import csharp
string prefix(string typename) {
typename = "System.Web.UI.WebControls.Label" and result = "lbl"
string prefix(string qualifier, string typename) {
qualifier = "System.Web.UI.WebControls" and
(
typename = "Label" and result = "lbl"
or
typename = "TextBox" and result = "txt"
or
typename = ["Button", "LinkButton"] and result = "btn"
or
typename = "ImageButton" and result = "ibtn"
or
typename = "Hyperlink" and result = "hpl"
or
typename = "DropDownList" and result = "cmb"
or
typename = "ListBox" and result = "lst"
or
typename = "Datagrid" and result = "dgr"
or
typename = "Datalist" and result = "dtl"
or
typename = "Repeater" and result = "rpt"
or
typename = "CheckBox" and result = "chk"
or
typename = "CheckBoxList" and result = "chklst"
or
typename = "RadioButtonList" and result = "radlst"
or
typename = "RadioButton" and result = "rad"
or
typename = "Image" and result = "img"
or
typename = "Panel" and result = "pnl"
or
typename = "PlaceHolder" and result = "plh"
or
typename = "Calendar" and result = "cal"
or
typename = "AdRotator" and result = "adr"
or
typename = "Table" and result = "tbl"
or
typename = "RequiredFieldValidator" and result = "rfv"
or
typename = "CompareValidator" and result = "cmv"
or
typename = "RegularExpressionValidator" and result = "rev"
or
typename = "CustomValidator" and result = "csv"
or
typename = "ValidationSummary" and result = "vsm"
or
typename = "XML" and result = "xml"
or
typename = "Literal" and result = "lit"
or
typename = "Form" and result = "frm"
or
typename = "Frame" and result = "fra"
or
typename = "CrystalReportViewer" and result = "crvr"
)
or
typename = "System.Web.UI.WebControls.TextBox" and result = "txt"
or
typename = "System.Web.UI.WebControls.Button" and result = "btn"
or
typename = "System.Web.UI.WebControls.LinkButton" and result = "btn"
or
typename = "System.Web.UI.WebControls.ImageButton" and result = "ibtn"
or
typename = "System.Web.UI.WebControls.Hyperlink" and result = "hpl"
or
typename = "System.Web.UI.WebControls.DropDownList" and result = "cmb"
or
typename = "System.Web.UI.WebControls.ListBox" and result = "lst"
or
typename = "System.Web.UI.WebControls.Datagrid" and result = "dgr"
or
typename = "System.Web.UI.WebControls.Datalist" and result = "dtl"
or
typename = "System.Web.UI.WebControls.Repeater" and result = "rpt"
or
typename = "System.Web.UI.WebControls.CheckBox" and result = "chk"
or
typename = "System.Web.UI.WebControls.CheckBoxList" and result = "chklst"
or
typename = "System.Web.UI.WebControls.RadioButtonList" and result = "radlst"
or
typename = "System.Web.UI.WebControls.RadioButton" and result = "rad"
or
typename = "System.Web.UI.WebControls.Image" and result = "img"
or
typename = "System.Web.UI.WebControls.Panel" and result = "pnl"
or
typename = "System.Web.UI.WebControls.PlaceHolder" and result = "plh"
or
typename = "System.Web.UI.WebControls.Calendar" and result = "cal"
or
typename = "System.Web.UI.WebControls.AdRotator" and result = "adr"
or
typename = "System.Web.UI.WebControls.Table" and result = "tbl"
or
typename = "System.Web.UI.WebControls.RequiredFieldValidator" and result = "rfv"
or
typename = "System.Web.UI.WebControls.CompareValidator" and result = "cmv"
or
typename = "System.Web.UI.WebControls.RegularExpressionValidator" and result = "rev"
or
typename = "System.Web.UI.WebControls.CustomValidator" and result = "csv"
or
typename = "System.Web.UI.WebControls.ValidationSummary" and result = "vsm"
or
typename = "System.Web.UI.WebControls.XML" and result = "xml"
or
typename = "System.Web.UI.WebControls.Literal" and result = "lit"
or
typename = "System.Web.UI.WebControls.Form" and result = "frm"
or
typename = "System.Web.UI.WebControls.Frame" and result = "fra"
or
typename = "System.Web.UI.WebControls.CrystalReportViewer" and result = "crvr"
or
typename = "System.Web.UI.HtmlControls.TextArea" and result = "txa"
or
typename = "System.Web.UI.HtmlControls.FileField" and result = "fle"
or
typename = "System.Web.UI.HtmlControls.PasswordField" and result = "pwd"
or
typename = "System.Web.UI.HtmlControls.Hidden" and result = "hdn"
or
typename = "System.Web.UI.HtmlControls.Table" and result = "tbl"
or
typename = "System.Web.UI.HtmlControls.FlowLayoutPanel" and result = "flp"
or
typename = "System.Web.UI.HtmlControls.GridLayoutPanel" and result = "glp"
or
typename = "System.Web.UI.HtmlControls.HorizontalRule" and result = "hr"
qualifier = "System.Web.UI.HtmlControls" and
(
typename = "TextArea" and result = "txa"
or
typename = "FileField" and result = "fle"
or
typename = "PasswordField" and result = "pwd"
or
typename = "Hidden" and result = "hdn"
or
typename = "Table" and result = "tbl"
or
typename = "FlowLayoutPanel" and result = "flp"
or
typename = "GridLayoutPanel" and result = "glp"
or
typename = "HorizontalRule" and result = "hr"
)
}
from Field f, RefType t, string name, string prefix
from Field f, RefType t, string name, string prefix, string qualifier, string type
where
f.getType() = t and
f.getName() = name and
prefix = prefix(t.getQualifiedName()) and
t.hasQualifiedName(qualifier, type) and
prefix = prefix(qualifier, type) and
not name.matches(prefix + "%")
select f, "This field should have the prefix '" + prefix + "' to match its types."

View File

@@ -29,7 +29,7 @@ predicate usedInHumanWrittenCode(Field f) {
from Field field, ValueOrRefType widget, string prefix
where
widget.getABaseType*().hasQualifiedName("System.Windows.Forms.Control") and
widget.getABaseType*().hasQualifiedName("System.Windows.Forms", "Control") and
field.getType() = widget and
field.getName().regexpMatch(prefix + "[0-9]+") and
controlName(prefix) and

View File

@@ -14,30 +14,30 @@ import semmle.code.csharp.commons.Util
predicate isConsoleOutRedefinedSomewhere() {
exists(MethodCall mc |
mc.getTarget().hasName("SetOut") and
mc.getTarget().getDeclaringType().hasQualifiedName("System.Console")
mc.getTarget().getDeclaringType().hasQualifiedName("System", "Console")
)
}
predicate isConsoleErrorRedefinedSomewhere() {
exists(MethodCall mc |
mc.getTarget().hasName("SetError") and
mc.getTarget().getDeclaringType().hasQualifiedName("System.Console")
mc.getTarget().getDeclaringType().hasQualifiedName("System", "Console")
)
}
predicate isCallToConsoleWrite(MethodCall mc) {
mc.getTarget().getName().matches("Write%") and
mc.getTarget().getDeclaringType().hasQualifiedName("System.Console")
mc.getTarget().getDeclaringType().hasQualifiedName("System", "Console")
}
predicate isAccessToConsoleOut(PropertyAccess pa) {
pa.getTarget().hasName("Out") and
pa.getTarget().getDeclaringType().hasQualifiedName("System.Console")
pa.getTarget().getDeclaringType().hasQualifiedName("System", "Console")
}
predicate isAccessToConsoleError(PropertyAccess pa) {
pa.getTarget().hasName("Error") and
pa.getTarget().getDeclaringType().hasQualifiedName("System.Console")
pa.getTarget().getDeclaringType().hasQualifiedName("System", "Console")
}
from Expr e

View File

@@ -5,7 +5,7 @@ import csharp
private class WaitCall extends MethodCall {
WaitCall() {
this.getTarget().hasName("Wait") and
this.getTarget().getDeclaringType().hasQualifiedName("System.Threading.Monitor")
this.getTarget().getDeclaringType().hasQualifiedName("System.Threading", "Monitor")
}
Expr getExpr() { result = this.getArgument(0) }
@@ -30,12 +30,12 @@ class WaitStmt extends ExprStmt {
private class SynchronizedMethodAttribute extends Attribute {
SynchronizedMethodAttribute() {
this.getType().hasQualifiedName("System.Runtime.CompilerServices.MethodImplAttribute") and
this.getType().hasQualifiedName("System.Runtime.CompilerServices", "MethodImplAttribute") and
exists(MemberConstantAccess a, MemberConstant mc |
a = this.getArgument(0) and
a.getTarget() = mc and
mc.hasName("Synchronized") and
mc.getDeclaringType().hasQualifiedName("System.Runtime.CompilerServices.MethodImplOptions")
mc.getDeclaringType().hasQualifiedName("System.Runtime.CompilerServices", "MethodImplOptions")
)
}
}

View File

@@ -9,14 +9,18 @@ import Concurrency
*/
class ThreadStartingCallable extends Callable {
ThreadStartingCallable() {
this.(Constructor).getDeclaringType().getQualifiedName() = "System.Threading.Tasks.Task" or
this.(Method).getQualifiedName() = "System.Threading.Tasks.Task.Run" or
this.(Constructor).getDeclaringType().getQualifiedName() = "System.Threading.Thread" or
this.(Method).getQualifiedName() = "System.Threading.Thread.Start" or
this.(Constructor)
.getDeclaringType()
.getQualifiedName()
.matches("System.Threading.Tasks.Task<%>")
this.(Constructor).getDeclaringType().hasQualifiedName("System.Threading.Tasks", "Task")
or
this.(Method).hasQualifiedName("System.Threading.Tasks", "Task", "Run")
or
this.(Constructor).getDeclaringType().hasQualifiedName("System.Threading", "Thread")
or
this.(Method).hasQualifiedName("System.Threading", "Thread", "Start")
or
exists(string name |
this.(Constructor).getDeclaringType().hasQualifiedName("System.Threading.Tasks", name) and
name.matches("Task<%>")
)
}
}

View File

@@ -26,7 +26,7 @@ Expr getAnAccessByDynamicCall(Method m) {
exists(MethodCall mc, Method target |
target = mc.getTarget() and
target.hasName("InvokeMember") and
target.getDeclaringType().hasQualifiedName("System.Type") and
target.getDeclaringType().hasQualifiedName("System", "Type") and
mc.getArgument(0).(StringLiteral).getValue() = m.getName() and
mc.getArgument(3).getType().(RefType).hasMethod(m) and
result = mc
@@ -42,7 +42,7 @@ Expr getAMethodAccess(Method m) {
predicate potentiallyAccessedByForEach(Method m) {
m.hasName("GetEnumerator") and
m.getDeclaringType().getABaseType+().hasQualifiedName("System.Collections.IEnumerable")
m.getDeclaringType().getABaseType+().hasQualifiedName("System.Collections", "IEnumerable")
or
foreach_stmt_desugar(_, m, 1)
}

View File

@@ -16,15 +16,13 @@ import semmle.code.csharp.frameworks.Test
import semmle.code.csharp.metrics.Coupling
predicate potentiallyUsedFromXaml(RefType t) {
exists(string name | name = t.getABaseType*().getQualifiedName() |
name = "System.Windows.Data.IValueConverter" or
name = "System.Windows.Data.IMultiValueConverter"
)
t.getABaseType*()
.hasQualifiedName("System.Windows.Data", ["IValueConverter", "IMultiValueConverter"])
}
class ExportAttribute extends Attribute {
ExportAttribute() {
getType().hasQualifiedName("System.ComponentModel.Composition.ExportAttribute")
getType().hasQualifiedName("System.ComponentModel.Composition", "ExportAttribute")
}
}

View File

@@ -10,6 +10,7 @@
*/
import Documentation
import semmle.code.csharp.commons.QualifiedName
from SourceMethodOrConstructor m, ThrowElement throw, RefType throwType
where
@@ -20,8 +21,15 @@ where
comment = getADeclarationXmlComment(m) and
exceptionName = comment.getCref(offset) and
throwType.getABaseType*() = throwBaseType and
(throwBaseType.hasName(exceptionName) or throwBaseType.hasQualifiedName(exceptionName))
// and comment.hasBody(offset) // Too slow
(
throwBaseType.hasName(exceptionName)
or
exists(string qualifier, string type |
splitQualifiedName(exceptionName, qualifier, type) and
throwBaseType.hasQualifiedName(qualifier, type)
)
// and comment.hasBody(offset) // Too slow
)
) and
not getADeclarationXmlComment(m) instanceof InheritDocXmlComment
select m, "Exception $@ should be documented.", throw, throw.getExpr().getType().getName()

View File

@@ -13,9 +13,9 @@
import csharp
import semmle.code.csharp.commons.Assertions
private predicate propertyOverrides(Property p, string baseClass, string property) {
private predicate propertyOverrides(Property p, string qualifier, string baseClass, string property) {
exists(Property p2 |
p2.getUnboundDeclaration().getDeclaringType().hasQualifiedName(baseClass) and
p2.getUnboundDeclaration().getDeclaringType().hasQualifiedName(qualifier, baseClass) and
p2.hasName(property)
|
p.overridesOrImplementsOrEquals(p2)
@@ -24,16 +24,16 @@ private predicate propertyOverrides(Property p, string baseClass, string propert
private predicate containerSizeAccess(PropertyAccess pa, string containerKind) {
(
propertyOverrides(pa.getTarget(), "System.Collections.Generic.ICollection<>", "Count") or
propertyOverrides(pa.getTarget(), "System.Collections.Generic.IReadOnlyCollection<>", "Count") or
propertyOverrides(pa.getTarget(), "System.Collections.ICollection", "Count")
propertyOverrides(pa.getTarget(), "System.Collections.Generic", "ICollection<>", "Count") or
propertyOverrides(pa.getTarget(), "System.Collections.Generic", "IReadOnlyCollection<>", "Count") or
propertyOverrides(pa.getTarget(), "System.Collections", "ICollection", "Count")
) and
containerKind = "a collection"
or
(
propertyOverrides(pa.getTarget(), "System.String", "Length") and containerKind = "a string"
propertyOverrides(pa.getTarget(), "System", "String", "Length") and containerKind = "a string"
or
propertyOverrides(pa.getTarget(), "System.Array", "Length") and containerKind = "an array"
propertyOverrides(pa.getTarget(), "System", "Array", "Length") and containerKind = "an array"
)
}

View File

@@ -20,14 +20,14 @@ class UnsafeYearCreationFromArithmeticConfiguration extends TaintTracking::Confi
override predicate isSource(DataFlow::Node source) {
exists(ArithmeticOperation ao, PropertyAccess pa | ao = source.asExpr() |
pa = ao.getAChild*() and
pa.getProperty().hasQualifiedName("System.DateTime.Year")
pa.getProperty().hasQualifiedName("System.DateTime", "Year")
)
}
override predicate isSink(DataFlow::Node sink) {
exists(ObjectCreation oc |
sink.asExpr() = oc.getArgumentForName("year") and
oc.getObjectType().getABaseType*().hasQualifiedName("System.DateTime")
oc.getObjectType().getABaseType*().hasQualifiedName("System", "DateTime")
)
}
}

View File

@@ -23,8 +23,8 @@ predicate isEraStart(int year, int month, int day) {
predicate isExactEraStartDateCreation(ObjectCreation cr) {
(
cr.getType().hasQualifiedName("System.DateTime") or
cr.getType().hasQualifiedName("System.DateTimeOffset")
cr.getType().hasQualifiedName("System", "DateTime") or
cr.getType().hasQualifiedName("System", "DateTimeOffset")
) and
isEraStart(cr.getArgument(0).getValue().toInt(), cr.getArgument(1).getValue().toInt(),
cr.getArgument(2).getValue().toInt())
@@ -32,8 +32,10 @@ predicate isExactEraStartDateCreation(ObjectCreation cr) {
predicate isDateFromJapaneseCalendarToDateTime(MethodCall mc) {
(
mc.getQualifier().getType().hasQualifiedName("System.Globalization.JapaneseCalendar") or
mc.getQualifier().getType().hasQualifiedName("System.Globalization.JapaneseLunisolarCalendar")
mc.getQualifier().getType().hasQualifiedName("System.Globalization", "JapaneseCalendar") or
mc.getQualifier()
.getType()
.hasQualifiedName("System.Globalization", "JapaneseLunisolarCalendar")
) and
mc.getTarget().hasName("ToDateTime") and
mc.getArgument(0).hasValue() and
@@ -47,16 +49,16 @@ predicate isDateFromJapaneseCalendarToDateTime(MethodCall mc) {
predicate isDateFromJapaneseCalendarCreation(ObjectCreation cr) {
(
cr.getType().hasQualifiedName("System.DateTime") or
cr.getType().hasQualifiedName("System.DateTimeOffset")
cr.getType().hasQualifiedName("System", "DateTime") or
cr.getType().hasQualifiedName("System", "DateTimeOffset")
) and
(
cr.getArgumentForName("calendar")
.getType()
.hasQualifiedName("System.Globalization.JapaneseCalendar") or
.hasQualifiedName("System.Globalization", "JapaneseCalendar") or
cr.getArgumentForName("calendar")
.getType()
.hasQualifiedName("System.Globalization.JapaneseLunisolarCalendar")
.hasQualifiedName("System.Globalization", "JapaneseLunisolarCalendar")
) and
cr.getArgumentForName("year").hasValue()
}

View File

@@ -19,7 +19,7 @@ import semmle.code.csharp.frameworks.system.collections.Generic
class UnsafeField extends Field {
UnsafeField() {
this.isStatic() and
not this.getAnAttribute().getType().getQualifiedName() = "System.ThreadStaticAttribute" and
not this.getAnAttribute().getType().hasQualifiedName("System", "ThreadStaticAttribute") and
this.getType() instanceof UsesICryptoTransform
}
}

View File

@@ -10,11 +10,16 @@
*/
import csharp
import semmle.code.csharp.commons.QualifiedName
import semmle.code.csharp.security.dataflow.ExternalAPIsQuery
import DataFlow::PathGraph
from UntrustedDataToExternalApiConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
from
UntrustedDataToExternalApiConfig config, DataFlow::PathNode source, DataFlow::PathNode sink,
string qualifier, string name
where
config.hasFlowPath(source, sink) and
sink.getNode().(ExternalApiDataNode).hasQualifiedName(qualifier, name)
select sink, source, sink,
"Call to " + sink.getNode().(ExternalApiDataNode).getCallableDescription() +
" with untrusted data from $@.", source, source.toString()
"Call to " + getQualifiedName(qualifier, name) + " with untrusted data from $@.", source,
source.toString()

View File

@@ -27,7 +27,7 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) {
exists(MethodCall mc |
mc.getTarget().hasName("WriteRaw") and
mc.getTarget().getDeclaringType().getABaseType*().hasQualifiedName("System.Xml.XmlWriter")
mc.getTarget().getDeclaringType().getABaseType*().hasQualifiedName("System.Xml", "XmlWriter")
|
mc.getArgument(0) = sink.asExpr()
)
@@ -39,7 +39,7 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
mc.getTarget()
.getDeclaringType()
.getABaseType*()
.hasQualifiedName("System.Security.SecurityElement")
.hasQualifiedName("System.Security", "SecurityElement")
|
mc = node.asExpr()
)

View File

@@ -34,7 +34,7 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
mc.getTarget()
.getDeclaringType()
.getABaseType*()
.hasQualifiedName("System.Reflection.Assembly") and
.hasQualifiedName("System.Reflection", "Assembly") and
mc.getArgument(arg) = sink.asExpr()
|
name = "LoadFrom" and arg = 0 and mc.getNumberOfArguments() = [1 .. 2]

Some files were not shown because too many files have changed in this diff Show More