mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge branch 'main' into py/add-ssrf-sinks
This commit is contained in:
1
.github/workflows/ruby-qltest.yml
vendored
1
.github/workflows/ruby-qltest.yml
vendored
@@ -63,6 +63,7 @@ jobs:
|
||||
qltest:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
slice: ["1/2", "2/2"]
|
||||
steps:
|
||||
|
||||
29
.pre-commit-config.yaml
Normal file
29
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.2.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: codeql-format
|
||||
name: Fix QL file formatting
|
||||
files: \.qll?$
|
||||
language: system
|
||||
entry: codeql query format --in-place
|
||||
|
||||
- id: sync-files
|
||||
name: Fix files required to be identical
|
||||
language: system
|
||||
entry: python3 config/sync-files.py --latest
|
||||
pass_filenames: false
|
||||
|
||||
- id: qhelp
|
||||
name: Check query help generation
|
||||
files: \.qhelp$
|
||||
language: system
|
||||
entry: python3 misc/scripts/check-qhelp.py
|
||||
@@ -42,7 +42,11 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
||||
|
||||
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode/procedures/about-codeql-for-vscode.html).
|
||||
|
||||
If you prefer, you can use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted. See the [pre-commit hook installation guide](docs/pre-commit-hook-setup.md) for instructions on how to install the hook.
|
||||
If you prefer, you can either:
|
||||
1. install the [pre-commit framework](https://pre-commit.com/) and install the configured hooks on this repo via `pre-commit install`, or
|
||||
2. use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted.
|
||||
|
||||
See the [pre-commit hook installation guide](docs/pre-commit-hook-setup.md) for instructions on the two approaches.
|
||||
|
||||
4. **Compilation**
|
||||
|
||||
@@ -63,6 +67,6 @@ After the experimental query is merged, we welcome pull requests to improve it.
|
||||
|
||||
## Using your personal data
|
||||
|
||||
If you contribute to this project, we will record your name and email address (as provided by you with your contributions) as part of the code repositories, which are public. We might also use this information to contact you in relation to your contributions, as well as in the normal course of software development. We also store records of CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product.
|
||||
If you contribute to this project, we will record your name and email address (as provided by you with your contributions) as part of the code repositories, which are public. We might also use this information to contact you in relation to your contributions, as well as in the normal course of software development. We also store records of CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product.
|
||||
|
||||
Please do get in touch (privacy@github.com) if you have any questions about this or our data protection policies.
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.0.10
|
||||
|
||||
### New Features
|
||||
|
||||
* Added a `isStructuredBinding` predicate to the `Variable` class which holds when the variable is declared as part of a structured binding declaration.
|
||||
|
||||
## 0.0.9
|
||||
|
||||
## 0.0.8
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Many queries now support structured bindings, as structured bindings are now handled in the IR translation.
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
## 0.0.10
|
||||
|
||||
### New Features
|
||||
|
||||
* Added a `isStructuredBinding` predicate to the `Variable` class which holds when the variable is declared as part of a structured binding declaration.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.9
|
||||
lastReleaseVersion: 0.0.10
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.0.10-dev
|
||||
version: 0.0.11-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -241,8 +241,8 @@ private module Cached {
|
||||
// For compatibility, send flow from arguments to parameters, even for
|
||||
// functions with no body.
|
||||
exists(FunctionCall call, int i |
|
||||
sink.asExpr() = call.getArgument(i) and
|
||||
result = resolveCall(call).getParameter(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
|
||||
|
||||
@@ -106,6 +106,12 @@ private predicate filteredNumberableInstruction(Instruction instr) {
|
||||
or
|
||||
instr instanceof FieldAddressInstruction and
|
||||
count(instr.(FieldAddressInstruction).getField()) != 1
|
||||
or
|
||||
instr instanceof InheritanceConversionInstruction and
|
||||
(
|
||||
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
|
||||
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
|
||||
)
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(
|
||||
@@ -115,8 +121,7 @@ private predicate variableAddressValueNumber(
|
||||
// The underlying AST element is used as value-numbering key instead of the
|
||||
// `IRVariable` to work around a problem where a variable or expression with
|
||||
// multiple types gives rise to multiple `IRVariable`s.
|
||||
instr.getIRVariable().getAST() = ast and
|
||||
strictcount(instr.getIRVariable().getAST()) = 1
|
||||
unique( | | instr.getIRVariable().getAST()) = ast
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
@@ -133,8 +138,7 @@ private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
strictcount(instr.getResultIRType()) = 1 and
|
||||
instr.getResultIRType() = type and
|
||||
unique( | | instr.getResultIRType()) = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
@@ -151,8 +155,7 @@ private predicate fieldAddressValueNumber(
|
||||
TValueNumber objectAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
strictcount(instr.getField()) = 1 and
|
||||
unique( | | instr.getField()) = field and
|
||||
tvalueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
@@ -195,9 +198,9 @@ private predicate inheritanceConversionValueNumber(
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getBaseClass() = baseClass and
|
||||
instr.getDerivedClass() = derivedClass and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
tvalueNumber(instr.getUnary()) = operand and
|
||||
unique( | | instr.getBaseClass()) = baseClass and
|
||||
unique( | | instr.getDerivedClass()) = derivedClass
|
||||
}
|
||||
|
||||
private predicate loadTotalOverlapValueNumber(
|
||||
|
||||
@@ -106,6 +106,12 @@ private predicate filteredNumberableInstruction(Instruction instr) {
|
||||
or
|
||||
instr instanceof FieldAddressInstruction and
|
||||
count(instr.(FieldAddressInstruction).getField()) != 1
|
||||
or
|
||||
instr instanceof InheritanceConversionInstruction and
|
||||
(
|
||||
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
|
||||
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
|
||||
)
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(
|
||||
@@ -115,8 +121,7 @@ private predicate variableAddressValueNumber(
|
||||
// The underlying AST element is used as value-numbering key instead of the
|
||||
// `IRVariable` to work around a problem where a variable or expression with
|
||||
// multiple types gives rise to multiple `IRVariable`s.
|
||||
instr.getIRVariable().getAST() = ast and
|
||||
strictcount(instr.getIRVariable().getAST()) = 1
|
||||
unique( | | instr.getIRVariable().getAST()) = ast
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
@@ -133,8 +138,7 @@ private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
strictcount(instr.getResultIRType()) = 1 and
|
||||
instr.getResultIRType() = type and
|
||||
unique( | | instr.getResultIRType()) = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
@@ -151,8 +155,7 @@ private predicate fieldAddressValueNumber(
|
||||
TValueNumber objectAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
strictcount(instr.getField()) = 1 and
|
||||
unique( | | instr.getField()) = field and
|
||||
tvalueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
@@ -195,9 +198,9 @@ private predicate inheritanceConversionValueNumber(
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getBaseClass() = baseClass and
|
||||
instr.getDerivedClass() = derivedClass and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
tvalueNumber(instr.getUnary()) = operand and
|
||||
unique( | | instr.getBaseClass()) = baseClass and
|
||||
unique( | | instr.getDerivedClass()) = derivedClass
|
||||
}
|
||||
|
||||
private predicate loadTotalOverlapValueNumber(
|
||||
|
||||
@@ -71,7 +71,8 @@ newtype TInstructionTag =
|
||||
AsmTag() or
|
||||
AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) } or
|
||||
ThisAddressTag() or
|
||||
ThisLoadTag()
|
||||
ThisLoadTag() or
|
||||
StructuredBindingAccessTag()
|
||||
|
||||
class InstructionTag extends TInstructionTag {
|
||||
final string toString() { result = "Tag" }
|
||||
@@ -221,4 +222,6 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = ThisAddressTag() and result = "ThisAddress"
|
||||
or
|
||||
tag = ThisLoadTag() and result = "ThisLoad"
|
||||
or
|
||||
tag = StructuredBindingAccessTag() and result = "StructuredBindingAccess"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ private import semmle.code.cpp.ir.implementation.IRType
|
||||
private import semmle.code.cpp.ir.implementation.Opcode
|
||||
private import semmle.code.cpp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.cpp.ir.internal.CppType
|
||||
private import semmle.code.cpp.ir.internal.IRUtilities
|
||||
private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
private import InstructionTag
|
||||
private import TranslatedCondition
|
||||
@@ -813,7 +814,9 @@ abstract class TranslatedVariableAccess extends TranslatedNonConstantExpr {
|
||||
}
|
||||
|
||||
class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess {
|
||||
TranslatedNonFieldVariableAccess() { not expr instanceof FieldAccess }
|
||||
TranslatedNonFieldVariableAccess() {
|
||||
not expr instanceof FieldAccess and not isNonReferenceStructuredBinding(expr.getTarget())
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
if exists(this.getQualifier())
|
||||
@@ -860,6 +863,71 @@ class TranslatedFieldAccess extends TranslatedVariableAccess {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a variable access of a structured binding, where the type
|
||||
* of the structured binding is not of a reference type, e.g., `x0` and `x1`
|
||||
* in `auto [x0, x1] = xs` where `xs` is an array. Although the type of the
|
||||
* structured binding is a non-reference type, the structured binding behaves
|
||||
* like a reference. Hence, the translation requires a `VariableAddress` followed
|
||||
* by a `Load` instead of only a `VariableAddress` as produced by
|
||||
* `TranslatedVariableAccess`.
|
||||
*/
|
||||
class TranslatedStructuredBindingVariableAccess extends TranslatedNonConstantExpr {
|
||||
override VariableAccess expr;
|
||||
|
||||
TranslatedStructuredBindingVariableAccess() { isNonReferenceStructuredBinding(expr.getTarget()) }
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
// Structured bindings cannot be qualified.
|
||||
result = this.getInstruction(StructuredBindingAccessTag())
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
// Structured bindings cannot be qualified.
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getResult() { result = this.getInstruction(LoadTag()) }
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = StructuredBindingAccessTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(LoadTag())
|
||||
or
|
||||
tag = LoadTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = StructuredBindingAccessTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getTypeForGLValue(this.getLValueReferenceType())
|
||||
or
|
||||
tag = LoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getTypeForPRValue(this.getLValueReferenceType())
|
||||
}
|
||||
|
||||
private LValueReferenceType getLValueReferenceType() {
|
||||
// The extractor ensures `result` exists when `isNonReferenceStructuredBinding(expr.getTarget())` holds.
|
||||
result.getBaseType() = expr.getUnspecifiedType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = LoadTag() and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = this.getInstruction(StructuredBindingAccessTag())
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = StructuredBindingAccessTag() and
|
||||
result = getIRUserVariable(expr.getEnclosingFunction(), expr.getTarget())
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedFunctionAccess extends TranslatedNonConstantExpr {
|
||||
override FunctionAccess expr;
|
||||
|
||||
|
||||
@@ -106,6 +106,12 @@ private predicate filteredNumberableInstruction(Instruction instr) {
|
||||
or
|
||||
instr instanceof FieldAddressInstruction and
|
||||
count(instr.(FieldAddressInstruction).getField()) != 1
|
||||
or
|
||||
instr instanceof InheritanceConversionInstruction and
|
||||
(
|
||||
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
|
||||
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
|
||||
)
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(
|
||||
@@ -115,8 +121,7 @@ private predicate variableAddressValueNumber(
|
||||
// The underlying AST element is used as value-numbering key instead of the
|
||||
// `IRVariable` to work around a problem where a variable or expression with
|
||||
// multiple types gives rise to multiple `IRVariable`s.
|
||||
instr.getIRVariable().getAST() = ast and
|
||||
strictcount(instr.getIRVariable().getAST()) = 1
|
||||
unique( | | instr.getIRVariable().getAST()) = ast
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
@@ -133,8 +138,7 @@ private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
strictcount(instr.getResultIRType()) = 1 and
|
||||
instr.getResultIRType() = type and
|
||||
unique( | | instr.getResultIRType()) = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
@@ -151,8 +155,7 @@ private predicate fieldAddressValueNumber(
|
||||
TValueNumber objectAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
strictcount(instr.getField()) = 1 and
|
||||
unique( | | instr.getField()) = field and
|
||||
tvalueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
@@ -195,9 +198,9 @@ private predicate inheritanceConversionValueNumber(
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getBaseClass() = baseClass and
|
||||
instr.getDerivedClass() = derivedClass and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
tvalueNumber(instr.getUnary()) = operand and
|
||||
unique( | | instr.getBaseClass()) = baseClass and
|
||||
unique( | | instr.getDerivedClass()) = derivedClass
|
||||
}
|
||||
|
||||
private predicate loadTotalOverlapValueNumber(
|
||||
|
||||
@@ -11,6 +11,15 @@ private Type getDecayedType(Type type) {
|
||||
result.(PointerType).getBaseType() = type.(ArrayType).getBaseType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the sepcified variable is a structured binding with a non-reference
|
||||
* type.
|
||||
*/
|
||||
predicate isNonReferenceStructuredBinding(Variable v) {
|
||||
v.isStructuredBinding() and
|
||||
not v.getUnspecifiedType() instanceof ReferenceType
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual type of the specified variable, as opposed to the declared type.
|
||||
* This returns the type of the variable after any pointer decay is applied, and
|
||||
@@ -30,7 +39,12 @@ Type getVariableType(Variable v) {
|
||||
result = v.getInitializer().getExpr().getType()
|
||||
or
|
||||
not exists(v.getInitializer()) and result = v.getType()
|
||||
else result = v.getType()
|
||||
else
|
||||
if isNonReferenceStructuredBinding(v)
|
||||
then
|
||||
// The extractor ensures `r` exists when `isNonReferenceStructuredBinding(v)` holds.
|
||||
exists(LValueReferenceType r | r.getBaseType() = v.getUnspecifiedType() | result = r)
|
||||
else result = v.getType()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.0.10
|
||||
|
||||
### Deprecated Classes
|
||||
|
||||
* The `CodeDuplication.Copy`, `CodeDuplication.DuplicateBlock`, and `CodeDuplication.SimilarBlock` classes have been deprecated.
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### New Queries
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @description Exposing system data or debugging information helps
|
||||
* an adversary learn about the system and form an
|
||||
* attack plan.
|
||||
* @kind problem
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 6.5
|
||||
* @precision medium
|
||||
@@ -14,7 +14,9 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Environment
|
||||
import semmle.code.cpp.security.OutputWrite
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* An element that should not be exposed to an adversary.
|
||||
@@ -24,42 +26,19 @@ abstract class SystemData extends Element {
|
||||
* Gets an expression that is part of this `SystemData`.
|
||||
*/
|
||||
abstract Expr getAnExpr();
|
||||
|
||||
/**
|
||||
* Gets an expression whose value originates from, or is used by,
|
||||
* this `SystemData`.
|
||||
*/
|
||||
Expr getAnExprIndirect() {
|
||||
// direct SystemData
|
||||
result = this.getAnExpr() or
|
||||
// flow via global or member variable (conservative approximation)
|
||||
result = this.getAnAffectedVar().getAnAccess() or
|
||||
// flow via stack variable
|
||||
definitionUsePair(_, this.getAnExprIndirect(), result) or
|
||||
useUsePair(_, this.getAnExprIndirect(), result) or
|
||||
useUsePair(_, result, this.getAnExprIndirect()) or
|
||||
// flow from assigned value to assignment expression
|
||||
result.(AssignExpr).getRValue() = this.getAnExprIndirect()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a global or member variable that may be affected by this system
|
||||
* data (conservative approximation).
|
||||
*/
|
||||
private Variable getAnAffectedVar() {
|
||||
(
|
||||
result.getAnAssignedValue() = this.getAnExprIndirect() or
|
||||
result.getAnAccess() = this.getAnExprIndirect()
|
||||
) and
|
||||
not result instanceof LocalScopeVariable
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data originating from the environment.
|
||||
*/
|
||||
class EnvData extends SystemData {
|
||||
EnvData() { this instanceof EnvironmentRead }
|
||||
EnvData() {
|
||||
// identify risky looking environment variables only
|
||||
this.(EnvironmentRead)
|
||||
.getEnvironmentVariable()
|
||||
.toLowerCase()
|
||||
.regexpMatch(".*(user|host|admin|root|home|path|http|ssl|snmp|sock|port|proxy|pass|token|crypt|key).*")
|
||||
}
|
||||
|
||||
override Expr getAnExpr() { result = this }
|
||||
}
|
||||
@@ -91,11 +70,6 @@ class SQLConnectInfo extends SystemData {
|
||||
}
|
||||
|
||||
private predicate posixSystemInfo(FunctionCall source, Element use) {
|
||||
// long sysconf(int name)
|
||||
// - various OS / system values and limits
|
||||
source.getTarget().hasName("sysconf") and
|
||||
use = source
|
||||
or
|
||||
// size_t confstr(int name, char *buf, size_t len)
|
||||
// - various OS / system strings, such as the libc version
|
||||
// int statvfs(const char *__path, struct statvfs *__buf)
|
||||
@@ -311,70 +285,31 @@ class RegQuery extends SystemData {
|
||||
override Expr getAnExpr() { regQuery(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Somewhere data is output.
|
||||
*/
|
||||
abstract class DataOutput extends Element {
|
||||
/**
|
||||
* Get an expression containing data that is output.
|
||||
*/
|
||||
abstract Expr getASource();
|
||||
}
|
||||
class ExposedSystemDataConfiguration extends TaintTracking::Configuration {
|
||||
ExposedSystemDataConfiguration() { this = "ExposedSystemDataConfiguration" }
|
||||
|
||||
/**
|
||||
* Data that is output via standard output or standard error.
|
||||
*/
|
||||
class StandardOutput extends DataOutput instanceof OutputWrite {
|
||||
override Expr getASource() { result = OutputWrite.super.getASource() }
|
||||
}
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source.asConvertedExpr() = any(SystemData sd).getAnExpr()
|
||||
}
|
||||
|
||||
private predicate socketCallOrIndirect(FunctionCall call) {
|
||||
// direct socket call
|
||||
// int socket(int domain, int type, int protocol);
|
||||
call.getTarget().getName() = "socket"
|
||||
or
|
||||
exists(ReturnStmt rtn |
|
||||
// indirect socket call
|
||||
call.getTarget() = rtn.getEnclosingFunction() and
|
||||
(
|
||||
socketCallOrIndirect(rtn.getExpr()) or
|
||||
socketCallOrIndirect(rtn.getExpr().(VariableAccess).getTarget().getAnAssignedValue())
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall fc, FunctionInput input, int arg |
|
||||
fc.getTarget().(RemoteFlowSinkFunction).hasRemoteFlowSink(input, _) and
|
||||
input.isParameterDeref(arg) and
|
||||
fc.getArgument(arg).getAChild*() = sink.asExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate socketFileDescriptor(Expr e) {
|
||||
exists(Variable var, FunctionCall socket |
|
||||
socketCallOrIndirect(socket) and
|
||||
var.getAnAssignedValue() = socket and
|
||||
e = var.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate socketOutput(FunctionCall call, Expr data) {
|
||||
(
|
||||
// ssize_t send(int sockfd, const void *buf, size_t len, int flags);
|
||||
// ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
|
||||
// const struct sockaddr *dest_addr, socklen_t addrlen);
|
||||
// ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
|
||||
// int write(int handle, void *buffer, int nbyte);
|
||||
call.getTarget().hasGlobalName(["send", "sendto", "sendmsg", "write"]) and
|
||||
data = call.getArgument(1) and
|
||||
socketFileDescriptor(call.getArgument(0))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Data that is output via a socket.
|
||||
*/
|
||||
class SocketOutput extends DataOutput {
|
||||
SocketOutput() { socketOutput(this, _) }
|
||||
|
||||
override Expr getASource() { socketOutput(this, result) }
|
||||
}
|
||||
|
||||
from SystemData sd, DataOutput ow
|
||||
from ExposedSystemDataConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where
|
||||
sd.getAnExprIndirect() = ow.getASource() or
|
||||
sd.getAnExprIndirect() = ow.getASource().getAChild*()
|
||||
select ow, "This operation exposes system data from $@.", sd, sd.toString()
|
||||
config.hasFlowPath(source, sink) and
|
||||
not exists(
|
||||
DataFlow::Node alt // remove duplicate results on conversions
|
||||
|
|
||||
config.hasFlow(source.getNode(), alt) and
|
||||
alt.asConvertedExpr() = sink.getNode().asExpr() and
|
||||
alt != sink.getNode()
|
||||
)
|
||||
select sink, source, sink, "This operation exposes system data from $@.", source,
|
||||
source.getNode().toString()
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `cpp/system-data-exposure` query has been modernized and has converted to a `path-problem` query. There are now fewer false positive results.
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
## 0.0.10
|
||||
|
||||
### Deprecated Classes
|
||||
|
||||
* The `CodeDuplication.Copy`, `CodeDuplication.DuplicateBlock`, and `CodeDuplication.SimilarBlock` classes have been deprecated.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.9
|
||||
lastReleaseVersion: 0.0.10
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# These queries are infeasible to compute on large projects:
|
||||
- exclude:
|
||||
query path:
|
||||
- Security/CWE/CWE-497/ExposedSystemData.ql
|
||||
- Critical/DescriptorMayNotBeClosed.ql
|
||||
- Critical/DescriptorNeverClosed.ql
|
||||
- Critical/FileMayNotBeClosed.ql
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.0.10-dev
|
||||
version: 0.0.11-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1471,31 +1471,43 @@ void array_structured_binding() {
|
||||
// explicit reference version
|
||||
{
|
||||
auto& unnamed_local_variable = xs;
|
||||
auto& x0 = xs[0];
|
||||
auto& x1 = xs[1];
|
||||
auto& x0 = unnamed_local_variable[0];
|
||||
auto& x1 = unnamed_local_variable[1];
|
||||
x1 = 3;
|
||||
int &rx1 = x1;
|
||||
int x = x1;
|
||||
}
|
||||
}
|
||||
|
||||
struct StructuredBindingDataMemberMemberStruct {
|
||||
int x = 5;
|
||||
};
|
||||
|
||||
struct StructuredBindingDataMemberStruct {
|
||||
typedef int ArrayType[2];
|
||||
typedef int &RefType;
|
||||
int i = 1;
|
||||
double d = 2.0;
|
||||
unsigned int b : 3;
|
||||
int& r = i;
|
||||
int* p = &i;
|
||||
ArrayType xs = {1, 2};
|
||||
RefType r_alt = i;
|
||||
StructuredBindingDataMemberMemberStruct m;
|
||||
};
|
||||
|
||||
void data_member_structured_binding() {
|
||||
StructuredBindingDataMemberStruct s;
|
||||
// structured binding use
|
||||
{
|
||||
auto [i, d, b, r] = s;
|
||||
auto [i, d, b, r, p, xs, r_alt, m] = s;
|
||||
d = 4.0;
|
||||
double& rd = d;
|
||||
int v = i;
|
||||
r = 5;
|
||||
*p = 6;
|
||||
int& rr = r;
|
||||
int* pr = &r;
|
||||
int w = r;
|
||||
}
|
||||
// explicit reference version
|
||||
@@ -1505,58 +1517,67 @@ void data_member_structured_binding() {
|
||||
auto& d = unnamed_local_variable.d;
|
||||
// no equivalent for b
|
||||
auto& r = unnamed_local_variable.r;
|
||||
auto& p = unnamed_local_variable.p;
|
||||
d = 4.0;
|
||||
double& rd = d;
|
||||
int v = i;
|
||||
r = 5;
|
||||
*p = 6;
|
||||
int& rr = r;
|
||||
int* pr = &r;
|
||||
int w = r;
|
||||
}
|
||||
}
|
||||
struct StructuredBindingTuple;
|
||||
|
||||
namespace std {
|
||||
template<typename T>
|
||||
struct tuple_size;
|
||||
template<>
|
||||
struct tuple_size<StructuredBindingTuple> {
|
||||
static const unsigned int value = 3;
|
||||
};
|
||||
|
||||
template<int, typename T>
|
||||
struct tuple_element;
|
||||
template<>
|
||||
struct tuple_element<0, StructuredBindingTuple> {
|
||||
using type = int;
|
||||
};
|
||||
template<>
|
||||
struct tuple_element<1, StructuredBindingTuple> {
|
||||
using type = double;
|
||||
};
|
||||
template<>
|
||||
struct tuple_element<2, StructuredBindingTuple> {
|
||||
using type = int&;
|
||||
};
|
||||
}
|
||||
|
||||
struct StructuredBindingTuple {
|
||||
struct StructuredBindingTupleRefGet {
|
||||
int i = 1;
|
||||
double d = 2.2;
|
||||
int& r = i;
|
||||
|
||||
template<int i>
|
||||
typename std::tuple_element<i, StructuredBindingTuple>::type& get();
|
||||
typename std::tuple_element<i, StructuredBindingTupleRefGet>::type& get();
|
||||
};
|
||||
|
||||
template<>
|
||||
std::tuple_element<0, StructuredBindingTuple>::type& StructuredBindingTuple::get<0>() { return i; }
|
||||
template<>
|
||||
std::tuple_element<1, StructuredBindingTuple>::type& StructuredBindingTuple::get<1>() { return d; }
|
||||
template<>
|
||||
std::tuple_element<2, StructuredBindingTuple>::type& StructuredBindingTuple::get<2>() { return r; }
|
||||
struct std::tuple_size<StructuredBindingTupleRefGet> {
|
||||
static const unsigned int value = 3;
|
||||
};
|
||||
|
||||
void tuple_structured_binding() {
|
||||
StructuredBindingTuple t;
|
||||
template<>
|
||||
struct std::tuple_element<0, StructuredBindingTupleRefGet> {
|
||||
using type = int;
|
||||
};
|
||||
template<>
|
||||
struct std::tuple_element<1, StructuredBindingTupleRefGet> {
|
||||
using type = double;
|
||||
};
|
||||
template<>
|
||||
struct std::tuple_element<2, StructuredBindingTupleRefGet> {
|
||||
using type = int&;
|
||||
};
|
||||
|
||||
template<>
|
||||
std::tuple_element<0, StructuredBindingTupleRefGet>::type& StructuredBindingTupleRefGet::get<0>() {
|
||||
return i;
|
||||
}
|
||||
template<>
|
||||
std::tuple_element<1, StructuredBindingTupleRefGet>::type& StructuredBindingTupleRefGet::get<1>() {
|
||||
return d;
|
||||
}
|
||||
template<>
|
||||
std::tuple_element<2, StructuredBindingTupleRefGet>::type& StructuredBindingTupleRefGet::get<2>() {
|
||||
return r;
|
||||
}
|
||||
|
||||
void tuple_structured_binding_ref_get() {
|
||||
StructuredBindingTupleRefGet t;
|
||||
// structured binding use
|
||||
{
|
||||
auto [i, d, r] = t;
|
||||
@@ -1582,4 +1603,70 @@ void tuple_structured_binding() {
|
||||
}
|
||||
}
|
||||
|
||||
struct StructuredBindingTupleNoRefGet {
|
||||
int i = 1;
|
||||
int& r = i;
|
||||
|
||||
template<int i>
|
||||
typename std::tuple_element<i, StructuredBindingTupleNoRefGet>::type get();
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::tuple_size<StructuredBindingTupleNoRefGet> {
|
||||
static const unsigned int value = 3;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::tuple_element<0, StructuredBindingTupleNoRefGet> {
|
||||
using type = int;
|
||||
};
|
||||
template<>
|
||||
struct std::tuple_element<1, StructuredBindingTupleNoRefGet> {
|
||||
using type = int&;
|
||||
};
|
||||
template<>
|
||||
struct std::tuple_element<2, StructuredBindingTupleNoRefGet> {
|
||||
using type = int&&;
|
||||
};
|
||||
|
||||
template<>
|
||||
std::tuple_element<0, StructuredBindingTupleNoRefGet>::type StructuredBindingTupleNoRefGet::get<0>() {
|
||||
return i;
|
||||
}
|
||||
template<>
|
||||
std::tuple_element<1, StructuredBindingTupleNoRefGet>::type StructuredBindingTupleNoRefGet::get<1>() {
|
||||
return r;
|
||||
}
|
||||
template<>
|
||||
std::tuple_element<2, StructuredBindingTupleNoRefGet>::type StructuredBindingTupleNoRefGet::get<2>() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
void tuple_structured_binding_no_ref_get() {
|
||||
StructuredBindingTupleNoRefGet t;
|
||||
//structured binding use
|
||||
{
|
||||
auto&& [i, r, rv] = t;
|
||||
i = 4;
|
||||
int& ri = i;
|
||||
int v = i;
|
||||
r = 5;
|
||||
int& rr = r;
|
||||
int w = r;
|
||||
}
|
||||
// explicit reference version
|
||||
{
|
||||
auto&& unnamed_local_variable = t;
|
||||
auto&& i = unnamed_local_variable.get<0>();
|
||||
auto& r = unnamed_local_variable.get<1>();
|
||||
auto&& rv = unnamed_local_variable.get<2>();
|
||||
i = 4;
|
||||
int& ri = i;
|
||||
int v = i;
|
||||
r = 5;
|
||||
int& rr = r;
|
||||
int w = r;
|
||||
}
|
||||
}
|
||||
|
||||
// semmle-extractor-options: -std=c++17 --clang
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@ containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
| ir.cpp:1486:8:1486:8 | Unary | Operand 'Unary' is not dominated by its definition in function '$@'. | ir.cpp:1486:8:1486:8 | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() |
|
||||
switchInstructionWithoutDefaultEdge
|
||||
notMarkedAsConflated
|
||||
wronglyMarkedAsConflated
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1,4 @@
|
||||
| tests.c:70:9:70:15 | call to fprintf | This operation exposes system data from $@. | tests.c:54:13:54:22 | call to LogonUserA | call to LogonUserA |
|
||||
edges
|
||||
nodes
|
||||
subpaths
|
||||
#select
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
| tests.c:29:9:29:14 | call to printf | tests.c:29:16:29:21 | %s\n |
|
||||
| tests.c:29:9:29:14 | call to printf | tests.c:29:24:29:27 | line |
|
||||
| tests.c:43:13:43:21 | call to printLine | tests.c:43:23:43:38 | fgets() failed |
|
||||
| tests.c:62:13:62:21 | call to printLine | tests.c:62:23:62:52 | User logged in successfully. |
|
||||
| tests.c:67:13:67:21 | call to printLine | tests.c:67:23:67:40 | Unable to login. |
|
||||
| tests.c:70:9:70:15 | call to fprintf | tests.c:70:25:70:67 | User attempted access with password: %s\n |
|
||||
| tests.c:70:9:70:15 | call to fprintf | tests.c:70:70:70:77 | password |
|
||||
@@ -1,4 +0,0 @@
|
||||
import semmle.code.cpp.security.OutputWrite
|
||||
|
||||
from OutputWrite ow
|
||||
select ow, ow.getASource()
|
||||
@@ -67,6 +67,6 @@ void CWE535_Info_Exposure_Shell_Error__w32_char_01_bad()
|
||||
printLine("Unable to login.");
|
||||
}
|
||||
/* FLAW: Write sensitive data to stderr */
|
||||
fprintf(stderr, "User attempted access with password: %s\n", password);
|
||||
fprintf(stderr, "User attempted access with password: %s\n", password); // [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,47 @@
|
||||
| tests2.cpp:27:12:27:12 | call to operator<< | This operation exposes system data from $@. | tests2.cpp:27:15:27:20 | call to getenv | call to getenv |
|
||||
| tests2.cpp:28:25:28:25 | call to operator<< | This operation exposes system data from $@. | tests2.cpp:28:28:28:33 | call to getenv | call to getenv |
|
||||
edges
|
||||
| tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:26 | (const char *)... |
|
||||
| tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:26 | (const char *)... |
|
||||
| tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:30 | (const char *)... |
|
||||
| tests2.cpp:76:18:76:38 | call to mysql_get_client_info | tests2.cpp:79:14:79:19 | (const char *)... |
|
||||
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info |
|
||||
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info |
|
||||
| tests2.cpp:89:42:89:45 | str1 | tests2.cpp:91:14:91:17 | str1 |
|
||||
| tests2.cpp:99:8:99:15 | call to getpwuid | tests2.cpp:100:14:100:15 | pw |
|
||||
| tests2.cpp:107:3:107:4 | c1 [post update] [ptr] | tests2.cpp:109:14:109:15 | c1 [read] [ptr] |
|
||||
| tests2.cpp:107:6:107:8 | ptr [post update] | tests2.cpp:107:3:107:4 | c1 [post update] [ptr] |
|
||||
| tests2.cpp:107:12:107:17 | call to getenv | tests2.cpp:107:6:107:8 | ptr [post update] |
|
||||
| tests2.cpp:109:14:109:15 | c1 [read] [ptr] | tests2.cpp:109:14:109:19 | (const char *)... |
|
||||
nodes
|
||||
| tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv |
|
||||
| tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv |
|
||||
| tests2.cpp:63:13:63:26 | (const char *)... | semmle.label | (const char *)... |
|
||||
| tests2.cpp:64:13:64:18 | call to getenv | semmle.label | call to getenv |
|
||||
| tests2.cpp:64:13:64:18 | call to getenv | semmle.label | call to getenv |
|
||||
| tests2.cpp:64:13:64:26 | (const char *)... | semmle.label | (const char *)... |
|
||||
| tests2.cpp:65:13:65:18 | call to getenv | semmle.label | call to getenv |
|
||||
| tests2.cpp:65:13:65:18 | call to getenv | semmle.label | call to getenv |
|
||||
| tests2.cpp:65:13:65:30 | (const char *)... | semmle.label | (const char *)... |
|
||||
| tests2.cpp:76:18:76:38 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
|
||||
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
|
||||
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
|
||||
| tests2.cpp:79:14:79:19 | (const char *)... | semmle.label | (const char *)... |
|
||||
| tests2.cpp:89:42:89:45 | str1 | semmle.label | str1 |
|
||||
| tests2.cpp:91:14:91:17 | str1 | semmle.label | str1 |
|
||||
| tests2.cpp:99:8:99:15 | call to getpwuid | semmle.label | call to getpwuid |
|
||||
| tests2.cpp:100:14:100:15 | pw | semmle.label | pw |
|
||||
| tests2.cpp:107:3:107:4 | c1 [post update] [ptr] | semmle.label | c1 [post update] [ptr] |
|
||||
| tests2.cpp:107:6:107:8 | ptr [post update] | semmle.label | ptr [post update] |
|
||||
| tests2.cpp:107:12:107:17 | call to getenv | semmle.label | call to getenv |
|
||||
| tests2.cpp:109:14:109:15 | c1 [read] [ptr] | semmle.label | c1 [read] [ptr] |
|
||||
| tests2.cpp:109:14:109:19 | (const char *)... | semmle.label | (const char *)... |
|
||||
subpaths
|
||||
#select
|
||||
| tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:63:13:63:18 | call to getenv | call to getenv |
|
||||
| tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:64:13:64:18 | call to getenv | call to getenv |
|
||||
| tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:18 | call to getenv | This operation exposes system data from $@. | tests2.cpp:65:13:65:18 | call to getenv | call to getenv |
|
||||
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | call to mysql_get_client_info |
|
||||
| tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:78:14:78:34 | call to mysql_get_client_info | call to mysql_get_client_info |
|
||||
| tests2.cpp:79:14:79:19 | (const char *)... | tests2.cpp:76:18:76:38 | call to mysql_get_client_info | tests2.cpp:79:14:79:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:76:18:76:38 | call to mysql_get_client_info | call to mysql_get_client_info |
|
||||
| tests2.cpp:91:14:91:17 | str1 | tests2.cpp:89:42:89:45 | str1 | tests2.cpp:91:14:91:17 | str1 | This operation exposes system data from $@. | tests2.cpp:89:42:89:45 | str1 | str1 |
|
||||
| tests2.cpp:100:14:100:15 | pw | tests2.cpp:99:8:99:15 | call to getpwuid | tests2.cpp:100:14:100:15 | pw | This operation exposes system data from $@. | tests2.cpp:99:8:99:15 | call to getpwuid | call to getpwuid |
|
||||
| tests2.cpp:109:14:109:19 | (const char *)... | tests2.cpp:107:12:107:17 | call to getenv | tests2.cpp:109:14:109:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:107:12:107:17 | call to getenv | call to getenv |
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
| tests2.cpp:27:12:27:12 | call to operator<< | tests2.cpp:27:15:27:20 | call to getenv |
|
||||
| tests2.cpp:28:12:28:12 | call to operator<< | tests2.cpp:28:15:28:23 | PATH = |
|
||||
| tests2.cpp:28:25:28:25 | call to operator<< | tests2.cpp:28:28:28:33 | call to getenv |
|
||||
| tests2.cpp:28:43:28:43 | call to operator<< | tests2.cpp:28:46:28:48 | . |
|
||||
| tests2.cpp:29:12:29:12 | call to operator<< | tests2.cpp:29:15:29:28 | PATHPATHPATH |
|
||||
@@ -1,4 +0,0 @@
|
||||
import semmle.code.cpp.security.OutputWrite
|
||||
|
||||
from OutputWrite ow
|
||||
select ow, ow.getASource()
|
||||
@@ -3,6 +3,7 @@
|
||||
// library functions etc
|
||||
|
||||
char *getenv(const char *name);
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
|
||||
namespace std
|
||||
{
|
||||
@@ -20,11 +21,92 @@ namespace std
|
||||
extern ostream cout;
|
||||
}
|
||||
|
||||
int socket(int p1, int p2, int p3);
|
||||
void send(int sock, const char *buffer, int p3, int p4);
|
||||
|
||||
const char *mysql_get_client_info();
|
||||
void mysql_real_connect(int p1, int p2, int p3, const char *password, int p5, int p6, int p7, int p8);
|
||||
|
||||
struct container
|
||||
{
|
||||
char *ptr;
|
||||
};
|
||||
|
||||
struct passwd
|
||||
{
|
||||
// ...
|
||||
|
||||
char *pw_passwd;
|
||||
|
||||
// ...
|
||||
};
|
||||
|
||||
passwd *getpwuid(int uid);
|
||||
|
||||
int val();
|
||||
|
||||
// test cases
|
||||
|
||||
const char *global1 = mysql_get_client_info();
|
||||
const char *global2 = "abc";
|
||||
|
||||
void test1()
|
||||
{
|
||||
std::cout << getenv("HOME"); // BAD: outputs HOME environment variable
|
||||
std::cout << "PATH = " << getenv("PATH") << "."; // BAD: outputs PATH environment variable
|
||||
int sock = socket(val(), val(), val());
|
||||
|
||||
// tests for a strict implementation of CWE-497
|
||||
std::cout << getenv("HOME"); // BAD: outputs HOME environment variable [NOT DETECTED]
|
||||
std::cout << "PATH = " << getenv("PATH") << "."; // BAD: outputs PATH environment variable [NOT DETECTED]
|
||||
std::cout << "PATHPATHPATH"; // GOOD: not system data
|
||||
|
||||
// tests for a more pragmatic implementation of CWE-497
|
||||
send(sock, getenv("HOME"), val(), val()); // BAD
|
||||
send(sock, getenv("PATH"), val(), val()); // BAD
|
||||
send(sock, getenv("USERNAME"), val(), val()); // BAD
|
||||
send(sock, getenv("HARMLESS"), val(), val()); // GOOD: harmless information
|
||||
send(sock, "HOME", val(), val()); // GOOD: not system data
|
||||
send(sock, "PATH", val(), val()); // GOOD: not system data
|
||||
send(sock, "USERNAME", val(), val()); // GOOD: not system data
|
||||
send(sock, "HARMLESS", val(), val()); // GOOD: not system data
|
||||
|
||||
// tests for `mysql_get_client_info`, including via a global
|
||||
{
|
||||
char buffer[256];
|
||||
|
||||
strcpy(buffer, mysql_get_client_info());
|
||||
|
||||
send(sock, mysql_get_client_info(), val(), val()); // BAD
|
||||
send(sock, buffer, val(), val()); // BAD
|
||||
send(sock, global1, val(), val()); // BAD [NOT DETECTED]
|
||||
send(sock, global2, val(), val()); // GOOD: not system data
|
||||
}
|
||||
|
||||
// tests for `mysql_real_connect`
|
||||
{
|
||||
const char *str1 = "123456";
|
||||
const char *str2 = "abcdef";
|
||||
|
||||
mysql_real_connect(sock, val(), val(), str1, val(), val(), val(), val());
|
||||
|
||||
send(sock, str1, val(), val()); // BAD
|
||||
send(sock, str2, val(), val()); // GOOD: not system data
|
||||
}
|
||||
|
||||
// tests for `getpwuid`
|
||||
{
|
||||
passwd *pw;
|
||||
|
||||
pw = getpwuid(val());
|
||||
send(sock, pw->pw_passwd, val(), val()); // BAD
|
||||
}
|
||||
|
||||
// tests for containers
|
||||
{
|
||||
container c1, c2;
|
||||
|
||||
c1.ptr = getenv("MY_SECRET_TOKEN");
|
||||
c2.ptr = "";
|
||||
send(sock, c1.ptr, val(), val()); // BAD
|
||||
send(sock, c2.ptr, val(), val()); // GOOD: not system data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,6 +399,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
actions.GetEnvironmentVariable["LGTM_INDEX_SOLUTION"] = solution;
|
||||
actions.GetEnvironmentVariable["LGTM_INDEX_IGNORE_ERRORS"] = ignoreErrors;
|
||||
actions.GetEnvironmentVariable["LGTM_INDEX_BUILDLESS"] = buildless;
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_OPTION_BUILDLESS"] = buildless;
|
||||
actions.GetEnvironmentVariable["LGTM_INDEX_ALL_SOLUTIONS"] = allSolutions;
|
||||
actions.GetEnvironmentVariable["LGTM_INDEX_NUGET_RESTORE"] = nugetRestore;
|
||||
actions.GetEnvironmentVariable["ProgramFiles(x86)"] = isWindows ? @"C:\Program Files (x86)" : null;
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace Semmle.Autobuild.Shared
|
||||
/// </summary>
|
||||
public class AutobuildOptions
|
||||
{
|
||||
private const string prefix = "LGTM_INDEX_";
|
||||
private const string lgtmPrefix = "LGTM_INDEX_";
|
||||
private const string extractorOptionPrefix = "CODEQL_EXTRACTOR_CSHARP_OPTION_";
|
||||
|
||||
public int SearchDepth { get; } = 3;
|
||||
public string RootDirectory { get; }
|
||||
@@ -36,20 +37,21 @@ namespace Semmle.Autobuild.Shared
|
||||
public AutobuildOptions(IBuildActions actions, Language language)
|
||||
{
|
||||
RootDirectory = actions.GetCurrentDirectory();
|
||||
VsToolsVersion = actions.GetEnvironmentVariable(prefix + "VSTOOLS_VERSION");
|
||||
MsBuildArguments = actions.GetEnvironmentVariable(prefix + "MSBUILD_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions);
|
||||
MsBuildPlatform = actions.GetEnvironmentVariable(prefix + "MSBUILD_PLATFORM");
|
||||
MsBuildConfiguration = actions.GetEnvironmentVariable(prefix + "MSBUILD_CONFIGURATION");
|
||||
MsBuildTarget = actions.GetEnvironmentVariable(prefix + "MSBUILD_TARGET");
|
||||
DotNetArguments = actions.GetEnvironmentVariable(prefix + "DOTNET_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions);
|
||||
DotNetVersion = actions.GetEnvironmentVariable(prefix + "DOTNET_VERSION");
|
||||
BuildCommand = actions.GetEnvironmentVariable(prefix + "BUILD_COMMAND");
|
||||
Solution = actions.GetEnvironmentVariable(prefix + "SOLUTION").AsListWithExpandedEnvVars(actions, Array.Empty<string>());
|
||||
VsToolsVersion = actions.GetEnvironmentVariable(lgtmPrefix + "VSTOOLS_VERSION");
|
||||
MsBuildArguments = actions.GetEnvironmentVariable(lgtmPrefix + "MSBUILD_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions);
|
||||
MsBuildPlatform = actions.GetEnvironmentVariable(lgtmPrefix + "MSBUILD_PLATFORM");
|
||||
MsBuildConfiguration = actions.GetEnvironmentVariable(lgtmPrefix + "MSBUILD_CONFIGURATION");
|
||||
MsBuildTarget = actions.GetEnvironmentVariable(lgtmPrefix + "MSBUILD_TARGET");
|
||||
DotNetArguments = actions.GetEnvironmentVariable(lgtmPrefix + "DOTNET_ARGUMENTS")?.AsStringWithExpandedEnvVars(actions);
|
||||
DotNetVersion = actions.GetEnvironmentVariable(lgtmPrefix + "DOTNET_VERSION");
|
||||
BuildCommand = actions.GetEnvironmentVariable(lgtmPrefix + "BUILD_COMMAND");
|
||||
Solution = actions.GetEnvironmentVariable(lgtmPrefix + "SOLUTION").AsListWithExpandedEnvVars(actions, Array.Empty<string>());
|
||||
|
||||
IgnoreErrors = actions.GetEnvironmentVariable(prefix + "IGNORE_ERRORS").AsBool("ignore_errors", false);
|
||||
Buildless = actions.GetEnvironmentVariable(prefix + "BUILDLESS").AsBool("buildless", false);
|
||||
AllSolutions = actions.GetEnvironmentVariable(prefix + "ALL_SOLUTIONS").AsBool("all_solutions", false);
|
||||
NugetRestore = actions.GetEnvironmentVariable(prefix + "NUGET_RESTORE").AsBool("nuget_restore", true);
|
||||
IgnoreErrors = actions.GetEnvironmentVariable(lgtmPrefix + "IGNORE_ERRORS").AsBool("ignore_errors", false);
|
||||
Buildless = actions.GetEnvironmentVariable(lgtmPrefix + "BUILDLESS").AsBool("buildless", false) ||
|
||||
actions.GetEnvironmentVariable(extractorOptionPrefix + "BUILDLESS").AsBool("buildless", false);
|
||||
AllSolutions = actions.GetEnvironmentVariable(lgtmPrefix + "ALL_SOLUTIONS").AsBool("all_solutions", false);
|
||||
NugetRestore = actions.GetEnvironmentVariable(lgtmPrefix + "NUGET_RESTORE").AsBool("nuget_restore", true);
|
||||
|
||||
Language = language;
|
||||
}
|
||||
@@ -62,21 +64,12 @@ namespace Semmle.Autobuild.Shared
|
||||
if (value is null)
|
||||
return defaultValue;
|
||||
|
||||
switch (value.ToLower())
|
||||
return value.ToLower() switch
|
||||
{
|
||||
case "on":
|
||||
case "yes":
|
||||
case "true":
|
||||
case "enabled":
|
||||
return true;
|
||||
case "off":
|
||||
case "no":
|
||||
case "false":
|
||||
case "disabled":
|
||||
return false;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(param, value, "The Boolean value is invalid.");
|
||||
}
|
||||
"on" or "yes" or "true" or "enabled" => true,
|
||||
"off" or "no" or "false" or "disabled" => false,
|
||||
_ => throw new ArgumentOutOfRangeException(param, value, "The Boolean value is invalid."),
|
||||
};
|
||||
}
|
||||
|
||||
public static string[] AsListWithExpandedEnvVars(this string? value, IBuildActions actions, string[] defaultValue)
|
||||
|
||||
@@ -16,3 +16,28 @@ file_types:
|
||||
extensions:
|
||||
- .cs
|
||||
legacy_qltest_extraction: true
|
||||
options:
|
||||
trap:
|
||||
title: Options pertaining to TRAP.
|
||||
type: object
|
||||
properties:
|
||||
compression:
|
||||
title: Controls compression for the TRAP files written by the extractor.
|
||||
description: >
|
||||
This option is only intended for use in debugging the extractor. Accepted
|
||||
values are 'brotli' (the default, to write brotli-compressed TRAP), 'gzip', and 'none'
|
||||
(to write uncompressed TRAP).
|
||||
type: string
|
||||
pattern: "^(none|gzip|brotli)$"
|
||||
buildless:
|
||||
title: Whether to use buildless (standalone) extraction.
|
||||
description: >
|
||||
A value indicating, which type of extraction the autobuilder should perform.
|
||||
If 'true', then the standalone extractor will be used, otherwise tracing extraction
|
||||
will be performed.
|
||||
The default is 'false'.
|
||||
Note that buildless extraction will generally yield less accurate analysis results,
|
||||
and should only be used in cases where it is not possible to build
|
||||
the code (for example if it uses inaccessible dependencies).
|
||||
type: string
|
||||
pattern: "^(false|true)$"
|
||||
|
||||
@@ -46,7 +46,9 @@ namespace Semmle.Extraction.CSharp
|
||||
var argsList = new List<string>(arguments);
|
||||
|
||||
if (!string.IsNullOrEmpty(extractionOptions))
|
||||
{
|
||||
argsList.AddRange(extractionOptions.Split(' '));
|
||||
}
|
||||
|
||||
options.ParseArguments(argsList);
|
||||
return options;
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Semmle.Extraction.Tests
|
||||
Assert.False(options.ClrTracer);
|
||||
Assert.False(options.PDB);
|
||||
Assert.False(options.Fast);
|
||||
Assert.Equal(TrapWriter.CompressionMode.Brotli, options.TrapCompression);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -205,5 +206,25 @@ namespace Semmle.Extraction.Tests
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompressionTests()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_TRAP_COMPRESSION", "gzip");
|
||||
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
|
||||
Assert.Equal(TrapWriter.CompressionMode.Gzip, options.TrapCompression);
|
||||
|
||||
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_TRAP_COMPRESSION", "brotli");
|
||||
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
|
||||
Assert.Equal(TrapWriter.CompressionMode.Brotli, options.TrapCompression);
|
||||
|
||||
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_TRAP_COMPRESSION", "none");
|
||||
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
|
||||
Assert.Equal(TrapWriter.CompressionMode.None, options.TrapCompression);
|
||||
|
||||
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_TRAP_COMPRESSION", null);
|
||||
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
|
||||
Assert.Equal(TrapWriter.CompressionMode.Brotli, options.TrapCompression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Semmle.Util.Logging;
|
||||
using Semmle.Util;
|
||||
|
||||
@@ -49,10 +50,11 @@ namespace Semmle.Extraction
|
||||
/// </summary>
|
||||
public bool QlTest { get; private set; } = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The compression algorithm used for trap files.
|
||||
/// </summary>
|
||||
public TrapWriter.CompressionMode TrapCompression { get; set; } = TrapWriter.CompressionMode.Brotli;
|
||||
public TrapWriter.CompressionMode TrapCompression { get; private set; } = TrapWriter.CompressionMode.Brotli;
|
||||
|
||||
public virtual bool HandleOption(string key, string value)
|
||||
{
|
||||
@@ -64,6 +66,13 @@ namespace Semmle.Extraction
|
||||
case "verbosity":
|
||||
Verbosity = (Verbosity)int.Parse(value);
|
||||
return true;
|
||||
case "trap_compression":
|
||||
if (Enum.TryParse<TrapWriter.CompressionMode>(value, true, out var mode))
|
||||
{
|
||||
TrapCompression = mode;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -102,9 +111,6 @@ namespace Semmle.Extraction
|
||||
case "qltest":
|
||||
QlTest = value;
|
||||
return true;
|
||||
case "brotli":
|
||||
TrapCompression = value ? TrapWriter.CompressionMode.Brotli : TrapWriter.CompressionMode.Gzip;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Semmle.Util
|
||||
@@ -39,8 +40,26 @@ namespace Semmle.Util
|
||||
|
||||
public static class OptionsExtensions
|
||||
{
|
||||
public static void ParseArguments(this ICommandLineOptions options, IReadOnlyList<string> arguments)
|
||||
private static string? GetExtractorOption(string name) =>
|
||||
Environment.GetEnvironmentVariable($"CODEQL_EXTRACTOR_CSHARP_OPTION_{name.ToUpper()}");
|
||||
|
||||
private static List<string> GetExtractorOptions()
|
||||
{
|
||||
var extractorOptions = new List<string>();
|
||||
|
||||
var trapCompression = GetExtractorOption("trap_compression");
|
||||
if (!string.IsNullOrEmpty(trapCompression))
|
||||
{
|
||||
extractorOptions.Add($"--trap_compression:{trapCompression}");
|
||||
}
|
||||
|
||||
return extractorOptions;
|
||||
}
|
||||
|
||||
public static void ParseArguments(this ICommandLineOptions options, IReadOnlyList<string> providedArguments)
|
||||
{
|
||||
var arguments = GetExtractorOptions();
|
||||
arguments.AddRange(providedArguments);
|
||||
for (var i = 0; i < arguments.Count; ++i)
|
||||
{
|
||||
var arg = arguments[i];
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
## 1.0.4
|
||||
|
||||
## 1.0.3
|
||||
|
||||
## 1.0.2
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
## 1.0.4
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.0.3
|
||||
lastReleaseVersion: 1.0.4
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.0.4-dev
|
||||
version: 1.0.5-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
## 1.0.4
|
||||
|
||||
## 1.0.3
|
||||
|
||||
## 1.0.2
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
## 1.0.4
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.0.3
|
||||
lastReleaseVersion: 1.0.4
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.0.4-dev
|
||||
version: 1.0.5-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
## 0.0.10
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
1
csharp/ql/lib/change-notes/released/0.0.10.md
Normal file
1
csharp/ql/lib/change-notes/released/0.0.10.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.0.10
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.9
|
||||
lastReleaseVersion: 0.0.10
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 0.0.10-dev
|
||||
version: 0.0.11-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
## 0.0.10
|
||||
|
||||
### Query Metadata Changes
|
||||
|
||||
* The precision of hardcoded credentials queries (`cs/hardcoded-credentials` and
|
||||
`cs/hardcoded-connection-string-credentials`) have been downgraded to medium.
|
||||
|
||||
## 0.0.9
|
||||
|
||||
## 0.0.8
|
||||
|
||||
@@ -204,6 +204,8 @@ class ExplicitUpcast extends ExplicitCast {
|
||||
this = any(LocalVariableDeclAndInitExpr decl | decl.isImplicitlyTyped()).getInitializer()
|
||||
or
|
||||
exists(LambdaExpr c | c.canReturn(this))
|
||||
or
|
||||
dest instanceof DynamicType
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: queryMetadata
|
||||
---
|
||||
The precision of hardcoded credentials queries (`cs/hardcoded-credentials` and
|
||||
`cs/hardcoded-connection-string-credentials`) have been downgraded to medium.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The C# extractor now accepts an extractor option `trap.compression`, which is used to decide the compression format for TRAP files. The legal values are `brotli` (default), `gzip` or `none`.
|
||||
The option is added via `codeql database create --language=csharp -Otrap.compression=value ...`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The C# extractor now accepts an extractor option `buildless`, which is used to decide what type of extraction that should be performed. If `true` then buildless (standalone) extraction will be performed. Otherwise tracing extraction will be performed (default).
|
||||
The option is added via `codeql database create --language=csharp -Obuildless=true ...`.
|
||||
4
csharp/ql/src/change-notes/2022-02-28-useless-upcast.md
Normal file
4
csharp/ql/src/change-notes/2022-02-28-useless-upcast.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Casts to `dynamic` are excluded from the useless upcasts check (`cs/useless-upcast`).
|
||||
6
csharp/ql/src/change-notes/released/0.0.10.md
Normal file
6
csharp/ql/src/change-notes/released/0.0.10.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## 0.0.10
|
||||
|
||||
### Query Metadata Changes
|
||||
|
||||
* The precision of hardcoded credentials queries (`cs/hardcoded-credentials` and
|
||||
`cs/hardcoded-connection-string-credentials`) have been downgraded to medium.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.9
|
||||
lastReleaseVersion: 0.0.10
|
||||
|
||||
@@ -106,6 +106,12 @@ private predicate filteredNumberableInstruction(Instruction instr) {
|
||||
or
|
||||
instr instanceof FieldAddressInstruction and
|
||||
count(instr.(FieldAddressInstruction).getField()) != 1
|
||||
or
|
||||
instr instanceof InheritanceConversionInstruction and
|
||||
(
|
||||
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
|
||||
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
|
||||
)
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(
|
||||
@@ -115,8 +121,7 @@ private predicate variableAddressValueNumber(
|
||||
// The underlying AST element is used as value-numbering key instead of the
|
||||
// `IRVariable` to work around a problem where a variable or expression with
|
||||
// multiple types gives rise to multiple `IRVariable`s.
|
||||
instr.getIRVariable().getAST() = ast and
|
||||
strictcount(instr.getIRVariable().getAST()) = 1
|
||||
unique( | | instr.getIRVariable().getAST()) = ast
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
@@ -133,8 +138,7 @@ private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
strictcount(instr.getResultIRType()) = 1 and
|
||||
instr.getResultIRType() = type and
|
||||
unique( | | instr.getResultIRType()) = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
@@ -151,8 +155,7 @@ private predicate fieldAddressValueNumber(
|
||||
TValueNumber objectAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
strictcount(instr.getField()) = 1 and
|
||||
unique( | | instr.getField()) = field and
|
||||
tvalueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
@@ -195,9 +198,9 @@ private predicate inheritanceConversionValueNumber(
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getBaseClass() = baseClass and
|
||||
instr.getDerivedClass() = derivedClass and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
tvalueNumber(instr.getUnary()) = operand and
|
||||
unique( | | instr.getBaseClass()) = baseClass and
|
||||
unique( | | instr.getDerivedClass()) = derivedClass
|
||||
}
|
||||
|
||||
private predicate loadTotalOverlapValueNumber(
|
||||
|
||||
@@ -106,6 +106,12 @@ private predicate filteredNumberableInstruction(Instruction instr) {
|
||||
or
|
||||
instr instanceof FieldAddressInstruction and
|
||||
count(instr.(FieldAddressInstruction).getField()) != 1
|
||||
or
|
||||
instr instanceof InheritanceConversionInstruction and
|
||||
(
|
||||
count(instr.(InheritanceConversionInstruction).getBaseClass()) != 1 or
|
||||
count(instr.(InheritanceConversionInstruction).getDerivedClass()) != 1
|
||||
)
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(
|
||||
@@ -115,8 +121,7 @@ private predicate variableAddressValueNumber(
|
||||
// The underlying AST element is used as value-numbering key instead of the
|
||||
// `IRVariable` to work around a problem where a variable or expression with
|
||||
// multiple types gives rise to multiple `IRVariable`s.
|
||||
instr.getIRVariable().getAST() = ast and
|
||||
strictcount(instr.getIRVariable().getAST()) = 1
|
||||
unique( | | instr.getIRVariable().getAST()) = ast
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
@@ -133,8 +138,7 @@ private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
strictcount(instr.getResultIRType()) = 1 and
|
||||
instr.getResultIRType() = type and
|
||||
unique( | | instr.getResultIRType()) = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
|
||||
@@ -151,8 +155,7 @@ private predicate fieldAddressValueNumber(
|
||||
TValueNumber objectAddress
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
strictcount(instr.getField()) = 1 and
|
||||
unique( | | instr.getField()) = field and
|
||||
tvalueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
@@ -195,9 +198,9 @@ private predicate inheritanceConversionValueNumber(
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getBaseClass() = baseClass and
|
||||
instr.getDerivedClass() = derivedClass and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
tvalueNumber(instr.getUnary()) = operand and
|
||||
unique( | | instr.getBaseClass()) = baseClass and
|
||||
unique( | | instr.getDerivedClass()) = derivedClass
|
||||
}
|
||||
|
||||
private predicate loadTotalOverlapValueNumber(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 0.0.10-dev
|
||||
version: 0.0.11-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -17,8 +17,8 @@ class A : I1, I2
|
||||
|
||||
class B : A
|
||||
{
|
||||
public static bool operator==(B b1, B b2) { return false; }
|
||||
public static bool operator!=(B b1, B b2) { return true; }
|
||||
public static bool operator ==(B b1, B b2) { return false; }
|
||||
public static bool operator !=(B b1, B b2) { return true; }
|
||||
public void M(B b) { }
|
||||
}
|
||||
|
||||
@@ -68,11 +68,11 @@ class Tests
|
||||
|
||||
((I2)a).Foo(); // GOOD: Cast to an interface
|
||||
|
||||
o = a==(A)b; // GOOD: EQExpr
|
||||
o = a == (A)b; // GOOD: EQExpr
|
||||
|
||||
o = b==(B)b; // GOOD: Operator call
|
||||
o = b == (B)b; // GOOD: Operator call
|
||||
|
||||
var act = (Action) (() => { }); // GOOD
|
||||
var act = (Action)(() => { }); // GOOD
|
||||
|
||||
var objects = args.Select(arg => (object)arg); // GOOD
|
||||
|
||||
@@ -126,9 +126,9 @@ static class IExtensions
|
||||
|
||||
static class StaticMethods
|
||||
{
|
||||
public static void M1(A _) { }
|
||||
public static void M1(B _) { }
|
||||
public static void M2(B _) { }
|
||||
public static void M1(A _) { }
|
||||
public static void M1(B _) { }
|
||||
public static void M2(B _) { }
|
||||
}
|
||||
|
||||
class Constructors : I2
|
||||
@@ -162,4 +162,12 @@ class Constructors : I2
|
||||
new Sub((Sub)ss); // BAD
|
||||
}
|
||||
}
|
||||
|
||||
class Dynamic
|
||||
{
|
||||
void M(object o)
|
||||
{
|
||||
var s0 = ((dynamic)o).ToString(); // GOOD
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_"
|
||||
Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10",Not applicable,``.py``
|
||||
Ruby [7]_,"up to 3.0.2",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``"
|
||||
TypeScript [8]_,"2.6-4.5",Standard TypeScript compiler,"``.ts``, ``.tsx``"
|
||||
TypeScript [8]_,"2.6-4.6",Standard TypeScript compiler,"``.ts``, ``.tsx``"
|
||||
|
||||
.. container:: footnote-group
|
||||
|
||||
|
||||
@@ -78,6 +78,8 @@ To avoid these problems, a data-flow ``Configuration`` comes with a mechanism fo
|
||||
*/
|
||||
final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) {
|
||||
|
||||
There is also a ``Configuration.hasPartialFlowRev`` for exploring flow backwards from a sink.
|
||||
|
||||
As noted in the documentation for ``hasPartialFlow`` (for example, in the
|
||||
`CodeQL for Java documentation <https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/dataflow/internal/DataFlowImpl2.qll/predicate.DataFlowImpl2$Configuration$hasPartialFlow.3.html>`__) you must first enable this by adding an override of ``explorationLimit``. For example:
|
||||
|
||||
@@ -87,6 +89,8 @@ As noted in the documentation for ``hasPartialFlow`` (for example, in the
|
||||
|
||||
This defines the exploration radius within which ``hasPartialFlow`` returns results.
|
||||
|
||||
To get good performance when using ``hasPartialFlow`` it is important to ensure the ``isSink`` predicate of the configuration has no results. Likewise, when using ``hasPartialFlowRev`` the ``isSource`` predicate of the configuration should have no results.
|
||||
|
||||
It is also useful to focus on a single source at a time as the starting point for the flow exploration. This is most easily done by adding a temporary restriction in the ``isSource`` predicate.
|
||||
|
||||
To do quick evaluations of partial flow it is often easiest to add a predicate to the query that is solely intended for quick evaluation (right-click the predicate name and choose "CodeQL: Quick Evaluation"). A good starting point is something like:
|
||||
@@ -113,4 +117,4 @@ Further reading
|
||||
----------------
|
||||
|
||||
- :ref:`About data flow analysis <about-data-flow-analysis>`
|
||||
- :ref:`Creating path queries <creating-path-queries>`
|
||||
- :ref:`Creating path queries <creating-path-queries>`
|
||||
|
||||
@@ -1,6 +1,44 @@
|
||||
# CodeQL pre-commit-hook setup
|
||||
|
||||
As stated in [CONTRIBUTING](../CONTRIBUTING.md) all CodeQL files must be formatted according to our [CodeQL style guide](ql-style-guide.md). You can use our pre-commit hook to avoid committing incorrectly formatted code. To use it, simply copy the [pre-commit](../misc/scripts/pre-commit) script to `.git/hooks/pre-commit` and make sure that:
|
||||
|
||||
As stated in [CONTRIBUTING](../CONTRIBUTING.md) all CodeQL files must be formatted according to our [CodeQL style guide](ql-style-guide.md). You can use a pre-commit hook to avoid committing incorrectly formatted code, as well as prevent some other easily checkable errors.
|
||||
|
||||
## Using the `pre-commit` framework
|
||||
|
||||
Preferably, you can use the [pre-commit framework](https://pre-commit.com/). There are some pre-commit hooks already configured on [`.pre-commit-config.yaml`](../.pre-commit-config.yaml). In order to install them you need to follow pre-commit's [installation instructions](https://pre-commit.com/#installation) and then run `pre-commit install`. Typically (assuming you have [`pip`](https://pip.pypa.io/en/stable/installation/) installed):
|
||||
```
|
||||
python3 -m pip install pre-commit
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
Also, make sure that the CodeQL CLI has been added to your `PATH`.
|
||||
|
||||
By default, pre-commit will check and fix:
|
||||
* trailing whitespaces;
|
||||
* absence of or duplicate newlines at end of files;
|
||||
* QL formatting;
|
||||
* files out of sync (see [`config/sync-files.py`](../config/sync-files.py)).
|
||||
|
||||
It will additionally check:
|
||||
* `.qhelp` files for query help generation.
|
||||
|
||||
It will run the checks only on files changed by the commit (except for the file sync check) and it will skip all files under `test` directories unless they are `.ql`, `.qll` or `.qlref` files.
|
||||
|
||||
If you want to change any behaviour (for example, you want to skip the out-of-sync file check, or you want to avoid auto-fixing formatting or file syncing), you can copy the configuration file to a separate location, modify it and use that. For example
|
||||
```
|
||||
cp .pre-commit-config.yaml ~/my-codeql-pre-commit-config.yaml
|
||||
pre-commit install --config ~/my-codeql-pre-commit-config.yaml
|
||||
# edit ~/my-codeql-pre-commit-config.yaml to your liking
|
||||
```
|
||||
|
||||
You can for example:
|
||||
* change `--in-place` to `--check-only` in the `codeql-format` hook to have it report formatting problems instead of auto-fixing them;
|
||||
* remove `--latest` in the `sync-files` hook to do the same;
|
||||
* remove any hook altogether.
|
||||
|
||||
## Manual approach
|
||||
|
||||
You can have the formatting check in place by copying the [pre-commit](../misc/scripts/pre-commit) script to `.git/hooks/pre-commit` and make sure that:
|
||||
|
||||
- The script is executable. On Linux and macOS this can be done using `chmod +x`.
|
||||
- The CodeQL CLI has been added to your `PATH`.
|
||||
|
||||
@@ -13,5 +13,5 @@
|
||||
import java
|
||||
|
||||
from RefType type
|
||||
where type.getASupertype+().hasQualifiedName("com.example", "Class")
|
||||
where type.getAStrictAncestor().hasQualifiedName("com.example", "Class")
|
||||
select type
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
import java
|
||||
|
||||
from ThrowStmt throw
|
||||
where throw.getThrownExceptionType().getASupertype*().hasQualifiedName("com.example", "AnException")
|
||||
where throw.getThrownExceptionType().getAnAncestor().hasQualifiedName("com.example", "AnException")
|
||||
select throw, "Don't throw com.example.AnException"
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.0.10
|
||||
|
||||
### New Features
|
||||
|
||||
* Added predicates `ClassOrInterface.getAPermittedSubtype` and `isSealed` exposing information about sealed classes.
|
||||
|
||||
## 0.0.9
|
||||
|
||||
## 0.0.8
|
||||
|
||||
6
java/ql/lib/change-notes/2022-02-21-type-hierarchy.md
Normal file
6
java/ql/lib/change-notes/2022-02-21-type-hierarchy.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Added `hasDescendant(RefType anc, Type sub)`
|
||||
* Added `RefType.getADescendant()`
|
||||
* Added `RefType.getAStrictAncestor()`
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
## 0.0.10
|
||||
|
||||
### New Features
|
||||
|
||||
* Added predicates `ClassOrInterface.getAPermittedSubtype` and `isSealed` exposing information about sealed classes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.9
|
||||
lastReleaseVersion: 0.0.10
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-all
|
||||
version: 0.0.10-dev
|
||||
version: 0.0.11-dev
|
||||
groups: java
|
||||
dbscheme: config/semmlecode.dbscheme
|
||||
extractor: java
|
||||
|
||||
@@ -236,7 +236,7 @@ private module ControlFlowGraphImpl {
|
||||
*/
|
||||
private predicate mustCatch(CatchClause c, ThrowableType thrown) {
|
||||
thrown = thrownInBody(c.getTry()) and
|
||||
hasSubtype*(c.getACaughtType(), thrown)
|
||||
hasDescendant(c.getACaughtType(), thrown)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,7 +250,7 @@ private module ControlFlowGraphImpl {
|
||||
*/
|
||||
private predicate mayNotCatch(CatchClause c, ThrowableType thrown) {
|
||||
thrown = thrownInBody(c.getTry()) and
|
||||
not hasSubtype*(c.getACaughtType(), thrown)
|
||||
not hasDescendant(c.getACaughtType(), thrown)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2093,7 +2093,7 @@ class Argument extends Expr {
|
||||
p.isVarargs() and
|
||||
ptyp = p.getType() and
|
||||
(
|
||||
hasSubtype*(ptyp, typ)
|
||||
hasDescendant(ptyp, typ)
|
||||
or
|
||||
// If the types don't match then we'll guess based on whether there are type variables involved.
|
||||
hasInstantiation(ptyp.(Array).getComponentType())
|
||||
|
||||
@@ -18,7 +18,7 @@ class AnnotatedGeneratedClass extends GeneratedClass {
|
||||
/** A Java class generated by an ANTLR scanner or parser class. */
|
||||
class AntlrGenerated extends GeneratedClass {
|
||||
AntlrGenerated() {
|
||||
exists(RefType t | this.getASupertype+() = t |
|
||||
exists(RefType t | this.getAStrictAncestor() = t |
|
||||
// ANTLR v3
|
||||
t.hasQualifiedName("org.antlr.runtime", "Lexer") or
|
||||
t.hasQualifiedName("org.antlr.runtime", "Parser") or
|
||||
|
||||
@@ -114,7 +114,7 @@ class TypeNumber extends RefType {
|
||||
|
||||
/** A (reflexive, transitive) subtype of `java.lang.Number`. */
|
||||
class NumberType extends RefType {
|
||||
NumberType() { exists(TypeNumber number | hasSubtype*(number, this)) }
|
||||
NumberType() { exists(TypeNumber number | hasDescendant(number, this)) }
|
||||
}
|
||||
|
||||
/** A numeric type, including both primitive and boxed types. */
|
||||
@@ -436,13 +436,13 @@ class ArrayLengthField extends Field {
|
||||
|
||||
/** A (reflexive, transitive) subtype of `java.lang.Throwable`. */
|
||||
class ThrowableType extends RefType {
|
||||
ThrowableType() { exists(TypeThrowable throwable | hasSubtype*(throwable, this)) }
|
||||
ThrowableType() { exists(TypeThrowable throwable | hasDescendant(throwable, this)) }
|
||||
}
|
||||
|
||||
/** An unchecked exception. That is, a (reflexive, transitive) subtype of `java.lang.Error` or `java.lang.RuntimeException`. */
|
||||
class UncheckedThrowableType extends RefType {
|
||||
UncheckedThrowableType() {
|
||||
exists(TypeError e | hasSubtype*(e, this)) or
|
||||
exists(TypeRuntimeException e | hasSubtype*(e, this))
|
||||
exists(TypeError e | hasDescendant(e, this)) or
|
||||
exists(TypeRuntimeException e | hasDescendant(e, this))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ predicate catchesNFE(TryStmt t) {
|
||||
exists(CatchClause cc, LocalVariableDeclExpr v |
|
||||
t.getACatchClause() = cc and
|
||||
cc.getVariable() = v and
|
||||
v.getType().(RefType).getASubtype*() instanceof NumberFormatException
|
||||
v.getType().(RefType).getADescendant() instanceof NumberFormatException
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -295,7 +295,7 @@ class NewInstance extends MethodAccess {
|
||||
// If we cast the result of this method, then this is either the type specified, or a
|
||||
// sub-type of that type. Make sure we exclude overly generic types such as `Object`.
|
||||
not overlyGenericType(cast.getType()) and
|
||||
hasSubtype*(cast.getType(), result)
|
||||
hasDescendant(cast.getType(), result)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ abstract class DeserializableField extends Field { }
|
||||
*/
|
||||
library class StandardSerializableField extends SerializableField, DeserializableField {
|
||||
StandardSerializableField() {
|
||||
this.getDeclaringType().getASupertype*() instanceof TypeSerializable and
|
||||
this.getDeclaringType().getAnAncestor() instanceof TypeSerializable and
|
||||
not this.isTransient()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,16 @@ predicate hasSubtype(RefType t, Type sub) {
|
||||
typeVarSubtypeBound(t, sub) and t != sub
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if reference type `anc` is a direct or indirect supertype of `sub`, including itself.
|
||||
*/
|
||||
cached
|
||||
predicate hasDescendant(RefType anc, Type sub) {
|
||||
anc = sub
|
||||
or
|
||||
exists(RefType mid | hasSubtype(anc, mid) and hasDescendant(mid, sub))
|
||||
}
|
||||
|
||||
private predicate typeVarSubtypeBound(RefType t, TypeVariable tv) {
|
||||
if tv.hasTypeBound() then t = tv.getATypeBound().getType() else t instanceof TypeObject
|
||||
}
|
||||
@@ -394,11 +404,17 @@ class RefType extends Type, Annotatable, Modifiable, @reftype {
|
||||
/** Gets a direct subtype of this type. */
|
||||
RefType getASubtype() { hasSubtype(this, result) }
|
||||
|
||||
/** Gets a direct or indirect descendant of this type, including itself. */
|
||||
RefType getADescendant() { hasDescendant(this, result) }
|
||||
|
||||
/** Gets a direct supertype of this type. */
|
||||
RefType getASupertype() { hasSubtype(result, this) }
|
||||
|
||||
/** Gets a direct or indirect supertype of this type, including itself. */
|
||||
RefType getAnAncestor() { hasSubtype*(result, this) }
|
||||
RefType getAnAncestor() { hasDescendant(result, this) }
|
||||
|
||||
/** Gets a direct or indirect supertype of this type, not including itself. */
|
||||
RefType getAStrictAncestor() { result = this.getAnAncestor() and result != this }
|
||||
|
||||
/**
|
||||
* Gets the source declaration of a direct supertype of this type, excluding itself.
|
||||
|
||||
@@ -103,7 +103,7 @@ private class NumberTaintPreservingCallable extends TaintPreservingCallable {
|
||||
int argument;
|
||||
|
||||
NumberTaintPreservingCallable() {
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("java.lang", "Number") and
|
||||
this.getDeclaringType().getAnAncestor().hasQualifiedName("java.lang", "Number") and
|
||||
(
|
||||
this instanceof Constructor and
|
||||
argument = 0
|
||||
|
||||
@@ -641,7 +641,7 @@ private module SsaImpl {
|
||||
ssaDefReachesRank(v, def, b, lastRank(v, b))
|
||||
or
|
||||
exists(BasicBlock idom |
|
||||
bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
bbIDominates(pragma[only_bind_into](idom), b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
ssaDefReachesEndOfBlock(v, def, idom) and
|
||||
not any(TrackedSsaDef other).definesAt(v, b, _)
|
||||
)
|
||||
@@ -768,12 +768,12 @@ private module SsaImpl {
|
||||
*/
|
||||
private predicate varBlockReaches(TrackedVar v, BasicBlock b1, BasicBlock b2) {
|
||||
varOccursInBlock(v, b1) and
|
||||
b2 = b1.getABBSuccessor() and
|
||||
pragma[only_bind_into](b2) = b1.getABBSuccessor() and
|
||||
blockPrecedesVar(v, b2)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(v, b1, mid) and
|
||||
b2 = mid.getABBSuccessor() and
|
||||
pragma[only_bind_into](b2) = mid.getABBSuccessor() and
|
||||
not varOccursInBlock(v, mid) and
|
||||
blockPrecedesVar(v, b2)
|
||||
)
|
||||
|
||||
@@ -285,7 +285,7 @@ private predicate downcastSuccessorAux(
|
||||
*/
|
||||
private predicate downcastSuccessor(VarAccess va, RefType t) {
|
||||
exists(CastExpr cast, BaseSsaVariable v, RefType t1, RefType t2 |
|
||||
downcastSuccessorAux(cast, v, t, t1, t2) and
|
||||
downcastSuccessorAux(pragma[only_bind_into](cast), v, t, t1, t2) and
|
||||
t1.getASourceSupertype+() = t2 and
|
||||
va = v.getAUse() and
|
||||
dominates(cast, va) and
|
||||
@@ -360,7 +360,7 @@ private predicate typeFlowJoin(int r, TypeFlowNode n, RefType t) {
|
||||
) and
|
||||
forall(TypeFlowNode mid | joinStepRank(r, mid, n) |
|
||||
exists(RefType midtyp | exactType(mid, midtyp) or typeFlow(mid, midtyp) |
|
||||
midtyp.getASupertype*() = t
|
||||
pragma[only_bind_out](midtyp).getAnAncestor() = t
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -408,14 +408,14 @@ pragma[nomagic]
|
||||
private predicate irrelevantBound(TypeFlowNode n, RefType t) {
|
||||
exists(RefType bound |
|
||||
typeFlow(n, bound) and
|
||||
t = bound.getASupertype+() and
|
||||
t = bound.getAStrictAncestor() and
|
||||
typeBound(t) and
|
||||
typeFlow(n, t) and
|
||||
not t.getASupertype*() = bound
|
||||
not t.getAnAncestor() = bound
|
||||
or
|
||||
n.getType() = bound and
|
||||
n.getType() = pragma[only_bind_into](bound) and
|
||||
typeFlow(n, t) and
|
||||
t = bound.getASupertype*()
|
||||
t = bound.getAnAncestor()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ private module SsaImpl {
|
||||
ssaDefReachesRank(v, def, b, lastRank(v, b))
|
||||
or
|
||||
exists(BasicBlock idom |
|
||||
bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
bbIDominates(pragma[only_bind_into](idom), b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
|
||||
ssaDefReachesEndOfBlock(v, def, idom) and
|
||||
not any(TrackedSsaDef other).definesAt(v, b, _)
|
||||
)
|
||||
@@ -333,12 +333,12 @@ private module SsaImpl {
|
||||
*/
|
||||
private predicate varBlockReaches(BaseSsaSourceVariable v, BasicBlock b1, BasicBlock b2) {
|
||||
varOccursInBlock(v, b1) and
|
||||
b2 = b1.getABBSuccessor() and
|
||||
pragma[only_bind_into](b2) = b1.getABBSuccessor() and
|
||||
blockPrecedesVar(v, b2)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(v, b1, mid) and
|
||||
b2 = mid.getABBSuccessor() and
|
||||
pragma[only_bind_into](b2) = mid.getABBSuccessor() and
|
||||
not varOccursInBlock(v, mid) and
|
||||
blockPrecedesVar(v, b2)
|
||||
)
|
||||
|
||||
@@ -283,7 +283,7 @@ private predicate taintPreservingQualifierToMethod(Method m) {
|
||||
m.getName().matches("read%")
|
||||
or
|
||||
m instanceof GetterMethod and
|
||||
m.getDeclaringType().getASubtype*() instanceof SpringUntrustedDataType and
|
||||
m.getDeclaringType().getADescendant() instanceof SpringUntrustedDataType and
|
||||
not m.getDeclaringType() instanceof TypeObject
|
||||
or
|
||||
m.(TaintPreservingCallable).returnsTaintFrom(-1)
|
||||
@@ -607,7 +607,7 @@ private SrcRefType entrypointType() {
|
||||
s instanceof DataFlow::ExplicitParameterNode and
|
||||
t = pragma[only_bind_out](s).getType() and
|
||||
not t instanceof TypeObject and
|
||||
result = t.getASubtype*().getSourceDeclaration()
|
||||
result = t.getADescendant().getSourceDeclaration()
|
||||
)
|
||||
or
|
||||
result = entrypointType().getAField().getType().(RefType).getSourceDeclaration()
|
||||
|
||||
@@ -33,7 +33,7 @@ Callable possibleLivenessCause(Callable c, string reason) {
|
||||
or
|
||||
c.hasName("<clinit>") and
|
||||
reason = "class initialization" and
|
||||
exists(RefType clintedType | c = clintedType.getASupertype*().getACallable() |
|
||||
exists(RefType clintedType | c = clintedType.getAnAncestor().getACallable() |
|
||||
result.getDeclaringType() = clintedType or
|
||||
result.getAnAccessedField().getDeclaringType() = clintedType
|
||||
)
|
||||
@@ -155,7 +155,7 @@ library class SourceClassOrInterface extends ClassOrInterface {
|
||||
*/
|
||||
class LiveClass extends SourceClassOrInterface {
|
||||
LiveClass() {
|
||||
exists(Callable c | c.getDeclaringType().getASupertype*().getSourceDeclaration() = this |
|
||||
exists(Callable c | c.getDeclaringType().getAnAncestor().getSourceDeclaration() = this |
|
||||
isLive(c)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -93,7 +93,7 @@ class SerialVersionUIDField extends ReflectivelyReadField {
|
||||
this.isStatic() and
|
||||
this.isFinal() and
|
||||
this.getType().hasName("long") and
|
||||
this.getDeclaringType().getASupertype*() instanceof TypeSerializable
|
||||
this.getDeclaringType().getAnAncestor() instanceof TypeSerializable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ class DeserializedClass extends ReflectivelyConstructedClass {
|
||||
exists(CastExpr cast, ReadObjectMethod readObject |
|
||||
cast.getExpr().(MethodAccess).getMethod() = readObject
|
||||
|
|
||||
hasSubtype*(cast.getType(), this)
|
||||
hasDescendant(cast.getType(), this)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -315,7 +315,7 @@ class FacesComponentReflectivelyConstructedClass extends ReflectivelyConstructed
|
||||
* Entry point for EJB home interfaces.
|
||||
*/
|
||||
class EJBHome extends Interface, EntryPoint {
|
||||
EJBHome() { this.getASupertype*().hasQualifiedName("javax.ejb", "EJBHome") }
|
||||
EJBHome() { this.getAnAncestor().hasQualifiedName("javax.ejb", "EJBHome") }
|
||||
|
||||
override Callable getALiveCallable() { result = this.getACallable() }
|
||||
}
|
||||
@@ -324,7 +324,7 @@ class EJBHome extends Interface, EntryPoint {
|
||||
* Entry point for EJB object interfaces.
|
||||
*/
|
||||
class EJBObject extends Interface, EntryPoint {
|
||||
EJBObject() { this.getASupertype*().hasQualifiedName("javax.ejb", "EJBObject") }
|
||||
EJBObject() { this.getAnAncestor().hasQualifiedName("javax.ejb", "EJBObject") }
|
||||
|
||||
override Callable getALiveCallable() { result = this.getACallable() }
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import semmle.code.java.frameworks.struts.StrutsActions
|
||||
*/
|
||||
class Struts1ActionEntryPoint extends EntryPoint, Class {
|
||||
Struts1ActionEntryPoint() {
|
||||
this.getASupertype*().hasQualifiedName("org.apache.struts.action", "Action")
|
||||
this.getAnAncestor().hasQualifiedName("org.apache.struts.action", "Action")
|
||||
}
|
||||
|
||||
override Callable getALiveCallable() {
|
||||
@@ -22,7 +22,7 @@ class Struts1ActionEntryPoint extends EntryPoint, Class {
|
||||
result.(Method).overrides+(methodFromAction)
|
||||
)
|
||||
or
|
||||
this.getASupertype*().hasQualifiedName("org.apache.struts.actions", "DispatchAction") and
|
||||
this.getAnAncestor().hasQualifiedName("org.apache.struts.actions", "DispatchAction") and
|
||||
result.(Method).isPublic()
|
||||
or
|
||||
result.(Constructor).getNumberOfParameters() = 0
|
||||
|
||||
@@ -47,7 +47,7 @@ class ServletListenerClass extends ReflectivelyConstructedClass {
|
||||
*/
|
||||
class ServletFilterClass extends ReflectivelyConstructedClass {
|
||||
ServletFilterClass() {
|
||||
this.getASupertype*().hasQualifiedName("javax.servlet", "Filter") and
|
||||
this.getAnAncestor().hasQualifiedName("javax.servlet", "Filter") and
|
||||
// If we have seen any `web.xml` files, this filter will be considered to be live only if it is
|
||||
// referred to as a filter-class in at least one. If no `web.xml` files are found, we assume
|
||||
// that XML extraction was not enabled, and therefore consider all filter classes as live.
|
||||
|
||||
@@ -335,7 +335,7 @@ import Dispatch
|
||||
|
||||
private Expr variableTrackStep(Expr use) {
|
||||
exists(Variable v |
|
||||
use = v.getAnAccess() and
|
||||
pragma[only_bind_out](use) = v.getAnAccess() and
|
||||
use.getType() instanceof RefType and
|
||||
not result instanceof NullLiteral and
|
||||
not v.(LocalVariableDecl).getDeclExpr().hasImplicitInit()
|
||||
@@ -358,6 +358,7 @@ private Expr variableTrackPath(Expr use) {
|
||||
/**
|
||||
* Gets an expression by tracking `use` backwards through variable assignments.
|
||||
*/
|
||||
pragma[inline]
|
||||
Expr variableTrack(Expr use) {
|
||||
result = variableTrackPath(use)
|
||||
or
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user