@shriyanss/js-recon 1.0.0 → 1.1.0-beta.2
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 +65 -0
- package/.github/workflows/npm-publish.yml +35 -0
- package/.prettierignore +2 -0
- package/.prettierrc +4 -0
- package/CHANGELOG.md +50 -0
- package/README.md +35 -139
- package/build/api_gateway/checkFeasibility.js +32 -0
- package/build/api_gateway/checkFeasibility.js.map +1 -0
- package/build/api_gateway/checkFireWallBlocking.js +24 -0
- package/build/api_gateway/checkFireWallBlocking.js.map +1 -0
- package/build/api_gateway/genReq.js +199 -0
- package/build/api_gateway/genReq.js.map +1 -0
- package/build/api_gateway/index.js +275 -0
- package/build/api_gateway/index.js.map +1 -0
- package/build/endpoints/gen_report/gen_json.js +22 -0
- package/build/endpoints/gen_report/gen_json.js.map +1 -0
- package/build/endpoints/gen_report/gen_markdown.js +66 -0
- package/build/endpoints/gen_report/gen_markdown.js.map +1 -0
- package/build/endpoints/gen_report/utility/iterate_n_store.js +46 -0
- package/build/endpoints/gen_report/utility/iterate_n_store.js.map +1 -0
- package/build/endpoints/index.js +89 -0
- package/build/endpoints/index.js.map +1 -0
- package/build/endpoints/next_js/client_jsFilesHref.js +91 -0
- package/build/endpoints/next_js/client_jsFilesHref.js.map +1 -0
- package/build/endpoints/next_js/client_jsonParse.js +78 -0
- package/build/endpoints/next_js/client_jsonParse.js.map +1 -0
- package/build/endpoints/next_js/client_subsequentRequests.js +199 -0
- package/build/endpoints/next_js/client_subsequentRequests.js.map +1 -0
- package/build/endpoints/next_js/getWebpacks.js +45 -0
- package/build/endpoints/next_js/getWebpacks.js.map +1 -0
- package/build/globalConfig.js +11 -0
- package/build/globalConfig.js.map +1 -0
- package/build/index.js +166 -0
- package/build/index.js.map +1 -0
- package/build/lazyLoad/downloadFilesUtil.js +128 -0
- package/build/lazyLoad/downloadFilesUtil.js.map +1 -0
- package/build/lazyLoad/downloadLoadedJsUtil.js +51 -0
- package/build/lazyLoad/downloadLoadedJsUtil.js.map +1 -0
- package/build/lazyLoad/globals.js +22 -0
- package/build/lazyLoad/globals.js.map +1 -0
- package/build/lazyLoad/index.js +170 -0
- package/build/lazyLoad/index.js.map +1 -0
- package/build/lazyLoad/next_js/next_GetJSScript.js +94 -0
- package/build/lazyLoad/next_js/next_GetJSScript.js.map +1 -0
- package/build/lazyLoad/next_js/next_GetLazyResources.js +202 -0
- package/build/lazyLoad/next_js/next_GetLazyResources.js.map +1 -0
- package/build/lazyLoad/next_js/next_SubsequentRequests.js +120 -0
- package/build/lazyLoad/next_js/next_SubsequentRequests.js.map +1 -0
- package/build/lazyLoad/nuxt_js/nuxt_astParse.js +188 -0
- package/build/lazyLoad/nuxt_js/nuxt_astParse.js.map +1 -0
- package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +75 -0
- package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js.map +1 -0
- package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +94 -0
- package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js.map +1 -0
- package/build/lazyLoad/svelte/svelte_getFromPageSource.js +68 -0
- package/build/lazyLoad/svelte/svelte_getFromPageSource.js.map +1 -0
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +95 -0
- package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -0
- package/build/map/index.js +58 -0
- package/build/map/index.js.map +1 -0
- package/build/map/next_js/getFetchInstances.js +108 -0
- package/build/map/next_js/getFetchInstances.js.map +1 -0
- package/build/map/next_js/getWebpackConnections.js +227 -0
- package/build/map/next_js/getWebpackConnections.js.map +1 -0
- package/build/map/next_js/interactive.js +32 -0
- package/build/map/next_js/interactive.js.map +1 -0
- package/build/map/next_js/interactive_helpers/commandHandler.js +190 -0
- package/build/map/next_js/interactive_helpers/commandHandler.js.map +1 -0
- package/build/map/next_js/interactive_helpers/commandHelpers.js +91 -0
- package/build/map/next_js/interactive_helpers/commandHelpers.js.map +1 -0
- package/build/map/next_js/interactive_helpers/helpMenu.js +11 -0
- package/build/map/next_js/interactive_helpers/helpMenu.js.map +1 -0
- package/build/map/next_js/interactive_helpers/keybindings.js +80 -0
- package/build/map/next_js/interactive_helpers/keybindings.js.map +1 -0
- package/build/map/next_js/interactive_helpers/printer.js +17 -0
- package/build/map/next_js/interactive_helpers/printer.js.map +1 -0
- package/build/map/next_js/interactive_helpers/ui.js +81 -0
- package/build/map/next_js/interactive_helpers/ui.js.map +1 -0
- package/build/map/next_js/resolveFetch.js +201 -0
- package/build/map/next_js/resolveFetch.js.map +1 -0
- package/build/run/index.js +62 -0
- package/build/run/index.js.map +1 -0
- package/build/strings/index.js +238 -0
- package/build/strings/index.js.map +1 -0
- package/build/strings/openapi.js +55 -0
- package/build/strings/openapi.js.map +1 -0
- package/build/strings/permutate.js +55 -0
- package/build/strings/permutate.js.map +1 -0
- package/build/strings/secrets.js +89 -0
- package/build/strings/secrets.js.map +1 -0
- package/build/techDetect/index.js +229 -0
- package/build/techDetect/index.js.map +1 -0
- package/build/utility/ai.js +69 -0
- package/build/utility/ai.js.map +1 -0
- package/build/utility/globals.js +84 -0
- package/build/utility/globals.js.map +1 -0
- package/build/utility/interfaces.js +2 -0
- package/build/utility/interfaces.js.map +1 -0
- package/build/utility/makeReq.js +265 -0
- package/build/utility/makeReq.js.map +1 -0
- package/build/utility/resolvePath.js +44 -0
- package/build/utility/resolvePath.js.map +1 -0
- package/{utility → build/utility}/runSandboxed.js +10 -13
- package/build/utility/runSandboxed.js.map +1 -0
- package/{utility → build/utility}/urlUtils.js +9 -11
- package/build/utility/urlUtils.js.map +1 -0
- package/docs/CNAME +1 -0
- package/docs/README.md +20 -0
- package/docs/api-gateway.md +68 -0
- package/docs/endpoints.md +49 -0
- package/docs/example-scenario.md +258 -0
- package/docs/interactive-mode.md +76 -0
- package/docs/lazyload.md +56 -0
- package/docs/map.md +53 -0
- package/docs/run.md +54 -0
- package/docs/strings.md +75 -0
- package/package.json +50 -38
- package/api_gateway/checkFeasibility.js +0 -25
- package/api_gateway/checkFireWallBlocking.js +0 -17
- package/api_gateway/genReq.js +0 -214
- package/api_gateway/index.js +0 -325
- package/endpoints/index.js +0 -7
- package/globalConfig.js +0 -12
- package/index.js +0 -69
- package/lazyLoad/downloadFilesUtil.js +0 -122
- package/lazyLoad/downloadLoadedJsUtil.js +0 -54
- package/lazyLoad/globals.js +0 -15
- package/lazyLoad/index.js +0 -167
- package/lazyLoad/next_js/next_GetJSScript.js +0 -99
- package/lazyLoad/next_js/next_GetLazyResources.js +0 -201
- package/lazyLoad/next_js/next_SubsequentRequests.js +0 -138
- package/lazyLoad/nuxt_js/nuxt_astParse.js +0 -194
- package/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +0 -77
- package/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +0 -99
- package/research/firewall_bypass.md +0 -38
- package/research/next_js.md +0 -116
- package/research/nuxt_js.md +0 -125
- package/research/vue_js.md +0 -9
- package/strings/index.js +0 -145
- package/techDetect/index.js +0 -156
- package/utility/globals.js +0 -6
- package/utility/makeReq.js +0 -179
- package/utility/resolvePath.js +0 -43
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Build & Prettify Code
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- dev
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout
|
|
13
|
+
uses: actions/checkout@v4
|
|
14
|
+
with:
|
|
15
|
+
# Make sure the action checks out the repository to the pull request branch
|
|
16
|
+
ref: ${{ github.ref_name }}
|
|
17
|
+
|
|
18
|
+
- name: Set up Node.js
|
|
19
|
+
uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: "22"
|
|
22
|
+
cache: "npm"
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: npm ci
|
|
25
|
+
- name: Build code
|
|
26
|
+
run: npm run build
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
prettier:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
needs: build
|
|
32
|
+
permissions:
|
|
33
|
+
contents: write
|
|
34
|
+
steps:
|
|
35
|
+
- name: Checkout
|
|
36
|
+
uses: actions/checkout@v4
|
|
37
|
+
with:
|
|
38
|
+
# Make sure the action checks out the repository to the pull request branch
|
|
39
|
+
ref: ${{ github.ref_name }}
|
|
40
|
+
|
|
41
|
+
- name: Set up Node.js
|
|
42
|
+
uses: actions/setup-node@v4
|
|
43
|
+
with:
|
|
44
|
+
node-version: "22"
|
|
45
|
+
cache: "npm"
|
|
46
|
+
|
|
47
|
+
- name: Install dependencies
|
|
48
|
+
run: npm ci
|
|
49
|
+
|
|
50
|
+
- name: Install Prettier
|
|
51
|
+
run: npm install -g prettier
|
|
52
|
+
|
|
53
|
+
- name: Run Prettier
|
|
54
|
+
run: prettier --write . --tab-width 4 --trailing-comma es5 --no-color
|
|
55
|
+
|
|
56
|
+
- name: Remove node_modules
|
|
57
|
+
run: rm -rf node_modules
|
|
58
|
+
|
|
59
|
+
- name: Commit and push changes
|
|
60
|
+
run: |
|
|
61
|
+
git config user.name "prettier-action[bot]"
|
|
62
|
+
git config user.email "prettier-action[bot]@users.noreply.github.com"
|
|
63
|
+
git add .
|
|
64
|
+
git commit -m "chore: prettify code" || echo "No changes to commit"
|
|
65
|
+
git push
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
|
|
3
|
+
|
|
4
|
+
name: Node.js Package
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
release:
|
|
8
|
+
types: [created]
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 22
|
|
18
|
+
- run: npm ci
|
|
19
|
+
- run: npm run build
|
|
20
|
+
- run: npm run test
|
|
21
|
+
|
|
22
|
+
publish-npm:
|
|
23
|
+
needs: build
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-node@v4
|
|
28
|
+
with:
|
|
29
|
+
node-version: 22
|
|
30
|
+
registry-url: https://registry.npmjs.org/
|
|
31
|
+
- run: npm ci
|
|
32
|
+
- run: npm run build
|
|
33
|
+
- run: npm publish
|
|
34
|
+
env:
|
|
35
|
+
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
package/.prettierignore
ADDED
package/.prettierrc
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
## 1.1.0-beta.2 - 2025.07.07
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Fix build errors (type errors)
|
|
12
|
+
|
|
13
|
+
## 1.1.0-beta.1 - 2025.07.07
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Svelte framework detection, and JS extraction
|
|
18
|
+
- JSON-based file cache
|
|
19
|
+
- Auto-approve executing JS code (`-y`/`--yes`)
|
|
20
|
+
- Added max threads for downloading JS files `-t`/`--threads`
|
|
21
|
+
- Add timeout handling and network idle check for page load in tech detection
|
|
22
|
+
- Parse script tags to extract additional JavaScript URLs from page
|
|
23
|
+
- Add secret scanning
|
|
24
|
+
- Add feasibility check to API gateway function
|
|
25
|
+
- Add endpoints module to support client-side path extraction
|
|
26
|
+
- Add subsequent-requests feature (`RSC: 1`) in Next.JS
|
|
27
|
+
- Add map module
|
|
28
|
+
- Webpack chunk parsing
|
|
29
|
+
- Interactive mode
|
|
30
|
+
- `fetch()` detection
|
|
31
|
+
- Permutation in `strings` module
|
|
32
|
+
- OpenAI and Ollama integration for AI descriptions
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- Standardize UTF-8 encoding and improve URL path handling in lazy load module
|
|
39
|
+
|
|
40
|
+
## 1.0.0 - 2025.06.18
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- Lazy Load Support for Next.js and Nuxt.js
|
|
45
|
+
- JavaScript String Extraction from downloaded JS files
|
|
46
|
+
- API Gateway Proxying to rotate IP addresses
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
### Fixed
|
package/README.md
CHANGED
|
@@ -1,166 +1,62 @@
|
|
|
1
|
-
#
|
|
2
|
-
## Installation
|
|
3
|
-
To install the tool, run:
|
|
4
|
-
```bash
|
|
5
|
-
npm i -g @shriyanss/js-recon
|
|
6
|
-
```
|
|
7
|
-
|
|
8
|
-
## Usage
|
|
9
|
-
```
|
|
10
|
-
$ js-recon -h
|
|
11
|
-
Usage: js-recon [options] [command]
|
|
1
|
+
# JS Recon
|
|
12
2
|
|
|
13
|
-
|
|
3
|
+
     
|
|
14
4
|
|
|
15
|
-
|
|
16
|
-
-V, --version output the version number
|
|
17
|
-
-h, --help display help for command
|
|
18
|
-
|
|
19
|
-
Commands:
|
|
20
|
-
lazyload [options] Run lazy load module
|
|
21
|
-
endpoints [options] Extract API endpoints
|
|
22
|
-
strings [options] Extract strings from JS files
|
|
23
|
-
api-gateway [options] Configure AWS API Gateway to rotate IP addresses
|
|
24
|
-
help [command] display help for command
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### Lazy load
|
|
28
|
-
```
|
|
29
|
-
$ js-recon lazyload -h
|
|
30
|
-
Usage: js-recon lazyload [options]
|
|
31
|
-
|
|
32
|
-
Run lazy load module
|
|
33
|
-
|
|
34
|
-
Options:
|
|
35
|
-
-u, --url <url/file> Target URL or a file containing a list of URLs (one per line)
|
|
36
|
-
-o, --output <directory> Output directory (default: "output")
|
|
37
|
-
--strict-scope Download JS files from only the input URL domain (default: false)
|
|
38
|
-
-s, --scope <scope> Download JS files from specific domains (comma-separated) (default: "*")
|
|
39
|
-
-t, --threads <threads> Number of threads to use (default: 1)
|
|
40
|
-
--subsequent-requests Download JS files from subsequent requests (default: false)
|
|
41
|
-
--urls-file <file> Input JSON file containing URLs (default: "extracted_urls.json")
|
|
42
|
-
--api-gateway Generate requests using API Gateway (default: false)
|
|
43
|
-
--api-gateway-config <file> API Gateway config file (default: ".api_gateway_config.json")
|
|
44
|
-
-h, --help display help for command
|
|
45
|
-
```
|
|
5
|
+
A powerful tool for JavaScript reconnaissance. `js-recon` helps you discover, download, and analyze JavaScript files to uncover endpoints, secrets, and other valuable information from any web application running supported frameworks
|
|
46
6
|
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
$ js-recon strings -h
|
|
50
|
-
Usage: js-recon strings [options]
|
|
51
|
-
|
|
52
|
-
Extract strings from JS files
|
|
53
|
-
|
|
54
|
-
Options:
|
|
55
|
-
-d, --directory <directory> Directory containing JS files
|
|
56
|
-
-o, --output <file> JSON file to save the strings (default: "strings.json")
|
|
57
|
-
-e, --extract-urls Extract URLs from strings (default: false)
|
|
58
|
-
--extracted-url-path <file> Output JSON file for extracted URLs and paths (default: "extracted_urls.json")
|
|
59
|
-
-h, --help display help for command
|
|
60
|
-
```
|
|
7
|
+
## Installation
|
|
61
8
|
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
$ js-recon api-gateway -h
|
|
65
|
-
Usage: js-recon api-gateway [options]
|
|
66
|
-
|
|
67
|
-
Configure AWS API Gateway to rotate IP addresses
|
|
68
|
-
|
|
69
|
-
Options:
|
|
70
|
-
-i, --init Initialize the config file (create API) (default: false)
|
|
71
|
-
-d, --destroy <id> Destroy API with the given ID
|
|
72
|
-
--destroy-all Destroy all the API created by this tool in all regions (default: false)
|
|
73
|
-
-r, --region <region> AWS region (default: random region)
|
|
74
|
-
-a, --access-key <access-key> AWS access key (if not provided, AWS_ACCESS_KEY_ID environment variable will be used)
|
|
75
|
-
-s, --secret-key <secret-key> AWS secret key (if not provided, AWS_SECRET_ACCESS_KEY environment variable will be used)
|
|
76
|
-
-c, --config <config> Name of the config file (default: ".api_gateway_config.json")
|
|
77
|
-
-l, --list List all the API created by this tool (default: false)
|
|
78
|
-
--feasibility Check feasibility of API Gateway (default: false)
|
|
79
|
-
--feasibility-url <url> URL to check feasibility of
|
|
80
|
-
-h, --help display help for command
|
|
81
|
-
```
|
|
9
|
+
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.
|
|
82
10
|
|
|
83
|
-
|
|
84
|
-
### Download lazy loaded JS files
|
|
85
|
-
You can download the lazy loaded files. To do so, you can run the following command
|
|
86
|
-
```bash
|
|
87
|
-
js-recon lazyload -u <url> -o <output>
|
|
88
|
-
```
|
|
11
|
+
To install the tool globally, run:
|
|
89
12
|
|
|
90
|
-
For example, you can try this with [Vercel Docs](https://vercel.com/docs):
|
|
91
13
|
```bash
|
|
92
|
-
|
|
14
|
+
npm i -g @shriyanss/js-recon
|
|
93
15
|
```
|
|
94
16
|
|
|
95
|
-
|
|
96
|
-
- Next.js (read research [here](research/next_js.md))
|
|
17
|
+
## Quick Start
|
|
97
18
|
|
|
98
|
-
### Extract strings from JS files
|
|
99
|
-
You can extract strings from JS files. To do so, you can run the following command
|
|
100
19
|
```bash
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
For example, you can try this with [1Password](https://1password.com):
|
|
105
|
-
```bash
|
|
106
|
-
js-recon strings -d output/1password.com -o strings.json
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Examples
|
|
110
|
-
### Get all possible JS files for a Next.js app
|
|
111
|
-
*You can read the full research for the same [here](research/next_js.md#lazy-loaded-files)*
|
|
20
|
+
# Get a list of all commands
|
|
21
|
+
js-recon --help
|
|
112
22
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
js-recon lazyload -u <url> -o <output> --strict-scope -t 1
|
|
23
|
+
# Get help for a specific command
|
|
24
|
+
js-recon <command> --help
|
|
116
25
|
```
|
|
117
26
|
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
js-recon strings -d <directory> -o <output> -e
|
|
121
|
-
```
|
|
27
|
+
To launch a quick assesment against a target, the `run` module can be used to automate other modules
|
|
122
28
|
|
|
123
|
-
Finally, parse those URLs and paths to get more JS files (note the `--subsequent-requests` flag apart from `--strict-scope` and `--threads`) [research](research/next_js.md#analysis-of-xai):
|
|
124
29
|
```bash
|
|
125
|
-
js-recon
|
|
30
|
+
js-recon run -u https://app.example.com
|
|
126
31
|
```
|
|
127
32
|
|
|
128
|
-
|
|
129
|
-
First of all, the user has to configure the API keys for AWS with right permissions to use the API Gateway module of the tool. The access key and the secret key can be stored in the environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` respectively. Alternatively, these can be provided as command line argument to the tool.
|
|
33
|
+
## Commands
|
|
130
34
|
|
|
131
|
-
|
|
132
|
-
```bash
|
|
133
|
-
js-recon api-gateway -i -r <region> -a <access-key> -s <secret-key>
|
|
134
|
-
```
|
|
135
|
-
If the region is not provided, the tool will select a random region from a pre-defined list. If the `-a` and `-s` flags aren't provided, it will default to the environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` respectively.
|
|
35
|
+
`js-recon` provides a suite of commands for comprehensive JavaScript analysis. For detailed usage and examples, please refer to its full documentation.
|
|
136
36
|
|
|
137
|
-
|
|
37
|
+
**_The `lazyload` module will work on Next.JS, Nuxt.JS, and Svelte apps. All other modules are only expected to work on Next.JS apps._**
|
|
138
38
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
39
|
+
| Command | Description | Documentation |
|
|
40
|
+
| ------------- | ----------------------------------------------------------------------------- | ---------------------------------- |
|
|
41
|
+
| `lazyload` | Downloads dynamically loaded JavaScript files from a target. | [Read Docs](./docs/lazyload.md) |
|
|
42
|
+
| `endpoints` | Extracts API endpoints and client-side paths from JS files. | [Read Docs](./docs/endpoints.md) |
|
|
43
|
+
| `strings` | Extracts strings, URLs, and potential secrets from JS files. | [Read Docs](./docs/strings.md) |
|
|
44
|
+
| `map` | Maps function calls and analyzes code, with optional AI-powered descriptions. | [Read Docs](./docs/map.md) |
|
|
45
|
+
| `api-gateway` | Manages AWS API Gateway for IP rotation to bypass rate limits. | [Read Docs](./docs/api-gateway.md) |
|
|
46
|
+
| `run` | Runs all analysis modules automatically on a target. | [Read Docs](./docs/run.md) |
|
|
143
47
|
|
|
144
|
-
|
|
48
|
+
## Key Features
|
|
145
49
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
50
|
+
- Downloads all dynamically loaded JS files (refered as `lazyload`) from website with supported frameworks
|
|
51
|
+
- Use API gateway to rotate IP addresses to bypass firewall
|
|
52
|
+
- Extract strings from the discovered JS files, and extract potential secrets, endpoints, etc. from them
|
|
53
|
+
- Endpoints modules extracts client-side paths from the app
|
|
54
|
+
- Map feature analyzes the JS files, and outputs it to a JSON file. An interactive mode can be then used to analyze it
|
|
150
55
|
|
|
151
|
-
|
|
56
|
+
## Example Scenario
|
|
152
57
|
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
js-recon lazyload -u <url> -o <output> --api-gateway
|
|
156
|
-
```
|
|
58
|
+
Refer to [this page](./docs/example-scenario.md) where an example scenario of running this tool against a target is demonstrated.
|
|
157
59
|
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
js-recon api-gateway -d <api-gateway-id>
|
|
161
|
-
```
|
|
60
|
+
## Documentation
|
|
162
61
|
|
|
163
|
-
|
|
164
|
-
```bash
|
|
165
|
-
js-recon api-gateway --destroy-all
|
|
166
|
-
```
|
|
62
|
+
For detailed guides, command options, and advanced usage examples, please check out the **[full documentation here](./docs/README.md)**.
|
|
@@ -0,0 +1,32 @@
|
|
|
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 { get } from "./genReq.js";
|
|
11
|
+
import chalk from "chalk";
|
|
12
|
+
import checkFireWallBlocking from "./checkFireWallBlocking.js";
|
|
13
|
+
const checkFeasibility = (url) => __awaiter(void 0, void 0, void 0, function* () {
|
|
14
|
+
console.log(chalk.cyan(`[i] Checking feasibility of API Gateway with ${url}`));
|
|
15
|
+
try {
|
|
16
|
+
// send 10 requests, and check if any of those contain any signs of blocking
|
|
17
|
+
for (let i = 0; i < 10; i++) {
|
|
18
|
+
const response = yield get(url);
|
|
19
|
+
const isFireWallBlocking = yield checkFireWallBlocking(response);
|
|
20
|
+
if (isFireWallBlocking) {
|
|
21
|
+
console.log(chalk.magenta("[!] Please try again without API Gateway"));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
console.log(chalk.green("[✓] Feasibility check passed."), chalk.dim("However, this doesn't represent the true nature of the firewall used."));
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
console.log(chalk.red(`[!] An error occured in feasibility check: ${error}`));
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
export default checkFeasibility;
|
|
32
|
+
//# sourceMappingURL=checkFeasibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkFeasibility.js","sourceRoot":"","sources":["../../src/api_gateway/checkFeasibility.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,qBAAqB,MAAM,4BAA4B,CAAC;AAE/D,MAAM,gBAAgB,GAAG,CAAO,GAAG,EAAE,EAAE;IACnC,OAAO,CAAC,GAAG,CACP,KAAK,CAAC,IAAI,CAAC,gDAAgD,GAAG,EAAE,CAAC,CACpE,CAAC;IACF,IAAI,CAAC;QACD,4EAA4E;QAC5E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC;YAChC,MAAM,kBAAkB,GAAG,MAAM,qBAAqB,CAAC,QAAQ,CAAC,CAAC;YACjE,IAAI,kBAAkB,EAAE,CAAC;gBACrB,OAAO,CAAC,GAAG,CACP,KAAK,CAAC,OAAO,CAAC,0CAA0C,CAAC,CAC5D,CAAC;gBACF,OAAO;YACX,CAAC;QACL,CAAC;QACD,OAAO,CAAC,GAAG,CACP,KAAK,CAAC,KAAK,CAAC,+BAA+B,CAAC,EAC5C,KAAK,CAAC,GAAG,CACL,uEAAuE,CAC1E,CACJ,CAAC;IACN,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CACP,KAAK,CAAC,GAAG,CAAC,8CAA8C,KAAK,EAAE,CAAC,CACnE,CAAC;IACN,CAAC;AACL,CAAC,CAAA,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
const checkFireWallBlocking = (body) => __awaiter(void 0, void 0, void 0, function* () {
|
|
12
|
+
// check common signs of CF first
|
|
13
|
+
if (body.includes("<title>Just a moment...</title>")) {
|
|
14
|
+
console.log(chalk.red("[!] Cloudflare detected"));
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
else if (body.includes("<title>Attention Required! | Cloudflare</title>")) {
|
|
18
|
+
console.log(chalk.red("[!] Cloudflare detected"));
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
});
|
|
23
|
+
export default checkFireWallBlocking;
|
|
24
|
+
//# sourceMappingURL=checkFireWallBlocking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkFireWallBlocking.js","sourceRoot":"","sources":["../../src/api_gateway/checkFireWallBlocking.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,qBAAqB,GAAG,CAAO,IAAY,EAAoB,EAAE;IACnE,iCAAiC;IACjC,IAAI,IAAI,CAAC,QAAQ,CAAC,iCAAiC,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IAChB,CAAC;SAAM,IACH,IAAI,CAAC,QAAQ,CAAC,iDAAiD,CAAC,EAClE,CAAC;QACC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC,CAAA,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,199 @@
|
|
|
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 { APIGatewayClient, CreateResourceCommand, GetResourcesCommand, PutMethodCommand, PutIntegrationCommand,
|
|
11
|
+
// CreateDeploymentCommand,
|
|
12
|
+
// CreateStageCommand,
|
|
13
|
+
PutIntegrationResponseCommand, PutMethodResponseCommand, TestInvokeMethodCommand, DeleteResourceCommand, } from "@aws-sdk/client-api-gateway";
|
|
14
|
+
import fs from "fs";
|
|
15
|
+
import md5 from "md5";
|
|
16
|
+
import chalk from "chalk";
|
|
17
|
+
import * as globals from "../utility/globals.js";
|
|
18
|
+
import checkFireWallBlocking from "./checkFireWallBlocking.js";
|
|
19
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
20
|
+
/**
|
|
21
|
+
* Given a URL, generates a new API Gateway for it and returns the response of the URL.
|
|
22
|
+
* @param {string} url The URL to generate an API Gateway for.
|
|
23
|
+
* @param {object} [headers] The headers to include in the request.
|
|
24
|
+
* @returns {Promise<string>} The response of the URL.
|
|
25
|
+
*/
|
|
26
|
+
const get = (url_1, ...args_1) => __awaiter(void 0, [url_1, ...args_1], void 0, function* (url, headers = {}) {
|
|
27
|
+
// read the config file
|
|
28
|
+
// Load and parse API Gateway config with error handling
|
|
29
|
+
let config;
|
|
30
|
+
try {
|
|
31
|
+
config = JSON.parse(fs.readFileSync(globals.apiGatewayConfigFile, "utf8"));
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw new Error(`Failed to read or parse API Gateway config file: ${error.message}`);
|
|
35
|
+
}
|
|
36
|
+
// select a random api gateway
|
|
37
|
+
let apiGateway = Object.keys(config)[Math.floor(Math.random() * Object.keys(config).length)];
|
|
38
|
+
const client = new APIGatewayClient({
|
|
39
|
+
region: config[apiGateway].region,
|
|
40
|
+
credentials: {
|
|
41
|
+
accessKeyId: config[apiGateway].access_key,
|
|
42
|
+
secretAccessKey: config[apiGateway].secret_key,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
// get the root resource id
|
|
46
|
+
const getResourceCommand = new GetResourcesCommand({
|
|
47
|
+
restApiId: config[apiGateway].id,
|
|
48
|
+
limit: 999999999,
|
|
49
|
+
});
|
|
50
|
+
const getResourceResponse = yield client.send(getResourceCommand);
|
|
51
|
+
yield sleep(200);
|
|
52
|
+
// before creating a resource, check if the resource already exists
|
|
53
|
+
const resourceExists = getResourceResponse.items.find(
|
|
54
|
+
// file deepcode ignore InsecureHash: False positive
|
|
55
|
+
(item) => item.pathPart === md5(url));
|
|
56
|
+
let newResourceResponse;
|
|
57
|
+
if (resourceExists) {
|
|
58
|
+
// console.log(chalk.yellow("[!] Resource already exists"));
|
|
59
|
+
newResourceResponse = {
|
|
60
|
+
id: resourceExists.id,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// create a new resource
|
|
65
|
+
let rootId;
|
|
66
|
+
if (getResourceResponse.items.find((item) => item.path === "/")) {
|
|
67
|
+
rootId = getResourceResponse.items.find((item) => item.path === "/").id;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
rootId = getResourceResponse.items[0].parentId;
|
|
71
|
+
}
|
|
72
|
+
const newResourceCommand = new CreateResourceCommand({
|
|
73
|
+
restApiId: config[apiGateway].id,
|
|
74
|
+
parentId: rootId,
|
|
75
|
+
pathPart: md5(url), // md5 of the url
|
|
76
|
+
});
|
|
77
|
+
newResourceResponse = yield client.send(newResourceCommand);
|
|
78
|
+
yield sleep(200);
|
|
79
|
+
// add a new method
|
|
80
|
+
const newMethodCommand = new PutMethodCommand({
|
|
81
|
+
restApiId: config[apiGateway].id,
|
|
82
|
+
resourceId: newResourceResponse.id,
|
|
83
|
+
httpMethod: "GET",
|
|
84
|
+
authorizationType: "NONE",
|
|
85
|
+
requestParameters: {
|
|
86
|
+
"method.request.header.RSC": false,
|
|
87
|
+
"method.request.header.User-Agent": false,
|
|
88
|
+
"method.request.header.Referer": false,
|
|
89
|
+
"method.request.header.Accept": false,
|
|
90
|
+
"method.request.header.Accept-Language": false,
|
|
91
|
+
"method.request.header.Accept-Encoding": false,
|
|
92
|
+
"method.request.header.Content-Type": false,
|
|
93
|
+
"method.request.header.Content-Length": false,
|
|
94
|
+
"method.request.header.Origin": false,
|
|
95
|
+
"method.request.header.X-Forwarded-For": false,
|
|
96
|
+
"method.request.header.X-Forwarded-Host": false,
|
|
97
|
+
"method.request.header.X-IP": false,
|
|
98
|
+
"method.request.header.X-Forwarded-Proto": false,
|
|
99
|
+
"method.request.header.X-Forwarded-Port": false,
|
|
100
|
+
"method.request.header.Sec-Fetch-Site": false,
|
|
101
|
+
"method.request.header.Sec-Fetch-Mode": false,
|
|
102
|
+
"method.request.header.Sec-Fetch-Dest": false,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
const newMethodResponse = yield client.send(newMethodCommand);
|
|
106
|
+
yield sleep(100);
|
|
107
|
+
// create new integration
|
|
108
|
+
const newIntegrationCommand = new PutIntegrationCommand({
|
|
109
|
+
restApiId: config[apiGateway].id,
|
|
110
|
+
resourceId: newResourceResponse.id,
|
|
111
|
+
httpMethod: "GET",
|
|
112
|
+
integrationHttpMethod: "GET",
|
|
113
|
+
type: "HTTP",
|
|
114
|
+
timeoutInMillis: 29000,
|
|
115
|
+
uri: url,
|
|
116
|
+
});
|
|
117
|
+
const newIntegrationResponse = yield client.send(newIntegrationCommand);
|
|
118
|
+
yield sleep(100);
|
|
119
|
+
// create a new method response
|
|
120
|
+
const newMethodResponseCommand = new PutMethodResponseCommand({
|
|
121
|
+
httpMethod: "GET",
|
|
122
|
+
resourceId: newResourceResponse.id,
|
|
123
|
+
restApiId: config[apiGateway].id,
|
|
124
|
+
statusCode: "200",
|
|
125
|
+
});
|
|
126
|
+
const newMethodResponseResponse = yield client.send(newMethodResponseCommand);
|
|
127
|
+
yield sleep(100);
|
|
128
|
+
// put integration response
|
|
129
|
+
const putIntegrationResponseCommand = new PutIntegrationResponseCommand({
|
|
130
|
+
httpMethod: "GET",
|
|
131
|
+
resourceId: newResourceResponse.id,
|
|
132
|
+
restApiId: config[apiGateway].id,
|
|
133
|
+
statusCode: "200",
|
|
134
|
+
});
|
|
135
|
+
const putIntegrationResponseResponse = yield client.send(putIntegrationResponseCommand);
|
|
136
|
+
yield sleep(100);
|
|
137
|
+
}
|
|
138
|
+
// Generate dynamic stage name
|
|
139
|
+
// const dynamicStageName = `prod-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`;
|
|
140
|
+
// console.log(chalk.blue(`[*] Using dynamic stage name: ${dynamicStageName}`));
|
|
141
|
+
// Create a deployment
|
|
142
|
+
// const createDeploymentCommand = new CreateDeploymentCommand({
|
|
143
|
+
// restApiId: config[apiGateway].id,
|
|
144
|
+
// stageName: dynamicStageName,
|
|
145
|
+
// description: `Deployment for ${url} at ${new Date().toISOString()} to stage ${dynamicStageName}`,
|
|
146
|
+
// });
|
|
147
|
+
// const deploymentResponse = await client.send(createDeploymentCommand);
|
|
148
|
+
// console.log(chalk.green("[+] Deployment created:"), deploymentResponse.id);
|
|
149
|
+
// await sleep(100);
|
|
150
|
+
const testInvokeMethodQuery = new TestInvokeMethodCommand({
|
|
151
|
+
httpMethod: "GET",
|
|
152
|
+
resourceId: newResourceResponse.id,
|
|
153
|
+
restApiId: config[apiGateway].id,
|
|
154
|
+
headers: headers || {},
|
|
155
|
+
});
|
|
156
|
+
const testInvokeMethodResponse = yield client.send(testInvokeMethodQuery);
|
|
157
|
+
yield sleep(100);
|
|
158
|
+
const body = yield testInvokeMethodResponse.body;
|
|
159
|
+
// check if any firewall is there in the way
|
|
160
|
+
const isFireWallBlocking = yield checkFireWallBlocking(body);
|
|
161
|
+
// delete the resource
|
|
162
|
+
const deleteResourceCommand = new DeleteResourceCommand({
|
|
163
|
+
restApiId: config[apiGateway].id,
|
|
164
|
+
resourceId: newResourceResponse.id,
|
|
165
|
+
});
|
|
166
|
+
try {
|
|
167
|
+
yield client.send(deleteResourceCommand);
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
console.error(chalk.red(`[!] Error when sending delete resource command to AWS: ${err}`));
|
|
171
|
+
}
|
|
172
|
+
if (isFireWallBlocking) {
|
|
173
|
+
console.log(chalk.magenta("[!] Please try again without API Gateway"));
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
return body;
|
|
177
|
+
// create a new stage
|
|
178
|
+
// dynamicStageName = `prod-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`;
|
|
179
|
+
// const newStageCommand = new CreateStageCommand({
|
|
180
|
+
// restApiId: config[apiGateway].id,
|
|
181
|
+
// stageName: dynamicStageName, // Use dynamic stage name
|
|
182
|
+
// deploymentId: deploymentResponse.id, // Use the ID from the deployment
|
|
183
|
+
// cacheClusterEnabled: false,
|
|
184
|
+
// // cacheClusterSize: "0", // Removed as cacheClusterEnabled is false
|
|
185
|
+
// methodSettings: [
|
|
186
|
+
// {
|
|
187
|
+
// httpMethod: "*",
|
|
188
|
+
// // resourceId: "*", // This might need to be more specific if you don't want it for all resources
|
|
189
|
+
// throttlingBurstLimit: 5,
|
|
190
|
+
// throttlingRateLimit: 10,
|
|
191
|
+
// },
|
|
192
|
+
// ],
|
|
193
|
+
// });
|
|
194
|
+
// const newStageResponse = await client.send(newStageCommand);
|
|
195
|
+
// await sleep(100);
|
|
196
|
+
// console.log(newStageResponse);
|
|
197
|
+
});
|
|
198
|
+
export { get };
|
|
199
|
+
//# sourceMappingURL=genReq.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"genReq.js","sourceRoot":"","sources":["../../src/api_gateway/genReq.ts"],"names":[],"mappings":"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"}
|