@js-recon/js-recon 1.4.1-beta.1 → 1.4.1

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 (125) 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/post-merge +4 -0
  5. package/.claude-sync/hooks/pre-commit +3 -0
  6. package/.claude-sync/merge-agentic.sh +77 -0
  7. package/.claude-sync/setup.sh +17 -0
  8. package/.claude-sync/sync.sh +64 -0
  9. package/.claude-sync/watch.sh +17 -0
  10. package/.github/workflows/build-and-prettify.yaml +40 -0
  11. package/.github/workflows/promote-js-recon.yml +154 -10
  12. package/.vscode/tasks.json +42 -0
  13. package/CHANGELOG.md +27 -0
  14. package/Dockerfile.w-rules +13 -0
  15. package/README.md +1 -1
  16. package/build/analyze/helpers/initRules.js +27 -5
  17. package/build/analyze/helpers/initRules.js.map +1 -1
  18. package/build/analyze/index.js +3 -2
  19. package/build/analyze/index.js.map +1 -1
  20. package/build/completion/index.js +17 -11
  21. package/build/completion/index.js.map +1 -1
  22. package/build/globalConfig.js +1 -1
  23. package/build/globalConfig.js.map +1 -1
  24. package/build/index.js +55 -23
  25. package/build/index.js.map +1 -1
  26. package/build/lazyLoad/angular/angular_getFromMainJs.js +11 -29
  27. package/build/lazyLoad/angular/angular_getFromMainJs.js.map +1 -1
  28. package/build/lazyLoad/angular/angular_recursiveChunkImports.js +104 -0
  29. package/build/lazyLoad/angular/angular_recursiveChunkImports.js.map +1 -0
  30. package/build/lazyLoad/generic/generic_jsMimeTypes.js +19 -0
  31. package/build/lazyLoad/generic/generic_jsMimeTypes.js.map +1 -0
  32. package/build/lazyLoad/index.js +16 -9
  33. package/build/lazyLoad/index.js.map +1 -1
  34. package/build/lazyLoad/methodFilter.js +1 -1
  35. package/build/lazyLoad/methodFilter.js.map +1 -1
  36. package/build/lazyLoad/next_js/NextJsCrawler.js +5 -5
  37. package/build/lazyLoad/next_js/NextJsCrawler.js.map +1 -1
  38. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +17 -10
  39. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
  40. package/build/lazyLoad/next_js/next_bruteForceJsFiles.js +4 -3
  41. package/build/lazyLoad/next_js/next_bruteForceJsFiles.js.map +1 -1
  42. package/build/lazyLoad/next_js/next_parseLayoutJs.js +31 -31
  43. package/build/lazyLoad/next_js/next_parseLayoutJs.js.map +1 -1
  44. package/build/lazyLoad/next_js/next_promiseResolve.js +4 -3
  45. package/build/lazyLoad/next_js/next_promiseResolve.js.map +1 -1
  46. package/build/lazyLoad/next_js/next_scriptTagsSubsequentRequests.js +7 -8
  47. package/build/lazyLoad/next_js/next_scriptTagsSubsequentRequests.js.map +1 -1
  48. package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +6 -7
  49. package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js.map +1 -1
  50. package/build/lazyLoad/react/react_followImports.js +9 -8
  51. package/build/lazyLoad/react/react_followImports.js.map +1 -1
  52. package/build/lazyLoad/react/react_sourcemapUrls.js +5 -11
  53. package/build/lazyLoad/react/react_sourcemapUrls.js.map +1 -1
  54. package/build/lazyLoad/react/react_webpackChunkPaths.js +7 -6
  55. package/build/lazyLoad/react/react_webpackChunkPaths.js.map +1 -1
  56. package/build/lazyLoad/svelte/svelte_discoverPagesFromJs.js +13 -12
  57. package/build/lazyLoad/svelte/svelte_discoverPagesFromJs.js.map +1 -1
  58. package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +6 -7
  59. package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -1
  60. package/build/lazyLoad/techDetect/checkInterceptedEvidence.js +18 -0
  61. package/build/lazyLoad/techDetect/checkInterceptedEvidence.js.map +1 -0
  62. package/build/lazyLoad/techDetect/index.js +58 -13
  63. package/build/lazyLoad/techDetect/index.js.map +1 -1
  64. package/build/lazyLoad/vue/vue_discoverJsFiles.js +6 -6
  65. package/build/lazyLoad/vue/vue_discoverJsFiles.js.map +1 -1
  66. package/build/lazyLoad/vue/vue_getClientSidePaths.js +8 -7
  67. package/build/lazyLoad/vue/vue_getClientSidePaths.js.map +1 -1
  68. package/build/lazyLoad/vue/vue_jsImports.js +16 -16
  69. package/build/lazyLoad/vue/vue_jsImports.js.map +1 -1
  70. package/build/lazyLoad/vue/vue_reconstructSourceMaps.js +5 -4
  71. package/build/lazyLoad/vue/vue_reconstructSourceMaps.js.map +1 -1
  72. package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js +8 -1
  73. package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js.map +1 -1
  74. package/build/lazyLoad/vue/vue_stringJsFiles.js +15 -8
  75. package/build/lazyLoad/vue/vue_stringJsFiles.js.map +1 -1
  76. package/build/lazyLoad/vue/vue_viteMapDeps.js +6 -5
  77. package/build/lazyLoad/vue/vue_viteMapDeps.js.map +1 -1
  78. package/build/mcp/mcpServer.js +1 -2
  79. package/build/mcp/mcpServer.js.map +1 -1
  80. package/build/mcp/tools.js +1 -2
  81. package/build/mcp/tools.js.map +1 -1
  82. package/build/proxy/awsConfig.js +45 -0
  83. package/build/proxy/awsConfig.js.map +1 -0
  84. package/build/proxy/checkFeasibility.js.map +1 -0
  85. package/build/proxy/checkFireWallBlocking.js.map +1 -0
  86. package/build/proxy/configFile.js +31 -0
  87. package/build/proxy/configFile.js.map +1 -0
  88. package/build/{api_gateway → proxy}/genReq.js +4 -5
  89. package/build/proxy/genReq.js.map +1 -0
  90. package/build/proxy/genericProxy.js +21 -0
  91. package/build/proxy/genericProxy.js.map +1 -0
  92. package/build/proxy/index.js +421 -0
  93. package/build/proxy/index.js.map +1 -0
  94. package/build/proxy/oxylabsProxy.js +34 -0
  95. package/build/proxy/oxylabsProxy.js.map +1 -0
  96. package/build/proxy/resolveProxyConfig.js +38 -0
  97. package/build/proxy/resolveProxyConfig.js.map +1 -0
  98. package/build/refactor/index.js +23 -28
  99. package/build/refactor/index.js.map +1 -1
  100. package/build/report/utility/initGlobalReportDb.js +111 -0
  101. package/build/report/utility/initGlobalReportDb.js.map +1 -0
  102. package/build/report/utility/mergeDomainIntoGlobalDb.js +39 -0
  103. package/build/report/utility/mergeDomainIntoGlobalDb.js.map +1 -0
  104. package/build/run/index.js +129 -75
  105. package/build/run/index.js.map +1 -1
  106. package/build/run/interruptHandler.js +17 -1
  107. package/build/run/interruptHandler.js.map +1 -1
  108. package/build/utility/concurrency.js +35 -0
  109. package/build/utility/concurrency.js.map +1 -0
  110. package/build/utility/configureProxy.js +60 -0
  111. package/build/utility/configureProxy.js.map +1 -0
  112. package/build/utility/globals.js +108 -13
  113. package/build/utility/globals.js.map +1 -1
  114. package/build/utility/makeReq.js +103 -10
  115. package/build/utility/makeReq.js.map +1 -1
  116. package/package.json +9 -2
  117. package/patches/puppeteer-extra-plugin-user-data-dir+2.4.1.patch +31 -0
  118. package/CLAUDE.md +0 -641
  119. package/build/api_gateway/checkFeasibility.js.map +0 -1
  120. package/build/api_gateway/checkFireWallBlocking.js.map +0 -1
  121. package/build/api_gateway/genReq.js.map +0 -1
  122. package/build/api_gateway/index.js +0 -291
  123. package/build/api_gateway/index.js.map +0 -1
  124. /package/build/{api_gateway → proxy}/checkFeasibility.js +0 -0
  125. /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,4 @@
