Skip to content

Commit 5b569a4

Browse files
committed
add a sanitizer for chained replace-calls
1 parent b719192 commit 5b569a4

File tree

3 files changed

+52
-0
lines changed

3 files changed

+52
-0
lines changed

javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ private import semmle.javascript.PackageExports as Exports
1313
*/
1414
module UnsafeShellCommandConstruction {
1515
import IndirectCommandArgument
16+
import semmle.javascript.security.IncompleteBlacklistSanitizer as IncompleteBlacklistSanitizer
1617

1718
/**
1819
* A data flow source for shell command constructed from library input.
@@ -155,6 +156,19 @@ module UnsafeShellCommandConstruction {
155156
}
156157
}
157158

159+
/**
160+
* A chain of replace calls that replaces all unsafe chars for shell-commands.
161+
*/
162+
class ChainSanitizer extends Sanitizer, IncompleteBlacklistSanitizer::StringReplaceCallSequence {
163+
ChainSanitizer() {
164+
forall(string char |
165+
char = ["&", "`", "$", "|", ">", "<", "#", ";", "(", ")", "[", "]", "\n"]
166+
|
167+
this.getAMember().getAReplacedString() = char
168+
)
169+
}
170+
}
171+
158172
/**
159173
* A sanitizer that sanitizers paths that exist in the file-system.
160174
* For example: `x` is sanitized in `fs.existsSync(x)` or `fs.existsSync(x + "/suffix/path")`.

javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ nodes
169169
| lib/lib.js:277:23:277:26 | opts |
170170
| lib/lib.js:277:23:277:30 | opts.bla |
171171
| lib/lib.js:277:23:277:30 | opts.bla |
172+
| lib/lib.js:307:39:307:42 | name |
173+
| lib/lib.js:307:39:307:42 | name |
174+
| lib/lib.js:308:23:308:26 | name |
175+
| lib/lib.js:308:23:308:26 | name |
172176
edges
173177
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
174178
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
@@ -373,6 +377,10 @@ edges
373377
| lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:26 | opts |
374378
| lib/lib.js:277:23:277:26 | opts | lib/lib.js:277:23:277:30 | opts.bla |
375379
| lib/lib.js:277:23:277:26 | opts | lib/lib.js:277:23:277:30 | opts.bla |
380+
| lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name |
381+
| lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name |
382+
| lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name |
383+
| lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name |
376384
#select
377385
| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on libary input is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command |
378386
| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on libary input is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command |
@@ -424,3 +432,4 @@ edges
424432
| lib/lib.js:268:10:268:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:32 | obj.version | $@ based on libary input is later used in $@. | lib/lib.js:268:10:268:32 | "rm -rf ... version | String concatenation | lib/lib.js:268:2:268:33 | cp.exec ... ersion) | shell command |
425433
| lib/lib.js:272:10:272:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:272:22:272:32 | obj.version | $@ based on libary input is later used in $@. | lib/lib.js:272:10:272:32 | "rm -rf ... version | String concatenation | lib/lib.js:272:2:272:33 | cp.exec ... ersion) | shell command |
426434
| lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | lib/lib.js:276:8:276:11 | opts | lib/lib.js:277:23:277:30 | opts.bla | $@ based on libary input is later used in $@. | lib/lib.js:277:11:277:30 | "rm -rf " + opts.bla | String concatenation | lib/lib.js:277:3:277:31 | cp.exec ... ts.bla) | shell command |
435+
| lib/lib.js:308:11:308:26 | "rm -rf " + name | lib/lib.js:307:39:307:42 | name | lib/lib.js:308:23:308:26 | name | $@ based on libary input is later used in $@. | lib/lib.js:308:11:308:26 | "rm -rf " + name | String concatenation | lib/lib.js:308:3:308:27 | cp.exec ... + name) | shell command |

javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,33 @@ module.exports.Foo = class Foo {
280280

281281
cp.exec("rm -rf " + this.opts.bla); // NOT OK - but FN
282282
}
283+
}
284+
285+
function sanitizeShellString(str) {
286+
let result = str;
287+
result = result.replace(/>/g, "");
288+
result = result.replace(/</g, "");
289+
result = result.replace(/\*/g, "");
290+
result = result.replace(/\?/g, "");
291+
result = result.replace(/\[/g, "");
292+
result = result.replace(/\]/g, "");
293+
result = result.replace(/\|/g, "");
294+
result = result.replace(/\`/g, "");
295+
result = result.replace(/$/g, "");
296+
result = result.replace(/;/g, "");
297+
result = result.replace(/&/g, "");
298+
result = result.replace(/\)/g, "");
299+
result = result.replace(/\(/g, "");
300+
result = result.replace(/\$/g, "");
301+
result = result.replace(/#/g, "");
302+
result = result.replace(/\\/g, "");
303+
result = result.replace(/\n/g, "");
304+
return result
305+
}
306+
307+
module.exports.sanitizer2 = function (name) {
308+
cp.exec("rm -rf " + name); // NOT OK
309+
310+
var sanitized = sanitizeShellString(name);
311+
cp.exec("rm -rf " + sanitized); // OK
283312
}

0 commit comments

Comments
 (0)