Skip to content

Commit c3beacc

Browse files
authored
chore: env allowlist + .cache exclude + CLAUDE.md fleet rules (#624)
* chore: env allowlist + .cache exclude + CLAUDE.md fleet rules Doc/config-only updates split out from PR #620. CLAUDE.md - Sync sorting + open-PR + paths + inclusive-language + Set constructor sort + don't-revert-untouched rules from the fleet - Replace whitelist/blacklist with allowlist/denylist - Document workflow-dispatch rule .config/tsconfig.check.json - Restore .cache/** exclude (was previously dropped) .gitignore - Add **/.cache/ .claude/agents/security-reviewer.md .claude/skills/security-scan/SKILL.md - Sync from socket-repo-template canonical No runtime behavior changes. * chore(skills): narrow allowed-tools to specific commands Replaces blanket Bash/Grep/Glob entries with command-pattern-specific matchers (Bash(prefix:*)) so the skill cannot run anything the body of the skill doesn't actually invoke. Drops Grep/Glob from skills that don't use them. - security-scan: Task, Read, Bash(pnpm exec agentshield:*), Bash(zizmor:*), Bash(command -v:*), Bash(find .cache/external-tools/zizmor:*) - updating: Read, Edit, Bash(pnpm run:*), Bash(pnpm test:*), Bash(git status:*), Bash(git diff:*), Bash(git add:*), Bash(git commit:*), Bash(git log:*) Addresses billxinli's review on PR #624 / #1283. * chore(claude): add tools: frontmatter to agents, deny-list to settings, allowed-tools to quality-scan Extends the .claude/ tightening already on this branch: - Adds tools: frontmatter to all three agents so they declare exactly what they need instead of inheriting the default tool set: - code-reviewer (read-only): Read, Grep, Glob, Bash(git/rg/grep/find/ls/wc/cat/head/tail:*) - security-reviewer: same + Bash(pnpm exec agentshield:*), Bash(zizmor:*), Bash(command -v:*) - refactor-cleaner: adds Edit, Write, Bash(pnpm run/test/exec:*), Bash(node:*) - Adds permissions.deny block to .claude/settings.json blocking publish/release escape hatches: npm/pnpm/yarn publish, gh release create/delete, gh workflow run/dispatch, git push --force/-f. Particularly important for socket-sdk-js, which publishes to npm but currently has no Bash-targeted PreToolUse hooks. - Adds allowed-tools: to quality-scan/SKILL.md (was missing entirely, inheriting full default access). Mirrors the canonical pattern landed on socket-repo-template main.
1 parent e4f8c48 commit c3beacc

10 files changed

Lines changed: 113 additions & 4 deletions

File tree

.claude/agents/code-reviewer.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
---
2+
name: code-reviewer
3+
description: Reviews code in socket-sdk-js against CLAUDE.md rules and reports style violations, logic bugs, and test gaps. Spawned by the quality-scan skill or invoked directly on a diff.
4+
tools: Read, Grep, Glob, Bash(git:*), Bash(rg:*), Bash(grep:*), Bash(find:*), Bash(ls:*), Bash(wc:*), Bash(cat:*), Bash(head:*), Bash(tail:*)
5+
---
6+
17
You are a code reviewer for a Node.js/TypeScript monorepo (socket-sdk-js).
28

39
Apply the rules from CLAUDE.md sections listed below. Reference the full section in CLAUDE.md for details — these are summaries, not the complete rules.

.claude/agents/refactor-cleaner.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
---
2+
name: refactor-cleaner
3+
description: Refactor specialist for socket-sdk-js. Removes dead code first, batches changes into ≤5-file phases, verifies each with the project's check + test scripts. Use after quality-scan or before structural refactors.
4+
tools: Read, Edit, Write, Grep, Glob, Bash(git:*), Bash(rg:*), Bash(grep:*), Bash(find:*), Bash(ls:*), Bash(pnpm run:*), Bash(pnpm test:*), Bash(pnpm exec:*), Bash(node:*), Bash(cat:*), Bash(head:*), Bash(tail:*)
5+
---
6+
17
You are a refactoring specialist for a Node.js/TypeScript monorepo (socket-sdk-js).
28

39
Apply these rules from CLAUDE.md exactly:

.claude/agents/security-reviewer.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1+
---
2+
name: security-reviewer
3+
description: Reviews findings from AgentShield + zizmor against socket-sdk-js's CLAUDE.md security rules and grades the result A-F. Spawned by the security-scan skill after the static scans run.
4+
tools: Read, Grep, Glob, Bash(git:*), Bash(rg:*), Bash(grep:*), Bash(find:*), Bash(ls:*), Bash(pnpm exec agentshield:*), Bash(zizmor:*), Bash(command -v:*), Bash(cat:*), Bash(head:*), Bash(tail:*)
5+
---
6+
17
You are a security reviewer for Socket Security Node.js repositories.
28

39
Apply these rules from CLAUDE.md exactly:
410

511
**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.
612

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

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

1117
**Review checklist:**
1218

1319
1. **Secrets**: Hardcoded API keys, passwords, tokens, private keys in code or config
1420
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.
21+
3. **Dependencies**: npx/dlx usage. Unpinned versions (^ or ~). Missing minimumReleaseAge bypass justification. # zizmor: documentation-checklist
1622
4. **File operations**: fs.rm without safeDelete. process.chdir usage. fetch() usage (must use lib's httpRequest).
1723
5. **GitHub Actions**: Unpinned action versions (must use full SHA). Secrets outside env blocks. Template injection from untrusted inputs.
1824
6. **Error handling**: Sensitive data in error messages. Stack traces exposed to users.

.claude/settings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,18 @@
1111
]
1212
}
1313
]
14+
},
15+
"permissions": {
16+
"deny": [
17+
"Bash(gh release create:*)",
18+
"Bash(gh release delete:*)",
19+
"Bash(gh workflow dispatch:*)",
20+
"Bash(gh workflow run:*)",
21+
"Bash(git push --force:*)",
22+
"Bash(git push -f:*)",
23+
"Bash(npm publish:*)",
24+
"Bash(pnpm publish:*)",
25+
"Bash(yarn publish:*)"
26+
]
1427
}
1528
}

