Skip to content

Commit bcef8d1

Browse files
committed
Python: Add Escaping concept
1 parent d18b9a2 commit bcef8d1

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

python/ql/src/semmle/python/Concepts.qll

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,62 @@ module SqlExecution {
293293
}
294294
}
295295

296+
/**
297+
* A data-flow node that escapes meta-characters, which could be used to prevent
298+
* injection attacks.
299+
*
300+
* Extend this class to refine existing API models. If you want to model new APIs,
301+
* extend `Escaping::Range` instead.
302+
*/
303+
class Escaping extends DataFlow::Node {
304+
Escaping::Range range;
305+
306+
Escaping() { this = range }
307+
308+
/** Gets an input that will be escaped. */
309+
DataFlow::Node getAnInput() { result = range.getAnInput() }
310+
311+
/** Gets the output that contains the escaped data. */
312+
DataFlow::Node getOutput() { result = range.getOutput() }
313+
314+
/**
315+
* Gets the context that this function escapes for, such as `html`, or `url`.
316+
*/
317+
string getKind() { result = range.getKind() }
318+
}
319+
320+
/** Provides a class for modeling new escaping APIs. */
321+
module Escaping {
322+
/**
323+
* A data-flow node that escapes meta-characters, which could be used to prevent
324+
* injection attacks.
325+
*
326+
* Extend this class to model new APIs. If you want to refine existing API models,
327+
* extend `Escaping` instead.
328+
*/
329+
abstract class Range extends DataFlow::Node {
330+
/** Gets an input that will be escaped. */
331+
abstract DataFlow::Node getAnInput();
332+
333+
/** Gets the output that contains the escaped data. */
334+
abstract DataFlow::Node getOutput();
335+
336+
/**
337+
* Gets the context that this function escapes for, such as `html`, or `url`.
338+
*/
339+
abstract string getKind();
340+
}
341+
}
342+
343+
/**
344+
* An escape of a string so it can be safely included in
345+
* the body of an HTML element, for example, replacing `{}` in
346+
* `<p>{}</p>`.
347+
*/
348+
class HtmlEscaping extends Escaping {
349+
HtmlEscaping() { range.getKind() = "html" }
350+
}
351+
296352
/** Provides classes for modeling HTTP-related APIs. */
297353
module HTTP {
298354
import semmle.python.web.HttpConstants

python/ql/test/experimental/meta/ConceptsTest.qll

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,38 @@ class SqlExecutionTest extends InlineExpectationsTest {
129129
}
130130
}
131131

132+
class EscapingTest extends InlineExpectationsTest {
133+
EscapingTest() { this = "EscapingTest" }
134+
135+
override string getARelevantTag() { result in ["escapeInput", "escapeOutput", "escapeKind"] }
136+
137+
override predicate hasActualResult(Location location, string element, string tag, string value) {
138+
exists(location.getFile().getRelativePath()) and
139+
exists(Escaping esc |
140+
exists(DataFlow::Node data |
141+
location = data.getLocation() and
142+
element = data.toString() and
143+
value = prettyNodeForInlineTest(data) and
144+
(
145+
data = esc.getAnInput() and
146+
tag = "escapeInput"
147+
or
148+
data = esc.getOutput() and
149+
tag = "escapeOutput"
150+
)
151+
)
152+
or
153+
exists(string format |
154+
location = esc.getLocation() and
155+
element = format and
156+
value = format and
157+
format = esc.getKind() and
158+
tag = "escapeKind"
159+
)
160+
)
161+
}
162+
}
163+
132164
class HttpServerRouteSetupTest extends InlineExpectationsTest {
133165
HttpServerRouteSetupTest() { this = "HttpServerRouteSetupTest" }
134166

0 commit comments

Comments
 (0)