Merge branch 'master' into objectapi-to-valueapi-expectedmappingforformatstring

This commit is contained in:
Rebecca Valentine
2020-02-18 21:33:12 -08:00
committed by GitHub
353 changed files with 18730 additions and 14708 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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."

View File

@@ -15,24 +15,24 @@ import python
Value aSocket() { result.getClass() = Value::named("socket.socket") }
CallNode socketBindCall() {
result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3
or
result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and
major_version() = 2
result = aSocket().attr("bind").(CallableValue).getACall() and major_version() = 3
or
result.getFunction().(AttrNode).getObject("bind").pointsTo(aSocket()) and
major_version() = 2
}
string allInterfaces() { result = "0.0.0.0" or result = "" }
Value getTextValue(string address) {
result = Value::forUnicode(address) and major_version() = 3
or
result = Value::forString(address) and major_version() = 2
result = Value::forUnicode(address) and major_version() = 3
or
result = Value::forString(address) and major_version() = 2
}
from CallNode call, TupleValue args, string address
where
call = socketBindCall() and
call.getArg(0).pointsTo(args) and
args.getItem(0) = getTextValue(address) and
address = allInterfaces()
call = socketBindCall() and
call.getArg(0).pointsTo(args) and
args.getItem(0) = getTextValue(address) and
address = allInterfaces()
select call.getNode(), "'" + address + "' binds a socket to all interfaces."

View File

@@ -13,9 +13,7 @@
import python
import semmle.python.regex
private string commonTopLevelDomainRegex() {
result = "com|org|edu|gov|uk|net|io"
}
private string commonTopLevelDomainRegex() { result = "com|org|edu|gov|uk|net|io" }
/**
* Holds if `pattern` is a regular expression pattern for URLs with a host matched by `hostPart`,
@@ -23,22 +21,20 @@ private string commonTopLevelDomainRegex() {
*/
bindingset[pattern]
predicate isIncompleteHostNameRegExpPattern(string pattern, string hostPart) {
hostPart = pattern
.regexpCapture("(?i).*" +
// an unescaped single `.`
"(?<!\\\\)[.]" +
// immediately followed by a sequence of subdomains, perhaps with some regex characters mixed in, followed by a known TLD
"([():|?a-z0-9-]+(\\\\)?[.](" + commonTopLevelDomainRegex() + "))" + ".*", 1)
hostPart = pattern
.regexpCapture("(?i).*" +
// an unescaped single `.`
"(?<!\\\\)[.]" +
// immediately followed by a sequence of subdomains, perhaps with some regex characters mixed in, followed by a known TLD
"([():|?a-z0-9-]+(\\\\)?[.](" + commonTopLevelDomainRegex() + "))" + ".*", 1)
}
from Regex r, string pattern, string hostPart
where
(
r.getText() = pattern
) and
isIncompleteHostNameRegExpPattern(pattern, hostPart) and
// ignore patterns with capture groups after the TLD
not pattern.regexpMatch("(?i).*[.](" + commonTopLevelDomainRegex() + ").*[(][?]:.*[)].*")
r.getText() = pattern and
isIncompleteHostNameRegExpPattern(pattern, hostPart) and
// ignore patterns with capture groups after the TLD
not pattern.regexpMatch("(?i).*[.](" + commonTopLevelDomainRegex() + ").*[(][?]:.*[)].*")
select r,
"This regular expression has an unescaped '.' before '" + hostPart +
"', so it might match more hosts than expected."
"This regular expression has an unescaped '.' before '" + hostPart +
"', so it might match more hosts than expected."

View File

