@shriyanss/js-recon 1.4.1-alpha.3 → 1.4.1-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/.github/workflows/build-and-prettify.yaml +195 -9
  2. package/.github/workflows/publish-js-recon.yml +54 -10
  3. package/.github/workflows/rules-smoke-test.yaml +79 -0
  4. package/CHANGELOG.md +51 -0
  5. package/CLAUDE.md +232 -11
  6. package/README.md +35 -16
  7. package/build/analyze/engine/csMastSEngine.js +129 -0
  8. package/build/analyze/engine/csMastSEngine.js.map +1 -0
  9. package/build/analyze/engine/index.js +15 -0
  10. package/build/analyze/engine/index.js.map +1 -1
  11. package/build/analyze/helpers/engineHelpers/findDirectAssignment.js +2 -1
  12. package/build/analyze/helpers/engineHelpers/findDirectAssignment.js.map +1 -1
  13. package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js +2 -1
  14. package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js.map +1 -1
  15. package/build/analyze/helpers/engineHelpers/resolveFunctionIdentifier.js +2 -1
  16. package/build/analyze/helpers/engineHelpers/resolveFunctionIdentifier.js.map +1 -1
  17. package/build/analyze/helpers/engineHelpers/taintFlow.js +11 -2
  18. package/build/analyze/helpers/engineHelpers/taintFlow.js.map +1 -1
  19. package/build/analyze/helpers/schemas.js +5 -1
  20. package/build/analyze/helpers/schemas.js.map +1 -1
  21. package/build/analyze/helpers/validate.js +3 -3
  22. package/build/analyze/helpers/validate.js.map +1 -1
  23. package/build/completion/index.js +432 -0
  24. package/build/completion/index.js.map +1 -0
  25. package/build/cs_mast/index.js +94 -3
  26. package/build/cs_mast/index.js.map +1 -1
  27. package/build/endpoints/next_js/client_jsFilesHref.js +2 -1
  28. package/build/endpoints/next_js/client_jsFilesHref.js.map +1 -1
  29. package/build/endpoints/next_js/client_jsonParse.js +2 -1
  30. package/build/endpoints/next_js/client_jsonParse.js.map +1 -1
  31. package/build/endpoints/next_js/client_mappedJsonFile.js +2 -1
  32. package/build/endpoints/next_js/client_mappedJsonFile.js.map +1 -1
  33. package/build/endpoints/next_js/client_subsequentRequests.js +2 -1
  34. package/build/endpoints/next_js/client_subsequentRequests.js.map +1 -1
  35. package/build/fingerprint/index.js +56 -33
  36. package/build/fingerprint/index.js.map +1 -1
  37. package/build/globalConfig.js +1 -1
  38. package/build/index.js +76 -6
  39. package/build/index.js.map +1 -1
  40. package/build/lazyLoad/angular/angular_getFromMainJs.js +2 -1
  41. package/build/lazyLoad/angular/angular_getFromMainJs.js.map +1 -1
  42. package/build/lazyLoad/downloadFilesUtil.js +6 -5
  43. package/build/lazyLoad/downloadFilesUtil.js.map +1 -1
  44. package/build/lazyLoad/downloadLoadedJsUtil.js +56 -7
  45. package/build/lazyLoad/downloadLoadedJsUtil.js.map +1 -1
  46. package/build/lazyLoad/downloadQueue.js +9 -8
  47. package/build/lazyLoad/downloadQueue.js.map +1 -1
  48. package/build/lazyLoad/index.js +30 -47
  49. package/build/lazyLoad/index.js.map +1 -1
  50. package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js +36 -34
  51. package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js.map +1 -1
  52. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +63 -15
  53. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
  54. package/build/lazyLoad/next_js/next_parseLayoutJs.js +80 -68
  55. package/build/lazyLoad/next_js/next_parseLayoutJs.js.map +1 -1
  56. package/build/lazyLoad/next_js/next_promiseResolve.js +22 -16
  57. package/build/lazyLoad/next_js/next_promiseResolve.js.map +1 -1
  58. package/build/lazyLoad/nuxt_js/nuxt_astParse.js +49 -47
  59. package/build/lazyLoad/nuxt_js/nuxt_astParse.js.map +1 -1
  60. package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +3 -1
  61. package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js.map +1 -1
  62. package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +3 -2
  63. package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js.map +1 -1
  64. package/build/lazyLoad/react/react_followImports.js +12 -9
  65. package/build/lazyLoad/react/react_followImports.js.map +1 -1
  66. package/build/lazyLoad/react/react_webpackChunkPaths.js +175 -123
  67. package/build/lazyLoad/react/react_webpackChunkPaths.js.map +1 -1
  68. package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +3 -2
  69. package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -1
  70. package/build/lazyLoad/techDetect/checkAngularJS.js +27 -6
  71. package/build/lazyLoad/techDetect/checkAngularJS.js.map +1 -1
  72. package/build/lazyLoad/techDetect/checkReact.js +8 -0
  73. package/build/lazyLoad/techDetect/checkReact.js.map +1 -1
  74. package/build/lazyLoad/techDetect/index.js +23 -2
  75. package/build/lazyLoad/techDetect/index.js.map +1 -1
  76. package/build/lazyLoad/vue/vue_RuntimeJs.js +2 -1
  77. package/build/lazyLoad/vue/vue_RuntimeJs.js.map +1 -1
  78. package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js +2 -1
  79. package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js.map +1 -1
  80. package/build/lazyLoad/vue/vue_discoverJsFiles.js +22 -10
  81. package/build/lazyLoad/vue/vue_discoverJsFiles.js.map +1 -1
  82. package/build/lazyLoad/vue/vue_getClientSidePaths.js +2 -1
  83. package/build/lazyLoad/vue/vue_getClientSidePaths.js.map +1 -1
  84. package/build/lazyLoad/vue/vue_jsImports.js +2 -1
  85. package/build/lazyLoad/vue/vue_jsImports.js.map +1 -1
  86. package/build/lazyLoad/vue/vue_viteMapDeps.js +62 -54
  87. package/build/lazyLoad/vue/vue_viteMapDeps.js.map +1 -1
  88. package/build/map/angular_js/getAngularConnections.js +1 -1
  89. package/build/map/angular_js/getAngularConnections.js.map +1 -1
  90. package/build/map/graphql/resolveGraphql.js +2 -1
  91. package/build/map/graphql/resolveGraphql.js.map +1 -1
  92. package/build/map/next_js/getExports.js +2 -1
  93. package/build/map/next_js/getExports.js.map +1 -1
  94. package/build/map/next_js/getFetchInstances.js +2 -1
  95. package/build/map/next_js/getFetchInstances.js.map +1 -1
  96. package/build/map/next_js/getTurbopackConnections.js +2 -1
  97. package/build/map/next_js/getTurbopackConnections.js.map +1 -1
  98. package/build/map/next_js/getWebpackConnections.js +2 -1
  99. package/build/map/next_js/getWebpackConnections.js.map +1 -1
  100. package/build/map/next_js/resolveAxios.js +2 -1
  101. package/build/map/next_js/resolveAxios.js.map +1 -1
  102. package/build/map/next_js/resolveAxiosHelpers/directCallsWithoutAssignment.js +2 -1
  103. package/build/map/next_js/resolveAxiosHelpers/directCallsWithoutAssignment.js.map +1 -1
  104. package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js +2 -1
  105. package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js.map +1 -1
  106. package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js +2 -1
  107. package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js.map +1 -1
  108. package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js +2 -1
  109. package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js.map +1 -1
  110. package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js +2 -1
  111. package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js.map +1 -1
  112. package/build/map/next_js/resolveAxiosHelpers/interceptorHeaders.js +2 -1
  113. package/build/map/next_js/resolveAxiosHelpers/interceptorHeaders.js.map +1 -1
  114. package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js +2 -1
  115. package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js.map +1 -1
  116. package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js +2 -1
  117. package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js.map +1 -1
  118. package/build/map/next_js/resolveAxiosHelpers/traceBody.js +2 -1
  119. package/build/map/next_js/resolveAxiosHelpers/traceBody.js.map +1 -1
  120. package/build/map/next_js/resolveFetch.js +2 -1
  121. package/build/map/next_js/resolveFetch.js.map +1 -1
  122. package/build/map/next_js/resolveNewRequest.js +2 -1
  123. package/build/map/next_js/resolveNewRequest.js.map +1 -1
  124. package/build/map/next_js/resolveServerActions.js +2 -1
  125. package/build/map/next_js/resolveServerActions.js.map +1 -1
  126. package/build/map/next_js/utils.js +2 -1
  127. package/build/map/next_js/utils.js.map +1 -1
  128. package/build/map/react_js/getReactConnections.js +5 -4
  129. package/build/map/react_js/getReactConnections.js.map +1 -1
  130. package/build/map/vue_js/crossFileResolver.js +3 -2
  131. package/build/map/vue_js/crossFileResolver.js.map +1 -1
  132. package/build/map/vue_js/getViteConnections.js +5 -4
  133. package/build/map/vue_js/getViteConnections.js.map +1 -1
  134. package/build/map/vue_js/taint_utils.js +2 -1
  135. package/build/map/vue_js/taint_utils.js.map +1 -1
  136. package/build/map/vue_js/vue_resolveFetch.js +3 -2
  137. package/build/map/vue_js/vue_resolveFetch.js.map +1 -1
  138. package/build/map/vue_js/vue_resolveHttpClient.js +3 -2
  139. package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -1
  140. package/build/map/vue_js/vue_resolveXhr.js +3 -2
  141. package/build/map/vue_js/vue_resolveXhr.js.map +1 -1
  142. package/build/mcp/cli.js.map +1 -1
  143. package/build/refactor/index.js +399 -60
  144. package/build/refactor/index.js.map +1 -1
  145. package/build/refactor/next/helpers.js +194 -13
  146. package/build/refactor/next/helpers.js.map +1 -1
  147. package/build/refactor/next/index.js +223 -36
  148. package/build/refactor/next/index.js.map +1 -1
  149. package/build/refactor/next/transform.js +905 -55
  150. package/build/refactor/next/transform.js.map +1 -1
  151. package/build/refactor/react/index.js +3 -3
  152. package/build/refactor/react/index.js.map +1 -1
  153. package/build/refactor/react/library-classify.js.map +1 -1
  154. package/build/refactor/react/transform.js +2 -1
  155. package/build/refactor/react/transform.js.map +1 -1
  156. package/build/refactor/react/validator.js +2 -1
  157. package/build/refactor/react/validator.js.map +1 -1
  158. package/build/refactor/react-vite/index.js +39 -5
  159. package/build/refactor/react-vite/index.js.map +1 -1
  160. package/build/refactor/react-vite/vendor-analyze.js +3 -3
  161. package/build/refactor/react-vite/vendor-analyze.js.map +1 -1
  162. package/build/refactor/remote/config.js +17 -1
  163. package/build/refactor/remote/config.js.map +1 -1
  164. package/build/refactor/remote/hf-client.js +27 -2
  165. package/build/refactor/remote/hf-client.js.map +1 -1
  166. package/build/refactor/remote/version-detect.js +355 -0
  167. package/build/refactor/remote/version-detect.js.map +1 -0
  168. package/build/refactor/vue/index.js +176 -0
  169. package/build/refactor/vue/index.js.map +1 -0
  170. package/build/refactor/vue/vendor-analyze-vue.js +271 -0
  171. package/build/refactor/vue/vendor-analyze-vue.js.map +1 -0
  172. package/build/refactor/vue/vite.js +216 -0
  173. package/build/refactor/vue/vite.js.map +1 -0
  174. package/build/report/utility/genHtml.js +14 -3
  175. package/build/report/utility/genHtml.js.map +1 -1
  176. package/build/run/bundler-detect.js +140 -0
  177. package/build/run/bundler-detect.js.map +1 -0
  178. package/build/run/index.js +91 -5
  179. package/build/run/index.js.map +1 -1
  180. package/build/sourcemaps/index.js +89 -0
  181. package/build/sourcemaps/index.js.map +1 -0
  182. package/build/strings/index.js +6 -2
  183. package/build/strings/index.js.map +1 -1
  184. package/build/strings/trufflehog.js +71 -0
  185. package/build/strings/trufflehog.js.map +1 -0
  186. package/build/utility/makeReq.js +10 -9
  187. package/build/utility/makeReq.js.map +1 -1
  188. package/build/utility/postmanGenerator.js +2 -2
  189. package/build/utility/postmanGenerator.js.map +1 -1
  190. package/build/utility/puppeteerInstance.js +7 -1
  191. package/build/utility/puppeteerInstance.js.map +1 -1
  192. package/package.json +9 -5
  193. package/scripts/smoke-test.js +74 -0