.claude/skills/quality-scan/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
name: quality-scan
33
description: Runs comprehensive quality scans across the codebase using specialized agents to identify critical bugs, logic errors, caching issues, and workflow problems. Use when improving code quality, before releases, or investigating issues.
4+
allowed-tools: Task, Skill, Read, Edit, Grep, Glob, AskUserQuestion, Bash(pnpm run check:*), Bash(pnpm run test:*), Bash(pnpm test:*), Bash(pnpm run fix:*), Bash(git status:*), Bash(git diff:*), Bash(git log:*), Bash(git add:*), Bash(git commit:*), Bash(rg:*), Bash(grep:*), Bash(find:*), Bash(ls:*)
45
---
56

67
# quality-scan

.claude/skills/security-scan/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
name: security-scan
33
description: Runs a multi-tool security scan — AgentShield for Claude config, zizmor for GitHub Actions, and optionally Socket CLI for dependency scanning. Produces an A-F graded security report. Use after modifying `.claude/` config, hooks, agents, or GitHub Actions workflows, and before releases.
44
user-invocable: true
5+
allowed-tools: Task, Read, Bash(pnpm exec agentshield:*), Bash(zizmor:*), Bash(command -v:*), Bash(find .cache/external-tools/zizmor:*)
56
---
67

78
# Security Scan

.claude/skills/updating/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name: updating
33
description: Updates all npm dependencies to their latest versions. Triggers when user asks to "update dependencies", "update packages", or prepare for a release.
44
user-invocable: true
5-
allowed-tools: Bash, Read, Grep, Glob, Edit
5+
allowed-tools: Read, Edit, Bash(pnpm run:*), Bash(pnpm test:*), Bash(git status:*), Bash(git diff:*), Bash(git add:*), Bash(git commit:*), Bash(git log:*)
66
---
77

88
# updating