@@ -10,20 +10,16 @@
* external/cwe/cwe-20
*/
import python
import semmle.python.regex
private string commonTopLevelDomainRegex() {
result = "com|org|edu|gov|uk|net|io"
}
private string commonTopLevelDomainRegex() { result = "com|org|edu|gov|uk|net|io" }
predicate looksLikeUrl(StrConst s) {
exists(string text |
text = s.getText()
|
text.regexpMatch("(?i)([a-z]*:?//)?\\.?([a-z0-9-]+\\.)+(" +
commonTopLevelDomainRegex() +")(:[0-9]+)?/?")
exists(string text | text = s.getText() |
text
.regexpMatch("(?i)([a-z]*:?//)?\\.?([a-z0-9-]+\\.)+(" + commonTopLevelDomainRegex() +
")(:[0-9]+)?/?")
or
// target is a HTTP URL to a domain on any TLD
text.regexpMatch("(?i)https?://([a-z0-9-]+\\.)+([a-z]+)(:[0-9]+)?/?")
@@ -42,10 +38,8 @@ predicate incomplete_sanitization(Expr sanitizer, StrConst url) {
}
predicate unsafe_call_to_startswith(Call sanitizer, StrConst url) {
sanitizer.getFunc().(Attribute).getName() = "startswith"
and
sanitizer.getArg(0) = url
and
sanitizer.getFunc().(Attribute).getName() = "startswith" and
sanitizer.getArg(0) = url and
not url.getText().regexpMatch("(?i)https?://[\\.a-z0-9-]+/.*")
}

View File

@@ -18,18 +18,17 @@
import python
import semmle.python.security.Paths
/* Sources */
import semmle.python.web.HttpRequest
/* Sinks */
import semmle.python.security.injection.Path
class PathInjectionConfiguration extends TaintTracking::Configuration {
PathInjectionConfiguration() { this = "Path injection configuration" }
override predicate isSource(TaintTracking::Source source) { source instanceof HttpRequestTaintSource }
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
override predicate isSink(TaintTracking::Sink sink) { sink instanceof OpenNode }
@@ -41,10 +40,9 @@ class PathInjectionConfiguration extends TaintTracking::Configuration {
override predicate isExtension(TaintTracking::Extension extension) {
extension instanceof AbsPath
}
}
from PathInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "This path depends on $@.", src.getSource(), "a user-provided value"
select sink.getSink(), src, sink, "This path depends on $@.", src.getSource(),
"a user-provided value"

View File

@@ -3,7 +3,7 @@
* @description Extracting files from a malicious tar archive without validating that the
* destination file path is within the destination directory can cause files outside
* the destination directory to be overwritten.
* @kind path-problem
* @kind path-problem
* @id py/tarslip
* @problem.severity error
* @precision medium
@@ -13,15 +13,12 @@
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.security.strings.Basic
/** A TaintKind to represent open tarfile objects. That is, the result of calling `tarfile.open(...)` */
class OpenTarFile extends TaintKind {
OpenTarFile() {
this = "tarfile.open"
}
OpenTarFile() { this = "tarfile.open" }
override TaintKind getTaintOfMethodResult(string name) {
name = "getmember" and result instanceof TarFileInfo
@@ -29,60 +26,45 @@ class OpenTarFile extends TaintKind {
name = "getmembers" and result.(SequenceKind).getItem() instanceof TarFileInfo
}
override ClassValue getType() {
result = Module::named("tarfile").attr("TarFile")
}
override TaintKind getTaintForIteration() {
result instanceof TarFileInfo
}
override ClassValue getType() { result = Value::named("tarfile.TarFile") }
override TaintKind getTaintForIteration() { result instanceof TarFileInfo }
}
/** The source of open tarfile objects. That is, any call to `tarfile.open(...)` */
class TarfileOpen extends TaintSource {
TarfileOpen() {
Module::named("tarfile").attr("open").getACall() = this
and
/* If argument refers to a string object, then it's a hardcoded path and
Value::named("tarfile.open").getACall() = this and
/*
* If argument refers to a string object, then it's a hardcoded path and
* this tarfile is safe.
*/
not this.(CallNode).getAnArg().refersTo(any(StringObject str))
and
not this.(CallNode).getAnArg().pointsTo(any(StringValue str)) and
/* Ignore opens within the tarfile module itself */
not this.(ControlFlowNode).getLocation().getFile().getBaseName() = "tarfile.py"
}
override predicate isSourceOf(TaintKind kind) {
kind instanceof OpenTarFile
}
override predicate isSourceOf(TaintKind kind) { kind instanceof OpenTarFile }
}
class TarFileInfo extends TaintKind {
TarFileInfo() { this = "tarfile.entry" }
TarFileInfo() {
this = "tarfile.entry"
}
override TaintKind getTaintOfMethodResult(string name) {
name = "next" and result = this
}
override TaintKind getTaintOfMethodResult(string name) { name = "next" and result = this }
override TaintKind getTaintOfAttribute(string name) {
name = "name" and result instanceof TarFileInfo
}
}
/*
* For efficiency we don't want to track the flow of taint
* around the tarfile module.
*/
/* For efficiency we don't want to track the flow of taint
* around the tarfile module. */
class ExcludeTarFilePy extends Sanitizer {
ExcludeTarFilePy() {
this = "Tar sanitizer"
}
ExcludeTarFilePy() { this = "Tar sanitizer" }
override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) {
node.getLocation().getFile().getBaseName() = "tarfile.py" and
@@ -94,12 +76,10 @@ class ExcludeTarFilePy extends Sanitizer {
taint.(SequenceKind).getItem() instanceof TarFileInfo
)
}
}
/* Any call to an extractall method */
class ExtractAllSink extends TaintSink {
CallNode call;
ExtractAllSink() {
@@ -107,15 +87,11 @@ class ExtractAllSink extends TaintSink {
count(call.getAnArg()) = 0
}
override predicate sinks(TaintKind kind) {
kind instanceof OpenTarFile
}
override predicate sinks(TaintKind kind) { kind instanceof OpenTarFile }
}
/* Argument to extract method */
class ExtractSink extends TaintSink {
CallNode call;
ExtractSink() {
@@ -123,16 +99,11 @@ class ExtractSink extends TaintSink {
this = call.getArg(0)
}
override predicate sinks(TaintKind kind) {
kind instanceof TarFileInfo
}
override predicate sinks(TaintKind kind) { kind instanceof TarFileInfo }
}
/* Members argument to extract method */
class ExtractMembersSink extends TaintSink {
CallNode call;
ExtractMembersSink() {
@@ -145,21 +116,15 @@ class ExtractMembersSink extends TaintSink {
or
kind instanceof OpenTarFile
}
}
class TarFileInfoSanitizer extends Sanitizer {
TarFileInfoSanitizer() {
this = "TarInfo sanitizer"
}
TarFileInfoSanitizer() { this = "TarInfo sanitizer" }
override predicate sanitizingEdge(TaintKind taint, PyEdgeRefinement test) {
path_sanitizing_test(test.getTest()) and
taint instanceof TarFileInfo
}
}
private predicate path_sanitizing_test(ControlFlowNode test) {
@@ -170,7 +135,6 @@ private predicate path_sanitizing_test(ControlFlowNode test) {
}
class TarSlipConfiguration extends TaintTracking::Configuration {
TarSlipConfiguration() { this = "TarSlip configuration" }
override predicate isSource(TaintTracking::Source source) { source instanceof TarfileOpen }
@@ -193,7 +157,7 @@ class TarSlipConfiguration extends TaintTracking::Configuration {
node.asVariable().getDefinition() = def
or
node.asCfgNode() = def.getDefiningNode()
|
|
def.getScope() = Value::named("tarfile.open").(CallableValue).getScope()
or
def.isSelf() and def.getScope().getEnclosingModule().getName() = "tarfile"
@@ -201,8 +165,7 @@ class TarSlipConfiguration extends TaintTracking::Configuration {
}
}
from TarSlipConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "Extraction of tarfile from $@", src.getSource(), "a potentially untrusted source"
select sink.getSink(), src, sink, "Extraction of tarfile from $@", src.getSource(),
"a potentially untrusted source"

View File

@@ -11,7 +11,6 @@ urlpatterns = [
def user_picture1(request):
"""A view that is vulnerable to malicious file access."""
base_path = '/server/static/images'
filename = request.GET.get('p')
# BAD: This could read any file on the file system
data = open(filename, 'rb').read()

View File

@@ -16,18 +16,17 @@
import python
import semmle.python.security.Paths
/* Sources */
import semmle.python.web.HttpRequest
/* Sinks */
import semmle.python.security.injection.Command
class CommandInjectionConfiguration extends TaintTracking::Configuration {
CommandInjectionConfiguration() { this = "Command injection configuration" }
override predicate isSource(TaintTracking::Source source) { source instanceof HttpRequestTaintSource }
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
override predicate isSink(TaintTracking::Sink sink) {
sink instanceof OsCommandFirstArgument or
@@ -37,9 +36,9 @@ class CommandInjectionConfiguration extends TaintTracking::Configuration {
override predicate isExtension(TaintTracking::Extension extension) {
extension instanceof FirstElementFlow
}
}
from CommandInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "This command depends on $@.", src.getSource(), "a user-provided value"
select sink.getSink(), src, sink, "This command depends on $@.", src.getSource(),
"a user-provided value"

View File

@@ -20,5 +20,4 @@ def command_execution_safe(request):
if request.method == 'POST':
action = request.POST.get('action', '')
#GOOD -- Use a whitelist
subprocess.call(["application", COMMAND[action]])
subprocess.call(["application", COMMANDS[action]])

View File

@@ -12,37 +12,39 @@
import python
ClassObject jinja2EnvironmentOrTemplate() {
exists(ModuleObject jinja2, string name |
jinja2.getName() = "jinja2" and
jinja2.attr(name) = result |
name = "Environment" or
name = "Template"
)
/* Jinja 2 Docs:
* https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.Environment
* https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.Template
*
* Although the docs doesn't say very clearly, autoescape is a valid argument when constructing
* a Template manually
*
* unsafe_tmpl = Template('Hello {{ name }}!')
* safe1_tmpl = Template('Hello {{ name }}!', autoescape=True)
*/
ClassValue jinja2EnvironmentOrTemplate() {
result = Value::named("jinja2.Environment")
or
result = Value::named("jinja2.Template")
}
ControlFlowNode getAutoEscapeParameter(CallNode call) {
exists(Object callable |
call.getFunction().refersTo(callable) |
callable = jinja2EnvironmentOrTemplate() and
result = call.getArgByName("autoescape")
)
result = call.getArgByName("autoescape")
}
from CallNode call
where
not exists(call.getNode().getStarargs()) and
not exists(call.getNode().getKwargs()) and
(
not exists(getAutoEscapeParameter(call)) and
exists(Object env |
call.getFunction().refersTo(env) and
env = jinja2EnvironmentOrTemplate()
call.getFunction().pointsTo(jinja2EnvironmentOrTemplate()) and
not exists(call.getNode().getStarargs()) and
not exists(call.getNode().getKwargs()) and
(
not exists(getAutoEscapeParameter(call))
or
exists(Value isFalse |
getAutoEscapeParameter(call).pointsTo(isFalse) and
isFalse.getDefiniteBooleanValue() = false
)
)
or
exists(Object isFalse |
getAutoEscapeParameter(call).refersTo(isFalse) and isFalse.booleanValue() = false
)
)
select call, "Using jinja2 templates with autoescape=False can potentially allow XSS attacks."

View File

@@ -14,28 +14,24 @@
import python
import semmle.python.security.Paths
/* Sources */
import semmle.python.web.HttpRequest
/* Sinks */
import semmle.python.web.HttpResponse
/* Flow */
import semmle.python.security.strings.Untrusted
class ReflectedXssConfiguration extends TaintTracking::Configuration {
ReflectedXssConfiguration() { this = "Reflected XSS configuration" }
override predicate isSource(TaintTracking::Source source) { source instanceof HttpRequestTaintSource }
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpResponseTaintSink }
}
from ReflectedXssConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "Cross-site scripting vulnerability due to $@.", src.getSource(), "user-provided value"
select sink.getSink(), src, sink, "Cross-site scripting vulnerability due to $@.", src.getSource(),
"a user-provided value"

View File

@@ -13,37 +13,37 @@
import python
import semmle.python.security.Paths
/* Sources */
import semmle.python.web.HttpRequest
/* Sinks */
import semmle.python.security.injection.Sql
import semmle.python.web.django.Db
import semmle.python.web.django.Model
class SQLInjectionConfiguration extends TaintTracking::Configuration {
SQLInjectionConfiguration() { this = "SQL injection configuration" }
override predicate isSource(TaintTracking::Source source) { source instanceof HttpRequestTaintSource }
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
override predicate isSink(TaintTracking::Sink sink) { sink instanceof SqlInjectionSink }
}
/* Additional configuration to support tracking of DB objects. Connections, cursors, etc. */
/* Additional configuration to support tracking of DB objects. Connections, cursors, etc.
* Without this configuration (or the LegacyConfiguration), the pattern of
* `any(MyTaintKind k).taints(control_flow_node)` used in DbConnectionExecuteArgument would not work.
*/
class DbConfiguration extends TaintTracking::Configuration {
DbConfiguration() { this = "DB configuration" }
override predicate isSource(TaintTracking::Source source) {
source instanceof DjangoModelObjects or
source instanceof DbConnectionSource
}
}
from SQLInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "This SQL query depends on $@.", src.getSource(), "a user-provided value"
select sink.getSink(), src, sink, "This SQL query depends on $@.", src.getSource(),
"a user-provided value"

View File

@@ -16,24 +16,22 @@
import python
import semmle.python.security.Paths
/* Sources */
import semmle.python.web.HttpRequest
/* Sinks */
import semmle.python.security.injection.Exec
class CodeInjectionConfiguration extends TaintTracking::Configuration {
CodeInjectionConfiguration() { this = "Code injection configuration" }
override predicate isSource(TaintTracking::Source source) { source instanceof HttpRequestTaintSource }
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
override predicate isSink(TaintTracking::Sink sink) { sink instanceof StringEvaluationNode }
}
from CodeInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "$@ flows to here and is interpreted as code.", src.getSource(), "User-provided value"
select sink.getSink(), src, sink, "$@ flows to here and is interpreted as code.", src.getSource(),
"A user-provided value"

View File

@@ -2,7 +2,7 @@
* @name Information exposure through an exception
* @description Leaking information about an exception, such as messages and stack traces, to an
* external user can expose implementation details that are useful to an attacker for
* developing a subsequent exploit.
* developing a subsequent exploit.
* @kind path-problem
* @problem.severity error
* @precision high
@@ -14,22 +14,18 @@
import python
import semmle.python.security.Paths
import semmle.python.security.Exceptions
import semmle.python.web.HttpResponse
class StackTraceExposureConfiguration extends TaintTracking::Configuration {
StackTraceExposureConfiguration() { this = "Stack trace exposure configuration" }
override predicate isSource(TaintTracking::Source source) { source instanceof ErrorInfoSource }
override predicate isSink(TaintTracking::Sink sink) {
sink instanceof HttpResponseTaintSink
}
override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpResponseTaintSink }
}
from StackTraceExposureConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "$@ may be exposed to an external user", src.getSource(), "Error information"
select sink.getSink(), src, sink, "$@ may be exposed to an external user", src.getSource(),
"Error information"

View File

@@ -11,13 +11,12 @@
*/
import python
import semmle.python.web.flask.General
from CallNode call, Object isTrue
from CallNode call, Value isTrue
where
call = theFlaskClass().declaredAttribute("run").(FunctionValue).getACall() and
call.getArgByName("debug").refersTo(isTrue) and
isTrue.booleanValue() = true
select call, "A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger."
call.getArgByName("debug").pointsTo(isTrue) and
isTrue.getDefiniteBooleanValue() = true
select call,
"A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger."

View File

@@ -11,13 +11,13 @@
import python
private ModuleObject theParamikoClientModule() { result = ModuleObject::named("paramiko.client") }
private ModuleValue theParamikoClientModule() { result = Value::named("paramiko.client") }
private ClassObject theParamikoSSHClientClass() {
private ClassValue theParamikoSSHClientClass() {
result = theParamikoClientModule().attr("SSHClient")
}
private ClassObject unsafe_paramiko_policy(string name) {
private ClassValue unsafe_paramiko_policy(string name) {
(name = "AutoAddPolicy" or name = "WarningPolicy") and
result = theParamikoClientModule().attr(name)
}
@@ -25,12 +25,12 @@ private ClassObject unsafe_paramiko_policy(string name) {
from CallNode call, ControlFlowNode arg, string name
where
call = theParamikoSSHClientClass()
.lookupAttribute("set_missing_host_key_policy")
.(FunctionObject)
.getACall() and
.lookup("set_missing_host_key_policy")
.(FunctionValue)
.getACall() and
arg = call.getAnArg() and
(
arg.refersTo(unsafe_paramiko_policy(name)) or
arg.refersTo(_, unsafe_paramiko_policy(name), _)
arg.pointsTo(unsafe_paramiko_policy(name)) or
arg.pointsTo().getClass() = unsafe_paramiko_policy(name)
)
select call, "Setting missing host key policy to " + name + " may be unsafe."

View File

@@ -10,24 +10,17 @@
*/
import python
import semmle.python.web.Http
FunctionObject requestFunction() {
result = ModuleObject::named("requests").attr(httpVerbLower())
}
FunctionValue requestFunction() { result = Module::named("requests").attr(httpVerbLower()) }
/** requests treats None as the default and all other "falsey" values as False */
predicate falseNotNone(Object o) {
o.booleanValue() = false and not o = theNoneObject()
}
from CallNode call, FunctionObject func, Object falsey, ControlFlowNode origin
where
func = requestFunction() and
func.getACall() = call and
falseNotNone(falsey) and
call.getArgByName("verify").refersTo(falsey, origin)
predicate falseNotNone(Value v) { v.getDefiniteBooleanValue() = false and not v = Value::none_() }
from CallNode call, FunctionValue func, Value falsey, ControlFlowNode origin
where
func = requestFunction() and
func.getACall() = call and
falseNotNone(falsey) and
call.getArgByName("verify").pointsTo(falsey, origin)
select call, "Call to $@ with verify=$@", func, "requests." + func.getName(), origin, "False"

View File

@@ -14,15 +14,12 @@
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.security.SensitiveData
import semmle.python.security.ClearText
class CleartextLoggingConfiguration extends TaintTracking::Configuration {
CleartextLoggingConfiguration() { this = "ClearTextLogging" }
CleartextLoggingConfiguration() { this = "ClearTextLogging" }
override predicate isSource(DataFlow::Node src, TaintKind kind) {
src.asCfgNode().(SensitiveData::Source).isSourceOf(kind)
@@ -32,11 +29,9 @@ class CleartextLoggingConfiguration extends TaintTracking::Configuration {
sink.asCfgNode() instanceof ClearTextLogging::Sink and
kind instanceof SensitiveData
}
}
from CleartextLoggingConfiguration config, TaintedPathSource source, TaintedPathSink sink
where config.hasFlowPath(source, sink)
select sink.getSink(), source, sink, "Sensitive data returned by $@ is logged here.",
source.getSource(), source.getCfgNode().(SensitiveData::Source).repr()
source.getSource(), source.getCfgNode().(SensitiveData::Source).repr()

View File

@@ -14,14 +14,12 @@
import python
import semmle.python.security.Paths
import semmle.python.security.TaintTracking
import semmle.python.security.SensitiveData
import semmle.python.security.ClearText
class CleartextStorageConfiguration extends TaintTracking::Configuration {
CleartextStorageConfiguration() { this = "ClearTextStorage" }
CleartextStorageConfiguration() { this = "ClearTextStorage" }
override predicate isSource(DataFlow::Node src, TaintKind kind) {
src.asCfgNode().(SensitiveData::Source).isSourceOf(kind)
@@ -31,11 +29,9 @@ class CleartextStorageConfiguration extends TaintTracking::Configuration {
sink.asCfgNode() instanceof ClearTextStorage::Sink and
kind instanceof SensitiveData
}
}
from CleartextStorageConfiguration config, TaintedPathSource source, TaintedPathSink sink
where config.hasFlowPath(source, sink)
select sink.getSink(), source, sink, "Sensitive data from $@ is stored here.",
source.getSource(), source.getCfgNode().(SensitiveData::Source).repr()
select sink.getSink(), source, sink, "Sensitive data from $@ is stored here.", source.getSource(),
source.getCfgNode().(SensitiveData::Source).repr()

View File

@@ -19,63 +19,63 @@ int minimumSecureKeySize(string algo) {
algo = "ECC" and result = 224
}
predicate dsaRsaKeySizeArg(FunctionObject obj, string algorithm, string arg) {
exists(ModuleObject mod |
mod.attr(_) = obj |
predicate dsaRsaKeySizeArg(FunctionValue func, string algorithm, string arg) {
exists(ModuleValue mod | func = mod.attr(_) |
algorithm = "DSA" and
(
mod.getName() = "cryptography.hazmat.primitives.asymmetric.dsa" and arg = "key_size"
mod = Module::named("cryptography.hazmat.primitives.asymmetric.dsa") and arg = "key_size"
or
mod.getName() = "Crypto.PublicKey.DSA" and arg = "bits"
mod = Module::named("Crypto.PublicKey.DSA") and arg = "bits"
or
mod.getName() = "Cryptodome.PublicKey.DSA" and arg = "bits"
mod = Module::named("Cryptodome.PublicKey.DSA") and arg = "bits"
)
or
algorithm = "RSA" and
(
mod.getName() = "cryptography.hazmat.primitives.asymmetric.rsa" and arg = "key_size"
mod = Module::named("cryptography.hazmat.primitives.asymmetric.rsa") and arg = "key_size"
or
mod.getName() = "Crypto.PublicKey.RSA" and arg = "bits"
mod = Module::named("Crypto.PublicKey.RSA") and arg = "bits"
or
mod.getName() = "Cryptodome.PublicKey.RSA" and arg = "bits"
mod = Module::named("Cryptodome.PublicKey.RSA") and arg = "bits"
)
)
}
predicate ecKeySizeArg(FunctionObject obj, string arg) {
exists(ModuleObject mod |
mod.attr(_) = obj |
mod.getName() = "cryptography.hazmat.primitives.asymmetric.ec" and arg = "curve"
predicate ecKeySizeArg(FunctionValue func, string arg) {
exists(ModuleValue mod | func = mod.attr(_) |
mod = Module::named("cryptography.hazmat.primitives.asymmetric.ec") and arg = "curve"
)
}
int keySizeFromCurve(ClassObject curveClass) {
result = curveClass.declaredAttribute("key_size").(NumericObject).intValue()
int keySizeFromCurve(ClassValue curveClass) {
result = curveClass.declaredAttribute("key_size").(NumericValue).getIntValue()
}
predicate algorithmAndKeysizeForCall(CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin) {
exists(FunctionObject func, string argname, ControlFlowNode arg |
arg = func.getNamedArgumentForCall(call, argname) |
exists(NumericObject key |
arg.refersTo(key, keyOrigin) and
predicate algorithmAndKeysizeForCall(
CallNode call, string algorithm, int keySize, ControlFlowNode keyOrigin
) {
exists(FunctionValue func, string argname, ControlFlowNode arg |
arg = func.getNamedArgumentForCall(call, argname)
|
exists(NumericValue key |
arg.pointsTo(key, keyOrigin) and
dsaRsaKeySizeArg(func, algorithm, argname) and
keySize = key.intValue()
keySize = key.getIntValue()
)
or
exists(ClassObject curve |
arg.refersTo(_, curve, keyOrigin) and
ecKeySizeArg(func, argname) and
exists(Value curveClassInstance |
algorithm = "ECC" and
keySize = keySizeFromCurve(curve)
ecKeySizeArg(func, argname) and
arg.pointsTo(_, curveClassInstance, keyOrigin) and
keySize = keySizeFromCurve(curveClassInstance.getClass())
)
)
}
from CallNode call, ControlFlowNode origin, string algo, int keySize
from CallNode call, string algo, int keySize, ControlFlowNode origin
where
algorithmAndKeysizeForCall(call, algo, keySize, origin) and
keySize < minimumSecureKeySize(algo)
select call, "Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) + " and considered breakable.", origin, keySize.toString()
select call,
"Creation of an " + algo + " key uses $@ bits, which is below " + minimumSecureKeySize(algo) +
" and considered breakable.", origin, keySize.toString()

View File

@@ -12,13 +12,9 @@
import python
FunctionObject ssl_wrap_socket() {
result = ModuleObject::named("ssl").attr("wrap_socket")
}
FunctionValue ssl_wrap_socket() { result = Value::named("ssl.wrap_socket") }
ClassObject ssl_Context_class() {
result = ModuleObject::named("ssl").attr("SSLContext")
}
ClassValue ssl_Context_class() { result = Value::named("ssl.SSLContext") }
CallNode unsafe_call(string method_name) {
result = ssl_wrap_socket().getACall() and
@@ -32,10 +28,7 @@ CallNode unsafe_call(string method_name) {
}
from CallNode call, string method_name
where
call = unsafe_call(method_name)
select call, "Call to " + method_name + " does not specify a protocol, which may result in an insecure default being used."
where call = unsafe_call(method_name)
select call,
"Call to " + method_name +
" does not specify a protocol, which may result in an insecure default being used."

View File

@@ -11,12 +11,16 @@
import python
FunctionObject ssl_wrap_socket() {
result = the_ssl_module().attr("wrap_socket")
}
private ModuleValue the_ssl_module() { result = Module::named("ssl") }
ClassObject ssl_Context_class() {
result = the_ssl_module().attr("SSLContext")
FunctionValue ssl_wrap_socket() { result = the_ssl_module().attr("wrap_socket") }
ClassValue ssl_Context_class() { result = the_ssl_module().attr("SSLContext") }
private ModuleValue the_pyOpenSSL_module() { result = Value::named("pyOpenSSL.SSL") }
ClassValue the_pyOpenSSL_Context_class() {
result = Value::named("pyOpenSSL.SSL.Context")
}
string insecure_version_name() {
@@ -33,25 +37,21 @@ string insecure_version_name() {
result = "PROTOCOL_TLSv1"
}
private ModuleObject the_ssl_module() {
result = ModuleObject::named("ssl")
}
private ModuleObject the_pyOpenSSL_module() {
result = ModuleObject::named("pyOpenSSL.SSL")
}
/* A syntactic check for cases where points-to analysis cannot infer the presence of
/*
* A syntactic check for cases where points-to analysis cannot infer the presence of
* a protocol constant, e.g. if it has been removed in later versions of the `ssl`
* library.
*/
bindingset[named_argument]
predicate probable_insecure_ssl_constant(CallNode call, string insecure_version, string named_argument) {
predicate probable_insecure_ssl_constant(
CallNode call, string insecure_version, string named_argument
) {
exists(ControlFlowNode arg |
arg = call.getArgByName(named_argument) or
arg = call.getArg(0)
|
arg.(AttrNode).getObject(insecure_version).refersTo(the_ssl_module())
arg.(AttrNode).getObject(insecure_version).pointsTo(the_ssl_module())
or
arg.(NameNode).getId() = insecure_version and
exists(Import imp |
@@ -61,7 +61,9 @@ predicate probable_insecure_ssl_constant(CallNode call, string insecure_version,
)
}
predicate unsafe_ssl_wrap_socket_call(CallNode call, string method_name, string insecure_version, string named_argument) {
predicate unsafe_ssl_wrap_socket_call(
CallNode call, string method_name, string insecure_version, string named_argument
) {
(
call = ssl_wrap_socket().getACall() and
method_name = "deprecated method ssl.wrap_socket" and
@@ -70,30 +72,26 @@ predicate unsafe_ssl_wrap_socket_call(CallNode call, string method_name, string
call = ssl_Context_class().getACall() and
named_argument = "protocol" and
method_name = "ssl.SSLContext"
)
and
insecure_version = insecure_version_name()
and
) and
insecure_version = insecure_version_name() and
(
call.getArgByName(named_argument).refersTo(the_ssl_module().attr(insecure_version))
call.getArgByName(named_argument).pointsTo(the_ssl_module().attr(insecure_version))
or
probable_insecure_ssl_constant(call, insecure_version, named_argument)
)
}
ClassObject the_pyOpenSSL_Context_class() {
result = ModuleObject::named("pyOpenSSL.SSL").attr("Context")
}
predicate unsafe_pyOpenSSL_Context_call(CallNode call, string insecure_version) {
call = the_pyOpenSSL_Context_class().getACall() and
insecure_version = insecure_version_name() and
call.getArg(0).refersTo(the_pyOpenSSL_module().attr(insecure_version))
call.getArg(0).pointsTo(the_pyOpenSSL_module().attr(insecure_version))
}
from CallNode call, string method_name, string insecure_version
where
unsafe_ssl_wrap_socket_call(call, method_name, insecure_version, _)
or
or
unsafe_pyOpenSSL_Context_call(call, insecure_version) and method_name = "pyOpenSSL.SSL.Context"
select call, "Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name + "."
select call,
"Insecure SSL/TLS protocol version " + insecure_version + " specified in call to " + method_name +
"."

View File

@@ -12,7 +12,7 @@
import python
FunctionObject temporary_name_function(string mod, string function) {
FunctionValue temporary_name_function(string mod, string function) {
(
mod = "tempfile" and function = "mktemp"
or
@@ -23,10 +23,9 @@ FunctionObject temporary_name_function(string mod, string function) {
function = "tempnam"
)
) and
result = ModuleObject::named(mod).attr(function)
result = Module::named(mod).attr(function)
}
from Call c, string mod, string function
where
temporary_name_function(mod, function).getACall().getNode() = c
where temporary_name_function(mod, function).getACall().getNode() = c
select c, "Call to deprecated function " + mod + "." + function + " may be insecure."

View File

@@ -10,28 +10,26 @@
* security
* serialization
*/
import python
import python
import semmle.python.security.Paths
// Sources -- Any untrusted input
import semmle.python.web.HttpRequest
import semmle.python.security.Paths
// Flow -- untrusted string
import semmle.python.security.strings.Untrusted
// Sink -- Unpickling and other deserialization formats.
import semmle.python.security.injection.Pickle
import semmle.python.security.injection.Marshal
import semmle.python.security.injection.Yaml
class UnsafeDeserializationConfiguration extends TaintTracking::Configuration {
class UnsafeDeserializationConfiguration extends TaintTracking::Configuration {
UnsafeDeserializationConfiguration() { this = "Unsafe deserialization configuration" }
override predicate isSource(TaintTracking::Source source) { source instanceof HttpRequestTaintSource }
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
override predicate isSink(TaintTracking::Sink sink) { sink instanceof DeserializationSink }
}
from UnsafeDeserializationConfiguration config, TaintedPathSource src, TaintedPathSink sink

View File

@@ -13,34 +13,29 @@
import python
import semmle.python.security.Paths
import semmle.python.web.HttpRedirect
import semmle.python.web.HttpRequest
import semmle.python.security.strings.Untrusted
/** Url redirection is a problem only if the user controls the prefix of the URL */
class UntrustedPrefixStringKind extends UntrustedStringKind {
override TaintKind getTaintForFlowStep(ControlFlowNode fromnode, ControlFlowNode tonode) {
result = UntrustedStringKind.super.getTaintForFlowStep(fromnode, tonode) and
not tonode.(BinaryExprNode).getRight() = fromnode
}
}
class UrlRedirectConfiguration extends TaintTracking::Configuration {
UrlRedirectConfiguration() { this = "URL redirect configuration" }
override predicate isSource(TaintTracking::Source source) { source instanceof HttpRequestTaintSource }
override predicate isSink(TaintTracking::Sink sink) {
sink instanceof HttpRedirectTaintSink
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
override predicate isSink(TaintTracking::Sink sink) { sink instanceof HttpRedirectTaintSink }
}
from UrlRedirectConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "Untrusted URL redirection due to $@.", src.getSource(), "a user-provided value"
select sink.getSink(), src, sink, "Untrusted URL redirection due to $@.", src.getSource(),
"a user-provided value"

View File

@@ -3,7 +3,7 @@
<overview>
<p>
When creating a file POSIX systems allow permissions to be specified
When creating a file, POSIX systems allow permissions to be specified
for owner, group and others separately. Permissions should be kept as
strict as possible, preventing access to the files contents by other users.
</p>

View File

@@ -9,22 +9,20 @@
* @tags external/cwe/cwe-732
* security
*/
import python
bindingset[p]
int world_permission(int p) {
result = p % 8
}
int world_permission(int p) { result = p % 8 }
bindingset[p]
int group_permission(int p) {
result = (p/8) % 8
}
int group_permission(int p) { result = (p / 8) % 8 }
bindingset[p]
string access(int p) {
p%4 >= 2 and result = "writable" or
p%4 < 2 and p != 0 and result = "readable"
p % 4 >= 2 and result = "writable"
or
p % 4 < 2 and p != 0 and result = "readable"
}
bindingset[p]
@@ -34,20 +32,20 @@ string permissive_permission(int p) {
world_permission(p) = 0 and result = "group " + access(group_permission(p))
}
predicate chmod_call(CallNode call, FunctionObject chmod, NumericObject num) {
ModuleObject::named("os").attr("chmod") = chmod and
chmod.getACall() = call and call.getArg(1).refersTo(num)
predicate chmod_call(CallNode call, FunctionValue chmod, NumericValue num) {
Value::named("os.chmod") = chmod and
chmod.getACall() = call and
call.getArg(1).pointsTo(num)
}
predicate open_call(CallNode call, FunctionObject open, NumericObject num) {
ModuleObject::named("os").attr("open") = open and
open.getACall() = call and call.getArg(2).refersTo(num)
predicate open_call(CallNode call, FunctionValue open, NumericValue num) {
Value::named("os.open") = open and
open.getACall() = call and
call.getArg(2).pointsTo(num)
}
from CallNode call, FunctionObject func, NumericObject num, string permission
from CallNode call, FunctionValue func, NumericValue num, string permission
where
(chmod_call(call, func, num) or open_call(call, func, num))
and
permission = permissive_permission(num.intValue())
(chmod_call(call, func, num) or open_call(call, func, num)) and
permission = permissive_permission(num.getIntValue())
select call, "Overly permissive mask in " + func.getName() + " sets file to " + permission + "."

View File

@@ -33,15 +33,15 @@ predicate fewer_characters_than(StrConst str, string char, float fraction) {
}
predicate possible_reflective_name(string name) {
exists(any(ModuleObject m).attr(name))
exists(any(ModuleValue m).attr(name))
or
exists(any(ClassObject c).lookupAttribute(name))
exists(any(ClassValue c).lookup(name))
or
any(ClassObject c).getName() = name
any(ClassValue c).getName() = name
or
exists(ModuleObject::named(name))
exists(Module::named(name))
or
exists(Object::builtin(name))
exists(Value::named(name))
}
int char_count(StrConst str) { result = count(string c | c = str.getText().charAt(_)) }
@@ -89,7 +89,7 @@ class CredentialSink extends TaintSink {
name.regexpMatch(getACredentialRegex()) and
not name.suffix(name.length() - 4) = "file"
|
any(FunctionObject func).getNamedArgumentForCall(_, name) = this
any(FunctionValue func).getNamedArgumentForCall(_, name) = this
or
exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this)
or

View File

@@ -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) {

View File

@@ -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()
)
}
}
}

View File

@@ -1 +1 @@
import semmle.python.security.TaintTracking
import semmle.python.security.TaintTracking

View File

@@ -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

View File

@@ -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 {
)
)
}
}

View File

@@ -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)
}
}

View File

@@ -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"))
)
}

View File

@@ -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 {
}
}

View File

@@ -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())

View File

@@ -118,10 +118,20 @@ class Value extends TObject {
)
}
/** Gets the boolean value of this value. */
boolean booleanValue() {
/** Gets the boolean interpretation of this value.
* Could be both `true` and `false`, if we can't determine the result more precisely.
*/
boolean getABooleanValue() {
result = this.(ObjectInternal).booleanValue()
}
/** Gets the boolean interpretation of this value, only if we can determine the result precisely.
* The result can be `none()`, but never both `true` and `false`.
*/
boolean getDefiniteBooleanValue() {
result = getABooleanValue() and
not (getABooleanValue() = true and getABooleanValue() = false)
}
}
/** Class representing modules in the Python program
@@ -241,14 +251,14 @@ module Value {
name = "False" and result = TFalse()
}
/** Gets the `Value` for the integer constant `i`, if it exists.
* There will be no `Value` for most integers, but the following are
/** Gets the `NumericValue` for the integer constant `i`, if it exists.
* There will be no `NumericValue` for most integers, but the following are
* guaranteed to exist:
* * From zero to 511 inclusive.
* * All powers of 2 (up to 2**30)
* * Any integer explicitly mentioned in the source program.
*/
Value forInt(int i) {
NumericValue forInt(int i) {
result.(IntObjectInternal).intValue() = i
}
@@ -256,7 +266,7 @@ module Value {
* There will be no `Value` for most byte strings, unless it is explicitly
* declared in the source program.
*/
Value forBytes(string bytes) {
StringValue forBytes(string bytes) {
result.(BytesObjectInternal).strValue() = bytes
}
@@ -264,7 +274,7 @@ module Value {
* There will be no `Value` for most text strings, unless it is explicitly
* declared in the source program.
*/
Value forUnicode(string text) {
StringValue forUnicode(string text) {
result.(UnicodeObjectInternal).strValue() = text
}
@@ -272,7 +282,7 @@ module Value {
* There will be no `Value` for most strings, unless it is explicitly
* declared in the source program.
*/
Value forString(string text) {
StringValue forString(string text) {
result.(UnicodeObjectInternal).strValue() = text
or
major_version() = 2 and
@@ -658,6 +668,26 @@ class StringValue extends Value {
}
}
/** A class representing numbers (ints and floats), either present in the source as a literal,
* or in a builtin as a value.
*/
class NumericValue extends Value {
NumericValue() {
this instanceof IntObjectInternal or
this instanceof FloatObjectInternal
}
/** Gets the integer-value if it is a constant integer, and it fits in a QL int */
int getIntValue() {
result = this.(IntObjectInternal).intValue()
}
/** Gets the float-value if it is a constant float */
int getFloatValue() {
result = this.(FloatObjectInternal).floatValue()
}
}
/** A method-resolution-order sequence of classes */
class MRO extends TClassList {
@@ -706,45 +736,57 @@ module ClassValue {
ClassValue bool() {
result = TBuiltinClassObject(Builtin::special("bool"))
}
/** Get the `ClassValue` for the `tuple` class. */
ClassValue tuple() {
result = TBuiltinClassObject(Builtin::special("tuple"))
}
/** Get the `ClassValue` for the `list` class. */
ClassValue list() {
result = TBuiltinClassObject(Builtin::special("list"))
}
/** Get the `ClassValue` for `xrange` (Python 2), or `range` (only Python 3) */
ClassValue range() {
major_version() = 2 and result = TBuiltinClassObject(Builtin::special("xrange"))
or
major_version() = 3 and result = TBuiltinClassObject(Builtin::special("range"))
}
/** Get the `ClassValue` for the `dict` class. */
ClassValue dict() {
result = TBuiltinClassObject(Builtin::special("dict"))
}
/** Get the `ClassValue` for the class of Python functions. */
ClassValue function() {
result = TBuiltinClassObject(Builtin::special("FunctionType"))
/** Get the `ClassValue` for the `set` class. */
ClassValue set() {
result = TBuiltinClassObject(Builtin::special("set"))
}
/** Get the `ClassValue` for the `type` class. */
ClassValue type() {
result = TType()
}
/** Get the `ClassValue` for the class of builtin functions. */
ClassValue builtinFunction() {
result = Value::named("len").getClass()
/** Get the `ClassValue` for the `object` class. */
ClassValue object() {
result = TBuiltinClassObject(Builtin::special("object"))
}
/** Get the `ClassValue` for the `int` class. */
ClassValue int_() {
result = TBuiltinClassObject(Builtin::special("int"))
}
/** Get the `ClassValue` for the `long` class. */
ClassValue long() {
result = TBuiltinClassObject(Builtin::special("long"))
}
/** Get the `ClassValue` for the `float` class. */
ClassValue float_() {
result = TBuiltinClassObject(Builtin::special("float"))
}
/** Get the `ClassValue` for the `tuple` class. */
ClassValue tupleType() {
result = TBuiltinClassObject(Builtin::special("tuple"))
}
/** Get the `ClassValue` for the `list` class. */
ClassValue list() {
result = TBuiltinClassObject(Builtin::special("list"))
/** Get the `ClassValue` for the `complex` class. */
ClassValue complex() {
result = TBuiltinClassObject(Builtin::special("complex"))
}
/** The builtin class '(x)range' */
@@ -773,7 +815,47 @@ module ClassValue {
else
result = unicode()
}
/** Get the `ClassValue` for the `property` class. */
ClassValue property() {
result = TBuiltinClassObject(Builtin::special("property"))
}
/** Get the `ClassValue` for the class of Python functions. */
ClassValue functionType() {
result = TBuiltinClassObject(Builtin::special("FunctionType"))
}
/** Get the `ClassValue` for the class of builtin functions. */
ClassValue builtinFunction() {
result = Value::named("len").getClass()
}
/** Get the `ClassValue` for the `generatorType` class. */
ClassValue generator() {
result = TBuiltinClassObject(Builtin::special("generator"))
}
/** Get the `ClassValue` for the `type` class. */
ClassValue type() {
result = TType()
}
/** Get the `ClassValue` for `ClassType`. */
ClassValue classType() {
result = TBuiltinClassObject(Builtin::special("ClassType"))
}
/** Get the `ClassValue` for `InstanceType`. */
ClassValue instanceType() {
result = TBuiltinClassObject(Builtin::special("InstanceType"))
}
/** Get the `ClassValue` for `super`. */
ClassValue super_() {
result = TBuiltinClassObject(Builtin::special("super"))
}
/** Get the `ClassValue` for the `classmethod` class. */
ClassValue classmethod() {
result = TBuiltinClassObject(Builtin::special("ClassMethod"))
@@ -783,20 +865,81 @@ module ClassValue {
ClassValue staticmethod() {
result = TBuiltinClassObject(Builtin::special("StaticMethod"))
}
/** Get the `ClassValue` for the `MethodType` class. */
pragma [noinline]
ClassValue methodType() {
result = TBuiltinClassObject(Builtin::special("MethodType"))
}
/** Get the `ClassValue` for the `MethodDescriptorType` class. */
ClassValue methodDescriptorType() {
result = TBuiltinClassObject(Builtin::special("MethodDescriptorType"))
}
/** Get the `ClassValue` for the `GetSetDescriptorType` class. */
ClassValue getSetDescriptorType() {
result = TBuiltinClassObject(Builtin::special("GetSetDescriptorType"))
}
/** Get the `ClassValue` for the `StopIteration` class. */
ClassValue stopIteration() {
result = TBuiltinClassObject(Builtin::special("StopIteration"))
}
/** Get the `ClassValue` for the class of modules. */
ClassValue module_() {
result = TBuiltinClassObject(Builtin::special("ModuleType"))
}
/** Get the `ClassValue` for the `Exception` class. */
ClassValue exception() {
result = TBuiltinClassObject(Builtin::special("Exception"))
}
/** Get the `ClassValue` for the `BaseException` class. */
ClassValue baseException() {
result = TBuiltinClassObject(Builtin::special("BaseException"))
}
/** Get the `ClassValue` for the `NoneType` class. */
ClassValue nonetype() {
result = TBuiltinClassObject(Builtin::special("NoneType"))
}
/** Get the `ClassValue` for the `TypeError` class */
ClassValue typeError() {
result = TBuiltinClassObject(Builtin::special("TypeError"))
}
/** Get the `ClassValue` for the `NameError` class. */
ClassValue nameError() {
result = TBuiltinClassObject(Builtin::builtin("NameError"))
}
/** Get the `ClassValue` for the `AttributeError` class. */
ClassValue attributeError() {
result = TBuiltinClassObject(Builtin::builtin("AttributeError"))
}
/** Get the `ClassValue` for the `KeyError` class. */
ClassValue keyError() {
result = TBuiltinClassObject(Builtin::builtin("KeyError"))
}
/** Get the `ClassValue` for the `IOError` class. */
ClassValue ioError() {
result = TBuiltinClassObject(Builtin::builtin("IOError"))
}
/** Get the `ClassValue` for the `NotImplementedError` class. */
ClassValue notImplementedError() {
result = TBuiltinClassObject(Builtin::builtin("NotImplementedError"))
}
/** Get the `ClassValue` for the `ImportError` class. */
ClassValue importError() {
result = TBuiltinClassObject(Builtin::builtin("ImportError"))
}
}

View File

@@ -21,7 +21,7 @@ class BottleRequestKind extends TaintKind {
}
}
private class RequestSource extends TaintSource {
private class RequestSource extends HttpRequestTaintSource {
RequestSource() { this.(ControlFlowNode).pointsTo(theBottleRequestObject()) }
override predicate isSourceOf(TaintKind kind) { kind instanceof BottleRequestKind }
@@ -69,7 +69,7 @@ class UntrustedFile extends TaintKind {
// Move UntrustedFile to shared location
//
/** Parameter to a bottle request handler function */
class BottleRequestParameter extends TaintSource {
class BottleRequestParameter extends HttpRequestTaintSource {
BottleRequestParameter() {
exists(BottleRoute route | route.getNamedArgument() = this.(ControlFlowNode).getNode())
}

View File

@@ -25,7 +25,7 @@ class CherryPyRequest extends TaintKind {
}
}
class CherryPyExposedFunctionParameter extends TaintSource {
class CherryPyExposedFunctionParameter extends HttpRequestTaintSource {
CherryPyExposedFunctionParameter() {
exists(Parameter p |
p = any(CherryPyExposedFunction f).getAnArg() and
@@ -39,7 +39,7 @@ class CherryPyExposedFunctionParameter extends TaintSource {
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
}
class CherryPyRequestSource extends TaintSource {
class CherryPyRequestSource extends HttpRequestTaintSource {
CherryPyRequestSource() { this.(ControlFlowNode).pointsTo(Value::named("cherrypy.request")) }
override predicate isSourceOf(TaintKind kind) { kind instanceof CherryPyRequest }

View File

@@ -18,8 +18,8 @@ private ClassValue theDjangoHttpResponseClass() {
not result = theDjangoHttpRedirectClass()
}
/** Instantiation of a django response. */
class DjangoResponseSource extends TaintSource {
/** internal class used for tracking a django response. */
private class DjangoResponseSource extends TaintSource {
DjangoResponseSource() {
exists(ClassValue cls |
cls.getASuperType() = theDjangoHttpResponseClass() and

View File

@@ -35,7 +35,7 @@ class FalconRequest extends TaintKind {
}
}
class FalconRequestParameter extends TaintSource {
class FalconRequestParameter extends HttpRequestTaintSource {
FalconRequestParameter() {
exists(FalconHandlerFunction f | f.getRequest() = this.(ControlFlowNode).getNode())
}

View File

@@ -9,7 +9,8 @@ class FalconResponse extends TaintKind {
FalconResponse() { this = "falcon.response" }
}
class FalconResponseParameter extends TaintSource {
/** Only used internally to track the response parameter */
private class FalconResponseParameter extends TaintSource {
FalconResponseParameter() {
exists(FalconHandlerFunction f | f.getResponse() = this.(ControlFlowNode).getNode())
}

View File

@@ -47,7 +47,7 @@ class FlaskRequestArgs extends HttpRequestTaintSource {
}
/** Source of dictionary whose values are externally controlled */
class FlaskRequestJson extends TaintSource {
class FlaskRequestJson extends HttpRequestTaintSource {
FlaskRequestJson() { flask_request_attr(this, "json") }
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalJsonKind }

View File

@@ -11,7 +11,7 @@ class PyramidRequest extends BaseWebobRequest {
}
/** Source of pyramid request objects */
class PyramidViewArgument extends TaintSource {
class PyramidViewArgument extends HttpRequestTaintSource {
PyramidViewArgument() {
exists(Function view_func |
is_pyramid_view_function(view_func) and

View File

@@ -13,14 +13,16 @@ import Tornado
/**
* Represents an argument to the `tornado.redirect` function.
*/
class TornadoRedirect extends HttpRedirectTaintSink {
override string toString() { result = "tornado.redirect" }
class TornadoHttpRequestHandlerRedirect extends HttpRedirectTaintSink {
override string toString() { result = "tornado.HttpRequestHandler.redirect" }
TornadoRedirect() {
TornadoHttpRequestHandlerRedirect() {
exists(CallNode call, ControlFlowNode node |
node = call.getFunction().(AttrNode).getObject("redirect") and
isTornadoRequestHandlerInstance(node) and
this = call.getAnArg()
this = call.getArg(0)
)
}
override predicate sinks(TaintKind kind) { kind instanceof StringKind }
}

View File

@@ -30,7 +30,7 @@ class TornadoRequest extends TaintKind {
}
}
class TornadoRequestSource extends TaintSource {
class TornadoRequestSource extends HttpRequestTaintSource {
TornadoRequestSource() { isTornadoRequestHandlerInstance(this.(AttrNode).getObject("request")) }
override string toString() { result = "Tornado request source" }
@@ -38,7 +38,7 @@ class TornadoRequestSource extends TaintSource {
override predicate isSourceOf(TaintKind kind) { kind instanceof TornadoRequest }
}
class TornadoExternalInputSource extends TaintSource {
class TornadoExternalInputSource extends HttpRequestTaintSource {
TornadoExternalInputSource() {
exists(string name |
name = "get_argument" or
@@ -55,7 +55,7 @@ class TornadoExternalInputSource extends TaintSource {
override predicate isSourceOf(TaintKind kind) { kind instanceof ExternalStringKind }
}
class TornadoExternalInputListSource extends TaintSource {
class TornadoExternalInputListSource extends HttpRequestTaintSource {
TornadoExternalInputListSource() {
exists(string name |
name = "get_arguments" or

View File

@@ -24,11 +24,8 @@ class TornadoConnectionWrite extends HttpResponseTaintSink {
TornadoConnectionWrite() {
exists(CallNode call, ControlFlowNode conn |
conn = call.getFunction().(AttrNode).getObject("write") and
this = call.getAnArg()
|
this = call.getAnArg() and
exists(TornadoConnection tc | tc.taints(conn))
or
isTornadoRequestHandlerInstance(conn)
)
}
@@ -36,27 +33,13 @@ class TornadoConnectionWrite extends HttpResponseTaintSink {
}
class TornadoHttpRequestHandlerWrite extends HttpResponseTaintSink {
override string toString() { result = "tornado.HttpRequesHandler.write" }
override string toString() { result = "tornado.HttpRequestHandler.write" }
TornadoHttpRequestHandlerWrite() {
exists(CallNode call, ControlFlowNode node |
node = call.getFunction().(AttrNode).getObject("write") and
isTornadoRequestHandlerInstance(node) and
this = call.getAnArg()
)
}
override predicate sinks(TaintKind kind) { kind instanceof StringKind }
}
class TornadoHttpRequestHandlerRedirect extends HttpResponseTaintSink {
override string toString() { result = "tornado.HttpRequesHandler.redirect" }
TornadoHttpRequestHandlerRedirect() {
exists(CallNode call, ControlFlowNode node |
node = call.getFunction().(AttrNode).getObject("redirect") and
isTornadoRequestHandlerInstance(node) and
this = call.getArg(0)
this = call.getAnArg() and
isTornadoRequestHandlerInstance(node)
)
}

View File

@@ -1,5 +1,6 @@
import python
import semmle.python.security.strings.Untrusted
import semmle.python.web.Http
import TurboGears
private class ValidatedMethodParameter extends Parameter {
@@ -11,7 +12,7 @@ private class ValidatedMethodParameter extends Parameter {
}
}
class UnvalidatedControllerMethodParameter extends TaintSource {
class UnvalidatedControllerMethodParameter extends HttpRequestTaintSource {
UnvalidatedControllerMethodParameter() {
exists(Parameter p |
any(TurboGearsControllerMethod m | not m.getName() = "onerror").getAnArg() = p and

View File

@@ -5,6 +5,8 @@ import semmle.python.web.Http
import TurboGears
class ControllerMethodReturnValue extends HttpResponseTaintSink {
override string toString() { result = "TurboGears ControllerMethodReturnValue" }
ControllerMethodReturnValue() {
exists(TurboGearsControllerMethod m |
m.getAReturnValueFlowNode() = this and
@@ -16,6 +18,8 @@ class ControllerMethodReturnValue extends HttpResponseTaintSink {
}
class ControllerMethodTemplatedReturnValue extends HttpResponseTaintSink {
override string toString() { result = "TurboGears ControllerMethodTemplatedReturnValue" }
ControllerMethodTemplatedReturnValue() {
exists(TurboGearsControllerMethod m |
m.getAReturnValueFlowNode() = this and

View File

@@ -26,7 +26,7 @@ class TwistedRequest extends TaintKind {
}
}
class TwistedRequestSource extends TaintSource {
class TwistedRequestSource extends HttpRequestTaintSource {
TwistedRequestSource() { isTwistedRequestInstance(this) }
override string toString() { result = "Twisted request source" }

View File

@@ -5,9 +5,9 @@ import semmle.python.security.strings.Basic
import Twisted
import Request
class TwistedResponse extends TaintSink {
class TwistedResponse extends HttpResponseTaintSink {
TwistedResponse() {
exists(PythonFunctionValue func, string name, Return ret |
exists(PythonFunctionValue func, string name |
isKnownRequestHandlerMethodName(name) and
name = func.getName() and
func = getTwistedRequestHandlerMethod(name) and

View File

@@ -1,2 +1 @@
automatic_locations: true
semmle-extractor-options: --lang=2

View File

@@ -1,2 +1,2 @@
semmle-extractor-options: --lang=2 --max-import-depth=3
semmle-extractor-options: --lang=2 --max-import-depth=4
optimize: true

View File

@@ -1,3 +1,9 @@
| six | Module six |
| six.moves | Module six.moves |
| six | Package six |
| six.moves | Package six.moves |
| six.moves.http_client | Module httplib |
| six.moves.http_client.HTTPConnection | class HTTPConnection |
| six.moves.range | builtin-class xrange |
| six.moves.urllib | Package six.moves.urllib |
| six.moves.urllib.parse | Module six.moves.urllib_parse |
| six.moves.urllib.parse.urlsplit | Function urlsplit |
| six.moves.zip | builtin-class itertools.izip |

View File

@@ -1,15 +1,11 @@
import python
string longname(Expr e) {
result = e.(Name).getId()
or
exists(Attribute a |
a = e |
result = longname(a.getObject()) + "." + a.getName()
)
exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName())
}
from Expr e, Object o
where e.refersTo(o) and e.getLocation().getFile().getShortName() = "test.py"
select longname(e), o.toString()
from Expr e, Value v
where e.pointsTo(v) and e.getLocation().getFile().getShortName() = "test.py"
select longname(e), v.toString()

View File

@@ -1,268 +0,0 @@
| Module six | BytesIO | class StringIO |
| Module six | Iterator | class Iterator |
| Module six | MAXSIZE | int() |
| Module six | PY2 | bool True |
| Module six | PY3 | bool False |
| Module six | StringIO | class StringIO |
| Module six | __author__ | str b'Benjamin Peterson <benjamin@python.org>' |
| Module six | __name__ | str b'six' |
| Module six | __version__ | str b'1.5.2' |
| Module six | _add_doc | Function _add_doc |
| Module six | _func_closure | str b'func_closure' |
| Module six | _func_code | str b'func_code' |
| Module six | _func_defaults | str b'func_defaults' |
| Module six | _func_globals | str b'func_globals' |
| Module six | _import_module | Function _import_module |
| Module six | _iteritems | str b'iteritems' |
| Module six | _iterkeys | str b'iterkeys' |
| Module six | _iterlists | str b'iterlists' |
| Module six | _itervalues | str b'itervalues' |
| Module six | _meth_func | str b'im_func' |
| Module six | _meth_self | str b'im_self' |
| Module six | add_metaclass | Function add_metaclass |
| Module six | add_move | Function add_move |
| Module six | advance_iterator | Builtin-function next |
| Module six | b | Function b |
| Module six | binary_type | builtin-class str |
| Module six | byte2int | Function byte2int |
| Module six | callable | Builtin-function callable |
| Module six | callable | Function callable |
| Module six | class_types | Tuple |
| Module six | create_bound_method | Function create_bound_method |
| Module six | exec_ | Function exec_ |
| Module six | get_function_closure | Attribute() |
| Module six | get_function_code | Attribute() |
| Module six | get_function_defaults | Attribute() |
| Module six | get_function_globals | Attribute() |
| Module six | get_method_function | Attribute() |
| Module six | get_method_self | Attribute() |
| Module six | get_unbound_function | Function get_unbound_function |
| Module six | indexbytes | Function indexbytes |
| Module six | int2byte | Builtin-function chr |
| Module six | integer_types | Tuple |
| Module six | iterbytes | Function iterbytes |
| Module six | iteritems | Function iteritems |
| Module six | iterkeys | Function iterkeys |
| Module six | iterlists | Function iterlists |
| Module six | itervalues | Function itervalues |
| Module six | moves | Module six.moves |
| Module six | next | Builtin-function next |
| Module six | operator | Module operator |
| Module six | print_ | Function print_ |
| Module six | remove_move | Function remove_move |
| Module six | reraise | Function reraise |
| Module six | string_types | Tuple |
| Module six | sys | Module sys |
| Module six | text_type | builtin-class unicode |
| Module six | types | Module types |
| Module six | u | Function u |
| Module six | unichr | Builtin-function unichr |
| Module six | with_metaclass | Function with_metaclass |
| Module six.__init__ | BytesIO | class StringIO |
| Module six.__init__ | Iterator | class Iterator |
| Module six.__init__ | MAXSIZE | int() |
| Module six.__init__ | PY2 | bool True |
| Module six.__init__ | PY3 | bool False |
| Module six.__init__ | StringIO | class StringIO |
| Module six.__init__ | __author__ | str b'Benjamin Peterson <benjamin@python.org>' |
| Module six.__init__ | __name__ | str b'six' |
| Module six.__init__ | __version__ | str b'1.5.2' |
| Module six.__init__ | _add_doc | Function _add_doc |
| Module six.__init__ | _func_closure | str b'func_closure' |
| Module six.__init__ | _func_code | str b'func_code' |
| Module six.__init__ | _func_defaults | str b'func_defaults' |
| Module six.__init__ | _func_globals | str b'func_globals' |
| Module six.__init__ | _import_module | Function _import_module |
| Module six.__init__ | _iteritems | str b'iteritems' |
| Module six.__init__ | _iterkeys | str b'iterkeys' |
| Module six.__init__ | _iterlists | str b'iterlists' |
| Module six.__init__ | _itervalues | str b'itervalues' |
| Module six.__init__ | _meth_func | str b'im_func' |
| Module six.__init__ | _meth_self | str b'im_self' |
| Module six.__init__ | add_metaclass | Function add_metaclass |
| Module six.__init__ | add_move | Function add_move |
| Module six.__init__ | advance_iterator | Builtin-function next |
| Module six.__init__ | b | Function b |
| Module six.__init__ | binary_type | builtin-class str |
| Module six.__init__ | byte2int | Function byte2int |
| Module six.__init__ | callable | Builtin-function callable |
| Module six.__init__ | callable | Function callable |
| Module six.__init__ | class_types | Tuple |
| Module six.__init__ | create_bound_method | Function create_bound_method |
| Module six.__init__ | exec_ | Function exec_ |
| Module six.__init__ | get_function_closure | Attribute() |
| Module six.__init__ | get_function_code | Attribute() |
| Module six.__init__ | get_function_defaults | Attribute() |
| Module six.__init__ | get_function_globals | Attribute() |
| Module six.__init__ | get_method_function | Attribute() |
| Module six.__init__ | get_method_self | Attribute() |
| Module six.__init__ | get_unbound_function | Function get_unbound_function |
| Module six.__init__ | indexbytes | Function indexbytes |
| Module six.__init__ | int2byte | Builtin-function chr |
| Module six.__init__ | integer_types | Tuple |
| Module six.__init__ | iterbytes | Function iterbytes |
| Module six.__init__ | iteritems | Function iteritems |
| Module six.__init__ | iterkeys | Function iterkeys |
| Module six.__init__ | iterlists | Function iterlists |
| Module six.__init__ | itervalues | Function itervalues |
| Module six.__init__ | moves | Module six.moves |
| Module six.__init__ | next | Builtin-function next |
| Module six.__init__ | operator | Module operator |
| Module six.__init__ | print_ | Function print_ |
| Module six.__init__ | remove_move | Function remove_move |
| Module six.__init__ | reraise | Function reraise |
| Module six.__init__ | string_types | Tuple |
| Module six.__init__ | sys | Module sys |
| Module six.__init__ | text_type | builtin-class unicode |
| Module six.__init__ | types | Module types |
| Module six.__init__ | u | Function u |
| Module six.__init__ | unichr | Builtin-function unichr |
| Module six.__init__ | with_metaclass | Function with_metaclass |
| Module six.moves | BaseHTTPServer | Module BaseHTTPServer |
| Module six.moves | CGIHTTPServer | Module CGIHTTPServer |
| Module six.moves | PY2 | bool True |
| Module six.moves | PY3 | bool False |
| Module six.moves | SimpleHTTPServer | Module SimpleHTTPServer |
| Module six.moves | StringIO | class StringIO |
| Module six.moves | UserDict | class UserDict |
| Module six.moves | UserList | class UserList |
| Module six.moves | UserString | class UserString |
| Module six.moves | __name__ | str b'six.moves' |
| Module six.moves | _dummy_thread | Module dummy_thread |
| Module six.moves | _thread | Module thread |
| Module six.moves | builtins | Module __builtin__ |
| Module six.moves | cPickle | Module cPickle |
| Module six.moves | cStringIO | Builtin-function StringIO |
| Module six.moves | configparser | Module ConfigParser |
| Module six.moves | copyreg | Module copy_reg |
| Module six.moves | filter | builtin-class itertools.ifilter |
| Module six.moves | filterfalse | builtin-class itertools.ifilterfalse |
| Module six.moves | html_entities | Module htmlentitydefs |
| Module six.moves | html_parser | Module HTMLParser |
| Module six.moves | http_client | Module httplib |
| Module six.moves | http_cookiejar | Module cookielib |
| Module six.moves | http_cookies | Module Cookie |
| Module six.moves | input | Builtin-function raw_input |
| Module six.moves | intern | Builtin-function intern |
| Module six.moves | map | builtin-class itertools.imap |
| Module six.moves | queue | Module Queue |
| Module six.moves | range | builtin-class xrange |
| Module six.moves | reduce | Builtin-function reduce |
| Module six.moves | reload_module | Builtin-function reload |
| Module six.moves | reprlib | Module repr |
| Module six.moves | shlex_quote | Function quote |
| Module six.moves | socketserver | Module SocketServer |
| Module six.moves | tkinter | Module Tkinter |
| Module six.moves | tkinter_colorchooser | Module tkColorChooser |
| Module six.moves | tkinter_commondialog | Module tkCommonDialog |
| Module six.moves | tkinter_constants | Module Tkconstants |
| Module six.moves | tkinter_dialog | Module Dialog |
| Module six.moves | tkinter_dnd | Module Tkdnd |
| Module six.moves | tkinter_filedialog | Module FileDialog |
| Module six.moves | tkinter_font | Module tkFont |
| Module six.moves | tkinter_messagebox | Module tkMessageBox |
| Module six.moves | tkinter_scrolledtext | Module ScrolledText |
| Module six.moves | tkinter_simpledialog | Module SimpleDialog |
| Module six.moves | tkinter_tix | Module Tix |
| Module six.moves | tkinter_tkfiledialog | Module tkFileDialog |
| Module six.moves | tkinter_tksimpledialog | Module tkSimpleDialog |
| Module six.moves | tkinter_ttk | Module ttk |
| Module six.moves | urllib | Module six.moves.urllib |
| Module six.moves | urllib_error | Module six.moves.urllib_error |
| Module six.moves | urllib_parse | Module six.moves.urllib_parse |
| Module six.moves | urllib_request | Module six.moves.urllib_request |
| Module six.moves | urllib_response | Module six.moves.urllib_response |
| Module six.moves | urllib_robotparser | Module six.moves.urllib_robotparser |
| Module six.moves | xmlrpc_client | Module xmlrpclib |
| Module six.moves | xmlrpc_server | Module SimpleXMLRPCServer |
| Module six.moves | xrange | builtin-class xrange |
| Module six.moves | zip | builtin-class itertools.izip |
| Module six.moves | zip_longest | builtin-class itertools.izip_longest |
| Module six.moves.__init__ | BaseHTTPServer | Module BaseHTTPServer |
| Module six.moves.__init__ | CGIHTTPServer | Module CGIHTTPServer |
| Module six.moves.__init__ | PY2 | bool True |
| Module six.moves.__init__ | PY3 | bool False |
| Module six.moves.__init__ | SimpleHTTPServer | Module SimpleHTTPServer |
| Module six.moves.__init__ | StringIO | class StringIO |
| Module six.moves.__init__ | UserDict | class UserDict |
| Module six.moves.__init__ | UserList | class UserList |
| Module six.moves.__init__ | UserString | class UserString |
| Module six.moves.__init__ | __name__ | str b'six.moves' |
| Module six.moves.__init__ | _dummy_thread | Module dummy_thread |
| Module six.moves.__init__ | _thread | Module thread |
| Module six.moves.__init__ | builtins | Module __builtin__ |
| Module six.moves.__init__ | cPickle | Module cPickle |
| Module six.moves.__init__ | cStringIO | Builtin-function StringIO |
| Module six.moves.__init__ | configparser | Module ConfigParser |
| Module six.moves.__init__ | copyreg | Module copy_reg |
| Module six.moves.__init__ | filter | builtin-class itertools.ifilter |
| Module six.moves.__init__ | filterfalse | builtin-class itertools.ifilterfalse |
| Module six.moves.__init__ | html_entities | Module htmlentitydefs |
| Module six.moves.__init__ | html_parser | Module HTMLParser |
| Module six.moves.__init__ | http_client | Module httplib |
| Module six.moves.__init__ | http_cookiejar | Module cookielib |
| Module six.moves.__init__ | http_cookies | Module Cookie |
| Module six.moves.__init__ | input | Builtin-function raw_input |
| Module six.moves.__init__ | intern | Builtin-function intern |
| Module six.moves.__init__ | map | builtin-class itertools.imap |
| Module six.moves.__init__ | queue | Module Queue |
| Module six.moves.__init__ | range | builtin-class xrange |
| Module six.moves.__init__ | reduce | Builtin-function reduce |
| Module six.moves.__init__ | reload_module | Builtin-function reload |
| Module six.moves.__init__ | reprlib | Module repr |
| Module six.moves.__init__ | shlex_quote | Function quote |
| Module six.moves.__init__ | socketserver | Module SocketServer |
| Module six.moves.__init__ | tkinter | Module Tkinter |
| Module six.moves.__init__ | tkinter_colorchooser | Module tkColorChooser |
| Module six.moves.__init__ | tkinter_commondialog | Module tkCommonDialog |
| Module six.moves.__init__ | tkinter_constants | Module Tkconstants |
| Module six.moves.__init__ | tkinter_dialog | Module Dialog |
| Module six.moves.__init__ | tkinter_dnd | Module Tkdnd |
| Module six.moves.__init__ | tkinter_filedialog | Module FileDialog |
| Module six.moves.__init__ | tkinter_font | Module tkFont |
| Module six.moves.__init__ | tkinter_messagebox | Module tkMessageBox |
| Module six.moves.__init__ | tkinter_scrolledtext | Module ScrolledText |
| Module six.moves.__init__ | tkinter_simpledialog | Module SimpleDialog |
| Module six.moves.__init__ | tkinter_tix | Module Tix |
| Module six.moves.__init__ | tkinter_tkfiledialog | Module tkFileDialog |
| Module six.moves.__init__ | tkinter_tksimpledialog | Module tkSimpleDialog |
| Module six.moves.__init__ | tkinter_ttk | Module ttk |
| Module six.moves.__init__ | urllib | Module six.moves.urllib |
| Module six.moves.__init__ | urllib_error | Module six.moves.urllib_error |
| Module six.moves.__init__ | urllib_parse | Module six.moves.urllib_parse |
| Module six.moves.__init__ | urllib_request | Module six.moves.urllib_request |
| Module six.moves.__init__ | urllib_response | Module six.moves.urllib_response |
| Module six.moves.__init__ | urllib_robotparser | Module six.moves.urllib_robotparser |
| Module six.moves.__init__ | xmlrpc_client | Module xmlrpclib |
| Module six.moves.__init__ | xmlrpc_server | Module SimpleXMLRPCServer |
| Module six.moves.__init__ | xrange | builtin-class xrange |
| Module six.moves.__init__ | zip | builtin-class itertools.izip |
| Module six.moves.__init__ | zip_longest | builtin-class itertools.izip_longest |
| Module six.moves.urllib | __name__ | str b'six.moves.urllib' |
| Module six.moves.urllib | error | Module six.moves.urllib_error |
| Module six.moves.urllib | parse | Module six.moves.urllib_parse |
| Module six.moves.urllib | request | Module six.moves.urllib_request |
| Module six.moves.urllib | response | Module six.moves.urllib_response |
| Module six.moves.urllib | robotparser | Module six.moves.urllib_robotparser |
| Module six.moves.urllib.__init__ | __name__ | str b'six.moves.urllib' |
| Module six.moves.urllib.__init__ | error | Module six.moves.urllib_error |
| Module six.moves.urllib.__init__ | parse | Module six.moves.urllib_parse |
| Module six.moves.urllib.__init__ | request | Module six.moves.urllib_request |
| Module six.moves.urllib.__init__ | response | Module six.moves.urllib_response |
| Module six.moves.urllib.__init__ | robotparser | Module six.moves.urllib_robotparser |
| Module six.moves.urllib_error | PY2 | bool True |
| Module six.moves.urllib_error | PY3 | bool False |
| Module six.moves.urllib_error | __name__ | str b'six.moves.urllib_error' |
| Module six.moves.urllib_parse | PY2 | bool True |
| Module six.moves.urllib_parse | PY3 | bool False |
| Module six.moves.urllib_parse | __name__ | str b'six.moves.urllib_parse' |
| Module six.moves.urllib_request | PY2 | bool True |
| Module six.moves.urllib_request | PY3 | bool False |
| Module six.moves.urllib_request | __name__ | str b'six.moves.urllib_request' |
| Module six.moves.urllib_response | PY2 | bool True |
| Module six.moves.urllib_response | PY3 | bool False |
| Module six.moves.urllib_response | __name__ | str b'six.moves.urllib_response' |
| Module six.moves.urllib_robotparser | PY2 | bool True |
| Module six.moves.urllib_robotparser | PY3 | bool False |
| Module six.moves.urllib_robotparser | RobotFileParser | class RobotFileParser |
| Module six.moves.urllib_robotparser | __name__ | str b'six.moves.urllib_robotparser' |

View File

@@ -1,5 +1,9 @@
import six
#Check that some expected attributes are visible
# Check that some expected attributes are visible -- this is the reason we added stubs in
# the first place! If this works, we're happy!
six.moves
six.moves.range
six.moves.zip
six.moves.http_client.HTTPConnection
six.moves.urllib.parse.urlsplit

View File

@@ -1,11 +0,0 @@
import python
predicate six(ModuleObject m) {
m.getName() = "six"
or
six(m.getPackage())
}
from ModuleObject mod, string name, Object obj
where mod.attributeRefersTo(name, obj, _) and six(mod)
select mod.toString(), name, obj.toString()

View File

@@ -1,2 +1 @@
automatic_locations: true
semmle-extractor-options: --lang=2 --max-import-depth=1

View File

@@ -1 +1 @@
semmle-extractor-options: --max-import-depth=0
semmle-extractor-options: --lang=3 --max-import-depth=0

View File

@@ -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

View File

@@ -1 +1 @@
semmle-extractor-options: --max-import-depth=3
semmle-extractor-options: --lang=3 --max-import-depth=3

View File

@@ -1 +1 @@
semmle-extractor-options: --respect-init=true
semmle-extractor-options: --lang=3 --respect-init=true

View File

@@ -1 +1 @@
automatic_locations: true
semmle-extractor-options: --lang=3

View File

@@ -1,2 +1,2 @@
semmle-extractor-options: --max-import-depth=3
semmle-extractor-options: --lang=3 --max-import-depth=4
optimize: true

View File

@@ -1,3 +1,9 @@
| six | Module six |
| six.moves | Module six.moves |
| six | Package six |
| six.moves | Package six.moves |
| six.moves.http_client | Module http.client |
| six.moves.http_client.HTTPConnection | class HTTPConnection |
| six.moves.range | builtin-class range |
| six.moves.urllib | Package six.moves.urllib |
| six.moves.urllib.parse | Module six.moves.urllib_parse |
| six.moves.urllib.parse.urlsplit | Function urlsplit |
| six.moves.zip | builtin-class zip |

View File

@@ -1,16 +1,11 @@
import python
string longname(Expr e) {
result = e.(Name).getId()
or
exists(Attribute a |
a = e |
result = longname(a.getObject()) + "." + a.getName()
)
exists(Attribute a | a = e | result = longname(a.getObject()) + "." + a.getName())
}
from Expr e, Object o
where e.refersTo(o) and e.getLocation().getFile().getShortName() = "test.py"
select longname(e), o.toString()
from Expr e, Value v
where e.pointsTo(v) and e.getLocation().getFile().getShortName() = "test.py"
select longname(e), v.toString()

View File

@@ -1,262 +0,0 @@
| Module six | BytesIO | builtin-class _io.BytesIO |
| Module six | Iterator | builtin-class object |
| Module six | MAXSIZE | int 9223372036854775807 |
| Module six | PY2 | bool False |
| Module six | PY3 | bool True |
| Module six | StringIO | builtin-class _io.StringIO |
| Module six | __author__ | str u'Benjamin Peterson <benjamin@python.org>' |
| Module six | __name__ | str u'six' |
| Module six | __version__ | str u'1.5.2' |
| Module six | _add_doc | Function _add_doc |
| Module six | _func_closure | str u'__closure__' |
| Module six | _func_code | str u'__code__' |
| Module six | _func_defaults | str u'__defaults__' |
| Module six | _func_globals | str u'__globals__' |
| Module six | _import_module | Function _import_module |
| Module six | _iteritems | str u'items' |
| Module six | _iterkeys | str u'keys' |
| Module six | _iterlists | str u'lists' |
| Module six | _itervalues | str u'values' |
| Module six | _meth_func | str u'__func__' |
| Module six | _meth_self | str u'__self__' |
| Module six | add_metaclass | Function add_metaclass |
| Module six | add_move | Function add_move |
| Module six | advance_iterator | Builtin-function next |
| Module six | b | Function b |
| Module six | binary_type | builtin-class bytes |
| Module six | byte2int | Function byte2int |
| Module six | callable | Builtin-function callable |
| Module six | callable | Function callable |
| Module six | class_types | Tuple |
| Module six | create_bound_method | builtin-class method |
| Module six | get_function_closure | Attribute() |
| Module six | get_function_code | Attribute() |
| Module six | get_function_defaults | Attribute() |
| Module six | get_function_globals | Attribute() |
| Module six | get_method_function | Attribute() |
| Module six | get_method_self | Attribute() |
| Module six | get_unbound_function | Function get_unbound_function |
| Module six | indexbytes | Builtin-function getitem |
| Module six | int2byte | Function int2byte |
| Module six | integer_types | Tuple |
| Module six | io | Module io |
| Module six | iterbytes | Builtin-function iter |
| Module six | iteritems | Function iteritems |
| Module six | iterkeys | Function iterkeys |
| Module six | iterlists | Function iterlists |
| Module six | itervalues | Function itervalues |
| Module six | moves | Module six.moves |
| Module six | next | Builtin-function next |
| Module six | operator | Module operator |
| Module six | print_ | Function print_ |
| Module six | remove_move | Function remove_move |
| Module six | reraise | Function reraise |
| Module six | string_types | Tuple |
| Module six | sys | Module sys |
| Module six | text_type | builtin-class str |
| Module six | types | Module types |
| Module six | u | Function u |
| Module six | unichr | Builtin-function chr |
| Module six | with_metaclass | Function with_metaclass |
| Module six.__init__ | BytesIO | builtin-class _io.BytesIO |
| Module six.__init__ | Iterator | builtin-class object |
| Module six.__init__ | MAXSIZE | int 9223372036854775807 |
| Module six.__init__ | PY2 | bool False |
| Module six.__init__ | PY3 | bool True |
| Module six.__init__ | StringIO | builtin-class _io.StringIO |
| Module six.__init__ | __author__ | str u'Benjamin Peterson <benjamin@python.org>' |
| Module six.__init__ | __name__ | str u'six' |
| Module six.__init__ | __version__ | str u'1.5.2' |
| Module six.__init__ | _add_doc | Function _add_doc |
| Module six.__init__ | _func_closure | str u'__closure__' |
| Module six.__init__ | _func_code | str u'__code__' |
| Module six.__init__ | _func_defaults | str u'__defaults__' |
| Module six.__init__ | _func_globals | str u'__globals__' |
| Module six.__init__ | _import_module | Function _import_module |
| Module six.__init__ | _iteritems | str u'items' |
| Module six.__init__ | _iterkeys | str u'keys' |
| Module six.__init__ | _iterlists | str u'lists' |
| Module six.__init__ | _itervalues | str u'values' |
| Module six.__init__ | _meth_func | str u'__func__' |
| Module six.__init__ | _meth_self | str u'__self__' |
| Module six.__init__ | add_metaclass | Function add_metaclass |
| Module six.__init__ | add_move | Function add_move |
| Module six.__init__ | advance_iterator | Builtin-function next |
| Module six.__init__ | b | Function b |
| Module six.__init__ | binary_type | builtin-class bytes |
| Module six.__init__ | byte2int | Function byte2int |
| Module six.__init__ | callable | Builtin-function callable |
| Module six.__init__ | callable | Function callable |
| Module six.__init__ | class_types | Tuple |
| Module six.__init__ | create_bound_method | builtin-class method |
| Module six.__init__ | get_function_closure | Attribute() |
| Module six.__init__ | get_function_code | Attribute() |
| Module six.__init__ | get_function_defaults | Attribute() |
| Module six.__init__ | get_function_globals | Attribute() |
| Module six.__init__ | get_method_function | Attribute() |
| Module six.__init__ | get_method_self | Attribute() |
| Module six.__init__ | get_unbound_function | Function get_unbound_function |
| Module six.__init__ | indexbytes | Builtin-function getitem |
| Module six.__init__ | int2byte | Function int2byte |
| Module six.__init__ | integer_types | Tuple |
| Module six.__init__ | io | Module io |
| Module six.__init__ | iterbytes | Builtin-function iter |
| Module six.__init__ | iteritems | Function iteritems |
| Module six.__init__ | iterkeys | Function iterkeys |
| Module six.__init__ | iterlists | Function iterlists |
| Module six.__init__ | itervalues | Function itervalues |
| Module six.__init__ | moves | Module six.moves |
| Module six.__init__ | next | Builtin-function next |
| Module six.__init__ | operator | Module operator |
| Module six.__init__ | print_ | Function print_ |
| Module six.__init__ | remove_move | Function remove_move |
| Module six.__init__ | reraise | Function reraise |
| Module six.__init__ | string_types | Tuple |
| Module six.__init__ | sys | Module sys |
| Module six.__init__ | text_type | builtin-class str |
| Module six.__init__ | types | Module types |
| Module six.__init__ | u | Function u |
| Module six.__init__ | unichr | Builtin-function chr |
| Module six.__init__ | with_metaclass | Function with_metaclass |
| Module six.moves | BaseHTTPServer | Module http.server |
| Module six.moves | CGIHTTPServer | Module http.server |
| Module six.moves | PY2 | bool False |
| Module six.moves | PY3 | bool True |
| Module six.moves | SimpleHTTPServer | Module http.server |
| Module six.moves | StringIO | builtin-class _io.StringIO |
| Module six.moves | UserString | class UserString |
| Module six.moves | __name__ | str u'six.moves' |
| Module six.moves | _thread | Module _thread |
| Module six.moves | builtins | Module builtins |
| Module six.moves | cPickle | Module pickle |
| Module six.moves | cStringIO | builtin-class _io.StringIO |
| Module six.moves | configparser | Module configparser |
| Module six.moves | copyreg | Module copyreg |
| Module six.moves | dbm_gnu | Module dbm.gnu |
| Module six.moves | email_mime_base | Module email.mime.base |
| Module six.moves | email_mime_multipart | Module email.mime.multipart |
| Module six.moves | email_mime_text | Module email.mime.text |
| Module six.moves | filter | builtin-class filter |
| Module six.moves | filterfalse | builtin-class itertools.filterfalse |
| Module six.moves | html_entities | Module html.entities |
| Module six.moves | html_parser | Module html.parser |
| Module six.moves | http_client | Module http.client |
| Module six.moves | http_cookiejar | Module http.cookiejar |
| Module six.moves | http_cookies | Module http.cookies |
| Module six.moves | input | Builtin-function input |
| Module six.moves | map | builtin-class map |
| Module six.moves | queue | Module queue |
| Module six.moves | range | builtin-class range |
| Module six.moves | reload_module | Function reload |
| Module six.moves | reprlib | Module reprlib |
| Module six.moves | socketserver | Module socketserver |
| Module six.moves | tkinter | Module tkinter |
| Module six.moves | tkinter_colorchooser | Module tkinter.colorchooser |
| Module six.moves | tkinter_commondialog | Module tkinter.commondialog |
| Module six.moves | tkinter_constants | Module tkinter.constants |
| Module six.moves | tkinter_dialog | Module tkinter.dialog |
| Module six.moves | tkinter_dnd | Module tkinter.dnd |
| Module six.moves | tkinter_filedialog | Module tkinter.filedialog |
| Module six.moves | tkinter_font | Module tkinter.font |
| Module six.moves | tkinter_messagebox | Module tkinter.messagebox |
| Module six.moves | tkinter_scrolledtext | Module tkinter.scrolledtext |
| Module six.moves | tkinter_simpledialog | Module tkinter.simpledialog |
| Module six.moves | tkinter_tix | Module tkinter.tix |
| Module six.moves | tkinter_tkfiledialog | Module tkinter.filedialog |
| Module six.moves | tkinter_tksimpledialog | Module tkinter.simpledialog |
| Module six.moves | tkinter_ttk | Module tkinter.ttk |
| Module six.moves | urllib | Module six.moves.urllib |
| Module six.moves | urllib_error | Module six.moves.urllib_error |
| Module six.moves | urllib_parse | Module six.moves.urllib_parse |
| Module six.moves | urllib_request | Module six.moves.urllib_request |
| Module six.moves | urllib_response | Module six.moves.urllib_response |
| Module six.moves | urllib_robotparser | Module six.moves.urllib_robotparser |
| Module six.moves | xmlrpc_client | Module xmlrpc.client |
| Module six.moves | xrange | builtin-class range |
| Module six.moves | zip | builtin-class zip |
| Module six.moves | zip_longest | builtin-class itertools.zip_longest |
| Module six.moves.__init__ | BaseHTTPServer | Module http.server |
| Module six.moves.__init__ | CGIHTTPServer | Module http.server |
| Module six.moves.__init__ | PY2 | bool False |
| Module six.moves.__init__ | PY3 | bool True |
| Module six.moves.__init__ | SimpleHTTPServer | Module http.server |
| Module six.moves.__init__ | StringIO | builtin-class _io.StringIO |
| Module six.moves.__init__ | UserString | class UserString |
| Module six.moves.__init__ | __name__ | str u'six.moves' |
| Module six.moves.__init__ | _thread | Module _thread |
| Module six.moves.__init__ | builtins | Module builtins |
| Module six.moves.__init__ | cPickle | Module pickle |
| Module six.moves.__init__ | cStringIO | builtin-class _io.StringIO |
| Module six.moves.__init__ | configparser | Module configparser |
| Module six.moves.__init__ | copyreg | Module copyreg |
| Module six.moves.__init__ | dbm_gnu | Module dbm.gnu |
| Module six.moves.__init__ | email_mime_base | Module email.mime.base |
| Module six.moves.__init__ | email_mime_multipart | Module email.mime.multipart |
| Module six.moves.__init__ | email_mime_text | Module email.mime.text |
| Module six.moves.__init__ | filter | builtin-class filter |
| Module six.moves.__init__ | filterfalse | builtin-class itertools.filterfalse |
| Module six.moves.__init__ | html_entities | Module html.entities |
| Module six.moves.__init__ | html_parser | Module html.parser |
| Module six.moves.__init__ | http_client | Module http.client |
| Module six.moves.__init__ | http_cookiejar | Module http.cookiejar |
| Module six.moves.__init__ | http_cookies | Module http.cookies |
| Module six.moves.__init__ | input | Builtin-function input |
| Module six.moves.__init__ | map | builtin-class map |
| Module six.moves.__init__ | queue | Module queue |
| Module six.moves.__init__ | range | builtin-class range |
| Module six.moves.__init__ | reload_module | Function reload |
| Module six.moves.__init__ | reprlib | Module reprlib |
| Module six.moves.__init__ | socketserver | Module socketserver |
| Module six.moves.__init__ | tkinter | Module tkinter |
| Module six.moves.__init__ | tkinter_colorchooser | Module tkinter.colorchooser |
| Module six.moves.__init__ | tkinter_commondialog | Module tkinter.commondialog |
| Module six.moves.__init__ | tkinter_constants | Module tkinter.constants |
| Module six.moves.__init__ | tkinter_dialog | Module tkinter.dialog |
| Module six.moves.__init__ | tkinter_dnd | Module tkinter.dnd |
| Module six.moves.__init__ | tkinter_filedialog | Module tkinter.filedialog |
| Module six.moves.__init__ | tkinter_font | Module tkinter.font |
| Module six.moves.__init__ | tkinter_messagebox | Module tkinter.messagebox |
| Module six.moves.__init__ | tkinter_scrolledtext | Module tkinter.scrolledtext |
| Module six.moves.__init__ | tkinter_simpledialog | Module tkinter.simpledialog |
| Module six.moves.__init__ | tkinter_tix | Module tkinter.tix |
| Module six.moves.__init__ | tkinter_tkfiledialog | Module tkinter.filedialog |
| Module six.moves.__init__ | tkinter_tksimpledialog | Module tkinter.simpledialog |
| Module six.moves.__init__ | tkinter_ttk | Module tkinter.ttk |
| Module six.moves.__init__ | urllib | Module six.moves.urllib |
| Module six.moves.__init__ | urllib_error | Module six.moves.urllib_error |
| Module six.moves.__init__ | urllib_parse | Module six.moves.urllib_parse |
| Module six.moves.__init__ | urllib_request | Module six.moves.urllib_request |
| Module six.moves.__init__ | urllib_response | Module six.moves.urllib_response |
| Module six.moves.__init__ | urllib_robotparser | Module six.moves.urllib_robotparser |
| Module six.moves.__init__ | xmlrpc_client | Module xmlrpc.client |
| Module six.moves.__init__ | xrange | builtin-class range |
| Module six.moves.__init__ | zip | builtin-class zip |
| Module six.moves.__init__ | zip_longest | builtin-class itertools.zip_longest |
| Module six.moves.urllib | __name__ | str u'six.moves.urllib' |
| Module six.moves.urllib | error | Module six.moves.urllib_error |
| Module six.moves.urllib | parse | Module six.moves.urllib_parse |
| Module six.moves.urllib | request | Module six.moves.urllib_request |
| Module six.moves.urllib | response | Module six.moves.urllib_response |
| Module six.moves.urllib | robotparser | Module six.moves.urllib_robotparser |
| Module six.moves.urllib.__init__ | __name__ | str u'six.moves.urllib' |
| Module six.moves.urllib.__init__ | error | Module six.moves.urllib_error |
| Module six.moves.urllib.__init__ | parse | Module six.moves.urllib_parse |
| Module six.moves.urllib.__init__ | request | Module six.moves.urllib_request |
| Module six.moves.urllib.__init__ | response | Module six.moves.urllib_response |
| Module six.moves.urllib.__init__ | robotparser | Module six.moves.urllib_robotparser |
| Module six.moves.urllib_error | PY2 | bool False |
| Module six.moves.urllib_error | PY3 | bool True |
| Module six.moves.urllib_error | __name__ | str u'six.moves.urllib_error' |
| Module six.moves.urllib_parse | PY2 | bool False |
| Module six.moves.urllib_parse | PY3 | bool True |
| Module six.moves.urllib_parse | __name__ | str u'six.moves.urllib_parse' |
| Module six.moves.urllib_request | PY2 | bool False |
| Module six.moves.urllib_request | PY3 | bool True |
| Module six.moves.urllib_request | __name__ | str u'six.moves.urllib_request' |
| Module six.moves.urllib_response | PY2 | bool False |
| Module six.moves.urllib_response | PY3 | bool True |
| Module six.moves.urllib_response | __name__ | str u'six.moves.urllib_response' |
| Module six.moves.urllib_robotparser | PY2 | bool False |
| Module six.moves.urllib_robotparser | PY3 | bool True |
| Module six.moves.urllib_robotparser | RobotFileParser | class RobotFileParser |
| Module six.moves.urllib_robotparser | __name__ | str u'six.moves.urllib_robotparser' |

View File

@@ -1,5 +1,9 @@
import six
#Check that some expected attributes are visible
# Check that some expected attributes are visible -- this is the reason we added stubs in
# the first place! If this works, we're happy!
six.moves
six.moves.range
six.moves.range
six.moves.zip
six.moves.http_client.HTTPConnection
six.moves.urllib.parse.urlsplit

View File

@@ -1,11 +0,0 @@
import python
predicate six(ModuleObject m) {
m.getName() = "six"
or
six(m.getPackage())
}
from ModuleObject mod, string name, Object obj
where mod.attributeRefersTo(name, obj, _) and six(mod)
select mod.toString(), name, obj.toString()

View 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" }
}

View File

@@ -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] |

View 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

View 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)

View File

@@ -1 +1 @@
semmle-extractor-options: --max-import-depth=2
semmle-extractor-options: --lang=3 --max-import-depth=2

View File

@@ -1,2 +1 @@
automatic_locations: true
semmle-extractor-options: --max-import-depth=1
semmle-extractor-options: --lang=3 --max-import-depth=1

View File

@@ -12,15 +12,19 @@
| 21 | file://:0:0:0:0 | bool True |
| 22 | file://:0:0:0:0 | bool False |
| 22 | file://:0:0:0:0 | bool True |
| 24 | file://:0:0:0:0 | bool False |
| 24 | file://:0:0:0:0 | bool True |
| 25 | file://:0:0:0:0 | bool False |
| 26 | file://:0:0:0:0 | bool False |
| 27 | file://:0:0:0:0 | bool True |
| 28 | file://:0:0:0:0 | bool True |
| 25 | file://:0:0:0:0 | bool True |
| 28 | file://:0:0:0:0 | bool False |
| 29 | file://:0:0:0:0 | bool False |
| 30 | file://:0:0:0:0 | bool True |
| 33 | file://:0:0:0:0 | bool False |
| 34 | file://:0:0:0:0 | bool True |
| 35 | file://:0:0:0:0 | bool False |
| 31 | file://:0:0:0:0 | bool True |
| 32 | file://:0:0:0:0 | bool False |
| 33 | file://:0:0:0:0 | bool True |
| 36 | file://:0:0:0:0 | bool False |
| 37 | file://:0:0:0:0 | bool True |
| 38 | file://:0:0:0:0 | bool True |
| 38 | file://:0:0:0:0 | bool False |
| 39 | file://:0:0:0:0 | bool False |
| 40 | file://:0:0:0:0 | bool True |
| 41 | file://:0:0:0:0 | bool True |

View File

@@ -21,6 +21,9 @@ len(unknown()) < 7
len(unknown()) == len(unknown())
len(unknown()) < len(unknown())
0+0 == 0
0+1 == 0
#All ops
2 > 3
2 == 3

View File

@@ -1,2 +1 @@
semmle-extractor-options: --max-import-depth=1
automatic_locations: true

View File

@@ -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() | |

View File

@@ -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 |

View File

@@ -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)

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View 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

View File

@@ -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 |

View 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)

View 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" }
}

View File

@@ -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 | |

View 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()

View File

@@ -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 |

View 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

View 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/

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