Skip to content

Commit 28af4c5

Browse files
committed
chore: env allowlist + path-guard + token-guard + hooks .mts conversion
Consolidates the work previously split across PRs #620 (env allowlist), #621 (path-guard infra), and #622 (.sh→.mts hook conversion) into a single commit on chore/harden-env-allowlist. What's included: Env allowlist + .cache/ + CLAUDE.md - Drop NODE_COMPILE_CACHE-equivalent stale env entries - Allow .env.precommit at any depth in commit-msg hook - Skip hook scripts in scanners (they contain the literal regex) - Exclude .cache/** in tsconfig.check.json - 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) Token-guard renamed from token-hygiene - Word-boundary match for sensitive env names (no more false positives on substring matches inside identifiers) - 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 for personal paths, AWS keys, GitHub tokens, private keys, AI attribution - .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 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 to npm Dep - Add @sinclair/typebox 0.34.49 to devDependencies (used by scripts/xport-schema.mts)
1 parent 9d5f189 commit 28af4c5

44 files changed

Lines changed: 6626 additions & 474 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/agents/security-reviewer.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ Apply these rules from CLAUDE.md exactly:
44

55
**Safe File Operations**: Use safeDelete()/safeDeleteSync() from @socketsecurity/lib/fs. NEVER fs.rm(), fs.rmSync(), or rm -rf. Use os.tmpdir() + fs.mkdtemp() for temp dirs. NEVER use fetch() — use httpJson/httpText/httpRequest from @socketsecurity/lib/http-request.
66

7-
**Absolute Rules**: NEVER use npx, pnpm dlx, or yarn dlx. Use pnpm exec or pnpm run with pinned devDeps.
7+
**Absolute Rules**: NEVER use npx, pnpm dlx, or yarn dlx. Use pnpm exec or pnpm run with pinned devDeps. # zizmor: documentation-prohibition
88

99
**Work Safeguards**: Scripts modifying multiple files must have backup/rollback. Git operations that rewrite history require explicit confirmation.
1010

1111
**Review checklist:**
1212

