@shriyanss/js-recon 1.4.1-alpha.4 → 1.4.1-alpha.6
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.
- package/.github/workflows/build-and-prettify.yaml +187 -1
- package/.github/workflows/promote-js-recon.yml +116 -0
- package/.github/workflows/publish-js-recon.yml +7 -114
- package/.github/workflows/rules-smoke-test.yaml +79 -0
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +208 -14
- package/Dockerfile.release +29 -0
- package/README.md +18 -17
- package/build/analyze/engine/csMastSEngine.js +129 -0
- package/build/analyze/engine/csMastSEngine.js.map +1 -0
- package/build/analyze/engine/index.js +15 -0
- package/build/analyze/engine/index.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/findDirectAssignment.js +2 -1
- package/build/analyze/helpers/engineHelpers/findDirectAssignment.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js +2 -1
- package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/resolveFunctionIdentifier.js +2 -1
- package/build/analyze/helpers/engineHelpers/resolveFunctionIdentifier.js.map +1 -1
- package/build/analyze/helpers/engineHelpers/taintFlow.js +11 -2
- package/build/analyze/helpers/engineHelpers/taintFlow.js.map +1 -1
- package/build/analyze/helpers/schemas.js +5 -1
- package/build/analyze/helpers/schemas.js.map +1 -1
- package/build/analyze/helpers/validate.js +3 -3
- package/build/analyze/helpers/validate.js.map +1 -1
- package/build/completion/index.js +432 -0
- package/build/completion/index.js.map +1 -0
- package/build/cs_mast/index.js +94 -3
- package/build/cs_mast/index.js.map +1 -1
- package/build/endpoints/next_js/client_jsFilesHref.js +2 -1
- package/build/endpoints/next_js/client_jsFilesHref.js.map +1 -1
- package/build/endpoints/next_js/client_jsonParse.js +2 -1
- package/build/endpoints/next_js/client_jsonParse.js.map +1 -1
- package/build/endpoints/next_js/client_mappedJsonFile.js +2 -1
- package/build/endpoints/next_js/client_mappedJsonFile.js.map +1 -1
- package/build/endpoints/next_js/client_subsequentRequests.js +2 -1
- package/build/endpoints/next_js/client_subsequentRequests.js.map +1 -1
- package/build/fingerprint/index.js +56 -33
- package/build/fingerprint/index.js.map +1 -1
- package/build/globalConfig.js +1 -1
- package/build/index.js +67 -6
- package/build/index.js.map +1 -1
- package/build/lazyLoad/angular/angular_getFromMainJs.js +2 -1
- package/build/lazyLoad/angular/angular_getFromMainJs.js.map +1 -1
- package/build/lazyLoad/downloadFilesUtil.js +6 -5
- package/build/lazyLoad/downloadFilesUtil.js.map +1 -1
- package/build/lazyLoad/downloadLoadedJsUtil.js +56 -7
- package/build/lazyLoad/downloadLoadedJsUtil.js.map +1 -1
- package/build/lazyLoad/downloadQueue.js +9 -8
- package/build/lazyLoad/downloadQueue.js.map +1 -1
- package/build/lazyLoad/index.js +28 -1
- package/build/lazyLoad/index.js.map +1 -1
- package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js +36 -34
- package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js.map +1 -1
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +63 -15
- package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
- package/build/lazyLoad/next_js/next_parseLayoutJs.js +80 -68
- package/build/lazyLoad/next_js/next_parseLayoutJs.js.map +1 -1
- package/build/lazyLoad/next_js/next_promiseResolve.js +22 -16
- package/build/lazyLoad/next_js/next_promiseResolve.js.map +1 -1
- package/build/lazyLoad/nuxt_js/nuxt_astParse.js +49 -47
- package/build/lazyLoad/nuxt_js/nuxt_astParse.js.map +1 -1
- package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +3 -1
- package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js.map +1 -1
- package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +3 -2
- package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js.map +1 -1
- package/build/lazyLoad/react/react_followImports.js +12 -9
- package/build/lazyLoad/react/react_followImports.js.map +1 -1
- package/build/lazyLoad/react/react_webpackChunkPaths.js +175 -123
- package/build/lazyLoad/react/react_webpackChunkPaths.js.map +1 -1
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +3 -2
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -1
- package/build/lazyLoad/techDetect/checkAngularJS.js +27 -6
- package/build/lazyLoad/techDetect/checkAngularJS.js.map +1 -1
- package/build/lazyLoad/techDetect/checkReact.js +8 -0
- package/build/lazyLoad/techDetect/checkReact.js.map +1 -1
- package/build/lazyLoad/techDetect/index.js +23 -2
- package/build/lazyLoad/techDetect/index.js.map +1 -1
- package/build/lazyLoad/vue/vue_RuntimeJs.js +2 -1
- package/build/lazyLoad/vue/vue_RuntimeJs.js.map +1 -1
- package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js +2 -1
- package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js.map +1 -1
- package/build/lazyLoad/vue/vue_discoverJsFiles.js +22 -10
- package/build/lazyLoad/vue/vue_discoverJsFiles.js.map +1 -1
- package/build/lazyLoad/vue/vue_getClientSidePaths.js +2 -1
- package/build/lazyLoad/vue/vue_getClientSidePaths.js.map +1 -1
- package/build/lazyLoad/vue/vue_jsImports.js +2 -1
- package/build/lazyLoad/vue/vue_jsImports.js.map +1 -1
- package/build/lazyLoad/vue/vue_viteMapDeps.js +62 -54
- package/build/lazyLoad/vue/vue_viteMapDeps.js.map +1 -1
- package/build/map/angular_js/getAngularConnections.js +1 -1
- package/build/map/angular_js/getAngularConnections.js.map +1 -1
- package/build/map/graphql/resolveGraphql.js +2 -1
- package/build/map/graphql/resolveGraphql.js.map +1 -1
- package/build/map/next_js/getExports.js +2 -1
- package/build/map/next_js/getExports.js.map +1 -1
- package/build/map/next_js/getFetchInstances.js +2 -1
- package/build/map/next_js/getFetchInstances.js.map +1 -1
- package/build/map/next_js/getTurbopackConnections.js +2 -1
- package/build/map/next_js/getTurbopackConnections.js.map +1 -1
- package/build/map/next_js/getWebpackConnections.js +2 -1
- package/build/map/next_js/getWebpackConnections.js.map +1 -1
- package/build/map/next_js/resolveAxios.js +2 -1
- package/build/map/next_js/resolveAxios.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/directCallsWithoutAssignment.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/directCallsWithoutAssignment.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/interceptorHeaders.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/interceptorHeaders.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js.map +1 -1
- package/build/map/next_js/resolveAxiosHelpers/traceBody.js +2 -1
- package/build/map/next_js/resolveAxiosHelpers/traceBody.js.map +1 -1
- package/build/map/next_js/resolveFetch.js +2 -1
- package/build/map/next_js/resolveFetch.js.map +1 -1
- package/build/map/next_js/resolveNewRequest.js +2 -1
- package/build/map/next_js/resolveNewRequest.js.map +1 -1
- package/build/map/next_js/resolveServerActions.js +2 -1
- package/build/map/next_js/resolveServerActions.js.map +1 -1
- package/build/map/next_js/utils.js +2 -1
- package/build/map/next_js/utils.js.map +1 -1
- package/build/map/react_js/getReactConnections.js +5 -4
- package/build/map/react_js/getReactConnections.js.map +1 -1
- package/build/map/vue_js/crossFileResolver.js +3 -2
- package/build/map/vue_js/crossFileResolver.js.map +1 -1
- package/build/map/vue_js/getViteConnections.js +5 -4
- package/build/map/vue_js/getViteConnections.js.map +1 -1
- package/build/map/vue_js/taint_utils.js +2 -1
- package/build/map/vue_js/taint_utils.js.map +1 -1
- package/build/map/vue_js/vue_resolveFetch.js +3 -2
- package/build/map/vue_js/vue_resolveFetch.js.map +1 -1
- package/build/map/vue_js/vue_resolveHttpClient.js +3 -2
- package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -1
- package/build/map/vue_js/vue_resolveXhr.js +3 -2
- package/build/map/vue_js/vue_resolveXhr.js.map +1 -1
- package/build/refactor/index.js +399 -60
- package/build/refactor/index.js.map +1 -1
- package/build/refactor/next/helpers.js +194 -13
- package/build/refactor/next/helpers.js.map +1 -1
- package/build/refactor/next/index.js +223 -36
- package/build/refactor/next/index.js.map +1 -1
- package/build/refactor/next/transform.js +905 -55
- package/build/refactor/next/transform.js.map +1 -1
- package/build/refactor/react/index.js +3 -3
- package/build/refactor/react/index.js.map +1 -1
- package/build/refactor/react/transform.js +2 -1
- package/build/refactor/react/transform.js.map +1 -1
- package/build/refactor/react/validator.js +2 -1
- package/build/refactor/react/validator.js.map +1 -1
- package/build/refactor/react-vite/index.js +39 -5
- package/build/refactor/react-vite/index.js.map +1 -1
- package/build/refactor/react-vite/vendor-analyze.js +3 -3
- package/build/refactor/react-vite/vendor-analyze.js.map +1 -1
- package/build/refactor/remote/config.js +17 -1
- package/build/refactor/remote/config.js.map +1 -1
- package/build/refactor/remote/hf-client.js +27 -2
- package/build/refactor/remote/hf-client.js.map +1 -1
- package/build/refactor/remote/version-detect.js +355 -0
- package/build/refactor/remote/version-detect.js.map +1 -0
- package/build/refactor/vue/index.js +176 -0
- package/build/refactor/vue/index.js.map +1 -0
- package/build/refactor/vue/vendor-analyze-vue.js +271 -0
- package/build/refactor/vue/vendor-analyze-vue.js.map +1 -0
- package/build/refactor/vue/vite.js +216 -0
- package/build/refactor/vue/vite.js.map +1 -0
- package/build/report/utility/genHtml.js +14 -3
- package/build/report/utility/genHtml.js.map +1 -1
- package/build/run/bundler-detect.js +140 -0
- package/build/run/bundler-detect.js.map +1 -0
- package/build/run/index.js +91 -5
- package/build/run/index.js.map +1 -1
- package/build/strings/index.js +6 -2
- package/build/strings/index.js.map +1 -1
- package/build/strings/trufflehog.js +71 -0
- package/build/strings/trufflehog.js.map +1 -0
- package/build/utility/makeReq.js +10 -9
- package/build/utility/makeReq.js.map +1 -1
- package/build/utility/postmanGenerator.js +2 -2
- package/build/utility/postmanGenerator.js.map +1 -1
- package/build/utility/puppeteerInstance.js +7 -1
- package/build/utility/puppeteerInstance.js.map +1 -1
- package/package.json +9 -5
- package/scripts/smoke-test.js +74 -0
package/CLAUDE.md
CHANGED
|
@@ -77,9 +77,9 @@ npm run start -- <subcommand> [options]
|
|
|
77
77
|
1. **Lazyload** — downloads initial JS chunks via Puppeteer; detects framework; sets `globalsUtil.getTech()` to `"next"`
|
|
78
78
|
2. **Strings** — scans downloaded JS for strings, extracts URL paths → `extracted_urls.json`
|
|
79
79
|
3. **Lazyload (subsequent requests)** — re-crawls using the extracted paths to fetch dynamically loaded chunks; also fetches `buildId`
|
|
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`)
|
|
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`)
|
|
81
81
|
5. **Lazyload re-pass (step 4.5)** — a second subsequent-requests crawl to pick up chunks for dynamic routes discovered in pass 2
|
|
82
|
-
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
|
|
83
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`
|
|
84
84
|
8. **Endpoints** — extracts client-side route paths; uses `___subsequent_requests` directory presence to decide whether to pass a JS directory
|
|
85
85
|
9. **Analyze** — loads YAML rules (from `-r/--rules` if provided, otherwise default rules cache); runs AST engine and request engine; writes `analyze.json`
|
|
@@ -107,7 +107,7 @@ npm run start -- <subcommand> [options]
|
|
|
107
107
|
|
|
108
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.
|
|
109
109
|
|
|
110
|
-
**`__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
|
|
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.
|
|
111
111
|
|
|
112
112
|
### Batch mode
|
|
113
113
|
|
|
@@ -200,6 +200,7 @@ Rule categories:
|
|
|
200
200
|
|
|
201
201
|
- `ast/` — AST-based pattern matching against chunk code (uses `@babel/parser` + `esquery`)
|
|
202
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.
|
|
203
204
|
|
|
204
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.
|
|
205
206
|
|
|
@@ -207,11 +208,137 @@ When `-r` points to a single file, only that rule is loaded. When it points to a
|
|
|
207
208
|
|
|
208
209
|
**Testing is mandatory for every change.** Before reporting a task complete:
|
|
209
210
|
|
|
210
|
-
1. Run `npm
|
|
211
|
-
2. Run
|
|
212
|
-
3.
|
|
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.
|
|
213
215
|
|
|
214
|
-
|
|
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:
|
|
215
342
|
|
|
216
343
|
```bash
|
|
217
344
|
npm run cleanup && npm run start -- run -u <target-url> -y -k
|
|
@@ -274,26 +401,44 @@ Before writing any files, gather the current state:
|
|
|
274
401
|
|
|
275
402
|
Previous tag is left to GitHub's automatic detection (do not set `--target` or `--tag` beyond the tag name itself).
|
|
276
403
|
|
|
277
|
-
9. **Wait for npm publish** — monitor the release pipeline: `gh run list --repo shriyanss/js-recon --workflow
|
|
404
|
+
9. **Wait for npm stage publish** — monitor the release pipeline: `gh run list --repo shriyanss/js-recon --workflow "Publish JS Recon"`. `publish-npm` uses OIDC trusted publishing (`npm stage publish`, no token) to _stage_ the release — this is NOT the same as it being live.
|
|
405
|
+
|
|
406
|
+
10. **Approve the staged release** — npm's staged-publish approval always requires interactive 2FA, so this step can never be automated or scripted:
|
|
278
407
|
|
|
279
|
-
|
|
408
|
+
- Find the stage id: `npm stage list @shriyanss/js-recon` (or the "Staged Packages" tab on npmjs.com)
|
|
409
|
+
- Approve it: `npm stage approve <stage-id>` (prompts for 2FA), or click "Approve" on npmjs.com
|
|
410
|
+
- Confirm it's live: `npm view @shriyanss/js-recon@<version>`
|
|
280
411
|
|
|
281
|
-
|
|
412
|
+
11. **Manually trigger the promote workflow** — once the release is live, run `promote-js-recon.yml` to update Homebrew and publish the Docker/GHCR images:
|
|
282
413
|
|
|
283
|
-
|
|
414
|
+
```bash
|
|
415
|
+
gh workflow run promote-js-recon.yml --repo shriyanss/js-recon -f version=<version>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
This workflow installs js-recon from the published npm registry artifact (`npm pack`/`npm install <pkg>@<version>`) rather than building from git source — an additional supply-chain check that the shipped images/formula match exactly what was approved on npm. Monitor: `gh run list --repo shriyanss/js-recon --workflow "Promote JS Recon Release"`.
|
|
419
|
+
|
|
420
|
+
### Homebrew tap (manual, part of `promote-js-recon.yml`)
|
|
421
|
+
|
|
422
|
+
The `update-homebrew-tap` job (now in `promote-js-recon.yml`, triggered per step 11 above):
|
|
423
|
+
|
|
424
|
+
1. `npm pack @shriyanss/js-recon@<version>` — downloads the exact published tarball from the registry and computes its SHA256 locally (no dependency on a public tarball URL being reachable yet)
|
|
284
425
|
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)
|
|
285
|
-
3. Updates `
|
|
426
|
+
3. Updates `url` and `sha256` in `Formula/js-recon.rb` via anchored `sed` — the formula has no explicit `version` field; Homebrew derives it from the `url`
|
|
286
427
|
4. Commits `chore: update js-recon formula to <version>` and pushes
|
|
287
428
|
|
|
288
429
|
Monitor: `gh run list --repo shriyanss/homebrew-tap --workflow ci.yml`
|
|
289
430
|
|
|
290
|
-
**If the job fails:**
|
|
431
|
+
**If the job fails:** manually update: `npm pack @shriyanss/js-recon@<version> && sha256sum shriyanss-js-recon-<version>.tgz`, edit `Formula/js-recon.rb`, commit, and push to `shriyanss/homebrew-tap`.
|
|
291
432
|
|
|
292
433
|
**One-time setup** (must be done before the first release, already completed):
|
|
293
434
|
|
|
294
435
|
- `shriyanss/homebrew-tap` is a public GitHub repo with the formula at `Formula/js-recon.rb`
|
|
295
436
|
- `HOMEBREW_TAP_TOKEN` is a fine-grained PAT stored in `shriyanss/js-recon` → Settings → Secrets → Actions, scoped exclusively to the `homebrew-tap` repo
|
|
296
437
|
|
|
438
|
+
### Docker / GHCR images (manual, part of `promote-js-recon.yml`)
|
|
439
|
+
|
|
440
|
+
`publish-docker` and `publish-ghcr` (now in `promote-js-recon.yml`) build from `Dockerfile.release` instead of the default `Dockerfile`. `Dockerfile.release` runs `npm install -g @shriyanss/js-recon@<version>` against the live registry rather than copying local source and building — the published images are provably built from the approved npm artifact. The default `Dockerfile` (source build) is unchanged and still used for local/dev builds.
|
|
441
|
+
|
|
297
442
|
### Phase 2 — js-recon-docs (after npm is live)
|
|
298
443
|
|
|
299
444
|
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.
|
|
@@ -384,6 +529,11 @@ When a user asks to fix or implement a GitHub issue, follow these steps:
|
|
|
384
529
|
- `--min-collisions <n>` — minimum occurrences to report (default: 2)
|
|
385
530
|
- `--co / --collision-output <file>` — write collision data to a file (independent of `--ct`)
|
|
386
531
|
- `--cf / --collision-format json|csv` — output format (default: csv)
|
|
532
|
+
- `--scat <categories>` — comma-separated scat categories to use (default: `lit,decl,loop,cond`). Overrides the fixed config for this run.
|
|
533
|
+
- `--sinc <nodes>` — comma-separated exact node types to include via sinc (e.g. `IfStatement`).
|
|
534
|
+
- `--all-scat-permutations` — run all 511 non-empty scat subsets and write one collision file per subset to `--perm-output`.
|
|
535
|
+
- `--perm-output <dir>` — output directory for per-permutation files (required with `--all-scat-permutations`).
|
|
536
|
+
- `--perm-concurrency <n>` — parallel permutation workers (default: half of CPU count).
|
|
387
537
|
|
|
388
538
|
**`--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.
|
|
389
539
|
|
|
@@ -395,15 +545,28 @@ When a user asks to fix or implement a GitHub issue, follow these steps:
|
|
|
395
545
|
npm run build
|
|
396
546
|
node build/index.js cs-mast -o output --ct --min-collisions 2
|
|
397
547
|
node build/index.js cs-mast -o output --co output --cf csv # writes ./collisions.csv
|
|
548
|
+
node build/index.js cs-mast -o output --all-scat-permutations --perm-output ./perm-out --cf json
|
|
398
549
|
```
|
|
399
550
|
|
|
400
551
|
## refactor
|
|
401
552
|
|
|
402
|
-
The `refactor` command supports
|
|
553
|
+
The `refactor` command supports the following techs:
|
|
403
554
|
|
|
404
555
|
- **`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`.
|
|
405
556
|
- **`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`.
|
|
406
557
|
- **`next`** — Next.js bundles (legacy).
|
|
558
|
+
- **`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`.
|
|
559
|
+
- **`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`.
|
|
560
|
+
- **`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`.
|
|
561
|
+
- **`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`.
|
|
562
|
+
|
|
563
|
+
### Known react-vite bugs (discovered 2026-07-01, test against js-recon-research/react/20-cve-app)
|
|
564
|
+
|
|
565
|
+
**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.
|
|
566
|
+
|
|
567
|
+
**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.
|
|
568
|
+
|
|
569
|
+
**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.
|
|
407
570
|
|
|
408
571
|
### refactor `--collisions <file>` (react-webpack only)
|
|
409
572
|
|
|
@@ -413,6 +576,37 @@ Plumbing: `src/index.ts` (CLI) → `src/refactor/index.ts` (resolves the path vi
|
|
|
413
576
|
|
|
414
577
|
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.
|
|
415
578
|
|
|
579
|
+
### refactor `--detect-version` (react-webpack, react-vite)
|
|
580
|
+
|
|
581
|
+
`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.
|
|
582
|
+
|
|
583
|
+
**Related flags:**
|
|
584
|
+
|
|
585
|
+
- `--detect-version-config <config>` — `"dynamic"` (default) or comma-separated scat categories (e.g. `lit,decl,loop,cond`).
|
|
586
|
+
- `--detect-version-dynamic-threshold <n>` — number of scat configs to use in dynamic mode (default: 3).
|
|
587
|
+
- `--detect-version-dynamic-conf-purge` — clears the cached dynamic scat config and recomputes.
|
|
588
|
+
|
|
589
|
+
**How it works:**
|
|
590
|
+
|
|
591
|
+
1. **Scat config resolution** (`resolveVersionDetectionScatDirs()` in `src/refactor/index.ts`):
|
|
592
|
+
- `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`.
|
|
593
|
+
- 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.
|
|
594
|
+
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.
|
|
595
|
+
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.
|
|
596
|
+
4. The version with the highest total match count is returned as the detected version.
|
|
597
|
+
5. The detected version's npm semver is used to pin `react` and `react-dom` in the refactored output's `package.json`.
|
|
598
|
+
|
|
599
|
+
**Caches:**
|
|
600
|
+
|
|
601
|
+
- Signature cache: `~/.js-recon/refactor/version_sigs_cache/<bundler>/<version>/<scatDir>/reliable_signatures.json` + `.cached_at` (7-day TTL).
|
|
602
|
+
- Dynamic config cache: `~/.js-recon/refactor/config.json` (`dynamicVersionDetectionScatConfig` field).
|
|
603
|
+
|
|
604
|
+
**Dataset coverage:** webpack (react-0.12 through react-19), vite (react-16 through react-19).
|
|
605
|
+
|
|
606
|
+
**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.
|
|
607
|
+
|
|
608
|
+
**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.
|
|
609
|
+
|
|
416
610
|
## Security / confidentiality
|
|
417
611
|
|
|
418
612
|
When a change is informed by behavior observed on a real target (URLs, endpoint names, response shapes, finding details, etc.):
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
FROM ghcr.io/puppeteer/puppeteer:25.3.0
|
|
2
|
+
|
|
3
|
+
WORKDIR /home/pptruser
|
|
4
|
+
|
|
5
|
+
USER root
|
|
6
|
+
ENV PUPPETEER_SKIP_DOWNLOAD=true
|
|
7
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
8
|
+
unzip \
|
|
9
|
+
libnspr4 libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 \
|
|
10
|
+
libxcomposite1 libxdamage1 libxrandr2 libgbm1 libxkbcommon0 \
|
|
11
|
+
libasound2 libpangocairo-1.0-0 libxfixes3 libxi6 libxinerama1 \
|
|
12
|
+
libxcursor1 libdrm2 && \
|
|
13
|
+
rm -rf /var/lib/apt/lists/*
|
|
14
|
+
|
|
15
|
+
ARG JS_RECON_VERSION
|
|
16
|
+
RUN npm install -g @shriyanss/js-recon@${JS_RECON_VERSION}
|
|
17
|
+
|
|
18
|
+
USER pptruser
|
|
19
|
+
RUN "$(npm root -g)/@shriyanss/js-recon/node_modules/.bin/puppeteer" browsers install chrome && \
|
|
20
|
+
for zip in /home/pptruser/.cache/puppeteer/chrome/*-chrome-linux64.zip; do \
|
|
21
|
+
[ -f "$zip" ] || break; \
|
|
22
|
+
version="${zip%-chrome-linux64.zip}"; version="${version##*/}"; \
|
|
23
|
+
dest="/home/pptruser/.cache/puppeteer/chrome/linux-${version}"; \
|
|
24
|
+
unzip -o "$zip" -d "${dest}/" && chmod +x "${dest}/chrome-linux64/chrome"; \
|
|
25
|
+
done
|
|
26
|
+
|
|
27
|
+
ENV IS_DOCKER=true
|
|
28
|
+
ENV NODE_OPTIONS="--max-http-header-size=99999999"
|
|
29
|
+
ENTRYPOINT ["js-recon"]
|
package/README.md
CHANGED
|
@@ -69,28 +69,29 @@ js-recon run -u https://app.example.com
|
|
|
69
69
|
|
|
70
70
|
`js-recon` provides a suite of commands for comprehensive JavaScript analysis. For detailed usage and examples, please refer to its full documentation.
|
|
71
71
|
|
|
72
|
-
| Command | Description
|
|
73
|
-
| ------------- |
|
|
74
|
-
| `lazyload` | Downloads dynamically loaded JavaScript files from a target.
|
|
75
|
-
| `endpoints` | Extracts API endpoints and client-side paths from JS files.
|
|
76
|
-
| `strings` | Extracts strings, URLs, and potential secrets from JS files.
|
|
77
|
-
| `map` | Maps function calls and analyzes code, with optional AI-powered descriptions.
|
|
78
|
-
| `api-gateway` | Manages AWS API Gateway for IP rotation to bypass rate limits.
|
|
79
|
-
| `run` | Runs all analysis modules automatically on a target.
|
|
80
|
-
| `analyze` | Analyzes the code.
|
|
81
|
-
| `report` | Generates a report from the analysis modules.
|
|
82
|
-
| `mcp` | AI-powered interactive CLI, one-shot chat, and MCP stdio server.
|
|
83
|
-
| `fingerprint` | Detects the JavaScript framework used by a target URL (JSON/JSONL output).
|
|
84
|
-
| `refactor` |
|
|
85
|
-
| `load` | Populates the response cache from a Caido or Burp Suite export.
|
|
86
|
-
| `cs-mast` | Computes CS-MAST structural hashes for JS files and finds hash collisions.
|
|
87
|
-
| `sourcemaps` | Extracts original source files from `.map` sourcemap files.
|
|
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) |
|
|
88
89
|
|
|
89
90
|
## Key Features
|
|
90
91
|
|
|
91
92
|
- Downloads all dynamically loaded JS files (refered as `lazyload`) from website with supported frameworks
|
|
92
93
|
- Use API gateway to rotate IP addresses to bypass firewall
|
|
93
|
-
- 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`)
|
|
94
95
|
- Endpoints modules extracts client-side paths from the app
|
|
95
96
|
- Map feature analyzes the JS files and outputs it to a JSON file. An interactive mode can be then used to analyze it
|
|
96
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;
|
|
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;
|
|
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;
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveFunctionIdentifier.js","sourceRoot":"","sources":["../../../../src/analyze/helpers/engineHelpers/resolveFunctionIdentifier.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,MAAM,iBAAiB,CAAC;AACxC,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"resolveFunctionIdentifier.js","sourceRoot":"","sources":["../../../../src/analyze/helpers/engineHelpers/resolveFunctionIdentifier.ts"],"names":[],"mappings":";AACA,OAAO,SAAS,MAAM,iBAAiB,CAAC;AACxC,MAAM,QAAQ,GAAG,CAAC,MAAA,SAAS,CAAC,OAAO,mCAAI,SAAS,CAA6B,CAAC;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,yBAAyB,CAAC,UAAgB,EAAE,GAAS;IACjE,IAAI,eAAiC,CAAC;IAEtC,IAAI,UAAU,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QACnC,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,QAAQ,CAAC,GAAG,EAAE;QACV,mBAAmB,CAAC,IAAI;YACpB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,EAAE,CAAC;gBACxD,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC;gBAC5B,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,CAAC;QACL,CAAC;QACD,kBAAkB,CAAC,IAAI;YACnB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,EAAE,CAAC;gBAC9E,IACI,IAAI,CAAC,IAAI,CAAC,IAAI;oBACd,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,yBAAyB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,oBAAoB,CAAC,EACrG,CAAC;oBACC,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;oBACjC,IAAI,CAAC,IAAI,EAAE,CAAC;gBAChB,CAAC;YACL,CAAC;QACL,CAAC;KACJ,CAAC,CAAC;IAEH,OAAO,eAAe,CAAC;AAC3B,CAAC"}
|