1
+ #!/usr/bin/env bash
2
+ # git post-merge hook: merges the corresponding js-recon-agentic-files branch
3
+ # whenever a merge/pull brings another branch's history in here.
4
+ exec "$(git rev-parse --show-toplevel)/.claude-sync/merge-agentic.sh" "$@"
@@ -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,77 @@
1
+ #!/usr/bin/env bash
2
+ # Mirrors a js-recon branch merge/pull onto the corresponding branches in the
3
+ # sibling js-recon-agentic-files repo, since CLAUDE.md files are gitignored
4
+ # symlinks here and never participate in this repo's own merges.
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
+
10
+ TARGET_BRANCH="$(git -C "$JS_RECON_ROOT" symbolic-ref --short HEAD 2>/dev/null || echo "")"
11
+
12
+ if [ -z "$TARGET_BRANCH" ]; then
13
+ echo "[claude-sync] detached HEAD, skipping agentic-files merge"
14
+ exit 0
15
+ fi
16
+
17
+ if [ ! -d "$AGENTIC_REPO_PATH/.git" ]; then
18
+ echo "[claude-sync] agentic-files repo not found at $AGENTIC_REPO_PATH — see js-recon-agentic-files/README.md for setup"
19
+ exit 0
20
+ fi
21
+
22
+ # git gives post-merge no argument naming the branch that was merged in, and
23
+ # this fires for both fast-forward and real merges (plus `git pull`). The
24
+ # reflog message git writes for the merge is the one place that reliably
25
+ # names the source branch in both cases:
26
+ # "merge <branch>: Fast-forward"
27
+ # "merge <branch>: Merge made by the 'ort' strategy."
28
+ # "pull <remote> <branch>: Fast-forward" (or similar, from `git pull`)
29
+ REFLOG_MSG="$(git -C "$JS_RECON_ROOT" reflog -1 --format=%gs HEAD 2>/dev/null || echo "")"
30
+
31
+ SOURCE_BRANCH=""
32
+ if [[ "$REFLOG_MSG" =~ ^merge\ ([^:]+): ]]; then
33
+ SOURCE_BRANCH="${BASH_REMATCH[1]}"
34
+ elif [[ "$REFLOG_MSG" =~ ^pull\ [^\ ]+\ ([^:]+): ]]; then
35
+ SOURCE_BRANCH="${BASH_REMATCH[1]}"
36
+ fi
37
+
38
+ # Strip a remote prefix (e.g. "origin/dev" -> "dev") to match agentic-files'
39
+ # bare branch naming.
40
+ SOURCE_BRANCH="${SOURCE_BRANCH#origin/}"
41
+
42
+ if [ -z "$SOURCE_BRANCH" ]; then
43
+ echo "[claude-sync] could not determine merged-in branch from reflog ('$REFLOG_MSG') — skipping agentic-files merge"
44
+ exit 0
45
+ fi
46
+
47
+ if [ "$SOURCE_BRANCH" = "$TARGET_BRANCH" ]; then
48
+ exit 0
49
+ fi
50
+
51
+ (
52
+ cd "$AGENTIC_REPO_PATH"
53
+ git fetch origin --quiet
54
+
55
+ if git show-ref --verify --quiet "refs/remotes/origin/$TARGET_BRANCH"; then
56
+ git checkout -B "$TARGET_BRANCH" "origin/$TARGET_BRANCH" --quiet
57
+ else
58
+ echo "[claude-sync] WARNING: no 'origin/$TARGET_BRANCH' branch in agentic-files repo — skipping merge"
59
+ exit 0
60
+ fi
61
+
62
+ if ! git show-ref --verify --quiet "refs/remotes/origin/$SOURCE_BRANCH"; then
63
+ echo "[claude-sync] WARNING: no 'origin/$SOURCE_BRANCH' branch in agentic-files repo — nothing to merge"
64
+ exit 0
65
+ fi
66
+
67
+ if git merge --no-edit "origin/$SOURCE_BRANCH" --quiet; then
68
+ git push --quiet origin "$TARGET_BRANCH"
69
+ echo "[claude-sync] merged agentic-files '$SOURCE_BRANCH' into '$TARGET_BRANCH' and pushed"
70
+ else
71
+ git merge --abort
72
+ echo "[claude-sync] WARNING: merging agentic-files '$SOURCE_BRANCH' into '$TARGET_BRANCH' conflicted — aborted automatically."
73
+ echo "[claude-sync] resolve manually: cd $AGENTIC_REPO_PATH && git checkout $TARGET_BRANCH && git merge origin/$SOURCE_BRANCH"
74
+ fi
75
+ )
76
+
77
+ "$JS_RECON_ROOT/.claude-sync/sync.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
@@ -65,8 +65,14 @@ jobs:
65
65
  git config user.name "github-actions[bot]"
