Skip to content

Commit 5204255

Browse files
authored
Merge pull request #21234 from owen-mc/python/convert-sanitizers-to-mad
Python: Allow models-as-data sanitizers
2 parents 1667051 + 0222159 commit 5204255

File tree

20 files changed

+175
-84
lines changed

20 files changed

+175
-84
lines changed

python/ql/lib/semmle/python/Concepts.qll

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,16 @@ module SystemCommandExecution {
116116
class FileSystemAccess extends DataFlow::Node instanceof FileSystemAccess::Range {
117117
/** Gets an argument to this file system access that is interpreted as a path. */
118118
DataFlow::Node getAPathArgument() { result = super.getAPathArgument() }
119+
120+
/**
121+
* Gets an argument to this file system access that is interpreted as a path
122+
* which is vulnerable to path injection.
123+
*
124+
* By default all path arguments are considered vulnerable, but this can be overridden to
125+
* exclude certain arguments that are known to be safe, for example because they are
126+
* restricted to a specific directory.
127+
*/
128+
DataFlow::Node getAVulnerablePathArgument() { result = super.getAVulnerablePathArgument() }
119129
}
120130

121131
/** Provides a class for modeling new file system access APIs. */
@@ -130,6 +140,16 @@ module FileSystemAccess {
130140
abstract class Range extends DataFlow::Node {
131141
/** Gets an argument to this file system access that is interpreted as a path. */
132142
abstract DataFlow::Node getAPathArgument();
143+
144+
/**
145+
* Gets an argument to this file system access that is interpreted as a path
146+
* which is vulnerable to path injection.
147+
*
148+
* By default all path arguments are considered vulnerable, but this can be overridden to
149+
* exclude certain arguments that are known to be safe, for example because they are
150+
* restricted to a specific directory.
151+
*/
152+
DataFlow::Node getAVulnerablePathArgument() { result = this.getAPathArgument() }
133153
}
134154
}
135155

python/ql/lib/semmle/python/frameworks/Flask.qll

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -621,24 +621,15 @@ module Flask {
621621
}
622622

623623
override DataFlow::Node getAPathArgument() {
624-
result in [
625-
this.getArg(0), this.getArgByName("directory"),
626-
// as described in the docs, the `filename` argument is restrained to be within
627-
// the provided directory, so is not exposed to path-injection. (but is still a
628-
// path-argument).
629-
this.getArg(1), this.getArgByName("filename")
630-
]
624+
result = this.getArg([0, 1]) or
625+
result = this.getArgByName(["directory", "filename"])
631626
}
632-
}
633627

634-
/**
635-
* To exclude `filename` argument to `flask.send_from_directory` as a path-injection sink.
636-
*/
637-
private class FlaskSendFromDirectoryCallFilenameSanitizer extends PathInjection::Sanitizer {
638-
FlaskSendFromDirectoryCallFilenameSanitizer() {
639-
this = any(FlaskSendFromDirectoryCall c).getArg(1)
640-
or
641-
this = any(FlaskSendFromDirectoryCall c).getArgByName("filename")
628+
override DataFlow::Node getAVulnerablePathArgument() {
629+
result = this.getAPathArgument() and
630+
// as described in the docs, the `filename` argument is restricted to be within
631+
// the provided directory, so is not exposed to path-injection.
632+
not result in [this.getArg(1), this.getArgByName("filename")]
642633
}
643634
}
644635

python/ql/lib/semmle/python/security/dataflow/CodeInjectionCustomizations.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,11 @@ module CodeInjection {
6060

6161
/** DEPRECATED: Use ConstCompareAsSanitizerGuard instead. */
6262
deprecated class StringConstCompareAsSanitizerGuard = ConstCompareAsSanitizerGuard;
63+
64+
/**
65+
* A sanitizer defined via models-as-data with kind "code-injection".
66+
*/
67+
class SanitizerFromModel extends Sanitizer {
68+
SanitizerFromModel() { ModelOutput::barrierNode(this, "code-injection") }
69+
}
6370
}

python/ql/lib/semmle/python/security/dataflow/CommandInjectionCustomizations.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,11 @@ module CommandInjection {
9595

9696
/** DEPRECATED: Use ConstCompareAsSanitizerGuard instead. */
9797
deprecated class StringConstCompareAsSanitizerGuard = ConstCompareAsSanitizerGuard;
98+
99+
/**
100+
* A sanitizer defined via models-as-data with kind "command-injection".
101+
*/
102+
class SanitizerFromModel extends Sanitizer {
103+
SanitizerFromModel() { ModelOutput::barrierNode(this, "command-injection") }
104+
}
98105
}

python/ql/lib/semmle/python/security/dataflow/LogInjectionCustomizations.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,11 @@ module LogInjection {
106106
this.getArg(0).asExpr().(StringLiteral).getText() in ["\r\n", "\n"]
107107
}
108108
}
109+
110+
/**
111+
* A sanitizer defined via models-as-data with kind "log-injection".
112+
*/
113+
class SanitizerFromModel extends Sanitizer {
114+
SanitizerFromModel() { ModelOutput::barrierNode(this, "log-injection") }
115+
}
109116
}

