@@ -25,21 +25,6 @@ module IndirectCommandInjection {
25
25
*/
26
26
abstract class Sanitizer extends DataFlow:: Node { }
27
27
28
- /**
29
- * A source of user input from the command-line, considered as a flow source for command injection.
30
- */
31
- private class CommandLineArgumentsArrayAsSource extends Source instanceof CommandLineArgumentsArray
32
- { }
33
-
34
- /**
35
- * An array of command-line arguments.
36
- */
37
- class CommandLineArgumentsArray extends DataFlow:: SourceNode {
38
- CommandLineArgumentsArray ( ) {
39
- this = DataFlow:: globalVarRef ( "process" ) .getAPropertyRead ( "argv" )
40
- }
41
- }
42
-
43
28
/**
44
29
* A read of `process.env`, considered as a flow source for command injection.
45
30
*/
@@ -82,109 +67,9 @@ module IndirectCommandInjection {
82
67
}
83
68
84
69
/**
85
- * An object containing parsed command-line arguments, considered as a flow source for command injection.
70
+ * An object containing command-line arguments, considered as a flow source for command injection.
86
71
*/
87
- class ParsedCommandLineArgumentsAsSource extends Source {
88
- ParsedCommandLineArgumentsAsSource ( ) {
89
- // `require('get-them-args')(...)` => `{ unknown: [], a: ... b: ... }`
90
- this = DataFlow:: moduleImport ( "get-them-args" ) .getACall ( )
91
- or
92
- // `require('optimist').argv` => `{ _: [], a: ... b: ... }`
93
- this = DataFlow:: moduleMember ( "optimist" , "argv" )
94
- or
95
- // `require("arg")({...spec})` => `{_: [], a: ..., b: ...}`
96
- this = DataFlow:: moduleImport ( "arg" ) .getACall ( )
97
- or
98
- // `(new (require(argparse)).ArgumentParser({...spec})).parse_args()` => `{a: ..., b: ...}`
99
- this =
100
- API:: moduleImport ( "argparse" )
101
- .getMember ( "ArgumentParser" )
102
- .getInstance ( )
103
- .getMember ( "parse_args" )
104
- .getACall ( )
105
- or
106
- // `require('command-line-args')({...spec})` => `{a: ..., b: ...}`
107
- this = DataFlow:: moduleImport ( "command-line-args" ) .getACall ( )
108
- or
109
- // `require('meow')(help, {...spec})` => `{a: ..., b: ....}`
110
- this = DataFlow:: moduleImport ( "meow" ) .getACall ( )
111
- or
112
- // `require("dashdash").createParser(...spec)` => `{a: ..., b: ...}`
113
- this =
114
- [
115
- API:: moduleImport ( "dashdash" ) ,
116
- API:: moduleImport ( "dashdash" ) .getMember ( "createParser" ) .getReturn ( )
117
- ] .getMember ( "parse" ) .getACall ( )
118
- or
119
- // `require('commander').myCmdArgumentName`
120
- this = commander ( ) .getAMember ( ) .asSource ( )
121
- or
122
- // `require('commander').opt()` => `{a: ..., b: ...}`
123
- this = commander ( ) .getMember ( "opts" ) .getACall ( )
124
- }
125
- }
126
-
127
- /**
128
- * Holds if there is a command line parsing step from `pred` to `succ`.
129
- * E.g: `var succ = require("minimist")(pred)`.
130
- */
131
- predicate argsParseStep ( DataFlow:: Node pred , DataFlow:: Node succ ) {
132
- exists ( DataFlow:: CallNode call |
133
- call = DataFlow:: moduleMember ( "args" , "parse" ) .getACall ( ) or
134
- call = DataFlow:: moduleImport ( [ "yargs-parser" , "minimist" , "subarg" ] ) .getACall ( )
135
- |
136
- succ = call and
137
- pred = call .getArgument ( 0 )
138
- )
139
- }
140
-
141
- /**
142
- * Gets a Command instance from the `commander` library.
143
- */
144
- private API:: Node commander ( ) {
145
- result = API:: moduleImport ( "commander" )
146
- or
147
- // `require("commander").program === require("commander")`
148
- result = commander ( ) .getMember ( "program" )
149
- or
150
- result = commander ( ) .getMember ( "Command" ) .getInstance ( )
151
- or
152
- // lots of chainable methods
153
- result = commander ( ) .getAMember ( ) .getReturn ( )
154
- }
155
-
156
- /**
157
- * Gets an instance of `yargs`.
158
- * Either directly imported as a module, or through some chained method call.
159
- */
160
- private DataFlow:: SourceNode yargs ( ) {
161
- result = DataFlow:: moduleImport ( "yargs" )
162
- or
163
- // script used to generate list of chained methods: https://gist.github.com/erik-krogh/f8afe952c0577f4b563a993e613269ba
164
- exists ( string method |
165
- not method =
166
- // the methods that does not return a chained `yargs` object.
167
- [
168
- "getContext" , "getDemandedOptions" , "getDemandedCommands" , "getDeprecatedOptions" ,
169
- "_getParseContext" , "getOptions" , "getGroups" , "getStrict" , "getStrictCommands" ,
170
- "getExitProcess" , "locale" , "getUsageInstance" , "getCommandInstance"
171
- ]
172
- |
173
- result = yargs ( ) .getAMethodCall ( method )
174
- )
175
- }
176
-
177
- /**
178
- * An array of command line arguments (`argv`) parsed by the `yargs` library.
179
- */
180
- class YargsArgv extends Source {
181
- YargsArgv ( ) {
182
- this = yargs ( ) .getAPropertyRead ( "argv" )
183
- or
184
- this = yargs ( ) .getAMethodCall ( "parse" ) and
185
- this .( DataFlow:: MethodCallNode ) .getNumArgument ( ) = 0
186
- }
187
- }
72
+ private class CommandLineArgumentsAsSource extends Source instanceof CommandLineArguments { }
188
73
189
74
/**
190
75
* A command-line argument that effectively is system-controlled, and therefore not likely to be exploitable when used in the execution of another command.
@@ -193,7 +78,7 @@ module IndirectCommandInjection {
193
78
SystemControlledCommandLineArgumentSanitizer ( ) {
194
79
// `process.argv[0]` and `process.argv[1]` are paths to `node` and `main`.
195
80
exists ( string index | index = "0" or index = "1" |
196
- this = any ( CommandLineArgumentsArray a ) .getAPropertyRead ( index )
81
+ this = DataFlow :: globalVarRef ( "process" ) . getAPropertyRead ( "argv" ) .getAPropertyRead ( index )
197
82
)
198
83
}
199
84
}
0 commit comments