1313
1. **Secrets**: Hardcoded API keys, passwords, tokens, private keys in code or config
1414
2. **Injection**: Command injection via shell: true or string interpolation in spawn/exec. Path traversal in file operations.
15-
3. **Dependencies**: npx/dlx usage. Unpinned versions (^ or ~). Missing minimumReleaseAge bypass justification.
15+
3. **Dependencies**: npx/dlx usage. Unpinned versions (^ or ~). Missing minimumReleaseAge bypass justification. # zizmor: documentation-checklist
1616
4. **File operations**: fs.rm without safeDelete. process.chdir usage. fetch() usage (must use lib's httpRequest).
1717
5. **GitHub Actions**: Unpinned action versions (must use full SHA). Secrets outside env blocks. Template injection from untrusted inputs.
1818
6. **Error handling**: Sensitive data in error messages. Stack traces exposed to users.

.claude/hooks/check-new-deps/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "@socketsecurity/hook-check-new-deps",
2+
"name": "hook-check-new-deps",
33
"private": true,
44
"type": "module",
55
"main": "./index.mts",
@@ -11,7 +11,7 @@
1111
},
1212
"dependencies": {
1313
"@socketregistry/packageurl-js": "1.4.2",
14-
"@socketsecurity/lib": "5.21.0",
14+
"@socketsecurity/lib": "5.24.0",
1515
"@socketsecurity/sdk": "4.0.1"
1616
},
1717
"devDependencies": {

.claude/hooks/path-guard/README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# path-guard
2+
3+
Claude Code `PreToolUse` hook that refuses `Edit`/`Write` tool calls that would *construct* a multi-segment build/output path inline in a `.mts` or `.cts` file. Mandatory across the Socket fleet — every repo ships this file byte-for-byte via `scripts/sync-scaffolding.mjs`.
4+
5+
**Mantra: 1 path, 1 reference.**
6+
7+
Construct a path *once* in the canonical `paths.mts` (or a build-infra helper); reference the computed value everywhere else.
8+
9+
## What it blocks
10+
11+
| Rule | Example | Fix |
12+
|------|---------|-----|
13+
| **A** — Multi-stage path constructed inline | `path.join(PKG, 'build', mode, 'out', 'Final', name)` | Construct in the package's `scripts/paths.mts` (or use `getFinalBinaryPath` from `build-infra/lib/paths`); import the computed value here |
14+
| **B** — Cross-package path traversal | `path.join(PKG, '..', 'lief-builder', 'build', ...)` | Add `lief-builder: workspace:*` as a dep; import its `paths.mts` via the workspace `exports` field |
15+
16+
The hook fires on `Edit` and `Write` tool calls when the target path ends in `.mts` or `.cts`. Other extensions (`.ts`, `.mjs`, `.js`, `.yml`, `.json`, `.md`) pass through — TS path code lives in `.mts` per CLAUDE.md, and other file types are covered by the `scripts/check-paths.mts` gate at commit time.
17+
18+
## What it allows
19+
20+
- Edits to a `paths.mts` (canonical constructor — every package's source of truth).
21+
- Edits to `scripts/check-paths.mts` (the gate, which legitimately enumerates patterns).
22+
- Edits to this hook's own files (the test suite has to enumerate the same patterns).
23+
- Edits to `scripts/check-consistency.mts` (existing path-scanning gate).
24+
- `path.join` calls with a single stage segment (e.g. `path.join(packageRoot, 'build', 'temp')`) — that's a one-off helper path, not a multi-stage build output.
25+
- `path.join` calls with no stage segments at all (most general-purpose joins).
26+
- Any string concatenation that doesn't go through `path.join` — the hook is regex-based and intentionally narrow; the gate runs a deeper scan at commit time.
27+
28+
## Stage segments the hook recognizes
29+
30+
These come from `build-infra/lib/constants.mts` `BUILD_STAGES` plus the lowercase directory-name siblings used by some builders:
31+
32+
`Final`, `Release`, `Stripped`, `Compressed`, `Optimized`, `Synced`, `wasm`, `downloaded`
33+
34+
Two or more in the same `path.join` call (or one stage + one of `'build'`/`'out'` + one mode `'dev'`/`'prod'`) triggers Rule A.
35+
36+
## Known sibling packages (for Rule B)
37+
38+
The hook recognizes Rule B traversals only when the next segment after `..` is a known fleet package name:
39+
40+
`binflate`, `binject`, `binpress`, `bin-infra`, `build-infra`, `codet5-models-builder`, `curl-builder`, `iocraft-builder`, `ink-builder`, `libpq-builder`, `lief-builder`, `minilm-builder`, `models`, `napi-go`, `node-smol-builder`, `onnxruntime-builder`, `opentui-builder`, `stubs-builder`, `ultraviolet-builder`, `yoga-layout-builder`
41+
42+
When a new package joins the workspace, add it here.
43+
44+
## Control flow
45+
46+
The hook reads the tool-use payload from stdin, type-checks `tool_name === 'Edit'` or `'Write'`, filters to `.mts`/`.cts` files, and runs `check(source)`. Any rule violation `throw`s a typed `BlockError`; a single top-level `try/catch` in `main()` writes the block message to stderr and sets `process.exitCode = 2`.
47+
48+
Hook bugs fail **open** — a crash in the hook writes a log line and returns exit 0 so legitimate work isn't blocked on a bad deploy. The companion `scripts/check-paths.mts` gate runs a thorough whole-repo scan at `pnpm check` time, catching anything the hook misses.
49+
50+
## Testing
51+
52+
```bash
53+
pnpm --filter hook-path-guard test
54+
```
55+
56+
Adding a new detection pattern: update `STAGE_SEGMENTS` (or `KNOWN_SIBLING_PACKAGES`) in `index.mts`, add a positive and negative test in `test/path-guard.test.mts`.
57+
58+
## Updating across the fleet
59+
60+
This file is in `IDENTICAL_FILES` in `scripts/sync-scaffolding.mjs` (in `socket-repo-template`). After editing, run from `socket-repo-template`:
61+
62+
```bash
63+
node scripts/sync-scaffolding.mjs --all --fix
64+
```
65+
66+
to propagate the change to every fleet repo.

0 commit comments

Comments
 (0)