@@ -12,7 +12,7 @@ jobs:
12
12
  !startsWith(github.event.head_commit.message, 'chore: prettify code')
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
- - uses: actions/checkout@v4
15
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
16
16
  - name: Check version mismatch
17
17
  env:
18
18
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -42,8 +42,8 @@ jobs:
42
42
  runs-on: ubuntu-latest
43
43
  needs: version_check
44
44
  steps:
45
- - uses: actions/checkout@v4
46
- - uses: actions/setup-node@v4
45
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
46
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
47
47
  with:
48
48
  node-version: "22"
49
49
  cache: "npm"
@@ -57,14 +57,14 @@ jobs:
57
57
  needs: audit
58
58
  steps:
59
59
  - name: Checkout
60
- uses: actions/checkout@v4
60
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
61
61
  with:
62
62
  # Make sure the action checks out the repository to the pull request branch
63
63
  ref: ${{ github.ref_name }}
64
64
  ssh-key: ${{ secrets.DEPLOY_KEY }}
65
65
 
66
66
  - name: Set up Node.js
67
- uses: actions/setup-node@v4
67
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
68
68
  with:
69
69
  node-version: "22"
70
70
  cache: "npm"
@@ -89,18 +89,39 @@ jobs:
89
89
  git commit -m "chore: prettify code" || echo "No changes to commit"