.config/tsconfig.check.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@
1111
"verbatimModuleSyntax": false
1212
},
1313
"include": ["../**/*.ts", "../**/*.mts"],
14-
"exclude": ["../**/node_modules/**/*"]
14+
"exclude": ["../**/.cache/**", "../**/node_modules/**/*"]
1515
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ desktop.ini
5959
# store scratch dirs — cleared by pnpm install automatically).
6060
node_modules
6161
**/node_modules
62+
# Defensive cache ignore — Node compile-cache, corepack, and other
63+
# tools occasionally drop scratch dirs into a project-local .cache/.
64+
**/.cache/
6265

6366
# Misc temporary/generated files
6467
Do

CLAUDE.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,72 @@
2929

3030
- **REQUIRED for staging**: surgical `git add <specific-file> [<file>…]` with explicit paths. Never `-A` / `.`.
3131
- **If you need a quick WIP save**: commit on a new branch from inside a worktree, not a stash.
32+
- **NEVER revert files you didn't touch.** If `git status` shows files you didn't modify, those belong to another session, an upstream pull, or a hook side-effect — leave them alone. Specifically: do not run `git checkout -- <unrelated-path>` to "clean up" the diff before committing, and do not include unrelated paths in `git add`. Stage only the explicit files you edited.
3233

3334
The umbrella rule: never run a git command that mutates state belonging to a path other than the file you just edited.
3435

3536
## 📚 SHARED STANDARDS
3637

3738
- Commits: [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) `<type>(<scope>): <description>` — NO AI attribution
39+
- **Open PRs:** when adding commits to an OPEN PR, ALWAYS update the PR title and description to match the new scope. A title like `chore: foo` after you've added security-fix and docs commits to it is now a lie. Use `gh pr edit <num> --title "..." --body "..."` (or `--body-file`) and rewrite the body so it reflects every commit on the branch, grouped by theme. The reviewer should be able to read the PR description and know what's in it without scrolling commits.
3840
- Scripts: Prefer `pnpm run foo --flag` over `foo:bar` scripts
3941
- Dependencies: After `package.json` edits, run `pnpm install`
4042
- Backward Compatibility: 🚨 FORBIDDEN to maintain — actively remove when encountered
4143
- 🚨 **NEVER use `npx`, `pnpm dlx`, or `yarn dlx`** — use `pnpm exec <package>` or `pnpm run <script>`. Add tools as pinned devDependencies first.
4244
- **minimumReleaseAge**: NEVER add packages to `minimumReleaseAgeExclude` in CI. Locally, ASK before adding — the age threshold is a security control.
45+
- 🚨 **NEVER mention private repos or internal project names** in commits, PR titles/descriptions/comments, issues, release notes, or any public-surface text. Internal codenames, unreleased product names, internal tooling repo names not on the public org page, customer names, partner names — none belong in public surfaces. **Omit the reference entirely.** Don't substitute a placeholder ("an internal tool", "a downstream consumer", etc.) — the placeholder itself is a tell that something is being elided. Rewrite the sentence to not need the reference at all.
46+
- 🚨 **NEVER trigger Publish / Release / Provenance / Build-Release workflows** — no `gh workflow run`, `gh workflow dispatch`, or `gh api .../dispatches`. Workflow dispatches are irrevocable: Publish workflows push npm versions (unpublishable after 24h), Build/Release workflows pin GitHub releases by SHA, container workflows push immutable tags. Even build workflows with a `dry_run` input still treat the dispatch itself as the prod trigger. The user runs workflow_dispatch jobs manually after CI passes on the release commit + tag — Claude **never** dispatches them. If the user asks for a publish, tell them to run the command in their own terminal (or the GitHub Actions UI).
4347
- File existence: ALWAYS `existsSync` from `node:fs`. NEVER `fs.access`, `fs.stat`-for-existence, or an async `fileExists` wrapper. Import form: `import { existsSync, promises as fs } from 'node:fs'`.
4448
- Null-prototype objects: ALWAYS use `{ __proto__: null, ...rest }` for config, return, and internal-state objects. Prevents prototype pollution and accidental inheritance. See `src/socket-sdk-class.ts` and `src/file-upload.ts` for examples.
4549
- Linear references: NEVER reference Linear issues (e.g. `SOC-123`, `ENG-456`, Linear URLs) in code, code comments, or PR titles/descriptions/review comments. Keep the codebase and PR history tool-agnostic — tracking lives in Linear.
4650