66
66
  git config user.email "github-actions[bot]@users.noreply.github.com"
67
67
  git add "${FORMULA}"
68
- git commit -m "chore: update ${FORMULA} to ${VERSION}"
69
- git push
68
+ # Idempotent: re-promoting an already-promoted version leaves the formula
69
+ # unchanged, so skip the commit/push instead of failing on "nothing to commit".
70
+ if git diff --cached --quiet; then
71
+ echo "${FORMULA} already at ${VERSION}; nothing to commit."
72
+ else
73
+ git commit -m "chore: update ${FORMULA} to ${VERSION}"
74
+ git push
75
+ fi
70
76
  env:
71
77
  VERSION: ${{ inputs.version }}
72
78
  FORMULA: ${{ steps.formula.outputs.formula }}
@@ -78,6 +84,15 @@ jobs:
78
84
  packages: write
79
85
  steps:
80
86
  - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
87
+ - name: Checkout js-recon-rules
88
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
89
+ with:
90
+ repository: js-recon/js-recon-rules
91
+ path: js-recon-rules
92
+ # Don't persist the checkout token into js-recon-rules/.git/config —
93
+ # Dockerfile.w-rules builds from this context and would otherwise bake
94
+ # the writable GHCR credential into an image layer.
95
+ persist-credentials: false
81
96
  - uses: docker/login-action@v3
