Skip to content

Commit a9469b7

Browse files
committed
Python: Port py/clear-text-storage-sensitive-data
1 parent 8926b3e commit a9469b7

File tree

10 files changed

+208
-34
lines changed

10 files changed

+208
-34
lines changed

python/ql/src/Security/CWE-312/CleartextStorage.ql

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,13 @@
1414
*/
1515

1616
import python
17-
import semmle.python.security.Paths
18-
import semmle.python.dataflow.TaintTracking
19-
import semmle.python.security.SensitiveData
20-
import semmle.python.security.ClearText
17+
private import semmle.python.dataflow.new.DataFlow
18+
import DataFlow::PathGraph
19+
import semmle.python.security.dataflow.CleartextStorage::CleartextStorage
2120

22-
class CleartextStorageConfiguration extends TaintTracking::Configuration {
23-
CleartextStorageConfiguration() { this = "ClearTextStorage" }
24-
25-
override predicate isSource(DataFlow::Node src, TaintKind kind) {
26-
src.asCfgNode().(SensitiveData::Source).isSourceOf(kind)
27-
}
28-
29-
override predicate isSink(DataFlow::Node sink, TaintKind kind) {
30-
sink.asCfgNode() instanceof ClearTextStorage::Sink and
31-
kind instanceof SensitiveData
32-
}
33-
}
34-
35-
from CleartextStorageConfiguration config, TaintedPathSource source, TaintedPathSink sink
36-
where config.hasFlowPath(source, sink)
37-
select sink.getSink(), source, sink, "Sensitive data from $@ is stored here.", source.getSource(),
38-
source.getCfgNode().(SensitiveData::Source).repr()
21+
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink, string classification
22+
where
23+
config.hasFlowPath(source, sink) and
24+
classification = source.getNode().(Source).getClassification()
25+
select sink.getNode(), source, sink, "$@ is stored here.", source.getNode(),
26+
"Sensitive data (" + classification + ")"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @name Clear-text logging of sensitive information
3+
* @description OLD QUERY: Logging sensitive information without encryption or hashing can
4+
* expose it to an attacker.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @id py/old/clear-text-logging-sensitive-data
8+
* @deprecated
9+
*/
10+
11+
import python
12+
import semmle.python.security.Paths
13+
import semmle.python.dataflow.TaintTracking
14+
import semmle.python.security.SensitiveData
15+
import semmle.python.security.ClearText
16+
17+
class CleartextLoggingConfiguration extends TaintTracking::Configuration {
18+
CleartextLoggingConfiguration() { this = "ClearTextLogging" }
19+
20+
override predicate isSource(DataFlow::Node src, TaintKind kind) {
21+
src.asCfgNode().(SensitiveData::Source).isSourceOf(kind)
22+
}
23+
24+
override predicate isSink(DataFlow::Node sink, TaintKind kind) {
25+
sink.asCfgNode() instanceof ClearTextLogging::Sink and
26+
kind instanceof SensitiveData
27+
}
28+
}
29+
30+
from CleartextLoggingConfiguration config, TaintedPathSource source, TaintedPathSink sink
31+
where config.hasFlowPath(source, sink)
32+
select sink.getSink(), source, sink, "Sensitive data returned by $@ is logged here.",
33+
source.getSource(), source.getCfgNode().(SensitiveData::Source).repr()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @name Clear-text storage of sensitive information
3+
* @description OLD QUERY: Sensitive information stored without encryption or hashing can expose it to an
4+
* attacker.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @id py/old/clear-text-storage-sensitive-data
8+
* @deprecated
9+
*/
10+
11+
import python
12+
import semmle.python.security.Paths
13+
import semmle.python.dataflow.TaintTracking
14+
import semmle.python.security.SensitiveData
15+
import semmle.python.security.ClearText
16+
17+
class CleartextStorageConfiguration extends TaintTracking::Configuration {
18+
CleartextStorageConfiguration() { this = "ClearTextStorage" }
19+
20+
override predicate isSource(DataFlow::Node src, TaintKind kind) {
21+
src.asCfgNode().(SensitiveData::Source).isSourceOf(kind)
22+
}
23+
24+
override predicate isSink(DataFlow::Node sink, TaintKind kind) {
25+
sink.asCfgNode() instanceof ClearTextStorage::Sink and
26+
kind instanceof SensitiveData
27+
}
28+
}
29+
30+
from CleartextStorageConfiguration config, TaintedPathSource source, TaintedPathSink sink
31+
where config.hasFlowPath(source, sink)
32+
select sink.getSink(), source, sink, "Sensitive data from $@ is stored here.", source.getSource(),
33+
source.getCfgNode().(SensitiveData::Source).repr()
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Provides a taint-tracking configuration for "Clear-text storage of sensitive information".
3+
*
4+
* Note, for performance reasons: only import this file if
5+
* `CleartextStorage::Configuration` is needed, otherwise
6+
* `CleartextStorageCustomizations` should be imported instead.
7+
*/
8+
9+
private import python
10+
private import semmle.python.dataflow.new.DataFlow
11+
private import semmle.python.dataflow.new.TaintTracking
12+
private import semmle.python.Concepts
13+
private import semmle.python.dataflow.new.RemoteFlowSources
14+
private import semmle.python.dataflow.new.BarrierGuards
15+
private import semmle.python.dataflow.new.SensitiveDataSources
16+
17+
/**
18+
* Provides a taint-tracking configuration for detecting "Clear-text storage of sensitive information".
19+
*/
20+
module CleartextStorage {
21+
import CleartextStorageCustomizations::CleartextStorage
22+
23+
/**
24+
* A taint-tracking configuration for detecting use of a broken or weak
25+
* cryptographic hashing algorithm on sensitive data.
26+
*/
27+
class Configuration extends TaintTracking::Configuration {
28+
Configuration() { this = "CleartextStorage" }
29+
30+
override predicate isSource(DataFlow::Node source) { source instanceof Source }
31+
32+
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
33+
34+
override predicate isSanitizer(DataFlow::Node node) {
35+
super.isSanitizer(node)
36+
or
37+
node instanceof Sanitizer
38+
}
39+
}
40+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for detecting
3+
* "Clear-text storage of sensitive information"
4+
* vulnerabilities, as well as extension points for adding your own.
5+
*/
6+
7+
private import python
8+
private import semmle.python.dataflow.new.DataFlow
9+
private import semmle.python.dataflow.new.TaintTracking
10+
private import semmle.python.Concepts
11+
private import semmle.python.dataflow.new.SensitiveDataSources
12+
private import semmle.python.dataflow.new.BarrierGuards
13+
14+
/**
15+
* Provides default sources, sinks and sanitizers for detecting
16+
* "Clear-text storage of sensitive information"
17+
* vulnerabilities, as well as extension points for adding your own.
18+
*/
19+
module CleartextStorage {
20+
/**
21+
* A data flow source for "Clear-text storage of sensitive information" vulnerabilities.
22+
*/
23+
abstract class Source extends DataFlow::Node {
24+
/** Gets the classification of the sensitive data. */
25+
abstract string getClassification();
26+
}
27+
28+
/**
29+
* A data flow sink for "Clear-text storage of sensitive information" vulnerabilities.
30+
*/
31+
abstract class Sink extends DataFlow::Node { }
32+
33+
/**
34+
* A sanitizer for "Clear-text storage of sensitive information" vulnerabilities.
35+
*/
36+
abstract class Sanitizer extends DataFlow::Node { }
37+
38+
/**
39+
* A source of sensitive data, considered as a flow source.
40+
*/
41+
class SensitiveDataSourceAsSource extends Source, SensitiveDataSource {
42+
override SensitiveDataClassification getClassification() {
43+
result = SensitiveDataSource.super.getClassification()
44+
}
45+
}
46+
47+
/** The data written to a file, considered as a flow sink. */
48+
class FileWriteDataAsSink extends Sink {
49+
FileWriteDataAsSink() { this = any(FileSystemWriteAccess write).getADataNode() }
50+
}
51+
52+
/** The data written to a cookie on a HTTP response, considered as a flow sink. */
53+
class CookieWriteAsSink extends Sink {
54+
CookieWriteAsSink() {
55+
exists(HTTP::Server::CookieWrite write |
56+
this = write.getValueArg()
57+
or
58+
this = write.getHeaderArg()
59+
)
60+
}
61+
}
62+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,13 @@
11
edges
2+
| test.py:9:12:9:21 | ControlFlowNode for get_cert() | test.py:12:21:12:24 | ControlFlowNode for cert |
3+
| test.py:9:12:9:21 | ControlFlowNode for get_cert() | test.py:13:22:13:41 | ControlFlowNode for Attribute() |
4+
| test.py:9:12:9:21 | ControlFlowNode for get_cert() | test.py:15:26:15:29 | ControlFlowNode for cert |
5+
nodes
6+
| test.py:9:12:9:21 | ControlFlowNode for get_cert() | semmle.label | ControlFlowNode for get_cert() |
7+
| test.py:12:21:12:24 | ControlFlowNode for cert | semmle.label | ControlFlowNode for cert |
8+
| test.py:13:22:13:41 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
9+
| test.py:15:26:15:29 | ControlFlowNode for cert | semmle.label | ControlFlowNode for cert |
210
#select
11+
| test.py:12:21:12:24 | ControlFlowNode for cert | test.py:9:12:9:21 | ControlFlowNode for get_cert() | test.py:12:21:12:24 | ControlFlowNode for cert | $@ is stored here. | test.py:9:12:9:21 | ControlFlowNode for get_cert() | Sensitive data (certificate) |
12+
| test.py:13:22:13:41 | ControlFlowNode for Attribute() | test.py:9:12:9:21 | ControlFlowNode for get_cert() | test.py:13:22:13:41 | ControlFlowNode for Attribute() | $@ is stored here. | test.py:9:12:9:21 | ControlFlowNode for get_cert() | Sensitive data (certificate) |
13+
| test.py:15:26:15:29 | ControlFlowNode for cert | test.py:9:12:9:21 | ControlFlowNode for get_cert() | test.py:15:26:15:29 | ControlFlowNode for cert | $@ is stored here. | test.py:9:12:9:21 | ControlFlowNode for get_cert() | Sensitive data (certificate) |

python/ql/test/query-tests/Security/CWE-312-CleartextStorage-py3/options

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
edges
2-
| password_in_cookie.py:7:16:7:43 | a password | password_in_cookie.py:9:33:9:40 | a password |
3-
| password_in_cookie.py:7:16:7:43 | a password | password_in_cookie.py:9:33:9:40 | a password |
4-
| password_in_cookie.py:14:16:14:43 | a password | password_in_cookie.py:16:33:16:40 | a password |
5-
| password_in_cookie.py:14:16:14:43 | a password | password_in_cookie.py:16:33:16:40 | a password |
6-
| test.py:6:12:6:21 | a certificate or key | test.py:8:20:8:23 | a certificate or key |
7-
| test.py:6:12:6:21 | a certificate or key | test.py:8:20:8:23 | a certificate or key |
2+
| password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | password_in_cookie.py:9:33:9:40 | ControlFlowNode for password |
3+
| password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | password_in_cookie.py:16:33:16:40 | ControlFlowNode for password |
4+
| test.py:6:12:6:21 | ControlFlowNode for get_cert() | test.py:8:20:8:23 | ControlFlowNode for cert |
5+
| test.py:6:12:6:21 | ControlFlowNode for get_cert() | test.py:9:17:9:29 | ControlFlowNode for List |
6+
| test.py:9:17:9:29 | ControlFlowNode for List | test.py:10:25:10:29 | ControlFlowNode for lines |
7+
nodes
8+
| password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
9+
| password_in_cookie.py:9:33:9:40 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
10+
| password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
11+
| password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | semmle.label | ControlFlowNode for password |
12+
| test.py:6:12:6:21 | ControlFlowNode for get_cert() | semmle.label | ControlFlowNode for get_cert() |
13+
| test.py:8:20:8:23 | ControlFlowNode for cert | semmle.label | ControlFlowNode for cert |
14+
| test.py:9:17:9:29 | ControlFlowNode for List | semmle.label | ControlFlowNode for List |
15+
| test.py:10:25:10:29 | ControlFlowNode for lines | semmle.label | ControlFlowNode for lines |
816
#select
9-
| password_in_cookie.py:9:33:9:40 | password | password_in_cookie.py:7:16:7:43 | a password | password_in_cookie.py:9:33:9:40 | a password | Sensitive data from $@ is stored here. | password_in_cookie.py:7:16:7:43 | Attribute() | a request parameter containing a password |
10-
| password_in_cookie.py:16:33:16:40 | password | password_in_cookie.py:14:16:14:43 | a password | password_in_cookie.py:16:33:16:40 | a password | Sensitive data from $@ is stored here. | password_in_cookie.py:14:16:14:43 | Attribute() | a request parameter containing a password |
11-
| test.py:8:20:8:23 | cert | test.py:6:12:6:21 | a certificate or key | test.py:8:20:8:23 | a certificate or key | Sensitive data from $@ is stored here. | test.py:6:12:6:21 | get_cert() | a call returning a certificate or key |
17+
| password_in_cookie.py:9:33:9:40 | ControlFlowNode for password | password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | password_in_cookie.py:9:33:9:40 | ControlFlowNode for password | $@ is stored here. | password_in_cookie.py:7:16:7:43 | ControlFlowNode for Attribute() | Sensitive data (password) |
18+
| password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | password_in_cookie.py:16:33:16:40 | ControlFlowNode for password | $@ is stored here. | password_in_cookie.py:14:16:14:43 | ControlFlowNode for Attribute() | Sensitive data (password) |
19+
| test.py:8:20:8:23 | ControlFlowNode for cert | test.py:6:12:6:21 | ControlFlowNode for get_cert() | test.py:8:20:8:23 | ControlFlowNode for cert | $@ is stored here. | test.py:6:12:6:21 | ControlFlowNode for get_cert() | Sensitive data (certificate) |
20+
| test.py:10:25:10:29 | ControlFlowNode for lines | test.py:6:12:6:21 | ControlFlowNode for get_cert() | test.py:10:25:10:29 | ControlFlowNode for lines | $@ is stored here. | test.py:6:12:6:21 | ControlFlowNode for get_cert() | Sensitive data (certificate) |

python/ql/test/query-tests/Security/CWE-312-CleartextStorage/options

Lines changed: 0 additions & 1 deletion
This file was deleted.

python/ql/test/query-tests/Security/CWE-312-CleartextStorage/password_in_cookie.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
def index():
77
password = request.args.get("password")
88
resp = make_response(render_template(...))
9-
resp.set_cookie("password", password)
9+
resp.set_cookie("password", password) # NOT OK
1010
return resp
1111

1212
@app.route('/')
1313
def index2():
1414
password = request.args.get("password")
1515
resp = Response(...)
16-
resp.set_cookie("password", password)
16+
resp.set_cookie("password", password) # NOT OK
1717
return resp

0 commit comments

Comments
 (0)