51+
### Sorting
52+
53+
Sort lists alphanumerically (literal byte order, ASCII before letters). Apply this to:
54+
55+
- **Config lists**`permissions.allow` / `permissions.deny` in `.claude/settings.json`, `external-tools.json` checksum keys, allowlists in workflow YAML.
56+
- **Object key entries** — sort keys in plain JSON config + return-shape literals + internal-state objects. (Exception: `__proto__: null` always comes first, ahead of any data keys.)
57+
- **Import specifiers** — sort named imports inside a single statement: `import { encrypt, randomDataKey, wrapKey } from './crypto.mts'`. Imports that say `import type` follow the same rule. Statement _order_ is the project's existing convention (`node:` → external → local → types) — that's separate from specifier order _within_ a statement.
58+
- **Method / function source placement** — within a module, sort top-level functions alphabetically. Convention: private functions (lowercase / un-exported) sort first, exported functions second. The first-line `export` keyword is the divider.
59+
- **Array literals** — when the array is a config list, allowlist, or set-like collection. Position-bearing arrays (e.g. argv, anything where index matters semantically) keep their meaningful order.
60+
61+
When in doubt, sort. The cost of a sorted list that didn't need to be is approximately zero; the cost of an unsorted list that did need to be is a merge conflict.
62+
63+
### Paths: One Path, One Reference
64+
65+
**If a path appears in two places, that's a bug.** Every artifact (build output, cache directory, generated file, config location) lives at exactly one canonical location, and that location is defined in exactly one place — typically a `paths.mts` (or equivalent path helper) module. Everything else — other scripts, READMEs, Dockerfiles, workflows, tests — derives from that source. No hand-assembled `path.join(...)` strings outside the module that owns them.
66+
67+
- **Within a package**: every script imports its own path module. No script computes paths from raw segments.
68+
- **Across packages**: when package B consumes package A's artifact, B imports A's path module (or a typed helper exported from it) — never reconstructs the path from string segments. The classic failure: A adds a new path segment (e.g. inserts a `wasm/` directory), B's hand-built copy of the path drifts, builds break.
69+
- **Doc strings**: README "Output:" lines and `@fileoverview` comments describe the path; they don't _encode_ it for tools to parse. The doc is for humans only — and even there, it must match what the path module actually produces, verified by running the function.
70+
- **Workflows / Dockerfiles**: GitHub Actions YAML and Dockerfiles can't `import` TS, so they're allowed to reference the path string directly — but they MUST add a comment pointing at the canonical path module so the next person editing knows where the source of truth lives, and any path string must match the module byte-for-byte. If you find yourself writing the same path twice in one workflow, hoist it to a step output or a job-level env var; reference that everywhere downstream.
71+
- **Comments that re-state the path**: forbidden. A comment like `// Path mirrors getBuildPaths(): build/<mode>/<arch>/out/Final/...` is duplication wearing a comment costume. The import statement is the comment.
72+
73+
When you spot duplication, the answer is never "update both" — the answer is "delete one and import the other." Fix the architecture, not the symptom.
74+
75+
### Inclusive Language
76+
77+
Use precise, neutral terms over historical metaphors that imply hierarchy or exclusion. The substitutes are not euphemisms — they're more _accurate_ (a list of allowed values genuinely is an "allowlist"; "whitelist" is a metaphor that hides what the list does).
78+
79+
| Replace | With |
80+
| -------------------------------- | --------------------------------------------------- |
81+
| `whitelist` / `whitelisted` | `allowlist` / `allowed` / `allowlisted` |
82+
| `blacklist` / `blacklisted` | `denylist` / `denied` / `blocklisted` / `blocked` |
83+
| `master` (branch, process, copy) | `main` (branch); `primary` / `controller` (process) |
84+
| `slave` | `replica`, `worker`, `secondary`, `follower` |
85+
| `grandfathered` | `legacy`, `pre-existing`, `exempted` |
86+
| `sanity check` | `quick check`, `confidence check`, `smoke test` |
87+
| `dummy` (placeholder) | `placeholder`, `stub` |
88+
89+
Apply across **code** (identifiers, comments, string literals), **docs** (READMEs, CLAUDE.md, markdown), **config files** (YAML, JSON), **commit messages**, **PR titles/descriptions**, and **CI logs** you control.
90+
91+
Two exceptions where the legacy term must remain (because changing it breaks something external):
92+
93+
- **Third-party APIs / upstream code**: when interfacing with an external API field literally named `whitelist`, keep the field name; rename your local variable. E.g. `const allowedDomains = response.whitelist`.
94+
- **Vendored upstream sources**: don't rewrite vendored code (`vendor/**`, `upstream/**`, `**/fixtures/**`). Patch around it if needed.
95+
96+
When you encounter a legacy term during unrelated work, fix it inline — don't defer.
97+
4798
### Promise.race in loops
4899