82
97
  with:
83
98
  registry: ghcr.io
@@ -103,6 +118,22 @@ jobs:
103
118
  env:
104
119
  VERSION: ${{ inputs.version }}
105
120
 
121
+ - name: Build and push the rules-bundled (-w-rules) variant
122
+ run: |
123
+ # layer js-recon-rules on top of the image just built/pushed above
124
+ 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 .
125
+
126
+ TAG="latest"
127
+ [[ "$VERSION" == *"beta"* ]] && TAG="beta"
128
+ [[ "$VERSION" == *"alpha"* ]] && TAG="alpha"
129
+
130
+ docker tag ghcr.io/js-recon/js-recon:$VERSION-w-rules ghcr.io/js-recon/js-recon:$TAG-w-rules
131
+
132
+ docker push ghcr.io/js-recon/js-recon:$VERSION-w-rules
133
+ docker push ghcr.io/js-recon/js-recon:$TAG-w-rules
134
+ env:
135
+ VERSION: ${{ inputs.version }}
136
+
106
137
  verify-npm-install:
107
138
  runs-on: ubuntu-latest
108
139
  permissions:
@@ -140,7 +171,7 @@ jobs:
140
171
  npm start &
141
172
 
142
173
  - 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'
174
+ run: timeout 60 bash -c 'until curl -sf --max-time 2 http://localhost:3001 > /dev/null; do sleep 2; done'
144
175
 
145
176
  - name: Run installed js-recon against the lab app
146
177
  run: |
@@ -194,17 +225,23 @@ jobs:
194
225
  npm start &
195
226
 
196
227
  - 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'
228
+ run: timeout 60 bash -c 'until curl -sf --max-time 2 http://localhost:3001 > /dev/null; do sleep 2; done'
198
229
 
199
230
  - name: Run containerized js-recon against the lab app
200
231
  run: |
