|
4 | 4 |
|
5 | 5 | import javascript
|
6 | 6 |
|
| 7 | +/** |
| 8 | + * A call that can produce a file name. |
| 9 | + */ |
| 10 | +abstract private class FileNameProducer extends DataFlow::Node { |
| 11 | + /** |
| 12 | + * Gets a file name produced by this producer. |
| 13 | + */ |
| 14 | + abstract DataFlow::Node getAFileName(); |
| 15 | +} |
| 16 | + |
| 17 | +/** |
| 18 | + * A node that contains a file name, and is produced by a `ProducesFileNames`. |
| 19 | + */ |
| 20 | +private class ProducedFileName extends FileNameSource { |
| 21 | + ProducedFileName() { this = any(FileNameProducer producer).getAFileName() } |
| 22 | +} |
| 23 | + |
7 | 24 | /**
|
8 | 25 | * A file name from the `walk-sync` library.
|
9 | 26 | */
|
@@ -106,3 +123,143 @@ private class FastGlobFileNameSource extends FileNameSource {
|
106 | 123 | )
|
107 | 124 | }
|
108 | 125 | }
|
| 126 | + |
| 127 | +/** |
| 128 | + * Classes and predicates for modelling the `fstream` library (https://www.npmjs.com/package/fstream). |
| 129 | + */ |
| 130 | +private module FStream { |
| 131 | + /** |
| 132 | + * Gets a reference to a method in the `fstream` library. |
| 133 | + */ |
| 134 | + private DataFlow::SourceNode getAnFStreamProperty() { |
| 135 | + exists(DataFlow::SourceNode mod, string readOrWrite, string subMod | |
| 136 | + mod = DataFlow::moduleImport("fstream") and |
| 137 | + (readOrWrite = "Reader" or readOrWrite = "Writer") and |
| 138 | + (subMod = "File" or subMod = "Dir" or subMod = "Link" or subMod = "Proxy") |
| 139 | + | |
| 140 | + result = mod.getAPropertyRead(readOrWrite) or |
| 141 | + result = mod.getAPropertyRead(readOrWrite).getAPropertyRead(subMod) or |
| 142 | + result = mod.getAPropertyRead(subMod).getAPropertyRead(readOrWrite) |
| 143 | + ) |
| 144 | + } |
| 145 | + |
| 146 | + /** |
| 147 | + * An invocation of a method defined in the `fstream` library. |
| 148 | + */ |
| 149 | + private class FStream extends FileSystemAccess, DataFlow::InvokeNode { |
| 150 | + FStream() { this = getAnFStreamProperty().getAnInvocation() } |
| 151 | + |
| 152 | + override DataFlow::Node getAPathArgument() { |
| 153 | + result = getOptionArgument(0, "path") |
| 154 | + or |
| 155 | + not exists(getOptionArgument(0, "path")) and |
| 156 | + result = getArgument(0) |
| 157 | + } |
| 158 | + } |
| 159 | +} |
| 160 | + |
| 161 | +/** |
| 162 | + * A call to the library `write-file-atomic`. |
| 163 | + */ |
| 164 | +private class WriteFileAtomic extends FileSystemWriteAccess, DataFlow::CallNode { |
| 165 | + WriteFileAtomic() { |
| 166 | + this = DataFlow::moduleImport("write-file-atomic").getACall() |
| 167 | + or |
| 168 | + this = DataFlow::moduleMember("write-file-atomic", "sync").getACall() |
| 169 | + } |
| 170 | + |
| 171 | + override DataFlow::Node getAPathArgument() { result = getArgument(0) } |
| 172 | + |
| 173 | + override DataFlow::Node getADataNode() { result = getArgument(1) } |
| 174 | +} |
| 175 | + |
| 176 | +/** |
| 177 | + * A call to the library `recursive-readdir`. |
| 178 | + */ |
| 179 | +private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, DataFlow::CallNode { |
| 180 | + RecursiveReadDir() { this = DataFlow::moduleImport("recursive-readdir").getACall() } |
| 181 | + |
| 182 | + override DataFlow::Node getAPathArgument() { result = getArgument(0) } |
| 183 | + |
| 184 | + override DataFlow::Node getAFileName() { result = getCallback([1 .. 2]).getParameter(1) } |
| 185 | +} |
| 186 | + |
| 187 | +/** |
| 188 | + * Classes and predicates for modelling the `jsonfile` library (https://www.npmjs.com/package/jsonfile). |
| 189 | + */ |
| 190 | +private module JSONFile { |
| 191 | + /** |
| 192 | + * A reader for JSON files. |
| 193 | + */ |
| 194 | + class JSONFileReader extends FileSystemReadAccess, DataFlow::CallNode { |
| 195 | + JSONFileReader() { |
| 196 | + this = |
| 197 | + DataFlow::moduleMember("jsonfile", any(string s | s = "readFile" or s = "readFileSync")) |
| 198 | + .getACall() |
| 199 | + } |
| 200 | + |
| 201 | + override DataFlow::Node getAPathArgument() { result = getArgument(0) } |
| 202 | + |
| 203 | + override DataFlow::Node getADataNode() { |
| 204 | + this.getCalleeName() = "readFile" and result = getCallback([1 .. 2]).getParameter(1) |
| 205 | + or |
| 206 | + this.getCalleeName() = "readFileSync" and result = this |
| 207 | + } |
| 208 | + } |
| 209 | + |
| 210 | + /** |
| 211 | + * A writer for JSON files. |
| 212 | + */ |
| 213 | + class JSONFileWriter extends FileSystemWriteAccess, DataFlow::CallNode { |
| 214 | + JSONFileWriter() { |
| 215 | + this = |
| 216 | + DataFlow::moduleMember("jsonfile", any(string s | s = "writeFile" or s = "writeFileSync")) |
| 217 | + .getACall() |
| 218 | + } |
| 219 | + |
| 220 | + override DataFlow::Node getAPathArgument() { result = getArgument(0) } |
| 221 | + |
| 222 | + override DataFlow::Node getADataNode() { result = getArgument(1) } |
| 223 | + } |
| 224 | +} |
| 225 | + |
| 226 | +/** |
| 227 | + * A file system access made by a NodeJS library. |
| 228 | + * This class models multiple NodeJS libraries that access files. |
| 229 | + */ |
| 230 | +private class LibraryAccess extends FileSystemAccess, DataFlow::InvokeNode { |
| 231 | + int pathArgument; // The index of the path argument. |
| 232 | + |
| 233 | + LibraryAccess() { |
| 234 | + pathArgument = 0 and |
| 235 | + ( |
| 236 | + this = DataFlow::moduleImport("path-exists").getACall() |
| 237 | + or |
| 238 | + this = DataFlow::moduleImport("rimraf").getACall() |
| 239 | + or |
| 240 | + this = |
| 241 | + DataFlow::moduleMember("node-dir", |
| 242 | + any(string s | |
| 243 | + s = "readFiles" or |
| 244 | + s = "readFilesStream" or |
| 245 | + s = "files" or |
| 246 | + s = "promiseFiles" or |
| 247 | + s = "subdirs" or |
| 248 | + s = "paths" |
| 249 | + )).getACall() |
| 250 | + ) |
| 251 | + or |
| 252 | + pathArgument = 0 and |
| 253 | + this = |
| 254 | + DataFlow::moduleMember("vinyl-fs", any(string s | s = "src" or s = "dest" or s = "symlink")) |
| 255 | + .getACall() |
| 256 | + or |
| 257 | + pathArgument = [0 .. 1] and |
| 258 | + ( |
| 259 | + this = DataFlow::moduleImport("ncp").getACall() or |
| 260 | + this = DataFlow::moduleMember("ncp", "ncp").getACall() |
| 261 | + ) |
| 262 | + } |
| 263 | + |
| 264 | + override DataFlow::Node getAPathArgument() { result = getArgument(pathArgument) } |
| 265 | +} |
0 commit comments