@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
package/CLAUDE.md CHANGED
@@ -35,6 +35,7 @@ npm run start -- <subcommand> [options]
35
35
  | `api-gateway` | Manage AWS API Gateway for IP rotation |
36
36
  | `mcp` | AI-powered CLI / one-shot chat (`-c`) / Model Context Protocol server (`--server`) |
37
37
  | `cs-mast` | Compute CS-MAST structural hashes for downloaded JS files; find hash collisions |
38
+ | `sourcemaps` | Extract source files from `.map` sourcemap file(s) |
38
39
 
39
40
  ## Key source files
40
41
 
@@ -76,9 +77,9 @@ npm run start -- <subcommand> [options]
76
77
  1. **Lazyload** — downloads initial JS chunks via Puppeteer; detects framework; sets `globalsUtil.getTech()` to `"next"`
77
78
  2. **Strings** — scans downloaded JS for strings, extracts URL paths → `extracted_urls.json`
78
79
  3. **Lazyload (subsequent requests)** — re-crawls using the extracted paths to fetch dynamically loaded chunks; also fetches `buildId`
79
- 4. **Strings (pass 2)** — re-runs strings on the expanded chunk set; generates `extracted_urls.txt` (permuted) and `extracted_urls-openapi.json`; optionally scans secrets (`--secrets`)
80
+ 4. **Strings (pass 2)** — re-runs strings on the expanded chunk set; generates `extracted_urls.txt` (permuted) and `extracted_urls-openapi.json`; optionally scans secrets (`--secrets`); optionally runs TruffleHog (`--trufflehog`)
80
81
  5. **Lazyload re-pass (step 4.5)** — a second subsequent-requests crawl to pick up chunks for dynamic routes discovered in pass 2
81
- 6. **Strings re-pass (step 4.6)** — strings pass over the re-pass chunks
82
+ 6. **Strings re-pass (step 4.6)** — strings pass over the re-pass chunks; also runs `--secrets` / `--trufflehog` if those flags are set
82
83
  7. **Map** — parses webpack/turbopack bundles; resolves `fetch()` calls and axios usage; generates `mapped.json` and `mapped-openapi.json`; CDN-aware: if JS was served from a different host, `getCdnDir` finds the CDN output dir and passes that to map instead of `outputDir/host`
83
84
  8. **Endpoints** — extracts client-side route paths; uses `___subsequent_requests` directory presence to decide whether to pass a JS directory
84
85
  9. **Analyze** — loads YAML rules (from `-r/--rules` if provided, otherwise default rules cache); runs AST engine and request engine; writes `analyze.json`
@@ -106,7 +107,7 @@ npm run start -- <subcommand> [options]
106
107
 
107
108
  **SvelteKit `adapter-static` (SSG/SPA) boot pattern**: The static builds produce a shell HTML file (`404.html` for SSG, `index.html` for SPA) that contains both `<link rel="modulepreload">` tags for all initial chunks AND the same inline `import()` boot script as adapter-node. `svelte_getFromPageSource` picks up 17+ JS URLs from the modulepreload links plus 2 from the inline script, giving a much larger seed set than the adapter-node case.
108
109
 
109
- **`__vite_mapDeps` path formats**: SvelteKit emits `m.f = ["../nodes/0.js", "../chunks/x.js", ...]` (file-relative paths) inside entry chunks at `_app/immutable/entry/`. Vue and React emit `m.f = ["/assets/chunk.js", ...]` (root-relative paths). `react_followImports` differentiates by checking `p.startsWith("/")` absolute paths resolve against the origin (`baseUrl`), relative paths resolve against the chunk's own URL (`fileUrl`). See `src/lazyload/react/CLAUDE.md` for details.
110
+ **`__vite_mapDeps` path formats**: SvelteKit emits `m.f = ["../nodes/0.js", "../chunks/x.js", ...]` (explicit file-relative paths) inside entry chunks at `_app/immutable/entry/`. Vue and React can emit either `m.f = ["/assets/chunk.js", ...]` (absolute root-relative) or `m.f = ["assets/chunk.js", ...]` (bare root-relative, no leading `/`). `react_followImports` differentiates by checking for a `./` or `../` prefix: only explicitly relative paths resolve against the chunk's own URL (`fileUrl`); all others (absolute `/` or bare names) resolve against the origin (`baseUrl`). Bare names like `assets/x.js` must NOT be resolved against `fileUrl` — when the chunk is inside `assets/`, that would produce a double-directory path. See `src/lazyload/react/CLAUDE.md` for details.
110
111
 
111
112
  ### Batch mode
112
113
 
@@ -199,6 +200,7 @@ Rule categories:
199
200
 
200
201
  - `ast/` — AST-based pattern matching against chunk code (uses `@babel/parser` + `esquery`)
201
202
  - `request/` — OpenAPI/request-level checks against the resolved endpoint list
203
+ - `cs-mast-s/` — CS-MAST-S structural signature matching; each step embeds a PHC string and fires if that node-level hash is found anywhere in the chunk AST. Suitable for regression detection after a vulnerability is confirmed via AST rules. See `src/analyze/engine/csMastSEngine.ts` and `src/analyze/CLAUDE.md` for details.
202
204
 
203
205
  When `-r` points to a single file, only that rule is loaded. When it points to a directory, all `.yml`/`.yaml` files are loaded recursively.
204
206
 
@@ -206,11 +208,137 @@ When `-r` points to a single file, only that rule is loaded. When it points to a
206
208
 
207
209
  **Testing is mandatory for every change.** Before reporting a task complete:
208
210
 
209
- 1. Run `npm run cleanup` to rebuild TypeScript.
210
- 2. Run the `run` subcommand against the target the user provides. Do not use `analyze` or other individual subcommands as a substitute — the `run` subcommand must be used to validate end-to-end behavior.
211
- 3. If the user has not provided a target, ask for one before proceeding.
211
+ 1. Run `npm test` to execute the unit test suite (Vitest).
212
+ 2. Run `npm run cleanup` to rebuild TypeScript.
213
+ 3. Run the `run` subcommand against the target the user provides. Do not use `analyze` or other individual subcommands as a substitute the `run` subcommand must be used to validate end-to-end behavior.
214
+ 4. If the user has not provided a target, ask for one before proceeding.
212
215
 