90
90
  git push
91
91
 
92
- build:
92
+ test:
93
93
  needs: prettier
94
94
  runs-on: ubuntu-latest
95
95
  steps:
96
96
  - name: Checkout
97
- uses: actions/checkout@v4
97
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
98
+ with:
99
+ ref: ${{ github.ref_name }}
100
+
101
+ - name: Set up Node.js
102
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
103
+ with:
104
+ node-version: "22"
105
+ cache: "npm"
106
+
107
+ - name: Install dependencies
108
+ run: npm ci
109
+
110
+ - name: Run unit tests
111
+ run: npm test
112
+
113
+ build:
114
+ needs: test
115
+ runs-on: ubuntu-latest
116
+ steps:
117
+ - name: Checkout
118
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
98
119
  with:
99
120
  # Make sure the action checks out the repository to the pull request branch
100
121
  ref: ${{ github.ref_name }}
101
122
 
102
123
  - name: Set up Node.js
103
- uses: actions/setup-node@v4
124
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
104
125
  with:
105
126
  node-version: "22"
106
127
  cache: "npm"
@@ -113,9 +134,174 @@ jobs:
113
134
  runs-on: ubuntu-latest
114
135
  steps:
115
136
  - name: Checkout
116
- uses: actions/checkout@v4
137
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
117
138
  with:
118
139
  # Make sure the action checks out the repository to the pull request branch
119
140
  ref: ${{ github.ref_name }}
120
141
  - name: Build docker container
121
142
  run: docker build -t shriyanss/js-recon .
