@js-recon/js-recon 1.4.1-beta.1 → 1.4.1-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.
Files changed (75) hide show
  1. package/.claude-sync/capture-new.sh +61 -0
  2. package/.claude-sync/capture-scan.sh +55 -0
  3. package/.claude-sync/hooks/post-checkout +16 -0
  4. package/.claude-sync/hooks/pre-commit +3 -0
  5. package/.claude-sync/setup.sh +17 -0
  6. package/.claude-sync/sync.sh +64 -0
  7. package/.claude-sync/watch.sh +17 -0
  8. package/.github/workflows/build-and-prettify.yaml +40 -0
  9. package/.github/workflows/promote-js-recon.yml +129 -8
  10. package/.vscode/tasks.json +42 -0
  11. package/CHANGELOG.md +17 -0
  12. package/Dockerfile.w-rules +13 -0
  13. package/README.md +1 -1
  14. package/build/analyze/helpers/initRules.js +27 -5
  15. package/build/analyze/helpers/initRules.js.map +1 -1
  16. package/build/analyze/index.js +3 -2
  17. package/build/analyze/index.js.map +1 -1
  18. package/build/completion/index.js +17 -11
  19. package/build/completion/index.js.map +1 -1
  20. package/build/globalConfig.js +1 -1
  21. package/build/index.js +55 -23
  22. package/build/index.js.map +1 -1
  23. package/build/lazyLoad/generic/generic_jsMimeTypes.js +19 -0
  24. package/build/lazyLoad/generic/generic_jsMimeTypes.js.map +1 -0
  25. package/build/lazyLoad/index.js +1 -0
  26. package/build/lazyLoad/index.js.map +1 -1
  27. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +8 -2
  28. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
  29. package/build/lazyLoad/techDetect/checkInterceptedEvidence.js +18 -0
  30. package/build/lazyLoad/techDetect/checkInterceptedEvidence.js.map +1 -0
  31. package/build/lazyLoad/techDetect/index.js +58 -13
  32. package/build/lazyLoad/techDetect/index.js.map +1 -1
  33. package/build/mcp/mcpServer.js +1 -2
  34. package/build/mcp/mcpServer.js.map +1 -1
  35. package/build/mcp/tools.js +1 -2
  36. package/build/mcp/tools.js.map +1 -1
  37. package/build/proxy/awsConfig.js +45 -0
  38. package/build/proxy/awsConfig.js.map +1 -0
  39. package/build/proxy/checkFeasibility.js.map +1 -0
  40. package/build/proxy/checkFireWallBlocking.js.map +1 -0
  41. package/build/proxy/configFile.js +31 -0
  42. package/build/proxy/configFile.js.map +1 -0
  43. package/build/{api_gateway → proxy}/genReq.js +4 -5
  44. package/build/proxy/genReq.js.map +1 -0
  45. package/build/proxy/genericProxy.js +21 -0
  46. package/build/proxy/genericProxy.js.map +1 -0
  47. package/build/proxy/index.js +421 -0
  48. package/build/proxy/index.js.map +1 -0
  49. package/build/proxy/oxylabsProxy.js +34 -0
  50. package/build/proxy/oxylabsProxy.js.map +1 -0
  51. package/build/proxy/resolveProxyConfig.js +38 -0
  52. package/build/proxy/resolveProxyConfig.js.map +1 -0
  53. package/build/report/utility/initGlobalReportDb.js +111 -0
  54. package/build/report/utility/initGlobalReportDb.js.map +1 -0
  55. package/build/report/utility/mergeDomainIntoGlobalDb.js +39 -0
  56. package/build/report/utility/mergeDomainIntoGlobalDb.js.map +1 -0
  57. package/build/run/index.js +110 -74
  58. package/build/run/index.js.map +1 -1
  59. package/build/run/interruptHandler.js +17 -1
  60. package/build/run/interruptHandler.js.map +1 -1
  61. package/build/utility/configureProxy.js +60 -0
  62. package/build/utility/configureProxy.js.map +1 -0
  63. package/build/utility/globals.js +108 -13
  64. package/build/utility/globals.js.map +1 -1
  65. package/build/utility/makeReq.js +103 -10
  66. package/build/utility/makeReq.js.map +1 -1
  67. package/package.json +5 -1
  68. package/CLAUDE.md +0 -641
  69. package/build/api_gateway/checkFeasibility.js.map +0 -1
  70. package/build/api_gateway/checkFireWallBlocking.js.map +0 -1
  71. package/build/api_gateway/genReq.js.map +0 -1
  72. package/build/api_gateway/index.js +0 -291
  73. package/build/api_gateway/index.js.map +0 -1
  74. /package/build/{api_gateway → proxy}/checkFeasibility.js +0 -0
  75. /package/build/{api_gateway → proxy}/checkFireWallBlocking.js +0 -0
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env bash
2
+ # Intercepts newly-added real CLAUDE.md files at commit time, redirects their
3
+ # content into the sibling js-recon-agentic-files repo on the current branch,
4
+ # and replaces them with a symlink — so they never land in this repo's history.
5
+ set -euo pipefail
6
+
7
+ JS_RECON_ROOT="$(git rev-parse --show-toplevel)"
8
+ AGENTIC_REPO_PATH="${JSR_AGENTIC_FILES_PATH:-$JS_RECON_ROOT/../js-recon-agentic-files}"
9
+ CURRENT_BRANCH="$(git -C "$JS_RECON_ROOT" symbolic-ref --short HEAD)"
10
+
11
+ cd "$JS_RECON_ROOT"
12
+
13
+ staged_real_claude_md="$(git diff --cached --name-only --diff-filter=ACM | grep -E '(^|/)CLAUDE\.md$' || true)"
14
+
15
+ if [ -z "$staged_real_claude_md" ]; then
16
+ exit 0
17
+ fi
18
+
19
+ captured=0
20
+ while IFS= read -r f; do
21
+ [ -z "$f" ] && continue
22
+
23
+ if [ -L "$f" ]; then
24
+ continue
25
+ fi
26
+
27
+ dest="$AGENTIC_REPO_PATH/$f"
28
+ mkdir -p "$(dirname "$dest")"
29
+ cp "$JS_RECON_ROOT/$f" "$dest"
30
+
31
+ (
32
+ cd "$AGENTIC_REPO_PATH"
33
+ if ! git show-ref --verify --quiet "refs/heads/$CURRENT_BRANCH"; then
34
+ git fetch origin --quiet || true
35
+ if git show-ref --verify --quiet "refs/remotes/origin/$CURRENT_BRANCH"; then
36
+ git checkout -q "$CURRENT_BRANCH"
37
+ else
38
+ git checkout -q -b "$CURRENT_BRANCH"
39
+ fi
40
+ else
41
+ git checkout -q "$CURRENT_BRANCH"
42
+ fi
43
+ git add "$f"
44
+ git commit -q -m "sync: capture $f from js-recon@$CURRENT_BRANCH"
45
+ git push -q origin "$CURRENT_BRANCH"
46
+ )
47
+
48
+ git reset -q HEAD -- "$f" >/dev/null 2>&1 || true
49
+ rm -f "$JS_RECON_ROOT/$f"
50
+ ln -s "$dest" "$JS_RECON_ROOT/$f"
51
+ echo "[claude-sync] captured $f into js-recon-agentic-files (branch $CURRENT_BRANCH), replaced with symlink"
52
+ captured=1
53
+ done <<< "$staged_real_claude_md"
54
+
55
+ if [ "$captured" -eq 1 ]; then
56
+ echo "[claude-sync] commit aborted — CLAUDE.md file(s) were redirected to js-recon-agentic-files."
57
+ echo "[claude-sync] please 'git add' any remaining changes and re-run 'git commit'."
58
+ exit 1
59
+ fi
60
+
61
+ exit 0
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env bash
2
+ # Walks the working tree for real (non-symlink) CLAUDE.md / .claude/skills /
3
+ # .claude/agents files and captures each into the sibling js-recon-agentic-files
4
+ # repo on the current branch, replacing it with a symlink. This is the primary
5
+ # "new file" trigger — .gitignore refuses `git add` on these paths outright, so
6
+ # a git-staged-diff-based hook (see capture-new.sh) never sees them; a
7
+ # filesystem scan is required.
8
+ set -euo pipefail
9
+
10
+ JS_RECON_ROOT="$(git rev-parse --show-toplevel)"
11
+ AGENTIC_REPO_PATH="${JSR_AGENTIC_FILES_PATH:-$JS_RECON_ROOT/../js-recon-agentic-files}"
12
+
13
+ # Optional $1: attribute captures to this branch instead of the current HEAD.
14
+ # Used by the post-checkout hook, which only runs *after* HEAD has already
15
+ # moved to the new branch — git has no pre-checkout hook, so any real file
16
+ # left over from the branch being switched away from must be attributed to
17
+ # that branch explicitly, or it would be mis-captured onto the new branch.
18
+ CURRENT_BRANCH="${1:-$(git -C "$JS_RECON_ROOT" symbolic-ref --short HEAD 2>/dev/null || echo "")}"
19
+
20
+ if [ -z "$CURRENT_BRANCH" ] || [ ! -d "$AGENTIC_REPO_PATH/.git" ]; then
21
+ exit 0
22
+ fi
23
+
24
+ captured=0
25
+ while IFS= read -r f; do
26
+ [ -z "$f" ] && continue
27
+ rel="${f#$JS_RECON_ROOT/}"
28
+ dest="$AGENTIC_REPO_PATH/$rel"
29
+
30
+ mkdir -p "$(dirname "$dest")"
31
+ cp "$f" "$dest"
32
+
33
+ (
34
+ cd "$AGENTIC_REPO_PATH"
35
+ if git show-ref --verify --quiet "refs/heads/$CURRENT_BRANCH"; then
36
+ git checkout -q "$CURRENT_BRANCH"
37
+ elif git show-ref --verify --quiet "refs/remotes/origin/$CURRENT_BRANCH"; then
38
+ git checkout -q "$CURRENT_BRANCH"
39
+ else
40
+ git checkout -q -b "$CURRENT_BRANCH"
41
+ fi
42
+ git add "$rel"
43
+ git commit -q -m "sync: capture $rel from js-recon@$CURRENT_BRANCH"
44
+ git push -q origin "$CURRENT_BRANCH" 2>/dev/null || echo "[claude-sync] WARNING: push failed for $rel (branch may need 'git push -u origin $CURRENT_BRANCH' manually)"
45
+ )
46
+
47
+ rm -f "$f"
48
+ ln -s "$dest" "$f"
49
+ echo "[claude-sync] captured $rel into js-recon-agentic-files (branch $CURRENT_BRANCH), replaced with symlink"
50
+ captured=1
51
+ done < <(find "$JS_RECON_ROOT" \( -name "CLAUDE.md" -o -path "*/.claude/skills/*" -o -path "*/.claude/agents/*" \) -not -path "*/.git/*" -not -path "*/node_modules/*" -not -path "*/build/*" -not -path "$AGENTIC_REPO_PATH/*" -type f)
52
+
53
+ if [ "$captured" -eq 0 ]; then
54
+ exit 0
55
+ fi
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env bash
2
+ # git post-checkout hook: re-syncs CLAUDE.md symlinks whenever a branch switch happens.
3
+ prev_head="$1"
4
+ new_head="$2"
5
+ branch_checkout="$3"
6
+
7
+ if [ "$branch_checkout" = "1" ]; then
8
+ ROOT="$(git rev-parse --show-toplevel)"
9
+ # Resolve which branch prev_head belongs to (the branch we're leaving) so any
10
+ # leftover real CLAUDE.md file gets attributed correctly — HEAD has already
11
+ # moved to the new branch by the time this hook runs, and git has no
12
+ # pre-checkout hook to capture beforehand.
13
+ prev_branch="$(git for-each-ref --points-at="$prev_head" --format='%(refname:short)' refs/heads/ 2>/dev/null | head -1)"
14
+ "$ROOT/.claude-sync/capture-scan.sh" "$prev_branch"
15
+ "$ROOT/.claude-sync/sync.sh"
16
+ fi
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+ # git pre-commit hook: redirects newly-staged real CLAUDE.md files to js-recon-agentic-files.
3
+ exec "$(git rev-parse --show-toplevel)/.claude-sync/capture-new.sh"
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bash
2
+ # One-time collaborator setup: installs the git hooks and runs an initial sync.
3
+ # Requires js-recon-agentic-files to already be cloned as a sibling directory
4
+ # (../js-recon-agentic-files) or JSR_AGENTIC_FILES_PATH to point at it.
5
+ set -euo pipefail
6
+
7
+ JS_RECON_ROOT="$(git rev-parse --show-toplevel)"
8
+
9
+ chmod +x "$JS_RECON_ROOT/.claude-sync/sync.sh"
10
+ chmod +x "$JS_RECON_ROOT/.claude-sync/capture-new.sh"
11
+ chmod +x "$JS_RECON_ROOT/.claude-sync/hooks/post-checkout"
12
+ chmod +x "$JS_RECON_ROOT/.claude-sync/hooks/pre-commit"
13
+
14
+ git -C "$JS_RECON_ROOT" config core.hooksPath .claude-sync/hooks
15
+
16
+ echo "[claude-sync] hooksPath configured, running initial sync..."
17
+ "$JS_RECON_ROOT/.claude-sync/sync.sh"
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bash
2
+ # Syncs CLAUDE.md / .claude/skills / .claude/agents symlinks from the sibling
3
+ # js-recon-agentic-files checkout to match the currently checked-out branch of
4
+ # this repo.
5
+ set -euo pipefail
6
+
7
+ JS_RECON_ROOT="$(git rev-parse --show-toplevel)"
8
+ AGENTIC_REPO_PATH="${JSR_AGENTIC_FILES_PATH:-$JS_RECON_ROOT/../js-recon-agentic-files}"
9
+ FALLBACK_BRANCH="${JSR_AGENTIC_FALLBACK_BRANCH:-dev}"
10
+
11
+ CURRENT_BRANCH="$(git -C "$JS_RECON_ROOT" symbolic-ref --short HEAD 2>/dev/null || echo "")"
12
+
13
+ if [ -z "$CURRENT_BRANCH" ]; then
14
+ echo "[claude-sync] detached HEAD, skipping sync"
15
+ exit 0
16
+ fi
17
+
18
+ if [ ! -d "$AGENTIC_REPO_PATH/.git" ]; then
19
+ echo "[claude-sync] agentic-files repo not found at $AGENTIC_REPO_PATH — see js-recon-agentic-files/README.md for setup"
20
+ exit 0
21
+ fi
22
+
23
+ (
24
+ cd "$AGENTIC_REPO_PATH"
25
+ git fetch origin --quiet
26
+
27
+ if git show-ref --verify --quiet "refs/remotes/origin/$CURRENT_BRANCH"; then
28
+ git checkout -B "$CURRENT_BRANCH" "origin/$CURRENT_BRANCH" --quiet
29
+ else
30
+ echo "[claude-sync] WARNING: no 'origin/$CURRENT_BRANCH' branch in agentic-files repo — falling back to 'origin/$FALLBACK_BRANCH'"
31
+ git checkout -B "$CURRENT_BRANCH" "origin/$FALLBACK_BRANCH" --quiet
32
+ fi
33
+ )
34
+
35
+ # 1. Remove stale symlinks that point into the agentic checkout but whose target
36
+ # no longer exists on this branch (e.g. switched to a branch with fewer files).
37
+ while IFS= read -r link; do
38
+ target="$(readlink "$link")"
39
+ case "$target" in
40
+ "$AGENTIC_REPO_PATH"/*)
41
+ if [ ! -e "$target" ]; then
42
+ rm "$link"
43
+ echo "[claude-sync] removed stale symlink: ${link#$JS_RECON_ROOT/}"
44
+ fi
45
+ ;;
46
+ esac
47
+ done < <(find "$JS_RECON_ROOT" -type l \( -name "CLAUDE.md" -o -path "*/.claude/skills/*" -o -path "*/.claude/agents/*" \) -not -path "*/.git/*" -not -path "*/node_modules/*" -not -path "*/build/*")
48
+
49
+ # 2. Create/update symlinks for every CLAUDE.md (and .claude/skills / .claude/agents
50
+ # entry) present on this branch in the agentic checkout.
51
+ while IFS= read -r src; do
52
+ rel="${src#$AGENTIC_REPO_PATH/}"
53
+ dest="$JS_RECON_ROOT/$rel"
54
+
55
+ if [ -e "$dest" ] && [ ! -L "$dest" ]; then
56
+ echo "[claude-sync] WARNING: $rel exists as a real (non-symlink) file — not overwriting. Stage it to trigger capture, or remove it manually."
57
+ continue
58
+ fi
59
+
60
+ mkdir -p "$(dirname "$dest")"
61
+ ln -sf "$src" "$dest"
62
+ done < <(find "$AGENTIC_REPO_PATH" \( -name "CLAUDE.md" -o -path "*/.claude/skills/*" -o -path "*/.claude/agents/*" \) -type f -not -path "*/.git/*")
63
+
64
+ echo "[claude-sync] synced CLAUDE.md files for branch '$CURRENT_BRANCH'"
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bash
2
+ # Background watcher intended to be run as a VS Code task (runOn: folderOpen,
3
+ # isBackground: true). Polls for new real CLAUDE.md files every few seconds and
4
+ # captures them immediately via capture-scan.sh, so a file created interactively
5
+ # (e.g. by an editor or an agent session) gets redirected without waiting for the
6
+ # next branch switch or commit.
7
+ set -uo pipefail
8
+
9
+ ROOT="$(git rev-parse --show-toplevel)"
10
+ INTERVAL="${JSR_WATCH_INTERVAL:-3}"
11
+
12
+ echo "[claude-sync] watching for new CLAUDE.md files (interval: ${INTERVAL}s)"
13
+
14
+ while true; do
15
+ "$ROOT/.claude-sync/capture-scan.sh"
16
+ sleep "$INTERVAL"
17
+ done
@@ -169,6 +169,9 @@ jobs:
169
169
  with:
170
170
  repository: js-recon/js-recon-rules
171
171
  path: js-recon-rules
172
+ # Keep the checkout token out of js-recon-rules/.git/config — this
173
+ # context is consumed by later build/run steps (incl. Dockerfile.w-rules).
174
+ persist-credentials: false
172
175
 
173
176
  - name: Set up Node.js
174
177
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -225,6 +228,40 @@ jobs:
225
228
  path: output/localhost_3001/analyze.json
226
229
  if-no-files-found: ignore
227
230
 
231
+ - name: Build the rules-bundled (-w-rules) image
232
+ run: docker build -f Dockerfile.w-rules --build-arg BASE_IMAGE=js-recon/js-recon -t js-recon/js-recon-w-rules .
233
+
234
+ - name: Run js-recon-w-rules via Docker against lab app (no rules mount, no -r)
235
+ run: |
236
+ set -o pipefail
237
+ mkdir -p output-w-rules
238
+ docker run --name js-recon-w-rules-run \
239
+ --network host \
240
+ --entrypoint /bin/bash \
241
+ js-recon/js-recon-w-rules \
242
+ -c 'printf "http://localhost:3001\n" > /tmp/urls.txt && node build/index.js run -u /tmp/urls.txt --no-sandbox -y -k' \
243
+ | tee docker-w-rules-run.log
244
+ docker cp js-recon-w-rules-run:/home/pptruser/output/. ./output-w-rules/ 2>/dev/null || true
245
+ docker rm js-recon-w-rules-run
246
+
247
+ - name: Verify the version check was actually skipped
248
+ run: |
249
+ grep -q "Rules version check disabled" docker-w-rules-run.log || {
250
+ echo "Expected log line confirming JS_RECON_DISABLE_RULES_VERSION_CHECK was honored not found"
251
+ exit 1
252
+ }
253
+
254
+ - name: Verify all rules fired (w-rules image)
255
+ run: node scripts/smoke-test.js output-w-rules/localhost_3001/analyze.json
256
+
257
+ - name: Upload w-rules analyze.json on failure
258
+ if: failure()
259
+ uses: actions/upload-artifact@v4
260
+ with:
261
+ name: analyze-json-docker-w-rules
262
+ path: output-w-rules/localhost_3001/analyze.json
263
+ if-no-files-found: ignore
264
+
228
265
  integration-test-macos:
229
266
  needs: build
230
267
  runs-on: macos-latest
@@ -246,6 +283,9 @@ jobs:
246
283
  with:
247
284
  repository: js-recon/js-recon-rules
248
285
  path: js-recon-rules
286
+ # Keep the checkout token out of js-recon-rules/.git/config — this
287
+ # context is consumed by later build/run steps (incl. Dockerfile.w-rules).
288
+ persist-credentials: false
249
289
 
250
290
  - name: Set up Node.js
251
291
  uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
@@ -78,6 +78,15 @@ jobs:
78
78
  packages: write
79
79
  steps:
80
80
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
81
+ - name: Checkout js-recon-rules
82
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
83
+ with:
84
+ repository: js-recon/js-recon-rules
85
+ path: js-recon-rules
86
+ # Don't persist the checkout token into js-recon-rules/.git/config —
87
+ # Dockerfile.w-rules builds from this context and would otherwise bake
88
+ # the writable GHCR credential into an image layer.
89
+ persist-credentials: false
81
90
  - uses: docker/login-action@v3
82
91
  with:
83
92
  registry: ghcr.io
@@ -103,6 +112,22 @@ jobs:
103
112
  env:
104
113
  VERSION: ${{ inputs.version }}
105
114
 
115
+ - name: Build and push the rules-bundled (-w-rules) variant
116
+ run: |
117
+ # layer js-recon-rules on top of the image just built/pushed above
118
+ docker build -f Dockerfile.w-rules --build-arg BASE_IMAGE=ghcr.io/js-recon/js-recon:$VERSION -t ghcr.io/js-recon/js-recon:$VERSION-w-rules .
119
+
120
+ TAG="latest"
121
+ [[ "$VERSION" == *"beta"* ]] && TAG="beta"
122
+ [[ "$VERSION" == *"alpha"* ]] && TAG="alpha"
123
+
124
+ docker tag ghcr.io/js-recon/js-recon:$VERSION-w-rules ghcr.io/js-recon/js-recon:$TAG-w-rules
125
+
126
+ docker push ghcr.io/js-recon/js-recon:$VERSION-w-rules
127
+ docker push ghcr.io/js-recon/js-recon:$TAG-w-rules
128
+ env:
129
+ VERSION: ${{ inputs.version }}
130
+
106
131
  verify-npm-install:
107
132
  runs-on: ubuntu-latest
108
133
  permissions:
@@ -140,7 +165,7 @@ jobs:
140
165
  npm start &
141
166
 
142
167
  - name: Wait for app to be ready
143
- run: timeout 60 bash -c 'until curl -sf http://localhost:3001 > /dev/null; do sleep 2; done'
168
+ run: timeout 60 bash -c 'until curl -sf --max-time 2 http://localhost:3001 > /dev/null; do sleep 2; done'
144
169
 
145
170
  - name: Run installed js-recon against the lab app
146
171
  run: |
@@ -194,17 +219,23 @@ jobs:
194
219
  npm start &
195
220
 
196
221
  - name: Wait for app to be ready
197
- run: timeout 60 bash -c 'until curl -sf http://localhost:3001 > /dev/null; do sleep 2; done'
222
+ run: timeout 60 bash -c 'until curl -sf --max-time 2 http://localhost:3001 > /dev/null; do sleep 2; done'
198
223
 
199
224
  - name: Run containerized js-recon against the lab app
200
225
  run: |
226
+ # The release image runs as the non-root `pptruser` (see Dockerfile.release),
227
+ # so a bind-mounted host directory landing inside its home dir inherits the
228
+ # host-side owner/perms and can be unwritable to that user. Mount outside
229
+ # $HOME with permissive perms and point js-recon at it explicitly via `-o`
230
+ # rather than relying on its default CWD-relative output path.
201
231
  mkdir -p /tmp/docker-output
232
+ chmod 777 /tmp/docker-output
202
233
  echo "http://localhost:3001" > /tmp/docker-smoke-urls.txt
203
234
  docker run --rm --network host \
204
- -v /tmp/docker-output:/app/output \
205
- -v /tmp/docker-smoke-urls.txt:/app/urls.txt \
235
+ -v /tmp/docker-output:/tmp/js-recon-output \
236
+ -v /tmp/docker-smoke-urls.txt:/tmp/urls.txt \
206
237
  ghcr.io/js-recon/js-recon:${VERSION} \
207
- run -u /app/urls.txt --no-sandbox -y -k
238
+ run -u /tmp/urls.txt -o /tmp/js-recon-output --no-sandbox -y -k
208
239
  env:
209
240
  VERSION: ${{ inputs.version }}
210
241
 
@@ -213,6 +244,72 @@ jobs:
213
244
  test -n "$(find /tmp/docker-output -name analyze.json)" || { echo "analyze.json missing"; exit 1; }
214
245
  test -n "$(find /tmp/docker-output -name js-recon.db)" || { echo "js-recon.db missing"; exit 1; }
215
246
 
247
+ verify-docker-w-rules-install:
248
+ runs-on: ubuntu-latest
249
+ needs: publish-ghcr
250
+ permissions:
251
+ contents: read
252
+ packages: read
253
+ steps:
254
+ - name: Checkout js-recon
255
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
256
+
257
+ - name: Checkout js-recon-labs
258
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
259
+ with:
260
+ repository: js-recon/js-recon-labs
261
+ path: js-recon-labs
262
+ persist-credentials: false
263
+
264
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
265
+ with:
266
+ node-version: "22"
267
+
268
+ - uses: docker/login-action@v3
269
+ with:
270
+ registry: ghcr.io
271
+ username: ${{ github.actor }}
272
+ password: ${{ secrets.GITHUB_TOKEN }}
273
+
274
+ - name: Install and start vulnerable app
275
+ working-directory: js-recon-labs/next_js/vuln-all-rules
276
+ run: |
277
+ npm ci
278
+ npm run build
279
+ npm start &
280
+
281
+ - name: Wait for app to be ready
282
+ run: timeout 60 bash -c 'until curl -sf --max-time 2 http://localhost:3001 > /dev/null; do sleep 2; done'
283
+
284
+ - name: Run the rules-bundled image against the lab app (no rules mount, no -r)
285
+ run: |
286
+ set -o pipefail
287
+ # Deliberately don't mount any rules directory and don't pass -r — the
288
+ # -w-rules image must be able to analyze using only what's baked in.
289
+ mkdir -p /tmp/docker-w-rules-output
290
+ chmod 777 /tmp/docker-w-rules-output
291
+ echo "http://localhost:3001" > /tmp/docker-w-rules-smoke-urls.txt
292
+ docker run --network host \
293
+ --name js-recon-w-rules-run \
294
+ -v /tmp/docker-w-rules-output:/tmp/js-recon-output \
295
+ -v /tmp/docker-w-rules-smoke-urls.txt:/tmp/urls.txt \
296
+ ghcr.io/js-recon/js-recon:${VERSION}-w-rules \
297
+ run -u /tmp/urls.txt -o /tmp/js-recon-output --no-sandbox -y -k \
298
+ | tee /tmp/docker-w-rules-run.log
299
+ docker rm js-recon-w-rules-run
300
+ env:
301
+ VERSION: ${{ inputs.version }}
302
+
303
+ - name: Verify the version check was actually skipped
304
+ run: |
305
+ grep -q "Rules version check disabled" /tmp/docker-w-rules-run.log || {
306
+ echo "Expected log line confirming JS_RECON_DISABLE_RULES_VERSION_CHECK was honored not found"
307
+ exit 1
308
+ }
309
+
310
+ - name: Verify all rules fired
311
+ run: node scripts/smoke-test.js "$(find /tmp/docker-w-rules-output -name analyze.json | head -n1)"
312
+
216
313
  verify-brew-install:
217
314
  runs-on: macos-latest
218
315
  needs: update-homebrew-tap
@@ -249,9 +346,22 @@ jobs:
249
346
  env:
250
347
  VERSION: ${{ inputs.version }}
251
348
 
349
+ - name: Resolve js-recon binary path
350
+ id: bin
351
+ run: |
352
+ # js-recon-alpha/js-recon-beta are keg-only (they share the `js-recon`
353
+ # binary name with the stable formula), so brew never symlinks them
354
+ # into PATH. Resolve the keg path directly instead of relying on PATH.
355
+ KEG_BIN="$(brew --prefix)/opt/${{ matrix.formula }}/bin/js-recon"
356
+ if [[ -x "$KEG_BIN" ]]; then
357
+ echo "bin=$KEG_BIN" >> "$GITHUB_OUTPUT"
358
+ else
359
+ echo "bin=js-recon" >> "$GITHUB_OUTPUT"
360
+ fi
361
+
252
362
  - name: Verify installed version
253
363
  run: |
254
- INSTALLED=$(js-recon -V)
364
+ INSTALLED=$("$BIN" -V)
255
365
  echo "Installed: ${INSTALLED}"
256
366
  if [[ "${{ matrix.formula }}" == "${{ steps.expected.outputs.formula }}" ]]; then
257
367
  [[ "$INSTALLED" == *"$VERSION"* ]] || { echo "Version mismatch: expected ${VERSION} in ${{ matrix.formula }}"; exit 1; }
@@ -260,6 +370,7 @@ jobs:
260
370
  fi
261
371
  env:
262
372
  VERSION: ${{ inputs.version }}
373
+ BIN: ${{ steps.bin.outputs.bin }}
263
374
 
264
375
  - name: Install and start vulnerable app
265
376
  working-directory: js-recon-labs/next_js/vuln-all-rules
@@ -269,12 +380,22 @@ jobs:
269
380
  npm start &
270
381
 
271
382
  - name: Wait for app to be ready
272
- run: timeout 60 bash -c 'until curl -sf http://localhost:3001 > /dev/null; do sleep 2; done'
383
+ run: |
384
+ SECONDS=0
385
+ until curl -sf --max-time 2 http://localhost:3001 > /dev/null; do
386
+ if (( SECONDS > 60 )); then
387
+ echo "Timed out waiting for app to be ready"
388
+ exit 1
389
+ fi
390
+ sleep 2
391
+ done
273
392
 
274
393
  - name: Run installed js-recon against the lab app
275
394
  run: |
276
395
  echo "http://localhost:3001" > /tmp/brew-smoke-urls.txt
277
- js-recon run -u /tmp/brew-smoke-urls.txt -y -k
396
+ "$BIN" run -u /tmp/brew-smoke-urls.txt -y -k
397
+ env:
398
+ BIN: ${{ steps.bin.outputs.bin }}
278
399
 
279
400
  - name: Verify expected output files exist
280
401
  run: |
@@ -0,0 +1,42 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "label": "claude-sync: sync CLAUDE.md files",
6
+ "type": "shell",
7
+ "command": "${workspaceFolder}/.claude-sync/sync.sh",
8
+ "runOptions": {
9
+ "runOn": "folderOpen"
10
+ },
11
+ "presentation": {
12
+ "reveal": "silent",
13
+ "panel": "dedicated"
14
+ },
15
+ "problemMatcher": []
16
+ },
17
+ {
18
+ "label": "claude-sync: watch for new CLAUDE.md files",
19
+ "type": "shell",
20
+ "command": "${workspaceFolder}/.claude-sync/watch.sh",
21
+ "isBackground": true,
22
+ "runOptions": {
23
+ "runOn": "folderOpen"
24
+ },
25
+ "presentation": {
26
+ "reveal": "silent",
27
+ "panel": "dedicated"
28
+ },
29
+ "problemMatcher": {
30
+ "pattern": {
31
+ "regexp": "^(.*)$",
32
+ "message": 1
33
+ },
34
+ "background": {
35
+ "activeOnStart": true,
36
+ "beginsPattern": "^\\[claude-sync\\] watching",
37
+ "endsPattern": "^\\[claude-sync\\] watching"
38
+ }
39
+ }
40
+ }
41
+ ]
42
+ }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.4.1-beta.2 - 2026-07-22
4
+
5
+ ### Added
6
+
7
+ - The `api-gateway` module has been renamed to **`proxy`** and now supports three outbound-proxy methods: AWS API Gateway IP rotation (as before), generic SOCKS5/HTTP proxies, and Oxylabs. Proxy setup is driven by an interactive configuration wizard, and `lazyload`/`run` now consume proxy settings exclusively from the generated config file (`proxy_config.json`). Proxy request logic is centralized in `makeReq.ts`. (`proxy`, `lazyload`, `run`)
8
+ - `analyze`: added a `--disable-rules-version-check` flag (and a corresponding `JS_RECON_DISABLE_RULES_VERSION_CHECK` environment variable) so the rules version-compatibility check can be skipped. A new rules-bundled Docker image variant (`ghcr.io/js-recon/js-recon:<version>-w-rules`) ships with `js-recon-rules` baked in, allowing `analyze` to run without a rules mount or `-r` flag. (`analyze`, `docker`)
9
+ - `run`: added `--disable-refactor` flag to skip the automatic bundler-detection and refactor step that otherwise runs after `report`. (`run`)
10
+ - `run` (batch mode, `-u <file>`): each domain still gets its own `<output>/<host>/js-recon.db`, but a combined `<output>/js-recon.db` is now also maintained across the whole batch, so findings/endpoints/mapped chunks can be queried across every target without opening each per-domain database individually. Every table in the combined database gains a `domain` column (the sanitized host, doubling as a foreign key back to the corresponding output directory), and primary keys that were only unique within a single domain (`mapped`'s chunk `id`, `mapped_openapi`'s `(path, method)`, `endpoints`' `url`) are widened or replaced with an autoincrement `globalId` so rows from different domains never collide. (`run`, `report`)
11
+
12
+ ### Fixed
13
+
14
+ - Tech detection's intercepted-URL fallback (`techDetect/index.ts`) accepted a framework-shaped URL path (e.g. `/_nuxt/`, `/_next/`) as detection evidence without checking its actual response. A framework-shaped path resolving to a non-JS response (e.g. an error/maintenance page) could mis-fingerprint the whole target and send it down the wrong per-framework pipeline. The fallback now also confirms the captured response is a genuine 2xx JS response (status + `Content-Type` + body shape) before accepting the match. (`lazyload`)
15
+ - `proxy`: corrected the Oxylabs method to use the datacenter proxy scheme (verified against a live account); the module now fails cleanly on a malformed proxy config or URL, warns on a malformed config parse, and exits with an error when AWS credentials are missing for `-l`/`-d`. (`proxy`)
16
+ - `run`: replaced a fragile `@ts-ignore` on the final `analyze()` call with an explicit cast, and the Ctrl-C interrupt menu now waits for user input instead of exiting immediately. (`run`)
17
+ - Suppressed the misleading suggestion to pass `-k` when SSL verification is already disabled. (`run`, `lazyload`)
18
+ - Resolved npm-audit advisories: a path-traversal advisory in `@hono/node-server`, plus `fast-uri`/`hono` and `body-parser`/`brace-expansion` advisories. (`deps`)
19
+
3
20
  ## 1.4.1-beta.1 - 2026-07-16
4
21
 
5
22
  ### Fixed
@@ -0,0 +1,13 @@
1
+ ARG BASE_IMAGE=js-recon/js-recon
2
+ FROM ${BASE_IMAGE}
3
+
4
+ USER root
5
+ COPY js-recon-rules/ /home/pptruser/.js-recon/rules/
6
+ RUN rm -rf /home/pptruser/.js-recon/rules/.github && \
7
+ if [ -d /home/pptruser/.js-recon/rules/skills ]; then \
8
+ mv /home/pptruser/.js-recon/rules/skills /home/pptruser/.js-recon/skills; \
9
+ fi && \
10
+ chown -R pptruser:pptruser /home/pptruser/.js-recon
11
+ USER pptruser
12
+
13
+ ENV JS_RECON_DISABLE_RULES_VERSION_CHECK=true
package/README.md CHANGED
@@ -84,7 +84,7 @@ js-recon run -u https://app.example.com
84
84
  | `endpoints` | Extracts API endpoints and client-side paths from JS files. | [Read Docs](https://js-recon.io/docs/docs/modules/endpoints) |
85
85
  | `strings` | Extracts strings, URLs, and potential secrets from JS files. | [Read Docs](https://js-recon.io/docs/docs/modules/strings) |
86
86
  | `map` | Maps function calls and analyzes code, with optional AI-powered descriptions. | [Read Docs](https://js-recon.io/docs/docs/modules/map) |
87
- | `api-gateway` | Manages AWS API Gateway for IP rotation to bypass rate limits. | [Read Docs](https://js-recon.io/docs/docs/modules/api-gateway) |
87
+ | `proxy` | Routes requests through AWS API Gateway, SOCKS5/HTTP, or Oxylabs proxies. | [Read Docs](https://js-recon.io/docs/docs/modules/proxy) |
88
88
  | `run` | Runs all analysis modules automatically on a target. | [Read Docs](https://js-recon.io/docs/docs/modules/run) |
89
89
  | `analyze` | Analyzes the code. | [Read Docs](https://js-recon.io/docs/docs/modules/analyze) |
90
90
  | `report` | Generates a report from the analysis modules. | [Read Docs](https://js-recon.io/docs/docs/modules/report) |