213
- Typical test invocation:
216
+ ### Rules smoke test (CI)
217
+
218
+ The `rules-smoke-test` GitHub Actions workflow (`.github/workflows/rules-smoke-test.yaml`) runs on every non-main push. It:
219
+
220
+ 1. Checks out `shriyanss/js-recon-labs` and `shriyanss/js-recon-rules` alongside js-recon.
221
+ 2. Builds js-recon and the `next_js/vuln-all-rules` lab app.
222
+ 3. Starts the lab app on port 3001.
223
+ 4. Runs `node build/index.js run -u http://localhost:3001 -r ./js-recon-rules --no-sandbox -y -k`.
224
+ 5. Runs `node scripts/smoke-test.js` which reads `output/localhost:3001/analyze.json` and asserts that all 22 expected rule IDs are present.
225
+
226
+ **`scripts/smoke-test.js`** maintains the `EXPECTED_RULES` list. When a new rule is added to js-recon-rules:
227
+
228
+ - The `next_js/vuln-all-rules` app in js-recon-labs must be updated to seed the new vulnerability.
229
+ - The new rule ID must be appended to `EXPECTED_RULES` in `scripts/smoke-test.js`.
230
+
231
+ The lab app seeds:
232
+
233
+ - 19 AST rules for the `next` tech stack
234
+ - 3 request rules (`api_path`, `admin_api`, `missing_authorization_header`)
235
+ - The Angular-only rule (`detect_angular_bypass_security_trust`) is intentionally excluded.
236
+
237
+ ### Unit tests
238
+
239
+ Unit tests live in `src/__tests__/` and cover pure-logic components. Test framework is **Vitest** (ESM-native, TypeScript-native — no compilation step needed).
240
+
241
+ ```bash
242
+ npm test # run all unit tests once
243
+ npm run test:watch # watch mode
244
+ npm run test:build # legacy build smoke test (node build/index.js -h)
245
+ ```
246
+
247
+ Test files follow the pattern `src/__tests__/<component>/<name>.test.ts`.
248
+
249
+ Covered components:
250
+
251
+ | File | Tests in |
252
+ | ---------------------------------------------------------------------------------------- | ------------------------------------------------------ |
253
+ | `utility/urlUtils.ts` — `getURLDirectory` | `src/__tests__/utility/urlUtils.test.ts` |
254
+ | `utility/replaceUrlPlaceholders.ts` — `replacePlaceholders` | `src/__tests__/utility/replaceUrlPlaceholders.test.ts` |
255
+ | `utility/resolvePath.ts` — `resolvePath` | `src/__tests__/utility/resolvePath.test.ts` |
256
+ | `strings/index.ts` — `extractStrings` | `src/__tests__/strings/extractStrings.test.ts` |
257
+ | `analyze/helpers/validate.ts` — `parseVersion`, `compareVersions`, `isVersionCompatible` | `src/__tests__/analyze/versionCompat.test.ts` |
258
+ | `map/next_js/utils.ts` — `memberChainToString` | `src/__tests__/map/memberChainToString.test.ts` |
259
+ | `fingerprint/index.ts` — `deriveOutputPath` | `src/__tests__/fingerprint/deriveOutputPath.test.ts` |
260
+
261
+ When adding new pure-logic helpers, add a corresponding test file. Components that require Puppeteer, network I/O, or the full pipeline are still validated through the `run` subcommand.
262
+
263
+ ### Writing unit tests
264
+
265
+ **What to test.** Test pure functions: anything that takes plain inputs and returns a value without I/O. The standard pattern for I/O-bound modules is to extract the parse/transform step into an exported pure function and test that. Leave the orchestrator (Puppeteer, `makeRequest`, file writes) untested at the unit level.
266
+
267
+ **Extracting testable functions.** When a function mixes I/O with logic, split it:
268
+
269
+ ```typescript
270
+ // Exported pure function — testable
271
+ export const parseThings = (content: string, baseUrl: string): string[] => { ... };
272
+
273
+ // Orchestrator — not unit-tested
274
+ const myModule = async (url: string): Promise<string[]> => {
275
+ const resp = await makeRequest(url);
276
+ const content = await resp.text();
277
+ return parseThings(content, url);
278
+ };
279
+ ```
280
+
281
+ **Test file structure.** Use `describe` + `it` blocks. Group by function name. Cover: happy path, edge cases (empty input, malformed input), and threshold boundaries (e.g. "fewer than N entries returns []").
282
+
283
+ ```typescript
284
+ import { describe, it, expect } from "vitest";
285
+ import { myPureFunction } from "../../path/to/module.js"; // .js extension required
286
+
287
+ describe("myPureFunction", () => {
288
+ it("extracts X from valid input", () => {
289
+ const result = myPureFunction("...");
290
+ expect(result).toContain("expected");
291
+ });
292
+
293
+ it("returns [] for empty input", () => {
294
+ expect(myPureFunction("")).toEqual([]);
295
+ });
296
+
297
+ it("returns [] for invalid JS", () => {
298
+ expect(myPureFunction("{{{{ not valid")).toEqual([]);
299
+ });
300
+ });
301
+ ```
302
+
303
+ **Imports always use `.js` extension** for local files (ESM project with `"module": "node16"`):
304
+
305
+ ```typescript
306
+ import { fn } from "../../lazyLoad/next_js/myModule.js";
307
+ ```
308
+
309
+ **Constructing Babel AST nodes for tests.** When a function under test requires real Babel AST nodes (with `scope`, `path`, correct `start`/`end` offsets), parse a code snippet in the test and capture the node via a traverse visitor — do not construct AST nodes by hand. Wrap the expression in a `const _x = <expr>;` declaration so `start`/`end` offsets are preserved for any `code.slice()` calls inside the function:
310
+
311
+ ```typescript
312
+ import parser from "@babel/parser";
313
+ import _traverse from "@babel/traverse";
314
+ const traverse = (_traverse.default ?? _traverse) as typeof _traverse.default;
315
+
316
+ function parseExpr(code: string) {
317
+ const src = `const _x = ${code};`;
318
+ const ast = parser.parse(src, { sourceType: "unambiguous", plugins: ["jsx", "typescript"] });
319
+ let node: any;
320
+ traverse(ast, {
321
+ VariableDeclarator(p) {
322
+ node = p.node.init;
323
+ p.stop();
324
+ },
325
+ });
326
+ return { node, src };
327
+ }
328
+ ```
329
+
330
+ **Avoiding GitHub secret scanning.** Strings that look like real secrets (Slack webhook URLs, Stripe keys, etc.) will be blocked by GitHub push protection even in test files. Construct them at runtime from parts arrays:
331
+
332
+ ```typescript
333
+ // BAD — blocked by secret scanner
334
+ const url = "https://hooks.slack.com/services/TABCDEF/BABCDEF/xxxxxxxxxxxx";
335
+
336
+ // GOOD — assembled at runtime
337
+ const parts = ["https://hooks.slack.com/services/T", "ABCDEF/B", "ABCDEF/xxxx"];
338
+ const url = parts.join("");
339
+ ```
340
+
341
+ Typical full test invocation:
214
342
 
