@vibefoundry/vibe-shield 1.1.0 → 2.0.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.
Files changed (39) hide show
  1. package/README.md +74 -48
  2. package/SECURITY.md +1 -1
  3. package/SKILL.md +27 -94
  4. package/dist/bin/vibe-shield.d.ts +1 -1
  5. package/dist/bin/vibe-shield.js +572 -541
  6. package/dist/src/init/scaffold.d.ts +29 -31
  7. package/dist/src/init/scaffold.js +69 -153
  8. package/dist/src/mcp/server.js +6 -2
  9. package/dist/src/mcp-guard/skill-auditor.js +11 -3
  10. package/dist/src/reporter/console-reporter.js +17 -3
  11. package/dist/src/reporter/executive.d.ts +4 -0
  12. package/dist/src/reporter/executive.js +58 -0
  13. package/dist/src/reporter/formatters.d.ts +1 -1
  14. package/dist/src/reporter/formatters.js +10 -3
  15. package/dist/src/reporter/patch-prompt-writer.js +5 -5
  16. package/dist/src/scanner/attack-surface.d.ts +4 -0
  17. package/dist/src/scanner/attack-surface.js +50 -0
  18. package/dist/src/scanner/engine.d.ts +2 -3
  19. package/dist/src/scanner/engine.js +21 -35
  20. package/dist/src/scanner/mappings.d.ts +17 -0
  21. package/dist/src/scanner/mappings.js +39 -0
  22. package/dist/src/scanner/redact.d.ts +1 -0
  23. package/dist/src/scanner/redact.js +12 -0
  24. package/dist/src/scanner/rules/database-exposure.js +1 -1
  25. package/dist/src/scanner/rules/hardening.d.ts +5 -0
  26. package/dist/src/scanner/rules/hardening.js +116 -0
  27. package/dist/src/scanner/rules/helpers.js +2 -0
  28. package/dist/src/scanner/rules/index.js +6 -0
  29. package/dist/src/scanner/rules/response-hardening.d.ts +2 -0
  30. package/dist/src/scanner/rules/response-hardening.js +120 -0
  31. package/dist/src/scanner/rules/secret-entropy.js +0 -2
  32. package/dist/src/scanner/rules/shared.d.ts +1 -1
  33. package/dist/src/scanner/rules/shared.js +1 -1
  34. package/dist/src/scanner/types.d.ts +33 -1
  35. package/dist/src/scanner/types.js +6 -2
  36. package/dist/src/scanner/version.d.ts +2 -0
  37. package/dist/src/scanner/version.js +69 -0
  38. package/dist/src/secrets/patterns.js +40 -0
  39. package/package.json +99 -96
@@ -1,5 +1,5 @@
1
1
  /**
2
- * vibe-shield :: `npx github:avivgerz-hue/vibe-shield init`
2
+ * vibe-shield :: `npx -y @vibefoundry/vibe-shield init`
3
3
  * -------------------------------------
4
4
  * One command turns any repository into a guarded one. It installs:
5
5
  *
@@ -36,14 +36,14 @@ export interface InitOptions {
36
36
  force?: boolean;
37
37
  /** Print what would happen without writing. */
38
38
  dryRun?: boolean;
39
- /** Pin a specific version in the workflow (default `latest`). */
40
- version?: string;
41
39
  /**
42
- * Commit to pin the `.mcp.json` entry to. Resolved from the installed package
43
- * when omitted; see `resolveInstallRev`.
40
+ * Version to pin in both the workflow and the `.mcp.json` entry.
41
+ *
42
+ * Resolved from the installed package when omitted; see
43
+ * `packageVersionFromDisk`.
44
44
  */
45
- rev?: string | null;
46
- /** Environment to read the revision override from. Defaults to `process.env`. */
45
+ version?: string | null;
46
+ /** Environment to read the version override from. Defaults to `process.env`. */
47
47
  env?: NodeJS.ProcessEnv;
48
48
  }
