mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Merge branch 'main' into MybatisSqli
This commit is contained in:
26
.github/actions/find-latest-bundle/action.yml
vendored
Normal file
26
.github/actions/find-latest-bundle/action.yml
vendored
Normal 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 }}
|
||||
6
.github/workflows/mad_modelDiff.yml
vendored
6
.github/workflows/mad_modelDiff.yml
vendored
@@ -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 ..
|
||||
}
|
||||
|
||||
|
||||
5
.github/workflows/ql-for-ql-build.yml
vendored
5
.github/workflows/ql-for-ql-build.yml
vendored
@@ -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
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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`.
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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 + ".",
|
||||
|
||||
@@ -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`. */
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
/*
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 | |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 | ! ... |
|
||||
|
||||
@@ -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) |
|
||||
|
||||
@@ -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) |
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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+");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
import csharp
|
||||
|
||||
from ObjectCreation new
|
||||
where new.getObjectType().hasQualifiedName("System.Exception")
|
||||
where new.getObjectType().hasQualifiedName("System", "Exception")
|
||||
select new
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* `Element::hasQualifiedName/1` has been deprecated. Use `hasQualifiedName/2` or `hasQualifiedName/3` instead.
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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) + ">"
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
34
csharp/ql/lib/semmle/code/csharp/commons/QualifiedName.qll
Normal file
34
csharp/ql/lib/semmle/code/csharp/commons/QualifiedName.qll
Normal 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
|
||||
)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -768,7 +768,7 @@ module Expressions {
|
||||
nc.getOuterCompletion()
|
||||
.(ThrowCompletion)
|
||||
.getExceptionClass()
|
||||
.hasQualifiedName("System.InvalidOperationException")
|
||||
.hasQualifiedName("System", "InvalidOperationException")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`. */
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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`. */
|
||||
|
||||
@@ -4,7 +4,7 @@ import csharp
|
||||
|
||||
/** The `Moq.Language` Namespace. */
|
||||
class MoqLanguageNamespace extends Namespace {
|
||||
MoqLanguageNamespace() { this.hasQualifiedName("Moq.Language") }
|
||||
MoqLanguageNamespace() { this.getFullName() = "Moq.Language" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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") }
|
||||
}
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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()'."
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<%>")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user