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

Skip to content

Commit 2097a00

Browse files
committed
apply code review suggestions, fix qldoc, add experimental additional taint steps that can improve performance
1 parent 2e4e5ef commit 2097a00

3 files changed

Lines changed: 85 additions & 47 deletions

File tree

ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qhelp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<p>When you want to decompress a user-provided compressed file you must be careful about the decompression ratio or read these files within a loop byte by byte to be able to manage the decompressed size in each cycle of the loop.</p>
1313

1414
</recommendation>
15-
<p>Please read official RubyZip Documentation <a href="https://github.com/rubyzip/rubyzip/#size-validation">here</a>
15+
<p>Please read official RubyZip Documentation <a href="https://github.com/rubyzip/rubyzip/#size-validation">here</a></p>
1616
<example>
1717
<p>Rubyzip: According to <a href="https://github.com/rubyzip/rubyzip/#reading-a-zip-file">official</a> Documentation</p>
1818
<sample src="example_good.rb" />

ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* external/cwe/cwe-409
1212
*/
1313

14+
import ruby
1415
private import codeql.ruby.Concepts
1516
private import codeql.ruby.DataFlow
1617
private import codeql.ruby.TaintTracking
@@ -26,40 +27,31 @@ class IoCopyStream extends DataFlow::CallNode {
2627
}
2728

