|
| 1 | +/** |
| 2 | + * EXPERIMENTAL. This API may change in the future. |
| 3 | + * |
| 4 | + * Provides predicates for working with values exported from a package. |
| 5 | + */ |
| 6 | + |
| 7 | +import javascript |
| 8 | + |
| 9 | +/** |
| 10 | + * Gets the number of occurrences of "/" in `path`. |
| 11 | + */ |
| 12 | +bindingset[path] |
| 13 | +private int countSlashes(string path) { result = count(path.splitAt("/")) - 1 } |
| 14 | + |
| 15 | +/** |
| 16 | + * Gets the topmost package.json that appears in the project. |
| 17 | + * |
| 18 | + * There can be multiple results if the there exists multiple package.json that are equally deeply nested in the folder structure. |
| 19 | + * Results are limited to package.json files that are at most nested 2 directories deep. |
| 20 | + */ |
| 21 | +PackageJSON getTopmostPackageJSON() { |
| 22 | + result = |
| 23 | + min(PackageJSON j | |
| 24 | + countSlashes(j.getFile().getRelativePath()) <= 3 |
| 25 | + | |
| 26 | + j order by countSlashes(j.getFile().getRelativePath()) |
| 27 | + ) |
| 28 | +} |
| 29 | + |
| 30 | +/** |
| 31 | + * Gets a value exported by the main module from the package.json `packageJSON`. |
| 32 | + * The value is either directly the `module.exports` value, a nested property of `module.exports`, or a method on an exported class. |
| 33 | + */ |
| 34 | +DataFlow::Node getAValueExportedBy(PackageJSON packageJSON) { |
| 35 | + result = getAnExportFromModule(packageJSON.getMainModule()) |
| 36 | + or |
| 37 | + result = getAValueExportedBy(packageJSON).(DataFlow::PropWrite).getRhs() |
| 38 | + or |
| 39 | + exists(DataFlow::SourceNode callee | |
| 40 | + callee = getAValueExportedBy(packageJSON).(DataFlow::NewNode).getCalleeNode().getALocalSource() |
| 41 | + | |
| 42 | + result = callee.getAPropertyRead("prototype").getAPropertyWrite() |
| 43 | + or |
| 44 | + result = callee.(DataFlow::ClassNode).getAnInstanceMethod() |
| 45 | + ) |
| 46 | + or |
| 47 | + result = getAValueExportedBy(packageJSON).getALocalSource() |
| 48 | + or |
| 49 | + result = getAValueExportedBy(packageJSON).(DataFlow::SourceNode).getAPropertyReference() |
| 50 | + or |
| 51 | + exists(Module mod | |
| 52 | + mod = getAValueExportedBy(packageJSON).getEnclosingExpr().(Import).getImportedModule() |
| 53 | + | |
| 54 | + result = getAnExportFromModule(mod) |
| 55 | + ) |
| 56 | + or |
| 57 | + exists(DataFlow::ClassNode cla | cla = getAValueExportedBy(packageJSON) | |
| 58 | + result = cla.getAnInstanceMethod() or |
| 59 | + result = cla.getAStaticMethod() or |
| 60 | + result = cla.getConstructor() |
| 61 | + ) |
| 62 | +} |
| 63 | + |
| 64 | +/** |
| 65 | + * Gets an exported node from the module `mod`. |
| 66 | + */ |
| 67 | +private DataFlow::Node getAnExportFromModule(Module mod) { |
| 68 | + result.analyze().getAValue() = mod.(NodeModule).getAModuleExportsValue() |
| 69 | + or |
| 70 | + exists(ASTNode export | result.getEnclosingExpr() = export | mod.exports(_, export)) |
| 71 | +} |
0 commit comments