If you discover a security vulnerability in Nanostack, please report it through GitHub Security Advisories.
Do not open a public issue.
- Guard rule bypass (command that should be blocked passes through)
- Artifact injection (malicious data in .nanostack/ artifacts that affects downstream skills)
- Setup script vulnerabilities (symlink attacks, path traversal)
- Secrets exposure in skill output or artifacts
- Command injection through bin/ scripts
- Vulnerabilities in the AI agent itself (Claude Code, Codex, etc.)
- Issues in code that nanostack reviews or generates (that's what /security is for)
- Third-party skill sets built on top of nanostack
| Stage | Timeline |
|---|---|
| Acknowledgment | 48 hours |
| Initial assessment | 7 days |
| Fix or mitigation | 30 days |
| Version | Supported |
|---|---|
| Latest on main | Yes |
| Older commits | Best effort |
We follow coordinated disclosure. We will:
- Confirm the vulnerability
- Develop a fix
- Release a patch
- Credit the reporter (unless anonymity is requested)
bin/init-project.sh writes entries to .claude/settings.json so autopilot can run without constant permission prompts. Nanostack treats the permission list as the first line of defense and the guard hooks as the authoritative one.
New installs receive:
Bash(rm:.nanostack/**)for sprint artifact cleanupBash(rm:/tmp/**)for temporary file cleanup
Any rm outside these paths prompts the user. Destructive deletion of arbitrary paths should be a conscious choice, not a silent default.
Bash(curl:*) remains in the default list because curl is a common dev-path primitive (testing endpoints, fetching release artifacts, hitting localhost). The dangerous case, piping curl output into a shell, is caught by the guard as a block rule:
G-023blockscurl ... | shG-024blockscurl ... | bash
The guard runs on every Bash tool use, ahead of the in-project fast-path, and logs every block to .nanostack/audit.log.
Installs that existed before the narrowing got Bash(rm:*) written to their .claude/settings.json. Running init-project.sh a second time does NOT remove it. The install keeps what it had.
/nano-doctor surfaces a warning when a broad Bash(rm:*) entry is present. To migrate without editing JSON by hand, run one of:
| Command | What it does |
|---|---|
init-project.sh --check |
Read-only diagnostic. Runs /nano-doctor and exits. |
init-project.sh --repair |
Adds missing hooks and adds narrow rm rules. Never removes existing entries. Safe to run on any project. |
init-project.sh --migrate-hooks |
Adds missing PreToolUse hooks only. |
init-project.sh --migrate-permissions |
Removes Bash(rm:*) and adds Bash(rm:.nanostack/**) and Bash(rm:/tmp/**). |
Every migration path makes a timestamped backup of .claude/settings.json before changing anything, and re-runs /nano-doctor at the end so you can see the new state without a separate command.
Coding agents need to write code, so Write(*) and Edit(*) stay broad in the permission list. The safety boundary for those tools lives in a dedicated PreToolUse hook: guard/bin/check-write.sh. It runs before every Write, Edit, and MultiEdit call and rejects a narrow denylist:
- Environment files with real secrets (
.env,.env.local,.env.production,.env.staging,.env.development,.env.dev,.env.prod). Template files like.env.example,.env.sample,.env.templateare allowed. - Private cryptographic material (
*.pem,*.key,*.p12,*.pfx). - SSH keys and config (
id_rsa,id_ed25519,id_ecdsa,id_dsa, their.pubpairs,authorized_keys,known_hosts). - Shell history (
.bash_history,.zsh_history,.python_history). - System directories (
/etc,/var,/usr/bin,/usr/sbin,/usr/lib,/System,/private/etc). - User secret directories (
~/.ssh,~/.gnupg,~/.aws,~/.gcp,~/.config/gcloud,~/.kube).
Fresh installs receive this hook wired automatically. Existing installs are not modified and need to wire it manually to gain the Write/Edit layer.
Add the following to your project's .claude/settings.json. The paths assume a standard install at ~/.claude/skills/nanostack.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{"type": "command", "command": "$HOME/.claude/skills/nanostack/guard/bin/check-dangerous.sh"}]
},
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [{"type": "command", "command": "$HOME/.claude/skills/nanostack/guard/bin/check-write.sh"}]
}
]
}
}Run /nano-doctor to verify; it warns when the hooks are missing.
| Layer | Purpose | Failure mode |
|---|---|---|
Permissions (.claude/settings.json) |
Cheap gate, no network call. | User can grant broad perms manually. |
Guard hooks (check-dangerous.sh, check-write.sh) |
Pattern match against block rules before a Bash, Write, or Edit call runs. | If hooks are disabled, permissions become the only gate. |
Audit trail (.nanostack/audit.log) |
Record every blocked and allowed command for post-hoc review. | If the store path is missing, logging silently no-ops; guard still blocks. |
Each layer is independent. The audit trail works even when the store path is unresolved; the guard works even when permissions are broad; the permissions work even if the guard is missing. That independence is the point.