49
49
  export interface InitResult {
@@ -54,8 +54,8 @@ export interface InitResult {
54
54
  blocked: string[];
55
55
  warnings: string[];
56
56
  }
57
- export declare const SKILL_TEMPLATE = "---\nname: vibe-shield\ndescription: Audit any change for AI-introduced security bugs (leaked keys, unguarded API routes, disabled Row Level Security, SQL injection, prompt injection, poisoned MCP servers) and fix them before reporting a task as done. Use whenever you write or modify application code, API routes, server actions, database migrations, auth logic, environment variables, LLM calls, webhook handlers, agent skills or MCP configuration.\nallowed-tools: Bash(npx github:avivgerz-hue/vibe-shield scan*), Bash(npx github:avivgerz-hue/vibe-shield audit-skills*), Bash(npx github:avivgerz-hue/vibe-shield explain*), Read(./**), Grep, Glob\nlicense: MIT\nmetadata:\n author: VibeFoundry\n homepage: https://github.com/avivgerz-hue/vibe-shield\n version: 1.0.0\n---\n\n# vibe-shield\n\nYou are not allowed to say \"done\" until the code you just wrote passes a security audit.\n\nAI coding agents ship a specific, repeatable class of vulnerability: a `SUPABASE_SERVICE_ROLE_KEY` behind a `NEXT_PUBLIC_` prefix, a `DELETE` route handler with no session check, a `CREATE TABLE` with no `ENABLE ROW LEVEL SECURITY`, a tool whose `execute` forwards model output into `exec`. These are not exotic \u2014 they are the default shape of code written quickly by something optimising for \"it works\". This skill is the second pair of eyes.\n\n## When to run\n\nRun the audit **after every change that touches**:\n\n- an API route, route handler, server action, RPC endpoint or GraphQL mutation\n- authentication, session, role, permission or ownership logic\n- a database migration, policy, grant, schema or Supabase table\n- an environment variable, config file, `.env*` file or build config\n- anything calling an LLM, a tool, a webhook, a shell command or the filesystem\n- `.claude/`, `.cursor/`, `AGENTS.md`, `mcp.json` or any skill/config file\n\nIf you did not touch any of those, you do not need to run it.\n\n## The loop\n\n```bash\n# 1. Scan (a whole-repo scan finishes in well under a second)\nnpx github:avivgerz-hue/vibe-shield scan --fail-on high\n\n# 2. If it fails, generate the machine-readable fix list\nnpx github:avivgerz-hue/vibe-shield scan --fix-prompt\n\n# 3. Read SECURITY_PATCH_PROMPT.md and fix every finding, critical first\n# Each entry has the exact file:line, the vulnerable snippet, the CWE and\n# the required change. Apply it literally.\n\n# 4. Re-run until it exits 0\nnpx github:avivgerz-hue/vibe-shield scan --fail-on high\n```\n\n`--fail-on high` exits non-zero while any high or critical finding exists. That is the gate.\n\n## Reading a finding\n\n```\nCRITICAL 47:15 VSB-CLIENT-001 Server-only secret exposed through a public environment prefix\n const key = process.env.NEXT_PUBLIC_OPENAI_API_KEY;\n```\n\n| Part | Meaning |\n| --- | --- |\n| `47:15` | exact line and column to edit |\n| `VSB-CLIENT-001` | rule id \u2014 use it with `explain` for full docs |\n| message | why this specific line is exploitable |\n| `(CWE-200)` | bug class, for a deeper search if the fix is unclear |\n\nNeed the full picture? `npx github:avivgerz-hue/vibe-shield explain VSB-CLIENT-001` prints the vulnerable shape, the fixed shape, remediation steps and references.\n\n## Hard rules\n\n1. **Fix the root cause.** Never delete an auth check, disable RLS, silence a log or add a catch-all `catch {}` to make the finding disappear.\n2. **Never suppress.** Do not add `// vibe-shield-disable`, do not edit `.vibe-shieldignore`, do not pass `--ignore`. Suppression is a decision for the human.\n3. **Rotate exposed credentials.** If the scan reported a live key, tell the user to rotate it at the provider in the same message. Removing the line does not un-leak it \u2014 the value may already be in a bundle, a cache or a git history.\n4. **Report honestly.** If you cannot fix a finding (you need a decision, a credential or a provider change), say so explicitly, name the rule id, and stop.\n5. **Re-verify.** A fix is not finished until the gate exits 0.\n\n## Agent surface audit\n\nWhen you add or modify a skill, rule file, MCP server or instruction file, also run:\n\n```bash\nnpx github:avivgerz-hue/vibe-shield audit-skills\n```\n\nTwelve `VSB-AGENT-*` rules check for hidden Unicode (zero-width and bidi characters used to smuggle instructions past review), `curl`/`wget` exfiltration of environment variables, credential-harvesting instructions (`.env`, `~/.ssh`, `~/.aws`), registry-tag or remote MCP servers, literal tokens in an `env` block, safety-override language, over-broad tool permissions, base64 `eval` payloads, remote-instruction fetching, wildcard `allowed-tools`, and shell injection in skill scripts.\n\nEach finding carries a `triage` field: `block` means refuse and escalate, `review` means ask the user, `report` means mention it and continue. Do not blanket-refuse a repository because a rule fired \u2014 read the triage.\n\n## Flags worth knowing\n\n| Flag | Effect |\n| --- | --- |\n| `--format json` | full machine-readable report (schemaVersion 1) |\n| `--format sarif` | SARIF 2.1.0 for GitHub code scanning |\n| `--format compact` | one line per finding |\n| `--format github` | inline GitHub Actions annotations |\n| `--severity high` | only report high and critical |\n| `--fail-on medium` | stricter gate |\n| `--include-tests` | include test and fixture files |\n| `--cache` | reuse results between runs |\n| `--quiet` | one-line verdict (ideal for piping) |\n| `rules` | list every rule |\n| `explain <RULE_ID>` | full documentation for one rule |\n\nIf the repository has a `.mcp.json`, the same three capabilities are also\navailable as MCP tools \u2014 `vibe_shield_scan`, `vibe_shield_audit_skills` and\n`vibe_shield_explain_rule`. Prefer those when they are listed, because they\nreturn structured findings instead of text you have to parse.\n\n## Definition of done\n\n- [ ] `npx github:avivgerz-hue/vibe-shield scan --fail-on high` exits `0`\n- [ ] `npx github:avivgerz-hue/vibe-shield audit-skills` exits `0` if you touched agent files\n- [ ] every credential the scan surfaced has been flagged to the user for rotation\n- [ ] no suppression directive was added\n- [ ] the feature still works and the existing tests still pass\n\n## Install it into a repository\n\n```bash\nnpx github:avivgerz-hue/vibe-shield init\n```\n\nThat writes this file to `.claude/skills/vibe-shield/SKILL.md`, appends a security\ngate section to `AGENTS.md`, adds a `.cursor/rules/vibe-shield.mdc`, and installs a\nGitHub Actions workflow that fails the pull request on new high or critical\nfindings. Every future task in that repository starts guarded.\n";
58
- export declare const AGENTS_SECTION = "## Security gate (vibe-shield)\n\nThis repository is guarded by [vibe-shield](https://github.com/avivgerz-hue/vibe-shield).\n\nBefore you report any task as complete:\n\n1. Run `npx github:avivgerz-hue/vibe-shield scan --fail-on high` and fix every finding it reports.\n2. Never suppress a finding (`vibe-shield-disable`, `.vibe-shieldignore` edits,\n `--ignore`) to make the gate pass \u2014 that is the human's call.\n3. If you touched `.claude/`, `.cursor/`, `AGENTS.md`, `mcp.json` or a skill,\n also run `npx github:avivgerz-hue/vibe-shield audit-skills`.\n4. Surface any exposed credential to the user for rotation. Do not \"fix\" it by\n deleting the line and moving on.\n\nThe gate exists because the most common way AI-written code reaches production\nwith a leaked `SUPABASE_SERVICE_ROLE_KEY` or an unauthenticated `DELETE`\nendpoint is that nobody asked it a second question.\n";
57
+ export declare const SKILL_TEMPLATE = "---\nname: vibe-shield\ndescription: Apply a six-phase defensive security review to changes affecting applications, APIs, data, AI agents, and MCP configuration. Use static evidence, preserve behavior, and report limitations.\nallowed-tools: Bash(npx -y @vibefoundry/vibe-shield scan*), Bash(npx -y @vibefoundry/vibe-shield audit-skills*), Bash(npx -y @vibefoundry/vibe-shield explain*), Read(./**), Grep, Glob\nlicense: MIT\nmetadata:\n author: VibeFoundry\n homepage: https://github.com/avivgerz-hue/vibe-shield\n version: 2.0.0\n---\n\n# Defensive AppSec architect protocol\n\nWork only within the authorized project. Static findings are evidence for review, not proof of exploitability or a guarantee of safety. Do not transmit source code or secrets. Ask before changing production services, rotating credentials, or making destructive changes.\n\n## Phase 1: Attack-surface reconnaissance\n\nMap entrypoints and trust boundaries: API methods, authentication and authorization, middleware matchers, database tables and policies, storage, LLM inputs and tools, webhooks, and agent/MCP configuration. Check deployment configuration and exclusions. Identify what the scanner cannot see.\n\n## Phase 2: Automated static scan and skill audit\n\n```bash\nnpx -y @vibefoundry/vibe-shield scan . --format json\nnpx -y @vibefoundry/vibe-shield audit-skills . --format json\n```\n\nInspect findings, inventory, ignored paths, and the files scanned. The explicit `.` includes test and documentation files: review fixtures as examples rather than blindly changing them. Use `explain <RULE_ID>` to investigate a rule. These commands run locally; first-time installation with npx requires registry access.\n\n## Phase 3: Deep contextual threat modeling\n\nTrace untrusted input through authentication, ownership checks and tenant boundaries to database, filesystem, network, HTML, and LLM sinks. Inspect IDOR, business-logic bypasses, webhook signature verification and asynchronous jobs. Check cross-file guards and deployed database policies; absence of a static finding is not evidence of protection.\n\n## Phase 4: Surgical zero-regression remediation\n\n- On Next.js mutation routes, authenticate the session on the server and authorize the target resource against the session principal before writes. Do not trust identity from request bodies.\n- Enable Supabase RLS and write action-specific policies using the authenticated principal; test both permitted and denied tenants before deploying migrations.\n- Validate inputs at the boundary with a schema such as Zod, then use parameterized database queries. Treat LLM output as untrusted.\n- Use explicit CORS allowlists, secure cookie flags and a CSP appropriate for the application. Test login, redirects, and browser behavior.\n- Report exposed credentials to the owner for provider-side rotation; avoid reproducing their values in reports. Do not suppress findings or weaken controls to obtain a higher score. Ask for missing business or deployment decisions.\n\n## Phase 5: Verification gate\n\nRun project tests and both audits again. Aim for 100/100 Grade A+ with no high or critical findings, but report any unresolved finding, exclusion, false positive, or verification limitation honestly rather than claiming success. Never equate a clean score with a complete security assessment.\n\n```bash\nnpx -y @vibefoundry/vibe-shield scan --fail-on high\nnpx -y @vibefoundry/vibe-shield audit-skills --fail-on high\n```\n\n## Phase 6: Executive security sign-off\n\nProduce a Markdown or HTML report with scope, inventory, risks prioritized by severity, evidence, actions, test results and remaining uncertainty. Illustrative code examples are not project-specific patches; review and adapt them. Obtain human approval before deployment or provider-side action.\n\n```bash\nnpx -y @vibefoundry/vibe-shield scan --report SECURITY-REPORT.md --patch-prompt PATCH-PROMPT.md\nnpx -y @vibefoundry/vibe-shield audit-skills --report AGENT-REPORT.html\n```\n";
58
+ export declare const AGENTS_SECTION = "## Security gate (vibe-shield)\n\nThis repository is guarded by [vibe-shield](https://github.com/avivgerz-hue/vibe-shield).\n\nBefore you report any task as complete:\n\n1. Run `npx -y @vibefoundry/vibe-shield scan --fail-on high` and fix every finding it reports.\n2. Never suppress a finding (`vibe-shield-disable`, `.vibe-shieldignore` edits,\n `--ignore`) to make the gate pass \u2014 that is the human's call.\n3. If you touched `.claude/`, `.cursor/`, `AGENTS.md`, `mcp.json` or a skill,\n also run `npx -y @vibefoundry/vibe-shield audit-skills`.\n4. Surface any exposed credential to the user for rotation. Do not \"fix\" it by\n deleting the line and moving on.\n\nThe gate exists because the most common way AI-written code reaches production\nwith a leaked `SUPABASE_SERVICE_ROLE_KEY` or an unauthenticated `DELETE`\nendpoint is that nobody asked it a second question.\n";
59
59
  /** Key this tool owns inside `mcpServers`; nothing else is ever touched. */
60
60
  export declare const MCP_SERVER_KEY = "vibe-shield";
61
61
  /**
@@ -64,19 +64,23 @@ export declare const MCP_SERVER_KEY = "vibe-shield";
64
64
  * `npx` is used rather than a local path so the config keeps working when the
65
65
  * project is cloned elsewhere or the package is upgraded.
66
66
  *
67
- * The spec carries a `#<commit>` pin when `init` can determine one. A bare
68
- * `github:owner/repo` re-resolves to the default branch head on every fresh
69
- * install, so a mixed commit could change what runs the next time the server
70
- * starts. Pin the commit that wrote this file and upgrades become explicit
71
- * (`init --force` rewrites it).
67
+ * The spec is always pinned to an exact version — `@vibefoundry/vibe-shield@1.1.0`.
68
+ * Two reasons: a bare name re-resolves to whatever `latest` points at on every
69
+ * launch, and vibe-shield's own `VSB-AGENT-004` rule flags an unpinned `npx -y`
70
+ * server as high severity. Pinning keeps the gate at A+ for projects that adopt
71
+ * the tool, and upgrades become explicit (`init --force` rewrites the pin).
72
72
  */
73
- export declare const MCP_SERVER_SPEC = "github:avivgerz-hue/vibe-shield";
74
- /** Build the `.mcp.json` entry, pinned to `rev` when one is known. */
75
- export declare function mcpServerEntry(rev?: string | null): {
73
+ export declare const MCP_SERVER_SPEC = "@vibefoundry/vibe-shield";
74
+ /** Build the `.mcp.json` entry, pinned to `version` when one is known. */
75
+ export declare function mcpServerEntry(version?: string | null): {
76
76
  command: string;
77
77
  args: string[];
78
78
  };
79
- /** The unpinned entry, kept for callers that have no commit available. */
79
+ /**
80
+ * The entry as this install writes it: pinned to the running package version.
81
+ * Falls back to unpinned only in a stripped checkout where no `package.json`
82
+ * names this package, which is the one case where no version is knowable.
83
+ */
80
84
  export declare const MCP_SERVER_ENTRY: {
81
85
  command: string;
82
86
  args: string[];
@@ -91,28 +95,22 @@ export declare const MCP_SERVER_ENTRY: {
91
95
  * rewriting someone's MCP config could break every server they run.
92
96
  */
93
97
  /**
94
- * Find the commit of the running install, so `.mcp.json` can pin it.
95
- *
96
- * Deliberately conservative: returns `null` rather than guessing, because a
97
- * wrong pin is worse than no pin (it would silently freeze the server on an
98
- * unrelated commit). The revision is only derivable when the CLI is running
99
- * from inside an npm install of this package, which is the `npx github:` case.
98
+ * The version of the running install, used to pin `.mcp.json`.
100
99
  *
101
- * Order:
102
- * 1. `VIBE_SHIELD_REV` — an explicit override, used by tests and CI.
103
- * 2. The `resolved` field npm records in the *parent* project's lockfile.
104
- * 3. `/app/.rev` — written into the container image at build time.
100
+ * Read from the nearest `package.json` that names this package. Falls back to
101
+ * the range in `package.json` only if nothing is found, which keeps `init`
102
+ * working in a stripped-down checkout rather than writing a nameless spec.
105
103
  */
106
- export declare function resolveInstallRev(rootDir?: string, env?: NodeJS.ProcessEnv): Promise<string | null>;
104
+ export declare function packageVersionFromDisk(): string | null;
107
105
  export declare function mergeMcpConfig(existing: string | null, options?: {
108
106
  force?: boolean;
109
- rev?: string | null;
107
+ version?: string | null;
110
108
  }): {
111
109
  contents: string;
112
110
  changed: boolean;
113
111
  error?: string;
114
112
  };
115
- export declare const WORKFLOW_TEMPLATE = "name: vibe-shield\n\non:\n push:\n branches: [main, master]\n pull_request:\n workflow_dispatch:\n\npermissions:\n contents: read\n security-events: write\n pull-requests: write\n\njobs:\n scan:\n name: Security audit\n runs-on: ubuntu-latest\n timeout-minutes: 10\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n\n - uses: actions/setup-node@v4\n with:\n node-version: 20\n\n - name: Scan for vulnerabilities\n run: npx --yes github:avivgerz-hue/vibe-shield scan --format sarif --out vibe-shield.sarif --fail-on high\n continue-on-error: true\n\n - name: Upload SARIF to code scanning\n if: always()\n uses: github/codeql-action/upload-sarif@v3\n with:\n sarif_file: vibe-shield.sarif\n category: vibe-shield\n\n - name: Audit agent surface\n run: npx --yes github:avivgerz-hue/vibe-shield audit-skills --fail-on high\n\n - name: Enforce the gate\n run: npx --yes github:avivgerz-hue/vibe-shield scan --fail-on high --quiet\n";
113
+ export declare const WORKFLOW_TEMPLATE = "name: vibe-shield\n\non:\n push:\n branches: [main, master]\n pull_request:\n workflow_dispatch:\n\npermissions:\n contents: read\n security-events: write\n pull-requests: write\n\njobs:\n scan:\n name: Security audit\n runs-on: ubuntu-latest\n timeout-minutes: 10\n steps:\n - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n\n - uses: actions/setup-node@v4\n with:\n node-version: 20\n\n - name: Scan for vulnerabilities\n run: npx -y @vibefoundry/vibe-shield scan --format sarif --out vibe-shield.sarif --fail-on high\n continue-on-error: true\n\n - name: Upload SARIF to code scanning\n if: always()\n uses: github/codeql-action/upload-sarif@v3\n with:\n sarif_file: vibe-shield.sarif\n category: vibe-shield\n\n - name: Audit agent surface\n run: npx -y @vibefoundry/vibe-shield audit-skills --fail-on high\n\n - name: Enforce the gate\n run: npx -y @vibefoundry/vibe-shield scan --fail-on high --quiet\n";
116
114
  export declare const IGNORE_TEMPLATE = "# .vibe-shieldignore \u2014 gitignore syntax.\n#\n# Keep this file as short as possible: every line hides real code from the\n# security gate. Prefer an inline suppression with a reason where it applies:\n#\n# // vibe-shield-disable-next-line VSB-AI-001 -- path comes from a fixed enum\n#\n# Generated code, vendored third-party sources and build output are the only\n# good reasons to ignore a path wholesale.\n\n# Generated / vendored\n# vendor/**\n# **/*.generated.ts\n# **/__generated__/**\n\n# Local-only scratch work\n# scratch/**\n";
117
115
  /** Build the full set of files for a given target repository. */
118
116
  export declare function buildScaffold(options: InitOptions): ScaffoldFile[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * vibe-shield :: `npx github:avivgerz-hue/vibe-shield init`
3
+ * vibe-shield :: `npx -y @vibefoundry/vibe-shield init`
4
4
  * -------------------------------------
5
5
  * One command turns any repository into a guarded one. It installs:
6
6
  *
@@ -50,7 +50,7 @@ var __importStar = (this && this.__importStar) || (function () {
50
50
  Object.defineProperty(exports, "__esModule", { value: true });
51
51
  exports.IGNORE_TEMPLATE = exports.WORKFLOW_TEMPLATE = exports.MCP_SERVER_ENTRY = exports.MCP_SERVER_SPEC = exports.MCP_SERVER_KEY = exports.AGENTS_SECTION = exports.SKILL_TEMPLATE = void 0;
52
52
  exports.mcpServerEntry = mcpServerEntry;
53
- exports.resolveInstallRev = resolveInstallRev;
53
+ exports.packageVersionFromDisk = packageVersionFromDisk;
54
54
  exports.mergeMcpConfig = mergeMcpConfig;
55
55
  exports.buildScaffold = buildScaffold;
56
56
  exports.installScaffold = installScaffold;
@@ -58,134 +58,67 @@ exports.locateBundledSkill = locateBundledSkill;
58
58
  const node_fs_1 = require("node:fs");
59
59
  const path = __importStar(require("node:path"));
60
60
  const shared_1 = require("../scanner/rules/shared");
61
- const BANNER = '<!-- Managed by vibe-shield. Edit freely; `npx github:avivgerz-hue/vibe-shield init --force` regenerates this file. -->';
61
+ const BANNER = '<!-- Managed by vibe-shield. Edit freely; `npx -y @vibefoundry/vibe-shield init --force` regenerates this file. -->';
62
62
  /* -------------------------------------------------------------------------- */
63
63
  /* SKILL.md */
64
64
  /* -------------------------------------------------------------------------- */
65
65
  exports.SKILL_TEMPLATE = `---
66
66
  name: vibe-shield
67
- description: Audit any change for AI-introduced security bugs (leaked keys, unguarded API routes, disabled Row Level Security, SQL injection, prompt injection, poisoned MCP servers) and fix them before reporting a task as done. Use whenever you write or modify application code, API routes, server actions, database migrations, auth logic, environment variables, LLM calls, webhook handlers, agent skills or MCP configuration.
68
- allowed-tools: Bash(npx github:avivgerz-hue/vibe-shield scan*), Bash(npx github:avivgerz-hue/vibe-shield audit-skills*), Bash(npx github:avivgerz-hue/vibe-shield explain*), Read(./**), Grep, Glob
67
+ description: Apply a six-phase defensive security review to changes affecting applications, APIs, data, AI agents, and MCP configuration. Use static evidence, preserve behavior, and report limitations.
68
+ allowed-tools: Bash(npx -y @vibefoundry/vibe-shield scan*), Bash(npx -y @vibefoundry/vibe-shield audit-skills*), Bash(npx -y @vibefoundry/vibe-shield explain*), Read(./**), Grep, Glob
69
69
  license: MIT
70
70
  metadata:
71
71
  author: VibeFoundry
72
72
  homepage: https://github.com/avivgerz-hue/vibe-shield
73
- version: 1.0.0
73
+ version: 2.0.0
74
74
  ---
75
75
 
76
- # vibe-shield
76
+ # Defensive AppSec architect protocol
77
77
 
78
- You are not allowed to say "done" until the code you just wrote passes a security audit.
78
+ Work only within the authorized project. Static findings are evidence for review, not proof of exploitability or a guarantee of safety. Do not transmit source code or secrets. Ask before changing production services, rotating credentials, or making destructive changes.
79
79
 
80
- AI coding agents ship a specific, repeatable class of vulnerability: a \`SUPABASE_SERVICE_ROLE_KEY\` behind a \`NEXT_PUBLIC_\` prefix, a \`DELETE\` route handler with no session check, a \`CREATE TABLE\` with no \`ENABLE ROW LEVEL SECURITY\`, a tool whose \`execute\` forwards model output into \`exec\`. These are not exotic — they are the default shape of code written quickly by something optimising for "it works". This skill is the second pair of eyes.
80
+ ## Phase 1: Attack-surface reconnaissance
81
81
 
82
- ## When to run
82
+ Map entrypoints and trust boundaries: API methods, authentication and authorization, middleware matchers, database tables and policies, storage, LLM inputs and tools, webhooks, and agent/MCP configuration. Check deployment configuration and exclusions. Identify what the scanner cannot see.
83
83
 
84
- Run the audit **after every change that touches**:
85
-
86
- - an API route, route handler, server action, RPC endpoint or GraphQL mutation
87
- - authentication, session, role, permission or ownership logic
88
- - a database migration, policy, grant, schema or Supabase table
89
- - an environment variable, config file, \`.env*\` file or build config
90
- - anything calling an LLM, a tool, a webhook, a shell command or the filesystem
91
- - \`.claude/\`, \`.cursor/\`, \`AGENTS.md\`, \`mcp.json\` or any skill/config file
92
-
93
- If you did not touch any of those, you do not need to run it.
94
-
95
- ## The loop
84
+ ## Phase 2: Automated static scan and skill audit
96
85
 
97
86
  \`\`\`bash
98
- # 1. Scan (a whole-repo scan finishes in well under a second)
99
- npx github:avivgerz-hue/vibe-shield scan --fail-on high
100
-
101
- # 2. If it fails, generate the machine-readable fix list
102
- npx github:avivgerz-hue/vibe-shield scan --fix-prompt
103
-
104
- # 3. Read SECURITY_PATCH_PROMPT.md and fix every finding, critical first
105
- # Each entry has the exact file:line, the vulnerable snippet, the CWE and
106
- # the required change. Apply it literally.
107
-
108
- # 4. Re-run until it exits 0
109
- npx github:avivgerz-hue/vibe-shield scan --fail-on high
87
+ npx -y @vibefoundry/vibe-shield scan . --format json
88
+ npx -y @vibefoundry/vibe-shield audit-skills . --format json
110
89
  \`\`\`
111
90
 
112
- \`--fail-on high\` exits non-zero while any high or critical finding exists. That is the gate.
91
+ Inspect findings, inventory, ignored paths, and the files scanned. The explicit \`.\` includes test and documentation files: review fixtures as examples rather than blindly changing them. Use \`explain <RULE_ID>\` to investigate a rule. These commands run locally; first-time installation with npx requires registry access.
113
92
 
114
- ## Reading a finding
93
+ ## Phase 3: Deep contextual threat modeling
115
94
 
116
- \`\`\`
117
- CRITICAL 47:15 VSB-CLIENT-001 Server-only secret exposed through a public environment prefix
118
- const key = process.env.NEXT_PUBLIC_OPENAI_API_KEY;
119
- \`\`\`
120
-
121
- | Part | Meaning |
122
- | --- | --- |
123
- | \`47:15\` | exact line and column to edit |
124
- | \`VSB-CLIENT-001\` | rule id — use it with \`explain\` for full docs |
125
- | message | why this specific line is exploitable |
126
- | \`(CWE-200)\` | bug class, for a deeper search if the fix is unclear |
127
-
128
- Need the full picture? \`npx github:avivgerz-hue/vibe-shield explain VSB-CLIENT-001\` prints the vulnerable shape, the fixed shape, remediation steps and references.
95
+ Trace untrusted input through authentication, ownership checks and tenant boundaries to database, filesystem, network, HTML, and LLM sinks. Inspect IDOR, business-logic bypasses, webhook signature verification and asynchronous jobs. Check cross-file guards and deployed database policies; absence of a static finding is not evidence of protection.
129
96
 
130
- ## Hard rules
97
+ ## Phase 4: Surgical zero-regression remediation
131
98
 
132
- 1. **Fix the root cause.** Never delete an auth check, disable RLS, silence a log or add a catch-all \`catch {}\` to make the finding disappear.
133
- 2. **Never suppress.** Do not add \`// vibe-shield-disable\`, do not edit \`.vibe-shieldignore\`, do not pass \`--ignore\`. Suppression is a decision for the human.
134
- 3. **Rotate exposed credentials.** If the scan reported a live key, tell the user to rotate it at the provider in the same message. Removing the line does not un-leak it — the value may already be in a bundle, a cache or a git history.
135
- 4. **Report honestly.** If you cannot fix a finding (you need a decision, a credential or a provider change), say so explicitly, name the rule id, and stop.
136
- 5. **Re-verify.** A fix is not finished until the gate exits 0.
99
+ - On Next.js mutation routes, authenticate the session on the server and authorize the target resource against the session principal before writes. Do not trust identity from request bodies.
100
+ - Enable Supabase RLS and write action-specific policies using the authenticated principal; test both permitted and denied tenants before deploying migrations.
101
+ - Validate inputs at the boundary with a schema such as Zod, then use parameterized database queries. Treat LLM output as untrusted.
102
+ - Use explicit CORS allowlists, secure cookie flags and a CSP appropriate for the application. Test login, redirects, and browser behavior.
103
+ - Report exposed credentials to the owner for provider-side rotation; avoid reproducing their values in reports. Do not suppress findings or weaken controls to obtain a higher score. Ask for missing business or deployment decisions.
137
104
 
138
- ## Agent surface audit
105
+ ## Phase 5: Verification gate
139
106
 
140
- When you add or modify a skill, rule file, MCP server or instruction file, also run:
107
+ Run project tests and both audits again. Aim for 100/100 Grade A+ with no high or critical findings, but report any unresolved finding, exclusion, false positive, or verification limitation honestly rather than claiming success. Never equate a clean score with a complete security assessment.
141
108
 
142
109
  \`\`\`bash
143
- npx github:avivgerz-hue/vibe-shield audit-skills
110
+ npx -y @vibefoundry/vibe-shield scan --fail-on high
111
+ npx -y @vibefoundry/vibe-shield audit-skills --fail-on high
144
112
  \`\`\`
145
113
 
146
- Twelve \`VSB-AGENT-*\` rules check for hidden Unicode (zero-width and bidi characters used to smuggle instructions past review), \`curl\`/\`wget\` exfiltration of environment variables, credential-harvesting instructions (\`.env\`, \`~/.ssh\`, \`~/.aws\`), registry-tag or remote MCP servers, literal tokens in an \`env\` block, safety-override language, over-broad tool permissions, base64 \`eval\` payloads, remote-instruction fetching, wildcard \`allowed-tools\`, and shell injection in skill scripts.
147
-
148
- Each finding carries a \`triage\` field: \`block\` means refuse and escalate, \`review\` means ask the user, \`report\` means mention it and continue. Do not blanket-refuse a repository because a rule fired — read the triage.
149
-
150
- ## Flags worth knowing
151
-
152
- | Flag | Effect |
153
- | --- | --- |
154
- | \`--format json\` | full machine-readable report (schemaVersion 1) |
155
- | \`--format sarif\` | SARIF 2.1.0 for GitHub code scanning |
156
- | \`--format compact\` | one line per finding |
157
- | \`--format github\` | inline GitHub Actions annotations |
158
- | \`--severity high\` | only report high and critical |
159
- | \`--fail-on medium\` | stricter gate |
160
- | \`--include-tests\` | include test and fixture files |
161
- | \`--cache\` | reuse results between runs |
162
- | \`--quiet\` | one-line verdict (ideal for piping) |
163
- | \`rules\` | list every rule |
164
- | \`explain <RULE_ID>\` | full documentation for one rule |
114
+ ## Phase 6: Executive security sign-off
165
115
 
166
- If the repository has a \`.mcp.json\`, the same three capabilities are also
167
- available as MCP tools — \`vibe_shield_scan\`, \`vibe_shield_audit_skills\` and
168
- \`vibe_shield_explain_rule\`. Prefer those when they are listed, because they
169
- return structured findings instead of text you have to parse.
170
-
171
- ## Definition of done
172
-
173
- - [ ] \`npx github:avivgerz-hue/vibe-shield scan --fail-on high\` exits \`0\`
174
- - [ ] \`npx github:avivgerz-hue/vibe-shield audit-skills\` exits \`0\` if you touched agent files
175
- - [ ] every credential the scan surfaced has been flagged to the user for rotation
176
- - [ ] no suppression directive was added
177
- - [ ] the feature still works and the existing tests still pass
178
-
179
- ## Install it into a repository
116
+ Produce a Markdown or HTML report with scope, inventory, risks prioritized by severity, evidence, actions, test results and remaining uncertainty. Illustrative code examples are not project-specific patches; review and adapt them. Obtain human approval before deployment or provider-side action.
180
117
 
181
118
  \`\`\`bash
182
- npx github:avivgerz-hue/vibe-shield init
119
+ npx -y @vibefoundry/vibe-shield scan --report SECURITY-REPORT.md --patch-prompt PATCH-PROMPT.md
120
+ npx -y @vibefoundry/vibe-shield audit-skills --report AGENT-REPORT.html
183
121
  \`\`\`
184
-
185
- That writes this file to \`.claude/skills/vibe-shield/SKILL.md\`, appends a security
186
- gate section to \`AGENTS.md\`, adds a \`.cursor/rules/vibe-shield.mdc\`, and installs a
187
- GitHub Actions workflow that fails the pull request on new high or critical
188
- findings. Every future task in that repository starts guarded.
189
122
  `;
190
123
  /* -------------------------------------------------------------------------- */
191
124
  /* AGENTS.md section */
@@ -196,11 +129,11 @@ This repository is guarded by [vibe-shield](https://github.com/avivgerz-hue/vibe
196
129
 
197
130
  Before you report any task as complete:
198
131
 
199
- 1. Run \`npx github:avivgerz-hue/vibe-shield scan --fail-on high\` and fix every finding it reports.
132
+ 1. Run \`npx -y @vibefoundry/vibe-shield scan --fail-on high\` and fix every finding it reports.
200
133
  2. Never suppress a finding (\`vibe-shield-disable\`, \`.vibe-shieldignore\` edits,
201
134
  \`--ignore\`) to make the gate pass — that is the human's call.
202
135
  3. If you touched \`.claude/\`, \`.cursor/\`, \`AGENTS.md\`, \`mcp.json\` or a skill,
203
- also run \`npx github:avivgerz-hue/vibe-shield audit-skills\`.
136
+ also run \`npx -y @vibefoundry/vibe-shield audit-skills\`.
204
137
  4. Surface any exposed credential to the user for rotation. Do not "fix" it by
205
138
  deleting the line and moving on.
206
139
 
@@ -219,20 +152,24 @@ exports.MCP_SERVER_KEY = 'vibe-shield';
219
152
  * `npx` is used rather than a local path so the config keeps working when the
220
153
  * project is cloned elsewhere or the package is upgraded.
221
154
  *
222
- * The spec carries a `#<commit>` pin when `init` can determine one. A bare
223
- * `github:owner/repo` re-resolves to the default branch head on every fresh
224
- * install, so a mixed commit could change what runs the next time the server
225
- * starts. Pin the commit that wrote this file and upgrades become explicit
226
- * (`init --force` rewrites it).
155
+ * The spec is always pinned to an exact version — `@vibefoundry/vibe-shield@1.1.0`.
156
+ * Two reasons: a bare name re-resolves to whatever `latest` points at on every
157
+ * launch, and vibe-shield's own `VSB-AGENT-004` rule flags an unpinned `npx -y`
158
+ * server as high severity. Pinning keeps the gate at A+ for projects that adopt
159
+ * the tool, and upgrades become explicit (`init --force` rewrites the pin).
227
160
  */
228
161
  exports.MCP_SERVER_SPEC = shared_1.SELF_GITHUB_SPEC;
229
- /** Build the `.mcp.json` entry, pinned to `rev` when one is known. */
230
- function mcpServerEntry(rev) {
231
- const spec = rev ? `${exports.MCP_SERVER_SPEC}#${rev}` : exports.MCP_SERVER_SPEC;
162
+ /** Build the `.mcp.json` entry, pinned to `version` when one is known. */
163
+ function mcpServerEntry(version) {
164
+ const spec = version ? `${exports.MCP_SERVER_SPEC}@${version}` : exports.MCP_SERVER_SPEC;
232
165
  return { command: 'npx', args: ['--yes', spec, 'mcp'] };
233
166
  }
234
- /** The unpinned entry, kept for callers that have no commit available. */
235
- exports.MCP_SERVER_ENTRY = mcpServerEntry();
167
+ /**
168
+ * The entry as this install writes it: pinned to the running package version.
169
+ * Falls back to unpinned only in a stripped checkout where no `package.json`
170
+ * names this package, which is the one case where no version is knowable.
171
+ */
172
+ exports.MCP_SERVER_ENTRY = mcpServerEntry(packageVersionFromDisk());
236
173
  /**
237
174
  * Merge the vibe-shield server into an existing `.mcp.json` without disturbing
238
175
  * anything already there.
@@ -243,55 +180,34 @@ exports.MCP_SERVER_ENTRY = mcpServerEntry();
243
180
  * rewriting someone's MCP config could break every server they run.
244
181
  */
245
182
  /**
246
- * Find the commit of the running install, so `.mcp.json` can pin it.
247
- *
248
- * Deliberately conservative: returns `null` rather than guessing, because a
249
- * wrong pin is worse than no pin (it would silently freeze the server on an
250
- * unrelated commit). The revision is only derivable when the CLI is running
251
- * from inside an npm install of this package, which is the `npx github:` case.
183
+ * The version of the running install, used to pin `.mcp.json`.
252
184
  *
253
- * Order:
254
- * 1. `VIBE_SHIELD_REV` — an explicit override, used by tests and CI.
255
- * 2. The `resolved` field npm records in the *parent* project's lockfile.
256
- * 3. `/app/.rev` — written into the container image at build time.
185
+ * Read from the nearest `package.json` that names this package. Falls back to
186
+ * the range in `package.json` only if nothing is found, which keeps `init`
187
+ * working in a stripped-down checkout rather than writing a nameless spec.
257
188
  */
258
- async function resolveInstallRev(rootDir, env = process.env) {
259
- const explicit = env.VIBE_SHIELD_REV?.trim();
260
- if (explicit)
261
- return explicit;
262
- const shaPattern = /(?:[?#]([0-9a-f]{40}))\b/i;
263
- // A lockfile one or two directories above this file, in an npm install.
189
+ function packageVersionFromDisk() {
264
190
  let dir = __dirname;
265
- for (let depth = 0; depth < 4; depth += 1) {
266
- for (const name of ['package-lock.json', 'npm-shrinkwrap.json']) {
267
- try {
268
- const raw = await node_fs_1.promises.readFile(path.join(dir, name), 'utf8');
269
- const match = raw.match(shaPattern);
270
- if (match?.[1])
271
- return match[1];
272
- }
273
- catch {
274
- // Keep walking.
191
+ for (let depth = 0; depth < 6; depth += 1) {
192
+ try {
193
+ const raw = (0, node_fs_1.readFileSync)(path.join(dir, 'package.json'), 'utf8');
194
+ const parsed = JSON.parse(raw);
195
+ if ((parsed.name === shared_1.SELF_GITHUB_SPEC || parsed.name === 'vibe-shield') && parsed.version) {
196
+ return parsed.version;
275
197
  }
276
198
  }
199
+ catch {
200
+ // Keep walking up.
201
+ }
277
202
  const parent = path.dirname(dir);
278
203
  if (parent === dir)
279
204
  break;
280
205
  dir = parent;
281
206
  }
282
- try {
283
- const rev = (await node_fs_1.promises.readFile('/app/.rev', 'utf8')).trim();
284
- if (/^[0-9a-f]{40}$/i.test(rev))
285
- return rev;
286
- }
287
- catch {
288
- // Not in the container image.
289
- }
290
- void rootDir;
291
207
  return null;
292
208
  }
293
209
  function mergeMcpConfig(existing, options = {}) {
294
- const entry = mcpServerEntry(options.rev);
210
+ const entry = mcpServerEntry(options.version ?? packageVersionFromDisk());
295
211
  if (existing === null || existing.trim() === '') {
296
212
  return {
297
213
  contents: `${JSON.stringify({ mcpServers: { [exports.MCP_SERVER_KEY]: entry } }, null, 2)}\n`,
@@ -356,7 +272,7 @@ jobs:
356
272
  node-version: 20
357
273
 
358
274
  - name: Scan for vulnerabilities
359
- run: npx --yes github:avivgerz-hue/vibe-shield scan --format sarif --out vibe-shield.sarif --fail-on high
275
+ run: npx -y @vibefoundry/vibe-shield scan --format sarif --out vibe-shield.sarif --fail-on high
360
276
  continue-on-error: true
361
277
 
362
278
  - name: Upload SARIF to code scanning
@@ -367,10 +283,10 @@ jobs:
367
283
  category: vibe-shield
368
284
 
369
285
  - name: Audit agent surface
370
- run: npx --yes github:avivgerz-hue/vibe-shield audit-skills --fail-on high
286
+ run: npx -y @vibefoundry/vibe-shield audit-skills --fail-on high
371
287
 
372
288
  - name: Enforce the gate
373
- run: npx --yes github:avivgerz-hue/vibe-shield scan --fail-on high --quiet
289
+ run: npx -y @vibefoundry/vibe-shield scan --fail-on high --quiet
374
290
  `;
375
291
  /* -------------------------------------------------------------------------- */
376
292
  /* .vibe-shieldignore */
@@ -406,7 +322,7 @@ ${BANNER}
406
322
 
407
323
  # Security gate
408
324
 
409
- Before reporting a task complete, run \`npx github:avivgerz-hue/vibe-shield scan --fail-on high\` and
325
+ Before reporting a task complete, run \`npx -y @vibefoundry/vibe-shield scan --fail-on high\` and
410
326
  fix every finding. Never suppress a finding to make the gate pass. If the scan
411
327
  reports an exposed credential, tell the user to rotate it at the provider.
412
328
  `;
@@ -474,7 +390,7 @@ const exists = async (file) => {
474
390
  async function installScaffold(options) {
475
391
  const rootDir = path.resolve(options.rootDir);
476
392
  const result = { written: [], appended: [], skipped: [], blocked: [], warnings: [] };
477
- const resolvedRev = options.rev === undefined ? await resolveInstallRev(rootDir, options.env) : options.rev;
393
+ const resolvedVersion = options.version ?? options.env?.VIBE_SHIELD_VERSION ?? packageVersionFromDisk();
478
394
  const files = buildScaffold(options);
479
395
  for (const file of files) {
480
396
  const absolute = path.join(rootDir, file.relativePath);
@@ -513,7 +429,7 @@ async function installScaffold(options) {
513
429
  existing = null;
514
430
  }
515
431
  }
516
- const merged = mergeMcpConfig(existing, { force: options.force, rev: resolvedRev });
432
+ const merged = mergeMcpConfig(existing, { force: options.force, version: resolvedVersion });
517
433
  if (merged.error) {
518
434
  // Not a `skipped` entry: `--force` cannot repair invalid JSON, so the
519
435
  // summary must not suggest it can.
@@ -546,10 +462,10 @@ async function installScaffold(options) {
546
462
  await node_fs_1.promises.writeFile(absolute, file.contents, 'utf8');
547
463
  result.written.push(file.relativePath);
548
464
  }
549
- // A repository without a package.json will not be able to `npx github:avivgerz-hue/vibe-shield`
465
+ // A repository without a package.json will not be able to `npx -y @vibefoundry/vibe-shield`
550
466
  // as a dev dependency; it still works through npx, so this is only a warning.
551
467
  if (!(await exists(path.join(rootDir, 'package.json')))) {
552
- result.warnings.push('No package.json found — run the gate with `npx github:avivgerz-hue/vibe-shield scan`.');
468
+ result.warnings.push('No package.json found — run the gate with `npx -y @vibefoundry/vibe-shield scan`.');
553
469
  }
554
470
  if (!(await exists(path.join(rootDir, '.git')))) {
555
471
  result.warnings.push('No .git directory found — the workflow only runs once this is a git repository.');
@@ -230,6 +230,7 @@ const toToolResult = (result, options) => {
230
230
  filesScanned: result.stats.filesScanned,
231
231
  durationMs: result.stats.durationMs,
232
232
  rootDir: result.rootDir,
233
+ attackSurface: result.attackSurface,
233
234
  findings: shown.map((finding) => ({
234
235
  ruleId: finding.ruleId,
235
236
  slug: finding.slug,
@@ -239,10 +240,12 @@ const toToolResult = (result, options) => {
239
240
  title: finding.title,
240
241
  message: finding.message,
241
242
  filePath: finding.filePath,
243
+ file: finding.file,
242
244
  line: finding.line,
243
245
  column: finding.column,
244
246
  snippet: finding.snippet,
245
247
  cwe: finding.cwe,
248
+ owasp: finding.owasp,
246
249
  remediation: finding.remediation,
247
250
  secureExample: finding.secureExample,
248
251
  references: finding.references,
@@ -281,6 +284,7 @@ const explainRuleResult = (rule) => {
281
284
  severity: rule.severity,
282
285
  confidence: rule.confidence,
283
286
  cwe: rule.cwe,
287
+ owasp: rule.owasp,
284
288
  description: rule.description,
285
289
  insecureExample: rule.insecureExample,
286
290
  secureExample: rule.secureExample,
@@ -334,7 +338,7 @@ async function callTool(name, args, options = {}) {
334
338
  return errorResult('`ruleId` is required, e.g. "VSB-CLIENT-001".');
335
339
  const rule = (0, rules_1.getRule)(ruleId);
336
340
  if (!rule)
337
- return errorResult(`Unknown rule "${ruleId}". Call tools/list or \`npx github:avivgerz-hue/vibe-shield rules\` for the catalogue.`);
341
+ return errorResult(`Unknown rule "${ruleId}". Call tools/list or \`npx -y @vibefoundry/vibe-shield rules\` for the catalogue.`);
338
342
  return explainRuleResult(rule);
339
343
  }
340
344
  return errorResult(`Unknown tool "${name}".`);
@@ -393,7 +397,7 @@ async function handleMessage(message, options = {}) {
393
397
  serverInfo: {
394
398
  name: exports.SERVER_NAME,
395
399
  title: 'vibe-shield',
396
- version: options.version ?? '1.1.0',
400
+ version: options.version ?? await (0, engine_1.packageVersion)(),
397
401
  },
398
402
  instructions: 'vibe-shield finds security bugs that AI coding agents ship: leaked secrets, unguarded API routes, missing Postgres RLS, SQL injection, prompt injection and poisoned agent skills. Call vibe_shield_scan after writing application code and before reporting a task as done, and vibe_shield_audit_skills after touching any agent instruction or MCP config file.',
399
403
  });