2829
module BombsConfig implements DataFlow::ConfigSig {
29-
predicate isBarrier(DataFlow::Node node) {
30-
node.getLocation().hasLocationInfo("%spec%", _, _, _, _)
31-
}
32-
3330
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
3431

3532
predicate isSink(DataFlow::Node sink) { sink instanceof DecompressionBomb::Sink }
3633

3734
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
3835
any(DecompressionBomb::AdditionalTaintStep ats).isAdditionalTaintStep(nodeFrom, nodeTo)
3936
or
40-
exists(API::Node n | n = API::root().getMember("File").getMethod("open") |
37+
exists(API::Node n | n = API::getTopLevelMember("File").getMethod("open") |
4138
nodeFrom = n.getParameter(0).asSink() and
4239
nodeTo = n.getReturn().asSource()
4340
)
4441
or
45-
exists(File::FileOpen n |
46-
nodeFrom = n.getAPathArgument() and
47-
nodeTo = n
48-
)
42+
nodeFrom = nodeTo.(File::FileOpen).getAPathArgument()
4943
or
50-
exists(API::Node n | n = API::root().getMember("StringIO").getMethod("new") |
44+
exists(API::Node n | n = API::getTopLevelMember("StringIO").getMethod("new") |
5145
nodeFrom = n.getParameter(0).asSink() and
5246
nodeTo = n.getReturn().asSource()
5347
)
5448
or
55-
exists(IoCopyStream n |
56-
nodeFrom = n.getAPathArgument() and
57-
nodeTo = n
58-
)
49+
nodeFrom = nodeTo.(IoCopyStream).getAPathArgument()
5950
or
6051
// following can be a global additional step
6152
exists(DataFlow::CallNode cn |
62-
cn.getMethodName() = "open" and cn.getReceiver().toString() = "self"
53+
cn.getMethodName() = "open" and
54+
cn.getReceiver().getExprNode().getExpr() instanceof Ast::SelfVariableAccess
6355
|
6456
nodeFrom = cn.getArgument(0) and
6557
nodeTo = cn

ruby/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.qll

Lines changed: 77 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ import codeql.ruby.dataflow.RemoteFlowSources
66

77
module DecompressionBomb {
88
/**
9-
* The Sinks of uncontrolled data decompression
9+
* A abstract class responsible for extending new decompression sinks
10+
*
11+
* can be a path, stream of compressed data,
12+
* or a call to function that use pipe
1013
*/
11-
class Sink extends DataFlow::Node {
12-
Sink() { this = any(Range r).sink() }
13-
}
14+
abstract class Sink extends DataFlow::Node { }
1415

1516
/**
1617
* The additional taint steps that need for creating taint tracking or dataflow.
@@ -23,19 +24,6 @@ module DecompressionBomb {
2324
*/
2425
abstract predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ);
2526
}
26-
27-
/**
28-
* A abstract class responsible for extending new decompression sinks
29-
*/
30-
abstract class Range extends API::Node {
31-
/**
32-
* Gets the sink of responsible for decompression node
33-
*
34-
* it can be a path, stream of compressed data,
35-
* or a call to function that use pipe
36-
*/
37-
abstract DataFlow::Node sink();
38-
}
3927
}
4028

4129
module Zlib {
@@ -54,10 +42,10 @@ module Zlib {
5442
* `Zlib::GzipReader.zcat`
5543
* `Zlib::GzipReader.new`
5644
*/
57-
class DecompressionBombSink extends DecompressionBomb::Range {
58-
DecompressionBombSink() { this = gzipReaderInstance().getMethod(["open", "new", "zcat"]) }
59-
60-
override DataFlow::Node sink() { result = this.getReturn().asSource() }
45+
class DecompressionBombSink extends DecompressionBomb::Sink {
46+
DecompressionBombSink() {
47+
this = gzipReaderInstance().getMethod(["open", "new", "zcat"]).getReturn().asSource()
48+
}
6149
}
6250

6351
/**
@@ -94,10 +82,10 @@ module ZipInputStream {
9482
*
9583
* as source of decompression bombs, they need an additional taint step for a dataflow or taint tracking query
9684
*/
97-
class DecompressionBombSink extends DecompressionBomb::Range {
98-
DecompressionBombSink() { this = zipInputStream().getMethod(["open", "new"]) }
99-
100-
override DataFlow::Node sink() { result = this.getReturn().asSource() }
85+
class DecompressionBombSink extends DecompressionBomb::Sink {
86+
DecompressionBombSink() {
87+
this = zipInputStream().getMethod(["open", "new"]).getReturn().asSource()
88+
}
10189
}
10290

10391
/**
@@ -144,12 +132,14 @@ module ZipFile {
144132
* `ZipEntry.extract`
145133
* A sanitizer exists inside the nodes which have `entry.size > someOBJ`
146134
*/
147-
class DecompressionBombSink extends DecompressionBomb::Range {
148-
DecompressionBombSink() { this = rubyZipNode(zipFile()) }
149-
150-
override DataFlow::Node sink() {
151-
result = this.getMethod(["extract", "read"]).getReturn().asSource() and
152-
not exists(this.getMethod("size").getReturn().getMethod(">").getParameter(0))
135+
class DecompressionBombSink extends DecompressionBomb::Sink {
136+
DecompressionBombSink() {
137+
exists(API::Node rubyZip | rubyZip = rubyZipNode(zipFile()) |
138+
this = rubyZip.getMethod(["extract", "read"]).getReturn().asSource() and
139+
not exists(
140+
rubyZip.getMethod("size").getReturn().getMethod([">", " <", "<=", " >="]).getParameter(0)
141+
)
142+
)
153143
}
154144
}
155145

@@ -166,4 +156,60 @@ module ZipFile {
166156
)
167157
}
168158
}
159+
160+
predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
161+
exists(API::Node zipFile | zipFile = zipFile().getMethod("open") |
162+
nodeFrom = zipFile.getParameter(0).asSink() and
163+
nodeTo = rubyZipNode(zipFile).getMethod(["extract", "read"]).getReturn().asSource()
164+
)
165+
}
166+
// /**
167+
// * The additional taint steps that need for creating taint tracking or dataflow for `Zip::File`.
168+
// */
169+
// class AdditionalTaintStep1 extends DecompressionBomb::AdditionalTaintStep {
170+
// AdditionalTaintStep1() { this = "AdditionalTaintStep" }
171+
// override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
172+
// exists(API::Node zipFile | zipFile = zipFile().getMethod("open") |
173+
// nodeFrom = zipFile.getParameter(0).asSink() and
174+
// nodeTo = zipFile.asSource()
175+
// )
176+
// }
177+
// }
178+
// API::Node rubyZipNode2() {
179+
// result = zipFile().getMethod("open")
180+
// or
181+
// result = rubyZipNode2().getMethod(_)
182+
// or
183+
// result = rubyZipNode2().getReturn()
184+
// or
185+
// result = rubyZipNode2().getParameter(_)
186+
// or
187+
// result = rubyZipNode2().getAnElement()
188+
// or
189+
// result = rubyZipNode2().getBlock()
190+
// }
191+
// /**
192+
// * The additional taint steps that need for creating taint tracking or dataflow for `Zip::File`.
193+
// */
194+
// class AdditionalTaintStep2 extends DecompressionBomb::AdditionalTaintStep {
195+
// AdditionalTaintStep2() { this = "AdditionalTaintStep" }
196+
// override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
197+
// exists(API::Node zipFileOpen | zipFileOpen = rubyZipNode2() |
198+
// nodeFrom = zipFileOpen.getReturn().asSource() and
199+
// nodeTo = zipFileOpen.getMethod(["extract", "read"]).getReturn().asSource()
200+
// )
201+
// }
202+
// }
203+
// /**
204+
// * The additional taint steps that need for creating taint tracking or dataflow for `Zip::File`.
205+
// */
206+
// class AdditionalTaintStep3 extends DecompressionBomb::AdditionalTaintStep {
207+
// AdditionalTaintStep3() { this = "AdditionalTaintStep" }
208+
// override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
209+
// exists(API::Node zipFileOpen | zipFileOpen = rubyZipNode2() |
210+
// nodeFrom = zipFileOpen.asCall() and
211+
// nodeTo = zipFileOpen.getMethod(["extract", "read"]).getReturn().asSource()
212+
// )
213+
// }
214+
// }
169215
}

0 commit comments

Comments
 (0)