@vibefoundry/vibe-shield 0.0.0-stage → 1.1.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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +571 -3
  3. package/SECURITY.md +110 -0
  4. package/SKILL.md +124 -0
  5. package/dist/bin/vibe-shield.d.ts +59 -0
  6. package/dist/bin/vibe-shield.js +541 -0
  7. package/dist/src/index.d.ts +25 -0
  8. package/dist/src/index.js +108 -0
  9. package/dist/src/init/scaffold.d.ts +122 -0
  10. package/dist/src/init/scaffold.js +573 -0
  11. package/dist/src/mcp/server.d.ts +127 -0
  12. package/dist/src/mcp/server.js +479 -0
  13. package/dist/src/mcp-guard/skill-auditor.d.ts +105 -0
  14. package/dist/src/mcp-guard/skill-auditor.js +1129 -0
  15. package/dist/src/reporter/console-reporter.d.ts +58 -0
  16. package/dist/src/reporter/console-reporter.js +254 -0
  17. package/dist/src/reporter/formatters.d.ts +38 -0
  18. package/dist/src/reporter/formatters.js +262 -0
  19. package/dist/src/reporter/patch-prompt-writer.d.ts +35 -0
  20. package/dist/src/reporter/patch-prompt-writer.js +238 -0
  21. package/dist/src/scanner/context.d.ts +51 -0
  22. package/dist/src/scanner/context.js +568 -0
  23. package/dist/src/scanner/engine.d.ts +130 -0
  24. package/dist/src/scanner/engine.js +722 -0
  25. package/dist/src/scanner/gitignore.d.ts +49 -0
  26. package/dist/src/scanner/gitignore.js +195 -0
  27. package/dist/src/scanner/lexicon.d.ts +56 -0
  28. package/dist/src/scanner/lexicon.js +357 -0
  29. package/dist/src/scanner/rules/ai-vulnerabilities.d.ts +15 -0
  30. package/dist/src/scanner/rules/ai-vulnerabilities.js +859 -0
  31. package/dist/src/scanner/rules/client-exposure.d.ts +18 -0
  32. package/dist/src/scanner/rules/client-exposure.js +410 -0
  33. package/dist/src/scanner/rules/database-exposure.d.ts +15 -0
  34. package/dist/src/scanner/rules/database-exposure.js +626 -0
  35. package/dist/src/scanner/rules/helpers.d.ts +31 -0
  36. package/dist/src/scanner/rules/helpers.js +85 -0
  37. package/dist/src/scanner/rules/index.d.ts +30 -0
  38. package/dist/src/scanner/rules/index.js +95 -0
  39. package/dist/src/scanner/rules/secret-entropy.d.ts +17 -0
  40. package/dist/src/scanner/rules/secret-entropy.js +338 -0
  41. package/dist/src/scanner/rules/shared.d.ts +100 -0
  42. package/dist/src/scanner/rules/shared.js +161 -0
  43. package/dist/src/scanner/rules/unguarded-routes.d.ts +16 -0
  44. package/dist/src/scanner/rules/unguarded-routes.js +526 -0
  45. package/dist/src/scanner/text.d.ts +90 -0
  46. package/dist/src/scanner/text.js +472 -0
  47. package/dist/src/scanner/types.d.ts +340 -0
  48. package/dist/src/scanner/types.js +195 -0
  49. package/dist/src/secrets/patterns.d.ts +63 -0
  50. package/dist/src/secrets/patterns.js +392 -0
  51. package/package.json +94 -4
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * vibe-shield :: public API
4
+ * -------------------------
5
+ * Everything an embedder needs: run a scan, audit agent surface, render a
6
+ * report, or write a patch prompt — without spawning the CLI.
7
+ *
8
+ * ```ts
9
+ * import { scanProject, formatSarif } from 'vibe-shield';
10
+ *
11
+ * const result = await scanProject({ rootDir: process.cwd() });
12
+ * process.stdout.write(formatSarif(result));
13
+ * ```
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.formatCompact = exports.formatSummary = exports.formatJson = exports.formatResult = exports.verdictLine = exports.createColorizer = exports.renderConsoleReport = exports.reportConsole = exports.jwtHasPrivilegedRole = exports.decodeJwtPayload = exports.SECRET_PATTERNS = exports.findSecrets = exports.tolerantJsonParse = exports.detectLanguage = exports.computeFlags = exports.createFileContext = exports.detectProject = exports.readGitTrackedPaths = exports.mergeSqlTables = exports.parseSqlTables = exports.parseMiddlewareMatchers = exports.parseFrontmatter = exports.discoverAgentFiles = exports.auditSkills = exports.AGENT_RULES = exports.selectRules = exports.rulesByCategory = exports.getRule = exports.RULES_FINGERPRINT = exports.SCAN_RULES = exports.ALL_RULES = exports.CACHE_FILE_NAME = exports.DEFAULT_CONCURRENCY = exports.MAX_FILE_SIZE_BYTES = exports.BINARY_EXTENSIONS = exports.DEFAULT_IGNORED_DIRS = exports.isDocPath = exports.isTestPath = exports.contentHash = exports.packageVersion = exports.gitTrackedPaths = exports.dedupe = exports.hasFindingsAtOrAbove = exports.suppressionFor = exports.parseSuppressions = exports.applySuppressions = exports.scoreFindings = exports.runRulesOnFile = exports.discoverFiles = exports.scanProject = void 0;
17
+ exports.VERSION = exports.isRuleCategory = exports.isSeverity = exports.meetsSeverity = exports.severityRank = exports.emptySeverityCounts = exports.emptyProjectContext = exports.CATEGORY_TITLES = exports.RULE_CATEGORIES = exports.GRADE_THRESHOLDS = exports.GRADES = exports.SEVERITY_SCORE_CEILING = exports.SEVERITY_SCORE_PENALTY = exports.SEVERITY_WEIGHT = exports.SEVERITIES = exports.IGNORE_TEMPLATE = exports.WORKFLOW_TEMPLATE = exports.AGENTS_SECTION = exports.SKILL_TEMPLATE = exports.installScaffold = exports.buildScaffold = exports.PATCH_PROMPT_FILENAME = exports.writePatchPrompt = exports.buildPatchPrompt = exports.SCHEMA_VERSION = exports.OUTPUT_FORMATS = exports.isOutputFormat = exports.groupByFile = exports.formatGithub = exports.formatSarif = void 0;
18
+ var engine_1 = require("./scanner/engine");
19
+ Object.defineProperty(exports, "scanProject", { enumerable: true, get: function () { return engine_1.scanProject; } });
20
+ Object.defineProperty(exports, "discoverFiles", { enumerable: true, get: function () { return engine_1.discoverFiles; } });
21
+ Object.defineProperty(exports, "runRulesOnFile", { enumerable: true, get: function () { return engine_1.runRulesOnFile; } });
22
+ Object.defineProperty(exports, "scoreFindings", { enumerable: true, get: function () { return engine_1.scoreFindings; } });
23
+ Object.defineProperty(exports, "applySuppressions", { enumerable: true, get: function () { return engine_1.applySuppressions; } });
24
+ Object.defineProperty(exports, "parseSuppressions", { enumerable: true, get: function () { return engine_1.parseSuppressions; } });
25
+ Object.defineProperty(exports, "suppressionFor", { enumerable: true, get: function () { return engine_1.suppressionFor; } });
26
+ Object.defineProperty(exports, "hasFindingsAtOrAbove", { enumerable: true, get: function () { return engine_1.hasFindingsAtOrAbove; } });
27
+ Object.defineProperty(exports, "dedupe", { enumerable: true, get: function () { return engine_1.dedupe; } });
28
+ Object.defineProperty(exports, "gitTrackedPaths", { enumerable: true, get: function () { return engine_1.gitTrackedPaths; } });
29
+ Object.defineProperty(exports, "packageVersion", { enumerable: true, get: function () { return engine_1.packageVersion; } });
30
+ Object.defineProperty(exports, "contentHash", { enumerable: true, get: function () { return engine_1.contentHash; } });
31
+ Object.defineProperty(exports, "isTestPath", { enumerable: true, get: function () { return engine_1.isTestPath; } });
32
+ Object.defineProperty(exports, "isDocPath", { enumerable: true, get: function () { return engine_1.isDocPath; } });
33
+ Object.defineProperty(exports, "DEFAULT_IGNORED_DIRS", { enumerable: true, get: function () { return engine_1.DEFAULT_IGNORED_DIRS; } });
34
+ Object.defineProperty(exports, "BINARY_EXTENSIONS", { enumerable: true, get: function () { return engine_1.BINARY_EXTENSIONS; } });
35
+ Object.defineProperty(exports, "MAX_FILE_SIZE_BYTES", { enumerable: true, get: function () { return engine_1.MAX_FILE_SIZE_BYTES; } });
36
+ Object.defineProperty(exports, "DEFAULT_CONCURRENCY", { enumerable: true, get: function () { return engine_1.DEFAULT_CONCURRENCY; } });
37
+ Object.defineProperty(exports, "CACHE_FILE_NAME", { enumerable: true, get: function () { return engine_1.CACHE_FILE_NAME; } });
38
+ var rules_1 = require("./scanner/rules");
39
+ Object.defineProperty(exports, "ALL_RULES", { enumerable: true, get: function () { return rules_1.ALL_RULES; } });
40
+ Object.defineProperty(exports, "SCAN_RULES", { enumerable: true, get: function () { return rules_1.SCAN_RULES; } });
41
+ Object.defineProperty(exports, "RULES_FINGERPRINT", { enumerable: true, get: function () { return rules_1.RULES_FINGERPRINT; } });
42
+ Object.defineProperty(exports, "getRule", { enumerable: true, get: function () { return rules_1.getRule; } });
43
+ Object.defineProperty(exports, "rulesByCategory", { enumerable: true, get: function () { return rules_1.rulesByCategory; } });
44
+ Object.defineProperty(exports, "selectRules", { enumerable: true, get: function () { return rules_1.selectRules; } });
45
+ var skill_auditor_1 = require("./mcp-guard/skill-auditor");
46
+ Object.defineProperty(exports, "AGENT_RULES", { enumerable: true, get: function () { return skill_auditor_1.AGENT_RULES; } });
47
+ Object.defineProperty(exports, "auditSkills", { enumerable: true, get: function () { return skill_auditor_1.auditSkills; } });
48
+ Object.defineProperty(exports, "discoverAgentFiles", { enumerable: true, get: function () { return skill_auditor_1.discoverAgentFiles; } });
49
+ Object.defineProperty(exports, "parseFrontmatter", { enumerable: true, get: function () { return skill_auditor_1.parseFrontmatter; } });
50
+ var context_1 = require("./scanner/context");
51
+ Object.defineProperty(exports, "parseMiddlewareMatchers", { enumerable: true, get: function () { return context_1.parseMiddlewareMatchers; } });
52
+ Object.defineProperty(exports, "parseSqlTables", { enumerable: true, get: function () { return context_1.parseSqlTables; } });
53
+ Object.defineProperty(exports, "mergeSqlTables", { enumerable: true, get: function () { return context_1.mergeSqlTables; } });
54
+ Object.defineProperty(exports, "readGitTrackedPaths", { enumerable: true, get: function () { return context_1.readGitTrackedPaths; } });
55
+ Object.defineProperty(exports, "detectProject", { enumerable: true, get: function () { return context_1.detectProject; } });
56
+ Object.defineProperty(exports, "createFileContext", { enumerable: true, get: function () { return context_1.createFileContext; } });
57
+ Object.defineProperty(exports, "computeFlags", { enumerable: true, get: function () { return context_1.computeFlags; } });
58
+ Object.defineProperty(exports, "detectLanguage", { enumerable: true, get: function () { return context_1.detectLanguage; } });
59
+ Object.defineProperty(exports, "tolerantJsonParse", { enumerable: true, get: function () { return context_1.tolerantJsonParse; } });
60
+ var patterns_1 = require("./secrets/patterns");
61
+ Object.defineProperty(exports, "findSecrets", { enumerable: true, get: function () { return patterns_1.findSecrets; } });
62
+ Object.defineProperty(exports, "SECRET_PATTERNS", { enumerable: true, get: function () { return patterns_1.SECRET_PATTERNS; } });
63
+ Object.defineProperty(exports, "decodeJwtPayload", { enumerable: true, get: function () { return patterns_1.decodeJwtPayload; } });
64
+ Object.defineProperty(exports, "jwtHasPrivilegedRole", { enumerable: true, get: function () { return patterns_1.jwtHasPrivilegedRole; } });
65
+ var console_reporter_1 = require("./reporter/console-reporter");
66
+ Object.defineProperty(exports, "reportConsole", { enumerable: true, get: function () { return console_reporter_1.reportConsole; } });
67
+ Object.defineProperty(exports, "renderConsoleReport", { enumerable: true, get: function () { return console_reporter_1.renderConsoleReport; } });
68
+ Object.defineProperty(exports, "createColorizer", { enumerable: true, get: function () { return console_reporter_1.createColorizer; } });
69
+ Object.defineProperty(exports, "verdictLine", { enumerable: true, get: function () { return console_reporter_1.verdictLine; } });
70
+ var formatters_1 = require("./reporter/formatters");
71
+ Object.defineProperty(exports, "formatResult", { enumerable: true, get: function () { return formatters_1.formatResult; } });
72
+ Object.defineProperty(exports, "formatJson", { enumerable: true, get: function () { return formatters_1.formatJson; } });
73
+ Object.defineProperty(exports, "formatSummary", { enumerable: true, get: function () { return formatters_1.formatSummary; } });
74
+ Object.defineProperty(exports, "formatCompact", { enumerable: true, get: function () { return formatters_1.formatCompact; } });
75
+ Object.defineProperty(exports, "formatSarif", { enumerable: true, get: function () { return formatters_1.formatSarif; } });
76
+ Object.defineProperty(exports, "formatGithub", { enumerable: true, get: function () { return formatters_1.formatGithub; } });
77
+ Object.defineProperty(exports, "groupByFile", { enumerable: true, get: function () { return formatters_1.groupByFile; } });
78
+ Object.defineProperty(exports, "isOutputFormat", { enumerable: true, get: function () { return formatters_1.isOutputFormat; } });
79
+ Object.defineProperty(exports, "OUTPUT_FORMATS", { enumerable: true, get: function () { return formatters_1.OUTPUT_FORMATS; } });
80
+ Object.defineProperty(exports, "SCHEMA_VERSION", { enumerable: true, get: function () { return formatters_1.SCHEMA_VERSION; } });
81
+ var patch_prompt_writer_1 = require("./reporter/patch-prompt-writer");
82
+ Object.defineProperty(exports, "buildPatchPrompt", { enumerable: true, get: function () { return patch_prompt_writer_1.buildPatchPrompt; } });
83
+ Object.defineProperty(exports, "writePatchPrompt", { enumerable: true, get: function () { return patch_prompt_writer_1.writePatchPrompt; } });
84
+ Object.defineProperty(exports, "PATCH_PROMPT_FILENAME", { enumerable: true, get: function () { return patch_prompt_writer_1.PATCH_PROMPT_FILENAME; } });
85
+ var scaffold_1 = require("./init/scaffold");
86
+ Object.defineProperty(exports, "buildScaffold", { enumerable: true, get: function () { return scaffold_1.buildScaffold; } });
87
+ Object.defineProperty(exports, "installScaffold", { enumerable: true, get: function () { return scaffold_1.installScaffold; } });
88
+ Object.defineProperty(exports, "SKILL_TEMPLATE", { enumerable: true, get: function () { return scaffold_1.SKILL_TEMPLATE; } });
89
+ Object.defineProperty(exports, "AGENTS_SECTION", { enumerable: true, get: function () { return scaffold_1.AGENTS_SECTION; } });
90
+ Object.defineProperty(exports, "WORKFLOW_TEMPLATE", { enumerable: true, get: function () { return scaffold_1.WORKFLOW_TEMPLATE; } });
91
+ Object.defineProperty(exports, "IGNORE_TEMPLATE", { enumerable: true, get: function () { return scaffold_1.IGNORE_TEMPLATE; } });
92
+ var types_1 = require("./scanner/types");
93
+ Object.defineProperty(exports, "SEVERITIES", { enumerable: true, get: function () { return types_1.SEVERITIES; } });
94
+ Object.defineProperty(exports, "SEVERITY_WEIGHT", { enumerable: true, get: function () { return types_1.SEVERITY_WEIGHT; } });
95
+ Object.defineProperty(exports, "SEVERITY_SCORE_PENALTY", { enumerable: true, get: function () { return types_1.SEVERITY_SCORE_PENALTY; } });
96
+ Object.defineProperty(exports, "SEVERITY_SCORE_CEILING", { enumerable: true, get: function () { return types_1.SEVERITY_SCORE_CEILING; } });
97
+ Object.defineProperty(exports, "GRADES", { enumerable: true, get: function () { return types_1.GRADES; } });
98
+ Object.defineProperty(exports, "GRADE_THRESHOLDS", { enumerable: true, get: function () { return types_1.GRADE_THRESHOLDS; } });
99
+ Object.defineProperty(exports, "RULE_CATEGORIES", { enumerable: true, get: function () { return types_1.RULE_CATEGORIES; } });
100
+ Object.defineProperty(exports, "CATEGORY_TITLES", { enumerable: true, get: function () { return types_1.CATEGORY_TITLES; } });
101
+ Object.defineProperty(exports, "emptyProjectContext", { enumerable: true, get: function () { return types_1.emptyProjectContext; } });
102
+ Object.defineProperty(exports, "emptySeverityCounts", { enumerable: true, get: function () { return types_1.emptySeverityCounts; } });
103
+ Object.defineProperty(exports, "severityRank", { enumerable: true, get: function () { return types_1.severityRank; } });
104
+ Object.defineProperty(exports, "meetsSeverity", { enumerable: true, get: function () { return types_1.meetsSeverity; } });
105
+ Object.defineProperty(exports, "isSeverity", { enumerable: true, get: function () { return types_1.isSeverity; } });
106
+ Object.defineProperty(exports, "isRuleCategory", { enumerable: true, get: function () { return types_1.isRuleCategory; } });
107
+ /** Package version, injected at build time from `package.json`. */
108
+ exports.VERSION = '1.1.0';
@@ -0,0 +1,122 @@
1
+ /**
2
+ * vibe-shield :: `npx github:avivgerz-hue/vibe-shield init`
3
+ * -------------------------------------
4
+ * One command turns any repository into a guarded one. It installs:
5
+ *
6
+ * - `.claude/skills/vibe-shield/SKILL.md` — the Agent Skill, so Claude Code
7
+ * (and any SKILL.md-aware agent) audits before it reports "done".
8
+ * - an `AGENTS.md` section — the same contract for Codex, Cursor, Windsurf and
9
+ * every other agent that reads a repo-level instruction file.
10
+ * - `.github/workflows/vibe-shield.yml` — fails the build on new findings.
11
+ * - `.vibe-shieldignore` — the documented escape hatch for generated code.
12
+ *
13
+ * Nothing is overwritten without `--force`, and every write is reported so the
14
+ * command is safe to re-run.
15
+ */
16
+ /** One file the scaffolder wants to create. */
17
+ export interface ScaffoldFile {
18
+ /** Path relative to the target repository root (POSIX). */
19
+ relativePath: string;
20
+ contents: string;
21
+ /** `write` creates if missing, `append` adds a section, `merge` adds one JSON key. */
22
+ mode: 'write' | 'append' | 'merge';
23
+ /** Marker used to detect an existing section for `append` mode. */
24
+ marker?: string;
25
+ /** Human description shown in the init summary. */
26
+ description: string;
27
+ }
28
+ export interface InitOptions {
29
+ /** Repository root (absolute). */
30
+ rootDir: string;
31
+ /** Which agent integration to install. */
32
+ agents?: 'claude' | 'cursor' | 'generic' | 'all';
33
+ /** Write the GitHub Actions workflow. Default true. */
34
+ workflow?: boolean;
35
+ /** Overwrite existing files. Default false. */
36
+ force?: boolean;
37
+ /** Print what would happen without writing. */
38
+ dryRun?: boolean;
39
+ /** Pin a specific version in the workflow (default `latest`). */
40
+ version?: string;
41
+ /**
42
+ * Commit to pin the `.mcp.json` entry to. Resolved from the installed package
43
+ * when omitted; see `resolveInstallRev`.
44
+ */
45
+ rev?: string | null;
46
+ /** Environment to read the revision override from. Defaults to `process.env`. */
47
+ env?: NodeJS.ProcessEnv;
48
+ }
49
+ export interface InitResult {
50
+ written: string[];
51
+ appended: string[];
52
+ skipped: string[];
53
+ /** Files that exist but were left alone because they could not be merged. */
54
+ blocked: string[];
55
+ warnings: string[];
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";
59
+ /** Key this tool owns inside `mcpServers`; nothing else is ever touched. */
60
+ export declare const MCP_SERVER_KEY = "vibe-shield";
61
+ /**
62
+ * The server entry written into `.mcp.json`.
63
+ *
64
+ * `npx` is used rather than a local path so the config keeps working when the
65
+ * project is cloned elsewhere or the package is upgraded.
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).
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): {
76
+ command: string;
77
+ args: string[];
78
+ };
79
+ /** The unpinned entry, kept for callers that have no commit available. */
80
+ export declare const MCP_SERVER_ENTRY: {
81
+ command: string;
82
+ args: string[];
83
+ };
84
+ /**
85
+ * Merge the vibe-shield server into an existing `.mcp.json` without disturbing
86
+ * anything already there.
87
+ *
88
+ * Returns the serialised file and whether our key was newly added. Existing
89
+ * servers, other top-level keys and the user's formatting choices are preserved;
90
+ * a malformed file is reported rather than overwritten, because silently
91
+ * rewriting someone's MCP config could break every server they run.
92
+ */
93
+ /**
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.
100
+ *
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.
105
+ */
106
+ export declare function resolveInstallRev(rootDir?: string, env?: NodeJS.ProcessEnv): Promise<string | null>;
107
+ export declare function mergeMcpConfig(existing: string | null, options?: {
108
+ force?: boolean;
109
+ rev?: string | null;
110
+ }): {
111
+ contents: string;
112
+ changed: boolean;
113
+ error?: string;
114
+ };
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";
116
+ 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
+ /** Build the full set of files for a given target repository. */
118
+ export declare function buildScaffold(options: InitOptions): ScaffoldFile[];
119
+ /** Write the scaffold into `options.rootDir`. Idempotent unless `force`. */
120
+ export declare function installScaffold(options: InitOptions): Promise<InitResult>;
121
+ /** Locate the shipped `SKILL.md` (used by `init` to copy the canonical file). */
122
+ export declare function locateBundledSkill(startDir: string): Promise<string | null>;