143
+
144
+ integration-test-docker:
145
+ needs: build-docker
146
+ runs-on: ubuntu-latest
147
+ timeout-minutes: 30
148
+ steps:
149
+ - name: Checkout js-recon
150
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
151
+ with:
152
+ ref: ${{ github.ref_name }}
153
+
154
+ - name: Checkout js-recon-labs
155
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
156
+ with:
157
+ repository: shriyanss/js-recon-labs
158
+ path: js-recon-labs
159
+
160
+ - name: Checkout js-recon-rules
161
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
162
+ with:
163
+ repository: shriyanss/js-recon-rules
164
+ path: js-recon-rules
165
+
166
+ - name: Set up Node.js
167
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
168
+ with:
169
+ node-version: "22"
170
+ cache: "npm"
171
+
172
+ - name: Build js-recon Docker image
173
+ run: docker build -t shriyanss/js-recon .
174
+
175
+ - name: Install vuln-all-rules app dependencies
176
+ working-directory: js-recon-labs/next_js/vuln-all-rules
177
+ run: npm ci
178
+
179
+ - name: Build vuln-all-rules app
180
+ working-directory: js-recon-labs/next_js/vuln-all-rules
181
+ run: npm run build
182
+
183
+ - name: Start vuln-all-rules app
184
+ working-directory: js-recon-labs/next_js/vuln-all-rules
185
+ run: npm start &
186
+
187
+ - name: Wait for lab app to be ready
188
+ run: |
189
+ echo "Waiting for lab app on port 3001..."
190
+ n=0
191
+ until curl -sf http://localhost:3001 > /dev/null; do
192
+ n=$((n+1))
193
+ [ $n -ge 30 ] && echo "Timed out waiting for lab app" && exit 1
194
+ sleep 2
195
+ done
196
+ echo "Lab app is ready."
197
+
198
+ - name: Run js-recon via Docker against lab app
199
+ run: |
200
+ docker run --name js-recon-run \
201
+ --network host \
202
+ -v "$(pwd)/js-recon-rules:/home/pptruser/js-recon-rules" \
203
+ --entrypoint /bin/bash \
204
+ shriyanss/js-recon \
205
+ -c 'mkdir -p ~/.js-recon && ln -sf /home/pptruser/js-recon-rules ~/.js-recon/rules && printf "http://localhost:3001\n" > /tmp/urls.txt && node build/index.js run -u /tmp/urls.txt -r ./js-recon-rules --no-sandbox -y -k'
206
+ mkdir -p output
207
+ docker cp js-recon-run:/home/pptruser/output/. ./output/ 2>/dev/null || true
208
+ docker rm js-recon-run
209
+
210
+ - name: Verify all rules fired
211
+ run: node scripts/smoke-test.js
212
+
213
+ - name: Upload analyze.json on failure
214
+ if: failure()
215
+ uses: actions/upload-artifact@v4
216
+ with:
217
+ name: analyze-json-docker
218
+ path: output/localhost_3001/analyze.json
219
+ if-no-files-found: ignore
220
+
221
+ integration-test-macos:
222
+ needs: build
223
+ runs-on: macos-latest
224
+ timeout-minutes: 30
225
+ steps:
226
+ - name: Checkout js-recon
227
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
228
+ with:
229
+ ref: ${{ github.ref_name }}
230
+
231
+ - name: Checkout js-recon-labs
232
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
233
+ with:
234
+ repository: shriyanss/js-recon-labs
235
+ path: js-recon-labs
236
+
237
+ - name: Checkout js-recon-rules
238
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
239
+ with:
240
+ repository: shriyanss/js-recon-rules
241
+ path: js-recon-rules
242
+
243
+ - name: Set up Node.js
244
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
245
+ with:
246
+ node-version: "22"
247
+ cache: "npm"
248
+
249
+ - name: Install Chrome
250
+ run: brew install --cask google-chrome
251
+
252
+ - name: Install js-recon dependencies
253
+ run: npm ci
254
+ env:
255
+ PUPPETEER_SKIP_DOWNLOAD: "true"
256
+
257
+ - name: Build js-recon
258
+ run: npm run build
259
+
260
+ - name: Install vuln-all-rules app dependencies
261
+ working-directory: js-recon-labs/next_js/vuln-all-rules
262
+ run: npm ci
263
+
264
+ - name: Build vuln-all-rules app
265
+ working-directory: js-recon-labs/next_js/vuln-all-rules
266
+ run: npm run build
267
+
268
+ - name: Start vuln-all-rules app
269
+ working-directory: js-recon-labs/next_js/vuln-all-rules
270
+ run: npm start &
271
+
272
+ - name: Wait for lab app to be ready
273
+ run: |
274
+ echo "Waiting for lab app on port 3001..."
275
+ n=0
276
+ until curl -sf http://localhost:3001 > /dev/null; do
277
+ n=$((n+1))
278
+ [ $n -ge 30 ] && echo "Timed out waiting for lab app" && exit 1
279
+ sleep 2
280
+ done
281
+ echo "Lab app is ready."
282
+
283
+ - name: Pre-populate js-recon rules cache
284
+ run: |
285
+ mkdir -p ~/.js-recon
286
+ ln -sf "$GITHUB_WORKSPACE/js-recon-rules" ~/.js-recon/rules
287
+
288
+ - name: Run js-recon natively against lab app
289
+ run: |
290
+ echo "http://localhost:3001" > /tmp/smoke-urls.txt
291
+ node --max-old-space-size=4096 build/index.js run \
292
+ -u /tmp/smoke-urls.txt \
293
+ -r ./js-recon-rules \
294
+ -y -k
295
+ env:
296
+ PUPPETEER_EXECUTABLE_PATH: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
297
+
298
+ - name: Verify all rules fired
299
+ run: node scripts/smoke-test.js
300
+
301
+ - name: Upload analyze.json on failure
302
+ if: failure()
303
+ uses: actions/upload-artifact@v4
304
+ with:
305
+ name: analyze-json-macos
306
+ path: output/localhost_3001/analyze.json
307
+ if-no-files-found: ignore
@@ -11,7 +11,7 @@ jobs:
11
11
  version_check:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
- - uses: actions/checkout@v4
14
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
15
15
  - name: Check version mismatch
16
16
  run: |
17
17
  echo "[i] Checking if the version in the CHANGELOG.md, package.json, and Github release version matches"