215
343
  ```bash
216
344
  npm run cleanup && npm run start -- run -u <target-url> -y -k
@@ -250,9 +378,10 @@ Before writing any files, gather the current state:
250
378
  5. **Push** `js-recon` dev branch: `git push origin dev`
251
379
 
252
380
  6. **Open PR** using `gh pr create`:
253
- | Repo | Source | Target | Title | Body |
254
- |------|--------|--------|-------|------|
255
- | `shriyanss/js-recon` | `dev` | `main` | bare version string (e.g. `v1.3.1-alpha.4`) | raw `## <version>` changelog section |
381
+
382
+ | Repo | Source | Target | Title | Body |
383
+ | -------------------- | ------ | ------ | ------------------------------------------- | ------------------------------------ |
384
+ | `shriyanss/js-recon` | `dev` | `main` | bare version string (e.g. `v1.3.1-alpha.4`) | raw `## <version>` changelog section |
256
385
 
257
386
  7. **Monitor js-recon CI** — use `gh pr checks <pr-number> --repo shriyanss/js-recon` and poll until all checks complete. Handle CodeRabbit suggestions (see below). Do NOT merge — wait for user approval.
258
387
 
@@ -274,6 +403,24 @@ Before writing any files, gather the current state:
274
403
 
275
404
  9. **Wait for npm publish** — monitor the release pipeline: `gh run list --repo shriyanss/js-recon --workflow release`. Confirm the npm package is live at the new version before proceeding to Phase 2.
276
405
 
406
+ ### Homebrew tap (automatic, runs in parallel with Phase 2)
407
+
408
+ After `publish-npm` succeeds, the `update-homebrew-tap` job in `publish-js-recon.yml` runs automatically. It:
409
+
410
+ 1. Computes the SHA256 of the published npm tarball from the public npmjs.org URL (no auth)
411
+ 2. Checks out `shriyanss/homebrew-tap` using `HOMEBREW_TAP_TOKEN` (a fine-grained PAT stored in `shriyanss/js-recon` secrets, scoped to `homebrew-tap` repo `Contents: Read and write` only — automatically masked in all log output, never echoed)
412
+ 3. Updates `version`, `url`, and `sha256` in `Formula/js-recon.rb` via anchored `sed`
413
+ 4. Commits `chore: update js-recon formula to <version>` and pushes
414
+
415
+ Monitor: `gh run list --repo shriyanss/homebrew-tap --workflow ci.yml`
416
+
417
+ **If the job fails:** The npm package is already live. Manually update: compute `curl -fsSL <tarball-url> | sha256sum`, edit `Formula/js-recon.rb`, commit, and push to `shriyanss/homebrew-tap`.
418
+
419
+ **One-time setup** (must be done before the first release, already completed):
420
+
421
+ - `shriyanss/homebrew-tap` is a public GitHub repo with the formula at `Formula/js-recon.rb`
422
+ - `HOMEBREW_TAP_TOKEN` is a fine-grained PAT stored in `shriyanss/js-recon` → Settings → Secrets → Actions, scoped exclusively to the `homebrew-tap` repo
423
+
277
424
  ### Phase 2 — js-recon-docs (after npm is live)
278
425
 
279
426
  10. **Fix doc gaps** — cross-check `docs/docs/modules/*.md` against `src/index.ts` and the new CHANGELOG entries. Add or update any missing flags, options, or command descriptions.
@@ -316,6 +463,31 @@ For each suggestion: apply a fix commit to `dev` for correctness bugs or convent
316
463
 
317
464
  Do NOT merge any PR. Once all CI checks pass and CodeRabbit suggestions are addressed, present a summary to the user: what changed in each repo, PR links, CI status, CodeRabbit disposition. Wait for explicit merge approval.
318
465
 
466
+ ## Resolving a GitHub issue
467
+
468
+ When a user asks to fix or implement a GitHub issue, follow these steps:
469
+
470
+ 1. **Read the issue** — `gh issue view <number> --repo shriyanss/js-recon`
471
+
472
+ 2. **Implement** — make the code, docs, and exit-code changes required. Follow all existing conventions (subcommand structure, CHANGELOG format, README Commands table, js-recon-docs modules page, exit_codes.md). Document new exit codes in both `CLAUDE.md` and `js-recon-docs/docs/docs/exit_codes.md`.
473
+
474
+ 3. **Test** — run `npm run cleanup` and exercise the new/changed functionality manually (see "Testing a change" section). Verify error paths and exit codes.
475
+
476
+ 4. **Commit and push to `dev`** — use a `feat(...)` or `fix(...)` commit message. Push to `origin dev`.
477
+
478
+ 5. **Monitor CI** — `gh run list --repo shriyanss/js-recon --branch dev --limit 3`. Watch the `Build & Prettify Code` run. If the `version_check` job fails because `CHANGELOG.md` top version doesn't match `package.json`, bump `package.json` and `src/globalConfig.ts` to match (with a `chore: bump version to <X>` commit) and repush.
479
+
480
+ 6. **Pull prettifier commit** — after CI passes, `git pull origin dev` to pick up the `chore: prettify code` auto-commit.
481
+
482
+ 7. **Close the issue** — once all CI checks pass:
483
+
484
+ ```bash
485
+ gh issue close <number> --repo shriyanss/js-recon --comment \
486
+ "Implemented in commit <short-sha> on the \`dev\` branch. Will be released in **v<version>**."
487
+ ```
488
+
489
+ Use the short commit hash of the feature commit (not the prettifier chore). The target release version comes from the unreleased CHANGELOG entry.
490
+
319
491
  ## cs-mast
320
492
 
321
493
  `cs-mast` computes CS-MAST-S (Context-Stratified Merkelized Abstract Syntax Tree) signatures for every `.js` file found recursively under an output directory, then optionally finds and reports structural collisions — files sharing the same root signature.
