mirror of
https://github.com/github/codeql.git
synced 2026-05-02 04:05:14 +02:00
Merge remote-tracking branch 'upstream/master' into CVE74
This commit is contained in:
@@ -2,5 +2,4 @@
|
||||
"*/ql/test/qlpack.yml",
|
||||
"*/upgrades/qlpack.yml",
|
||||
"misc/legacy-support/*/qlpack.yml",
|
||||
"misc/suite-helpers/qlpack.yml",
|
||||
"codeql/.codeqlmanifest.json" ] }
|
||||
"misc/suite-helpers/qlpack.yml" ] }
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
# editor and OS artifacts
|
||||
*~
|
||||
.DS_STORE
|
||||
*.swp
|
||||
|
||||
# query compilation caches
|
||||
.cache
|
||||
|
||||
@@ -9,22 +9,26 @@
|
||||
|
||||
* Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
|
||||
|
||||
* Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
|
||||
|
||||
* The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [react](https://www.npmjs.com/package/react)
|
||||
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
|
||||
- [Handlebars](https://www.npmjs.com/package/handlebars)
|
||||
- [Electron](https://electronjs.org/)
|
||||
- [Handlebars](https://www.npmjs.com/package/handlebars)
|
||||
- [Koa](https://www.npmjs.com/package/koa)
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [Socket.IO](https://socket.io/)
|
||||
- [ws](https://github.com/websockets/ws)
|
||||
- [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
|
||||
- [Koa](https://www.npmjs.com/package/koa)
|
||||
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
|
||||
- [chrome-remote-interface](https://www.npmjs.com/package/chrome-remote-interface)
|
||||
- [for-in](https://www.npmjs.com/package/for-in)
|
||||
- [for-own](https://www.npmjs.com/package/for-own)
|
||||
- [http2](https://nodejs.org/api/http2.html)
|
||||
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
|
||||
- [react](https://www.npmjs.com/package/react)
|
||||
- [send](https://www.npmjs.com/package/send)
|
||||
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
|
||||
- [ws](https://github.com/websockets/ws)
|
||||
|
||||
## New queries
|
||||
|
||||
@@ -47,6 +51,7 @@
|
||||
| Use of call stack introspection in strict mode (`js/strict-mode-call-stack-introspection`) | Fewer false positive results | The query no longer flags expression statements. |
|
||||
| Missing CSRF middleware (`js/missing-token-validation`) | Fewer false positive results | The query reports fewer duplicates and only flags handlers that explicitly access cookie data. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional ways dangerous paths can be constructed. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
|
||||
// that the cached IR gets the same checksum here as it does in queries that use
|
||||
// `ValueNumbering` without `DataFlow`.
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
|
||||
private newtype TIRDataFlowNode =
|
||||
|
||||
@@ -19,19 +19,18 @@ newtype TValueNumber =
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand
|
||||
) {
|
||||
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
|
||||
binaryValueNumber(_, irFunc, opcode, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
|
||||
IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
) {
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, type, operand)
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, TValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, TValueNumber operand
|
||||
@@ -99,6 +98,19 @@ private predicate numberableInstruction(Instruction instr) {
|
||||
instr instanceof LoadTotalOverlapInstruction
|
||||
}
|
||||
|
||||
private predicate filteredNumberableInstruction(Instruction instr) {
|
||||
// count rather than strictcount to handle missing AST elements
|
||||
// separate instanceof and inline casts to avoid failed casts with a count of 0
|
||||
instr instanceof VariableAddressInstruction and
|
||||
count(instr.(VariableAddressInstruction).getIRVariable().getAST()) != 1
|
||||
or
|
||||
instr instanceof ConstantInstruction and
|
||||
count(instr.getResultIRType()) != 1
|
||||
or
|
||||
instr instanceof FieldAddressInstruction and
|
||||
count(instr.(FieldAddressInstruction).getField()) != 1
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(
|
||||
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
|
||||
) {
|
||||
@@ -106,7 +118,8 @@ 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
|
||||
instr.getIRVariable().getAST() = ast and
|
||||
strictcount(instr.getIRVariable().getAST()) = 1
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
@@ -123,10 +136,11 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
|
||||
instr.getEnclosingIRFunction() = irFunc
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(
|
||||
predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
strictcount(instr.getResultIRType()) = 1 and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
@@ -145,42 +159,40 @@ private predicate fieldAddressValueNumber(
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
strictcount(instr.getField()) = 1 and
|
||||
tvalueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof PointerArithmeticInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultIRType() = type and
|
||||
tvalueNumber(instr.getLeft()) = leftOperand and
|
||||
tvalueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
|
||||
int elementSize, TValueNumber leftOperand, TValueNumber rightOperand
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
|
||||
TValueNumber leftOperand, TValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
tvalueNumber(instr.getLeft()) = leftOperand and
|
||||
tvalueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
not instr instanceof FieldAddressInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultIRType() = type and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
|
||||
@@ -200,9 +212,9 @@ private predicate loadTotalOverlapValueNumber(
|
||||
TValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultIRType() = type and
|
||||
tvalueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
|
||||
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand
|
||||
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand and
|
||||
instr.getResultIRType() = type
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,7 +224,11 @@ private predicate loadTotalOverlapValueNumber(
|
||||
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr.getResultIRType() instanceof IRVoidType and
|
||||
not numberableInstruction(instr)
|
||||
(
|
||||
not numberableInstruction(instr)
|
||||
or
|
||||
filteredNumberableInstruction(instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +271,7 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
initializeThisValueNumber(instr, irFunc) and
|
||||
result = TInitializeThisValueNumber(irFunc)
|
||||
or
|
||||
exists(IRType type, string value |
|
||||
exists(string value, IRType type |
|
||||
constantValueNumber(instr, irFunc, type, value) and
|
||||
result = TConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
@@ -270,14 +286,14 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, IRType type, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
|
||||
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, IRType type, TValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, type, operand)
|
||||
exists(Opcode opcode, TValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
@@ -287,14 +303,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
|
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
|
||||
result =
|
||||
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
|
||||
|
||||
@@ -96,7 +96,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getMemoryOperandDefinition(
|
||||
private Instruction getMemoryOperandDefinition0(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
exists(OldInstruction oldInstruction, OldIR::NonPhiMemoryOperand oldOperand |
|
||||
@@ -142,6 +142,19 @@ private module Cached {
|
||||
overlap instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
// getMemoryOperandDefinition0 currently has a bug where it can match with multiple overlaps.
|
||||
// This predicate ensures that the chosen overlap is the most conservative if there's any doubt.
|
||||
result = getMemoryOperandDefinition0(instruction, tag, overlap) and
|
||||
not (
|
||||
overlap instanceof MustExactlyOverlap and
|
||||
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
|
||||
@@ -19,19 +19,18 @@ newtype TValueNumber =
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand
|
||||
) {
|
||||
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
|
||||
binaryValueNumber(_, irFunc, opcode, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
|
||||
IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
) {
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, type, operand)
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, TValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, TValueNumber operand
|
||||
@@ -99,6 +98,19 @@ private predicate numberableInstruction(Instruction instr) {
|
||||
instr instanceof LoadTotalOverlapInstruction
|
||||
}
|
||||
|
||||
private predicate filteredNumberableInstruction(Instruction instr) {
|
||||
// count rather than strictcount to handle missing AST elements
|
||||
// separate instanceof and inline casts to avoid failed casts with a count of 0
|
||||
instr instanceof VariableAddressInstruction and
|
||||
count(instr.(VariableAddressInstruction).getIRVariable().getAST()) != 1
|
||||
or
|
||||
instr instanceof ConstantInstruction and
|
||||
count(instr.getResultIRType()) != 1
|
||||
or
|
||||
instr instanceof FieldAddressInstruction and
|
||||
count(instr.(FieldAddressInstruction).getField()) != 1
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(
|
||||
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
|
||||
) {
|
||||
@@ -106,7 +118,8 @@ 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
|
||||
instr.getIRVariable().getAST() = ast and
|
||||
strictcount(instr.getIRVariable().getAST()) = 1
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
@@ -123,10 +136,11 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
|
||||
instr.getEnclosingIRFunction() = irFunc
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(
|
||||
predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
strictcount(instr.getResultIRType()) = 1 and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
@@ -145,42 +159,40 @@ private predicate fieldAddressValueNumber(
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
strictcount(instr.getField()) = 1 and
|
||||
tvalueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof PointerArithmeticInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultIRType() = type and
|
||||
tvalueNumber(instr.getLeft()) = leftOperand and
|
||||
tvalueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
|
||||
int elementSize, TValueNumber leftOperand, TValueNumber rightOperand
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
|
||||
TValueNumber leftOperand, TValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
tvalueNumber(instr.getLeft()) = leftOperand and
|
||||
tvalueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
not instr instanceof FieldAddressInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultIRType() = type and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
|
||||
@@ -200,9 +212,9 @@ private predicate loadTotalOverlapValueNumber(
|
||||
TValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultIRType() = type and
|
||||
tvalueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
|
||||
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand
|
||||
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand and
|
||||
instr.getResultIRType() = type
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,7 +224,11 @@ private predicate loadTotalOverlapValueNumber(
|
||||
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr.getResultIRType() instanceof IRVoidType and
|
||||
not numberableInstruction(instr)
|
||||
(
|
||||
not numberableInstruction(instr)
|
||||
or
|
||||
filteredNumberableInstruction(instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +271,7 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
initializeThisValueNumber(instr, irFunc) and
|
||||
result = TInitializeThisValueNumber(irFunc)
|
||||
or
|
||||
exists(IRType type, string value |
|
||||
exists(string value, IRType type |
|
||||
constantValueNumber(instr, irFunc, type, value) and
|
||||
result = TConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
@@ -270,14 +286,14 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, IRType type, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
|
||||
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, IRType type, TValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, type, operand)
|
||||
exists(Opcode opcode, TValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
@@ -287,14 +303,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
|
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
|
||||
result =
|
||||
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
|
||||
|
||||
@@ -19,19 +19,18 @@ newtype TValueNumber =
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand
|
||||
) {
|
||||
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
|
||||
binaryValueNumber(_, irFunc, opcode, leftOperand, rightOperand)
|
||||
} or
|
||||
TPointerArithmeticValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
|
||||
IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
) {
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
pointerArithmeticValueNumber(_, irFunc, opcode, elementSize, leftOperand, rightOperand)
|
||||
} or
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, type, operand)
|
||||
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, TValueNumber operand) {
|
||||
unaryValueNumber(_, irFunc, opcode, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, TValueNumber operand
|
||||
@@ -99,6 +98,19 @@ private predicate numberableInstruction(Instruction instr) {
|
||||
instr instanceof LoadTotalOverlapInstruction
|
||||
}
|
||||
|
||||
private predicate filteredNumberableInstruction(Instruction instr) {
|
||||
// count rather than strictcount to handle missing AST elements
|
||||
// separate instanceof and inline casts to avoid failed casts with a count of 0
|
||||
instr instanceof VariableAddressInstruction and
|
||||
count(instr.(VariableAddressInstruction).getIRVariable().getAST()) != 1
|
||||
or
|
||||
instr instanceof ConstantInstruction and
|
||||
count(instr.getResultIRType()) != 1
|
||||
or
|
||||
instr instanceof FieldAddressInstruction and
|
||||
count(instr.(FieldAddressInstruction).getField()) != 1
|
||||
}
|
||||
|
||||
private predicate variableAddressValueNumber(
|
||||
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
|
||||
) {
|
||||
@@ -106,7 +118,8 @@ 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
|
||||
instr.getIRVariable().getAST() = ast and
|
||||
strictcount(instr.getIRVariable().getAST()) = 1
|
||||
}
|
||||
|
||||
private predicate initializeParameterValueNumber(
|
||||
@@ -123,10 +136,11 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
|
||||
instr.getEnclosingIRFunction() = irFunc
|
||||
}
|
||||
|
||||
private predicate constantValueNumber(
|
||||
predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
strictcount(instr.getResultIRType()) = 1 and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getValue() = value
|
||||
}
|
||||
@@ -145,42 +159,40 @@ private predicate fieldAddressValueNumber(
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getField() = field and
|
||||
strictcount(instr.getField()) = 1 and
|
||||
tvalueNumber(instr.getObjectAddress()) = objectAddress
|
||||
}
|
||||
|
||||
private predicate binaryValueNumber(
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber leftOperand,
|
||||
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof PointerArithmeticInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultIRType() = type and
|
||||
tvalueNumber(instr.getLeft()) = leftOperand and
|
||||
tvalueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate pointerArithmeticValueNumber(
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
|
||||
int elementSize, TValueNumber leftOperand, TValueNumber rightOperand
|
||||
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
|
||||
TValueNumber leftOperand, TValueNumber rightOperand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultIRType() = type and
|
||||
instr.getElementSize() = elementSize and
|
||||
tvalueNumber(instr.getLeft()) = leftOperand and
|
||||
tvalueNumber(instr.getRight()) = rightOperand
|
||||
}
|
||||
|
||||
private predicate unaryValueNumber(
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, TValueNumber operand
|
||||
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr instanceof InheritanceConversionInstruction and
|
||||
not instr instanceof CopyInstruction and
|
||||
not instr instanceof FieldAddressInstruction and
|
||||
instr.getOpcode() = opcode and
|
||||
instr.getResultIRType() = type and
|
||||
tvalueNumber(instr.getUnary()) = operand
|
||||
}
|
||||
|
||||
@@ -200,9 +212,9 @@ private predicate loadTotalOverlapValueNumber(
|
||||
TValueNumber operand
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
instr.getResultIRType() = type and
|
||||
tvalueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
|
||||
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand
|
||||
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand and
|
||||
instr.getResultIRType() = type
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -212,7 +224,11 @@ private predicate loadTotalOverlapValueNumber(
|
||||
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
not instr.getResultIRType() instanceof IRVoidType and
|
||||
not numberableInstruction(instr)
|
||||
(
|
||||
not numberableInstruction(instr)
|
||||
or
|
||||
filteredNumberableInstruction(instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +271,7 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
initializeThisValueNumber(instr, irFunc) and
|
||||
result = TInitializeThisValueNumber(irFunc)
|
||||
or
|
||||
exists(IRType type, string value |
|
||||
exists(string value, IRType type |
|
||||
constantValueNumber(instr, irFunc, type, value) and
|
||||
result = TConstantValueNumber(irFunc, type, value)
|
||||
)
|
||||
@@ -270,14 +286,14 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, IRType type, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
|
||||
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
|
||||
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(Opcode opcode, IRType type, TValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, type, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, type, operand)
|
||||
exists(Opcode opcode, TValueNumber operand |
|
||||
unaryValueNumber(instr, irFunc, opcode, operand) and
|
||||
result = TUnaryValueNumber(irFunc, opcode, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
@@ -287,14 +303,10 @@ private TValueNumber nonUniqueValueNumber(Instruction instr) {
|
||||
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
Opcode opcode, IRType type, int elementSize, TValueNumber leftOperand,
|
||||
TValueNumber rightOperand
|
||||
|
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
|
||||
rightOperand) and
|
||||
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
|
||||
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
|
||||
result =
|
||||
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
|
||||
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
|
||||
)
|
||||
or
|
||||
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
|
||||
|
||||
@@ -96,7 +96,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getMemoryOperandDefinition(
|
||||
private Instruction getMemoryOperandDefinition0(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
exists(OldInstruction oldInstruction, OldIR::NonPhiMemoryOperand oldOperand |
|
||||
@@ -142,6 +142,19 @@ private module Cached {
|
||||
overlap instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
// getMemoryOperandDefinition0 currently has a bug where it can match with multiple overlaps.
|
||||
// This predicate ensures that the chosen overlap is the most conservative if there's any doubt.
|
||||
result = getMemoryOperandDefinition0(instruction, tag, overlap) and
|
||||
not (
|
||||
overlap instanceof MustExactlyOverlap and
|
||||
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
|
||||
@@ -97,6 +97,8 @@ class GVN extends TValueNumber {
|
||||
or
|
||||
this instanceof TInheritanceConversionValueNumber and result = "InheritanceConversion"
|
||||
or
|
||||
this instanceof TLoadTotalOverlapValueNumber and result = "LoadTotalOverlap"
|
||||
or
|
||||
this instanceof TUniqueValueNumber and result = "Unique"
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getMemoryOperandDefinition(
|
||||
private Instruction getMemoryOperandDefinition0(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
exists(OldInstruction oldInstruction, OldIR::NonPhiMemoryOperand oldOperand |
|
||||
@@ -142,6 +142,19 @@ private module Cached {
|
||||
overlap instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
// getMemoryOperandDefinition0 currently has a bug where it can match with multiple overlaps.
|
||||
// This predicate ensures that the chosen overlap is the most conservative if there's any doubt.
|
||||
result = getMemoryOperandDefinition0(instruction, tag, overlap) and
|
||||
not (
|
||||
overlap instanceof MustExactlyOverlap and
|
||||
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
|
||||
@@ -78,7 +78,7 @@ predicate importLookup(ASTNode path, Module target, string kind) {
|
||||
or
|
||||
exists(ReExportDeclaration red |
|
||||
path = red.getImportedPath() and
|
||||
target = red.getImportedModule()
|
||||
target = red.getReExportedModule()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -273,12 +273,12 @@ class BulkReExportDeclaration extends ReExportDeclaration, @exportalldeclaration
|
||||
override ConstantString getImportedPath() { result = getChildExpr(0) }
|
||||
|
||||
override predicate exportsAs(LexicalName v, string name) {
|
||||
getImportedModule().exportsAs(v, name) and
|
||||
getReExportedES2015Module().exportsAs(v, name) and
|
||||
not isShadowedFromBulkExport(this, name)
|
||||
}
|
||||
|
||||
override DataFlow::Node getSourceNode(string name) {
|
||||
result = getImportedModule().getAnExport().getSourceNode(name)
|
||||
result = getReExportedES2015Module().getAnExport().getSourceNode(name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @exportnameddeclaration
|
||||
exists(ExportSpecifier spec | spec = getASpecifier() and name = spec.getExportedName() |
|
||||
v = spec.getLocal().(LexicalAccess).getALexicalName()
|
||||
or
|
||||
this.(ReExportDeclaration).getImportedModule().exportsAs(v, spec.getLocalName())
|
||||
this.(ReExportDeclaration).getReExportedES2015Module().exportsAs(v, spec.getLocalName())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -393,7 +393,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @exportnameddeclaration
|
||||
not exists(getImportedPath()) and result = DataFlow::valueNode(spec.getLocal())
|
||||
or
|
||||
exists(ReExportDeclaration red | red = this |
|
||||
result = red.getImportedModule().getAnExport().getSourceNode(spec.getLocalName())
|
||||
result = red.getReExportedES2015Module().getAnExport().getSourceNode(spec.getLocalName())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -545,14 +545,18 @@ class ReExportDefaultSpecifier extends ExportDefaultSpecifier {
|
||||
}
|
||||
|
||||
/**
|
||||
* A namespace export specifier.
|
||||
* A namespace export specifier, that is `*` or `* as x` occuring in an export declaration.
|
||||
*
|
||||
* Example:
|
||||
* Examples:
|
||||
*
|
||||
* ```
|
||||
* export
|
||||
* * // namespace export specifier
|
||||
* from 'a';
|
||||
*
|
||||
* export
|
||||
* * as x // namespace export specifier
|
||||
* from 'a';
|
||||
* ```
|
||||
*/
|
||||
class ExportNamespaceSpecifier extends ExportSpecifier, @exportnamespacespecifier { }
|
||||
@@ -564,6 +568,7 @@ class ExportNamespaceSpecifier extends ExportSpecifier, @exportnamespacespecifie
|
||||
*
|
||||
* ```
|
||||
* export * from 'a'; // bulk re-export declaration
|
||||
* export * as x from 'a'; // namespace re-export declaration
|
||||
* export { x } from 'a'; // named re-export declaration
|
||||
* export x from 'a'; // default re-export declaration
|
||||
* ```
|
||||
@@ -572,8 +577,23 @@ abstract class ReExportDeclaration extends ExportDeclaration {
|
||||
/** Gets the path of the module from which this declaration re-exports. */
|
||||
abstract ConstantString getImportedPath();
|
||||
|
||||
/** Gets the module from which this declaration re-exports. */
|
||||
/**
|
||||
* DEPRECATED. Use `getReExportedES2015Module()` instead.
|
||||
*
|
||||
* Gets the module from which this declaration re-exports.
|
||||
*/
|
||||
deprecated
|
||||
ES2015Module getImportedModule() {
|
||||
result = getReExportedModule()
|
||||
}
|
||||
|
||||
/** Gets the module from which this declaration re-exports, if it is an ES2015 module. */
|
||||
ES2015Module getReExportedES2015Module() {
|
||||
result = getReExportedModule()
|
||||
}
|
||||
|
||||
/** Gets the module from which this declaration re-exports. */
|
||||
Module getReExportedModule() {
|
||||
result.getFile() = getEnclosingModule().resolve(getImportedPath().(PathExpr))
|
||||
or
|
||||
result = resolveFromTypeRoot()
|
||||
@@ -641,4 +661,4 @@ class OriginalExportDeclaration extends ExportDeclaration {
|
||||
result = this.(ExportDefaultDeclaration).getSourceNode(name) or
|
||||
result = this.(ExportNamedDeclaration).getSourceNode(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,69 +271,84 @@ module TaintTracking {
|
||||
ArrayFunctionTaintStep() { this = call }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
|
||||
// `elt` and `ary`; similar for `forEach`
|
||||
exists(string name, Function f, int i |
|
||||
(name = "map" or name = "forEach") and
|
||||
(i = 0 or i = 2) and
|
||||
call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = name and
|
||||
pred = call.getReceiver() and
|
||||
succ = DataFlow::parameterNode(f.getParameter(i))
|
||||
)
|
||||
or
|
||||
// `array.map` with tainted return value in callback
|
||||
exists(DataFlow::FunctionNode f |
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = "map" and
|
||||
call.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow
|
||||
pred = f.getAReturn() and
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name |
|
||||
name = "push" or
|
||||
name = "unshift"
|
||||
|
|
||||
pred = call.getAnArgument() and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
or
|
||||
// `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name |
|
||||
name = "push" or
|
||||
name = "unshift"
|
||||
|
|
||||
pred = call.getASpreadArgument() and
|
||||
// Make sure we handle reflective calls
|
||||
succ = call.getReceiver().getALocalSource() and
|
||||
call.getCalleeName() = name
|
||||
)
|
||||
or
|
||||
// `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name | name = "splice" |
|
||||
pred = call.getArgument(2) and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
or
|
||||
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
|
||||
exists(string name |
|
||||
name = "pop" or
|
||||
name = "shift" or
|
||||
name = "slice" or
|
||||
name = "splice"
|
||||
|
|
||||
call.(DataFlow::MethodCallNode).calls(pred, name) and
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
|
||||
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
|
||||
pred = call.getAnArgument() and
|
||||
succ = call
|
||||
arrayFunctionTaintStep(pred, succ, call)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge from `pred` to `succ` caused by a call `call` to a builtin array functions.
|
||||
*/
|
||||
predicate arrayFunctionTaintStep(DataFlow::Node pred, DataFlow::Node succ, DataFlow::CallNode call) {
|
||||
// `array.map(function (elt, i, ary) { ... })`: if `array` is tainted, then so are
|
||||
// `elt` and `ary`; similar for `forEach`
|
||||
exists(string name, Function f, int i |
|
||||
(name = "map" or name = "forEach") and
|
||||
(i = 0 or i = 2) and
|
||||
call.getArgument(0).analyze().getAValue().(AbstractFunction).getFunction() = f and
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = name and
|
||||
pred = call.getReceiver() and
|
||||
succ = DataFlow::parameterNode(f.getParameter(i))
|
||||
)
|
||||
or
|
||||
// `array.map` with tainted return value in callback
|
||||
exists(DataFlow::FunctionNode f |
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = "map" and
|
||||
call.getArgument(0) = f and // Require the argument to be a closure to avoid spurious call/return flow
|
||||
pred = f.getAReturn() and
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `array.push(e)`, `array.unshift(e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name |
|
||||
name = "push" or
|
||||
name = "unshift"
|
||||
|
|
||||
pred = call.getAnArgument() and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
or
|
||||
// `array.push(...e)`, `array.unshift(...e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name |
|
||||
name = "push" or
|
||||
name = "unshift"
|
||||
|
|
||||
pred = call.getASpreadArgument() and
|
||||
// Make sure we handle reflective calls
|
||||
succ = call.getReceiver().getALocalSource() and
|
||||
call.getCalleeName() = name
|
||||
)
|
||||
or
|
||||
// `array.splice(i, del, e)`: if `e` is tainted, then so is `array`.
|
||||
exists(string name | name = "splice" |
|
||||
pred = call.getArgument(2) and
|
||||
succ.(DataFlow::SourceNode).getAMethodCall(name) = call
|
||||
)
|
||||
or
|
||||
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
|
||||
exists(string name |
|
||||
name = "pop" or
|
||||
name = "shift" or
|
||||
name = "slice" or
|
||||
name = "splice"
|
||||
|
|
||||
call.(DataFlow::MethodCallNode).calls(pred, name) and
|
||||
succ = call
|
||||
)
|
||||
or
|
||||
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
|
||||
call = DataFlow::globalVarRef("Array").getAPropertyRead("from").getACall() and
|
||||
pred = call.getAnArgument() and
|
||||
succ = call
|
||||
or
|
||||
// `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
|
||||
call.(DataFlow::MethodCallNode).calls(pred, "concat") and
|
||||
succ = call
|
||||
or
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = "concat" and
|
||||
succ = call and
|
||||
pred = call.getAnArgument()
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge for assignments of the form `o[k] = v`, where
|
||||
* `k` is not a constant and `o` refers to some object literal; in this case, we consider
|
||||
|
||||
@@ -65,7 +65,7 @@ private predicate mayDynamicallyComputeExports(Module m) {
|
||||
or
|
||||
// `m` re-exports all exports of some other module that dynamically computes its exports
|
||||
exists(BulkReExportDeclaration rexp | rexp = m.(ES2015Module).getAnExport() |
|
||||
mayDynamicallyComputeExports(rexp.getImportedModule())
|
||||
mayDynamicallyComputeExports(rexp.getReExportedModule())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ private predicate relevantExport(ES2015Module m, string x) {
|
||||
)
|
||||
or
|
||||
exists(ReExportDeclaration rexp, string y |
|
||||
rexp.getImportedModule() = m and
|
||||
rexp.getReExportedModule() = m and
|
||||
reExportsAs(rexp, x, y)
|
||||
)
|
||||
}
|
||||
@@ -110,9 +110,9 @@ private predicate incompleteExport(ES2015Module m, string y) {
|
||||
mayDependOnLookupPath(rexp.getImportedPath().getStringValue())
|
||||
or
|
||||
// unresolvable path
|
||||
not exists(rexp.getImportedModule())
|
||||
not exists(rexp.getReExportedModule())
|
||||
or
|
||||
exists(Module n | n = rexp.getImportedModule() |
|
||||
exists(Module n | n = rexp.getReExportedModule() |
|
||||
// re-export from CommonJS/AMD
|
||||
mayDynamicallyComputeExports(n)
|
||||
or
|
||||
@@ -399,3 +399,16 @@ private class AnalyzedClosureGlobalAccessPath extends AnalyzedNode, AnalyzedProp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A namespace export declaration analyzed as a property write.
|
||||
*/
|
||||
private class AnalyzedExportNamespaceSpecifier extends AnalyzedPropertyWrite, DataFlow::ValueNode {
|
||||
override ExportNamespaceSpecifier astNode;
|
||||
|
||||
override predicate writesValue(AbstractValue baseVal, string propName, AbstractValue value) {
|
||||
baseVal = TAbstractExportsObject(getTopLevel()) and
|
||||
propName = astNode.getExportedName() and
|
||||
value = TAbstractExportsObject(astNode.getExportDeclaration().(ReExportDeclaration).getReExportedModule())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,4 +549,72 @@ module ClientRequest {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reference to an instance of `chrome-remote-interface`.
|
||||
*
|
||||
* An instantiation of `chrome-remote-interface` either accepts a callback or returns a promise.
|
||||
*
|
||||
* The `isPromise` parameter reflects whether the reference is a promise containing
|
||||
* an instance of `chrome-remote-interface`, or an instance of `chrome-remote-interface`.
|
||||
*/
|
||||
private DataFlow::SourceNode chromeRemoteInterface(DataFlow::TypeTracker t, boolean isPromise) {
|
||||
t.start() and
|
||||
exists(DataFlow::CallNode call |
|
||||
call = DataFlow::moduleImport("chrome-remote-interface").getAnInvocation()
|
||||
|
|
||||
result = call and isPromise = true
|
||||
or
|
||||
result = call.getCallback([0 .. 1]).getParameter(0) and isPromise = false
|
||||
)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = chromeRemoteInterface(t2, isPromise).track(t2, t))
|
||||
or
|
||||
// Simple promise tracking.
|
||||
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred |
|
||||
pred = chromeRemoteInterface(t2, true) and
|
||||
isPromise = false and
|
||||
(
|
||||
t2 = t and
|
||||
exists(AwaitExpr await | DataFlow::valueNode(await.getOperand()).getALocalSource() = pred |
|
||||
result.getEnclosingExpr() = await
|
||||
)
|
||||
or
|
||||
t2 = t and
|
||||
exists(DataFlow::MethodCallNode thenCall |
|
||||
thenCall.getMethodName() = "then" and pred = thenCall.getReceiver().getALocalSource()
|
||||
|
|
||||
result = thenCall.getCallback(0).getParameter(0)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to navigate a browser controlled by `chrome-remote-interface` to a specific URL.
|
||||
*/
|
||||
class ChromeRemoteInterfaceRequest extends ClientRequest::Range, DataFlow::CallNode {
|
||||
int optionsArg;
|
||||
|
||||
ChromeRemoteInterfaceRequest() {
|
||||
exists(DataFlow::SourceNode instance |
|
||||
instance = chromeRemoteInterface(DataFlow::TypeTracker::end(), false)
|
||||
|
|
||||
optionsArg = 0 and
|
||||
this = instance.getAPropertyRead("Page").getAMemberCall("navigate")
|
||||
or
|
||||
optionsArg = 1 and
|
||||
this = instance.getAMemberCall("send") and
|
||||
this.getArgument(0).mayHaveStringValue("Page.navigate")
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getUrl() {
|
||||
result = getArgument(optionsArg).getALocalSource().getAPropertyWrite("url").getRhs()
|
||||
}
|
||||
|
||||
override DataFlow::Node getHost() { none() }
|
||||
|
||||
override DataFlow::Node getADataNode() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +119,11 @@ module HTTP {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `http` or `https` directly as appropriate.
|
||||
*
|
||||
* Gets the string `http` or `https`.
|
||||
*/
|
||||
deprecated
|
||||
string httpOrHttps() { result = "http" or result = "https" }
|
||||
|
||||
/**
|
||||
|
||||
@@ -42,7 +42,18 @@ module NodeJSLib {
|
||||
* Holds if `call` is an invocation of `http.createServer` or `https.createServer`.
|
||||
*/
|
||||
predicate isCreateServer(CallExpr call) {
|
||||
call = DataFlow::moduleMember(HTTP::httpOrHttps(), "createServer").getAnInvocation().asExpr()
|
||||
exists(string pkg, string fn |
|
||||
pkg = "http" and fn = "createServer"
|
||||
or
|
||||
pkg = "https" and fn = "createServer"
|
||||
or
|
||||
// http2 compatibility API
|
||||
pkg = "http2" and fn = "createServer"
|
||||
or
|
||||
pkg = "http2" and fn = "createSecureServer"
|
||||
|
|
||||
call = DataFlow::moduleMember(pkg, fn).getAnInvocation().asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,10 +367,12 @@ module NodeJSLib {
|
||||
/** An expression that is passed as `http.request({ auth: <expr> }, ...)`. */
|
||||
class Credentials extends CredentialsExpr {
|
||||
Credentials() {
|
||||
this = DataFlow::moduleMember(HTTP::httpOrHttps(), "request")
|
||||
.getACall()
|
||||
.getOptionArgument(0, "auth")
|
||||
.asExpr()
|
||||
exists(string http | http = "http" or http = "https" |
|
||||
this = DataFlow::moduleMember(http, "request")
|
||||
.getACall()
|
||||
.getOptionArgument(0, "auth")
|
||||
.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override string getCredentialsKind() { result = "credentials" }
|
||||
@@ -881,7 +894,6 @@ module NodeJSLib {
|
||||
override string getSourceType() { result = "NodeJSClientRequest error event" }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An NodeJS EventEmitter instance.
|
||||
* Events dispatched on this EventEmitter will be handled by event handlers registered on this EventEmitter.
|
||||
|
||||
@@ -30,9 +30,14 @@ private DataFlow::Node commandArgument(SystemCommandExecution sys, DataFlow::Typ
|
||||
t.start() and
|
||||
result = sys.getACommandArgument()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 |
|
||||
t = t2.smallstep(result, commandArgument(sys, t2))
|
||||
)
|
||||
exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, commandArgument(sys, t2)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node whose value ends up being interpreted as the command argument in `sys`.
|
||||
*/
|
||||
private DataFlow::Node commandArgument(SystemCommandExecution sys) {
|
||||
result = commandArgument(sys, DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,11 +48,23 @@ private DataFlow::SourceNode argumentList(SystemCommandExecution sys, DataFlow::
|
||||
t.start() and
|
||||
result = sys.getArgumentList().getALocalSource()
|
||||
or
|
||||
exists(DataFlow::TypeBackTracker t2 |
|
||||
result = argumentList(sys, t2).backtrack(t2, t)
|
||||
exists(DataFlow::TypeBackTracker t2, DataFlow::SourceNode pred |
|
||||
pred = argumentList(sys, t2)
|
||||
|
|
||||
result = pred.backtrack(t2, t)
|
||||
or
|
||||
t = t2.continue() and
|
||||
TaintTracking::arrayFunctionTaintStep(result, pred, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node whose value ends up being interpreted as the argument list in `sys`.
|
||||
*/
|
||||
private DataFlow::SourceNode argumentList(SystemCommandExecution sys) {
|
||||
result = argumentList(sys, DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `source` contributes to the arguments of an indirect command execution `sys`.
|
||||
*
|
||||
@@ -61,15 +78,22 @@ private DataFlow::SourceNode argumentList(SystemCommandExecution sys, DataFlow::
|
||||
* let args = ["-c", cmd];
|
||||
* childProcess.spawn(sh, args, cb);
|
||||
* ```
|
||||
* or
|
||||
* ```
|
||||
* let cmd = getCommand();
|
||||
* childProcess.spawn("cmd.exe", ["/c"].concat(cmd), cb);
|
||||
* ```
|
||||
*/
|
||||
predicate isIndirectCommandArgument(DataFlow::Node source, SystemCommandExecution sys) {
|
||||
exists(
|
||||
DataFlow::ArrayCreationNode args, DataFlow::Node shell, string dashC
|
||||
|
|
||||
exists(DataFlow::ArrayCreationNode args, DataFlow::Node shell, string dashC |
|
||||
shellCmd(shell.asExpr(), dashC) and
|
||||
shell = commandArgument(sys, DataFlow::TypeBackTracker::end()) and
|
||||
args = argumentList(sys, DataFlow::TypeBackTracker::end()) and
|
||||
shell = commandArgument(sys) and
|
||||
args.getAPropertyWrite().getRhs().mayHaveStringValue(dashC) and
|
||||
source = args.getAPropertyWrite().getRhs()
|
||||
args = argumentList(sys) and
|
||||
(
|
||||
source = argumentList(sys)
|
||||
or
|
||||
source = argumentList(sys).getAPropertyWrite().getRhs()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --experimental
|
||||
@@ -0,0 +1 @@
|
||||
export * as ns from "./lib";
|
||||
@@ -0,0 +1,4 @@
|
||||
import { ns } from "./reExportLib";
|
||||
|
||||
/** calls:lib.f */
|
||||
ns.f();
|
||||
@@ -146,8 +146,10 @@
|
||||
| import.js:5:5:5:7 | myf | import.js:5:11:5:11 | f | n.js:1:1:1:15 | function f |
|
||||
| import.js:8:5:8:11 | someVar | import.js:8:15:8:23 | someStuff | file://:0:0:0:0 | indefinite value (call) |
|
||||
| import.js:11:5:11:6 | h1 | import.js:11:10:11:10 | h | file://:0:0:0:0 | indefinite value (import) |
|
||||
| import.js:11:5:11:6 | h1 | import.js:11:10:11:10 | h | h.js:1:1:3:0 | exports object of module h |
|
||||
| import.js:12:5:12:6 | hf | import.js:12:10:12:12 | h.f | file://:0:0:0:0 | indefinite value (heap) |
|
||||
| import.js:12:5:12:6 | hf | import.js:12:10:12:12 | h.f | file://:0:0:0:0 | indefinite value (import) |
|
||||
| import.js:12:5:12:6 | hf | import.js:12:10:12:12 | h.f | h.js:1:8:1:22 | function f |
|
||||
| imports.ts:2:5:2:6 | ax | imports.ts:2:10:2:11 | Ax | file://:0:0:0:0 | indefinite value (global) |
|
||||
| imports.ts:2:5:2:6 | ax | imports.ts:2:10:2:11 | Ax | file://:0:0:0:0 | indefinite value (heap) |
|
||||
| imports.ts:5:5:5:7 | fs_ | imports.ts:5:11:5:12 | fs | file://:0:0:0:0 | indefinite value (import) |
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_BulkReExportDeclarations(BulkReExportDeclaration bred) { any() }
|
||||
@@ -1,3 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_ExportDeclarations(ExportDeclaration ed) { any() }
|
||||
@@ -1,3 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_ExportDefaultDeclarations(ExportDefaultDeclaration edd) { any() }
|
||||
@@ -1,5 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_ExportSpecifiers(ExportSpecifier es, Identifier res0, Identifier res1) {
|
||||
res0 = es.getLocal() and res1 = es.getExported()
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_GlobalVariableRef(VarAccess access) {
|
||||
access.getVariable() instanceof GlobalVariable
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_ImportDefaultSpecifiers(ImportDefaultSpecifier ids) { any() }
|
||||
@@ -1,5 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_ImportMetaExpr(ImportMetaExpr meta) {
|
||||
any()
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_ImportNamespaceSpecifier(ImportNamespaceSpecifier ins) { any() }
|
||||
@@ -1,3 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_ImportSpecifiers(ImportSpecifier is, VarDecl res) { res = is.getLocal() }
|
||||
@@ -1,5 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_Imports(ImportDeclaration id, PathExprInModule res0, int res1) {
|
||||
res0 = id.getImportedPath() and res1 = count(id.getASpecifier())
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_Module_exports(Module m, string name, ASTNode export) {
|
||||
m.exports(name, export)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_NamedImportSpecifier(NamedImportSpecifier nis) { any() }
|
||||
@@ -1,5 +0,0 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_OtherImports(Import imprt, Module res) {
|
||||
not imprt instanceof ImportDeclaration and res = imprt.getImportedModule()
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import semmle.javascript.ES2015Modules
|
||||
|
||||
query predicate test_ReExportDeclarations(ReExportDeclaration red, ConstantString res) {
|
||||
res = red.getImportedPath()
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_getAnImportedModule(string res0, string res1) {
|
||||
exists(Module mod |
|
||||
res0 = mod.getFile().getRelativePath() and
|
||||
res1 = mod.getAnImportedModule().getFile().getRelativePath()
|
||||
)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_getExportedName(ExportSpecifier es, string res) { res = es.getLocalName() }
|
||||
@@ -1,3 +0,0 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_getImportedName(ImportSpecifier is, string res) { res = is.getImportedName() }
|
||||
@@ -1,3 +0,0 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_getLocalName(ExportSpecifier es, string res) { res = es.getLocalName() }
|
||||
@@ -1,5 +0,0 @@
|
||||
import javascript
|
||||
|
||||
query predicate test_getSourceNode(ExportDeclaration ed, string name, DataFlow::Node res) {
|
||||
res = ed.getSourceNode(name)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * as ns from "./a";
|
||||
@@ -0,0 +1,3 @@
|
||||
import { ns } from "./reExportNamespace";
|
||||
|
||||
ns.x(); // Calls f() from a.js
|
||||
@@ -1,57 +1,5 @@
|
||||
test_ImportSpecifiers
|
||||
| b.js:1:8:1:8 | f | b.js:1:8:1:8 | f |
|
||||
| d.js:1:10:1:21 | default as g | d.js:1:21:1:21 | g |
|
||||
| d.js:1:24:1:29 | x as y | d.js:1:29:1:29 | y |
|
||||
| exports.js:1:8:1:17 | * as dummy | exports.js:1:13:1:17 | dummy |
|
||||
| f.ts:1:8:1:8 | g | f.ts:1:8:1:8 | g |
|
||||
| g.ts:1:9:1:11 | foo | g.ts:1:9:1:11 | foo |
|
||||
| import-in-mjs.mjs:1:8:1:24 | exported_from_mjs | import-in-mjs.mjs:1:8:1:24 | exported_from_mjs |
|
||||
| import-ts-with-js-extension.ts:1:10:1:12 | foo | import-ts-with-js-extension.ts:1:10:1:12 | foo |
|
||||
| importcss.js:1:8:1:8 | A | importcss.js:1:8:1:8 | A |
|
||||
| m/c.js:1:8:1:13 | * as b | m/c.js:1:13:1:13 | b |
|
||||
| tst.html:5:10:5:10 | f | tst.html:5:10:5:10 | f |
|
||||
| unresolved.js:1:8:1:8 | f | unresolved.js:1:8:1:8 | f |
|
||||
test_getLocalName
|
||||
| b.js:5:10:5:15 | f as g | f |
|
||||
| b.js:7:8:7:9 | f2 | default |
|
||||
| e.js:2:10:2:10 | x | x |
|
||||
| e.js:2:13:2:13 | y | y |
|
||||
| e.js:3:10:3:21 | default as g | default |
|
||||
| m/c.js:5:10:5:15 | g as h | g |
|
||||
test_getExportedName
|
||||
| b.js:5:10:5:15 | f as g | f |
|
||||
| b.js:7:8:7:9 | f2 | default |
|
||||
| e.js:2:10:2:10 | x | x |
|
||||
| e.js:2:13:2:13 | y | y |
|
||||
| e.js:3:10:3:21 | default as g | default |
|
||||
| m/c.js:5:10:5:15 | g as h | g |
|
||||
test_OtherImports
|
||||
| es2015_require.js:1:11:1:24 | require('./d') | d.js:1:1:5:0 | <toplevel> |
|
||||
test_ImportMetaExpr
|
||||
| importmeta.js:1:33:1:43 | import.meta |
|
||||
test_ReExportDeclarations
|
||||
| b.js:7:1:7:21 | export ... './a'; | b.js:7:16:7:20 | './a' |
|
||||
| d.js:4:1:4:20 | export * from 'm/c'; | d.js:4:15:4:19 | 'm/c' |
|
||||
| e.js:3:1:3:35 | export ... './a'; | e.js:3:30:3:34 | './a' |
|
||||
| m/c.js:5:1:5:30 | export ... '../b'; | m/c.js:5:24:5:29 | '../b' |
|
||||
test_ImportDefaultSpecifiers
|
||||
| b.js:1:8:1:8 | f |
|
||||
| f.ts:1:8:1:8 | g |
|
||||
| import-in-mjs.mjs:1:8:1:24 | exported_from_mjs |
|
||||
| importcss.js:1:8:1:8 | A |
|
||||
| tst.html:5:10:5:10 | f |
|
||||
| unresolved.js:1:8:1:8 | f |
|
||||
test_getImportedName
|
||||
| b.js:1:8:1:8 | f | default |
|
||||
| d.js:1:10:1:21 | default as g | default |
|
||||
| d.js:1:24:1:29 | x as y | x |
|
||||
| f.ts:1:8:1:8 | g | default |
|
||||
| g.ts:1:9:1:11 | foo | foo |
|
||||
| import-in-mjs.mjs:1:8:1:24 | exported_from_mjs | default |
|
||||
| import-ts-with-js-extension.ts:1:10:1:12 | foo | foo |
|
||||
| importcss.js:1:8:1:8 | A | default |
|
||||
| tst.html:5:10:5:10 | f | default |
|
||||
| unresolved.js:1:8:1:8 | f | default |
|
||||
test_BulkReExportDeclarations
|
||||
| d.js:4:1:4:20 | export * from 'm/c'; |
|
||||
test_ExportDeclarations
|
||||
| a.js:1:1:3:1 | export ... n 23;\\n} |
|
||||
| a.js:5:1:5:32 | export ... } = o; |
|
||||
@@ -64,29 +12,48 @@ test_ExportDeclarations
|
||||
| export-in-mjs.mjs:1:1:1:34 | export ... s = 42; |
|
||||
| f.ts:5:1:5:24 | export ... oo() {} |
|
||||
| m/c.js:5:1:5:30 | export ... '../b'; |
|
||||
| reExportNamespace.js:1:1:1:26 | export ... "./a"; |
|
||||
| tst.html:7:3:7:22 | export const y = 42; |
|
||||
test_getAnImportedModule
|
||||
| library-tests/Modules/b.js | library-tests/Modules/a.js |
|
||||
| library-tests/Modules/d.js | library-tests/Modules/a.js |
|
||||
| library-tests/Modules/d.js | library-tests/Modules/b.js |
|
||||
| library-tests/Modules/es2015_require.js | library-tests/Modules/d.js |
|
||||
| library-tests/Modules/f.ts | library-tests/Modules/e.js |
|
||||
| library-tests/Modules/g.ts | library-tests/Modules/f.ts |
|
||||
| library-tests/Modules/import-ts-with-js-extension.ts | library-tests/Modules/f.ts |
|
||||
| library-tests/Modules/m/c.js | library-tests/Modules/b.js |
|
||||
test_getSourceNode
|
||||
| a.js:1:1:3:1 | export ... n 23;\\n} | default | a.js:1:16:3:1 | functio ... n 23;\\n} |
|
||||
| a.js:5:1:5:32 | export ... } = o; | x | a.js:5:18:5:20 | f() |
|
||||
| b.js:5:1:5:18 | export { f as g }; | g | b.js:5:10:5:10 | f |
|
||||
| b.js:7:1:7:21 | export ... './a'; | f2 | a.js:1:16:3:1 | functio ... n 23;\\n} |
|
||||
| e.js:2:1:2:16 | export { x, y }; | x | e.js:2:10:2:10 | x |
|
||||
| e.js:2:1:2:16 | export { x, y }; | y | e.js:2:13:2:13 | y |
|
||||
| e.js:3:1:3:35 | export ... './a'; | g | a.js:1:16:3:1 | functio ... n 23;\\n} |
|
||||
| es2015_require.js:3:1:3:25 | export ... ss C {} | default | es2015_require.js:3:16:3:25 | class C {} |
|
||||
| export-in-mjs.mjs:1:1:1:34 | export ... s = 42; | exported_from_mjs | export-in-mjs.mjs:1:32:1:33 | 42 |
|
||||
| f.ts:5:1:5:24 | export ... oo() {} | foo | f.ts:5:8:5:24 | function foo() {} |
|
||||
| m/c.js:5:1:5:30 | export ... '../b'; | h | b.js:5:10:5:10 | f |
|
||||
| tst.html:7:3:7:22 | export const y = 42; | y | tst.html:7:20:7:21 | 42 |
|
||||
test_ExportDefaultDeclarations
|
||||
| a.js:1:1:3:1 | export ... n 23;\\n} |
|
||||
| es2015_require.js:3:1:3:25 | export ... ss C {} |
|
||||
test_ExportSpecifiers
|
||||
| b.js:5:10:5:15 | f as g | b.js:5:10:5:10 | f | b.js:5:15:5:15 | g |
|
||||
| e.js:2:10:2:10 | x | e.js:2:10:2:10 | x | e.js:2:10:2:10 | x |
|
||||
| e.js:2:13:2:13 | y | e.js:2:13:2:13 | y | e.js:2:13:2:13 | y |
|
||||
| e.js:3:10:3:21 | default as g | e.js:3:10:3:16 | default | e.js:3:21:3:21 | g |
|
||||
| m/c.js:5:10:5:15 | g as h | m/c.js:5:10:5:10 | g | m/c.js:5:15:5:15 | h |
|
||||
test_GlobalVariableRef
|
||||
| a.js:5:31:5:31 | o |
|
||||
| exports.js:3:9:3:15 | exports |
|
||||
| importmeta.js:1:15:1:17 | URL |
|
||||
| tst.html:6:3:6:7 | alert |
|
||||
test_ImportDefaultSpecifiers
|
||||
| b.js:1:8:1:8 | f |
|
||||
| f.ts:1:8:1:8 | g |
|
||||
| import-in-mjs.mjs:1:8:1:24 | exported_from_mjs |
|
||||
| importcss.js:1:8:1:8 | A |
|
||||
| tst.html:5:10:5:10 | f |
|
||||
| unresolved.js:1:8:1:8 | f |
|
||||
test_ImportMetaExpr
|
||||
| importmeta.js:1:33:1:43 | import.meta |
|
||||
test_ImportNamespaceSpecifier
|
||||
| exports.js:1:8:1:17 | * as dummy |
|
||||
| m/c.js:1:8:1:13 | * as b |
|
||||
test_ImportSpecifiers
|
||||
| b.js:1:8:1:8 | f | b.js:1:8:1:8 | f |
|
||||
| d.js:1:10:1:21 | default as g | d.js:1:21:1:21 | g |
|
||||
| d.js:1:24:1:29 | x as y | d.js:1:29:1:29 | y |
|
||||
| exports.js:1:8:1:17 | * as dummy | exports.js:1:13:1:17 | dummy |
|
||||
| f.ts:1:8:1:8 | g | f.ts:1:8:1:8 | g |
|
||||
| g.ts:1:9:1:11 | foo | g.ts:1:9:1:11 | foo |
|
||||
| import-in-mjs.mjs:1:8:1:24 | exported_from_mjs | import-in-mjs.mjs:1:8:1:24 | exported_from_mjs |
|
||||
| import-ts-with-js-extension.ts:1:10:1:12 | foo | import-ts-with-js-extension.ts:1:10:1:12 | foo |
|
||||
| importcss.js:1:8:1:8 | A | importcss.js:1:8:1:8 | A |
|
||||
| m/c.js:1:8:1:13 | * as b | m/c.js:1:13:1:13 | b |
|
||||
| reExportNamespaceClient.js:1:10:1:11 | ns | reExportNamespaceClient.js:1:10:1:11 | ns |
|
||||
| tst.html:5:10:5:10 | f | tst.html:5:10:5:10 | f |
|
||||
| unresolved.js:1:8:1:8 | f | unresolved.js:1:8:1:8 | f |
|
||||
test_Imports
|
||||
| b.js:1:1:1:20 | import f from './a'; | b.js:1:15:1:19 | './a' | 1 |
|
||||
| d.js:1:1:1:43 | import ... './a'; | d.js:1:38:1:42 | './a' | 2 |
|
||||
@@ -98,20 +65,9 @@ test_Imports
|
||||
| import-ts-with-js-extension.ts:1:1:1:29 | import ... /f.js"; | import-ts-with-js-extension.ts:1:21:1:28 | "./f.js" | 1 |
|
||||
| importcss.js:1:1:1:24 | import ... a.css"; | importcss.js:1:15:1:23 | "./a.css" | 1 |
|
||||
| m/c.js:1:1:1:26 | import ... '../b'; | m/c.js:1:20:1:25 | '../b' | 1 |
|
||||
| reExportNamespaceClient.js:1:1:1:41 | import ... space"; | reExportNamespaceClient.js:1:20:1:40 | "./reEx ... espace" | 1 |
|
||||
| tst.html:5:3:5:20 | import f from 'a'; | tst.html:5:17:5:19 | 'a' | 1 |
|
||||
| unresolved.js:1:1:1:18 | import f from 'a'; | unresolved.js:1:15:1:17 | 'a' | 1 |
|
||||
test_NamedImportSpecifier
|
||||
| d.js:1:10:1:21 | default as g |
|
||||
| d.js:1:24:1:29 | x as y |
|
||||
| g.ts:1:9:1:11 | foo |
|
||||
| import-ts-with-js-extension.ts:1:10:1:12 | foo |
|
||||
test_GlobalVariableRef
|
||||
| a.js:5:31:5:31 | o |
|
||||
| exports.js:3:9:3:15 | exports |
|
||||
| importmeta.js:1:15:1:17 | URL |
|
||||
| tst.html:6:3:6:7 | alert |
|
||||
test_BulkReExportDeclarations
|
||||
| d.js:4:1:4:20 | export * from 'm/c'; |
|
||||
test_Module_exports
|
||||
| a.js:1:1:5:32 | <toplevel> | default | a.js:1:1:3:1 | export ... n 23;\\n} |
|
||||
| a.js:1:1:5:32 | <toplevel> | x | a.js:5:1:5:32 | export ... } = o; |
|
||||
@@ -126,15 +82,67 @@ test_Module_exports
|
||||
| f.ts:1:1:6:0 | <toplevel> | foo | f.ts:5:1:5:24 | export ... oo() {} |
|
||||
| m/c.js:1:1:6:0 | <toplevel> | h | m/c.js:5:1:5:30 | export ... '../b'; |
|
||||
| tst.html:4:23:8:0 | <toplevel> | y | tst.html:7:3:7:22 | export const y = 42; |
|
||||
test_ExportDefaultDeclarations
|
||||
| a.js:1:1:3:1 | export ... n 23;\\n} |
|
||||
| es2015_require.js:3:1:3:25 | export ... ss C {} |
|
||||
test_ExportSpecifiers
|
||||
| b.js:5:10:5:15 | f as g | b.js:5:10:5:10 | f | b.js:5:15:5:15 | g |
|
||||
| e.js:2:10:2:10 | x | e.js:2:10:2:10 | x | e.js:2:10:2:10 | x |
|
||||
| e.js:2:13:2:13 | y | e.js:2:13:2:13 | y | e.js:2:13:2:13 | y |
|
||||
| e.js:3:10:3:21 | default as g | e.js:3:10:3:16 | default | e.js:3:21:3:21 | g |
|
||||
| m/c.js:5:10:5:15 | g as h | m/c.js:5:10:5:10 | g | m/c.js:5:15:5:15 | h |
|
||||
test_ImportNamespaceSpecifier
|
||||
| exports.js:1:8:1:17 | * as dummy |
|
||||
| m/c.js:1:8:1:13 | * as b |
|
||||
test_NamedImportSpecifier
|
||||
| d.js:1:10:1:21 | default as g |
|
||||
| d.js:1:24:1:29 | x as y |
|
||||
| g.ts:1:9:1:11 | foo |
|
||||
| import-ts-with-js-extension.ts:1:10:1:12 | foo |
|
||||
| reExportNamespaceClient.js:1:10:1:11 | ns |
|
||||
test_OtherImports
|
||||
| es2015_require.js:1:11:1:24 | require('./d') | d.js:1:1:5:0 | <toplevel> |
|
||||
test_ReExportDeclarations
|
||||
| b.js:7:1:7:21 | export ... './a'; | b.js:7:16:7:20 | './a' |
|
||||
| d.js:4:1:4:20 | export * from 'm/c'; | d.js:4:15:4:19 | 'm/c' |
|
||||
| e.js:3:1:3:35 | export ... './a'; | e.js:3:30:3:34 | './a' |
|
||||
| m/c.js:5:1:5:30 | export ... '../b'; | m/c.js:5:24:5:29 | '../b' |
|
||||
| reExportNamespace.js:1:1:1:26 | export ... "./a"; | reExportNamespace.js:1:21:1:25 | "./a" |
|
||||
test_getAnImportedModule
|
||||
| library-tests/Modules/b.js | library-tests/Modules/a.js |
|
||||
| library-tests/Modules/d.js | library-tests/Modules/a.js |
|
||||
| library-tests/Modules/d.js | library-tests/Modules/b.js |
|
||||
| library-tests/Modules/es2015_require.js | library-tests/Modules/d.js |
|
||||
| library-tests/Modules/f.ts | library-tests/Modules/e.js |
|
||||
| library-tests/Modules/g.ts | library-tests/Modules/f.ts |
|
||||
| library-tests/Modules/import-ts-with-js-extension.ts | library-tests/Modules/f.ts |
|
||||
| library-tests/Modules/m/c.js | library-tests/Modules/b.js |
|
||||
| library-tests/Modules/reExportNamespaceClient.js | library-tests/Modules/reExportNamespace.js |
|
||||
test_getExportedName
|
||||
| b.js:5:10:5:15 | f as g | g |
|
||||
| b.js:7:8:7:9 | f2 | f2 |
|
||||
| e.js:2:10:2:10 | x | x |
|
||||
| e.js:2:13:2:13 | y | y |
|
||||
| e.js:3:10:3:21 | default as g | g |
|
||||
| m/c.js:5:10:5:15 | g as h | h |
|
||||
| reExportNamespace.js:1:8:1:14 | * as ns | ns |
|
||||
test_getImportedName
|
||||
| b.js:1:8:1:8 | f | default |
|
||||
| d.js:1:10:1:21 | default as g | default |
|
||||
| d.js:1:24:1:29 | x as y | x |
|
||||
| f.ts:1:8:1:8 | g | default |
|
||||
| g.ts:1:9:1:11 | foo | foo |
|
||||
| import-in-mjs.mjs:1:8:1:24 | exported_from_mjs | default |
|
||||
| import-ts-with-js-extension.ts:1:10:1:12 | foo | foo |
|
||||
| importcss.js:1:8:1:8 | A | default |
|
||||
| reExportNamespaceClient.js:1:10:1:11 | ns | ns |
|
||||
| tst.html:5:10:5:10 | f | default |
|
||||
| unresolved.js:1:8:1:8 | f | default |
|
||||
test_getLocalName
|
||||
| b.js:5:10:5:15 | f as g | f |
|
||||
| b.js:7:8:7:9 | f2 | default |
|
||||
| e.js:2:10:2:10 | x | x |
|
||||
| e.js:2:13:2:13 | y | y |
|
||||
| e.js:3:10:3:21 | default as g | default |
|
||||
| m/c.js:5:10:5:15 | g as h | g |
|
||||
test_getSourceNode
|
||||
| a.js:1:1:3:1 | export ... n 23;\\n} | default | a.js:1:16:3:1 | functio ... n 23;\\n} |
|
||||
| a.js:5:1:5:32 | export ... } = o; | x | a.js:5:18:5:20 | f() |
|
||||
| b.js:5:1:5:18 | export { f as g }; | g | b.js:5:10:5:10 | f |
|
||||
| b.js:7:1:7:21 | export ... './a'; | f2 | a.js:1:16:3:1 | functio ... n 23;\\n} |
|
||||
| e.js:2:1:2:16 | export { x, y }; | x | e.js:2:10:2:10 | x |
|
||||
| e.js:2:1:2:16 | export { x, y }; | y | e.js:2:13:2:13 | y |
|
||||
| e.js:3:1:3:35 | export ... './a'; | g | a.js:1:16:3:1 | functio ... n 23;\\n} |
|
||||
| es2015_require.js:3:1:3:25 | export ... ss C {} | default | es2015_require.js:3:16:3:25 | class C {} |
|
||||
| export-in-mjs.mjs:1:1:1:34 | export ... s = 42; | exported_from_mjs | export-in-mjs.mjs:1:32:1:33 | 42 |
|
||||
| f.ts:5:1:5:24 | export ... oo() {} | foo | f.ts:5:8:5:24 | function foo() {} |
|
||||
| m/c.js:5:1:5:30 | export ... '../b'; | h | b.js:5:10:5:10 | f |
|
||||
| tst.html:7:3:7:22 | export const y = 42; | y | tst.html:7:20:7:21 | 42 |
|
||||
|
||||
@@ -1,19 +1,58 @@
|
||||
import ImportSpecifiers
|
||||
import getLocalName
|
||||
import getExportedName
|
||||
import OtherImports
|
||||
import ImportMeta
|
||||
import ReExportDeclarations
|
||||
import ImportDefaultSpecifiers
|
||||
import getImportedName
|
||||
import ExportDeclarations
|
||||
import getAnImportedModule
|
||||
import getSourceNode
|
||||
import Imports
|
||||
import NamedImportSpecifier
|
||||
import GlobalVariableRef
|
||||
import BulkReExportDeclarations
|
||||
import Module_exports
|
||||
import ExportDefaultDeclarations
|
||||
import ExportSpecifiers
|
||||
import ImportNamespaceSpecifier
|
||||
import javascript
|
||||
|
||||
query predicate test_BulkReExportDeclarations(BulkReExportDeclaration bred) { any() }
|
||||
|
||||
query predicate test_ExportDeclarations(ExportDeclaration ed) { any() }
|
||||
|
||||
query predicate test_ExportDefaultDeclarations(ExportDefaultDeclaration edd) { any() }
|
||||
|
||||
query predicate test_ExportSpecifiers(ExportSpecifier es, Identifier res0, Identifier res1) {
|
||||
res0 = es.getLocal() and res1 = es.getExported()
|
||||
}
|
||||
|
||||
query predicate test_GlobalVariableRef(VarAccess access) {
|
||||
access.getVariable() instanceof GlobalVariable
|
||||
}
|
||||
|
||||
query predicate test_ImportDefaultSpecifiers(ImportDefaultSpecifier ids) { any() }
|
||||
|
||||
query predicate test_ImportMetaExpr(ImportMetaExpr meta) { any() }
|
||||
|
||||
query predicate test_ImportNamespaceSpecifier(ImportNamespaceSpecifier ins) { any() }
|
||||
|
||||
query predicate test_ImportSpecifiers(ImportSpecifier is, VarDecl res) { res = is.getLocal() }
|
||||
|
||||
query predicate test_Imports(ImportDeclaration id, PathExprInModule res0, int res1) {
|
||||
res0 = id.getImportedPath() and res1 = count(id.getASpecifier())
|
||||
}
|
||||
|
||||
query predicate test_Module_exports(Module m, string name, ASTNode export) {
|
||||
m.exports(name, export)
|
||||
}
|
||||
|
||||
query predicate test_NamedImportSpecifier(NamedImportSpecifier nis) { any() }
|
||||
|
||||
query predicate test_OtherImports(Import imprt, Module res) {
|
||||
not imprt instanceof ImportDeclaration and res = imprt.getImportedModule()
|
||||
}
|
||||
|
||||
query predicate test_ReExportDeclarations(ReExportDeclaration red, ConstantString res) {
|
||||
res = red.getImportedPath()
|
||||
}
|
||||
|
||||
query predicate test_getAnImportedModule(string res0, string res1) {
|
||||
exists(Module mod |
|
||||
res0 = mod.getFile().getRelativePath() and
|
||||
res1 = mod.getAnImportedModule().getFile().getRelativePath()
|
||||
)
|
||||
}
|
||||
|
||||
query predicate test_getExportedName(ExportSpecifier es, string res) { res = es.getExportedName() }
|
||||
|
||||
query predicate test_getImportedName(ImportSpecifier is, string res) { res = is.getImportedName() }
|
||||
|
||||
query predicate test_getLocalName(ExportSpecifier es, string res) { res = es.getLocalName() }
|
||||
|
||||
query predicate test_getSourceNode(ExportDeclaration ed, string name, DataFlow::Node res) {
|
||||
res = ed.getSourceNode(name)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
var https = require('https');
|
||||
https.createServer(function (req, res) {});
|
||||
https.createServer(o, function (req, res) {});
|
||||
require('http2').createServer((req, res) => {});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
test_isCreateServer
|
||||
| createServer.js:2:1:2:42 | https.c ... es) {}) |
|
||||
| createServer.js:3:1:3:45 | https.c ... es) {}) |
|
||||
| createServer.js:4:1:4:47 | require ... => {}) |
|
||||
| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) |
|
||||
| src/http.js:12:1:16:2 | http.cr ... r");\\n}) |
|
||||
| src/http.js:57:1:57:31 | http.cr ... dler()) |
|
||||
@@ -51,6 +52,7 @@ test_HeaderDefinition
|
||||
test_RouteSetup_getServer
|
||||
| createServer.js:2:1:2:42 | https.c ... es) {}) | createServer.js:2:1:2:42 | https.c ... es) {}) |
|
||||
| createServer.js:3:1:3:45 | https.c ... es) {}) | createServer.js:3:1:3:45 | https.c ... es) {}) |
|
||||
| createServer.js:4:1:4:47 | require ... => {}) | createServer.js:4:1:4:47 | require ... => {}) |
|
||||
| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | src/http.js:4:14:10:2 | http.cr ... foo;\\n}) |
|
||||
| src/http.js:12:1:16:2 | http.cr ... r");\\n}) | src/http.js:12:1:16:2 | http.cr ... r");\\n}) |
|
||||
| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:57:1:57:31 | http.cr ... dler()) |
|
||||
@@ -72,6 +74,7 @@ test_HeaderDefinition_getAHeaderName
|
||||
test_ServerDefinition
|
||||
| createServer.js:2:1:2:42 | https.c ... es) {}) |
|
||||
| createServer.js:3:1:3:45 | https.c ... es) {}) |
|
||||
| createServer.js:4:1:4:47 | require ... => {}) |
|
||||
| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) |
|
||||
| src/http.js:12:1:16:2 | http.cr ... r");\\n}) |
|
||||
| src/http.js:57:1:57:31 | http.cr ... dler()) |
|
||||
@@ -103,6 +106,7 @@ test_RouteHandler_getAResponseExpr
|
||||
test_ServerDefinition_getARouteHandler
|
||||
| createServer.js:2:1:2:42 | https.c ... es) {}) | createServer.js:2:20:2:41 | functio ... res) {} |
|
||||
| createServer.js:3:1:3:45 | https.c ... es) {}) | createServer.js:3:23:3:44 | functio ... res) {} |
|
||||
| createServer.js:4:1:4:47 | require ... => {}) | createServer.js:4:31:4:46 | (req, res) => {} |
|
||||
| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | src/http.js:4:32:10:1 | functio ... .foo;\\n} |
|
||||
| src/http.js:12:1:16:2 | http.cr ... r");\\n}) | src/http.js:12:19:16:1 | functio ... ar");\\n} |
|
||||
| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:55:12:55:30 | function(req,res){} |
|
||||
@@ -120,6 +124,7 @@ test_ResponseSendArgument
|
||||
test_RouteSetup_getARouteHandler
|
||||
| createServer.js:2:1:2:42 | https.c ... es) {}) | createServer.js:2:20:2:41 | functio ... res) {} |
|
||||
| createServer.js:3:1:3:45 | https.c ... es) {}) | createServer.js:3:23:3:44 | functio ... res) {} |
|
||||
| createServer.js:4:1:4:47 | require ... => {}) | createServer.js:4:31:4:46 | (req, res) => {} |
|
||||
| src/http.js:4:14:10:2 | http.cr ... foo;\\n}) | src/http.js:4:32:10:1 | functio ... .foo;\\n} |
|
||||
| src/http.js:12:1:16:2 | http.cr ... r");\\n}) | src/http.js:12:19:16:1 | functio ... ar");\\n} |
|
||||
| src/http.js:57:1:57:31 | http.cr ... dler()) | src/http.js:55:12:55:30 | function(req,res){} |
|
||||
@@ -147,6 +152,7 @@ test_RemoteFlowSources
|
||||
test_RouteHandler
|
||||
| createServer.js:2:20:2:41 | functio ... res) {} | createServer.js:2:1:2:42 | https.c ... es) {}) |
|
||||
| createServer.js:3:23:3:44 | functio ... res) {} | createServer.js:3:1:3:45 | https.c ... es) {}) |
|
||||
| createServer.js:4:31:4:46 | (req, res) => {} | createServer.js:4:1:4:47 | require ... => {}) |
|
||||
| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:4:14:10:2 | http.cr ... foo;\\n}) |
|
||||
| src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:12:1:16:2 | http.cr ... r");\\n}) |
|
||||
| src/http.js:55:12:55:30 | function(req,res){} | src/http.js:57:1:57:31 | http.cr ... dler()) |
|
||||
|
||||
@@ -22,12 +22,23 @@ nodes
|
||||
| child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" |
|
||||
| child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" |
|
||||
| child_process-test.js:25:21:25:23 | cmd |
|
||||
| child_process-test.js:39:18:39:30 | [ flag, cmd ] |
|
||||
| child_process-test.js:39:18:39:30 | [ flag, cmd ] |
|
||||
| child_process-test.js:39:26:39:28 | cmd |
|
||||
| child_process-test.js:39:26:39:28 | cmd |
|
||||
| child_process-test.js:43:15:43:17 | cmd |
|
||||
| child_process-test.js:43:15:43:17 | cmd |
|
||||
| child_process-test.js:50:15:50:17 | cmd |
|
||||
| child_process-test.js:50:15:50:17 | cmd |
|
||||
| child_process-test.js:53:25:53:58 | ['/C', ... , cmd]) |
|
||||
| child_process-test.js:53:25:53:58 | ['/C', ... , cmd]) |
|
||||
| child_process-test.js:53:46:53:57 | ["bar", cmd] |
|
||||
| child_process-test.js:53:46:53:57 | ["bar", cmd] |
|
||||
| child_process-test.js:53:54:53:56 | cmd |
|
||||
| child_process-test.js:53:54:53:56 | cmd |
|
||||
| child_process-test.js:54:25:54:49 | ['/C', ... at(cmd) |
|
||||
| child_process-test.js:54:25:54:49 | ['/C', ... at(cmd) |
|
||||
| child_process-test.js:54:46:54:48 | cmd |
|
||||
| execSeries.js:3:20:3:22 | arr |
|
||||
| execSeries.js:6:14:6:16 | arr |
|
||||
| execSeries.js:6:14:6:21 | arr[i++] |
|
||||
@@ -100,6 +111,9 @@ edges
|
||||
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:43:15:43:17 | cmd |
|
||||
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:50:15:50:17 | cmd |
|
||||
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:50:15:50:17 | cmd |
|
||||
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:53:54:53:56 | cmd |
|
||||
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:53:54:53:56 | cmd |
|
||||
| child_process-test.js:6:9:6:49 | cmd | child_process-test.js:54:46:54:48 | cmd |
|
||||
| child_process-test.js:6:15:6:38 | url.par ... , true) | child_process-test.js:6:15:6:44 | url.par ... ).query |
|
||||
| child_process-test.js:6:15:6:44 | url.par ... ).query | child_process-test.js:6:15:6:49 | url.par ... ry.path |
|
||||
| child_process-test.js:6:15:6:49 | url.par ... ry.path | child_process-test.js:6:9:6:49 | cmd |
|
||||
@@ -107,6 +121,14 @@ edges
|
||||
| child_process-test.js:6:25:6:31 | req.url | child_process-test.js:6:15:6:38 | url.par ... , true) |
|
||||
| child_process-test.js:25:21:25:23 | cmd | child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" |
|
||||
| child_process-test.js:25:21:25:23 | cmd | child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" |
|
||||
| child_process-test.js:39:26:39:28 | cmd | child_process-test.js:39:18:39:30 | [ flag, cmd ] |
|
||||
| child_process-test.js:39:26:39:28 | cmd | child_process-test.js:39:18:39:30 | [ flag, cmd ] |
|
||||
| child_process-test.js:53:46:53:57 | ["bar", cmd] | child_process-test.js:53:25:53:58 | ['/C', ... , cmd]) |
|
||||
| child_process-test.js:53:46:53:57 | ["bar", cmd] | child_process-test.js:53:25:53:58 | ['/C', ... , cmd]) |
|
||||
| child_process-test.js:53:54:53:56 | cmd | child_process-test.js:53:46:53:57 | ["bar", cmd] |
|
||||
| child_process-test.js:53:54:53:56 | cmd | child_process-test.js:53:46:53:57 | ["bar", cmd] |
|
||||
| child_process-test.js:54:46:54:48 | cmd | child_process-test.js:54:25:54:49 | ['/C', ... at(cmd) |
|
||||
| child_process-test.js:54:46:54:48 | cmd | child_process-test.js:54:25:54:49 | ['/C', ... at(cmd) |
|
||||
| execSeries.js:3:20:3:22 | arr | execSeries.js:6:14:6:16 | arr |
|
||||
| execSeries.js:6:14:6:16 | arr | execSeries.js:6:14:6:21 | arr[i++] |
|
||||
| execSeries.js:6:14:6:21 | arr[i++] | execSeries.js:14:24:14:30 | command |
|
||||
@@ -165,10 +187,16 @@ edges
|
||||
| child_process-test.js:22:18:22:20 | cmd | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:22:18:22:20 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:23:13:23:15 | cmd | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:23:13:23:15 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:25:13:25:31 | "foo" + cmd + "bar" | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:39:5:39:31 | cp.spaw ... cmd ]) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:39:18:39:30 | [ flag, cmd ] | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:39:5:39:31 | cp.spaw ... cmd ]) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:39:26:39:28 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:44:5:44:34 | cp.exec ... , args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:43:15:43:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:51:5:51:39 | cp.exec ... , args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:50:15:50:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:56:3:56:21 | cp.spawn(cmd, args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:43:15:43:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:53:5:53:59 | cp.spaw ... cmd])) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:53:25:53:58 | ['/C', ... , cmd]) | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:53:5:53:59 | cp.spaw ... cmd])) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:53:46:53:57 | ["bar", cmd] | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:53:5:53:59 | cp.spaw ... cmd])) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:53:54:53:56 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:54:5:54:50 | cp.spaw ... t(cmd)) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:54:25:54:49 | ['/C', ... at(cmd) | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:59:5:59:39 | cp.exec ... , args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:50:15:50:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| child_process-test.js:64:3:64:21 | cp.spawn(cmd, args) | child_process-test.js:6:25:6:31 | req.url | child_process-test.js:43:15:43:17 | cmd | This command depends on $@. | child_process-test.js:6:25:6:31 | req.url | a user-provided value |
|
||||
| execSeries.js:14:41:14:47 | command | execSeries.js:18:34:18:40 | req.url | execSeries.js:14:41:14:47 | command | This command depends on $@. | execSeries.js:18:34:18:40 | req.url | a user-provided value |
|
||||
| other.js:7:33:7:35 | cmd | other.js:5:25:5:31 | req.url | other.js:7:33:7:35 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value |
|
||||
| other.js:8:28:8:30 | cmd | other.js:5:25:5:31 | req.url | other.js:8:28:8:30 | cmd | This command depends on $@. | other.js:5:25:5:31 | req.url | a user-provided value |
|
||||
|
||||
@@ -50,6 +50,14 @@ var server = http.createServer(function(req, res) {
|
||||
args[1] = cmd;
|
||||
cp.execFile(`/bin` + "/bash", args); // NOT OK
|
||||
|
||||
cp.spawn('cmd.exe', ['/C', 'foo'].concat(["bar", cmd])); // NOT OK
|
||||
cp.spawn('cmd.exe', ['/C', 'foo'].concat(cmd)); // NOT OK
|
||||
|
||||
let myArgs = [];
|
||||
myArgs.push(`-` + "c");
|
||||
myArgs.push(cmd);
|
||||
cp.execFile(`/bin` + "/bash", args); // NOT OK
|
||||
|
||||
});
|
||||
|
||||
function run(cmd, args) {
|
||||
|
||||
@@ -37,6 +37,16 @@ nodes
|
||||
| tst.js:45:13:45:56 | 'http:/ ... tainted |
|
||||
| tst.js:45:13:45:56 | 'http:/ ... tainted |
|
||||
| tst.js:45:50:45:56 | tainted |
|
||||
| tst.js:58:9:58:52 | tainted |
|
||||
| tst.js:58:19:58:42 | url.par ... , true) |
|
||||
| tst.js:58:19:58:48 | url.par ... ).query |
|
||||
| tst.js:58:19:58:52 | url.par ... ery.url |
|
||||
| tst.js:58:29:58:35 | req.url |
|
||||
| tst.js:58:29:58:35 | req.url |
|
||||
| tst.js:61:29:61:35 | tainted |
|
||||
| tst.js:61:29:61:35 | tainted |
|
||||
| tst.js:64:30:64:36 | tainted |
|
||||
| tst.js:64:30:64:36 | tainted |
|
||||
edges
|
||||
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
|
||||
| tst.js:14:9:14:52 | tainted | tst.js:18:13:18:19 | tainted |
|
||||
@@ -75,6 +85,15 @@ edges
|
||||
| tst.js:43:46:43:52 | tainted | tst.js:43:13:43:54 | `http:/ ... inted}` |
|
||||
| tst.js:45:50:45:56 | tainted | tst.js:45:13:45:56 | 'http:/ ... tainted |
|
||||
| tst.js:45:50:45:56 | tainted | tst.js:45:13:45:56 | 'http:/ ... tainted |
|
||||
| tst.js:58:9:58:52 | tainted | tst.js:61:29:61:35 | tainted |
|
||||
| tst.js:58:9:58:52 | tainted | tst.js:61:29:61:35 | tainted |
|
||||
| tst.js:58:9:58:52 | tainted | tst.js:64:30:64:36 | tainted |
|
||||
| tst.js:58:9:58:52 | tainted | tst.js:64:30:64:36 | tainted |
|
||||
| tst.js:58:19:58:42 | url.par ... , true) | tst.js:58:19:58:48 | url.par ... ).query |
|
||||
| tst.js:58:19:58:48 | url.par ... ).query | tst.js:58:19:58:52 | url.par ... ery.url |
|
||||
| tst.js:58:19:58:52 | url.par ... ery.url | tst.js:58:9:58:52 | tainted |
|
||||
| tst.js:58:29:58:35 | req.url | tst.js:58:19:58:42 | url.par ... , true) |
|
||||
| tst.js:58:29:58:35 | req.url | tst.js:58:19:58:42 | url.par ... , true) |
|
||||
#select
|
||||
| tst.js:18:5:18:20 | request(tainted) | tst.js:14:29:14:35 | req.url | tst.js:18:13:18:19 | tainted | The $@ of this request depends on $@. | tst.js:18:13:18:19 | tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
|
||||
| tst.js:20:5:20:24 | request.get(tainted) | tst.js:14:29:14:35 | req.url | tst.js:20:17:20:23 | tainted | The $@ of this request depends on $@. | tst.js:20:17:20:23 | tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
|
||||
@@ -88,3 +107,5 @@ edges
|
||||
| tst.js:41:5:41:52 | request ... nted}`) | tst.js:14:29:14:35 | req.url | tst.js:41:13:41:51 | `http:/ ... inted}` | The $@ of this request depends on $@. | tst.js:41:13:41:51 | `http:/ ... inted}` | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
|
||||
| tst.js:43:5:43:55 | request ... nted}`) | tst.js:14:29:14:35 | req.url | tst.js:43:13:43:54 | `http:/ ... inted}` | The $@ of this request depends on $@. | tst.js:43:13:43:54 | `http:/ ... inted}` | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
|
||||
| tst.js:45:5:45:57 | request ... ainted) | tst.js:14:29:14:35 | req.url | tst.js:45:13:45:56 | 'http:/ ... tainted | The $@ of this request depends on $@. | tst.js:45:13:45:56 | 'http:/ ... tainted | URL | tst.js:14:29:14:35 | req.url | a user-provided value |
|
||||
| tst.js:61:2:61:37 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:61:29:61:35 | tainted | The $@ of this request depends on $@. | tst.js:61:29:61:35 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
|
||||
| tst.js:64:3:64:38 | client. ... inted}) | tst.js:58:29:58:35 | req.url | tst.js:64:30:64:36 | tainted | The $@ of this request depends on $@. | tst.js:64:30:64:36 | tainted | URL | tst.js:58:29:58:35 | req.url | a user-provided value |
|
||||
|
||||
@@ -52,3 +52,15 @@ var server = http.createServer(function(req, res) {
|
||||
|
||||
request(`${base}${tainted}`); // OK - assumed safe
|
||||
})
|
||||
|
||||
var CDP = require("chrome-remote-interface");
|
||||
var server = http.createServer(async function(req, res) {
|
||||
var tainted = url.parse(req.url, true).query.url;
|
||||
|
||||
var client = await CDP(options);
|
||||
client.Page.navigate({url: tainted}); // NOT OK.
|
||||
|
||||
CDP(options, (client) => {
|
||||
client.Page.navigate({url: tainted}); // NOT OK.
|
||||
});
|
||||
})
|
||||
|
||||
@@ -32,6 +32,7 @@ where
|
||||
not name = "__new__" and
|
||||
not name = "__metaclass__" and
|
||||
not name = "__init_subclass__" and
|
||||
not name = "__class_getitem__" and
|
||||
/* declared in scope */
|
||||
f.getScope() = cls.getScope()
|
||||
) and
|
||||
|
||||
@@ -12,9 +12,12 @@
|
||||
|
||||
import python
|
||||
|
||||
|
||||
/**
|
||||
* The module `name` was deprecated in Python version `major`.`minor`,
|
||||
* and module `instead` should be used instead (or `instead = "no replacement"`)
|
||||
*/
|
||||
predicate deprecated_module(string name, string instead, int major, int minor) {
|
||||
name = "posixfile" and instead = "email" and major = 1 and minor = 5
|
||||
name = "posixfile" and instead = "fcntl" and major = 1 and minor = 5
|
||||
or
|
||||
name = "gopherlib" and instead = "no replacement" and major = 2 and minor = 5
|
||||
or
|
||||
@@ -34,40 +37,49 @@ predicate deprecated_module(string name, string instead, int major, int minor) {
|
||||
or
|
||||
name = "rotor" and instead = "no replacement" and major = 2 and minor = 4
|
||||
or
|
||||
name = "statcache" and instead = "no replacement" and major = 2 and minor = 2
|
||||
name = "statcache" and instead = "no replacement" and major = 2 and minor = 2
|
||||
or
|
||||
name = "mpz" and instead = "a third party" and major = 2 and minor = 2
|
||||
name = "mpz" and instead = "a third party" and major = 2 and minor = 2
|
||||
or
|
||||
name = "xreadlines" and instead = "no replacement" and major = 2 and minor = 3
|
||||
or
|
||||
name = "multifile" and instead = "email" and major = 2 and minor = 5
|
||||
or
|
||||
name = "sets" and instead = "builtins" and major = 2 and minor = 6
|
||||
name = "sets" and instead = "builtins" and major = 2 and minor = 6
|
||||
or
|
||||
name = "buildtools" and instead = "no replacement" and major = 2 and minor = 3
|
||||
or
|
||||
name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4
|
||||
name = "cfmfile" and instead = "no replacement" and major = 2 and minor = 4
|
||||
or
|
||||
name = "macfs" and instead = "no replacement" and major = 2 and minor = 3
|
||||
or
|
||||
name = "md5" and instead = "hashlib" and major = 2 and minor = 5
|
||||
name = "md5" and instead = "hashlib" and major = 2 and minor = 5
|
||||
or
|
||||
name = "sha" and instead = "hashlib" and major = 2 and minor = 5
|
||||
name = "sha" and instead = "hashlib" and major = 2 and minor = 5
|
||||
}
|
||||
|
||||
string deprecation_message(string mod) {
|
||||
exists(int major, int minor | deprecated_module(mod, _, major, minor) |
|
||||
result = "The " + mod + " module was deprecated in version " + major.toString() + "." + minor.toString() + ".")
|
||||
exists(int major, int minor | deprecated_module(mod, _, major, minor) |
|
||||
result = "The " + mod + " module was deprecated in version " + major.toString() + "." +
|
||||
minor.toString() + "."
|
||||
)
|
||||
}
|
||||
|
||||
string replacement_message(string mod) {
|
||||
exists(string instead | deprecated_module(mod, instead, _, _) |
|
||||
result = " Use " + instead + " module instead." and not instead = "no replacement"
|
||||
or
|
||||
result = "" and instead = "no replacement"
|
||||
)
|
||||
exists(string instead | deprecated_module(mod, instead, _, _) |
|
||||
result = " Use " + instead + " module instead." and not instead = "no replacement"
|
||||
or
|
||||
result = "" and instead = "no replacement"
|
||||
)
|
||||
}
|
||||
|
||||
from ImportExpr imp, Stmt s, Expr e
|
||||
where s.getASubExpression() = e and (e = imp or e.contains(imp))
|
||||
select s, deprecation_message(imp.getName()) + replacement_message(imp.getName())
|
||||
from ImportExpr imp, string name, string instead
|
||||
where
|
||||
name = imp.getName() and
|
||||
deprecated_module(name, instead, _, _) and
|
||||
not exists(Try try, ExceptStmt except | except = try.getAHandler()
|
||||
|
|
||||
except.getType().pointsTo(ClassValue::importError()) and
|
||||
except.containsInScope(imp)
|
||||
)
|
||||
select imp, deprecation_message(name) + replacement_message(name)
|
||||
|
||||
@@ -12,11 +12,16 @@
|
||||
|
||||
import python
|
||||
|
||||
predicate modules_imports_itself(Import i, ModuleValue m) {
|
||||
predicate modules_imports_itself(ImportingStmt i, ModuleValue m) {
|
||||
i.getEnclosingModule() = m.getScope() and
|
||||
m.importedAs(i.getAnImportedModuleName())
|
||||
m = max(string s, ModuleValue m_ |
|
||||
s = i.getAnImportedModuleName() and
|
||||
m_.importedAs(s)
|
||||
|
|
||||
m_ order by s.length()
|
||||
)
|
||||
}
|
||||
|
||||
from Import i, ModuleValue m
|
||||
from ImportingStmt i, ModuleValue m
|
||||
where modules_imports_itself(i, m)
|
||||
select i, "The module '" + m.getName() + "' imports itself."
|
||||
|
||||
@@ -753,9 +753,8 @@ class DefinitionNode extends ControlFlowNode {
|
||||
or
|
||||
augstore(_, this)
|
||||
or
|
||||
exists(Assign a | a.getATarget().(Tuple).getAnElt().getAFlowNode() = this)
|
||||
or
|
||||
exists(Assign a | a.getATarget().(List).getAnElt().getAFlowNode() = this)
|
||||
// `x, y = 1, 2` where LHS is a combination of list or tuples
|
||||
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
|
||||
or
|
||||
exists(For for | for.getTarget().getAFlowNode() = this)
|
||||
}
|
||||
@@ -768,6 +767,18 @@ class DefinitionNode extends ControlFlowNode {
|
||||
}
|
||||
}
|
||||
|
||||
private Expr list_or_tuple_nested_element(Expr list_or_tuple) {
|
||||
exists(Expr elt |
|
||||
elt = list_or_tuple.(Tuple).getAnElt()
|
||||
or
|
||||
elt = list_or_tuple.(List).getAnElt()
|
||||
|
|
||||
result = elt
|
||||
or
|
||||
result = list_or_tuple_nested_element(elt)
|
||||
)
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a deletion statement, such as `del x`.
|
||||
* There can be multiple `DeletionNode`s for each `Delete` such that each
|
||||
* target has own `DeletionNode`. The CFG for `del a, x.y` looks like:
|
||||
@@ -887,18 +898,38 @@ private AstNode assigned_value(Expr lhs) {
|
||||
/* lhs += x => result = (lhs + x) */
|
||||
exists(AugAssign a, BinaryExpr b | b = a.getOperation() and result = b and lhs = b.getLeft())
|
||||
or
|
||||
/* ..., lhs, ... = ..., result, ... */
|
||||
exists(Assign a, Tuple target, Tuple values, int index |
|
||||
a.getATarget() = target and
|
||||
a.getValue() = values and
|
||||
lhs = target.getElt(index) and
|
||||
result = values.getElt(index)
|
||||
)
|
||||
/* ..., lhs, ... = ..., result, ...
|
||||
* or
|
||||
* ..., (..., lhs, ...), ... = ..., (..., result, ...), ...
|
||||
*/
|
||||
exists(Assign a | nested_sequence_assign(a.getATarget(), a.getValue(), lhs, result))
|
||||
or
|
||||
/* for lhs in seq: => `result` is the `for` node, representing the `iter(next(seq))` operation. */
|
||||
result.(For).getTarget() = lhs
|
||||
}
|
||||
|
||||
predicate nested_sequence_assign(Expr left_parent, Expr right_parent,
|
||||
Expr left_result, Expr right_result) {
|
||||
exists(int i, Expr left_elem, Expr right_elem
|
||||
|
|
||||
(
|
||||
left_elem = left_parent.(Tuple).getElt(i)
|
||||
or
|
||||
left_elem = left_parent.(List).getElt(i)
|
||||
)
|
||||
and
|
||||
(
|
||||
right_elem = right_parent.(Tuple).getElt(i)
|
||||
or
|
||||
right_elem = right_parent.(List).getElt(i)
|
||||
)
|
||||
|
|
||||
left_result = left_elem and right_result = right_elem
|
||||
or
|
||||
nested_sequence_assign(left_elem, right_elem, left_result, right_result)
|
||||
)
|
||||
}
|
||||
|
||||
/** A flow node for a `for` statement. */
|
||||
class ForNode extends ControlFlowNode {
|
||||
|
||||
@@ -1037,6 +1068,17 @@ class NameConstantNode extends NameNode {
|
||||
*/
|
||||
}
|
||||
|
||||
/** A control flow node correspoinding to a starred expression, `*a`. */
|
||||
class StarredNode extends ControlFlowNode {
|
||||
StarredNode() {
|
||||
toAst(this) instanceof Starred
|
||||
}
|
||||
|
||||
ControlFlowNode getValue() {
|
||||
toAst(result) = toAst(this).(Starred).getValue()
|
||||
}
|
||||
}
|
||||
|
||||
private module Scopes {
|
||||
|
||||
private predicate fast_local(NameNode n) {
|
||||
|
||||
@@ -4,7 +4,6 @@ private import semmle.python.objects.ObjectInternal
|
||||
private import semmle.python.dataflow.Implementation
|
||||
|
||||
module TaintTracking {
|
||||
|
||||
class Source = TaintSource;
|
||||
|
||||
class Sink = TaintSink;
|
||||
@@ -16,13 +15,11 @@ module TaintTracking {
|
||||
class PathSink = TaintTrackingNode;
|
||||
|
||||
abstract class Configuration extends string {
|
||||
|
||||
/* Required to prevent compiler warning */
|
||||
bindingset[this]
|
||||
Configuration() { this = this }
|
||||
|
||||
/* Old implementation API */
|
||||
|
||||
predicate isSource(Source src) { none() }
|
||||
|
||||
predicate isSink(Sink sink) { none() }
|
||||
@@ -32,7 +29,6 @@ module TaintTracking {
|
||||
predicate isExtension(Extension extension) { none() }
|
||||
|
||||
/* New implementation API */
|
||||
|
||||
/**
|
||||
* Holds if `src` is a source of taint of `kind` that is relevant
|
||||
* for this configuration.
|
||||
@@ -66,7 +62,9 @@ module TaintTracking {
|
||||
/**
|
||||
* Holds if `src -> dest` is a flow edge converting taint from `srckind` to `destkind`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind) {
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -79,9 +77,7 @@ module TaintTracking {
|
||||
* Holds if `node` should be considered as a barrier to flow of `kind`.
|
||||
*/
|
||||
predicate isBarrier(DataFlow::Node node, TaintKind kind) {
|
||||
exists(Sanitizer sanitizer |
|
||||
this.isSanitizer(sanitizer)
|
||||
|
|
||||
exists(Sanitizer sanitizer | this.isSanitizer(sanitizer) |
|
||||
sanitizer.sanitizingNode(kind, node.asCfgNode())
|
||||
or
|
||||
sanitizer.sanitizingEdge(kind, node.asVariable())
|
||||
@@ -112,16 +108,18 @@ module TaintTracking {
|
||||
* Holds if flow from `src` to `dest` is prohibited when the incoming taint is `srckind` and the outgoing taint is `destkind`.
|
||||
* Note that `srckind` and `destkind` can be the same.
|
||||
*/
|
||||
predicate isBarrierEdge(DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind) { none() }
|
||||
predicate isBarrierEdge(
|
||||
DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/* Common query API */
|
||||
|
||||
predicate hasFlowPath(PathSource src, PathSink sink) {
|
||||
this.(TaintTrackingImplementation).hasFlowPath(src, sink)
|
||||
}
|
||||
|
||||
/* Old query API */
|
||||
|
||||
/* deprecated */
|
||||
deprecated predicate hasFlow(Source src, Sink sink) {
|
||||
exists(PathSource psrc, PathSink psink |
|
||||
@@ -132,7 +130,6 @@ module TaintTracking {
|
||||
}
|
||||
|
||||
/* New query API */
|
||||
|
||||
predicate hasSimpleFlow(DataFlow::Node src, DataFlow::Node sink) {
|
||||
exists(PathSource psrc, PathSink psink |
|
||||
this.hasFlowPath(psrc, psink) and
|
||||
@@ -140,7 +137,5 @@ module TaintTracking {
|
||||
sink = psink.getNode()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
@@ -2,25 +2,18 @@ import python
|
||||
import semmle.python.security.TaintTracking
|
||||
|
||||
class OpenFile extends TaintKind {
|
||||
|
||||
OpenFile() { this = "file.open" }
|
||||
|
||||
override string repr() { result = "an open file" }
|
||||
|
||||
|
||||
}
|
||||
|
||||
class OpenFileConfiguration extends TaintTracking::Configuration {
|
||||
|
||||
OpenFileConfiguration() { this = "Open file configuration" }
|
||||
OpenFileConfiguration() { this = "Open file configuration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src, TaintKind kind) {
|
||||
theOpenFunction().(FunctionObject).getACall() = src.asCfgNode() and
|
||||
kind instanceof OpenFile
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, TaintKind kind) {
|
||||
none()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink, TaintKind kind) { none() }
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,12 +3,8 @@ private import semmle.python.objects.ObjectInternal
|
||||
import semmle.python.dataflow.Implementation
|
||||
|
||||
/* For backwards compatibility -- Use `TaintTrackingContext` instead. */
|
||||
deprecated
|
||||
class CallContext extends TaintTrackingContext {
|
||||
|
||||
TaintTrackingContext getCallee(CallNode call) {
|
||||
result.getCaller(call) = this
|
||||
}
|
||||
deprecated class CallContext extends TaintTrackingContext {
|
||||
TaintTrackingContext getCallee(CallNode call) { result.getCaller(call) = this }
|
||||
|
||||
predicate appliesToScope(Scope s) {
|
||||
exists(PythonFunctionObjectInternal func, TaintKind param, AttributePath path, int n |
|
||||
@@ -21,33 +17,23 @@ class CallContext extends TaintTrackingContext {
|
||||
or
|
||||
this.isTop()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Backwards compatibility with config-less taint-tracking */
|
||||
private class LegacyConfiguration extends TaintTracking::Configuration {
|
||||
|
||||
LegacyConfiguration() {
|
||||
/* A name that won't be accidentally chosen by users */
|
||||
this = "Semmle: Internal legacy configuration"
|
||||
}
|
||||
|
||||
override predicate isSource(TaintSource src) {
|
||||
src = src
|
||||
}
|
||||
override predicate isSource(TaintSource src) { src = src }
|
||||
|
||||
override predicate isSink(TaintSink sink) {
|
||||
sink = sink
|
||||
}
|
||||
override predicate isSink(TaintSink sink) { sink = sink }
|
||||
|
||||
override predicate isSanitizer(Sanitizer sanitizer) {
|
||||
sanitizer = sanitizer
|
||||
}
|
||||
override predicate isSanitizer(Sanitizer sanitizer) { sanitizer = sanitizer }
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest) {
|
||||
exists(DataFlowExtension::DataFlowNode legacyExtension |
|
||||
src.asCfgNode() = legacyExtension
|
||||
|
|
||||
exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension |
|
||||
dest.asCfgNode() = legacyExtension.getASuccessorNode()
|
||||
or
|
||||
dest.asVariable() = legacyExtension.getASuccessorVariable()
|
||||
@@ -58,10 +44,10 @@ private class LegacyConfiguration extends TaintTracking::Configuration {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind) {
|
||||
exists(DataFlowExtension::DataFlowNode legacyExtension |
|
||||
src.asCfgNode() = legacyExtension
|
||||
|
|
||||
override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node src, DataFlow::Node dest, TaintKind srckind, TaintKind destkind
|
||||
) {
|
||||
exists(DataFlowExtension::DataFlowNode legacyExtension | src.asCfgNode() = legacyExtension |
|
||||
dest.asCfgNode() = legacyExtension.getASuccessorNode(srckind, destkind)
|
||||
)
|
||||
}
|
||||
@@ -79,5 +65,4 @@ private class LegacyConfiguration extends TaintTracking::Configuration {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/** Provides classes and predicates for tracking global state across the control flow and call graphs.
|
||||
*
|
||||
/**
|
||||
* Provides classes and predicates for tracking global state across the control flow and call graphs.
|
||||
*
|
||||
* NOTE: State tracking tracks both whether a state may apply to a given node in a given context *and*
|
||||
* whether it may not apply.
|
||||
* That `state.appliesTo(f, ctx)` holds implies nothing about whether `state.mayNotApplyTo(f, ctx)` holds.
|
||||
@@ -15,14 +16,11 @@ private import semmle.python.objects.ObjectInternal
|
||||
|
||||
/** A state that should be tracked. */
|
||||
abstract class TrackableState extends string {
|
||||
|
||||
bindingset[this]
|
||||
TrackableState() { this = this }
|
||||
|
||||
/** Holds if this state may apply to the control flow node `f`, regardless of the context. */
|
||||
final predicate appliesTo(ControlFlowNode f) {
|
||||
this.appliesTo(f, _)
|
||||
}
|
||||
final predicate appliesTo(ControlFlowNode f) { this.appliesTo(f, _) }
|
||||
|
||||
/** Holds if this state may not apply to the control flow node `f`, given the context `ctx`. */
|
||||
final predicate appliesTo(ControlFlowNode f, Context ctx) {
|
||||
@@ -35,60 +33,56 @@ abstract class TrackableState extends string {
|
||||
}
|
||||
|
||||
/** Holds if this state may apply to the control flow node `f`, regardless of the context. */
|
||||
final predicate mayNotApplyTo(ControlFlowNode f) {
|
||||
this.mayNotApplyTo(f, _)
|
||||
}
|
||||
final predicate mayNotApplyTo(ControlFlowNode f) { this.mayNotApplyTo(f, _) }
|
||||
|
||||
/** Holds if `test` shows value to be untainted with `taint`, given the context `ctx`. */
|
||||
predicate testsFor(PyEdgeRefinement test, Context ctx, boolean sense) {
|
||||
predicate testsFor(PyEdgeRefinement test, Context ctx, boolean sense) {
|
||||
ctx.appliesToScope(test.getScope()) and this.testsFor(test, sense)
|
||||
}
|
||||
|
||||
/** Holds if `test` shows value to be untainted with `taint` */
|
||||
predicate testsFor(PyEdgeRefinement test, boolean sense) { none() }
|
||||
|
||||
/** Holds if state starts at `f`.
|
||||
/**
|
||||
* Holds if state starts at `f`.
|
||||
* Either this predicate or `startsAt(ControlFlowNode f, Context ctx)`
|
||||
* should be overriden by sub-classes.
|
||||
*/
|
||||
predicate startsAt(ControlFlowNode f) { none() }
|
||||
|
||||
/** Holds if state starts at `f` given context `ctx`.
|
||||
/**
|
||||
* Holds if state starts at `f` given context `ctx`.
|
||||
* Either this predicate or `startsAt(ControlFlowNode f)`
|
||||
* should be overriden by sub-classes.
|
||||
*/
|
||||
pragma [noinline]
|
||||
predicate startsAt(ControlFlowNode f, Context ctx) {
|
||||
ctx.appliesTo(f) and this.startsAt(f)
|
||||
}
|
||||
pragma[noinline]
|
||||
predicate startsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.startsAt(f) }
|
||||
|
||||
/** Holds if state ends at `f`.
|
||||
/**
|
||||
* Holds if state ends at `f`.
|
||||
* Either this predicate or `endsAt(ControlFlowNode f, Context ctx)`
|
||||
* may be overriden by sub-classes.
|
||||
*/
|
||||
predicate endsAt(ControlFlowNode f) { none() }
|
||||
|
||||
/** Holds if state ends at `f` given context `ctx`.
|
||||
/**
|
||||
* Holds if state ends at `f` given context `ctx`.
|
||||
* Either this predicate or `endsAt(ControlFlowNode f)`
|
||||
* may be overriden by sub-classes.
|
||||
*/
|
||||
pragma [noinline]
|
||||
predicate endsAt(ControlFlowNode f, Context ctx) {
|
||||
ctx.appliesTo(f) and this.endsAt(f)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate endsAt(ControlFlowNode f, Context ctx) { ctx.appliesTo(f) and this.endsAt(f) }
|
||||
}
|
||||
|
||||
|
||||
module StateTracking {
|
||||
|
||||
private predicate not_allowed(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) {
|
||||
state.endsAt(f, ctx) and sense = true
|
||||
or
|
||||
state.startsAt(f, ctx) and sense = false
|
||||
}
|
||||
|
||||
/** Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) to
|
||||
/**
|
||||
* Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) to
|
||||
* control flow node `f` given the context `ctx`.
|
||||
*/
|
||||
predicate appliesToNode(TrackableState state, ControlFlowNode f, Context ctx, boolean sense) {
|
||||
@@ -96,8 +90,7 @@ module StateTracking {
|
||||
or
|
||||
state.startsAt(f, ctx) and sense = true
|
||||
or
|
||||
not not_allowed(state, f, ctx, sense)
|
||||
and
|
||||
not not_allowed(state, f, ctx, sense) and
|
||||
(
|
||||
exists(BasicBlock b |
|
||||
/* First node in a block */
|
||||
@@ -106,7 +99,7 @@ module StateTracking {
|
||||
/* Other nodes in block, except trackable calls */
|
||||
exists(int n |
|
||||
f = b.getNode(n) and
|
||||
appliesToNode(state, b.getNode(n-1), ctx, sense) and
|
||||
appliesToNode(state, b.getNode(n - 1), ctx, sense) and
|
||||
not exists(PythonFunctionObjectInternal func, Context callee |
|
||||
callee.fromCall(f, func, ctx)
|
||||
)
|
||||
@@ -127,27 +120,32 @@ module StateTracking {
|
||||
)
|
||||
or
|
||||
/* Other scope entries */
|
||||
exists(Scope s |
|
||||
exists(Scope s |
|
||||
s.getEntryNode() = f and
|
||||
ctx.appliesToScope(s)
|
||||
|
|
||||
|
|
||||
not exists(Scope pred | pred.precedes(s)) and
|
||||
(ctx.isImport() or ctx.isRuntime()) and sense = false
|
||||
(ctx.isImport() or ctx.isRuntime()) and
|
||||
sense = false
|
||||
or
|
||||
exists(Scope pred, Context pred_ctx |
|
||||
appliesToNode(state, pred.getANormalExit(), pred_ctx, sense) and
|
||||
pred.precedes(s) and
|
||||
ctx.isRuntime() |
|
||||
ctx.isRuntime()
|
||||
|
|
||||
pred_ctx.isRuntime() or pred_ctx.isImport()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the
|
||||
/**
|
||||
* Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the
|
||||
* start of basic block `block` given the context `ctx`.
|
||||
*/
|
||||
private predicate appliesAtBlockStart(TrackableState state, BasicBlock block, Context ctx, boolean sense) {
|
||||
private predicate appliesAtBlockStart(
|
||||
TrackableState state, BasicBlock block, Context ctx, boolean sense
|
||||
) {
|
||||
exists(PyEdgeRefinement test |
|
||||
test.getSuccessor() = block and
|
||||
state.testsFor(test, ctx, sense)
|
||||
@@ -164,12 +162,13 @@ module StateTracking {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the
|
||||
/**
|
||||
* Holds if `state` may apply (with `sense` = true) or may not apply (with `sense` = false) at the
|
||||
* end of basic block `block` given the context `ctx`.
|
||||
*/
|
||||
private predicate appliesAtBlockEnd(TrackableState state, BasicBlock block, Context ctx, boolean sense) {
|
||||
private predicate appliesAtBlockEnd(
|
||||
TrackableState state, BasicBlock block, Context ctx, boolean sense
|
||||
) {
|
||||
appliesToNode(state, block.getLastNode(), ctx, sense)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,8 @@ private import semmle.python.objects.ObjectInternal
|
||||
private import semmle.python.dataflow.Implementation
|
||||
import semmle.python.dataflow.Configuration
|
||||
|
||||
/** A 'kind' of taint. This may be almost anything,
|
||||
/**
|
||||
* A 'kind' of taint. This may be almost anything,
|
||||
* but it is typically something like a "user-defined string".
|
||||
* Examples include, data from a http request object,
|
||||
* data from an SMS or other mobile data source,
|
||||
@@ -100,33 +101,38 @@ import semmle.python.dataflow.Configuration
|
||||
* the local file system.
|
||||
*/
|
||||
abstract class TaintKind extends string {
|
||||
|
||||
bindingset[this]
|
||||
TaintKind() { any() }
|
||||
|
||||
/** Gets the kind of taint that the named attribute will have if an object is tainted with this taint.
|
||||
/**
|
||||
* Gets the kind of taint that the named attribute will have if an object is tainted with this taint.
|
||||
* In other words, if `x` has this kind of taint then it implies that `x.name`
|
||||
* has `result` kind of taint.
|
||||
*/
|
||||
TaintKind getTaintOfAttribute(string name) { none() }
|
||||
|
||||
/** Gets the kind of taint results from calling the named method if an object is tainted with this taint.
|
||||
/**
|
||||
* Gets the kind of taint results from calling the named method if an object is tainted with this taint.
|
||||
* In other words, if `x` has this kind of taint then it implies that `x.name()`
|
||||
* has `result` kind of taint.
|
||||
*/
|
||||
TaintKind getTaintOfMethodResult(string name) { none() }
|
||||
|
||||
/** Gets the taint resulting from the flow step `fromnode` -> `tonode`.
|
||||
/**
|
||||
* Gets the taint resulting from the flow step `fromnode` -> `tonode`.
|
||||
*/
|
||||
TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) { none() }
|
||||
|
||||
/** Gets the taint resulting from the flow step `fromnode` -> `tonode`, with `edgeLabel`
|
||||
/**
|
||||
* Gets the taint resulting from the flow step `fromnode` -> `tonode`, with `edgeLabel`
|
||||
*/
|
||||
TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) {
|
||||
result = this.getTaintForFlowStep(fromnode, tonode) and edgeLabel = "custom taint flow step for " + this
|
||||
result = this.getTaintForFlowStep(fromnode, tonode) and
|
||||
edgeLabel = "custom taint flow step for " + this
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use `TaintFlow.additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind)` instead.
|
||||
/**
|
||||
* DEPRECATED -- Use `TaintFlow.additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind)` instead.
|
||||
*
|
||||
* Holds if this kind of taint passes from variable `fromvar` to variable `tovar`
|
||||
* This predicate is present for completeness. It is unlikely that any `TaintKind`
|
||||
@@ -134,40 +140,40 @@ abstract class TaintKind extends string {
|
||||
*/
|
||||
deprecated predicate additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar) { none() }
|
||||
|
||||
/** Holds if this kind of taint "taints" `expr`.
|
||||
/**
|
||||
* Holds if this kind of taint "taints" `expr`.
|
||||
*/
|
||||
final predicate taints(ControlFlowNode expr) {
|
||||
exists(TaintedNode n |
|
||||
n.getTaintKind() = this and n.getCfgNode() = expr
|
||||
)
|
||||
exists(TaintedNode n | n.getTaintKind() = this and n.getCfgNode() = expr)
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use getType() instead */
|
||||
deprecated ClassObject getClass() {
|
||||
none()
|
||||
}
|
||||
deprecated ClassObject getClass() { none() }
|
||||
|
||||
/** Gets the class of this kind of taint.
|
||||
/**
|
||||
* Gets the class of this kind of taint.
|
||||
* For example, if this were a kind of string taint
|
||||
* the `result` would be `theStrType()`.
|
||||
*/
|
||||
ClassValue getType() {
|
||||
result.(ClassObjectInternal).getSource() = this.getClass()
|
||||
}
|
||||
ClassValue getType() { result.(ClassObjectInternal).getSource() = this.getClass() }
|
||||
|
||||
/** Gets the boolean values (may be one, neither, or both) that
|
||||
/**
|
||||
* Gets the boolean values (may be one, neither, or both) that
|
||||
* may result from the Python expression `bool(this)`
|
||||
*/
|
||||
boolean booleanValue() {
|
||||
/* Default to true as the vast majority of taint is strings and
|
||||
/*
|
||||
* Default to true as the vast majority of taint is strings and
|
||||
* the empty string is almost always benign.
|
||||
*/
|
||||
|
||||
result = true
|
||||
}
|
||||
|
||||
string repr() { result = this }
|
||||
|
||||
/** Gets the taint resulting from iterating over this kind of taint.
|
||||
/**
|
||||
* Gets the taint resulting from iterating over this kind of taint.
|
||||
* For example iterating over a text file produces lines. So iterating
|
||||
* over a tainted file would result in tainted strings
|
||||
*/
|
||||
@@ -184,20 +190,21 @@ abstract class TaintKind extends string {
|
||||
*/
|
||||
class FlowLabel = TaintKind;
|
||||
|
||||
/** Taint kinds representing collections of other taint kind.
|
||||
/**
|
||||
* Taint kinds representing collections of other taint kind.
|
||||
* We use `{kind}` to represent a mapping of string to `kind` and
|
||||
* `[kind]` to represent a flat collection of `kind`.
|
||||
* The use of `{` and `[` is chosen to reflect dict and list literals
|
||||
* `[kind]` to represent a flat collection of `kind`.
|
||||
* The use of `{` and `[` is chosen to reflect dict and list literals
|
||||
* in Python. We choose a single character prefix and suffix for simplicity
|
||||
* and ease of preventing infinite recursion.
|
||||
*/
|
||||
abstract class CollectionKind extends TaintKind {
|
||||
|
||||
bindingset[this]
|
||||
CollectionKind() {
|
||||
(this.charAt(0) = "[" or this.charAt(0) = "{") and
|
||||
/* Prevent any collection kinds more than 2 deep */
|
||||
not this.charAt(2) = "[" and not this.charAt(2) = "{"
|
||||
not this.charAt(2) = "[" and
|
||||
not this.charAt(2) = "{"
|
||||
}
|
||||
|
||||
abstract TaintKind getMember();
|
||||
@@ -207,20 +214,16 @@ abstract class CollectionKind extends TaintKind {
|
||||
abstract predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode);
|
||||
}
|
||||
|
||||
/** A taint kind representing a flat collections of kinds.
|
||||
/**
|
||||
* A taint kind representing a flat collections of kinds.
|
||||
* Typically a sequence, but can include sets.
|
||||
*/
|
||||
class SequenceKind extends CollectionKind {
|
||||
|
||||
TaintKind itemKind;
|
||||
|
||||
SequenceKind() {
|
||||
this = "[" + itemKind + "]"
|
||||
}
|
||||
SequenceKind() { this = "[" + itemKind + "]" }
|
||||
|
||||
TaintKind getItem() {
|
||||
result = itemKind
|
||||
}
|
||||
TaintKind getItem() { result = itemKind }
|
||||
|
||||
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
exists(BinaryExprNode mod |
|
||||
@@ -236,17 +239,11 @@ class SequenceKind extends CollectionKind {
|
||||
name = "pop" and result = this.getItem()
|
||||
}
|
||||
|
||||
override string repr() {
|
||||
result = "sequence of " + itemKind
|
||||
}
|
||||
override string repr() { result = "sequence of " + itemKind }
|
||||
|
||||
override TaintKind getTaintForIteration() {
|
||||
result = itemKind
|
||||
}
|
||||
override TaintKind getTaintForIteration() { result = itemKind }
|
||||
|
||||
override TaintKind getMember() {
|
||||
result = itemKind
|
||||
}
|
||||
override TaintKind getMember() { result = itemKind }
|
||||
|
||||
override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) {
|
||||
sequence_construct(fromnode.asCfgNode(), tonode.asCfgNode())
|
||||
@@ -255,12 +252,9 @@ class SequenceKind extends CollectionKind {
|
||||
override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) {
|
||||
SequenceKind::itemFlowStep(fromnode.asCfgNode(), tonode.asCfgNode())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
module SequenceKind {
|
||||
|
||||
predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) {
|
||||
tonode.(BinaryExprNode).getAnOperand() = fromnode and edgeLabel = "binary operation"
|
||||
or
|
||||
@@ -275,11 +269,10 @@ module SequenceKind {
|
||||
predicate itemFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
|
||||
subscript_index(fromnode, tonode)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module DictKind {
|
||||
predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) {
|
||||
predicate flowStep(ControlFlowNode fromnode, ControlFlowNode tonode, string edgeLabel) {
|
||||
Implementation::copyCall(fromnode, tonode) and
|
||||
edgeLabel = "dict copy"
|
||||
or
|
||||
@@ -289,37 +282,31 @@ module DictKind {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Helper for sequence flow steps */
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate subscript_index(ControlFlowNode obj, SubscriptNode sub) {
|
||||
sub.isLoad() and
|
||||
sub.getValue() = obj and
|
||||
not sub.getNode().getIndex() instanceof Slice
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate subscript_slice(ControlFlowNode obj, SubscriptNode sub) {
|
||||
sub.isLoad() and
|
||||
sub.getValue() = obj and
|
||||
sub.getNode().getIndex() instanceof Slice
|
||||
}
|
||||
|
||||
|
||||
/** A taint kind representing a mapping of objects to kinds.
|
||||
/**
|
||||
* A taint kind representing a mapping of objects to kinds.
|
||||
* Typically a dict, but can include other mappings.
|
||||
*/
|
||||
class DictKind extends CollectionKind {
|
||||
|
||||
TaintKind valueKind;
|
||||
|
||||
DictKind() {
|
||||
this = "{" + valueKind + "}"
|
||||
}
|
||||
DictKind() { this = "{" + valueKind + "}" }
|
||||
|
||||
TaintKind getValue() {
|
||||
result = valueKind
|
||||
}
|
||||
TaintKind getValue() { result = valueKind }
|
||||
|
||||
override TaintKind getTaintOfMethodResult(string name) {
|
||||
name = "get" and result = valueKind
|
||||
@@ -329,13 +316,9 @@ class DictKind extends CollectionKind {
|
||||
name = "itervalues" and result.(SequenceKind).getItem() = valueKind
|
||||
}
|
||||
|
||||
override string repr() {
|
||||
result = "dict of " + valueKind
|
||||
}
|
||||
override string repr() { result = "dict of " + valueKind }
|
||||
|
||||
override TaintKind getMember() {
|
||||
result = valueKind
|
||||
}
|
||||
override TaintKind getMember() { result = valueKind }
|
||||
|
||||
override predicate flowFromMember(DataFlow::Node fromnode, DataFlow::Node tonode) {
|
||||
dict_construct(fromnode.asCfgNode(), tonode.asCfgNode())
|
||||
@@ -344,17 +327,15 @@ class DictKind extends CollectionKind {
|
||||
override predicate flowToMember(DataFlow::Node fromnode, DataFlow::Node tonode) {
|
||||
subscript_index(fromnode.asCfgNode(), tonode.asCfgNode())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** A type of sanitizer of untrusted data.
|
||||
/**
|
||||
* A type of sanitizer of untrusted data.
|
||||
* Examples include sanitizers for http responses, for DB access or for shell commands.
|
||||
* Usually a sanitizer can only sanitize data for one particular use.
|
||||
* For example, a sanitizer for DB commands would not be safe to use for http responses.
|
||||
*/
|
||||
abstract class Sanitizer extends string {
|
||||
|
||||
bindingset[this]
|
||||
Sanitizer() { any() }
|
||||
|
||||
@@ -372,42 +353,49 @@ abstract class Sanitizer extends string {
|
||||
|
||||
/** Holds if `def` shows value to be untainted with `taint` */
|
||||
predicate sanitizingDefinition(TaintKind taint, EssaDefinition def) { none() }
|
||||
|
||||
}
|
||||
|
||||
/** DEPRECATED -- Use DataFlowExtension instead.
|
||||
/**
|
||||
* DEPRECATED -- Use DataFlowExtension instead.
|
||||
* An extension to taint-flow. For adding library or framework specific flows.
|
||||
* Examples include flow from a request to untrusted part of that request or
|
||||
* from a socket to data from that socket.
|
||||
*/
|
||||
deprecated abstract class TaintFlow extends string {
|
||||
|
||||
abstract deprecated class TaintFlow extends string {
|
||||
bindingset[this]
|
||||
TaintFlow() { any() }
|
||||
|
||||
/** Holds if `fromnode` being tainted with `fromkind` will result in `tonode` being tainted with `tokind`.
|
||||
/**
|
||||
* Holds if `fromnode` being tainted with `fromkind` will result in `tonode` being tainted with `tokind`.
|
||||
* Extensions to `TaintFlow` should override this to provide additional taint steps.
|
||||
*/
|
||||
predicate additionalFlowStep(ControlFlowNode fromnode, TaintKind fromkind, ControlFlowNode tonode, TaintKind tokind) { none() }
|
||||
predicate additionalFlowStep(
|
||||
ControlFlowNode fromnode, TaintKind fromkind, ControlFlowNode tonode, TaintKind tokind
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if the given `kind` of taint passes from variable `fromvar` to variable `tovar`.
|
||||
/**
|
||||
* Holds if the given `kind` of taint passes from variable `fromvar` to variable `tovar`.
|
||||
* This predicate is present for completeness. Most `TaintFlow` implementations will not need to override it.
|
||||
*/
|
||||
predicate additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind) { none() }
|
||||
predicate additionalFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if the given `kind` of taint cannot pass from variable `fromvar` to variable `tovar`.
|
||||
/**
|
||||
* Holds if the given `kind` of taint cannot pass from variable `fromvar` to variable `tovar`.
|
||||
* This predicate is present for completeness. Most `TaintFlow` implementations will not need to override it.
|
||||
*/
|
||||
predicate prunedFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind) { none() }
|
||||
|
||||
predicate prunedFlowStepVar(EssaVariable fromvar, EssaVariable tovar, TaintKind kind) { none() }
|
||||
}
|
||||
|
||||
/** A source of taintedness.
|
||||
/**
|
||||
* A source of taintedness.
|
||||
* Users of the taint tracking library should override this
|
||||
* class to provide their own sources.
|
||||
*/
|
||||
abstract class TaintSource extends @py_flow_node {
|
||||
|
||||
string toString() { result = "Taint source" }
|
||||
|
||||
/**
|
||||
@@ -429,9 +417,7 @@ abstract class TaintSource extends @py_flow_node {
|
||||
context.isTop() and this.isSourceOf(kind)
|
||||
}
|
||||
|
||||
Location getLocation() {
|
||||
result = this.(ControlFlowNode).getLocation()
|
||||
}
|
||||
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
|
||||
|
||||
predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
|
||||
this.getLocation().hasLocationInfo(fp, bl, bc, el, ec)
|
||||
@@ -459,19 +445,16 @@ abstract class TaintSource extends @py_flow_node {
|
||||
}
|
||||
|
||||
/** Holds if taint can flow from this source to taint sink `sink` */
|
||||
final predicate flowsToSink(TaintSink sink) {
|
||||
this.flowsToSink(_, sink)
|
||||
}
|
||||
final predicate flowsToSink(TaintSink sink) { this.flowsToSink(_, sink) }
|
||||
}
|
||||
|
||||
|
||||
/** Warning: Advanced feature. Users are strongly recommended to use `TaintSource` instead.
|
||||
/**
|
||||
* Warning: Advanced feature. Users are strongly recommended to use `TaintSource` instead.
|
||||
* A source of taintedness on the ESSA data-flow graph.
|
||||
* Users of the taint tracking library can override this
|
||||
* class to provide their own sources on the ESSA graph.
|
||||
*/
|
||||
abstract class TaintedDefinition extends EssaNodeDefinition {
|
||||
|
||||
/**
|
||||
* Holds if `this` is a source of taint kind `kind`
|
||||
*
|
||||
@@ -490,48 +473,36 @@ abstract class TaintedDefinition extends EssaNodeDefinition {
|
||||
predicate isSourceOf(TaintKind kind, TaintTrackingContext context) {
|
||||
context.isTop() and this.isSourceOf(kind)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class DictUpdate extends DataFlowExtension::DataFlowNode {
|
||||
|
||||
MethodCallsiteRefinement call;
|
||||
|
||||
DictUpdate() {
|
||||
exists(CallNode c |
|
||||
c = call.getCall()
|
||||
|
|
||||
exists(CallNode c | c = call.getCall() |
|
||||
c.getFunction().(AttrNode).getName() = "update" and
|
||||
c.getArg(0) = this
|
||||
)
|
||||
}
|
||||
|
||||
override EssaVariable getASuccessorVariable() {
|
||||
call.getVariable() = result
|
||||
}
|
||||
|
||||
override EssaVariable getASuccessorVariable() { call.getVariable() = result }
|
||||
}
|
||||
|
||||
private class SequenceExtends extends DataFlowExtension::DataFlowNode {
|
||||
|
||||
MethodCallsiteRefinement call;
|
||||
|
||||
SequenceExtends() {
|
||||
exists(CallNode c |
|
||||
c = call.getCall()
|
||||
|
|
||||
exists(CallNode c | c = call.getCall() |
|
||||
c.getFunction().(AttrNode).getName() = "extend" and
|
||||
c.getArg(0) = this
|
||||
)
|
||||
}
|
||||
|
||||
override EssaVariable getASuccessorVariable() {
|
||||
call.getVariable() = result
|
||||
}
|
||||
|
||||
override EssaVariable getASuccessorVariable() { call.getVariable() = result }
|
||||
}
|
||||
|
||||
/** A node that is vulnerable to one or more types of taint.
|
||||
/**
|
||||
* A node that is vulnerable to one or more types of taint.
|
||||
* These nodes provide the sinks when computing the taint flow graph.
|
||||
* An example would be an argument to a write to a http response object,
|
||||
* such an argument would be vulnerable to unsanitized user-input (XSS).
|
||||
@@ -539,8 +510,7 @@ private class SequenceExtends extends DataFlowExtension::DataFlowNode {
|
||||
* Users of the taint tracking library should extend this
|
||||
* class to provide their own sink nodes.
|
||||
*/
|
||||
abstract class TaintSink extends @py_flow_node {
|
||||
|
||||
abstract class TaintSink extends @py_flow_node {
|
||||
string toString() { result = "Taint sink" }
|
||||
|
||||
/**
|
||||
@@ -551,134 +521,119 @@ abstract class TaintSink extends @py_flow_node {
|
||||
*/
|
||||
abstract predicate sinks(TaintKind taint);
|
||||
|
||||
Location getLocation() {
|
||||
result = this.(ControlFlowNode).getLocation()
|
||||
}
|
||||
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
|
||||
|
||||
predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
|
||||
this.getLocation().hasLocationInfo(fp, bl, bc, el, ec)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Extension for data-flow, to help express data-flow paths that are
|
||||
/**
|
||||
* Extension for data-flow, to help express data-flow paths that are
|
||||
* library or framework specific and cannot be inferred by the general
|
||||
* data-flow machinery.
|
||||
*/
|
||||
module DataFlowExtension {
|
||||
|
||||
/** A control flow node that modifies the basic data-flow. */
|
||||
abstract class DataFlowNode extends @py_flow_node {
|
||||
string toString() { result = "Dataflow extension node" }
|
||||
|
||||
string toString() {
|
||||
result = "Dataflow extension node"
|
||||
}
|
||||
|
||||
/** Gets a successor node for data-flow.
|
||||
/**
|
||||
* Gets a successor node for data-flow.
|
||||
* Data (all forms) is assumed to flow from `this` to `result`
|
||||
*/
|
||||
ControlFlowNode getASuccessorNode() { none() }
|
||||
|
||||
/** Gets a successor variable for data-flow.
|
||||
/**
|
||||
* Gets a successor variable for data-flow.
|
||||
* Data (all forms) is assumed to flow from `this` to `result`.
|
||||
* Note: This is an unlikely form of flow. See `DataFlowVariable.getASuccessorVariable()`
|
||||
*/
|
||||
EssaVariable getASuccessorVariable() { none() }
|
||||
|
||||
/** Holds if data cannot flow from `this` to `succ`,
|
||||
/**
|
||||
* Holds if data cannot flow from `this` to `succ`,
|
||||
* even though it would normally do so.
|
||||
*/
|
||||
predicate prunedSuccessor(ControlFlowNode succ) { none() }
|
||||
|
||||
/** Gets a successor node, where the successor node will be tainted with `tokind`
|
||||
/**
|
||||
* Gets a successor node, where the successor node will be tainted with `tokind`
|
||||
* when `this` is tainted with `fromkind`.
|
||||
* Extensions to `DataFlowNode` should override this to provide additional taint steps.
|
||||
*/
|
||||
ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) { none() }
|
||||
|
||||
/** Gets a successor node for data-flow with a change of context from callee to caller
|
||||
/**
|
||||
* Gets a successor node for data-flow with a change of context from callee to caller
|
||||
* (going *up* the call-stack) across call-site `call`.
|
||||
* Data (all forms) is assumed to flow from `this` to `result`
|
||||
* Extensions to `DataFlowNode` should override this to provide additional taint steps.
|
||||
*/
|
||||
ControlFlowNode getAReturnSuccessorNode(CallNode call) { none() }
|
||||
|
||||
/** Gets a successor node for data-flow with a change of context from caller to callee
|
||||
/**
|
||||
* Gets a successor node for data-flow with a change of context from caller to callee
|
||||
* (going *down* the call-stack) across call-site `call`.
|
||||
* Data (all forms) is assumed to flow from `this` to `result`
|
||||
* Extensions to `DataFlowNode` should override this to provide additional taint steps.
|
||||
*/
|
||||
ControlFlowNode getACalleeSuccessorNode(CallNode call) { none() }
|
||||
|
||||
}
|
||||
|
||||
/** Data flow variable that modifies the basic data-flow. */
|
||||
class DataFlowVariable extends EssaVariable {
|
||||
|
||||
/** Gets a successor node for data-flow.
|
||||
/**
|
||||
* Gets a successor node for data-flow.
|
||||
* Data (all forms) is assumed to flow from `this` to `result`
|
||||
* Note: This is an unlikely form of flow. See `DataFlowNode.getASuccessorNode()`
|
||||
*/
|
||||
ControlFlowNode getASuccessorNode() { none() }
|
||||
|
||||
/** Gets a successor variable for data-flow.
|
||||
/**
|
||||
* Gets a successor variable for data-flow.
|
||||
* Data (all forms) is assumed to flow from `this` to `result`.
|
||||
*/
|
||||
EssaVariable getASuccessorVariable() { none() }
|
||||
|
||||
/** Holds if data cannot flow from `this` to `succ`,
|
||||
/**
|
||||
* Holds if data cannot flow from `this` to `succ`,
|
||||
* even though it would normally do so.
|
||||
*/
|
||||
predicate prunedSuccessor(EssaVariable succ) { none() }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class TaintedPathSource extends TaintTrackingNode {
|
||||
TaintedPathSource() { this.isSource() }
|
||||
|
||||
TaintedPathSource() {
|
||||
this.isSource()
|
||||
}
|
||||
|
||||
DataFlow::Node getSource() {
|
||||
result = this.getNode()
|
||||
}
|
||||
|
||||
DataFlow::Node getSource() { result = this.getNode() }
|
||||
}
|
||||
|
||||
class TaintedPathSink extends TaintTrackingNode {
|
||||
TaintedPathSink() { this.isSink() }
|
||||
|
||||
TaintedPathSink() {
|
||||
this.isSink()
|
||||
}
|
||||
|
||||
DataFlow::Node getSink() {
|
||||
result = this.getNode()
|
||||
}
|
||||
|
||||
DataFlow::Node getSink() { result = this.getNode() }
|
||||
}
|
||||
|
||||
/* Backwards compatible name */
|
||||
class TaintedNode = TaintTrackingNode;
|
||||
|
||||
/* Helpers for Validating classes */
|
||||
|
||||
private import semmle.python.pointsto.PointsTo
|
||||
|
||||
|
||||
/** Data flow module providing an interface compatible with
|
||||
/**
|
||||
* Data flow module providing an interface compatible with
|
||||
* the other language implementations.
|
||||
*/
|
||||
module DataFlow {
|
||||
|
||||
/** Generic taint kind, source and sink classes for convenience and
|
||||
/**
|
||||
* Generic taint kind, source and sink classes for convenience and
|
||||
* compatibility with other language libraries
|
||||
*/
|
||||
|
||||
class Extension = DataFlowExtension::DataFlowNode;
|
||||
|
||||
deprecated abstract class Configuration extends string {
|
||||
|
||||
abstract deprecated class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { this = this }
|
||||
|
||||
@@ -702,14 +657,10 @@ module DataFlow {
|
||||
this.hasFlowPath(psource, psink)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ConfigurationAdapter extends TaintTracking::Configuration {
|
||||
|
||||
ConfigurationAdapter() {
|
||||
this instanceof Configuration
|
||||
}
|
||||
ConfigurationAdapter() { this instanceof Configuration }
|
||||
|
||||
override predicate isSource(DataFlow::Node node, TaintKind kind) {
|
||||
this.(Configuration).isSource(node.asCfgNode()) and
|
||||
@@ -720,16 +671,13 @@ module DataFlow {
|
||||
this.(Configuration).isSink(node.asCfgNode()) and
|
||||
kind instanceof DataFlowType
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private newtype TDataFlowNode =
|
||||
TEssaNode(EssaVariable var)
|
||||
or
|
||||
TEssaNode(EssaVariable var) or
|
||||
TCfgNode(ControlFlowNode node)
|
||||
|
||||
abstract class Node extends TDataFlowNode {
|
||||
|
||||
abstract ControlFlowNode asCfgNode();
|
||||
|
||||
abstract EssaVariable asVariable();
|
||||
@@ -742,86 +690,51 @@ module DataFlow {
|
||||
|
||||
abstract Location getLocation();
|
||||
|
||||
AstNode asAstNode() {
|
||||
result = this.asCfgNode().getNode()
|
||||
}
|
||||
AstNode asAstNode() { result = this.asCfgNode().getNode() }
|
||||
|
||||
/** For backwards compatibility -- Use asAstNode() instead */
|
||||
deprecated AstNode getNode() {
|
||||
result = this.asAstNode()
|
||||
}
|
||||
|
||||
deprecated AstNode getNode() { result = this.asAstNode() }
|
||||
}
|
||||
|
||||
class CfgNode extends Node, TCfgNode {
|
||||
override ControlFlowNode asCfgNode() { this = TCfgNode(result) }
|
||||
|
||||
override ControlFlowNode asCfgNode() {
|
||||
this = TCfgNode(result)
|
||||
}
|
||||
override EssaVariable asVariable() { none() }
|
||||
|
||||
override EssaVariable asVariable() {
|
||||
none()
|
||||
}
|
||||
override string toString() { result = this.asAstNode().toString() }
|
||||
|
||||
override string toString() {
|
||||
result = this.asAstNode().toString()
|
||||
}
|
||||
override Scope getScope() { result = this.asCfgNode().getScope() }
|
||||
|
||||
override Scope getScope() {
|
||||
result = this.asCfgNode().getScope()
|
||||
}
|
||||
|
||||
override BasicBlock getBasicBlock() {
|
||||
result = this.asCfgNode().getBasicBlock()
|
||||
}
|
||||
|
||||
override Location getLocation() {
|
||||
result = this.asCfgNode().getLocation()
|
||||
}
|
||||
override BasicBlock getBasicBlock() { result = this.asCfgNode().getBasicBlock() }
|
||||
|
||||
override Location getLocation() { result = this.asCfgNode().getLocation() }
|
||||
}
|
||||
|
||||
class EssaNode extends Node, TEssaNode {
|
||||
override ControlFlowNode asCfgNode() { none() }
|
||||
|
||||
override ControlFlowNode asCfgNode() {
|
||||
none()
|
||||
}
|
||||
override EssaVariable asVariable() { this = TEssaNode(result) }
|
||||
|
||||
override EssaVariable asVariable() {
|
||||
this = TEssaNode(result)
|
||||
}
|
||||
override string toString() { result = this.asVariable().toString() }
|
||||
|
||||
override string toString() {
|
||||
result = this.asVariable().toString()
|
||||
}
|
||||
|
||||
override Scope getScope() {
|
||||
result = this.asVariable().getScope()
|
||||
}
|
||||
override Scope getScope() { result = this.asVariable().getScope() }
|
||||
|
||||
override BasicBlock getBasicBlock() {
|
||||
result = this.asVariable().getDefinition().getBasicBlock()
|
||||
}
|
||||
|
||||
override Location getLocation() {
|
||||
result = this.asVariable().getDefinition().getLocation()
|
||||
}
|
||||
|
||||
override Location getLocation() { result = this.asVariable().getDefinition().getLocation() }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class DataFlowType extends TaintKind {
|
||||
|
||||
DataFlowType() {
|
||||
this = "Data flow" and
|
||||
this = "Data flow" and
|
||||
exists(DataFlow::Configuration c)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) {
|
||||
dictnode.(DictNode).getAValue() = itemnode
|
||||
or
|
||||
@@ -829,7 +742,7 @@ private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictn
|
||||
dictnode.(CallNode).getArgByName(_) = itemnode
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate sequence_construct(ControlFlowNode itemnode, ControlFlowNode seqnode) {
|
||||
seqnode.isLoad() and
|
||||
(
|
||||
@@ -841,13 +754,11 @@ private predicate sequence_construct(ControlFlowNode itemnode, ControlFlowNode s
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/* A call to construct a sequence from a sequence or iterator*/
|
||||
pragma [noinline]
|
||||
pragma[noinline]
|
||||
private predicate sequence_call(ControlFlowNode fromnode, CallNode tonode) {
|
||||
tonode.getArg(0) = fromnode and
|
||||
exists(ControlFlowNode cls |
|
||||
cls = tonode.getFunction() |
|
||||
exists(ControlFlowNode cls | cls = tonode.getFunction() |
|
||||
cls.pointsTo(ObjectInternal::builtin("list"))
|
||||
or
|
||||
cls.pointsTo(ObjectInternal::builtin("tuple"))
|
||||
@@ -855,4 +766,3 @@ private predicate sequence_call(ControlFlowNode fromnode, CallNode tonode) {
|
||||
cls.pointsTo(ObjectInternal::builtin("set"))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ class EssaVariable extends TEssaDefinition {
|
||||
result = "SSA variable " + this.getName()
|
||||
}
|
||||
|
||||
/** Gets a string representation of this variable.
|
||||
/** Gets a string representation of this variable.
|
||||
* WARNING: The format of this may change and it may be very inefficient to compute.
|
||||
* To used for debugging and testing only.
|
||||
*/
|
||||
@@ -77,7 +77,7 @@ class EssaVariable extends TEssaDefinition {
|
||||
|
||||
}
|
||||
|
||||
/* Helper for location_string
|
||||
/* Helper for location_string
|
||||
* NOTE: This is Python specific, to make `getRepresentation()` portable will require further work.
|
||||
*/
|
||||
private int exception_handling(BasicBlock b) {
|
||||
@@ -161,10 +161,10 @@ abstract class EssaDefinition extends TEssaDefinition {
|
||||
abstract predicate reachesEndOfBlock(BasicBlock b);
|
||||
|
||||
/** Gets the location of a control flow node that is indicative of this definition.
|
||||
* Since definitions may occur on edges of the control flow graph, the given location may
|
||||
* Since definitions may occur on edges of the control flow graph, the given location may
|
||||
* be imprecise.
|
||||
* Distinct `EssaDefinitions` may return the same ControlFlowNode even for
|
||||
* the same variable.
|
||||
* the same variable.
|
||||
*/
|
||||
abstract Location getLocation();
|
||||
|
||||
@@ -182,9 +182,9 @@ abstract class EssaDefinition extends TEssaDefinition {
|
||||
|
||||
}
|
||||
|
||||
/** An ESSA definition corresponding to an edge refinement of the underlying variable.
|
||||
/** An ESSA definition corresponding to an edge refinement of the underlying variable.
|
||||
* For example, the edges leaving a test on a variable both represent refinements of that
|
||||
* variable. On one edge the test is true, on the other it is false.
|
||||
* variable. On one edge the test is true, on the other it is false.
|
||||
*/
|
||||
class EssaEdgeRefinement extends EssaDefinition, TEssaEdgeDefinition {
|
||||
|
||||
@@ -361,7 +361,7 @@ class PhiFunction extends EssaDefinition, TPhiFunction {
|
||||
}
|
||||
|
||||
private EssaEdgeRefinement piInputDefinition(EssaVariable input) {
|
||||
input = this.getAnInput() and
|
||||
input = this.getAnInput() and
|
||||
result = input.getDefinition()
|
||||
or
|
||||
input = this.getAnInput() and result = input.getDefinition().(PhiFunction).piInputDefinition(_)
|
||||
@@ -864,4 +864,3 @@ class PyEdgeRefinement extends EssaEdgeRefinement {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/** Provides classes and predicates for determining the uses and definitions of
|
||||
/** Provides classes and predicates for determining the uses and definitions of
|
||||
* variables for ESSA form.
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,7 @@ cached module SsaSource {
|
||||
|
||||
/** Holds if `v` is defined by a with statement. */
|
||||
cached predicate with_definition(Variable v, ControlFlowNode defn) {
|
||||
exists(With with, Name var |
|
||||
exists(With with, Name var |
|
||||
with.getOptionalVars() = var and
|
||||
var.getAFlowNode() = defn |
|
||||
var = v.getAStore()
|
||||
@@ -39,7 +39,11 @@ cached module SsaSource {
|
||||
|
||||
/** Holds if `v` is defined by multiple assignment at `defn`. */
|
||||
cached predicate multi_assignment_definition(Variable v, ControlFlowNode defn, int n, SequenceNode lhs) {
|
||||
defn.(NameNode).defines(v) and
|
||||
(
|
||||
defn.(NameNode).defines(v)
|
||||
or
|
||||
defn.(StarredNode).getValue().(NameNode).defines(v)
|
||||
) and
|
||||
not exists(defn.(DefinitionNode).getValue()) and
|
||||
lhs.getElement(n) = defn and
|
||||
lhs.getBasicBlock().dominates(defn.getBasicBlock())
|
||||
|
||||
@@ -749,4 +749,9 @@ module ClassValue {
|
||||
result = TBuiltinClassObject(Builtin::builtin("NameError"))
|
||||
}
|
||||
|
||||
/** Get the `ClassValue` for the `ImportError` class. */
|
||||
ClassValue importError() {
|
||||
result = TBuiltinClassObject(Builtin::builtin("ImportError"))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
automatic_locations: true
|
||||
semmle-extractor-options: --lang=2
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
automatic_locations: true
|
||||
semmle-extractor-options: --lang=2 --max-import-depth=1
|
||||
|
||||
@@ -1 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=0
|
||||
semmle-extractor-options: --lang=3 --max-import-depth=0
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
semmle-extractor-options: -p ../../lib/ --max-import-depth=3
|
||||
semmle-extractor-options: --lang=3 -p ../../lib/ --max-import-depth=3
|
||||
optimize: true
|
||||
|
||||
@@ -1 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=3
|
||||
semmle-extractor-options: --lang=3 --max-import-depth=3
|
||||
|
||||
@@ -1 +1 @@
|
||||
semmle-extractor-options: --respect-init=true
|
||||
semmle-extractor-options: --lang=3 --respect-init=true
|
||||
|
||||
@@ -1 +1 @@
|
||||
automatic_locations: true
|
||||
semmle-extractor-options: --lang=3
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
semmle-extractor-options: --max-import-depth=4
|
||||
semmle-extractor-options: --lang=3 --max-import-depth=4
|
||||
optimize: true
|
||||
|
||||
27
python/ql/test/3/library-tests/taint/unpacking/Taint.qll
Normal file
27
python/ql/test/3/library-tests/taint/unpacking/Taint.qll
Normal file
@@ -0,0 +1,27 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
class SimpleSource extends TaintSource {
|
||||
SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
|
||||
override string toString() { result = "taint source" }
|
||||
}
|
||||
|
||||
class ListSource extends TaintSource {
|
||||
ListSource() { this.(NameNode).getId() = "TAINTED_LIST" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind }
|
||||
|
||||
override string toString() { result = "list taint source" }
|
||||
}
|
||||
|
||||
class DictSource extends TaintSource {
|
||||
DictSource() { this.(NameNode).getId() = "TAINTED_DICT" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind }
|
||||
|
||||
override string toString() { result = "dict taint source" }
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
| test.py:11 | extended_unpacking | first | externally controlled string |
|
||||
| test.py:11 | extended_unpacking | last | externally controlled string |
|
||||
| test.py:11 | extended_unpacking | rest | [externally controlled string] |
|
||||
| test.py:16 | also_allowed | a | [externally controlled string] |
|
||||
| test.py:24 | also_allowed | b | NO TAINT |
|
||||
| test.py:24 | also_allowed | c | NO TAINT |
|
||||
| test.py:31 | nested | x | externally controlled string |
|
||||
| test.py:31 | nested | xs | [externally controlled string] |
|
||||
| test.py:31 | nested | ys | [externally controlled string] |
|
||||
18
python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql
Normal file
18
python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import Taint
|
||||
|
||||
from Call call, Expr arg, string taint_string
|
||||
where
|
||||
call.getLocation().getFile().getShortName() = "test.py" and
|
||||
call.getFunc().(Name).getId() = "test" and
|
||||
arg = call.getAnArg() and
|
||||
(
|
||||
not exists(TaintedNode tainted | tainted.getAstNode() = arg) and
|
||||
taint_string = "NO TAINT"
|
||||
or
|
||||
exists(TaintedNode tainted | tainted.getAstNode() = arg |
|
||||
taint_string = tainted.getTaintKind().toString()
|
||||
)
|
||||
)
|
||||
select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), taint_string
|
||||
31
python/ql/test/3/library-tests/taint/unpacking/test.py
Normal file
31
python/ql/test/3/library-tests/taint/unpacking/test.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# Extended Iterable Unpacking -- PEP 3132
|
||||
# https://www.python.org/dev/peps/pep-3132/
|
||||
|
||||
|
||||
def test(*args):
|
||||
pass
|
||||
|
||||
|
||||
def extended_unpacking():
|
||||
first, *rest, last = TAINTED_LIST
|
||||
test(first, rest, last)
|
||||
|
||||
|
||||
def also_allowed():
|
||||
*a, = TAINTED_LIST
|
||||
test(a)
|
||||
|
||||
# for b, *c in [(1, 2, 3), (4, 5, 6, 7)]:
|
||||
# print(c)
|
||||
# i=0; c=[2,3]
|
||||
# i=1; c=[5,6,7]
|
||||
|
||||
for b, *c in [TAINTED_LIST, TAINTED_LIST]:
|
||||
test(b, c) # TODO: mark `c` as [taint]
|
||||
|
||||
def nested():
|
||||
l = TAINTED_LIST
|
||||
ll = [l,l]
|
||||
|
||||
[[x, *xs], ys] = ll
|
||||
test(x, xs, ys)
|
||||
@@ -1 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=2
|
||||
semmle-extractor-options: --lang=3 --max-import-depth=2
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
automatic_locations: true
|
||||
semmle-extractor-options: --max-import-depth=1
|
||||
semmle-extractor-options: --lang=3 --max-import-depth=1
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: --max-import-depth=1
|
||||
automatic_locations: true
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
| Taint [externally controlled string] | test.py:29 | test.py:29:9:29:25 | Subscript | | --> | Taint [externally controlled string] | test.py:32 | test.py:32:16:32:16 | c | |
|
||||
| Taint [externally controlled string] | test.py:30 | test.py:30:9:30:20 | tainted_list | | --> | Taint [externally controlled string] | test.py:30 | test.py:30:9:30:27 | Attribute() | |
|
||||
| Taint [externally controlled string] | test.py:30 | test.py:30:9:30:27 | Attribute() | | --> | Taint [externally controlled string] | test.py:32 | test.py:32:19:32:19 | d | |
|
||||
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:22:32:22 | e | |
|
||||
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:25:32:25 | f | |
|
||||
| Taint [externally controlled string] | test.py:31 | test.py:31:15:31:26 | tainted_list | | --> | Taint externally controlled string | test.py:32 | test.py:32:28:32:28 | g | |
|
||||
| Taint [externally controlled string] | test.py:33 | test.py:33:14:33:25 | tainted_list | | --> | Taint externally controlled string | test.py:33 | test.py:33:5:33:26 | For | |
|
||||
| Taint [externally controlled string] | test.py:35 | test.py:35:14:35:35 | reversed() | | --> | Taint externally controlled string | test.py:35 | test.py:35:5:35:36 | For | |
|
||||
| Taint [externally controlled string] | test.py:35 | test.py:35:23:35:34 | tainted_list | | --> | Taint [externally controlled string] | test.py:35 | test.py:35:14:35:35 | reversed() | |
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
| test.py:32 | test_access | b | externally controlled string |
|
||||
| test.py:32 | test_access | c | [externally controlled string] |
|
||||
| test.py:32 | test_access | d | [externally controlled string] |
|
||||
| test.py:32 | test_access | e | NO TAINT |
|
||||
| test.py:32 | test_access | f | NO TAINT |
|
||||
| test.py:32 | test_access | g | NO TAINT |
|
||||
| test.py:32 | test_access | e | externally controlled string |
|
||||
| test.py:32 | test_access | f | externally controlled string |
|
||||
| test.py:32 | test_access | g | externally controlled string |
|
||||
| test.py:34 | test_access | h | externally controlled string |
|
||||
| test.py:36 | test_access | i | externally controlled string |
|
||||
| test.py:43 | test_dict_access | a | externally controlled string |
|
||||
|
||||
@@ -28,7 +28,7 @@ def test_access():
|
||||
b = tainted_list[x]
|
||||
c = tainted_list[y:z]
|
||||
d = tainted_list.copy()
|
||||
e, f, g = tainted_list # TODO: currently not handled
|
||||
e, f, g = tainted_list
|
||||
test(a, b, c, d, e, f, g)
|
||||
for h in tainted_list:
|
||||
test(h)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
WARNING: Type CallContext has been deprecated and may be removed in future (Contexts.ql:6,6-17)
|
||||
WARNING: Type CallContext has been deprecated and may be removed in future (Contexts.ql:7,14-25)
|
||||
| assignment.py:1 | p0 = simple.test | Function test |
|
||||
| assignment.py:1 | p1 = simple.test | Function test |
|
||||
| assignment.py:1 | p2 = simple.test | Function test |
|
||||
| carrier.py:4 | p1 = explicit.carrier | Function __init__ |
|
||||
| carrier.py:4 | p1 = simple.test | Function __init__ |
|
||||
| carrier.py:10 | p0.attr = simple.test | Function get_attr |
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
| assignment.py:5 | SOURCE | assignment.py:5 | Taint simple.test | a |
|
||||
| assignment.py:7 | a | assignment.py:7 | Taint simple.test | b |
|
||||
| assignment.py:13 | SOURCE | assignment.py:13 | Taint simple.test | t1 |
|
||||
| assignment.py:13 | SOURCE | assignment.py:13 | Taint simple.test | t2 |
|
||||
| carrier.py:4 | ParameterDefinition | carrier.py:4 | Taint explicit.carrier | arg |
|
||||
| carrier.py:4 | ParameterDefinition | carrier.py:4 | Taint simple.test | arg |
|
||||
| carrier.py:10 | ParameterDefinition | carrier.py:10 | Taint .attr = simple.test | self |
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
| assignment.py:5 | SOURCE | simple.test |
|
||||
| assignment.py:13 | SOURCE | simple.test |
|
||||
| carrier.py:17 | SOURCE | simple.test |
|
||||
| carrier.py:21 | TAINT_CARRIER_SOURCE | explicit.carrier |
|
||||
| carrier.py:25 | SOURCE | simple.test |
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
| scissors | rockpaperscissors.py:29 | SCISSORS | | --> | scissors | rockpaperscissors.py:31 | x | |
|
||||
| scissors | rockpaperscissors.py:30 | x | | --> | paper | rockpaperscissors.py:30 | Attribute() | |
|
||||
| scissors | rockpaperscissors.py:31 | x | | --> | scissors | rockpaperscissors.py:6 | arg | p0 = scissors |
|
||||
| sequence of simple.test | assignment.py:13 | Tuple | | --> | sequence of [simple.test] | assignment.py:13 | Tuple | |
|
||||
| sequence of simple.test | test.py:168 | List | | --> | sequence of simple.test | test.py:170 | l | |
|
||||
| sequence of simple.test | test.py:168 | List | | --> | sequence of simple.test | test.py:174 | l | |
|
||||
| sequence of simple.test | test.py:170 | SSA variable x | | --> | sequence of simple.test | test.py:172 | x | |
|
||||
@@ -112,6 +113,14 @@
|
||||
| sequence of simple.test | test.py:208 | List | | --> | sequence of simple.test | test.py:209 | seq | |
|
||||
| sequence of simple.test | test.py:209 | seq | | --> | simple.test | test.py:209 | For | |
|
||||
| sequence of simple.test | test.py:213 | flow_in_generator() | | --> | simple.test | test.py:213 | For | |
|
||||
| simple.test | assignment.py:5 | SOURCE | | --> | sequence of simple.test | assignment.py:5 | Tuple | |
|
||||
| simple.test | assignment.py:5 | SOURCE | | --> | simple.test | assignment.py:6 | a | |
|
||||
| simple.test | assignment.py:5 | SOURCE | | --> | simple.test | assignment.py:7 | a | |
|
||||
| simple.test | assignment.py:7 | a | | --> | sequence of simple.test | assignment.py:7 | Tuple | |
|
||||
| simple.test | assignment.py:7 | a | | --> | simple.test | assignment.py:8 | b | |
|
||||
| simple.test | assignment.py:13 | SOURCE | | --> | sequence of simple.test | assignment.py:13 | Tuple | |
|
||||
| simple.test | assignment.py:13 | SOURCE | | --> | simple.test | assignment.py:14 | t1 | |
|
||||
| simple.test | assignment.py:13 | SOURCE | | --> | simple.test | assignment.py:14 | t2 | |
|
||||
| simple.test | carrier.py:4 | arg | p1 = simple.test | --> | simple.test | carrier.py:5 | arg | p1 = simple.test |
|
||||
| simple.test | carrier.py:17 | SOURCE | | --> | .attr = simple.test | carrier.py:17 | ImplicitCarrier() | |
|
||||
| simple.test | carrier.py:17 | SOURCE | | --> | simple.test | carrier.py:4 | arg | p1 = simple.test |
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
| assignment.py:6 | swap_taint | a | simple.test |
|
||||
| assignment.py:6 | swap_taint | b | NO TAINT |
|
||||
| assignment.py:8 | swap_taint | a | NO TAINT |
|
||||
| assignment.py:8 | swap_taint | b | simple.test |
|
||||
| assignment.py:14 | nested_assignment | s1 | NO TAINT |
|
||||
| assignment.py:14 | nested_assignment | s2 | NO TAINT |
|
||||
| assignment.py:14 | nested_assignment | t1 | simple.test |
|
||||
| assignment.py:14 | nested_assignment | t2 | simple.test |
|
||||
18
python/ql/test/library-tests/taint/general/TestTaint.ql
Normal file
18
python/ql/test/library-tests/taint/general/TestTaint.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import TaintLib
|
||||
|
||||
from Call call, Expr arg, string taint_string
|
||||
where
|
||||
call.getLocation().getFile().getShortName() = "assignment.py" and
|
||||
call.getFunc().(Name).getId() = "test" and
|
||||
arg = call.getAnArg() and
|
||||
(
|
||||
not exists(TaintedNode tainted | tainted.getAstNode() = arg) and
|
||||
taint_string = "NO TAINT"
|
||||
or
|
||||
exists(TaintedNode tainted | tainted.getAstNode() = arg |
|
||||
taint_string = tainted.getTaintKind().toString()
|
||||
)
|
||||
)
|
||||
select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), taint_string
|
||||
@@ -1,3 +1,9 @@
|
||||
| assignment.py:5 | a_0 | assignment.py:5 | Taint simple.test |
|
||||
| assignment.py:6 | a_1 | assignment.py:6 | Taint simple.test |
|
||||
| assignment.py:7 | b_1 | assignment.py:7 | Taint simple.test |
|
||||
| assignment.py:13 | t1_0 | assignment.py:13 | Taint simple.test |
|
||||
| assignment.py:13 | t2_0 | assignment.py:13 | Taint simple.test |
|
||||
| assignment.py:14 | t1_1 | assignment.py:14 | Taint simple.test |
|
||||
| carrier.py:4 | arg_0 | carrier.py:4 | Taint explicit.carrier |
|
||||
| carrier.py:4 | arg_0 | carrier.py:4 | Taint simple.test |
|
||||
| carrier.py:5 | self_1 | carrier.py:5 | Taint .attr = explicit.carrier |
|
||||
|
||||
14
python/ql/test/library-tests/taint/general/assignment.py
Normal file
14
python/ql/test/library-tests/taint/general/assignment.py
Normal file
@@ -0,0 +1,14 @@
|
||||
def test(*args):
|
||||
pass
|
||||
|
||||
def swap_taint():
|
||||
a, b = SOURCE, "safe"
|
||||
test(a, b)
|
||||
a, b = b, a
|
||||
test(a, b)
|
||||
|
||||
def nested_assignment():
|
||||
# A contrived example, that is a bit silly (and is not even iterable unpacking).
|
||||
# We do handle this case though.
|
||||
((t1, s1), t2, s2) = ((SOURCE, "safe"), SOURCE, "safe")
|
||||
test(t1, s1, t2, s2)
|
||||
27
python/ql/test/library-tests/taint/unpacking/Taint.qll
Normal file
27
python/ql/test/library-tests/taint/unpacking/Taint.qll
Normal file
@@ -0,0 +1,27 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import semmle.python.security.strings.Untrusted
|
||||
|
||||
class SimpleSource extends TaintSource {
|
||||
SimpleSource() { this.(NameNode).getId() = "TAINTED_STRING" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
|
||||
|
||||
override string toString() { result = "taint source" }
|
||||
}
|
||||
|
||||
class ListSource extends TaintSource {
|
||||
ListSource() { this.(NameNode).getId() = "TAINTED_LIST" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringSequenceKind }
|
||||
|
||||
override string toString() { result = "list taint source" }
|
||||
}
|
||||
|
||||
class DictSource extends TaintSource {
|
||||
DictSource() { this.(NameNode).getId() = "TAINTED_DICT" }
|
||||
|
||||
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringDictKind }
|
||||
|
||||
override string toString() { result = "dict taint source" }
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | |
|
||||
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | |
|
||||
| Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | | --> | Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:22:23:22 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint [externally controlled string] | test.py:23 | test.py:23:25:23:25 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:10:23:11 | a1 | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:14:23:15 | a2 | |
|
||||
| Taint [[externally controlled string]] | test.py:22 | test.py:22:28:22:29 | ll | | --> | Taint externally controlled string | test.py:23 | test.py:23:18:23:19 | a3 | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:22:27:22 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint [externally controlled string] | test.py:27 | test.py:27:25:27:25 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:10:27:11 | a1 | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:14:27:15 | a2 | |
|
||||
| Taint [[externally controlled string]] | test.py:26 | test.py:26:28:26:29 | ll | | --> | Taint externally controlled string | test.py:27 | test.py:27:18:27:19 | a3 | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:22:31:22 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint [externally controlled string] | test.py:31 | test.py:31:25:31:25 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:10:31:11 | a1 | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:14:31:15 | a2 | |
|
||||
| Taint [[externally controlled string]] | test.py:30 | test.py:30:28:30:29 | ll | | --> | Taint externally controlled string | test.py:31 | test.py:31:18:31:19 | a3 | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:10:48:10 | a | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:13:48:13 | b | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:16:48:16 | c | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:19:48:19 | d | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:22:48:22 | e | |
|
||||
| Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | | --> | Taint externally controlled string | test.py:48 | test.py:48:25:48:25 | f | |
|
||||
| Taint [externally controlled string] | test.py:6 | test.py:6:9:6:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | |
|
||||
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:10:8:10 | a | |
|
||||
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:13:8:13 | b | |
|
||||
| Taint [externally controlled string] | test.py:7 | test.py:7:15:7:15 | l | | --> | Taint externally controlled string | test.py:8 | test.py:8:16:8:16 | c | |
|
||||
| Taint [externally controlled string] | test.py:12 | test.py:12:9:12:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | |
|
||||
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:10:14:10 | a | |
|
||||
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:13:14:13 | b | |
|
||||
| Taint [externally controlled string] | test.py:13 | test.py:13:17:13:17 | l | | --> | Taint externally controlled string | test.py:14 | test.py:14:16:14:16 | c | |
|
||||
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:11:19:11 | l | |
|
||||
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:14:19:14 | l | |
|
||||
| Taint [externally controlled string] | test.py:18 | test.py:18:9:18:20 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:19 | test.py:19:17:19:17 | l | |
|
||||
| Taint [externally controlled string] | test.py:19 | test.py:19:11:19:11 | l | | --> | Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | |
|
||||
| Taint [externally controlled string] | test.py:19 | test.py:19:14:19:14 | l | | --> | Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | |
|
||||
| Taint [externally controlled string] | test.py:19 | test.py:19:17:19:17 | l | | --> | Taint [[externally controlled string]] | test.py:19 | test.py:19:10:19:18 | List | |
|
||||
| Taint [externally controlled string] | test.py:43 | test.py:43:20:43:31 | TAINTED_LIST | | --> | Taint [externally controlled string] | test.py:47 | test.py:47:28:47:39 | tainted_list | |
|
||||
| Taint [externally controlled string] | test.py:47 | test.py:47:28:47:39 | tainted_list | | --> | Taint [[externally controlled string]] | test.py:47 | test.py:47:28:47:54 | Tuple | |
|
||||
| Taint [externally controlled string] | test.py:55 | test.py:55:27:55:38 | TAINTED_LIST | | --> | Taint [[externally controlled string]] | test.py:55 | test.py:55:25:55:40 | List | |
|
||||
11
python/ql/test/library-tests/taint/unpacking/TestStep.ql
Normal file
11
python/ql/test/library-tests/taint/unpacking/TestStep.ql
Normal file
@@ -0,0 +1,11 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import Taint
|
||||
|
||||
from TaintedNode n, TaintedNode s
|
||||
where
|
||||
n.getLocation().getFile().getShortName() = "test.py" and
|
||||
s.getLocation().getFile().getShortName() = "test.py" and
|
||||
s = n.getASuccessor()
|
||||
select "Taint " + n.getTaintKind(), n.getLocation().toString(), n.getAstNode(), n.getContext(),
|
||||
" --> ", "Taint " + s.getTaintKind(), s.getLocation().toString(), s.getAstNode(), s.getContext()
|
||||
@@ -0,0 +1,33 @@
|
||||
| test.py:8 | unpacking | a | externally controlled string |
|
||||
| test.py:8 | unpacking | b | externally controlled string |
|
||||
| test.py:8 | unpacking | c | externally controlled string |
|
||||
| test.py:14 | unpacking_to_list | a | externally controlled string |
|
||||
| test.py:14 | unpacking_to_list | b | externally controlled string |
|
||||
| test.py:14 | unpacking_to_list | c | externally controlled string |
|
||||
| test.py:23 | nested | a1 | externally controlled string |
|
||||
| test.py:23 | nested | a2 | externally controlled string |
|
||||
| test.py:23 | nested | a3 | externally controlled string |
|
||||
| test.py:23 | nested | b | [externally controlled string] |
|
||||
| test.py:23 | nested | c | [externally controlled string] |
|
||||
| test.py:27 | nested | a1 | externally controlled string |
|
||||
| test.py:27 | nested | a2 | externally controlled string |
|
||||
| test.py:27 | nested | a3 | externally controlled string |
|
||||
| test.py:27 | nested | b | [externally controlled string] |
|
||||
| test.py:27 | nested | c | [externally controlled string] |
|
||||
| test.py:31 | nested | a1 | externally controlled string |
|
||||
| test.py:31 | nested | a2 | externally controlled string |
|
||||
| test.py:31 | nested | a3 | externally controlled string |
|
||||
| test.py:31 | nested | b | [externally controlled string] |
|
||||
| test.py:31 | nested | c | [externally controlled string] |
|
||||
| test.py:38 | unpack_from_set | a | NO TAINT |
|
||||
| test.py:38 | unpack_from_set | b | NO TAINT |
|
||||
| test.py:38 | unpack_from_set | c | NO TAINT |
|
||||
| test.py:48 | contrived_1 | a | externally controlled string |
|
||||
| test.py:48 | contrived_1 | b | externally controlled string |
|
||||
| test.py:48 | contrived_1 | c | externally controlled string |
|
||||
| test.py:48 | contrived_1 | d | externally controlled string |
|
||||
| test.py:48 | contrived_1 | e | externally controlled string |
|
||||
| test.py:48 | contrived_1 | f | externally controlled string |
|
||||
| test.py:56 | contrived_2 | a | NO TAINT |
|
||||
| test.py:56 | contrived_2 | b | NO TAINT |
|
||||
| test.py:56 | contrived_2 | c | NO TAINT |
|
||||
18
python/ql/test/library-tests/taint/unpacking/TestTaint.ql
Normal file
18
python/ql/test/library-tests/taint/unpacking/TestTaint.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
import python
|
||||
import semmle.python.security.TaintTracking
|
||||
import Taint
|
||||
|
||||
from Call call, Expr arg, string taint_string
|
||||
where
|
||||
call.getLocation().getFile().getShortName() = "test.py" and
|
||||
call.getFunc().(Name).getId() = "test" and
|
||||
arg = call.getAnArg() and
|
||||
(
|
||||
not exists(TaintedNode tainted | tainted.getAstNode() = arg) and
|
||||
taint_string = "NO TAINT"
|
||||
or
|
||||
exists(TaintedNode tainted | tainted.getAstNode() = arg |
|
||||
taint_string = tainted.getTaintKind().toString()
|
||||
)
|
||||
)
|
||||
select arg.getLocation().toString(), call.getScope().(Function).getName(), arg.toString(), taint_string
|
||||
58
python/ql/test/library-tests/taint/unpacking/test.py
Normal file
58
python/ql/test/library-tests/taint/unpacking/test.py
Normal file
@@ -0,0 +1,58 @@
|
||||
def test(*args):
|
||||
pass
|
||||
|
||||
|
||||
def unpacking():
|
||||
l = TAINTED_LIST
|
||||
a, b, c = l
|
||||
test(a, b, c)
|
||||
|
||||
|
||||
def unpacking_to_list():
|
||||
l = TAINTED_LIST
|
||||
[a, b, c] = l
|
||||
test(a, b, c)
|
||||
|
||||
|
||||
def nested():
|
||||
l = TAINTED_LIST
|
||||
ll = [l, l, l]
|
||||
|
||||
# list
|
||||
[[a1, a2, a3], b, c] = ll
|
||||
test(a1, a2, a3, b, c)
|
||||
|
||||
# tuple
|
||||
((a1, a2, a3), b, c) = ll
|
||||
test(a1, a2, a3, b, c)
|
||||
|
||||
# mixed
|
||||
[(a1, a2, a3), b, c] = ll
|
||||
test(a1, a2, a3, b, c)
|
||||
|
||||
|
||||
def unpack_from_set():
|
||||
# no guarantee on ordering ... don't know why you would ever do this
|
||||
a, b, c = {"foo", "bar", TAINTED_STRING}
|
||||
# either all should be tainted, or none of them
|
||||
test(a, b, c)
|
||||
|
||||
|
||||
def contrived_1():
|
||||
# A contrived example. Don't know why anyone would ever actually do this.
|
||||
tainted_list = TAINTED_LIST
|
||||
no_taint_list = [1,2,3]
|
||||
|
||||
# We don't handle this case currently, since we mark `d`, `e` and `f` as tainted.
|
||||
(a, b, c), (d, e, f) = tainted_list, no_taint_list
|
||||
test(a, b, c, d, e, f)
|
||||
|
||||
|
||||
def contrived_2():
|
||||
# A contrived example. Don't know why anyone would ever actually do this.
|
||||
|
||||
# We currently only handle taint nested 2 levels.
|
||||
[[[ (a,b,c) ]]] = [[[ TAINTED_LIST ]]]
|
||||
test(a, b, c)
|
||||
|
||||
# For Python 3, see https://www.python.org/dev/peps/pep-3132/
|
||||
@@ -1,2 +1 @@
|
||||
semmle-extractor-options: -R . --filter=include:**/*.thrift --filter exclude:**/src_archive/**
|
||||
automatic_locations: true
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
semmle-extractor-options: --max-import-depth=3 --lang=3 -p ../../../query-tests/Security/lib/
|
||||
semmle-extractor-options: --max-import-depth=3 -p ../../../query-tests/Security/lib/
|
||||
optimize: true
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user