@@ -39,8 +39,8 @@ jobs:
39
39
  needs: version_check
40
40
  runs-on: ubuntu-latest
41
41
  steps:
42
- - uses: actions/checkout@v4
43
- - uses: actions/setup-node@v4
42
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
43
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
44
44
  with:
45
45
  node-version: 22
46
46
  - run: npm ci
@@ -51,8 +51,8 @@ jobs:
51
51
  needs: audit
52
52
  runs-on: ubuntu-latest
53
53
  steps:
54
- - uses: actions/checkout@v4
55
- - uses: actions/setup-node@v4
54
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
55
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
56
56
  with:
57
57
  node-version: 22
58
58
  - run: npm ci
@@ -64,8 +64,8 @@ jobs:
64
64
  needs: build
65
65
  runs-on: ubuntu-latest
66
66
  steps:
67
- - uses: actions/checkout@v4
68
- - uses: actions/setup-node@v4
67
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
68
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
69
69
  with:
70
70
  node-version: 22
71
71
  registry-url: https://registry.npmjs.org/
@@ -82,11 +82,55 @@ jobs:
82
82
  VERSION: ${{ github.event.release.tag_name }}
83
83
 
84
84
 
85
+ update-homebrew-tap:
86
+ needs: publish-npm
87
+ runs-on: ubuntu-latest
88
+ steps:
89
+ - name: Get release version
90
+ id: version
91
+ run: |
92
+ RAW="${{ github.event.release.tag_name }}"
93
+ echo "version=${RAW#v}" >> "$GITHUB_OUTPUT"
94
+
95
+ - name: Compute npm tarball SHA256
96
+ id: sha256
97
+ run: |
98
+ VERSION="${{ steps.version.outputs.version }}"
99
+ URL="https://registry.npmjs.org/@shriyanss/js-recon/-/js-recon-${VERSION}.tgz"
100
+ SHA=$(curl -fsSL "${URL}" | sha256sum | awk '{print $1}')
101
+ echo "sha256=${SHA}" >> "$GITHUB_OUTPUT"
102
+
103
+ - name: Check out homebrew-tap
104
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
105
+ with:
106
+ repository: shriyanss/homebrew-tap
107
+ token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
108
+ path: homebrew-tap
109
+
110
+ - name: Update formula fields
111
+ working-directory: homebrew-tap
112
+ run: |
113
+ VERSION="${{ steps.version.outputs.version }}"
114
+ SHA="${{ steps.sha256.outputs.sha256 }}"
115
+ F="Formula/js-recon.rb"
116
+ sed -i "s|^ url \"https://registry\.npmjs\.org/@shriyanss/js-recon/-/js-recon-[^\"]*\"| url \"https://registry.npmjs.org/@shriyanss/js-recon/-/js-recon-${VERSION}.tgz\"|" "${F}"
117
+ sed -i "s|^ sha256 \"[a-f0-9]*\"| sha256 \"${SHA}\"|" "${F}"
118
+
119
+ - name: Commit and push
120
+ working-directory: homebrew-tap
121
+ run: |
122
+ git config user.name "github-actions[bot]"
123
+ git config user.email "github-actions[bot]@users.noreply.github.com"
124
+ git add Formula/js-recon.rb
125
+ git commit -m "chore: update js-recon formula to ${{ steps.version.outputs.version }}"
126
+ git push
127
+
128
+
85
129
  publish-docker:
86
130
  needs: build
87
131
  runs-on: ubuntu-latest
88
132
  steps:
89
- - uses: actions/checkout@v4
133
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
90
134
  - name: Build and push to dockerhub
91
135
  run: |
92
136
  # login to docker
@@ -119,7 +163,7 @@ jobs:
119
163
  contents: read
120
164
  packages: write
121
165
  steps:
122
- - uses: actions/checkout@v4
166
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
123
167
  - uses: docker/login-action@v3
124
168
  with:
125
169
  registry: ghcr.io
@@ -152,7 +196,7 @@ jobs:
152
196
  - publish-ghcr
153
197
  runs-on: ubuntu-latest
154
198
  steps:
155
- - uses: actions/checkout@v4
199
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
156
200
  with:
157
201
  ssh-key: ${{ secrets.DEPLOY_KEY }}
158
202
  fetch-depth: 0
