Please report security issues privately, not as a public issue: open a GitHub security advisory, or email tuandv.job@gmail.com.
Please include what you were running, the steps to reproduce, and what an attacker gets out of it. Expect a first reply within a week.
Worth knowing before you go looking, because it narrows the surface a lot:
- No runtime dependencies.
package.jsonhas an empty dependency set; the only dev dependencies are TypeScript and@types/node. There is no supply chain to speak of. - No network calls, and no listening socket. Nothing is sent anywhere, there is no telemetry, and there is no account, token or server. Every command reads and writes local files and exits.
- It writes to your repo, and only these files.
ds bootstrapseeds.ds/— skipping anything already there, so it never overwrites a spec you wrote — adds two keys to.claude/settings.json, and appends one line to.gitignore.ds compilewrites the artifacts you asked for.ds map --writerewrites thehashfield in your spec frontmatter, andds sync --writeis those two together. Nothing else is created, and no file outside.ds/is treated as ours to replace.ds syncnever rewrites a spec body and never deletes an element: those are decisions, not measurements. .claude/settings.jsonis merged, never overwritten, and if it is not valid JSON nothing at all is written to it and you are told. That file is yours, and one stray comma must never cost somebody their whole configuration.- The plugin ships executable JavaScript that Claude Code runs as hooks — on session start,
after a file edit, and on stop. They only ever run the bundled CLI and print what it says;
every path exits 0, and none of them can block a tool call. They are in
dist-plugin/hooks/, and they are short enough to read. - The plugin's slash commands are permitted as
Bash(node:*). Inside a plugin the CLI is addressed by an interpolated absolute path (node "${CLAUDE_PLUGIN_ROOT}/bin/ds.js" …), and a narrower per-subcommand allowlist cannot match that — a permission entry that does not match blocks the command silently, with no reason shown. The practical effect is worth stating plainly: approving these commands approves runningnode, not only running dspec. - It shells out to
gitto list tracked files, to read a model at another revision, and to find the repo root. It reads; it never commits, pushes or rewrites history. - It reads your source files to fingerprint the symbols your specs point at. Those fingerprints are hashes, stored in your own repo.
DSpec is distributed only as a Claude Code plugin, from
tuna781/dspec. It is published nowhere else — there is no
npm package, and anything claiming to be one is not ours.
Fixes land on the latest released version only. Take them with /plugin update ds; the version
you are running is shown by ds doctor and in Claude Code's /plugin manager.