python/ql/lib/semmle/python/security/dataflow/PathInjectionCustomizations.qll

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ module PathInjection {
5757
*/
5858
class FileSystemAccessAsSink extends Sink {
5959
FileSystemAccessAsSink() {
60-
this = any(FileSystemAccess e).getAPathArgument() and
60+
this = any(FileSystemAccess e).getAVulnerablePathArgument() and
6161
// since implementation of Path.open in pathlib.py is like
6262
// ```py
6363
// def open(self, ...):
@@ -98,4 +98,11 @@ module PathInjection {
9898

9999
/** DEPRECATED: Use ConstCompareAsSanitizerGuard instead. */
100100
deprecated class StringConstCompareAsSanitizerGuard = ConstCompareAsSanitizerGuard;
101+
102+
/**
103+
* A sanitizer defined via models-as-data with kind "path-injection".
104+
*/
105+
class SanitizerFromModel extends Sanitizer {
106+
SanitizerFromModel() { ModelOutput::barrierNode(this, "path-injection") }
107+
}
101108
}

python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,11 @@ module ReflectedXss {
8484

8585
/** DEPRECATED: Use ConstCompareAsSanitizerGuard instead. */
8686
deprecated class StringConstCompareAsSanitizerGuard = ConstCompareAsSanitizerGuard;
87+
88+
/**
89+
* A sanitizer defined via models-as-data with kind "html-injection" or "js-injection".
90+
*/
91+
class SanitizerFromModel extends Sanitizer {
92+
SanitizerFromModel() { ModelOutput::barrierNode(this, ["html-injection", "js-injection"]) }
93+
}
8794
}

python/ql/lib/semmle/python/security/dataflow/SqlInjectionCustomizations.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,11 @@ module SqlInjection {
6969
private class DataAsSqlSink extends Sink {
7070
DataAsSqlSink() { ModelOutput::sinkNode(this, "sql-injection") }
7171
}
72+
73+
/**
74+
* A sanitizer defined via models-as-data with kind "sql-injection".
75+
*/
76+
class SanitizerFromModel extends Sanitizer {
77+
SanitizerFromModel() { ModelOutput::barrierNode(this, "sql-injection") }
78+
}
7279
}

python/ql/lib/semmle/python/security/dataflow/UnsafeDeserializationCustomizations.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,11 @@ module UnsafeDeserialization {
6565

6666
/** DEPRECATED: Use ConstCompareAsSanitizerGuard instead. */
6767
deprecated class StringConstCompareAsSanitizerGuard = ConstCompareAsSanitizerGuard;
68+
69+
/**
70+
* A sanitizer defined via models-as-data with kind "unsafe-deserialization".
71+
*/
72+
class SanitizerFromModel extends Sanitizer {
73+
SanitizerFromModel() { ModelOutput::barrierNode(this, "unsafe-deserialization") }
74+
}
6875
}

python/ql/lib/semmle/python/security/dataflow/UrlRedirectCustomizations.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ module UrlRedirect {
162162
deprecated class StringConstCompareAsSanitizerGuard = ConstCompareAsSanitizerGuard;
163163

164164
/**
165-
* A sanitizer defined via models-as-data with kind "url-redirection".
165+
* A sanitizer which sanitizes all flow states, defined via models-as-data
166+
* with kind "url-redirection".
166167
*/
167168
class SanitizerFromModel extends Sanitizer {
168169
SanitizerFromModel() { ModelOutput::barrierNode(this, "url-redirection") }

0 commit comments

Comments
 (0)