Merge pull request #3065 from erik-krogh/PathSinks

Approved by esbena
This commit is contained in:
semmle-qlci
2020-03-17 13:00:00 +00:00
committed by GitHub
6 changed files with 251 additions and 0 deletions

View File

@@ -4,6 +4,23 @@
import javascript
/**
* A call that can produce a file name.
*/
abstract private class FileNameProducer extends DataFlow::Node {
/**
* Gets a file name produced by this producer.
*/
abstract DataFlow::Node getAFileName();
}
/**
* A node that contains a file name, and is produced by a `ProducesFileNames`.
*/
private class ProducedFileName extends FileNameSource {
ProducedFileName() { this = any(FileNameProducer producer).getAFileName() }
}
/**
* A file name from the `walk-sync` library.
*/
@@ -106,3 +123,143 @@ private class FastGlobFileNameSource extends FileNameSource {
)
}
}
/**
* Classes and predicates for modelling the `fstream` library (https://www.npmjs.com/package/fstream).
*/
private module FStream {
/**
* Gets a reference to a method in the `fstream` library.
*/
private DataFlow::SourceNode getAnFStreamProperty() {
exists(DataFlow::SourceNode mod, string readOrWrite, string subMod |
mod = DataFlow::moduleImport("fstream") and
(readOrWrite = "Reader" or readOrWrite = "Writer") and
(subMod = "File" or subMod = "Dir" or subMod = "Link" or subMod = "Proxy")
|
result = mod.getAPropertyRead(readOrWrite) or
result = mod.getAPropertyRead(readOrWrite).getAPropertyRead(subMod) or
result = mod.getAPropertyRead(subMod).getAPropertyRead(readOrWrite)
)
}
/**
* An invocation of a method defined in the `fstream` library.
*/
private class FStream extends FileSystemAccess, DataFlow::InvokeNode {
FStream() { this = getAnFStreamProperty().getAnInvocation() }
override DataFlow::Node getAPathArgument() {
result = getOptionArgument(0, "path")
or
not exists(getOptionArgument(0, "path")) and
result = getArgument(0)
}
}
}
/**
* A call to the library `write-file-atomic`.
*/
private class WriteFileAtomic extends FileSystemWriteAccess, DataFlow::CallNode {
WriteFileAtomic() {
this = DataFlow::moduleImport("write-file-atomic").getACall()
or
this = DataFlow::moduleMember("write-file-atomic", "sync").getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getADataNode() { result = getArgument(1) }
}
/**
* A call to the library `recursive-readdir`.
*/
private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, DataFlow::CallNode {
RecursiveReadDir() { this = DataFlow::moduleImport("recursive-readdir").getACall() }
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getAFileName() { result = getCallback([1 .. 2]).getParameter(1) }
}
/**
* Classes and predicates for modelling the `jsonfile` library (https://www.npmjs.com/package/jsonfile).
*/
private module JSONFile {
/**
* A reader for JSON files.
*/
class JSONFileReader extends FileSystemReadAccess, DataFlow::CallNode {
JSONFileReader() {
this =
DataFlow::moduleMember("jsonfile", any(string s | s = "readFile" or s = "readFileSync"))
.getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getADataNode() {
this.getCalleeName() = "readFile" and result = getCallback([1 .. 2]).getParameter(1)
or
this.getCalleeName() = "readFileSync" and result = this
}
}
/**
* A writer for JSON files.
*/
class JSONFileWriter extends FileSystemWriteAccess, DataFlow::CallNode {
JSONFileWriter() {
this =
DataFlow::moduleMember("jsonfile", any(string s | s = "writeFile" or s = "writeFileSync"))
.getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getADataNode() { result = getArgument(1) }
}
}
/**
* A file system access made by a NodeJS library.
* This class models multiple NodeJS libraries that access files.
*/
private class LibraryAccess extends FileSystemAccess, DataFlow::InvokeNode {
int pathArgument; // The index of the path argument.
LibraryAccess() {
pathArgument = 0 and
(
this = DataFlow::moduleImport("path-exists").getACall()
or
this = DataFlow::moduleImport("rimraf").getACall()
or
this =
DataFlow::moduleMember("node-dir",
any(string s |
s = "readFiles" or
s = "readFilesStream" or
s = "files" or
s = "promiseFiles" or
s = "subdirs" or
s = "paths"
)).getACall()
)
or
pathArgument = 0 and
this =
DataFlow::moduleMember("vinyl-fs", any(string s | s = "src" or s = "dest" or s = "symlink"))
.getACall()
or
pathArgument = [0 .. 1] and
(
this = DataFlow::moduleImport("ncp").getACall() or
this = DataFlow::moduleMember("ncp", "ncp").getACall()
)
}
override DataFlow::Node getAPathArgument() { result = getArgument(pathArgument) }
}

View File

@@ -0,0 +1,26 @@
getPathArgument
| file-access.js:3:1:4:34 | fstream ... file"}) | file-access.js:4:19:4:32 | "path/to/file" |
| file-access.js:8:1:9:24 | fstream ... o/dir") | file-access.js:9:11:9:23 | "path/to/dir" |
| file-access.js:10:9:10:43 | fstream ... r/dir") | file-access.js:10:24:10:42 | "path/to/other/dir" |
| file-access.js:15:1:15:60 | writeFi ... rr) {}) | file-access.js:15:17:15:28 | 'atmoic.txt' |
| file-access.js:18:1:18:59 | writeFi ... tions]) | file-access.js:18:21:18:34 | "syncFile.txt" |
| file-access.js:22:1:22:48 | recursi ... es) {}) | file-access.js:22:11:22:21 | "some/path" |
| file-access.js:25:1:25:59 | jsonfil ... bj) {}) | file-access.js:25:19:25:34 | '/tmp/data.json' |
| file-access.js:26:1:26:39 | jsonfil ... .json') | file-access.js:26:23:26:38 | '/tmp/data.json' |
| file-access.js:28:1:28:60 | jsonfil ... rr) {}) | file-access.js:28:20:28:35 | '/tmp/data.json' |
| file-access.js:29:1:29:45 | jsonfil ... ', obj) | file-access.js:29:24:29:39 | '/tmp/data.json' |
| file-access.js:34:4:34:23 | pathExists('foo.js') | file-access.js:34:15:34:22 | 'foo.js' |
| file-access.js:39:1:39:28 | rimraf( ... => {}) | file-access.js:39:8:39:10 | "/" |
| file-access.js:42:1:42:59 | dir.rea ... on(){}) | file-access.js:42:15:42:31 | "/some/directory" |
| file-access.js:46:1:46:25 | vfs.src ... path"]) | file-access.js:46:9:46:24 | ["some", "path"] |
| file-access.js:47:1:47:36 | vfs.des ... true }) | file-access.js:47:10:47:13 | './' |
| file-access.js:51:1:51:36 | ncp("fr ... rr) {}) | file-access.js:51:5:51:10 | "from" |
| file-access.js:51:1:51:36 | ncp("fr ... rr) {}) | file-access.js:51:13:51:16 | "to" |
getReadNode
| file-access.js:25:1:25:59 | jsonfil ... bj) {}) | file-access.js:25:52:25:54 | obj |
| file-access.js:26:1:26:39 | jsonfil ... .json') | file-access.js:26:1:26:39 | jsonfil ... .json') |
getWriteNode
| file-access.js:15:1:15:60 | writeFi ... rr) {}) | file-access.js:15:31:15:36 | 'Data' |
| file-access.js:18:1:18:59 | writeFi ... tions]) | file-access.js:18:37:18:47 | "More data" |
| file-access.js:28:1:28:60 | jsonfil ... rr) {}) | file-access.js:28:38:28:40 | obj |
| file-access.js:29:1:29:45 | jsonfil ... ', obj) | file-access.js:29:42:29:44 | obj |

