Skip to content

Commit 38b23f1

Browse files
authored
Merge pull request #10471 from erik-krogh/tooRacy
JS: filter out "file read after existence check" from js/file-system-race
2 parents 72d3261 + fb5a04a commit 38b23f1

File tree

2 files changed

+30
-12
lines changed

2 files changed

+30
-12
lines changed

javascript/ql/src/Security/CWE-367/FileSystemRace.ql

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,44 @@ import javascript
1818
* A call that checks a property of some file.
1919
*/
2020
class FileCheck extends DataFlow::CallNode {
21+
string member;
22+
2123
FileCheck() {
22-
this =
23-
NodeJSLib::FS::moduleMember([
24-
"open", "openSync", "exists", "existsSync", "stat", "statSync", "lstat", "lstatSync",
25-
"fstat", "fstatSync", "access", "accessSync"
26-
]).getACall()
24+
member =
25+
[
26+
"open", "openSync", "exists", "existsSync", "stat", "statSync", "lstat", "lstatSync",
27+
"fstat", "fstatSync", "access", "accessSync"
28+
] and
29+
this = NodeJSLib::FS::moduleMember(member).getACall()
2730
}
2831

2932
DataFlow::Node getPathArgument() { result = this.getArgument(0) }
33+
34+
/** Holds if this call is a simple existence check for a file. */
35+
predicate isExistsCheck() { member = ["exists", "existsSync"] }
3036
}
3137

3238
/**
3339
* A call that modifies or otherwise interacts with a file.
3440
*/
3541
class FileUse extends DataFlow::CallNode {
42+
string member;
43+
3644
FileUse() {
37-
this =
38-
NodeJSLib::FS::moduleMember([
39-
// these are the six methods that accept file paths and file descriptors
40-
"readFile", "readFileSync", "writeFile", "writeFileSync", "appendFile", "appendFileSync",
41-
// don't use "open" after e.g. "access"
42-
"open", "openSync"
43-
]).getACall()
45+
member =
46+
[
47+
// these are the six methods that accept file paths and file descriptors
48+
"readFile", "readFileSync", "writeFile", "writeFileSync", "appendFile", "appendFileSync",
49+
// don't use "open" after e.g. "access"
50+
"open", "openSync"
51+
] and
52+
this = NodeJSLib::FS::moduleMember(member).getACall()
4453
}
4554

4655
DataFlow::Node getPathArgument() { result = this.getArgument(0) }
56+
57+
/** Holds if this call reads from a file. */
58+
predicate isFileRead() { member = ["readFile", "readFileSync"] }
4759
}
4860

4961
/**
@@ -101,5 +113,6 @@ from FileCheck check, FileUse use
101113
where
102114
checkAndUseOnSame(check, use) and
103115
useAfterCheck(check, use) and
116+
not (check.isExistsCheck() and use.isFileRead()) and // a read after an exists check is fine
104117
not getAFileHandle(DataFlow::TypeTracker::end()).flowsTo(use.getPathArgument())
105118
select use, "The file may have changed since it $@.", check, "was checked"

javascript/ql/test/query-tests/Security/CWE-367/tst.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,8 @@ fs.access("myfile", (err) => {
3636
// ....
3737
});
3838
});
39+
40+
const filePath3 = createFile();
41+
if (fs.existsSync(filePath3)) {
42+
fs.readFileSync(filePath3); // OK - a read after an existence check is OK
43+
}

0 commit comments

Comments
 (0)