chore: env allowlist + path-guard + token-guard + hooks .mts + bootstrap + cascade#1279
chore: env allowlist + path-guard + token-guard + hooks .mts + bootstrap + cascade#1279John-David Dalton (jdalton) wants to merge 16 commits intomainfrom
Conversation
633bb68 to
a4f3ca0
Compare
|
Addressed in 499aeb2 + a4f3ca0:
|
…y/ scope) Consolidates PR #1280 (path-guard infra) and #1281 (.sh→.mts hook conversion) into this branch. Resolves the modify/delete conflict on .git-hooks/{commit-msg,pre-push} by accepting the .mts versions — the env allowlist tweak from #1279 (.env.precommit + skip-hook- scripts) is already covered in commit-msg.mts via shouldSkipFile and the precommit allowlist. Also renames internal hook packages to drop the @socketsecurity/ scope (hook-path-guard, hook-token-guard, hook-check-new-deps) — they're private:true and never published.
|
bugbot run |
….mts conversion + bootstrap-from-registry Consolidates the work previously split across PRs #1279 (NODE_COMPILE_CACHE drop), #1280 (path-guard infra), and #1281 (.sh→.mts hook conversion) into a single commit. What's included: Env allowlist + .cache/ + CLAUDE.md - Drop NODE_COMPILE_CACHE convention from .env.precommit, .env.test - Allow .env.precommit at any depth in commit-msg hook - Skip hook scripts in scanners (they contain the literal regex) - Restore .cache/** exclude in tsconfigs - Propagate CLAUDE.md sorting + open-PR + paths + inclusive-language rules; Set constructor sort rule; don't-revert-untouched rule; replace whitelist/blacklist with allowlist/denylist Path-guard infra (.claude/hooks/path-guard/, scripts/check-paths.mts, .github/paths-allowlist.yml, .claude/skills/path-guard/) - Mantra: 1 path, 1 reference. PreToolUse hook on Edit|Write blocks multi-stage build paths constructed inline; companion gate runs in pnpm check - Template-literal path detection - Drift-resistant allowlist via exact-line OR snippet_hash match - --show-hashes CLI flag for authoring allowlist entries - Centralized vocabulary in segments.mts (hook + gate share one source for stage / build-root / mode / sibling-package sets) - Paren-balanced parser handles nested function-call args - Multi-line YAML reasons (| and > block scalars) - scripts/check.mts resolves the gate via path.join(scriptsDir,...) so it runs from any cwd (root or workspace package) Token-guard renamed from token-hygiene - Word-boundary match for sensitive env names - Step 1 (ALWAYS_DANGEROUS) now gates on hasRedaction so 'env | sed s/=.*/=<redacted>/' (the suggested fix) actually passes .sh → .mts hook conversion (Node 25+) - .git-hooks/_helpers.mts (was _helpers.sh) — exports filterAllowedApiKeys + scanners (personal paths, AWS keys, GitHub tokens, private keys, AI attribution, Linear issue refs) - .git-hooks/{commit-msg,pre-commit,pre-push}.mts (were .sh) - _helpers.mts hard-fails at module load if Node < 25 (relies on stable type stripping, no flag) - Husky shims invoke node directly - .husky/pre-commit runs tests with SOCKET_CLI_NO_API_TOKEN=1 so contributors without a real token don't see test failures Hook package rename - Drop @socketsecurity/ scope from internal hook packages (hook-path-guard, hook-token-guard, hook-check-new-deps); they are private:true and never published Bootstrap-from-registry (NEW) - scripts/bootstrap-from-registry.mts downloads zero-dep Socket packages (currently @socketsecurity/lib) from the npm registry directly into node_modules/ before pnpm install runs - Wired via package.json preinstall hook - Reads pinned version from pnpm-workspace.yaml catalog: OR root package.json devDependencies (whichever is set) - Solves the chicken-and-egg where setup.mts needs @socketsecurity/lib at module-load time but pnpm install hasn't run yet on a fresh clone
1991df7 to
5e29496
Compare
…rap + cascade Consolidated PR — combines the original work from #1279, #1280, #1281 plus follow-up commits (private-name rule, socket-registry pin cascades) into a single squashed commit. Includes: - env allowlist + .cache/ + CLAUDE.md hygiene (drop NODE_COMPILE_CACHE convention; restore .cache/** exclude in tsconfigs; propagate CLAUDE.md sorting/open-PR/paths/inclusive-language/Set-sort/ don't-revert-untouched/private-name rules; replace whitelist/blacklist with allowlist/denylist) - path-guard infra (PreToolUse hook + scripts/check-paths.mts gate + .github/paths-allowlist.yml + /path-guard skill — enforces "1 path, 1 reference" so multi-stage build paths are constructed exactly once) - token-guard hook (renamed from token-hygiene; word-boundary match for sensitive env names; ALWAYS_DANGEROUS gates on hasRedaction so redacted env dumps pass) - .sh -> .mts hook conversion on Node 25+ (stable type stripping; _helpers.mts hard-fails at module load if Node < 25; husky shims invoke node directly; SOCKET_CLI_NO_API_TOKEN=1 for pre-commit tests) - internal hook package rename (drop @socketsecurity/ scope from hook-path-guard, hook-token-guard, hook-check-new-deps; private, never published) - xport lock-step manifest (scripts/xport.mts + scripts/xport-schema.mts + scripts/xport-emit-schema.mts + xport.schema.json) - bootstrap-from-registry (scripts/bootstrap-from-registry.mts downloads zero-dep Socket packages from npm registry into node_modules/ via preinstall hook, solving fresh-clone chicken-and-egg) - socket-registry pins cascaded to ceab1e26 (picks up the @socketsecurity/lib bootstrap move from the install action into setup, so consumers calling only setup also benefit)
7caded2 to
ba8f85c
Compare
|
bugbot run |
… align pre-commit .env scope Two issues from Cursor Bugbot's review: 1. _api-key-check.sh is unused (low) — accidental migration leftover. The replacement is _helpers.mts (already in this PR). 2. commit-msg.mts uses basename() so a nested .env.local is blocked, but pre-commit.mts only matched root-level paths (medium). A nested .env is just as much a leak as a root one. Aligned both to basename-based matching with the same allowlist.
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Pre-push env detection weaker than pre-commit/commit-msg
- Replaced the narrow root-only regex with the same basename()-based pattern used by pre-commit and commit-msg, catching .env files at any depth and with any suffix while excluding example/test/precommit.
Or push these changes by commenting:
@cursor push 3d8f5dd89b
Preview (3d8f5dd89b)
diff --git a/.git-hooks/pre-push.mts b/.git-hooks/pre-push.mts
--- a/.git-hooks/pre-push.mts
+++ b/.git-hooks/pre-push.mts
@@ -17,6 +17,7 @@
import { spawnSync } from 'node:child_process'
import { existsSync, statSync } from 'node:fs'
+import { basename } from 'node:path'
import process from 'node:process'
@@ -190,8 +191,15 @@
return 0
}
- // Top-level sensitive filenames in the change set.
- const envHits = changed.filter(f => /^\.env(\.local)?$/.test(f))
+ // .env files at any depth — allow only .env.example, .env.test,
+ // .env.precommit (templates / tracked placeholders).
+ const envHits = changed.filter(f => {
+ const base = basename(f)
+ return (
+ /^\.env(\.[^/]+)?$/.test(base) &&
+ !/^\.env\.(example|test|precommit)$/.test(base)
+ )
+ })
if (envHits.length > 0) {
out(red('✗ BLOCKED: Attempting to push .env file!'))
out(`Files: ${envHits.join(', ')}`)You can send follow-ups to the cloud agent here.
…Bugbot) Two issues from Cursor Bugbot's review: 1. token-guard sed redaction regex couldn't cross delimiter boundary (high). The pattern /\bsed\b[^|]*s[/|#][^/|#]*=[^/|#]*<?redact/i used [^/|#]* which stops at the / between sed pattern and replacement, so 'sed s/=.*/=<redacted>/' (the canonical fix the error message suggests) never matched. Replaced with [\s\S]*? to reach across the delimiter. 2. pre-push.mts .env detection only matched root-level .env / .env.local (high). commit-msg.mts and pre-commit.mts both use basename() with a broader pattern. pre-push is the mandatory enforcement layer for --no-verify bypasses; weaker detection there meant a nested packages/cli/.env.local would slip through. Aligned to basename- based matching with the same allowlist (.env.example/.env.test/ .env.precommit). Note on Bugbot finding #2 (rmSync in scripts/bootstrap-from-registry.mts): deliberate. The bootstrap script runs BEFORE pnpm install — that's its whole purpose — so @socketsecurity/lib's safeDelete isn't available yet. Discard.
|
bugbot run |
Bugbot flagged: readPinnedVersion stripped range prefixes (^, ~, >=) on the package.json path but returned the catalog match raw. A catalog entry of '^5.24.0' would have produced 'lib-^5.24.0.tgz' as the tarball URL — invalid. Extracted stripRange() and applied to both code paths.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 000c35e. Configure here.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
The token-hygiene hook was renamed to token-guard during the path-guard fleet rollout. The old directory was never deleted in cli; sync- scaffolding handles adds/modifies but not deletes. Removing it now since nothing references it (settings.json points at token-guard). Also pulls in the SKILL.md doc update that mentions segments.mts as the canonical stage/sibling vocabulary.
Bring public-surface-reminder + setup-security-tools in line with the fleet-wide rename: @socketsecurity/hook-* → hook-*. These two were missed in the original cli rename pass since they're cli-only hooks (not in the byte-synced fleet set).
…mts.tmpl The schema docs in paths-allowlist.yml drifted from canonical. The old comment claimed line tolerance is ±2 (FALSE since Gap 2; lines are now strict-exact) and didn't mention snippet_hash or --show-hashes. Also brings the path-guard skill reference template up to date.
Adds private-name-guard, public-surface-reminder, and release-workflow-guard hooks (previously the rules were in CLAUDE.md without the enforcement hook). Refreshes check-new-deps index.mts + README to canonical (Cargo.toml fragment-mode parsing, score-based warnings, module-aware main). Wires the 4 Bash hooks alphabetically in settings.json. Now byte-identical with template/.claude/hooks/ for index.mts + README.md across all four hooks. package.json kept at per-repo catalog/pin style.
Picks up the multi-package bootstrap loop (a645d921) that pre-seeds @socketsecurity/lib + @socketregistry/packageurl-js + @sinclair/typebox on a fresh checkout, plus the libuv-fix in check-firewall that drops process.exit() in favor of natural event-loop drain (necessary on Node 24 + Windows when the bootstrap runs the firewall checker multiple times in succession). Cascade chain: setup + check-firewall Layer 1 a645d921 setup-and-install Layer 2 521d0ba8 reusable workflows Layer 3 3f2f2c00 ← this is the propagation SHA _local-not-for-reuse-* Layer 4 b2905c2f (socket-registry only)
Hook sync (bcc5548) added @types/node devDeps to private-name-guard + public-surface-reminder + release-workflow-guard. Lockfile didn't update at the time, so CI's frozen-lockfile install rejected the push. Run pnpm i to refresh.
Replaces the older zod-based setup-security-tools with canonical
TypeBox version (sourced from socket-repo-template/template). Key
changes:
- TypeBox schemas (matches the rest of the fleet's xport pattern)
- PURL-based AgentShield package spec (pkg:npm/ecc-agentshield@1.4.0)
- downloadPackage from @socketsecurity/lib/dlx/package — installs
AgentShield via dlx instead of requiring it as a workspace
devDep, so consumers don't need ecc-agentshield in
devDependencies
- mkdtemp (collision-safe) instead of Date.now()-only naming
- normalizePath on binary paths
- parseSchema from @socketsecurity/lib/schema/parse
- pip3 added to ecosystems lists
The hook's package.json now declares @sinclair/typebox +
@socketregistry/packageurl-js (catalog refs); the new socket-registry
setup action provisions all three zero-dep packages
(@socketsecurity/lib + @socketregistry/packageurl-js +
@sinclair/typebox) via the multi-package bootstrap loop, so a
fresh checkout has them resolvable at hook-load time.
Picks up the firewall-checker fix in @SocketDev/socket-registry — any alert from Socket Firewall now blocks the bootstrap (no severity threshold; the API only returns alerts when a package is flagged as malware, so any alert means malware). Cascade chain: check-firewall.mts Layer 1 e4193847 setup-and-install Layer 2 b94c9571 reusable workflows Layer 3 85a2fc0d ← propagation SHA _local-not-for-reuse-* Layer 4 25ec2c76 (socket-registry only)
…tall Adds scripts/bootstrap-from-registry.mts that downloads zero-dep Socket packages (currently @socketsecurity/lib) from the npm registry tarball directly into node_modules/<scope>/<name>/ BEFORE pnpm install runs. Wired via package.json preinstall lifecycle hook. Why: setup.mts and other root-script importers of @socketsecurity/lib fail on a fresh clone because pnpm install hasn't run yet. Pre- seeding from the registry tarball solves the chicken-and-egg. Reads pinned version from pnpm-workspace.yaml `catalog:` OR root package.json deps/devDeps — single source of truth, no hardcoded version. Self-landable split from #1279.
Picks up the latest socket-registry workflow updates (currently the bootstrap-from-registry step in install/action.yml + the path-guard fleet rollout cascade). Self-landable split from #1279.
|
Superseded by 5 split PRs that can each land independently:
|

Summary
Consolidated PR — combines the work originally split across #1279, #1280, and #1281 into a single squashed commit on
chore/remove-node-compile-cache, plus follow-up commits (private-name rule, socket-registry pin cascades) merged in.50 files changed across 6 logical groups. Each section below lists the files in that group so reviewers can scope their attention.
🟦 Group 1 — Path-guard infra (15 files, additive)
New PreToolUse hook + companion gate that enforces "1 path, 1 reference" — every build/test/runtime path is constructed exactly once, everywhere else references the constructed value.
Files
.claude/hooks/path-guard/README.md.claude/hooks/path-guard/index.mts(the hook).claude/hooks/path-guard/package.json.claude/hooks/path-guard/segments.mts(canonical stage/build-root/mode/sibling vocabulary, imported by both hook and gate).claude/hooks/path-guard/test/path-guard.test.mts(29 tests).claude/hooks/path-guard/tsconfig.json.claude/skills/_shared/path-guard-rule.md.claude/skills/path-guard/SKILL.md(invokable/path-guardskill).claude/skills/path-guard/reference/check-paths.mts.tmpl.claude/skills/path-guard/reference/claude-md-rule.md.claude/skills/path-guard/reference/paths-allowlist.yml.tmplscripts/check-paths.mts(the whole-repo gate).github/paths-allowlist.yml(empty starter).claude/settings.json(wires the hook on Edit|Write)scripts/check.mts(invokes check-paths.mts via absolute path so it works from any cwd)Detection features: template-literal path detection · drift-resistant allowlist via
snippet_hash(exact-line OR hash match) ·--show-hashesCLI flag · paren-balanced parser handles nestedpath.joinargs · multi-line YAML reasons.🟪 Group 2 — Token-guard hook (5 files, mostly additive)
Renamed from
token-hygiene. Word-boundary match for sensitive env names. Step 1 (ALWAYS_DANGEROUS) now gates onhasRedactionsoenv | sed s/=.*/=<redacted>/(the suggested fix) actually passes.Files
.claude/hooks/token-guard/README.md.claude/hooks/token-guard/index.mts.claude/hooks/token-guard/package.json.claude/hooks/token-guard/test/token-guard.test.mts.claude/hooks/token-guard/tsconfig.json🟧 Group 3 —
.sh→.mtshook conversion (12 files, replacement)All four shell-based git hooks become
.mtsmodules running on Node 25+ (stable type stripping, no flag needed)._helpers.mtshard-fails at module load if Node < 25. Husky shims invokenodedirectly. Includes Linear-issue blocker (CLAUDE.md "ABSOLUTE RULES" enforcement) andSOCKET_CLI_NO_API_TOKEN=1for pre-commit tests.Files (deletions paired with additions)
Deleted (
.sh):.git-hooks/_helpers.sh.git-hooks/commit-msg.git-hooks/pre-pushAdded (
.mts):.git-hooks/_helpers.mts(exportsfilterAllowedApiKeys+ scanners for personal paths, AWS keys, GitHub tokens, private keys, AI attribution, Linear issue refs).git-hooks/commit-msg.mts.git-hooks/pre-commit.mts.git-hooks/pre-push.mtsModified (husky shims):
.husky/commit-msg.husky/pre-commit.husky/pre-push🟩 Group 4 — env allowlist +
.cache/+ CLAUDE.md hygiene (9 files, modify-only)Original #1279 scope plus the private-name rule. Documentation + tooling-config tightening; no runtime behavior changes outside the hooks.
Files
CLAUDE.md(sorting · open-PR · paths · inclusive-language · Set constructor sort · don't-revert-untouched · private-name rules; replace whitelist/blacklist).claude/agents/security-reviewer.md.claude/skills/security-scan/SKILL.md.config/tsconfig.check.json(restore.cache/**exclude).gitignore(add**/.cache/).env.precommit(drop NODE_COMPILE_CACHE).env.test(drop NODE_COMPILE_CACHE).claude/hooks/check-new-deps/package.json(rename: drop@socketsecurity/scope)package.json(addpreinstall: bootstrap-from-registry.mts)🟨 Group 5 — xport lock-step manifest (4 files, additive)
Machine-readable manifest format already adopted across the Socket fleet.
Files
scripts/xport.mtsscripts/xport-schema.mtsscripts/xport-emit-schema.mtsxport.schema.json(machine-generated)🟥 Group 6 — Bootstrap-from-registry + socket-registry cascade (4 files)
scripts/bootstrap-from-registry.mtsdownloads zero-dep Socket packages (currently@socketsecurity/lib) from the npm registry directly intonode_modules/<scope>/<name>/BEFOREpnpm installruns. Reads the pinned version frompnpm-workspace.yamlcatalog:OR rootpackage.jsondevDependencies— single source of truth. Wired viapreinstalllifecycle hook.A fresh clone now goes
git clone → pnpm install → working repo, no special setup ordering required.The matching
SocketDev/socket-registryworkflow pins are cascaded to ceab1e26 so this PR's CI runs through the same reusable workflows that pick up the@socketsecurity/libbootstrap from thesetupaction (a parallel cleanup landed upstream).Files
scripts/bootstrap-from-registry.mts.github/workflows/ci.yml.github/workflows/weekly-update.yml.github/workflows/provenance.ymlVerification
Note
Medium Risk
Medium risk because it changes developer workflow enforcement (PreToolUse hooks + git hooks) and adds a
preinstallscript that downloads/extracts packages beforepnpm install, which could break installs or block commits/pushes if misconfigured.Overview
Introduces path hygiene enforcement via a new Claude
PreToolUsehook (path-guard) plus a repo-wide gate (scripts/check-paths.mts) and allowlist, and wires it intopnpm check/check:pathsand.claude/settings.jsonto prevent duplicated multi-stage build/output path construction.Adds a new Claude Bash token leak firewall (
token-guard) that blocks risky commands (literal token shapes, env dumps, unredacted.envreads, andcurlauth to stdout) and replaces the prior Bash hook wiring.Migrates local security enforcement from shell to Node
.mtsgit hooks (commit-msg/pre-commit/pre-push) with shared scanners (secrets, personal paths, Linear refs, AI attribution) and updates Husky shims; also tweaks env/caching configs (.cacheignores/excludes,NODE_COMPILE_CACHEremovals), bumps pinned reusable workflow SHAs, and adds apreinstallbootstrap-from-registry step that fetches pinned zero-dep Socket packages intonode_modulesbefore install.Reviewed by Cursor Bugbot for commit 000c35e. Configure here.