@@ -339,6 +511,11 @@ Do NOT merge any PR. Once all CI checks pass and CodeRabbit suggestions are addr
339
511
  - `--min-collisions <n>` — minimum occurrences to report (default: 2)
340
512
  - `--co / --collision-output <file>` — write collision data to a file (independent of `--ct`)
341
513
  - `--cf / --collision-format json|csv` — output format (default: csv)
514
+ - `--scat <categories>` — comma-separated scat categories to use (default: `lit,decl,loop,cond`). Overrides the fixed config for this run.
515
+ - `--sinc <nodes>` — comma-separated exact node types to include via sinc (e.g. `IfStatement`).
516
+ - `--all-scat-permutations` — run all 511 non-empty scat subsets and write one collision file per subset to `--perm-output`.
517
+ - `--perm-output <dir>` — output directory for per-permutation files (required with `--all-scat-permutations`).
518
+ - `--perm-concurrency <n>` — parallel permutation workers (default: half of CPU count).
342
519
 
343
520
  **`--co` path resolution:** if the given path is a directory or has no extension, the file is written as `collisions.<fmt>` in the current working directory.
344
521
 
@@ -350,15 +527,28 @@ Do NOT merge any PR. Once all CI checks pass and CodeRabbit suggestions are addr
350
527
  npm run build
351
528
  node build/index.js cs-mast -o output --ct --min-collisions 2
352
529
  node build/index.js cs-mast -o output --co output --cf csv # writes ./collisions.csv
