{"_id":"@alikhanmammadli/secret-sentinel","name":"@alikhanmammadli/secret-sentinel","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@alikhanmammadli/secret-sentinel","version":"0.1.0","description":"A Claude Code PreToolUse hook that blocks hardcoded secrets, missing auth checks, open CORS, and disabled DB rules before they are written to disk.","license":"MIT","type":"module","engines":{"node":">=20.10"},"main":"./dist/src/hook.js","bin":{"secret-sentinel":"dist/bin/install.js"},"publishConfig":{"access":"public"},"keywords":["claude-code","hook","security","secrets","static-analysis"],"repository":{"type":"git","url":"git+https://github.com/Alikhan1980/secret-sentinel.git"},"bugs":{"url":"https://github.com/Alikhan1980/secret-sentinel/issues"},"homepage":"https://github.com/Alikhan1980/secret-sentinel#readme","scripts":{"build":"tsc -p tsconfig.json","dev":"tsc -p tsconfig.json --watch","clean":"node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"","typecheck":"tsc -p tsconfig.json --noEmit","pretest":"npm run build","test":"vitest run --passWithNoTests","test:watch":"vitest","prepublishOnly":"npm run clean && npm run build"},"devDependencies":{"@types/node":"^22.10.2","typescript":"^5.7.2","vitest":"^3.0.5"},"gitHead":"58691f211f610d317a0579079bbae6283ea5fae5","types":"./dist/src/hook.d.ts","_id":"@alikhanmammadli/secret-sentinel@0.1.0","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-wPQ7x+sfePSsqPxEmYMHQj811I9arLc3ty7gSZQsICaRwdFkhRiD7tUR7w83znK8RDrJSv8QYtfXiaz1j0KceA==","shasum":"2083392bd3238ebae8c46fe19ad0db8f94b55fcb","tarball":"https://registry.npmjs.org/@alikhanmammadli/secret-sentinel/-/secret-sentinel-0.1.0.tgz","fileCount":44,"unpackedSize":143855,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFPeDtG9vkW80I+FSReEP7OzAJGc+wddbz+XGHpysJZJAiEAni0Tq8+WWrty1WM5Rgoby6in5zkehoAW373D+XNxguY="}]},"_npmUser":{"name":"alikhanmammadli","email":"alikhanmammadli81@gmail.com"},"directories":{},"maintainers":[{"name":"alikhanmammadli","email":"alikhanmammadli81@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/secret-sentinel_0.1.0_1788381206483_0.10611249911543674"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-02T20:33:26.313Z","0.1.0":"2026-09-02T20:33:26.629Z","modified":"2026-09-02T20:33:26.842Z"},"maintainers":[{"name":"alikhanmammadli","email":"alikhanmammadli81@gmail.com"}],"description":"A Claude Code PreToolUse hook that blocks hardcoded secrets, missing auth checks, open CORS, and disabled DB rules before they are written to disk.","homepage":"https://github.com/Alikhan1980/secret-sentinel#readme","keywords":["claude-code","hook","security","secrets","static-analysis"],"repository":{"type":"git","url":"git+https://github.com/Alikhan1980/secret-sentinel.git"},"bugs":{"url":"https://github.com/Alikhan1980/secret-sentinel/issues"},"license":"MIT","readme":"# Secret Sentinel\n\n**A Claude Code hook that blocks security flaws the moment they'd be written to disk — not after they're committed, deployed, or exploited.**\n\nHardcoded secrets, missing auth checks, open CORS, disabled database rules. Secret Sentinel runs inside your Claude Code session as a `PreToolUse` hook, inspects every file Claude is about to write or edit, and stops the write before it happens.\n\n```\n🛑 BLOCKED — Hardcoded Stripe secret key detected\n\nFile: src/payments.ts, line 3\nFound: Stripe live secret key — sk_l************zXcV\n\nWhy this matters: this is a live Stripe key, not a test one. Anyone who can see\nthis file can charge your Stripe account, issue refunds, or read your customer data.\n```\n\n---\n\n## Why this exists\n\nAI-generated code ships with a higher vulnerability rate than hand-written code, and vibe-coded apps disproportionately leak secrets, skip auth, and leave databases wide open.\n\nTraditional workflows caught this with friction — code review, a senior engineer spotting a hardcoded key, a deploy checklist. Vibe coding removes that friction on purpose. That's the point of it. But it also removes the people who used to catch the mistakes.\n\nExisting tools — gitleaks, GitHub secret scanning, post-deploy scanners — all catch the problem *after* the fact: at commit time, at push time, or once the app is already live. By then you've already looked at the change, said \"looks good,\" and moved on.\n\nSecret Sentinel sits one step earlier, inside the loop itself.\n\n## Install\n\n```bash\nnpx @alikhanmammadli/secret-sentinel init\n```\n\nThat's it. It adds a `PreToolUse` entry to your project's `.claude/settings.json` and prints exactly what it wrote:\n\n```\nSecret Sentinel: added the PreToolUse hook in\n  /your/project/.claude/settings.json (created)\n\n  matcher: Write|Edit\n  command: node /path/to/secret-sentinel/dist/src/hook.js\n\nIt will check every file Claude Code writes or edits in this project.\n```\n\nNo account, no dashboard, no server. It merges into an existing `settings.json` without touching your other settings or hooks, and re-running it updates in place rather than adding a duplicate.\n\nRequires Node 20.10+.\n\n## What it catches\n\n| Scanner | Catches | Severity |\n|---|---|---|\n| **secrets** | AWS / Stripe / GitHub / Slack / Google keys, private key blocks, DB connection strings with credentials, generic `API_KEY = \"...\"` assignments, plus a Shannon-entropy fallback for secrets that match no known format | blocks |\n| **db-rules** | Firestore `allow read, write: if true`, Supabase RLS policies with `USING (true)` and no auth check, `DISABLE ROW LEVEL SECURITY` | blocks |\n| **cors** | Wildcard origins, `origin: true`, bare `cors()` | warns — **blocks** when paired with credentials |\n| **auth** | Express routes and Next.js API routes defined with no auth guard | warns |\n\n**Blocks** stop the write. **Warns** prompt you in-session, and you approve or reject right there.\n\n## Before / after\n\n### Blocked: a hardcoded secret\n\nClaude tries to write `src/payments.ts`:\n\n```ts\nimport Stripe from \"stripe\";\n\nexport const stripe = new Stripe(\"sk_live_51QwErTyUiOpAsDfGhJkLzXcV\");\n```\n\nThe write never lands. Claude sees this instead:\n\n```\n🛑 BLOCKED — Hardcoded Stripe secret key detected\n\nFile: src/payments.ts, line 3\nFound: Stripe live secret key — sk_l************zXcV\n\nWhy this matters: this is a live Stripe key, not a test one. Anyone who can see\nthis file can charge your Stripe account, issue refunds, or read your customer data.\n\nFix: move it to an environment variable instead:\n  1. Add STRIPE_SECRET_KEY=... to your .env file, using the value currently on line 3\n  2. Add .env to .gitignore if it isn't already\n  3. Reference it in code as process.env.STRIPE_SECRET_KEY\n\nIf this credential was ever committed or shared, rotate it — removing it from\nthe file does not un-leak it.\n\nOverride: if this is deliberate, tell Claude why and it can go ahead.\nTo stop it being flagged here again, add this to \"allow\" in .secret-sentinel.json:\n  { \"id\": \"secrets/stripe-live-key\", \"paths\": [\"src/payments.ts\"] }\n```\n\nNote the key is masked. The finding goes into your transcript, so it doesn't echo the credential back.\n\n### Blocked: an open database\n\nClaude tries to write `firestore.rules`:\n\n```\nmatch /{document=**} {\n  allow read, write: if true;\n}\n```\n\n```\n🛑 BLOCKED — Firestore rules allow public read/write\n\nFile: firestore.rules, line 4\nFound: a Firestore security rule granting access `if true` — that is, to everyone\n\nWhy this matters: this rule lets anyone on the internet read and write this\ncollection directly, without going through your app at all. They don't need an\naccount, and they don't need to find a bug — the database is simply open. This is\nthe single most common way vibe-coded apps leak their entire user table.\n\nFix: gate the rule on authentication and ownership instead of `true`:\n  1. Require a signed-in user:      allow read: if request.auth != null;\n  2. Scope writes to the owner:     allow write: if request.auth.uid == userId;\n  3. Re-deploy the rules and re-test with the Firestore rules simulator\n```\n\n### Warned: open CORS\n\nHeuristic findings prompt rather than block, so you stay in control:\n\n```\n⚠️  WARNING — Open CORS policy detected\n\nFile: src/server.ts, line 2\nFound: a CORS `origin` set to the `*` wildcard\n\nWhy this matters: any website on the internet can make browser requests to this\nAPI and read the responses. If any endpoint returns data that isn't fully public,\nanother site can pull it out of your users' browsers.\n\nFix: replace the wildcard with an explicit allowlist:\n  1. List the origins that actually need access, e.g.\n     const allowed = ['https://app.yourdomain.com'];\n  2. Pass that list as the origin: app.use(cors({ origin: allowed }))\n  3. Keep the list in an environment variable if it differs per environment\n```\n\nYou approve, and the write goes through. Reject, and Claude fixes it.\n\n## How it works\n\nClaude Code fires a `PreToolUse` hook before a tool call runs, and lets the hook allow, block, or prompt. Secret Sentinel is that hook.\n\n1. **Claude proposes a write.** Claude Code sends the hook a JSON payload on stdin with the tool name and its arguments.\n2. **The file is normalized.** `Write` carries the whole file. `Edit` carries only `old_string`/`new_string`, so Secret Sentinel reads the current file and applies the edit itself — scanners always see complete file content, never a fragment.\n3. **Scanners run.** Each is a pure `(filePath, content) => Finding[]` function. Every finding carries a plain-English explanation of what was found, why it's risky, and what to do instead.\n4. **Your config applies.** Ignored paths, allowlisted findings, and severity overrides from `.secret-sentinel.json`.\n5. **A decision goes back.** `deny` blocks the write outright. `ask` prompts you. Nothing found means the write proceeds under your normal permission settings.\n\n**It fails closed.** If a scanner crashes, the rules file is corrupt, or anything else goes wrong, Secret Sentinel blocks and tells you why, rather than waving the write through. A crash that blocks is a bug you report; a crash that allows is a leaked credential nobody notices.\n\n## Tuning it\n\nFalse positives are the reason tools like this get uninstalled, so there's a `.secret-sentinel.json` in your project root:\n\n```json\n{\n  \"ignorePaths\": [\"dist\", \"vendor\", \"**/*.generated.ts\"],\n  \"allow\": [\n    { \"id\": \"secrets/entropy\", \"paths\": [\"src/legacy/fixtures.ts\"] }\n  ],\n  \"severity\": {\n    \"cors\": \"block\",\n    \"auth\": \"warn\"\n  }\n}\n```\n\n- **`ignorePaths`** — globs that are never scanned at all.\n- **`allow`** — findings confirmed as false positives. Scope them to a path (as above) so you don't silence a rule everywhere; a bare `\"secrets/entropy\"` string suppresses it project-wide if that's genuinely what you want.\n- **`severity`** — override per scanner (`\"cors\"`) or per finding (`\"cors/wildcard-origin-option\"`).\n\nThe everyday override isn't this file, though — it's the in-session prompt. Reach for the config only when something keeps re-triggering.\n\n## Limitations\n\nWorth knowing before you rely on it:\n\n- **Write coverage is `Write` and `Edit`.** Files written via Bash redirects or other tools bypass the hook.\n- **The auth scanner sees one file at a time.** A Next.js route protected only by `middleware.ts` will warn. Mark it `// @public` to silence it.\n- **It is not a SAST tool.** The goal is the specific, high-frequency mistakes that actually ship — not comprehensive coverage. Keep using gitleaks and friends.\n- **Frameworks covered are Express and Next.js** for auth, Supabase and Firestore for database rules.\n\n## Development\n\n```bash\nnpm install\nnpm test\n```\n\n`npm test` builds first, then runs the suite: scanner fixtures (known-bad and known-good), config behavior, the installer's merge logic, and the fail-closed guarantees.\n\n## License\n\n[MIT](LICENSE)\n#\u0000 \u0000s\u0000e\u0000c\u0000r\u0000e\u0000t\u0000-\u0000s\u0000e\u0000n\u0000t\u0000i\u0000n\u0000a\u0000l\u0000\r\u0000\n\u0000","readmeFilename":"README.md","_rev":"1-6385ad8a4025f705af09aca0c5c34a46"}