codebase-snapshot 0.0.0-stage → 0.9.0
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.
- package/CHANGELOG.md +1242 -0
- package/LICENSE +21 -0
- package/README.md +276 -2
- package/SECURITY.md +110 -0
- package/dist/bin.js +9840 -0
- package/dist/chunk-FJ4RQYY2.js +7885 -0
- package/dist/cli.cjs +9822 -0
- package/dist/cli.d.cts +124 -0
- package/dist/cli.d.ts +124 -0
- package/dist/cli.js +1931 -0
- package/dist/index.cjs +7973 -0
- package/dist/index.d.cts +710 -0
- package/dist/index.d.ts +710 -0
- package/dist/index.js +3 -0
- package/dist/types-Ca4PmkiQ.d.cts +491 -0
- package/dist/types-Ca4PmkiQ.d.ts +491 -0
- package/docs/agent.md +135 -0
- package/docs/cli.md +437 -0
- package/docs/configuration.md +424 -0
- package/docs/how-to-context-budget.md +90 -0
- package/docs/how-to-feed-a-repo-to-claude.md +122 -0
- package/docs/install.md +196 -0
- package/docs/output.md +264 -0
- package/docs/troubleshooting.md +384 -0
- package/docs/vs-code2prompt.md +66 -0
- package/docs/vs-gitingest.md +69 -0
- package/docs/vs-repomix.md +83 -0
- package/package.json +112 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ankur Halder
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,277 @@
|
|
|
1
|
-
#
|
|
1
|
+
# codebase-snapshot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Turn any repository into an LLM-ready snapshot: code packed into size-budgeted markdown
|
|
4
|
+
parts, indexed by a JSON manifest, with a secret audit — in one command.**
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/codebase-snapshot)
|
|
7
|
+
[](https://www.npmjs.com/package/codebase-snapshot)
|
|
8
|
+
[](https://github.com/ankurhalder/codebase-snapshot/blob/main/LICENSE)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx codebase-snapshot generate
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
That writes `./snapshot/` — a directory you can drop into a chat, a prompt, a RAG pipeline or
|
|
16
|
+
a repository review. It works on TypeScript and JavaScript projects, and on Python, Go, Rust,
|
|
17
|
+
Java/Kotlin, C#/.NET, PHP, Ruby, C/C++, Vue, Svelte and Astro, without configuration.
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Why
|
|
24
|
+
|
|
25
|
+
Feeding a real repository to a model is awkward: files are too big, context windows are too
|
|
26
|
+
small, and picking what matters by hand does not scale or repeat. `codebase-snapshot` does it
|
|
27
|
+
deterministically:
|
|
28
|
+
|
|
29
|
+
- **One file per part, never over budget.** `maxPartBytes` is a hard ceiling. A file bigger
|
|
30
|
+
than the budget is truncated with an explicit omission marker naming the budget rather than
|
|
31
|
+
blowing the limit — and above `largeFileBytes` it becomes a head/tail excerpt instead.
|
|
32
|
+
- **An index, not just text.** `manifest.json` lists every included file with its layer, kind,
|
|
33
|
+
language, part, hash and a structural summary — and every *excluded* file with the reason.
|
|
34
|
+
- **Provably current.** `validate` re-hashes the repository against the snapshot and exits
|
|
35
|
+
non-zero on any drift, so a pipeline can gate on context being fresh.
|
|
36
|
+
- **Deterministic where it matters.** Two runs over unchanged input give byte-identical
|
|
37
|
+
`parts/`, `reports/navigation.md` and `reports/project-tree.md`, and the same
|
|
38
|
+
`manifest.fingerprint`. Only `manifest.json`, the snapshot's `README.md`,
|
|
39
|
+
`reports/audit.md` and `.codebase-snapshot` change between runs, because they carry
|
|
40
|
+
timestamps, a run id and a pid.
|
|
41
|
+
- **Safe by default.** Credential files are skipped unless asked for, secret-shaped strings are
|
|
42
|
+
redacted, and a mistyped output directory cannot delete source code.
|
|
43
|
+
- **Usable by agents, not just humans.** A machine-readable contract and an installable skill
|
|
44
|
+
let an agent with no prior knowledge drive the whole thing.
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx codebase-snapshot generate # no install, one-off
|
|
50
|
+
npm install -D codebase-snapshot # or add it to a project
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This is a **0.x** release. Version numbers follow semver's 0.x conventions, so the CLI surface may change in a minor release until 1.0.0 — pin an exact version if you need a fixed surface today.`n`nAny npm-compatible client works — npm, yarn (classic and Berry with strict PnP), pnpm and bun —
|
|
54
|
+
each verified against the packed tarball before release. Requires **Node.js 18+**.
|
|
55
|
+
|
|
56
|
+
Published from GitHub Actions with [build provenance](https://docs.npmjs.com/generating-provenance-statements): npm shows the tarball was built from this repository at a tagged commit, and `npm audit signatures` verifies it.
|
|
57
|
+
|
|
58
|
+
→ [Installation guide](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/install.md)
|
|
59
|
+
|
|
60
|
+
## Quick start
|
|
61
|
+
|
|
62
|
+
One command does the whole first run — detects the project, writes a commented config, adds the
|
|
63
|
+
npm scripts and the devDependency, installs it, and generates the snapshot:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx codebase-snapshot setup
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Or step by step, if you want to see each stage:
|
|
71
|
+
npx codebase-snapshot init --yes # config + scripts + devDependency + .gitignore
|
|
72
|
+
npm install # the package, into node_modules
|
|
73
|
+
npm run snapshot # generate
|
|
74
|
+
npm run snapshot:validate # exit 0 = current with disk
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`setup` keeps a config the repository already has, and `setup --no-install` skips the install
|
|
78
|
+
(CI, offline, or a lockfile you would rather not touch).
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
📸 Generating snapshot for: /Users/you/project
|
|
82
|
+
Branch: main | Part budget: 300 KB
|
|
83
|
+
|
|
84
|
+
✅ Snapshot generated in 0.14s:
|
|
85
|
+
- Tracked files: 128
|
|
86
|
+
- Total lines: 9,431
|
|
87
|
+
- Total size: 0.41 MB
|
|
88
|
+
- Parts written: 3 under snapshot/parts/
|
|
89
|
+
- Reports: snapshot/reports/ (project-tree, navigation, audit)
|
|
90
|
+
- Manifest: snapshot/manifest.json
|
|
91
|
+
- Fingerprint: e4607b570268c8def0911babbace97e4a0196d9eaf2cac22c8847500883d70aa
|
|
92
|
+
- Integrity: passed
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The fingerprint is printed in full — all 64 hex characters, never elided — and it is the same
|
|
96
|
+
value as `manifest.fingerprint`.
|
|
97
|
+
|
|
98
|
+
## What you get
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
snapshot/
|
|
102
|
+
├── README.md human summary of this snapshot
|
|
103
|
+
├── manifest.json machine index: files, parts, hashes, config, skips
|
|
104
|
+
├── parts/part-001.md … the code, grouped and size-budgeted
|
|
105
|
+
└── reports/
|
|
106
|
+
├── project-tree.md directory tree of included files
|
|
107
|
+
├── navigation.md links between layers, kinds and parts
|
|
108
|
+
└── audit.md secrets, large files, parse diagnostics, git state
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Every artifact can be turned off (`--skip-artifacts`, `--only-artifacts`, or the config).
|
|
112
|
+
|
|
113
|
+
→ [Snapshot output and manifest schema](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/output.md)
|
|
114
|
+
|
|
115
|
+
## Built to be trusted with a whole repository
|
|
116
|
+
|
|
117
|
+
A snapshot is only useful if you can believe it, so the tool is explicit about everything it does
|
|
118
|
+
*not* contain:
|
|
119
|
+
|
|
120
|
+
- **Nothing is dropped silently.** Every excluded path is recorded in `manifest.skipped[]` with a
|
|
121
|
+
reason — `binary`, `gitignore`, `default-ignore`, `sensitive-file`, `sensitive-path`, `symlink`,
|
|
122
|
+
`outside-root`, `read-error`, `not-file`, `nested-repository`, `excluded` — and `explain <path>`
|
|
123
|
+
answers the same question for any single path.
|
|
124
|
+
- **Encoding is stated, not guessed.** UTF-16 files are transcoded instead of vanishing as
|
|
125
|
+
"binary"; a file that is not valid UTF-8 is kept but labelled `utf-8-lossy` with the count of
|
|
126
|
+
undecodable bytes, so you never mistake mojibake for source.
|
|
127
|
+
- **Counts describe what was emitted.** `includedBytes` and `includedLines` are measured from the
|
|
128
|
+
block in the part, not from the file on disk.
|
|
129
|
+
- **Drift is checked, not assumed.** `validate` compares every file's hash *and* a digest of the
|
|
130
|
+
settings the snapshot was generated with, so a changed budget, layer rule or secret pattern is
|
|
131
|
+
drift rather than silence.
|
|
132
|
+
- **Memory is bounded.** Files above 2 MB are not parsed for a structural summary (that is where a 50 MB file
|
|
133
|
+
used to reach 3 GB of resident memory), and 130 000 files in one directory or a 3 000-level path
|
|
134
|
+
scan without crashing.
|
|
135
|
+
- **`diff` compares payloads, not just paths.** Two snapshots of identical sources with different
|
|
136
|
+
settings say so instead of reporting "0 changed".
|
|
137
|
+
- **When git cannot list files** — an interrupted clone, a dehydrated `.git`, `safe.directory` —
|
|
138
|
+
the run refuses rather than quietly scanning something different, and `--discovery walk` is the
|
|
139
|
+
documented escape hatch.
|
|
140
|
+
|
|
141
|
+
## Supported projects
|
|
142
|
+
|
|
143
|
+
25 presets, detected automatically from the repository — each contributing layers, kinds,
|
|
144
|
+
ecosystem ignore patterns and a recommended part budget.
|
|
145
|
+
|
|
146
|
+
| | | | |
|
|
147
|
+
| :--- | :--- | :--- | :--- |
|
|
148
|
+
| Next.js | Nuxt | SvelteKit | Remix |
|
|
149
|
+
| Astro | Angular | Solid | Vue + Vite |
|
|
150
|
+
| React + Vite | NestJS | Express / Fastify / Koa | Node library |
|
|
151
|
+
| Monorepo | Django | FastAPI / Flask | Python library |
|
|
152
|
+
| Go | Rust | Java / Spring | .NET |
|
|
153
|
+
| Laravel (PHP) | Rails (Ruby) | Terraform / Docker infra | Docs-only |
|
|
154
|
+
| generic fallback | | | |
|
|
155
|
+
|
|
156
|
+
Non-TypeScript languages get import/export/class/function summaries rather than being treated
|
|
157
|
+
as opaque text. Vue, Svelte and Astro script blocks go through the real TypeScript compiler.
|
|
158
|
+
Every summary is labelled `ast`, `structural` or `heuristic` so you know how much to trust it —
|
|
159
|
+
Python, for example, is a line-scan `heuristic`, while JSON and Markdown get `structural`
|
|
160
|
+
shapes.
|
|
161
|
+
|
|
162
|
+
→ [Preset table and detection rules](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/configuration.md#project-presets)
|
|
163
|
+
|
|
164
|
+
## Commands
|
|
165
|
+
|
|
166
|
+
| Command | Does |
|
|
167
|
+
| :--- | :--- |
|
|
168
|
+
| `generate` *(default)* | Write the snapshot. |
|
|
169
|
+
| `validate` / `check` | Compare the snapshot with the repository; exit `1` on drift. |
|
|
170
|
+
| `report` | Snapshot health, layer metrics, secret audit. |
|
|
171
|
+
| `init` | Interactive (or `--yes`) setup that writes a self-documenting config. |
|
|
172
|
+
| `explain <path>` | Why one path is included, which part it lands in, or why it was skipped. |
|
|
173
|
+
| `diff <a> <b>` | Compare two snapshots. |
|
|
174
|
+
| `describe` | The machine-readable contract (`--format json`). |
|
|
175
|
+
| `skill` | Print or install the agent skill. |
|
|
176
|
+
|
|
177
|
+
Exit codes: `0` success · `1` drift or refused generation · `2` usage error.
|
|
178
|
+
|
|
179
|
+
→ [Full CLI reference](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/cli.md)
|
|
180
|
+
|
|
181
|
+
## For AI agents
|
|
182
|
+
|
|
183
|
+
Everything an agent needs is available as data, and the tool can install its own skill:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# The contract: workflow, every option and default, exit codes, presets, safety rules
|
|
187
|
+
npx codebase-snapshot describe --format json
|
|
188
|
+
|
|
189
|
+
# Install the skill into the registries agent tools read: .claude/skills/, .cursor/rules/
|
|
190
|
+
# (it adds those files to .gitignore, and never touches your own AGENTS.md)
|
|
191
|
+
npx codebase-snapshot skill --install
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
An agent told only "use `npx codebase-snapshot`" can read the contract, follow its
|
|
195
|
+
ordered workflow, and produce a validated snapshot unaided — that path is covered by a test.
|
|
196
|
+
|
|
197
|
+
→ [Using this with an AI agent](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/agent.md) · [AGENTS.md](https://github.com/ankurhalder/codebase-snapshot/blob/main/AGENTS.md) · [llms.txt](https://github.com/ankurhalder/codebase-snapshot/blob/main/llms.txt)
|
|
198
|
+
|
|
199
|
+
## Configuration
|
|
200
|
+
|
|
201
|
+
`snapshot.config.ts` (or `.mjs`) is written by `init` with **every** setting at its current
|
|
202
|
+
value, commented and grouped, so it doubles as the reference. Deleting any line is safe: the
|
|
203
|
+
defaults are real. The object is exported through a name — `export default { … }` is
|
|
204
|
+
`import/no-anonymous-default-export`, on by default in `eslint-config-next`.
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
const config = {
|
|
208
|
+
maxPartBytes: "120k", // hard budget per part
|
|
209
|
+
excludePatterns: ["vendor/**", "**/*.min.js"],
|
|
210
|
+
artifacts: { monolith: false }, // single-file output stays off
|
|
211
|
+
budgets: { default: "120k", byLayer: { docs: 40_000 } },
|
|
212
|
+
includeSensitiveFiles: false, // .env, *.pem … stay out
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export default config;
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
→ [Configuration reference](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/configuration.md)
|
|
219
|
+
|
|
220
|
+
## Programmatic API
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
import { generateSnapshot, resolveConfig, validateSnapshot, loadConfigFile } from "codebase-snapshot";
|
|
224
|
+
|
|
225
|
+
// Pass the repository explicitly: loadConfigFile returns only the config it found, so a
|
|
226
|
+
// directory with no config file would leave resolveConfig scanning process.cwd() instead.
|
|
227
|
+
const dir = "/path/to/repo";
|
|
228
|
+
const config = resolveConfig(await loadConfigFile(dir), { cwd: dir });
|
|
229
|
+
const manifest = generateSnapshot(config);
|
|
230
|
+
const validation = validateSnapshot(config);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
CommonJS works too (`require("codebase-snapshot")`), with type definitions for
|
|
234
|
+
both. The CLI and the API share one implementation, so anything the CLI does is available here.
|
|
235
|
+
|
|
236
|
+
## Documentation
|
|
237
|
+
|
|
238
|
+
| | |
|
|
239
|
+
| :--- | :--- |
|
|
240
|
+
| [Installation](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/install.md) | Every client, version pinning, agent skill install |
|
|
241
|
+
| [CLI reference](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/cli.md) | Commands, options, exit codes, examples |
|
|
242
|
+
| [Configuration](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/configuration.md) | Every setting, all 25 presets, budgets, hooks, secrets |
|
|
243
|
+
| [Snapshot output](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/output.md) | Directory layout and the full manifest schema |
|
|
244
|
+
| [AI agents](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/agent.md) | The contract, the skill, what an agent should avoid |
|
|
245
|
+
| [Troubleshooting](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/troubleshooting.md) | Every message and what to do about it |
|
|
246
|
+
| [Changelog](https://github.com/ankurhalder/codebase-snapshot/blob/main/CHANGELOG.md) | Release history and breaking changes |
|
|
247
|
+
|
|
248
|
+
## Guides
|
|
249
|
+
|
|
250
|
+
| | |
|
|
251
|
+
| :--- | :--- |
|
|
252
|
+
| [Feeding a codebase to ChatGPT, Claude or Cursor](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/how-to-feed-a-repo-to-claude.md) | The full recipe, including the parts most guides skip |
|
|
253
|
+
| [Budgeting LLM context](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/how-to-context-budget.md) | Choosing the numbers from the destination, not from taste |
|
|
254
|
+
| [vs repomix](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/vs-repomix.md) | Including where repomix is the better choice |
|
|
255
|
+
| [vs gitingest](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/vs-gitingest.md) | Including where gitingest is the better choice |
|
|
256
|
+
| [vs code2prompt](https://github.com/ankurhalder/codebase-snapshot/blob/main/docs/vs-code2prompt.md) | Including where code2prompt is the better choice |
|
|
257
|
+
|
|
258
|
+
## Safety
|
|
259
|
+
|
|
260
|
+
- **Credential files are excluded by default** — `.env`, `*.pem`, `*.key`, `.npmrc`, `.netrc`,
|
|
261
|
+
`.git-credentials`, `id_rsa`, `.kube/config`, `*.tfstate` — and listed in
|
|
262
|
+
`manifest.skipped[]` with the reason.
|
|
263
|
+
- **Secrets are redacted** inside ordinary files: only the matched span is masked, never the
|
|
264
|
+
whole line, and no excerpt reaches any artifact.
|
|
265
|
+
- **The output directory is owned by the tool.** Each run is written to a scratch directory and
|
|
266
|
+
swapped in atomically; the previous snapshot survives a failure.
|
|
267
|
+
- **It refuses to replace a directory it did not create**, so `--out-dir src` cannot destroy
|
|
268
|
+
source code.
|
|
269
|
+
|
|
270
|
+
## Contributing
|
|
271
|
+
|
|
272
|
+
Issues and pull requests are welcome — see [CONTRIBUTING.md](https://github.com/ankurhalder/codebase-snapshot/blob/main/CONTRIBUTING.md) and the
|
|
273
|
+
[code of conduct](https://github.com/ankurhalder/codebase-snapshot/blob/main/CODE_OF_CONDUCT.md). Security reports: [SECURITY.md](https://github.com/ankurhalder/codebase-snapshot/blob/main/SECURITY.md).
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
[MIT](https://github.com/ankurhalder/codebase-snapshot/blob/main/LICENSE) © Ankur Halder
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Only the latest published release receives security fixes.
|
|
6
|
+
|
|
7
|
+
| Version | Supported |
|
|
8
|
+
| :--- | :--- |
|
|
9
|
+
| 4.x | ✅ |
|
|
10
|
+
| 3.x and older | ❌ |
|
|
11
|
+
|
|
12
|
+
## Reporting a vulnerability
|
|
13
|
+
|
|
14
|
+
**Do not open a public issue for a security problem.**
|
|
15
|
+
|
|
16
|
+
Email **ankur.halder12345@gmail.com** with:
|
|
17
|
+
|
|
18
|
+
- what the issue is and which version you reproduced it on,
|
|
19
|
+
- the smallest reproduction you can construct,
|
|
20
|
+
- the impact you believe it has.
|
|
21
|
+
|
|
22
|
+
You can expect an acknowledgement within a few days. If the report is valid, a fix and a
|
|
23
|
+
release will follow, and you will be credited in the release notes unless you ask otherwise.
|
|
24
|
+
|
|
25
|
+
## Scope
|
|
26
|
+
|
|
27
|
+
`codebase-snapshot` reads a repository and writes files. The security-relevant surfaces are:
|
|
28
|
+
|
|
29
|
+
**Secret handling.** Credential-bearing files (`.env`, `*.pem`, `*.key`, `.npmrc`, `.netrc`,
|
|
30
|
+
`.git-credentials`, `id_rsa`, `.kube/config`, `*.tfstate` and similar) are excluded by default.
|
|
31
|
+
Secret-shaped strings inside ordinary files are redacted in the emitted parts; only the matched
|
|
32
|
+
span is masked and no excerpt of a matched line is written to any artifact.
|
|
33
|
+
|
|
34
|
+
Detection is **pattern-based**, so it recognises the shapes it knows and nothing else. A
|
|
35
|
+
secret deliberately split across two string literals, joined with a backslash line
|
|
36
|
+
continuation, or assembled by template-literal concatenation can be missed, because the
|
|
37
|
+
pattern never sees the whole value on one line.
|
|
38
|
+
|
|
39
|
+
In YAML and similar `key: value` files, an **unquoted** value that reads like an expression is
|
|
40
|
+
treated as code rather than as a literal and is not masked: `password: opts.db.password` and
|
|
41
|
+
`password: my.secret.value` are emitted as written, and so is a value that starts with
|
|
42
|
+
punctuation, such as `token: !hunter2hunter2`. Quote the value — `password: "…"` — to have it
|
|
43
|
+
redacted; a plain unquoted value with no dots or leading punctuation is masked.
|
|
44
|
+
|
|
45
|
+
A clean audit is therefore evidence that the known patterns did not fire — not a guarantee
|
|
46
|
+
that the repository holds no credentials.
|
|
47
|
+
|
|
48
|
+
Reports we want to hear about: a pattern that is *not* detected, a redaction that leaves the
|
|
49
|
+
secret readable elsewhere (a report file, the manifest, a navigation index), or a way to make
|
|
50
|
+
the tool emit a sensitive file without `--include-sensitive-files`.
|
|
51
|
+
|
|
52
|
+
**Filesystem safety.** The output directory is written to a scratch directory and swapped in
|
|
53
|
+
atomically, and it is only replaced when a previous run wrote it. Ownership is proved by the
|
|
54
|
+
`.codebase-snapshot` marker — a JSON object naming this generator, with a schema version, a
|
|
55
|
+
per-snapshot id and the entries it wrote — not by a filename and not by a manifest alone.
|
|
56
|
+
`outDir`/`tmpDir` may not be the working directory, an ancestor, or the filesystem root, and
|
|
57
|
+
may not contain one another.
|
|
58
|
+
|
|
59
|
+
The marker is **evidence, not authentication**. It is unsigned and carries no secret, so anyone
|
|
60
|
+
who can write a marker and a stub manifest with a matching digest can satisfy the check — but
|
|
61
|
+
anyone who can write those files can also delete the directory by hand. The guard exists to stop
|
|
62
|
+
the tool from deleting a directory *it* did not create; it is not a defence against an attacker
|
|
63
|
+
who already has write access to the output path.
|
|
64
|
+
|
|
65
|
+
Reports we want to hear about: any path configuration that deletes or overwrites files the tool
|
|
66
|
+
did not create.
|
|
67
|
+
|
|
68
|
+
**Supply chain.** The package has exactly one runtime dependency (`typescript`), publishes with
|
|
69
|
+
provenance from a pinned GitHub Actions workflow, and every third-party action in that workflow is pinned
|
|
70
|
+
to a full commit SHA.
|
|
71
|
+
|
|
72
|
+
## Trust boundary
|
|
73
|
+
|
|
74
|
+
A configuration file is **code, and it runs**.
|
|
75
|
+
|
|
76
|
+
`snapshot.config.ts`, `.js`, `.mjs` and `.cjs` are imported by the CLI and executed in your
|
|
77
|
+
Node process. That is deliberate and cannot be avoided: a config that can express a `RegExp`
|
|
78
|
+
or a `match` function — and both are documented — has to be evaluable code. Only
|
|
79
|
+
`snapshot.config.json`, `.snapshotrc`, `.snapshotrc.json` and the `"snapshot"` key of
|
|
80
|
+
`package.json` are inert: they are parsed as data, never executed.
|
|
81
|
+
|
|
82
|
+
So running the tool inside a repository loads the code that repository's config contains. The
|
|
83
|
+
repository you scan is not necessarily a repository you own — a vendor drop, a dependency
|
|
84
|
+
checkout, a pull request under review, a CI job that scans contributed code.
|
|
85
|
+
|
|
86
|
+
- **`--no-config` avoids it.** The flag ignores every config file and runs on the built-in
|
|
87
|
+
defaults plus the detected preset, so nothing from the repository is imported.
|
|
88
|
+
- **Do not run the tool in a repository you do not trust** without `--no-config` — and do not
|
|
89
|
+
point `--cwd` at one. Review the config first, or run the scan somewhere with no credentials
|
|
90
|
+
in reach.
|
|
91
|
+
- **`hooks.afterGenerate` is the same surface.** It is a function in the config, called after
|
|
92
|
+
a successful run, in the process working directory, with the manifest as its argument.
|
|
93
|
+
|
|
94
|
+
This is a property of configuration-as-code, not a bug to be reported. What *is* in scope is
|
|
95
|
+
anything that widens it: a config format that executes when the documentation says it is
|
|
96
|
+
inert, a config loaded from a location the docs do not name, or a path where config code runs
|
|
97
|
+
when `--no-config` was passed.
|
|
98
|
+
|
|
99
|
+
## Not in scope
|
|
100
|
+
|
|
101
|
+
- The declared behaviour of `--include-sensitive-files` or a `customPatterns` redaction rule the
|
|
102
|
+
user configured themselves.
|
|
103
|
+
- Obfuscated forms: a credential packed inside an archive, compressed, or otherwise not
|
|
104
|
+
recognisable by shape is not something the redaction rules can find, and a report that one
|
|
105
|
+
survived this way is out of scope rather than a defect. One obfuscation *is* covered: a
|
|
106
|
+
base64-encoded PEM file, whose encoded form begins `LS0tLS1CRUdJTi` (`-----BEGIN `), is matched
|
|
107
|
+
by the `base64-private-key` rule without decoding it.
|
|
108
|
+
- Secrets that are already committed to a public repository, unless the tool emits them into a
|
|
109
|
+
snapshot they would otherwise not appear in.
|
|
110
|
+
- Anything that requires an attacker to already control the machine running the tool.
|