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

Skip to content

Commit 2fc2a39

Browse files
committed
JS: Address review comments
1 parent 09b9a57 commit 2fc2a39

4 files changed

Lines changed: 64 additions & 31 deletions

File tree

javascript/ql/src/Security/CWE-022/ZipSlip.qhelp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,22 @@ file paths are used to determine an output file to write the contents of the arc
1616
the file may be written to an unexpected location. This can result in sensitive information being
1717
revealed or deleted, or an attacker being able to influence behavior by modifying unexpected
1818
files.</p>
19-
</overview>
2019

20+
<p>For example, if a zip file contains a file entry <code>..\sneaky-file</code>, and the zip file
21+
is extracted to the directory <code>c:\output</code>, then naively combining the paths would result
22+
in an output file path of <code>c:\output\..\sneaky-file</code>, which would cause the file to be
23+
written to <code>c:\sneaky-file</code>.</p>
24+
25+
</overview>
2126
<recommendation>
2227

2328
<p>Ensure that output paths constructed from zip archive entries are validated
2429
to prevent writing files to unexpected locations.</p>
2530

31+
<p>The recommended way of writing an output file from a zip archive entry is to check that
32+
<code>".."</code> does not occur in the path.
33+
</p>
34+
2635
</recommendation>
2736

2837
<example>
@@ -34,5 +43,12 @@ instance, if it were created by something like <code>zip archive.zip
3443
</p>
3544

3645
<sample src="ZipSlipBad.js" />
46+
47+
<p>To fix this vulnerability, we can to check that the path does not
48+
contain any <code>".."</code> in it.
49+
</p>
50+
51+
<sample src="ZipSlipGood.java" />
52+
3753
</example>
3854
</qhelp>

javascript/ql/src/Security/CWE-022/ZipSlip.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* @kind path-problem
77
* @id js/zipslip
88
* @problem.severity error
9-
* @precision high
9+
* @precision medium
1010
* @tags security
1111
* external/cwe/cwe-022
1212
*/
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const fs = require('fs');
2+
const unzip = require('unzip');
3+
4+
fs.createReadStream('archive.zip')
5+
.pipe(unzip.Parse())
6+
.on('entry', entry => {
7+
const fileName = entry.path;
8+
if (entry.path.indexOf('..') == -1) {
9+
entry.pipe(fs.createWriteStream(entry.path));
10+
}
11+
else {
12+
console.log('skipping bad path', entry.path);
13+
}
14+
});

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

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module ZipSlip {
2020
*/
2121
abstract class SanitizerGuard extends TaintTracking::SanitizerGuardNode, DataFlow::ValueNode { }
2222

23-
/** A taint tracking configuration for Zip Slip */
23+
/** A taint tracking configuration for unsafe zip extraction. */
2424
class Configuration extends TaintTracking::Configuration {
2525
Configuration() { this = "ZipSlip" }
2626

@@ -36,51 +36,54 @@ module ZipSlip {
3636
/**
3737
* Gets a node that can be a parsed zip archive.
3838
*/
39-
DataFlow::SourceNode parsedArchive() {
39+
private DataFlow::SourceNode parsedArchive() {
4040
result = DataFlow::moduleImport("unzip").getAMemberCall("Parse")
4141
or
4242
// `streamProducer.pipe(unzip.Parse())` is a typical (but not
4343
// universal) pattern when using nodejs streams, whose return
4444
// value is the parsed stream.
45-
exists(DataFlow::MethodCallNode pipe | pipe.getMethodName() = "pipe"
46-
and parsedArchive().flowsTo(pipe.getArgument(0)))
45+
exists(DataFlow::MethodCallNode pipe |
46+
pipe.getMethodName() = "pipe" and
47+
parsedArchive().flowsTo(pipe.getArgument(0))
48+
)
4749
}
4850

49-
/**
50-
* An access to the filepath of an entry of a zipfile being extracted
51-
* by npm module `unzip`. For example, in
52-
* ```javascript
53-
* const unzip = require('unzip');
54-
*
55-
* fs.createReadStream('archive.zip')
56-
* .pipe(unzip.Parse())
57-
* .on('entry', entry => {
58-
* const path = entry.path;
59-
* });
60-
* ```
61-
* there is an `UnzipEntrySource` node corresponding to
62-
* the expression `entry.path`.
63-
*/
51+
/** A zip archive entry path access, as a source for unsafe zip extraction. */
6452
class UnzipEntrySource extends Source {
53+
// For example, in
54+
// ```javascript
55+
// const unzip = require('unzip');
56+
//
57+
// fs.createReadStream('archive.zip')
58+
// .pipe(unzip.Parse())
59+
// .on('entry', entry => {
60+
// const path = entry.path;
61+
// });
62+
// ```
63+
// there is an `UnzipEntrySource` node corresponding to
64+
// the expression `entry.path`.
6565
UnzipEntrySource() {
66-
this = parsedArchive().getAMemberCall("on").getCallback(1).getParameter(0).getAPropertyRead("path")
66+
this = parsedArchive()
67+
.getAMemberCall("on")
68+
.getCallback(1)
69+
.getParameter(0)
70+
.getAPropertyRead("path")
6771
}
6872
}
6973

70-
/**
71-
* A sink that is the path that a createWriteStream gets created at.
72-
* This is not covered by FileSystemWriteSink, because it is
73-
* required that a write actually takes place to the stream.
74-
* However, we want to consider even the bare createWriteStream to
75-
* be a zipslip vulnerability since it may truncate an existing file.
76-
*/
74+
/** A call to `fs.createWriteStream`, as a sink for unsafe zip extraction. */
7775
class CreateWriteStreamSink extends Sink {
7876
CreateWriteStreamSink() {
77+
// This is not covered by `FileSystemWriteSink`, because it is
78+
// required that a write actually takes place to the stream.
79+
// However, we want to consider even the bare createWriteStream
80+
// to be a zipslip vulnerability since it may truncate an
81+
// existing file.
7982
this = DataFlow::moduleImport("fs").getAMemberCall("createWriteStream").getArgument(0)
8083
}
8184
}
8285

83-
/** A sink that is a file path that gets written to. */
86+
/** A file path of a file write, as a sink for unsafe zip extraction. */
8487
class FileSystemWriteSink extends Sink {
8588
FileSystemWriteSink() { exists(FileSystemWriteAccess fsw | fsw.getAPathArgument() = this) }
8689
}
@@ -89,7 +92,7 @@ module ZipSlip {
8992
* Gets a string which suffices to search for to ensure that a
9093
* filepath will not refer to parent directories.
9194
*/
92-
string getAParentDirName() { result = ".." or result = "../" }
95+
private string getAParentDirName() { result = ".." or result = "../" }
9396

9497
/** A check that a path string does not include '..' */
9598
class NoParentDirSanitizerGuard extends SanitizerGuard {

0 commit comments

Comments
 (0)