232
+ # The release image runs as the non-root `pptruser` (see Dockerfile.release),
233
+ # so a bind-mounted host directory landing inside its home dir inherits the
234
+ # host-side owner/perms and can be unwritable to that user. Mount outside
235
+ # $HOME with permissive perms and point js-recon at it explicitly via `-o`
236
+ # rather than relying on its default CWD-relative output path.
201
237
  mkdir -p /tmp/docker-output
238
+ chmod 777 /tmp/docker-output
202
239
  echo "http://localhost:3001" > /tmp/docker-smoke-urls.txt
203
240
  docker run --rm --network host \
204
- -v /tmp/docker-output:/app/output \
205
- -v /tmp/docker-smoke-urls.txt:/app/urls.txt \
241
+ -v /tmp/docker-output:/tmp/js-recon-output \
242
+ -v /tmp/docker-smoke-urls.txt:/tmp/urls.txt \
206
243
  ghcr.io/js-recon/js-recon:${VERSION} \
207
- run -u /app/urls.txt --no-sandbox -y -k
244
+ run -u /tmp/urls.txt -o /tmp/js-recon-output --no-sandbox -y -k
208
245
  env:
209
246
  VERSION: ${{ inputs.version }}
210
247
 
@@ -213,6 +250,72 @@ jobs:
213
250
  test -n "$(find /tmp/docker-output -name analyze.json)" || { echo "analyze.json missing"; exit 1; }
214
251
  test -n "$(find /tmp/docker-output -name js-recon.db)" || { echo "js-recon.db missing"; exit 1; }
215
252
 
253
+ verify-docker-w-rules-install:
254
+ runs-on: ubuntu-latest
255
+ needs: publish-ghcr
256
+ permissions:
257
+ contents: read
258
+ packages: read
259
+ steps:
260
+ - name: Checkout js-recon
261
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
262
+
263
+ - name: Checkout js-recon-labs
264
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
265
+ with:
266
+ repository: js-recon/js-recon-labs
267
+ path: js-recon-labs
268
+ persist-credentials: false
269
+
270
+ - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
271
+ with:
272
+ node-version: "22"
273
+
274
+ - uses: docker/login-action@v3
275
+ with:
276
+ registry: ghcr.io
277
+ username: ${{ github.actor }}
278
+ password: ${{ secrets.GITHUB_TOKEN }}
279
+
280
+ - name: Install and start vulnerable app
281
+ working-directory: js-recon-labs/next_js/vuln-all-rules
282
+ run: |
283
+ npm ci
284
+ npm run build
285
+ npm start &
286
+
287
+ - name: Wait for app to be ready
288
+ run: timeout 60 bash -c 'until curl -sf --max-time 2 http://localhost:3001 > /dev/null; do sleep 2; done'
289
+
290
+ - name: Run the rules-bundled image against the lab app (no rules mount, no -r)
291
+ run: |
292
+ set -o pipefail
293
+ # Deliberately don't mount any rules directory and don't pass -r — the
294
+ # -w-rules image must be able to analyze using only what's baked in.
295
+ mkdir -p /tmp/docker-w-rules-output
296
+ chmod 777 /tmp/docker-w-rules-output
297
+ echo "http://localhost:3001" > /tmp/docker-w-rules-smoke-urls.txt
298
+ docker run --network host \
299
+ --name js-recon-w-rules-run \
300
+ -v /tmp/docker-w-rules-output:/tmp/js-recon-output \
301
+ -v /tmp/docker-w-rules-smoke-urls.txt:/tmp/urls.txt \
302
+ ghcr.io/js-recon/js-recon:${VERSION}-w-rules \
303
+ run -u /tmp/urls.txt -o /tmp/js-recon-output --no-sandbox -y -k \
304
+ | tee /tmp/docker-w-rules-run.log
305
+ docker rm js-recon-w-rules-run
306
+ env:
307
+ VERSION: ${{ inputs.version }}
308
+
309
+ - name: Verify the version check was actually skipped
310
+ run: |
311
+ grep -q "Rules version check disabled" /tmp/docker-w-rules-run.log || {
312
+ echo "Expected log line confirming JS_RECON_DISABLE_RULES_VERSION_CHECK was honored not found"
313
+ exit 1
314
+ }
315
+
316
+ - name: Verify all rules fired
317
+ run: node scripts/smoke-test.js "$(find /tmp/docker-w-rules-output -name analyze.json | head -n1)"
318
+
216
319
  verify-brew-install:
