@@ -2,6 +2,8 @@ private import javascript
22private import semmle.javascript.TSConfig
33private import semmle.javascript.internal.paths.PackageJsonEx
44private import semmle.javascript.internal.paths.JSPaths
5+ private import semmle.javascript.internal.paths.PathConcatenation
6+ private import semmle.javascript.dataflow.internal.DataFlowNode
57
68File getFileFromFolderImport ( Folder folder ) {
79 result = folder .getJavaScriptFileOrTypings ( "index" )
@@ -14,15 +16,68 @@ File getFileFromFolderImport(Folder folder) {
1416 )
1517}
1618
19+ private Variable dirname ( ) { result .getName ( ) = "__dirname" }
20+
21+ private Variable filename ( ) { result .getName ( ) = "__filename" }
22+
1723private signature predicate exprSig ( Expr e ) ;
1824
1925module ResolveExpr< exprSig / 1 shouldResolveExpr> {
26+ /** Holds if we need the constant-value of `node`. */
27+ private predicate needsConstantFolding ( EarlyStageNode node ) {
28+ exists ( Expr e |
29+ shouldResolveExpr ( e ) and
30+ node = TValueNode ( e )
31+ )
32+ or
33+ exists ( EarlyStageNode needsFolding | needsConstantFolding ( needsFolding ) |
34+ DataFlow:: Impl:: earlyStageImmediateFlowStep ( node , needsFolding )
35+ or
36+ exists ( PathConcatenation joiner |
37+ needsFolding = TValueNode ( joiner ) and
38+ node = TValueNode ( joiner .getOperand ( _) )
39+ )
40+ )
41+ }
42+
43+ /** Gets the constant-value of `node` */
44+ language [ monotonicAggregates]
45+ private string getValue ( EarlyStageNode node ) {
46+ needsConstantFolding ( node ) and
47+ (
48+ exists ( Expr e | node = TValueNode ( e ) |
49+ result = e .getStringValue ( )
50+ or
51+ e = dirname ( ) .getAnAccess ( ) and
52+ result = "./" // Ensure the path gets interpreted relative to the current directory
53+ or
54+ e = filename ( ) .getAnAccess ( ) and
55+ result = "./" + e .getFile ( ) .getBaseName ( )
56+ )
57+ or
58+ exists ( EarlyStageNode pred |
59+ DataFlow:: Impl:: earlyStageImmediateFlowStep ( pred , node ) and
60+ result = getValue ( pred )
61+ )
62+ or
63+ exists ( PathConcatenation join |
64+ node = TValueNode ( join ) and
65+ result =
66+ strictconcat ( int n , EarlyStageNode child , string sep |
67+ child = TValueNode ( join .getOperand ( n ) ) and sep = join .getSeparator ( )
68+ |
69+ getValue ( child ) , sep order by n
70+ )
71+ )
72+ )
73+ }
74+
2075 final private class FinalExpr = Expr ;
2176
2277 private class RelevantExpr extends FinalExpr {
2378 RelevantExpr ( ) { shouldResolveExpr ( this ) }
2479
25- string getValue ( ) { result = this . getStringValue ( ) }
80+ string getValue ( ) { result = getValue ( TValueNode ( this ) ) }
2681 }
2782
2883 /**
@@ -66,17 +121,6 @@ module ResolveExpr<exprSig/1 shouldResolveExpr> {
66121 result = expr .getValue ( ) .regexpFind ( "^(@[^/\\\\]+[/\\\\])?[^@./\\\\][^/\\\\]*" , _, _)
67122 }
68123
69- private Variable dirname ( ) { result .getName ( ) = "__dirname" }
70-
71- /** Holds if `add` is a relevant path expression of form `__dirname + expr`. */
72- private predicate prefixedByDirname ( Expr expr ) {
73- expr = dirname ( ) .getAnAccess ( )
74- or
75- prefixedByDirname ( expr .( AddExpr ) .getLeftOperand ( ) )
76- or
77- prefixedByDirname ( expr .( CallExpr ) .getArgument ( 0 ) )
78- }
79-
80124 /**
81125 * Holds if `expr` matches a path mapping, and should thus be resolved as `newPath` relative to `base`.
82126 */
@@ -139,12 +183,6 @@ module ResolveExpr<exprSig/1 shouldResolveExpr> {
139183 // Relative paths are resolved from their enclosing folder
140184 relativePathExpr ( expr , base , path )
141185 or
142- // Paths prefixed by __dirname should be resolved from the root dir, because __dirname
143- // currently has a getValue() that returns its absolute path.
144- prefixedByDirname ( expr ) and
145- not exists ( base .getParentContainer ( ) ) and // get root dir
146- path = expr .getValue ( )
147- or
148186 resolveViaPathMapping ( expr , base , path )
149187 or
150188 // Resolve from baseUrl of relevant tsconfig.json file
0 commit comments