530
+ node build/index.js cs-mast -o output --all-scat-permutations --perm-output ./perm-out --cf json
353
531
  ```
354
532
 
355
533
  ## refactor
356
534
 
357
- The `refactor` command supports three techs:
535
+ The `refactor` command supports the following techs:
358
536
 
359
537
  - **`react-webpack`** — webpack 5 React bundles. Splits a numeric module map into per-module ES files, rewrites require→import, recovers JSX. See `src/refactor/react/CLAUDE.md`.
360
538
  - **`react-vite`** — Vite (rolldown) React bundles. Removes CJS interop wrappers, rewrites vendor imports to canonical library imports (`react`, `react/jsx-runtime`, etc.), recovers JSX. Runs a Vite build check after writing output. See `src/refactor/react-vite/CLAUDE.md`.
361
539
  - **`next`** — Next.js bundles (legacy).
540
+ - **`next-turbopack`** — Next.js Turbopack chunks. Handles both turbopack 3-param `func_NNN=(runtime,module,exports)=>{}` and 1-param `func_NNN=(runtime)=>{}` formats plus webpack-style coexisting chunks. See `src/refactor/next/CLAUDE.md`.
541
+ - **`next-webpack`** — Next.js webpack chunks. Input format from `mapped.json`: `NNN:(module,exports,require)=>{}`. Recovers named exports (ODP, require.d), default exports (module.exports=V), re-exports (module.exports=require(N)→export*), and require hoisting. 277/280 modules recovered on a real bundle. Param order: params[0]=module, params[1]=exports, params[2]=require. See `src/refactor/next/CLAUDE.md`.
542
+ - **`vue-webpack`** — Vue.js webpack 4/5 chunks. Container format: `(window.webpackJsonp||[]).push([[chunkIds],{moduleId:function(t,e,r){...}}])`. Each chunk file may contain multiple module functions; each is extracted and transformed. Reuses the Next.js webpack transform (same module param semantics: params[0]=module, params[1]=exports, params[2]=require). See `src/refactor/vue/index.ts`.
543
+ - **`vue-vite`** — Vue 3 + Vite page chunks. The main index chunk (contains `__vccOpts`, all of Vue runtime) is analysed to build an export-alias→canonical-name map. Lazy page chunks then have their index imports rewritten to canonical `import {...} from 'vue'` statements. The `_export_sfc` compiler helper is inlined as a local const. See `src/refactor/vue/vite.ts` and `src/refactor/vue/vendor-analyze-vue.ts`.
544
+
545
+ ### Known react-vite bugs (discovered 2026-07-01, test against js-recon-research/react/20-cve-app)
546
+
547
+ **Multi-chunk file overwrite** — `map` segments each Vite chunk into multiple sub-chunks (one per top-level function). The refactor write path processes sub-chunks sequentially, each overwriting the previous output file. Only the last sub-chunk survives. For chunks with inlined library code followed by the component function (e.g. `ApiProxy`, `Editor`, `Search`), this means the component is the survivor (last in the map order), which is the correct and useful result — but any app-specific helper functions in the same chunk are also lost.
548
+
549
+ **Rename race** — When JSX is detected in a sub-chunk, the write path renames the output file `.js` → `.jsx`. When the same file is written by multiple sub-chunks, the rename is attempted on each JSX-containing chunk; all attempts after the first throw `ENOENT` because the `.js` file was already renamed. The `.jsx` file is correct; the unhandled rejection is noise. Fix: track which output files have already been renamed.
550
+
551
+ **Remote signatures** — The `react/vite/large` HuggingFace bucket is currently empty. The tool falls back gracefully with a warning but remote library stripping is disabled. Symptom: `[!] No remote collisions files found for scat "lit-decl-loop-cond" in branch "react/vite/large"`. Fix: populate the bucket by running CS-MAST-S generation against a corpus of large Vite apps.
362
552
 
363
553
  ### refactor `--collisions <file>` (react-webpack only)
364
554
 
@@ -368,6 +558,37 @@ Plumbing: `src/index.ts` (CLI) → `src/refactor/index.ts` (resolves the path vi
368
558
 
369
559
  The baseline files live in the sibling `js-recon-cs-mast-s/` repo (`baselines/<tech>/<scat>/collisions.json`). See its `README.md` for layout and provenance.
370
560
 
561
+ ### refactor `--detect-version` (react-webpack, react-vite)
562
+
563
+ `refactor -t react-webpack` and `refactor -t react-vite` accept a `--detect-version` flag that uses CS-MAST signatures to detect the React version used in the bundle.
564
+
565
+ **Related flags:**
566
+
567
+ - `--detect-version-config <config>` — `"dynamic"` (default) or comma-separated scat categories (e.g. `lit,decl,loop,cond`).
568
+ - `--detect-version-dynamic-threshold <n>` — number of scat configs to use in dynamic mode (default: 3).
569
+ - `--detect-version-dynamic-conf-purge` — clears the cached dynamic scat config and recomputes.
570
+
571
+ **How it works:**
572
+
573
+ 1. **Scat config resolution** (`resolveVersionDetectionScatDirs()` in `src/refactor/index.ts`):
574
+ - `dynamic` mode: reads cached scat configs from `~/.js-recon/refactor/config.json`. If absent (or purged), calls `selectDynamicScatConfigs()` which lists scat dirs from the HF bucket for a reference version, then validates that each scat dir has non-empty `reliable_signatures.json` for ALL versions. Saves the result to `config.json`.
575
+ - Static mode: parses the user's comma-separated categories, converts to a bucket dir name via `scatToDir()`, validates against all versions, and exits with code 26 if any version's file is empty.
576
+ 2. `generateBundleSignatures()` in `src/refactor/remote/version-detect.ts` runs `cs_mast_init` on every chunk + optional extra code snippets for each selected scat config. This produces a separate signature set per scat.
577
+ 3. For each available React version, `fetchReliableSignatures()` downloads (or loads from cache) the `reliable_signatures.json` per scat config. Match counts are **summed across all scat configs** per version.
578
+ 4. The version with the highest total match count is returned as the detected version.
579
+ 5. The detected version's npm semver is used to pin `react` and `react-dom` in the refactored output's `package.json`.
580
+
581
+ **Caches:**
582
+
583
+ - Signature cache: `~/.js-recon/refactor/version_sigs_cache/<bundler>/<version>/<scatDir>/reliable_signatures.json` + `.cached_at` (7-day TTL).
584
+ - Dynamic config cache: `~/.js-recon/refactor/config.json` (`dynamicVersionDetectionScatConfig` field).
585
+
586
+ **Dataset coverage:** webpack (react-0.12 through react-19), vite (react-16 through react-19).
587
+
588
+ **Important:** the version detection data in the HF bucket was generated with `@shriyanss/cs-mast` v0.1.8. The tool requires cs-mast 0.1.8 or later to produce matching signatures. Using an older cs-mast version will result in zero matches.
589
+
590
+ **Important:** the version detection data in the HF bucket was generated with `@shriyanss/cs-mast` v0.1.8. The tool requires cs-mast 0.1.8 or later to produce matching signatures. Using an older cs-mast version will result in zero matches.
591
+
371
592
  ## Security / confidentiality
372
593
 
373
594
  When a change is informed by behavior observed on a real target (URLs, endpoint names, response shapes, finding details, etc.):
package/README.md CHANGED
@@ -14,6 +14,23 @@ It can also reconstruct HTTP requests that the app makes to the server, and outp
14
14
 
15
15
  ## Installation
16
16
 
17
+ ### Homebrew (macOS and Linux)
18
+
19
+ ```bash
20
+ brew tap shriyanss/tap
21
+ brew install js-recon
22
+ ```
23
+
24
+ To update:
25
+
26
+ ```bash
27
+ brew update && brew upgrade js-recon
28
+ ```
29
+
30
+ > **Note:** After installing via Homebrew, the `lazyload` subcommand requires a Chromium browser. Run `brew info js-recon` for setup instructions.
31
+
32
+ ### npm (all platforms)
33
+
17
34
  This tool requires Node.JS and `npm` to be installed. The [official download page](https://nodejs.org/en/download) can be referred. Please install **22.17.0 (LTS)** or later. Downloading older versions might break the tool.
18
35
 
19
36
  To install the tool globally, run:
@@ -52,27 +69,29 @@ js-recon run -u https://app.example.com
52
69
 
53
70
  `js-recon` provides a suite of commands for comprehensive JavaScript analysis. For detailed usage and examples, please refer to its full documentation.
54
71
 
55
- | Command | Description | Documentation |
56
- | ------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------- |
57
- | `lazyload` | Downloads dynamically loaded JavaScript files from a target. | [Read Docs](https://js-recon.io/docs/docs/modules/lazyload) |
58
- | `endpoints` | Extracts API endpoints and client-side paths from JS files. | [Read Docs](https://js-recon.io/docs/docs/modules/endpoints) |
59
- | `strings` | Extracts strings, URLs, and potential secrets from JS files. | [Read Docs](https://js-recon.io/docs/docs/modules/strings) |
60
- | `map` | Maps function calls and analyzes code, with optional AI-powered descriptions. | [Read Docs](https://js-recon.io/docs/docs/modules/map) |
61
- | `api-gateway` | Manages AWS API Gateway for IP rotation to bypass rate limits. | [Read Docs](https://js-recon.io/docs/docs/modules/api-gateway) |
62
- | `run` | Runs all analysis modules automatically on a target. | [Read Docs](https://js-recon.io/docs/docs/modules/run) |
63
- | `analyze` | Analyzes the code. | [Read Docs](https://js-recon.io/docs/docs/modules/analyze) |
64
- | `report` | Generates a report from the analysis modules. | [Read Docs](https://js-recon.io/docs/docs/modules/report) |
65
- | `mcp` | AI-powered interactive CLI, one-shot chat, and MCP stdio server. | [Read Docs](https://js-recon.io/docs/docs/modules/mcp) |
66
- | `fingerprint` | Detects the JavaScript framework used by a target URL (JSON/JSONL output). | [Read Docs](https://js-recon.io/docs/docs/modules/fingerprint) |
67
- | `refactor` | Refactors and deobfuscates webpack modules from a mapped JSON file. | [Read Docs](https://js-recon.io/docs/docs/modules/refactor) |
68
- | `load` | Populates the response cache from a Caido or Burp Suite export. | [Read Docs](https://js-recon.io/docs/docs/modules/load) |
69
- | `cs-mast` | Computes CS-MAST structural hashes for JS files and finds hash collisions. | [Read Docs](https://js-recon.io/docs/docs/modules/cs-mast) |
72
+ | Command | Description | Documentation |
73
+ | ------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------- |
74
+ | `lazyload` | Downloads dynamically loaded JavaScript files from a target. | [Read Docs](https://js-recon.io/docs/docs/modules/lazyload) |
75
+ | `endpoints` | Extracts API endpoints and client-side paths from JS files. | [Read Docs](https://js-recon.io/docs/docs/modules/endpoints) |
76
+ | `strings` | Extracts strings, URLs, and potential secrets from JS files. | [Read Docs](https://js-recon.io/docs/docs/modules/strings) |
77
+ | `map` | Maps function calls and analyzes code, with optional AI-powered descriptions. | [Read Docs](https://js-recon.io/docs/docs/modules/map) |
78
+ | `api-gateway` | Manages AWS API Gateway for IP rotation to bypass rate limits. | [Read Docs](https://js-recon.io/docs/docs/modules/api-gateway) |
79
+ | `run` | Runs all analysis modules automatically on a target. | [Read Docs](https://js-recon.io/docs/docs/modules/run) |
80
+ | `analyze` | Analyzes the code. | [Read Docs](https://js-recon.io/docs/docs/modules/analyze) |
81
+ | `report` | Generates a report from the analysis modules. | [Read Docs](https://js-recon.io/docs/docs/modules/report) |
82
+ | `mcp` | AI-powered interactive CLI, one-shot chat, and MCP stdio server. | [Read Docs](https://js-recon.io/docs/docs/modules/mcp) |
83
+ | `fingerprint` | Detects the JavaScript framework used by a target URL (JSON/JSONL output). | [Read Docs](https://js-recon.io/docs/docs/modules/fingerprint) |
84
+ | `refactor` | Recovers readable JSX and strips library code from React (webpack/Vite) bundles. | [Read Docs](https://js-recon.io/docs/docs/modules/refactor) |
85
+ | `load` | Populates the response cache from a Caido or Burp Suite export. | [Read Docs](https://js-recon.io/docs/docs/modules/load) |
86
+ | `cs-mast` | Computes CS-MAST structural hashes for JS files and finds hash collisions. | [Read Docs](https://js-recon.io/docs/docs/modules/cs-mast) |
87
+ | `sourcemaps` | Extracts original source files from `.map` sourcemap files. | [Read Docs](https://js-recon.io/docs/docs/modules/sourcemaps) |
88
+ | `completion` | Generates shell completion scripts for bash, zsh, or fish. | [Read Docs](https://js-recon.io/docs/docs/modules/completion) |
70
89
 
71
90
  ## Key Features
72
91
 
73
92
  - Downloads all dynamically loaded JS files (refered as `lazyload`) from website with supported frameworks
74
93
  - Use API gateway to rotate IP addresses to bypass firewall
75
- - Extract strings from the discovered JS files, and extract potential secrets, endpoints, etc. from them
94
+ - Extract strings from the discovered JS files, and extract potential secrets, endpoints, etc. from them (built-in scanner via `--secrets`; TruffleHog integration via `--trufflehog`)
76
95
  - Endpoints modules extracts client-side paths from the app
77
96
  - Map feature analyzes the JS files and outputs it to a JSON file. An interactive mode can be then used to analyze it
78
97
  - Reconstruct HTTP requests that the app makes to the server, and output them to the OpenAPI spec
@@ -0,0 +1,129 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import chalk from "chalk";
11
+ import { cs_mast_init, parseSignature, ParseError } from "@shriyanss/cs-mast";
12
+ /**
13
+ * Walk all AdapterNode descendants post-order and check if any node's
14
+ * computedHash equals the target hex hash. The _signatureMap built by
15
+ * cs_mast_init only stores leaf-level node signatures; to match the
16
+ * compound (internal-node) signatures produced by the experiment scripts
17
+ * (buildSignatureFromConfig on a specific subtree), we must walk the tree
18
+ * and compare computedHash directly.
19
+ */
20
+ function treeContainsHash(node, targetHashHex) {
21
+ var _a;
22
+ if (node.computedHash === targetHashHex)
23
+ return true;
24
+ for (const child of (_a = node.children) !== null && _a !== void 0 ? _a : []) {
25
+ if (treeContainsHash(child, targetHashHex))
26
+ return true;
27
+ }
28
+ return false;
29
+ }
30
+ /**
31
+ * CS-MAST-S analysis engine. Checks whether a CS-MAST-S signature (PHC string)
32
+ * exists anywhere in a chunk's AST. Suitable for regression detection: once a
33
+ * vulnerability is confirmed via the AST engine, its signature can be embedded
34
+ * in a cs-mast-s rule to track whether the same code reappears in future builds.
35
+ *
36
+ * Each step must contain a `csMastS.signature` (PHC string). The scat config is
37
+ * derived automatically from the signature — no separate config field is needed.
38
+ * All steps must match in the same chunk for a finding to fire.
39
+ *
40
+ * @param rule - The cs-mast-s rule with signature steps
41
+ * @param mappedJsonData - Code chunks from mapped.json
42
+ * @returns Array of findings
43
+ */
44
+ const csMastSEngine = (rule, mappedJsonData) => __awaiter(void 0, void 0, void 0, function* () {
45
+ const findings = [];
46
+ for (const chunk of Object.values(mappedJsonData)) {
47
+ // Cache cs_mast trees by config fingerprint to avoid redundant parses when
48
+ // multiple steps share the same scat config.
49
+ const treeCache = new Map();
50
+ const completedSteps = new Set();
51
+ for (const step of rule.steps) {
52
+ // Honor requires: skip if any prerequisite step didn't match.
53
+ if (step.requires && step.requires.length > 0) {
54
+ const allMet = step.requires.every((r) => completedSteps.has(r));
55
+ if (!allMet)
56
+ continue;
57
+ }
58
+ if (!step.csMastS)
59
+ continue;
60
+ const { signature } = step.csMastS;
61
+ const parsed = parseSignature(signature);
62
+ if (!parsed) {
63
+ console.log(chalk.yellow(`[!] cs-mast-s step "${step.name}" in rule "${rule.id}" has an invalid signature — skipping`));
64
+ continue;
65
+ }
66
+ const config = {
67
+ hash: parsed.hash,
68
+ lang: parsed.lang,
69
+ prsr: parsed.prsr,
70
+ scat: parsed.scat,
71
+ sinc: parsed.sinc,
72
+ sourceType: "unambiguous",
73
+ };
74
+ // Build a stable cache key from the config fields that affect the hash tree.
75
+ const configKey = `${config.hash}:${config.lang}:${config.prsr}:${config.scat.join("_")}:${config.sinc.join("_")}`;
76
+ let tree;
77
+ if (treeCache.has(configKey)) {
78
+ tree = treeCache.get(configKey);
79
+ }
80
+ else {
81
+ try {
82
+ tree = cs_mast_init(chunk.code, config);
83
+ treeCache.set(configKey, tree);
84
+ }
85
+ catch (e) {
86
+ if (e instanceof ParseError) {
87
+ // Chunk is unparseable — skip silently (mirrors astEngine behavior).
88
+ break;
89
+ }
90
+ throw e;
91
+ }
92
+ }
93
+ if (treeContainsHash(tree.root, parsed.hashHex)) {
94
+ completedSteps.add(step.name);
95
+ }
96
+ }
97
+ // Fire only if every step in the rule completed (matched).
98
+ if (completedSteps.size === rule.steps.length) {
99
+ const matchedSigs = rule.steps.filter((s) => s.csMastS).map((s) => s.csMastS.signature);
100
+ const message = `[+] "${rule.name}" found in chunk ${chunk.id}`;
101
+ if (rule.severity === "info") {
102
+ console.log(chalk.cyan(message));
103
+ }
104
+ else if (rule.severity === "low") {
105
+ console.log(chalk.yellow(message));
106
+ }
107
+ else if (rule.severity === "medium") {
108
+ console.log(chalk.magenta(message));
109
+ }
110
+ else if (rule.severity === "high") {
111
+ console.error(chalk.red(message));
112
+ }
113
+ findings.push({
114
+ ruleId: rule.id,
115
+ ruleName: rule.name,
116
+ ruleType: rule.type,
117
+ ruleDescription: rule.description,
118
+ ruleAuthor: rule.author,
119
+ ruleTech: rule.tech,
120
+ severity: rule.severity,
121
+ message,
122
+ findingLocation: `// chunk: ${chunk.id}\n// CS-MAST-S signature: ${matchedSigs.join(", ")}`,
123
+ });
124
+ }
125
+ }
126
+ return findings;
127
+ });
128
+ export default csMastSEngine;
129
+ //# sourceMappingURL=csMastSEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csMastSEngine.js","sourceRoot":"","sources":["../../../src/analyze/engine/csMastSEngine.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import requestEngine from "./requestEngine.js";
11
11
  import astEngine from "./astEngine.js";