217
320
  runs-on: macos-latest
218
321
  needs: update-homebrew-tap
@@ -249,9 +352,22 @@ jobs:
249
352
  env:
250
353
  VERSION: ${{ inputs.version }}
251
354
 
355
+ - name: Resolve js-recon binary path
356
+ id: bin
357
+ run: |
358
+ # js-recon-alpha/js-recon-beta are keg-only (they share the `js-recon`
359
+ # binary name with the stable formula), so brew never symlinks them
360
+ # into PATH. Resolve the keg path directly instead of relying on PATH.
361
+ KEG_BIN="$(brew --prefix)/opt/${{ matrix.formula }}/bin/js-recon"
362
+ if [[ -x "$KEG_BIN" ]]; then
363
+ echo "bin=$KEG_BIN" >> "$GITHUB_OUTPUT"
364
+ else
365
+ echo "bin=js-recon" >> "$GITHUB_OUTPUT"
366
+ fi
367
+
252
368
  - name: Verify installed version
253
369
  run: |
254
- INSTALLED=$(js-recon -V)
370
+ INSTALLED=$("$BIN" -V)
255
371
  echo "Installed: ${INSTALLED}"
256
372
  if [[ "${{ matrix.formula }}" == "${{ steps.expected.outputs.formula }}" ]]; then
257
373
  [[ "$INSTALLED" == *"$VERSION"* ]] || { echo "Version mismatch: expected ${VERSION} in ${{ matrix.formula }}"; exit 1; }
@@ -260,6 +376,7 @@ jobs:
260
376
  fi
261
377
  env:
262
378
  VERSION: ${{ inputs.version }}
379
+ BIN: ${{ steps.bin.outputs.bin }}
263
380
 
264
381
  - name: Install and start vulnerable app
265
382
  working-directory: js-recon-labs/next_js/vuln-all-rules
@@ -269,12 +386,39 @@ jobs:
269
386
  npm start &
270
387
 
271
388
  - 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'
389
+ run: |
390
+ SECONDS=0
391
+ until curl -sf --max-time 2 http://localhost:3001 > /dev/null; do
392
+ if (( SECONDS > 60 )); then
393
+ echo "Timed out waiting for app to be ready"
394
+ exit 1
395
+ fi
396
+ sleep 2
397
+ done
398
+
399
+ - name: Ensure a Chrome is available for Puppeteer
400
+ run: |
401
+ # The Homebrew keg ships js-recon but not a Chromium — Puppeteer needs a
402
+ # browser to run lazyload. Prefer the runner's pre-installed Google Chrome
403
+ # (present on GitHub macOS images); fall back to a Chrome-for-Testing build.
404
+ CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
405
+ if [[ ! -x "$CHROME" ]]; then
406
+ echo "Pre-installed Chrome not found; downloading a Chrome-for-Testing build..."
407
+ CHROME="$(npx --yes @puppeteer/browsers install chrome@stable --format "{{path}}")"
408
+ fi
409
+ if [[ ! -x "$CHROME" ]]; then
410
+ echo "Resolved Chrome path is not executable: $CHROME"
411
+ exit 1
412
+ fi
413
+ echo "Using Chrome at: $CHROME"
414
+ echo "PUPPETEER_EXECUTABLE_PATH=$CHROME" >> "$GITHUB_ENV"
273
415
 
274
416
  - name: Run installed js-recon against the lab app
275
417
  run: |
276
418
  echo "http://localhost:3001" > /tmp/brew-smoke-urls.txt
277
- js-recon run -u /tmp/brew-smoke-urls.txt -y -k
419
+ "$BIN" run -u /tmp/brew-smoke-urls.txt -y -k
420
+ env:
421
+ BIN: ${{ steps.bin.outputs.bin }}
278
422
 
279
423
  - name: Verify expected output files exist
280
424
  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
+ }