Best-effort pre-publish PII and secret check. Not a replacement for manual review, a full secret scanner, or a security audit. Use it as one more pair of eyes before you
npm publish,clawhub publish, orgit pushto public.
npx @symbolstar/shipcheckBy default, shipcheck scans only the files that would actually be published from an npm package (static package.json.files + .npmignore + .gitignore resolution — it does not invoke npm pack). For generic repos or OpenClaw skills:
shipcheck --scan-mode=dir ./path/to/repo| Category | Examples | Severity |
|---|---|---|
secrets |
AWS access key, GitHub PAT (ghp_/gho_/ghu_/ghs_/ghr_), OpenAI sk-, Anthropic sk-ant-, Google AIza…, Slack xox[bp]-…, JWT, PEM/SSH private key (~30 rules) |
critical |
identity |
Email, China mobile + E.164, /Users/<name>/ and /home/<name>/ absolute paths, SSH fingerprint |
high |
infra |
RFC1918 private IPs, 100.64/10 Tailscale CGNAT, *.tail<id>.ts.net, *.lan/*.local, private git remotes |
high |
business |
User-defined forbidden_terms from shipcheck.config.json (project codenames, internal product names…) |
medium |
softNL |
Chinese first-person personal context in *.md (我家 / 我老板 / 我同事 + 关系词) |
info |
binaries |
*.png/.jpg/.mp4/.zip/.pdf > 50 KB in the publish set |
warn |
# One-shot
npx -y @symbolstar/shipcheck
# As a dev dependency + prepublishOnly hook
npm i -D @symbolstar/shipcheck
npx shipcheck initshipcheck init merges scripts.prepublishOnly (it appends; it will not clobber an existing chain) and seeds a .shipcheckignore template.
shipcheck [path] # scan; default path is "."
shipcheck init # add prepublishOnly + .shipcheckignore template
shipcheck --allow <finding-id> # append an allow line to .shipcheckignore (asks for reason)
shipcheck --scan-mode=dir|npm # default: npm
shipcheck --format=stylish|json # default: stylish
shipcheck --severity-threshold=high # critical|high|medium|warn|info (default: high)
shipcheck --include-deps # include node_modules
shipcheck --include-git # include .git
shipcheck --max-size=10MB # max per-file content scan size
shipcheck --skip # bypass (red banner + logged to .shipcheck/skips.log)Exit codes follow the eslint shape: 0 clean, 1 blocking findings (>= threshold), 2 tool error.
Gitignore-style globs. Append # rule:<id> to scope the ignore to one rule (otherwise it ignores every rule for the matching files):
# Ignore everything in fixtures/ for the aws rule only
fixtures/** # rule:secret.aws-access-key
# Allow the email rule once for a specific file
README.md # rule:identity.email
# Untargeted: ignore the whole path for every rule
CHANGELOG.md{
"forbidden_terms": ["MyInternalProject", "secret-codename"]
}Each term is matched case-insensitively as a literal substring across scanned content. Use this to keep project codenames, customer names, or other org-specific strings out of public packages.
Stylish output (default) prints colorized findings grouped by file with rule id, severity, line/column, and a triage hint:
src/config.ts
14:9 high identity macOS home absolute path identity.macos-home-path 4f3db8c69dc6
hint: (a) allow (b) remove (c) replace with placeholder
JSON output (--format=json) writes .shipcheck/last-report.json with:
{
"tool": { "name": "@symbolstar/shipcheck", "version": "0.1.0" },
"gitSha": "099c371",
"files": [{ "file": "...", "size": 1234, "binaryKind": "png" }],
"findings": [{ "id": "...", "ruleId": "...", "severity": "high", "file": "...", "line": 14, "column": 9, "message": "...", "snippet": "...", "severityMasked": true }]
}--allow <finding-id> reads this report to look up the file path and rule id for the allow line.
- No entropy / heuristic scanning. v0.1 is pure regex + small DSLs. You will miss novel-format secrets. Use a dedicated secret scanner (gitleaks, trufflehog) for security-critical workflows.
- Best-effort manifest resolution.
shipcheckdoes not runnpm pack. Edge cases infilesglobs,bundleDependencies, or workspace packing may differ. - False positives are normal. That's why
.shipcheckignoreexists and--allowis interactive. Tune as you go. - Not a CI gate replacement. Treat the exit-1 as advisory, not load-bearing. If you bypass with
--skip, that bypass is logged to.shipcheck/skips.logfor after-the-fact accountability.
- gitleaks delegation behind
--use-gitleaks --baseline(snapshot known findings, fail only on new ones)- SARIF output
// shipcheck-allow:<rule-id>inline comment support- Entropy + redaction heuristics
- ClawHub publish hook + VS Code surface
MIT