49100
**NEVER re-race the same pool of promises across loop iterations.** Each call to `Promise.race([A, B, ...])` attaches fresh `.then` handlers to every arm; a promise that survives N iterations accumulates N handler sets. See [nodejs/node#17469](https://github.com/nodejs/node/issues/17469) and `@watchable/unpromise`.
@@ -73,6 +124,27 @@ Emojis allowed sparingly: 📦 💡 🚀 🎉. Prefer text-based symbols for ter
73124

74125
---
75126

127+
### 1 path, 1 reference
128+
129+
**A path is _constructed_ exactly once. Everywhere else _references_ the constructed value.**
130+
131+
Referencing a single computed path many times is fine — that's the whole point of computing it once. What's banned is _re-constructing_ the same path in multiple places, because that's where drift is born.
132+
133+
- **Within a package**: every script imports its own `scripts/paths.mts` (or `lib/paths.mts`). No `path.join('build', mode, ...)` outside that module.
134+
- **Across packages**: when package B consumes package A's output, B imports A's `paths.mts` via the workspace `exports` field. Never `path.join(PKG, '..', '<sibling>', 'build', ...)`.
135+
- **Workflows, Dockerfiles, shell scripts**: they can't `import` TS, so they construct the string once and reference it everywhere downstream. Workflows: a "Compute paths" step exposes `steps.paths.outputs.final_dir`; later steps read `${{ steps.paths.outputs.final_dir }}`. Dockerfiles/shell: assign once to a variable / `ENV`, reference by name thereafter. Each canonical construction carries a comment naming the source-of-truth `paths.mts`. **Re-building** the same path in a second step is the violation, not referring to the constructed value many times.
136+
- **Comments**: may describe path _structure_ with placeholders ("`<mode>/<arch>`") but should not encode a complete literal path string. The import statement IS the comment.
137+
138+
Code execution takes priority over docs: violations in `.mts`/`.cts`, Makefiles, Dockerfiles, workflow YAML, and shell scripts are blocking. README and doc-comment violations are advisory unless they contain a fully-qualified path with no parametric placeholders.
139+
140+
**Three-level enforcement:**
141+
142+
- **Hook**`.claude/hooks/path-guard/` blocks `Edit`/`Write` calls that would introduce a violation in a `.mts`/`.cts` file at edit time.
143+
- **Gate**`scripts/check-paths.mts` runs in `pnpm check`. Fails the build on any violation that isn't allowlisted in `.github/paths-allowlist.yml`.
144+
- **Skill**`/path-guard` audits the repo and fixes findings; `/path-guard check` reports only; `/path-guard install` drops the gate + hook + rule into a fresh repo.
145+
146+
The mantra is intentionally short so it sticks: **1 path, 1 reference**. When in doubt, find the canonical owner and import from it.
147+
76148
## 🏗️ SDK-SPECIFIC
77149

78150
### Architecture
@@ -173,6 +245,7 @@ Configs live in `.config/`:
173245
- Type properties: required first, then optional; alphabetical within groups
174246
- Class members: 1) private properties, 2) private methods, 3) public methods (alphabetical)
175247
- Object properties & destructuring: alphabetical (except semantic ordering)
248+
- `Set` constructor arguments: `new Set([...])` literals are alphanumeric (runtime is order-insensitive)
176249

177250
### Testing
178251

0 commit comments

Comments
 (0)