@@ -0,0 +1,79 @@
1
+ name: Rules Smoke Test
2
+
3
+ on:
4
+ push:
5
+ branches-ignore:
6
+ - main
7
+
8
+ jobs:
9
+ rules-smoke-test:
10
+ runs-on: ubuntu-latest
11
+ timeout-minutes: 30
12
+
13
+ steps:
14
+ - name: Checkout js-recon
15
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
16
+
17
+ - name: Checkout js-recon-labs
18
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
19
+ with:
20
+ repository: shriyanss/js-recon-labs
21
+ path: js-recon-labs
22
+
23
+ - name: Checkout js-recon-rules
24
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
25
+ with:
26
+ repository: shriyanss/js-recon-rules
27
+ path: js-recon-rules
28
+
29
+ - name: Set up Node.js
30
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
31
+ with:
32
+ node-version: "22"
33
+ cache: "npm"
34
+
35
+ - name: Install js-recon dependencies
36
+ run: npm ci
37
+
38
+ - name: Build js-recon
39
+ run: npm run build
40
+
41
+ - name: Install vulnerable app dependencies
42
+ working-directory: js-recon-labs/next_js/vuln-all-rules
43
+ run: npm ci
44
+
45
+ - name: Build vulnerable app
46
+ working-directory: js-recon-labs/next_js/vuln-all-rules
47
+ run: npm run build
48
+
49
+ - name: Start vulnerable app
50
+ working-directory: js-recon-labs/next_js/vuln-all-rules
51
+ run: npm start &
52
+
53
+ - name: Wait for app to be ready
54
+ run: |
55
+ echo "Waiting for app on port 3001..."
56
+ timeout 60 bash -c 'until curl -sf http://localhost:3001 > /dev/null; do sleep 2; done'
57
+ echo "App is ready."
58
+
59
+ - name: Run js-recon against vulnerable app
60
+ run: |
61
+ echo "http://localhost:3001" > /tmp/smoke-urls.txt
62
+ node --max-old-space-size=4096 build/index.js run \
63
+ -u /tmp/smoke-urls.txt \
64
+ -r ./js-recon-rules \
65
+ --no-sandbox \
66
+ -y -k
67
+ env:
68
+ PUPPETEER_EXECUTABLE_PATH: /usr/bin/google-chrome-stable
69
+
70
+ - name: Verify all rules fired
71
+ run: node scripts/smoke-test.js
72
+
73
+ - name: Upload analyze.json on failure
74
+ if: failure()
75
+ uses: actions/upload-artifact@v4
76
+ with:
77
+ name: analyze-json
78
+ path: output/localhost_3001/analyze.json
79
+ if-no-files-found: ignore
package/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.4.1-alpha.5 - 2026-07-13
4
+
5
+ ### Fixed
6
+
7
+ - Homebrew install: `puppeteer-extra` now receives the ESM `puppeteer` instance directly via `addExtra()` instead of relying on its internal `require('puppeteer')` call. On newer Node.js versions (22+, as shipped by Homebrew), a synchronous `require()` of an ESM module triggers `ExperimentalWarning` on every run. Using `addExtra()` bypasses the `require()` entirely and silences the warning. (`all`)
8
+ - `lazyload` (tech detect, react): React framework detection now works for Vite dev-mode servers. Vite's `@vitejs/plugin-react` and `@vitejs/plugin-react-swc` inject an inline `<script>` block containing `import { injectIntoGlobalHook } from "/@react-refresh"` into every dev-served page. Previously, none of the `REACT_MARKERS` matched this content, so dev-mode targets returned `unknown`. Two fixes: (1) `@react-refresh` and `injectIntoGlobalHook` are now included in `REACT_MARKERS` so the inline-script branch of `checkReact` fires immediately; (2) `/@react-refresh` is now a fast-path match in `fetchAndCheck` so any script src pointing at the HMR endpoint is also caught; (3) the intercepted-URL fallback in `frameworkDetect` now recognises `/@react-refresh` as a React signal alongside the existing Next.js / Nuxt / Svelte URL patterns. (`lazyload`, `fingerprint`, `run`)
9
+ - `lazyload` (tech detect, angular): Angular framework detection is now more robust. Three HTML-level signals are checked before fetching `main.js` — `data-beasties-container` (Angular's Beasties SSR/prerendering marker), `ng-version` (set by the Angular runtime on the root component element after bootstrapping, visible in Puppeteer-rendered DOM), and `_nghost-*` view-encapsulation attributes. These checks require no extra HTTP request and work even for zoneless Angular apps (Angular 16+) that omit Zone.js and therefore lack the previous `isAngularZone`/`this.ngZone` patterns. The `main.js` src pattern was also broadened from `main-` (hashed production builds only) to also match `main.js` (unhashed development builds). (`lazyload`, `fingerprint`, `run`)
10
+ - `lazyload`, `map`, `strings`: `.mjs` (ES module) files are now fully supported across all framework pipelines. Previously, extension filters in `downloadQueue.ts`, `downloadFilesUtil.ts`, `downloadLoadedJsUtil.ts`, `react_followImports.ts`, `getReactConnections.ts`, `getViteConnections.ts`, `getAngularConnections.ts`, `vue_resolveHttpClient.ts`, `vue_resolveFetch.ts`, `vue_resolveXhr.ts`, `crossFileResolver.ts`, and `strings/index.ts` only matched `.js` files. Sites that bundle all application code as `.mjs` (e.g. Framer/Rolldown) were silently ignored — all chunks were downloaded but zero files were scanned for connections, strings, or secrets. All affected paths now match both `.js` and `.mjs`. (`run`, `lazyload`, `map`, `strings`)
11
+ - `run` (react): The map step now receives the full `outputDir` (all downloaded hosts) rather than only the target host's subdirectory. Previously, when a React site served all its JS from a CDN host, the map step was pointed at an empty target-host directory and reported zero chunks. Now mirrors the Vue pipeline's directory-passing behaviour so CDN-hosted chunks are analysed correctly. (`run`, `map`)
12
+ - `lazyload` (react): `react_followImports` now discovers dynamic imports written as template literals (``import(`./chunk.mjs`)``) in addition to single/double-quoted strings. Bundlers that use Rolldown (e.g. Framer) emit all dynamic import paths as template literals; the previous regex only matched quoted strings, silently missing all lazily-loaded page and collection chunks. (`lazyload`, `run`)
13
+ - `lazyload`: `.mjs.map` sourcemap files are now handled correctly. The filename extraction regex now prefers the `.mjs.map` suffix over the shorter `.mjs` match, preventing sourcemap JSON content from being written with a `.mjs` extension (which caused Prettier's Babel parser to throw a `SyntaxError` and discard the file). The sourcemap write path now uses the JSON parser for both `.js.map` and `.mjs.map` files. (`lazyload`, `run`)
14
+ - `lazyload` (Puppeteer): `downloadLoadedJsUtil` now uses `waitUntil: "networkidle0"` with a 10 s timeout on `page.goto()`. Previously the call had no timeout and no error handling, so pages whose `load` event never fires (deferred JS, service workers, some SPA patterns) caused the lazyload step to hang indefinitely. Navigation errors and timeouts are now caught and the URLs collected up to that point are returned normally. (`lazyload`, `run`)
15
+ - `lazyload` (Puppeteer): `browser.close()` in `downloadLoadedJsUtil` is now wrapped in a try/catch; if it hangs, the Chrome process is force-killed via `SIGKILL`. Previously a stuck `browser.close()` caused the process to hang after download was otherwise complete. (`lazyload`, `run`)
16
+ - `lazyload` (Puppeteer): All three Puppeteer-using modules (`downloadLoadedJsUtil`, `next_GetLazyResourcesWebpackJs`, `techDetect/index`) now abort non-http/s requests (e.g. `mailto:`, `data:`, `blob:`, `chrome-extension:`, `tel:`) in the request interceptor instead of calling `request.continue()`. Calling `continue()` on these schemes throws an unhandled error and can trigger OS protocol handlers. (`lazyload`, `run`)
17
+ - `lazyload` (Puppeteer): `downloadLoadedJsUtil` and `next_GetLazyResourcesWebpackJs` now pass `--disable-external-protocol-dialog` to Chrome at launch and install an `evaluateOnNewDocument` guard that overrides `window.open` and suppresses clicks on non-http/s anchors. This provides a three-layer defence (Chrome flag + JS intercept + request interceptor abort) ensuring that non-http protocol links on target pages never invoke OS handlers (mail client, phone, etc.). (`lazyload`, `run`)
18
+ - `lazyload` (Puppeteer): All three Puppeteer-using modules now issue a `Page.setDownloadBehavior({ behavior: "deny" })` CDP command immediately after page creation. This prevents accidental file downloads triggered by download-link clicks or JavaScript during crawl, which could block the browser and fill the output directory with unexpected files. (`lazyload`, `run`)
19
+ - `lazyload` (tech detect): After `waitUntil: "load"` resolves, if no framework URL has been captured in `interceptedUrls` yet, a conditional `page.waitForNavigation({ timeout: 5000 })` is now issued to wait for a second navigation event. This catches sites that serve a JS proof-of-work bot challenge (e.g. Vercel's `challenge.v2.min.js`) that fires its own `load` event before calling `window.location` to redirect to the real app. Previously, Puppeteer exited on the challenge page's `load` event and tech detection returned null (exit 10). On sites with no redirect the navigation wait times out after 5 s and detection continues normally. (`lazyload`, `run`)
20
+ - `lazyload`: When `frameworkDetect` returns null and `downloadLoadedJs` is used as a fallback, the downloaded file URL paths are now scanned for `/_next/`, `/_nuxt/`, and `/_app/immutable/` signatures as a second-chance tech detection pass. Previously, sites that serve their framework app at a non-root `basePath` (e.g. Next.js at `/app` instead of `/`) caused the initial Puppeteer-based detection to miss the framework because the intercepted JS paths didn't match any framework pattern, resulting in exit 10. (`lazyload`, `run`)
21
+ - `lazyload` (next_js): `next_parseLayoutJs` now applies a 1.5 MB per-file size guard — checked from both the `Content-Length` response header and the fetched content body — before passing layout files to the AST parser. Files exceeding the limit are skipped with a yellow warning. Previously, Next.js App Router sites with 50+ nested pages each loading a large `layout.js` caused cumulative AST memory of ~2 GB, exhausting the V8 heap and crashing with SIGSEGV (exit 139). (`lazyload`, `run`)
22
+ - `cs-mast`: `--scat`, `--sinc`, `--all-scat-permutations`, `--perm-output`, and `--perm-concurrency` flags were declared in the CLI but not wired to the underlying function, causing a TypeScript compile error (`TS2554: Expected 5 arguments, but got 10`). The function now accepts all five additional parameters: `--scat`/`--sinc` override the active CS-MAST config, and `--all-scat-permutations` runs all 511 non-empty scat subsets in parallel batches and writes one collision file per subset to `--perm-output`. (`cs-mast`)
23
+
24
+ - `refactor -t react-vite`: vendor chunks (`vendor-react-*.js`, `rolldown-runtime-*.js`) that are absent from `mapped.json` are now automatically located in the downloaded assets directory and injected before the refactor pass. Previously, `mapped.json` typically contained only app chunks — vendor chunks are excluded from mapping because they hold only third-party code. This left `vendorExportMaps` empty, so the `rewriteVendorImports` pass had nothing to match against and vendor import statements like `import { d as t } from './vendor-react-CLFLfR9F.js'` survived into the output, causing the Vite build check to fail with "Could not resolve". The fix uses the existing `findAssetsDir`/`findVendorChunkFiles` helpers (already used by the webpack branch) to discover the assets directory from `// File Source:` headers in chunk code.
25
+ - `lazyload` (nuxt): `nuxt_getFromPageSource` now prints the number of new JS files discovered (delta from the global URL set) instead of the running total. Previously the count included every URL already known from earlier in the session.
26
+ - `lazyload` (vue): Each discovery method in `vue_discoverJsFiles` now prints only the count of URL that are genuinely new — URLs already known from earlier methods are excluded. Previously `fromImports.length`, `fromStringRefs.length`, etc. could include URLs already in the accumulator and over-count.
27
+ - Error messages emitted by `makeReq` (fetch failures, cache errors, timeout notices, firewall detection) now use `progressError`/`progressLog` instead of `console.error`/`console.log`, so they are routed through the active progress bar's logger and no longer clutter the bar line when errors occur during a scan.
28
+ - `refactor` (`react-webpack`, `react-vite`): new `--detect-version` flag. When set, the tool fetches per-version `reliable_signatures.json` files from the `shriyanss/cs-mast-s-dataset` HuggingFace bucket (path: `version/react/<bundler>/<version>/<scat>/reliable_signatures.json`) and matches them against CS-MAST signatures generated from the target bundle's chunks. The best-matching React version is used to set `react` and `react-dom` version pins in the refactored output's `package.json` instead of the default `^18.3.1`. Signatures are cached under `~/.js-recon/refactor/version_sigs_cache/` with a 7-day TTL. Supports all webpack versions (react-0.12 through react-19) and all Vite versions (react-16 through react-19) available in the dataset. (`refactor`)
29
+
30
+ ### Added
31
+
32
+ - `fingerprint`: output files are now written incrementally — each result is flushed to disk immediately after detection rather than buffered and written at the end. For text, csv, and jsonl formats the result is appended; for json the full array is rewritten. Output files are also created (and csv headers written) before the worker pool starts, so a partial file always exists if the scan is interrupted. (`fingerprint`)
33
+ - CLI: help output is now colorized. Section titles (`Usage:`, `Options:`, `Commands:`) are bold cyan; flag names are yellow; `<required>` argument placeholders are magenta; `[optional]` argument placeholders are cyan; descriptions are dimmed; and `(default: …)` suffixes are dim italic. Applies to every subcommand.
34
+ - `run`: the pipeline now runs an optional `refactor` pass after the report step for React, Vue, Nuxt, and Next.js targets. The bundler (webpack vs vite) is detected automatically using CS-MAST-S signatures sampled from the `shriyanss/cs-mast-s-dataset` HuggingFace bucket — the same bucket and caching layer used by `refactor --remote-collisions`. If the detected match count meets `--cs-mast-tech-detect-threshold` (default `50`), `refactor` is called with the full tech identifier (e.g. `react-webpack`); otherwise the step is silently skipped. Refactored output is written to `refactored/` (single-URL mode) or `<workingDir>/refactored/` (batch mode). Currently only React bundles have bucket entries; Vue, Nuxt, and Next.js gracefully skip until their signatures are added to the bucket. (`run`, `refactor`)
35
+ - `run`: new `--cs-mast-tech-detect-threshold <n>` flag (default `50`). Sets the minimum number of CS-MAST-S signature matches required to consider a bundler detected for the refactor step. Pass `0` to disable refactor entirely. (`run`)
36
+ - `refactor` (`react-vite`, `react-webpack`): new `--remote-collisions <path>` flag. Accepts a HuggingFace bucket path (e.g. `react/vite/large-0.1.8`) and uses it as the signature source instead of the automatic `TECH_TO_BRANCH` mapping. When the path does not exist in the `shriyanss/cs-mast-s-dataset` bucket the tool exits with code 25. The existing caching layer is fully reused — signatures are cached under `~/.js-recon/refactor/signature_cache/` and the file list is cached in `~/.js-recon/refactor/cs-mast-s-list-cache.json`. Feature directories that contain no collision records in the dataset are skipped during intersection rather than collapsing the result to zero.
37
+ - `refactor` (`react-webpack`, `react-vite`): `--detect-version` now uses multiple scat configurations for improved accuracy. Three new flags control the behaviour: `--detect-version-config` (default `dynamic`), `--detect-version-dynamic-threshold` (default `3`), and `--detect-version-dynamic-conf-purge`. In `dynamic` mode the tool automatically selects up to `--detect-version-dynamic-threshold` scat configs that have non-empty reliable signatures across all known React versions, caches the selection in `~/.js-recon/refactor/config.json`, and aggregates match counts across all configs per version for a stronger detection signal. Alternatively, passing comma-separated scat categories (e.g. `lit,decl,loop,cond`) to `--detect-version-config` fixes the config; the tool validates signatures are non-empty for every version and exits with code 26 if not. Use `--detect-version-dynamic-conf-purge` to force recomputation of the cached config.
38
+
39
+ ### Changed
40
+
41
+ - Set `react/webpack/large-0.1.8` as default cs-mast-s dataset for react + webpack refactoring
42
+
43
+ ## 1.4.1-alpha.4 - 2026-06-29
44
+
45
+ ### Added
46
+
47
+ - `sourcemaps`: new subcommand to extract original source files from `.map` sourcemaps without running the full pipeline. Accepts a single `.map` file or a directory of `.map` files via `-i`/`--input`; writes recovered sources to `-o`/`--output` (default: `extracted`).
48
+ - Homebrew tap distribution: `brew tap shriyanss/tap && brew install js-recon` installs js-recon via the `shriyanss/homebrew-tap` Homebrew tap. The formula (`Formula/js-recon.rb`) auto-updates on every npm publish via the `update-homebrew-tap` CI job in `publish-js-recon.yml`.
49
+
50
+ ### Changed
51
+
52
+ - `lazyload`: sourcemap extraction logic moved to the new `sourcemaps` module (`src/sourcemaps/`). Behaviour is identical; `lazyload` now delegates to `extractSourceMaps` from that module rather than containing a private copy.
53
+
3
54
  ## 1.4.1-alpha.3 - 2026-06-25
4
55
 
5
56
  ### Added