Python: Autoformat security.

This commit is contained in:
Taus Brock-Nannestad
2020-03-20 16:36:48 +01:00
parent 4852bb7355
commit 51f1cf020c
21 changed files with 186 additions and 448 deletions

View File

@@ -5,17 +5,12 @@ import semmle.python.dataflow.Files
import semmle.python.web.Http
module ClearTextStorage {
abstract class Sink extends TaintSink {
override predicate sinks(TaintKind kind) {
kind instanceof SensitiveData
}
override predicate sinks(TaintKind kind) { kind instanceof SensitiveData }
}
class CookieStorageSink extends Sink {
CookieStorageSink() {
any(CookieSet cookie).getValue() = this
}
CookieStorageSink() { any(CookieSet cookie).getValue() = this }
}
class FileStorageSink extends Sink {
@@ -23,20 +18,17 @@ module ClearTextStorage {
exists(CallNode call, AttrNode meth, string name |
any(OpenFile fd).taints(meth.getObject(name)) and
call.getFunction() = meth and
call.getAnArg() = this |
call.getAnArg() = this
|
name = "write"
)
}
}
}
module ClearTextLogging {
abstract class Sink extends TaintSink {
override predicate sinks(TaintKind kind) {
kind instanceof SensitiveData
}
override predicate sinks(TaintKind kind) { kind instanceof SensitiveData }
}
class PrintSink extends Sink {
@@ -53,7 +45,8 @@ module ClearTextLogging {
exists(CallNode call, AttrNode meth, string name |
call.getFunction() = meth and
meth.getObject(name).(NameNode).getId().matches("logg%") and
call.getAnArg() = this |
call.getAnArg() = this
|
name = "error" or
name = "warn" or
name = "warning" or
@@ -62,5 +55,4 @@ module ClearTextLogging {
)
}
}
}

View File

@@ -1,49 +1,32 @@
import python
import semmle.python.security.TaintTracking
private import semmle.python.security.SensitiveData
private import semmle.crypto.Crypto as CryptoLib
abstract class WeakCryptoSink extends TaintSink {
override predicate sinks(TaintKind taint) {
taint instanceof SensitiveData
}
override predicate sinks(TaintKind taint) { taint instanceof SensitiveData }
}
/** Modeling the 'pycrypto' package https://github.com/dlitz/pycrypto (latest release 2013) */
module Pycrypto {
ModuleValue cipher(string name) {
result = Module::named("Crypto.Cipher").attr(name)
}
ModuleValue cipher(string name) { result = Module::named("Crypto.Cipher").attr(name) }
class CipherInstance extends TaintKind {
string name;
CipherInstance() {
this = "Crypto.Cipher." + name and
this = "Crypto.Cipher." + name and
exists(cipher(name))
}
string getName() {
result = name
}
string getName() { result = name }
CryptoLib::CryptographicAlgorithm getAlgorithm() {
result.getName() = name
}
predicate isWeak() {
this.getAlgorithm().isWeak()
}
CryptoLib::CryptographicAlgorithm getAlgorithm() { result.getName() = name }
predicate isWeak() { this.getAlgorithm().isWeak() }
}
class CipherInstanceSource extends TaintSource {
CipherInstance instance;
CipherInstanceSource() {
@@ -53,18 +36,12 @@ module Pycrypto {
)
}
override string toString() {
result = "Source of " + instance
}
override predicate isSourceOf(TaintKind kind) {
kind = instance
}
override string toString() { result = "Source of " + instance }
override predicate isSourceOf(TaintKind kind) { kind = instance }
}
class PycryptoWeakCryptoSink extends WeakCryptoSink {
string name;
PycryptoWeakCryptoSink() {
@@ -77,36 +54,24 @@ module Pycrypto {
)
}
override string toString() {
result = "Use of weak crypto algorithm " + name
}
override string toString() { result = "Use of weak crypto algorithm " + name }
}
}
module Cryptography {
ModuleValue ciphers() {
result = Module::named("cryptography.hazmat.primitives.ciphers") and
result.isPackage()
}
class CipherClass extends ClassValue {
CipherClass() {
ciphers().attr("Cipher") = this
}
CipherClass() { ciphers().attr("Cipher") = this }
}
class AlgorithmClass extends ClassValue {
AlgorithmClass() { ciphers().attr("algorithms").attr(_) = this }
AlgorithmClass() {
ciphers().attr("algorithms").attr(_) = this
}
string getAlgorithmName() {
result = this.declaredAttribute("name").(StringValue).getText()
}
string getAlgorithmName() { result = this.declaredAttribute("name").(StringValue).getText() }
predicate isWeak() {
exists(CryptoLib::CryptographicAlgorithm algo |
@@ -117,61 +82,39 @@ module Cryptography {
}
class CipherInstance extends TaintKind {
AlgorithmClass cls;
CipherInstance() {
this = "cryptography.Cipher." + cls.getAlgorithmName()
}
CipherInstance() { this = "cryptography.Cipher." + cls.getAlgorithmName() }
AlgorithmClass getAlgorithm() {
result = cls
}
AlgorithmClass getAlgorithm() { result = cls }
predicate isWeak() {
cls.isWeak()
}
predicate isWeak() { cls.isWeak() }
override TaintKind getTaintOfMethodResult(string name) {
name = "encryptor" and
result.(Encryptor).getAlgorithm() = this.getAlgorithm()
}
}
class CipherSource extends TaintSource {
CipherSource() {
this.(CallNode).getFunction().pointsTo(any(CipherClass cls))
}
CipherSource() { this.(CallNode).getFunction().pointsTo(any(CipherClass cls)) }
override predicate isSourceOf(TaintKind kind) {
this.(CallNode).getArg(0).pointsTo().getClass() = kind.(CipherInstance).getAlgorithm()
}
override string toString() {
result = "cryptography.Cipher.source"
}
override string toString() { result = "cryptography.Cipher.source" }
}
class Encryptor extends TaintKind {
AlgorithmClass cls;
Encryptor() {
this = "cryptography.encryptor." + cls.getAlgorithmName()
}
AlgorithmClass getAlgorithm() {
result = cls
}
Encryptor() { this = "cryptography.encryptor." + cls.getAlgorithmName() }
AlgorithmClass getAlgorithm() { result = cls }
}
class CryptographyWeakCryptoSink extends WeakCryptoSink {
CryptographyWeakCryptoSink() {
exists(CallNode call, AttrNode method, Encryptor encryptor |
call.getAnArg() = this and
@@ -181,17 +124,11 @@ module Cryptography {
)
}
override string toString() {
result = "Use of weak crypto algorithm"
}
override string toString() { result = "Use of weak crypto algorithm" }
}
}
private class CipherConfig extends TaintTracking::Configuration {
CipherConfig() { this = "Crypto cipher config" }
override predicate isSource(TaintTracking::Source source) {
@@ -199,5 +136,4 @@ private class CipherConfig extends TaintTracking::Configuration {
or
source instanceof Cryptography::CipherSource
}
}

View File

@@ -7,43 +7,31 @@ import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Basic
private Value traceback_function(string name) {
result = Module::named("traceback").attr(name)
}
private Value traceback_function(string name) { result = Module::named("traceback").attr(name) }
/**
* This represents information relating to an exception, for instance the
* message, arguments or parts of the exception traceback.
*/
class ExceptionInfo extends StringKind {
ExceptionInfo() { this = "exception.info" }
ExceptionInfo() {
this = "exception.info"
}
override string repr() {
result = "exception info"
}
override string repr() { result = "exception info" }
}
/** A class representing sources of information about
/**
* A class representing sources of information about
* execution state exposed in tracebacks and the like.
*/
abstract class ErrorInfoSource extends TaintSource {}
abstract class ErrorInfoSource extends TaintSource { }
/**
* This kind represents exceptions themselves.
*/
class ExceptionKind extends TaintKind {
ExceptionKind() { this = "exception.kind" }
ExceptionKind() {
this = "exception.kind"
}
override string repr() {
result = "exception"
}
override string repr() { result = "exception" }
override TaintKind getTaintOfAttribute(string name) {
name = "args" and result instanceof ExceptionInfoSequence
@@ -57,7 +45,6 @@ class ExceptionKind extends TaintKind {
* `except` statement.
*/
class ExceptionSource extends ErrorInfoSource {
ExceptionSource() {
exists(ClassValue cls |
cls.getASuperType() = ClassValue::baseException() and
@@ -67,13 +54,9 @@ class ExceptionSource extends ErrorInfoSource {
this = any(ExceptStmt s).getName().getAFlowNode()
}
override string toString() {
result = "exception.source"
}
override string toString() { result = "exception.source" }
override predicate isSourceOf(TaintKind kind) {
kind instanceof ExceptionKind
}
override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionKind }
}
/**
@@ -81,18 +64,14 @@ class ExceptionSource extends ErrorInfoSource {
* for instance the contents of the `args` attribute, or the stack trace.
*/
class ExceptionInfoSequence extends SequenceKind {
ExceptionInfoSequence() {
this.getItem() instanceof ExceptionInfo
}
ExceptionInfoSequence() { this.getItem() instanceof ExceptionInfo }
}
/**
* Represents calls to functions in the `traceback` module that return
* sequences of exception information.
*/
class CallToTracebackFunction extends ErrorInfoSource {
CallToTracebackFunction() {
exists(string name |
name = "extract_tb" or
@@ -107,13 +86,9 @@ class CallToTracebackFunction extends ErrorInfoSource {
)
}
override string toString() {
result = "exception.info.sequence.source"
}
override string toString() { result = "exception.info.sequence.source" }
override predicate isSourceOf(TaintKind kind) {
kind instanceof ExceptionInfoSequence
}
override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfoSequence }
}
/**
@@ -121,16 +96,9 @@ class CallToTracebackFunction extends ErrorInfoSource {
* string of information about an exception.
*/
class FormattedTracebackSource extends ErrorInfoSource {
FormattedTracebackSource() { this = traceback_function("format_exc").getACall() }
FormattedTracebackSource() {
this = traceback_function("format_exc").getACall()
}
override string toString() { result = "exception.info.source" }
override string toString() {
result = "exception.info.source"
}
override predicate isSourceOf(TaintKind kind) {
kind instanceof ExceptionInfo
}
override predicate isSourceOf(TaintKind kind) { kind instanceof ExceptionInfo }
}

View File

@@ -1,8 +1,6 @@
import semmle.python.dataflow.Implementation
module TaintTrackingPaths {
predicate edge(TaintTrackingNode src, TaintTrackingNode dest, string label) {
exists(TaintTrackingNode source, TaintTrackingNode sink |
source.getConfiguration().hasFlowPath(source, sink) and
@@ -11,10 +9,8 @@ module TaintTrackingPaths {
dest.getASuccessor*() = sink
)
}
}
query predicate edges(TaintTrackingNode fromnode, TaintTrackingNode tonode) {
TaintTrackingPaths::edge(fromnode, tonode, _)
}

View File

@@ -1,4 +1,4 @@
import python
import semmle.python.security.TaintTracking
abstract class SqlInjectionSink extends TaintSink {}
abstract class SqlInjectionSink extends TaintSink { }

View File

@@ -20,7 +20,6 @@ import semmle.python.web.HttpRequest
* This is copied from the javascript library, but should be language independent.
*/
private module HeuristicNames {
/**
* Gets a regular expression that identifies strings that may indicate the presence of secret
* or trusted data.
@@ -32,8 +31,8 @@ private module HeuristicNames {
* user names or other account information.
*/
string maybeAccountInfo() {
result = "(?is).*acc(ou)?nt.*" or
result = "(?is).*(puid|username|userid).*"
result = "(?is).*acc(ou)?nt.*" or
result = "(?is).*(puid|username|userid).*"
}
/**
@@ -41,8 +40,8 @@ private module HeuristicNames {
* a password or an authorization key.
*/
string maybePassword() {
result = "(?is).*pass(wd|word|code|phrase)(?!.*question).*" or
result = "(?is).*(auth(entication|ori[sz]ation)?)key.*"
result = "(?is).*pass(wd|word|code|phrase)(?!.*question).*" or
result = "(?is).*(auth(entication|ori[sz]ation)?)key.*"
}
/**
@@ -51,7 +50,7 @@ private module HeuristicNames {
*/
string maybeCertificate() { result = "(?is).*(cert)(?!.*(format|name)).*" }
/**
/**
* Gets a regular expression that identifies strings that may indicate the presence
* of sensitive data, with `classification` describing the kind of sensitive data involved.
*/
@@ -78,35 +77,35 @@ private module HeuristicNames {
name.regexpMatch(HeuristicNames::maybeSensitive(result)) and
not name.regexpMatch(HeuristicNames::notSensitive())
}
}
abstract class SensitiveData extends TaintKind {
bindingset[this]
SensitiveData() { this = this }
}
module SensitiveData {
class Secret extends SensitiveData {
Secret() { this = "sensitive.data.secret" }
override string repr() { result = "a secret" }
}
class Id extends SensitiveData {
Id() { this = "sensitive.data.id" }
override string repr() { result = "an ID" }
}
class Password extends SensitiveData {
Password() { this = "sensitive.data.password" }
override string repr() { result = "a password" }
}
class Certificate extends SensitiveData {
Certificate() { this = "sensitive.data.certificate" }
override string repr() { result = "a certificate or key" }
}
@@ -115,53 +114,35 @@ module SensitiveData {
}
abstract class Source extends TaintSource {
abstract string repr();
}
private class SensitiveCallSource extends Source {
SensitiveData data;
SensitiveCallSource() {
exists(Value callee |
callee.getACall() = this |
data = fromFunction(callee)
)
exists(Value callee | callee.getACall() = this | data = fromFunction(callee))
}
override predicate isSourceOf(TaintKind kind) {
kind = data
}
override string repr() {
result = "a call returning " + data.repr()
}
override predicate isSourceOf(TaintKind kind) { kind = data }
override string repr() { result = "a call returning " + data.repr() }
}
/** An access to a variable or property that might contain sensitive data. */
private class SensitiveVariableAccess extends SensitiveData::Source {
SensitiveData data;
SensitiveVariableAccess() {
data = HeuristicNames::getSensitiveDataForName(this.(AttrNode).getName())
}
override predicate isSourceOf(TaintKind kind) {
kind = data
}
override string repr() {
result = "an attribute or property containing " + data.repr()
}
override predicate isSourceOf(TaintKind kind) { kind = data }
override string repr() { result = "an attribute or property containing " + data.repr() }
}
private class SensitiveRequestParameter extends SensitiveData::Source {
SensitiveData data;
SensitiveRequestParameter() {
@@ -172,16 +153,10 @@ module SensitiveData {
)
}
override predicate isSourceOf(TaintKind kind) {
kind = data
}
override string repr() {
result = "a request parameter containing " + data.repr()
}
override predicate isSourceOf(TaintKind kind) { kind = data }
override string repr() { result = "a request parameter containing " + data.repr() }
}
}
//Backwards compatibility

View File

@@ -1,2 +1,3 @@
/** For backwards compatibility */
import semmle.python.dataflow.TaintTracking

View File

@@ -3,13 +3,7 @@ import semmle.python.security.strings.Basic
/** Assume that taint flows from argument to result for *any* call */
class AnyCallStringFlow extends DataFlowExtension::DataFlowNode {
AnyCallStringFlow() { any(CallNode call).getAnArg() = this }
AnyCallStringFlow() {
any(CallNode call).getAnArg() = this
}
override ControlFlowNode getASuccessorNode() {
result.(CallNode).getAnArg() = this
}
override ControlFlowNode getASuccessorNode() { result.(CallNode).getAnArg() = this }
}

View File

@@ -1,26 +1,24 @@
/** Provides class and predicates to track external data that
/**
* Provides class and predicates to track external data that
* may represent malicious OS commands.
*
* This module is intended to be imported into a taint-tracking query
* to extend `TaintKind` and `TaintSink`.
*
*/
import python
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
private ModuleObject osOrPopenModule() {
result.getName() = "os" or
result.getName() = "popen2"
}
private Object makeOsCall() {
exists(string name |
result = ModuleObject::named("subprocess").attr(name) |
exists(string name | result = ModuleObject::named("subprocess").attr(name) |
name = "Popen" or
name = "call" or
name = "call" or
name = "check_call" or
name = "check_output" or
name = "run"
@@ -29,40 +27,27 @@ private Object makeOsCall() {
/**Special case for first element in sequence. */
class FirstElementKind extends TaintKind {
FirstElementKind() { this = "sequence[" + any(ExternalStringKind key) + "][0]" }
FirstElementKind() {
this = "sequence[" + any(ExternalStringKind key) + "][0]"
}
override string repr() {
result = "first item in sequence of " + this.getItem().repr()
}
override string repr() { result = "first item in sequence of " + this.getItem().repr() }
/** Gets the taint kind for item in this sequence. */
ExternalStringKind getItem() {
this = "sequence[" + result + "][0]"
}
ExternalStringKind getItem() { this = "sequence[" + result + "][0]" }
}
class FirstElementFlow extends DataFlowExtension::DataFlowNode {
FirstElementFlow() { this = any(SequenceNode s).getElement(0) }
FirstElementFlow() {
this = any(SequenceNode s).getElement(0)
}
override
ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) {
override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) {
result.(SequenceNode).getElement(0) = this and tokind.(FirstElementKind).getItem() = fromkind
}
}
/** A taint sink that is potentially vulnerable to malicious shell commands.
/**
* A taint sink that is potentially vulnerable to malicious shell commands.
* The `vuln` in `subprocess.call(shell=vuln)` and similar calls.
*/
class ShellCommand extends TaintSink {
override string toString() { result = "shell command" }
ShellCommand() {
@@ -75,7 +60,8 @@ class ShellCommand extends TaintSink {
or
exists(CallNode call, string name |
call.getAnArg() = this and
call.getFunction().refersTo(osOrPopenModule().attr(name)) |
call.getFunction().refersTo(osOrPopenModule().attr(name))
|
name = "system" or
name = "popen" or
name.matches("popen_")
@@ -94,19 +80,18 @@ class ShellCommand extends TaintSink {
/* List (or tuple) containing a tainted string command */
kind instanceof ExternalStringSequenceKind
}
}
/** A taint sink that is potentially vulnerable to malicious shell commands.
/**
* A taint sink that is potentially vulnerable to malicious shell commands.
* The `vuln` in `subprocess.call(vuln, ...)` and similar calls.
*/
class OsCommandFirstArgument extends TaintSink {
override string toString() { result = "OS command first argument" }
OsCommandFirstArgument() {
not this instanceof ShellCommand and
exists(CallNode call|
exists(CallNode call |
call.getFunction().refersTo(makeOsCall()) and
call.getArg(0) = this
)
@@ -119,5 +104,4 @@ class OsCommandFirstArgument extends TaintSink {
/* List (or tuple) whose first element is tainted */
kind instanceof FirstElementKind
}
}

View File

@@ -1,14 +1,8 @@
import python
import semmle.python.security.TaintTracking
/** `pickle.loads(untrusted)` vulnerability. */
abstract class DeserializationSink extends TaintSink {
bindingset[this]
DeserializationSink() {
this = this
}
DeserializationSink() { this = this }
}

View File

@@ -1,33 +1,30 @@
/** Provides class and predicates to track external data that
/**
* Provides class and predicates to track external data that
* may represent malicious Python code.
*
* This module is intended to be imported into a taint-tracking query
* to extend `TaintKind` and `TaintSink`.
*
*/
import python
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
private FunctionObject exec_or_eval() {
result = Object::builtin("exec")
or
result = Object::builtin("eval")
}
/** A taint sink that represents an argument to exec or eval that is vulnerable to malicious input.
/**
* A taint sink that represents an argument to exec or eval that is vulnerable to malicious input.
* The `vuln` in `exec(vuln)` or similar.
*/
class StringEvaluationNode extends TaintSink {
override string toString() { result = "exec or eval" }
StringEvaluationNode() {
exists(Exec exec |
exec.getASubExpression().getAFlowNode() = this
)
exists(Exec exec | exec.getASubExpression().getAFlowNode() = this)
or
exists(CallNode call |
exec_or_eval().getACall() = call and
@@ -35,8 +32,5 @@ class StringEvaluationNode extends TaintSink {
)
}
override predicate sinks(TaintKind kind) {
kind instanceof ExternalStringKind
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -1,26 +1,23 @@
/** Provides class and predicates to track external data that
/**
* Provides class and predicates to track external data that
* may represent malicious marshals.
*
* This module is intended to be imported into a taint-tracking query
* to extend `TaintKind` and `TaintSink`.
*
*/
import python
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.injection.Deserialization
private FunctionObject marshalLoads() { result = ModuleObject::named("marshal").attr("loads") }
private FunctionObject marshalLoads() {
result = ModuleObject::named("marshal").attr("loads")
}
/** A taint sink that is potentially vulnerable to malicious marshaled objects.
* The `vuln` in `marshal.loads(vuln)`. */
/**
* A taint sink that is potentially vulnerable to malicious marshaled objects.
* The `vuln` in `marshal.loads(vuln)`.
*/
class UnmarshalingNode extends DeserializationSink {
override string toString() { result = "unmarshaling vulnerability" }
UnmarshalingNode() {
@@ -30,8 +27,5 @@ class UnmarshalingNode extends DeserializationSink {
)
}
override predicate sinks(TaintKind kind) {
kind instanceof ExternalStringKind
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -1,28 +1,22 @@
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
/** Prevents taint flowing through ntpath.normpath()
/**
* Prevents taint flowing through ntpath.normpath()
* NormalizedPath below handles that case.
*/
class PathSanitizer extends Sanitizer {
PathSanitizer() {
this = "path.sanitizer"
}
PathSanitizer() { this = "path.sanitizer" }
override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) {
taint instanceof ExternalStringKind and
abspath_call(node, _)
}
}
private FunctionObject abspath() {
exists(ModuleObject os_path |
ModuleObject::named("os").attr("path") = os_path
|
exists(ModuleObject os_path | ModuleObject::named("os").attr("path") = os_path |
os_path.attr("abspath") = result
or
os_path.attr("normpath") = result
@@ -31,15 +25,9 @@ private FunctionObject abspath() {
/** A path that has been normalized, but not verified to be safe */
class NormalizedPath extends TaintKind {
NormalizedPath() { this = "normalized.path.injection" }
NormalizedPath() {
this = "normalized.path.injection"
}
override string repr() {
result = "normalized path"
}
override string repr() { result = "normalized path" }
}
private predicate abspath_call(CallNode call, ControlFlowNode arg) {
@@ -47,39 +35,31 @@ private predicate abspath_call(CallNode call, ControlFlowNode arg) {
arg = call.getArg(0)
}
class AbsPath extends DataFlowExtension::DataFlowNode {
AbsPath() { abspath_call(_, this) }
AbsPath() {
abspath_call(_, this)
override ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) {
abspath_call(result, this) and
tokind instanceof NormalizedPath and
fromkind instanceof ExternalStringKind
}
override
ControlFlowNode getASuccessorNode(TaintKind fromkind, TaintKind tokind) {
abspath_call(result, this) and tokind instanceof NormalizedPath and fromkind instanceof ExternalStringKind
}
}
class NormalizedPathSanitizer extends Sanitizer {
NormalizedPathSanitizer() {
this = "normalized.path.sanitizer"
}
NormalizedPathSanitizer() { this = "normalized.path.sanitizer" }
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
taint instanceof NormalizedPath and
test.getTest().(CallNode).getFunction().(AttrNode).getName() = "startswith" and
test.getSense() = true
}
}
/** A taint sink that is vulnerable to malicious paths.
/**
* A taint sink that is vulnerable to malicious paths.
* The `vuln` in `open(vuln)` and similar.
*/
class OpenNode extends TaintSink {
override string toString() { result = "argument to open()" }
OpenNode() {
@@ -94,10 +74,4 @@ class OpenNode extends TaintSink {
or
kind instanceof NormalizedPath
}
}

View File

@@ -1,17 +1,16 @@
/** Provides class and predicates to track external data that
/**
* Provides class and predicates to track external data that
* may represent malicious pickles.
*
* This module is intended to be imported into a taint-tracking query
* to extend `TaintKind` and `TaintSink`.
*
*/
import python
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.injection.Deserialization
private ModuleObject pickleModule() {
result.getName() = "pickle"
or
@@ -20,13 +19,10 @@ private ModuleObject pickleModule() {
result.getName() = "dill"
}
private FunctionObject pickleLoads() {
result = pickleModule().attr("loads")
}
private FunctionObject pickleLoads() { result = pickleModule().attr("loads") }
/** `pickle.loads(untrusted)` vulnerability. */
class UnpicklingNode extends DeserializationSink {
override string toString() { result = "unpickling untrusted data" }
UnpicklingNode() {
@@ -36,8 +32,5 @@ class UnpicklingNode extends DeserializationSink {
)
}
override predicate sinks(TaintKind kind) {
kind instanceof ExternalStringKind
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -1,29 +1,27 @@
/** Provides class and predicates to track external data that
/**
* Provides class and predicates to track external data that
* may represent malicious SQL queries or parts of queries.
*
* This module is intended to be imported into a taint-tracking query
* to extend `TaintKind` and `TaintSink`.
*
*/
import python
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.SQL
private StringObject first_part(ControlFlowNode command) {
command.(BinaryExprNode).getOp() instanceof Add and
command.(BinaryExprNode).getLeft().refersTo(result)
or
exists(CallNode call, SequenceObject seq |
call = command |
exists(CallNode call, SequenceObject seq | call = command |
call = theStrType().lookupAttribute("join") and
call.getArg(0).refersTo(seq) and
seq.getInferredElement(0) = result
)
or
command.(BinaryExprNode).getOp() instanceof Mod and
command.(BinaryExprNode).getOp() instanceof Mod and
command.getNode().(StrConst).getLiteralObject() = result
}
@@ -32,52 +30,45 @@ predicate probable_sql_command(ControlFlowNode command, ControlFlowNode inject)
exists(string prefix |
inject = command.getAChild*() and
first_part(command).getText().regexpMatch(" *" + prefix + ".*")
|
|
prefix = "CREATE" or prefix = "SELECT"
)
}
/** A taint kind representing a DB cursor.
/**
* A taint kind representing a DB cursor.
* This will be overridden to provide specific kinds of DB cursor.
*/
abstract class DbCursor extends TaintKind {
bindingset[this]
DbCursor() { any() }
string getExecuteMethodName() { result = "execute" }
}
/** A part of a string that appears to be a SQL command and is thus
/**
* A part of a string that appears to be a SQL command and is thus
* vulnerable to malicious input.
*/
class SimpleSqlStringInjection extends SqlInjectionSink {
override string toString() { result = "simple SQL string injection" }
SimpleSqlStringInjection() {
probable_sql_command(_, this)
}
override predicate sinks(TaintKind kind) {
kind instanceof ExternalStringKind
}
SimpleSqlStringInjection() { probable_sql_command(_, this) }
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/** A taint source representing sources of DB connections.
/**
* A taint source representing sources of DB connections.
* This will be overridden to provide specific kinds of DB connection sources.
*/
abstract class DbConnectionSource extends TaintSource {
abstract class DbConnectionSource extends TaintSource { }
}
/** A taint sink that is vulnerable to malicious SQL queries.
/**
* A taint sink that is vulnerable to malicious SQL queries.
* The `vuln` in `db.connection.execute(vuln)` and similar.
*/
class DbConnectionExecuteArgument extends SqlInjectionSink {
override string toString() { result = "db.connection.execute" }
DbConnectionExecuteArgument() {
@@ -88,9 +79,5 @@ class DbConnectionExecuteArgument extends SqlInjectionSink {
)
}
override predicate sinks(TaintKind kind) {
kind instanceof ExternalStringKind
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -1,36 +1,26 @@
/** Provides class and predicates to track external data that
/**
* Provides class and predicates to track external data that
* may represent malicious XML objects.
*
* This module is intended to be imported into a taint-tracking query
* to extend `TaintKind` and `TaintSink`.
*/
import python
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.injection.Deserialization
private ModuleObject xmlElementTreeModule() { result.getName() = "xml.etree.ElementTree" }
private ModuleObject xmlElementTreeModule() {
result.getName() = "xml.etree.ElementTree"
}
private ModuleObject xmlMiniDomModule() { result.getName() = "xml.dom.minidom" }
private ModuleObject xmlMiniDomModule() {
result.getName() = "xml.dom.minidom"
}
private ModuleObject xmlPullDomModule() { result.getName() = "xml.dom.pulldom" }
private ModuleObject xmlPullDomModule() {
result.getName() = "xml.dom.pulldom"
}
private ModuleObject xmlSaxModule() {
result.getName() = "xml.sax"
}
private ModuleObject xmlSaxModule() { result.getName() = "xml.sax" }
private class ExpatParser extends TaintKind {
ExpatParser() { this = "expat.parser" }
}
private FunctionObject expatCreateParseFunction() {
@@ -38,18 +28,11 @@ private FunctionObject expatCreateParseFunction() {
}
private class ExpatCreateParser extends TaintSource {
ExpatCreateParser() { expatCreateParseFunction().getACall() = this }
ExpatCreateParser() {
expatCreateParseFunction().getACall() = this
}
override predicate isSourceOf(TaintKind kind) { kind instanceof ExpatParser }
override predicate isSourceOf(TaintKind kind) {
kind instanceof ExpatParser
}
string toString() {
result = "expat.create.parser"
}
string toString() { result = "expat.create.parser" }
}
private FunctionObject xmlFromString() {
@@ -64,30 +47,22 @@ private FunctionObject xmlFromString() {
/** A (potentially) malicious XML string. */
class ExternalXmlString extends ExternalStringKind {
ExternalXmlString() {
this = "external xml encoded object"
}
ExternalXmlString() { this = "external xml encoded object" }
}
/** A call to an XML library function that is potentially vulnerable to a
/**
* A call to an XML library function that is potentially vulnerable to a
* specially crafted XML string.
*/
class XmlLoadNode extends DeserializationSink {
override string toString() { result = "xml.load vulnerability" }
XmlLoadNode() {
exists(CallNode call |
call.getAnArg() = this |
exists(CallNode call | call.getAnArg() = this |
xmlFromString().getACall() = call or
any(ExpatParser parser).taints(call.getFunction().(AttrNode).getObject("Parse"))
)
}
override predicate sinks(TaintKind kind) {
kind instanceof ExternalXmlString
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalXmlString }
}

View File

@@ -1,25 +1,20 @@
/** Provides class and predicates to track external data that
/**
* Provides class and predicates to track external data that
* may represent malicious yaml-encoded objects.
*
* This module is intended to be imported into a taint-tracking query
* to extend `TaintKind` and `TaintSink`.
*
*/
import python
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Untrusted
import semmle.python.security.injection.Deserialization
private FunctionObject yamlLoad() {
result = ModuleObject::named("yaml").attr("load")
}
private FunctionObject yamlLoad() { result = ModuleObject::named("yaml").attr("load") }
/** `yaml.load(untrusted)` vulnerability. */
class YamlLoadNode extends DeserializationSink {
override string toString() { result = "yaml.load vulnerability" }
YamlLoadNode() {
@@ -29,8 +24,5 @@ class YamlLoadNode extends DeserializationSink {
)
}
override predicate sinks(TaintKind kind) {
kind instanceof ExternalStringKind
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -1,15 +1,11 @@
import python
private import Common
import semmle.python.security.TaintTracking
/** An extensible kind of taint representing any kind of string. */
abstract class StringKind extends TaintKind {
bindingset[this]
StringKind() {
this = this
}
StringKind() { this = this }
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
result = this and
@@ -27,43 +23,44 @@ abstract class StringKind extends TaintKind {
}
override ClassValue getType() {
result = Value::named("bytes") or result = Value::named("str") or result = Value::named("unicode")
result = Value::named("bytes") or
result = Value::named("str") or
result = Value::named("unicode")
}
}
private class StringEqualitySanitizer extends Sanitizer {
StringEqualitySanitizer() { this = "string equality sanitizer" }
/** The test `if untrusted == "KNOWN_VALUE":` sanitizes `untrusted` on its `true` edge. */
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
taint instanceof StringKind and
exists(ControlFlowNode const, Cmpop op |
const.getNode() instanceof StrConst |
exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst |
(
test.getTest().(CompareNode).operands(const, op, _)
or
test.getTest().(CompareNode).operands(_, op, const)
) and (
) and
(
op instanceof Eq and test.getSense() = true
or
op instanceof NotEq and test.getSense() = false
)
)
}
}
/* tonode = fromnode.xxx() where the call to xxx returns an identical or similar string */
private predicate str_method_call(ControlFlowNode fromnode, CallNode tonode) {
exists(string method_name |
tonode.getFunction().(AttrNode).getObject(method_name) = fromnode
|
method_name = "strip" or method_name = "format" or
method_name = "lstrip" or method_name = "rstrip" or
method_name = "ljust" or method_name = "rjust" or
method_name = "title" or method_name = "capitalize"
exists(string method_name | tonode.getFunction().(AttrNode).getObject(method_name) = fromnode |
method_name = "strip" or
method_name = "format" or
method_name = "lstrip" or
method_name = "rstrip" or
method_name = "ljust" or
method_name = "rjust" or
method_name = "title" or
method_name = "capitalize"
)
}
@@ -79,8 +76,10 @@ private predicate encode_decode(ControlFlowNode fromnode, CallNode tonode) {
not func.getFunction().isMethod() and
func.getACall() = tonode and
tonode.getAnArg() = fromnode and
func.getName() = name |
name = "encode" or name = "decode" or
func.getName() = name
|
name = "encode" or
name = "decode" or
name = "decodestring"
)
}
@@ -99,22 +98,19 @@ private predicate to_str(ControlFlowNode fromnode, CallNode tonode) {
private predicate slice(ControlFlowNode fromnode, SubscriptNode tonode) {
exists(Slice all |
all = tonode.getIndex().getNode() and
not exists(all.getStart()) and not exists(all.getStop()) and
not exists(all.getStart()) and
not exists(all.getStop()) and
tonode.getObject() = fromnode
)
}
/* tonode = os.path.join(..., fromnode, ...) */
private predicate os_path_join(ControlFlowNode fromnode, CallNode tonode) {
tonode = Value::named("os.path.join").getACall()
and tonode.getAnArg() = fromnode
tonode = Value::named("os.path.join").getACall() and
tonode.getAnArg() = fromnode
}
/** A kind of "taint", representing a dictionary mapping str->"taint" */
class StringDictKind extends DictKind {
StringDictKind() {
this.getValue() instanceof StringKind
}
StringDictKind() { this.getValue() instanceof StringKind }
}

View File

@@ -1,12 +1,10 @@
import python
/* A call that returns a copy (or similar) of the argument */
predicate copy_call(ControlFlowNode fromnode, CallNode tonode) {
tonode.getFunction().(AttrNode).getObject("copy") = fromnode
or
exists(ModuleValue copy, string name |
name = "copy" or name = "deepcopy" |
exists(ModuleValue copy, string name | name = "copy" or name = "deepcopy" |
copy.attr(name).(FunctionValue).getACall() = tonode and
tonode.getArg(0) = fromnode
)

View File

@@ -206,7 +206,6 @@ class ExternalFileObject extends TaintKind {
*/
class UrlsplitUrlparseTempSanitizer extends Sanitizer {
// TODO: remove this once we have better support for named tuples
UrlsplitUrlparseTempSanitizer() { this = "UrlsplitUrlparseTempSanitizer" }
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
@@ -238,9 +237,7 @@ class UrlsplitUrlparseTempSanitizer extends Sanitizer {
/** holds for `== "KNOWN_VALUE"` on `true` edge, and `!= "KNOWN_VALUE"` on `false` edge */
private predicate test_equality_with_const(CompareNode cmp, ControlFlowNode tainted, boolean sense) {
exists(ControlFlowNode const, Cmpop op |
const.getNode() instanceof StrConst
|
exists(ControlFlowNode const, Cmpop op | const.getNode() instanceof StrConst |
(
cmp.operands(const, op, tainted)
or
@@ -257,7 +254,9 @@ class UrlsplitUrlparseTempSanitizer extends Sanitizer {
/** holds for `in ["KNOWN_VALUE", ...]` on `true` edge, and `not in ["KNOWN_VALUE", ...]` on `false` edge */
private predicate test_in_const_seq(CompareNode cmp, ControlFlowNode tainted, boolean sense) {
exists(SequenceNode const_seq, Cmpop op |
forall(ControlFlowNode elem | elem = const_seq.getAnElement() | elem.getNode() instanceof StrConst)
forall(ControlFlowNode elem | elem = const_seq.getAnElement() |
elem.getNode() instanceof StrConst
)
|
cmp.operands(tainted, op, const_seq) and
(

View File

@@ -1,14 +1,10 @@
import python
import External
/** A kind of taint representing an externally controlled string.
/**
* A kind of taint representing an externally controlled string.
* This class is a simple sub-class of `ExternalStringKind`.
*/
class UntrustedStringKind extends ExternalStringKind {
UntrustedStringKind() {
this = "externally controlled string"
}
UntrustedStringKind() { this = "externally controlled string" }
}