12
+ import csMastSEngine from "./csMastSEngine.js";
12
13
  /**
13
14
  * Main analysis engine that routes rules to appropriate sub-engines based on rule type.
14
15
  *
@@ -53,6 +54,20 @@ export const engine = (rule, mappedJsonData, openapiData, tech) => __awaiter(voi
53
54
  findings.push(...(yield astEngine(rule, mappedJsonData)));
54
55
  }
55
56
  }
57
+ else if (rule.type === "cs-mast-s") {
58
+ if (!mappedJsonData) {
59
+ return;
60
+ }
61
+ let techValid = true;
62
+ for (const t of rule.tech) {
63
+ if (!rule.tech.includes(tech)) {
64
+ techValid = false;
65
+ }
66
+ }
67
+ if (techValid || tech === "all" || rule.tech.includes("all")) {
68
+ findings.push(...(yield csMastSEngine(rule, mappedJsonData)));
69
+ }
70
+ }
56
71
  return findings;
57
72
  });
58
73
  export default engine;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyze/engine/index.ts"],"names":[],"mappings":";;;;;;;;;AAGA,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,SAAS,MAAM,gBAAgB,CAAC;AAGvC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAClB,IAAU,EACV,cAAkC,EAClC,WAAoC,EACpC,IAA6D,EAC1B,EAAE;IACrC,yGAAyG;IAEzG,IAAI,QAAQ,GAAmB,EAAE,CAAC;IAElC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,CAAC,WAAW,EAAE,CAAC;YACf,OAAO;QACX,CAAC;QAED,IAAI,SAAS,GAAG,IAAI,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,SAAS,GAAG,KAAK,CAAC;YACtB,CAAC;QACL,CAAC;QAED,IAAI,SAAS,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3D,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;QAC/D,CAAC;IACL,CAAC;SAAM,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,cAAc,EAAE,CAAC;YAClB,OAAO;QACX,CAAC;QAED,IAAI,SAAS,GAAG,IAAI,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,SAAS,GAAG,KAAK,CAAC;YACtB,CAAC;QACL,CAAC;QAED,IAAI,SAAS,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3D,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,SAAS,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC,CAAA,CAAC;AAEF,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyze/engine/index.ts"],"names":[],"mappings":";;;;;;;;;AAGA,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAG/C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,CAClB,IAAU,EACV,cAAkC,EAClC,WAAoC,EACpC,IAA6D,EAC1B,EAAE;IACrC,yGAAyG;IAEzG,IAAI,QAAQ,GAAmB,EAAE,CAAC;IAElC,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,CAAC,WAAW,EAAE,CAAC;YACf,OAAO;QACX,CAAC;QAED,IAAI,SAAS,GAAG,IAAI,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,SAAS,GAAG,KAAK,CAAC;YACtB,CAAC;QACL,CAAC;QAED,IAAI,SAAS,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3D,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;QAC/D,CAAC;IACL,CAAC;SAAM,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,cAAc,EAAE,CAAC;YAClB,OAAO;QACX,CAAC;QAED,IAAI,SAAS,GAAG,IAAI,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,SAAS,GAAG,KAAK,CAAC;YACtB,CAAC;QACL,CAAC;QAED,IAAI,SAAS,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3D,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,SAAS,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC;IACL,CAAC;SAAM,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACnC,IAAI,CAAC,cAAc,EAAE,CAAC;YAClB,OAAO;QACX,CAAC;QAED,IAAI,SAAS,GAAG,IAAI,CAAC;QACrB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC5B,SAAS,GAAG,KAAK,CAAC;YACtB,CAAC;QACL,CAAC;QAED,IAAI,SAAS,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3D,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;QAClE,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC,CAAA,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -1,5 +1,6 @@
1
+ var _a;
1
2
  import _traverse from "@babel/traverse";
2
- const traverse = _traverse.default;
3
+ const traverse = ((_a = _traverse.default) !== null && _a !== void 0 ? _a : _traverse);
3
4
  /**
4
5
  * Finds direct assignment expressions or variable declarations for a given identifier node.
5
6
  *
@@ -1 +1 @@
1
- {"version":3,"file":"findDirectAssignment.js","sourceRoot":"","sources":["../../../../src/analyze/helpers/engineHelpers/findDirectAssignment.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,iBAAiB,CAAC;AAGxC,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC;AAEnC;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAgB,EAAE,KAAW;IAC9D,IAAI,cAAc,GAAgB,IAAI,CAAC;IAEvC,QAAQ,CAAC,KAAK,EAAE;QACZ,oBAAoB,CAAC,IAAI;YACrB,uEAAuE;YACvE,IACI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,YAAY;gBACpC,UAAU,CAAC,IAAI,KAAK,YAAY;gBAChC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,EACzC,CAAC;gBACC,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC;gBAC3B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,4BAA4B;YAC7C,CAAC;QACL,CAAC;QACD,kBAAkB,CAAC,IAAI;YACnB,8DAA8D;YAC9D,IACI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,YAAY;gBAClC,UAAU,CAAC,IAAI,KAAK,YAAY;gBAChC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,EACvC,CAAC;gBACC,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC;gBAC3B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,4BAA4B;YAC7C,CAAC;QACL,CAAC;KACJ,CAAC,CAAC;IAEH,OAAO,cAAc,CAAC;AAC1B,CAAC"}
1
+ {"version":3,"file":"findDirectAssignment.js","sourceRoot":"","sources":["../../../../src/analyze/helpers/engineHelpers/findDirectAssignment.ts"],"names":[],"mappings":";AAAA,OAAO,SAAS,MAAM,iBAAiB,CAAC;AAGxC,MAAM,QAAQ,GAAG,CAAC,MAAA,SAAS,CAAC,OAAO,mCAAI,SAAS,CAA6B,CAAC;AAE9E;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAgB,EAAE,KAAW;IAC9D,IAAI,cAAc,GAAgB,IAAI,CAAC;IAEvC,QAAQ,CAAC,KAAK,EAAE;QACZ,oBAAoB,CAAC,IAAI;YACrB,uEAAuE;YACvE,IACI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,YAAY;gBACpC,UAAU,CAAC,IAAI,KAAK,YAAY;gBAChC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,EACzC,CAAC;gBACC,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC;gBAC3B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,4BAA4B;YAC7C,CAAC;QACL,CAAC;QACD,kBAAkB,CAAC,IAAI;YACnB,8DAA8D;YAC9D,IACI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,YAAY;gBAClC,UAAU,CAAC,IAAI,KAAK,YAAY;gBAChC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,EACvC,CAAC;gBACC,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC;gBAC3B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,4BAA4B;YAC7C,CAAC;QACL,CAAC;KACJ,CAAC,CAAC;IAEH,OAAO,cAAc,CAAC;AAC1B,CAAC"}
@@ -1,5 +1,6 @@
1
+ var _a;
1
2
  import _traverse from "@babel/traverse";
2
- const traverse = _traverse.default;
3
+ const traverse = ((_a = _traverse.default) !== null && _a !== void 0 ? _a : _traverse);
3
4
  /**
4
5
  * Finds an assignment expression where the left side is a member expression with a property that matches the given name.
5
6
  * Traverses the given node and its children to find a match.
@@ -1 +1 @@
1
- {"version":3,"file":"findMemberExpressionAssignment.js","sourceRoot":"","sources":["../../../../src/analyze/helpers/engineHelpers/findMemberExpressionAssignment.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,MAAM,iBAAiB,CAAC;AACxC,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC;AAEnC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,IAAU,EAAE,OAAe,EAAE,KAAU,EAAoB,EAAE;IACxG,IAAI,SAA2B,CAAC;IAEhC,QAAQ,CACJ,IAAI,EACJ;QACI,oBAAoB,CAAC,IAAI;;YACrB,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC;YAEjC,IACI,CAAA,MAAA,cAAc,CAAC,IAAI,0CAAE,IAAI,MAAK,kBAAkB;gBAChD,CAAA,MAAA,cAAc,CAAC,KAAK,0CAAE,IAAI,MAAK,kBAAkB;gBACjD,CAAA,MAAC,cAAc,CAAC,IAAY,CAAC,QAAQ,0CAAE,IAAI,MAAK,YAAY;gBAC5D,CAAA,MAAC,cAAc,CAAC,IAAY,CAAC,QAAQ,0CAAE,IAAI,MAAK,OAAO,EACzD,CAAC;gBACC,SAAS,GAAG,cAAc,CAAC;gBAC3B,+CAA+C;gBAC/C,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,CAAC;QACL,CAAC;KACJ,EACD,KAAK,CACR,CAAC;IAEF,OAAO,SAAS,CAAC;AACrB,CAAC,CAAC"}
1
+ {"version":3,"file":"findMemberExpressionAssignment.js","sourceRoot":"","sources":["../../../../src/analyze/helpers/engineHelpers/findMemberExpressionAssignment.ts"],"names":[],"mappings":";AACA,OAAO,SAAS,MAAM,iBAAiB,CAAC;AACxC,MAAM,QAAQ,GAAG,CAAC,MAAA,SAAS,CAAC,OAAO,mCAAI,SAAS,CAA6B,CAAC;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,IAAU,EAAE,OAAe,EAAE,KAAU,EAAoB,EAAE;IACxG,IAAI,SAA2B,CAAC;IAEhC,QAAQ,CACJ,IAAI,EACJ;QACI,oBAAoB,CAAC,IAAI;;YACrB,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC;YAEjC,IACI,CAAA,MAAA,cAAc,CAAC,IAAI,0CAAE,IAAI,MAAK,kBAAkB;gBAChD,CAAA,MAAA,cAAc,CAAC,KAAK,0CAAE,IAAI,MAAK,kBAAkB;gBACjD,CAAA,MAAC,cAAc,CAAC,IAAY,CAAC,QAAQ,0CAAE,IAAI,MAAK,YAAY;gBAC5D,CAAA,MAAC,cAAc,CAAC,IAAY,CAAC,QAAQ,0CAAE,IAAI,MAAK,OAAO,EACzD,CAAC;gBACC,SAAS,GAAG,cAAc,CAAC;gBAC3B,+CAA+C;gBAC/C,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,CAAC;QACL,CAAC;KACJ,EACD,KAAK,CACR,CAAC;IAEF,OAAO,SAAS,CAAC;AACrB,CAAC,CAAC"}
@@ -1,5 +1,6 @@
1
+ var _a;
1
2
  import _traverse from "@babel/traverse";
2
- const traverse = _traverse.default;
3
+ const traverse = ((_a = _traverse.default) !== null && _a !== void 0 ? _a : _traverse);
3
4
  /**
4
5
  * Resolves a function identifier to its declaration node in the given AST.
5
6
  *