Skip to content

Commit 36c9ceb

Browse files
committed
Python: Add Logging concept
1 parent a7eb1b3 commit 36c9ceb

File tree

3 files changed

+91
-0
lines changed

3 files changed

+91
-0
lines changed

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,35 @@ private class EncodingAdditionalTaintStep extends TaintTracking::AdditionalTaint
268268
}
269269
}
270270

271+
/**
272+
* A data-flow node that logs data.
273+
*
274+
* Extend this class to refine existing API models. If you want to model new APIs,
275+
* extend `Logging::Range` instead.
276+
*/
277+
class Logging extends DataFlow::Node {
278+
Logging::Range range;
279+
280+
Logging() { this = range }
281+
282+
/** Gets an input that is logged. */
283+
DataFlow::Node getAnInput() { result = range.getAnInput() }
284+
}
285+
286+
/** Provides a class for modeling new logging mechanisms. */
287+
module Logging {
288+
/**
289+
* A data-flow node that logs data.
290+
*
291+
* Extend this class to model new APIs. If you want to refine existing API models,
292+
* extend `Logging` instead.
293+
*/
294+
abstract class Range extends DataFlow::Node {
295+
/** Gets an input that is logged. */
296+
abstract DataFlow::Node getAnInput();
297+
}
298+
}
299+
271300
/**
272301
* A data-flow node that dynamically executes Python code.
273302
*

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,23 @@ class EncodingTest extends InlineExpectationsTest {
9393
}
9494
}
9595

96+
class LoggingTest extends InlineExpectationsTest {
97+
LoggingTest() { this = "LoggingTest" }
98+
99+
override string getARelevantTag() { result in ["loggingInput"] }
100+
101+
override predicate hasActualResult(Location location, string element, string tag, string value) {
102+
exists(location.getFile().getRelativePath()) and
103+
exists(Logging logging, DataFlow::Node data |
104+
location = data.getLocation() and
105+
element = data.toString() and
106+
value = prettyNodeForInlineTest(data) and
107+
data = logging.getAnInput() and
108+
tag = "loggingInput"
109+
)
110+
}
111+
}
112+
96113
class CodeExecutionTest extends InlineExpectationsTest {
97114
CodeExecutionTest() { this = "CodeExecutionTest" }
98115

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import logging
2+
3+
# this bit just included to make this file runable
4+
logging.basicConfig(level=logging.DEBUG)
5+
6+
password = "<pass>"
7+
msg = "foo %s"
8+
9+
LOGGER = logging.getLogger("LOGGER")
10+
11+
logging.info(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
12+
logging.info(msg="hello") # $ MISSING: loggingInput="hello"
13+
14+
logging.log(logging.INFO, msg, password) # $ MISSING: loggingInput=msg loggingInput=password
15+
LOGGER.log(logging.INFO, msg, password) # $ MISSING: loggingInput=msg loggingInput=password
16+
17+
logging.root.info(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
18+
19+
# test of all levels
20+
21+
logging.critical(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
22+
logging.fatal(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
23+
logging.error(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
24+
logging.warning(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
25+
logging.warn(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
26+
logging.info(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
27+
logging.debug(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
28+
logging.exception(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
29+
30+
LOGGER.critical(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
31+
LOGGER.fatal(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
32+
LOGGER.error(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
33+
LOGGER.warning(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
34+
LOGGER.warn(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
35+
LOGGER.info(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
36+
LOGGER.debug(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
37+
LOGGER.exception(msg, password) # $ MISSING: loggingInput=msg loggingInput=password
38+
39+
# not sure how to make these print anything, but just to show that it works
40+
logging.Logger("foo").info("hello") # $ MISSING: loggingInput="hello"
41+
42+
class MyLogger(logging.Logger):
43+
pass
44+
45+
MyLogger("bar").info("hello") # $ MISSING: loggingInput="hello"

0 commit comments

Comments
 (0)