Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 769f569

Browse files
committed
Python: Add taint for StringIO and BytesIO
1 parent 57b9780 commit 769f569

3 files changed

Lines changed: 109 additions & 0 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added taint propagation for `io.StringIO` and `io.BytesIO`. This addition was originally [submitted as part of an experimental query by @jorgectf](https://github.com/github/codeql/pull/6112).

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3116,6 +3116,64 @@ private module StdlibPrivate {
31163116
result in [this.getArg(0), this.getArgByName("path")]
31173117
}
31183118
}
3119+
3120+
// ---------------------------------------------------------------------------
3121+
// io
3122+
// ---------------------------------------------------------------------------
3123+
/**
3124+
* Provides models for the `io.StringIO`/`io.BytesIO` classes
3125+
*
3126+
* See https://docs.python.org/3.10/library/io.html#io.StringIO.
3127+
*/
3128+
module StringIO {
3129+
/** Gets a reference to the `io.StringIO` class. */
3130+
private API::Node classRef() {
3131+
result = API::moduleImport("io").getMember(["StringIO", "BytesIO"])
3132+
}
3133+
3134+
/**
3135+
* A source of instances of `io.StringIO`/`io.BytesIO`, extend this class to model new instances.
3136+
*
3137+
* This can include instantiations of the class, return values from function
3138+
* calls, or a special parameter that will be set when functions are called by an external
3139+
* library.
3140+
*
3141+
* Use the predicate `StringIO::instance()` to get references to instances of `io.StringIO`.
3142+
*/
3143+
abstract class InstanceSource extends Stdlib::FileLikeObject::InstanceSource { }
3144+
3145+
/** A direct instantiation of `io.StringIO`/`io.BytesIO`. */
3146+
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
3147+
ClassInstantiation() { this = classRef().getACall() }
3148+
3149+
DataFlow::Node getInitialValue() {
3150+
result = this.getArg(0)
3151+
or
3152+
// `initial_value` for StringIO, `initial_bytes` for BytesIO
3153+
result = this.getArgByName(["initial_value", "initial_bytes"])
3154+
}
3155+
}
3156+
3157+
/** Gets a reference to an instance of `io.StringIO`/`io.BytesIO`. */
3158+
private DataFlow::TypeTrackingNode instance(DataFlow::TypeTracker t) {
3159+
t.start() and
3160+
result instanceof InstanceSource
3161+
or
3162+
exists(DataFlow::TypeTracker t2 | result = instance(t2).track(t2, t))
3163+
}
3164+
3165+
/** Gets a reference to an instance of `io.StringIO`/`io.BytesIO`. */
3166+
DataFlow::Node instance() { instance(DataFlow::TypeTracker::end()).flowsTo(result) }
3167+
3168+
/**
3169+
* Extra taint propagation for `io.StringIO`/`io.BytesIO`.
3170+
*/
3171+
private class AdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
3172+
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
3173+
nodeTo.(ClassInstantiation).getInitialValue() = nodeFrom
3174+
}
3175+
}
3176+
}
31193177
}
31203178

31213179
// ---------------------------------------------------------------------------
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from io import StringIO, BytesIO
2+
3+
TAINTED_STRING = "TS"
4+
TAINTED_BYTES = b"TB"
5+
6+
def ensure_tainted(*args):
7+
print("ensure_tainted")
8+
for arg in args:
9+
print("", repr(arg))
10+
11+
12+
def test_stringio():
13+
ts = TAINTED_STRING
14+
15+
x = StringIO()
16+
x.write(ts)
17+
x.seek(0)
18+
19+
ensure_tainted(
20+
StringIO(ts), # $ tainted
21+
StringIO(initial_value=ts), # $ tainted
22+
x, # $ tainted
23+
24+
x.read(), # $ tainted
25+
StringIO(ts).read(), # $ tainted
26+
)
27+
28+
29+
def test_bytesio():
30+
tb = TAINTED_BYTES
31+
32+
x = BytesIO()
33+
x.write(tb)
34+
x.seek(0)
35+
36+
ensure_tainted(
37+
BytesIO(tb), # $ tainted
38+
BytesIO(initial_bytes=tb), # $ tainted
39+
x, # $ tainted
40+
41+
x.read(), # $ tainted
42+
BytesIO(tb).read(), # $ tainted
43+
)
44+
45+
46+
test_stringio()
47+
test_bytesio()

0 commit comments

Comments
 (0)