View File

@@ -0,0 +1,7 @@
import javascript
query DataFlow::Node getPathArgument(FileSystemAccess access) { result = access.getAPathArgument() }
query DataFlow::Node getReadNode(FileSystemReadAccess access) { result = access.getADataNode() }
query DataFlow::Node getWriteNode(FileSystemWriteAccess access) { result = access.getADataNode() }

View File

@@ -1,3 +1,4 @@
| file-access.js:22:39:22:43 | files |
| tst-file-names.js:7:1:7:10 | walkSync() |
| tst-file-names.js:9:35:9:44 | stats.name |
| tst-file-names.js:11:1:11:12 | glob.sync(_) |

View File

@@ -0,0 +1,51 @@
var fstream = require("fstream");
fstream
.Writer({ path: "path/to/file"})
.write("hello\n")
.end()
fstream
.Reader("path/to/dir")
.pipe(fstream.Writer("path/to/other/dir"))
var writeFileAtomic= require("write-file-atomic");
writeFileAtomic('atmoic.txt', 'Data', {}, function (err) {});
var writeFileAtomicSync = require('write-file-atomic').sync
writeFileAtomicSync("syncFile.txt", "More data", [options])
var recursive = require("recursive-readdir");
recursive("some/path", function (err, files) {});
const jsonfile = require('jsonfile');
jsonfile.readFile('/tmp/data.json', function (err, obj) {});
jsonfile.readFileSync('/tmp/data.json');
jsonfile.writeFile('/tmp/data.json', obj, function (err) {});
jsonfile.writeFileSync('/tmp/data.json', obj);
const pathExists = require('path-exists');
if(pathExists('foo.js')) {
// do something.
}
var rimraf = require("rimraf");
rimraf("/", {}, (err) => {});
var dir = require("node-dir");
dir.readFiles("/some/directory",function() {},function(){});
var vfs = require("vinyl-fs");
vfs.src(["some", "path"]);
vfs.dest('./', { sourcemaps: true });
var ncp = require('ncp').ncp;
ncp("from", "to", function (err) {});