11
11
* external/cwe/cwe-409
12
12
*/
13
13
14
- import codeql.ruby.AST
15
- import codeql.ruby.frameworks.Files
16
- import codeql.ruby.ApiGraphs
17
- import codeql.ruby.DataFlow
18
- import codeql.ruby.dataflow.RemoteFlowSources
19
- import codeql.ruby.TaintTracking
20
- import DataFlow:: PathGraph
21
-
22
- module DecompressionBombs {
23
- abstract class DecompressionBombSink extends DataFlow:: Node { }
24
-
25
- module Zlib {
26
- /**
27
- * `Zlib::GzipReader`
28
- * > Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes.
29
- *
30
- * according to above warning from Doc we don't need to go forward after open()
31
- * or new() methods, we just need the argument node of them
32
- */
33
- private API:: Node gzipReaderInstance ( ) {
34
- result = API:: getTopLevelMember ( "Zlib" ) .getMember ( "GzipReader" )
35
- }
36
-
37
- /**
38
- * A return values of following methods
39
- * `Zlib::GzipReader.open`
40
- * `Zlib::GzipReader.zcat`
41
- * `Zlib::GzipReader.new`
42
- */
43
- class ZipSink extends DecompressionBombSink {
44
- ZipSink ( ) {
45
- this = gzipReaderInstance ( ) .getMethod ( [ "open" , "new" , "zcat" ] ) .getReturn ( ) .asSource ( )
46
- }
47
- }
48
-
49
- predicate isAdditionalTaintStep ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
50
- exists ( API:: Node zipnode | zipnode = gzipReaderInstance ( ) .getMethod ( [ "open" , "new" , "zcat" ] ) |
51
- nodeFrom = zipnode .getParameter ( 0 ) .asSink ( ) and
52
- nodeTo = zipnode .getReturn ( ) .asSource ( )
53
- )
54
- }
55
- }
56
-
57
- module ZipInputStream {
58
- /**
59
- * `Zip::InputStream`
60
- * > Note that if you use the lower level Zip::InputStream interface, rubyzip does not check the entry sizes.
61
- *
62
- * according to above warning from Doc we don't need to go forward after open()
63
- * or new() methods, we just need the argument node of them
64
- */
65
- private API:: Node zipInputStream ( ) {
66
- result = API:: getTopLevelMember ( "Zip" ) .getMember ( "InputStream" )
67
- }
68
-
69
- /**
70
- * A return values of following methods
71
- * `ZipIO.read`
72
- * `ZipEntry.extract`
73
- */
74
- class ZipSink extends DecompressionBombSink {
75
- ZipSink ( ) {
76
- this = zipInputStream ( ) .getMethod ( [ "open" , "new" ] ) .getReturn ( ) .asSource ( ) and
77
- not this .getLocation ( ) .getFile ( ) .getBaseName ( ) .matches ( "%spec%" )
78
- }
79
- }
80
-
81
- predicate isAdditionalTaintStep ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
82
- exists ( API:: Node zipnode | zipnode = zipInputStream ( ) .getMethod ( [ "open" , "new" ] ) |
83
- nodeFrom = zipnode .getParameter ( 0 ) .asSink ( ) and
84
- nodeTo = zipnode .getReturn ( ) .asSource ( )
85
- )
86
- }
87
- }
88
-
89
- module ZipFile {
90
- API:: Node rubyZipNode ( ) {
91
- result = zipFile ( )
92
- or
93
- result = rubyZipNode ( ) .getMethod ( _)
94
- or
95
- result = rubyZipNode ( ) .getReturn ( )
96
- or
97
- result = rubyZipNode ( ) .getParameter ( _)
98
- or
99
- result = rubyZipNode ( ) .getAnElement ( )
100
- or
101
- result = rubyZipNode ( ) .getBlock ( )
102
- }
103
-
104
- /**
105
- * A return values of following methods
106
- * `ZipIO.read`
107
- * `ZipEntry.extract`
108
- * sanitize the nodes which have `entry.size > someOBJ`
109
- */
110
- class ZipSink extends DecompressionBombSink {
111
- ZipSink ( ) {
112
- exists ( API:: Node zipnodes | zipnodes = rubyZipNode ( ) |
113
- this = zipnodes .getMethod ( [ "extract" , "read" ] ) .getReturn ( ) .asSource ( ) and
114
- not exists ( zipnodes .getMethod ( "size" ) .getReturn ( ) .getMethod ( ">" ) .getParameter ( 0 ) )
115
- )
116
- }
117
- }
118
-
119
- predicate isAdditionalTaintStep ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
120
- exists ( API:: Node zipnodes | zipnodes = rubyZipNode ( ) |
121
- nodeTo = zipnodes .getMethod ( [ "extract" , "read" ] ) .getReturn ( ) .asSource ( ) and
122
- nodeFrom = zipnodes .getMethod ( [ "new" , "open" ] ) .getParameter ( 0 ) .asSink ( )
123
- )
124
- }
125
-
126
- /**
127
- * `Zip::File`
128
- */
129
- API:: Node zipFile ( ) { result = API:: getTopLevelCall ( "Zip" ) .getMember ( "File" ) }
130
- }
131
- }
14
+ private import codeql.ruby.Concepts
15
+ private import codeql.ruby.DataFlow
16
+ private import codeql.ruby.TaintTracking
17
+ import DecompressionBombs
132
18
133
19
/**
134
20
* A call to `IO.copy_stream`
@@ -139,30 +25,17 @@ class IoCopyStream extends DataFlow::CallNode {
139
25
DataFlow:: Node getAPathArgument ( ) { result = this .getArgument ( 0 ) }
140
26
}
141
27
142
- class Bombs extends TaintTracking:: Configuration {
143
- Bombs ( ) { this = "Decompression Bombs" }
144
-
145
- override predicate isSanitizer ( DataFlow:: Node node ) {
146
- not node .getLocation ( ) .hasLocationInfo ( "%spec%" , _, _, _, _)
28
+ module BombsConfig implements DataFlow:: ConfigSig {
29
+ predicate isBarrier ( DataFlow:: Node node ) {
30
+ node .getLocation ( ) .hasLocationInfo ( "%spec%" , _, _, _, _)
147
31
}
148
32
149
- override predicate isSource ( DataFlow:: Node source ) {
150
- source instanceof RemoteFlowSource
151
- // or
152
- // source instanceof DataFlow::LocalSourceNode
153
- // source = API::getTopLevelCall("Zip").getMember("InputStream").getASuccessor*()
154
- }
33
+ predicate isSource ( DataFlow:: Node source ) { source instanceof RemoteFlowSource }
155
34
156
- override predicate isSink ( DataFlow:: Node sink ) {
157
- sink instanceof DecompressionBombs:: DecompressionBombSink
158
- }
35
+ predicate isSink ( DataFlow:: Node sink ) { sink instanceof DecompressionBomb:: Sink }
159
36
160
- override predicate isAdditionalTaintStep ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
161
- DecompressionBombs:: ZipFile:: isAdditionalTaintStep ( nodeFrom , nodeTo )
162
- or
163
- DecompressionBombs:: ZipInputStream:: isAdditionalTaintStep ( nodeFrom , nodeTo )
164
- or
165
- DecompressionBombs:: Zlib:: isAdditionalTaintStep ( nodeFrom , nodeTo )
37
+ predicate isAdditionalFlowStep ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
38
+ any ( DecompressionBomb:: AdditionalTaintStep ats ) .isAdditionalTaintStep ( nodeFrom , nodeTo )
166
39
or
167
40
exists ( API:: Node n | n = API:: root ( ) .getMember ( "File" ) .getMethod ( "open" ) |
168
41
nodeFrom = n .getParameter ( 0 ) .asSink ( ) and
@@ -194,7 +67,11 @@ class Bombs extends TaintTracking::Configuration {
194
67
}
195
68
}
196
69
197
- from Bombs cfg , DataFlow:: PathNode source , DataFlow:: PathNode sink
198
- where cfg .hasFlowPath ( source , sink )
199
- select sink .getNode ( ) , source , sink , "This file extraction depends on a $@." , source .getNode ( ) ,
70
+ module Bombs = TaintTracking:: Global< BombsConfig > ;
71
+
72
+ import Bombs:: PathGraph
73
+
74
+ from Bombs:: PathNode source , Bombs:: PathNode sink
75
+ where Bombs:: flowPath ( source , sink )
76
+ select sink .getNode ( ) , source , sink , "This file Decompression depends on a $@." , source .getNode ( ) ,
200
77
"potentially untrusted source"
0 commit comments