guardvibe 2.5.0 → 2.7.3
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.
- package/CHANGELOG.md +35 -0
- package/README.md +43 -14
- package/build/cli/args.d.ts +15 -0
- package/build/cli/args.js +78 -0
- package/build/cli/ci.d.ts +5 -0
- package/build/cli/ci.js +67 -0
- package/build/cli/doctor.d.ts +5 -0
- package/build/cli/doctor.js +35 -0
- package/build/cli/hook.d.ts +5 -0
- package/build/cli/hook.js +90 -0
- package/build/cli/init.d.ts +5 -0
- package/build/cli/init.js +214 -0
- package/build/cli/remediation.d.ts +14 -0
- package/build/cli/remediation.js +91 -0
- package/build/cli/scan.d.ts +11 -0
- package/build/cli/scan.js +222 -0
- package/build/cli.js +33 -639
- package/build/data/rules/ai-host-security.d.ts +2 -0
- package/build/data/rules/ai-host-security.js +128 -0
- package/build/data/rules/ai-tool-runtime.d.ts +2 -0
- package/build/data/rules/ai-tool-runtime.js +54 -0
- package/build/data/rules/index.js +4 -0
- package/build/index.js +36 -0
- package/build/server/register.d.ts +7 -0
- package/build/server/register.js +7 -0
- package/build/server/types.d.ts +40 -0
- package/build/server/types.js +130 -0
- package/build/tools/audit-mcp-config.d.ts +10 -0
- package/build/tools/audit-mcp-config.js +296 -0
- package/build/tools/doctor.d.ts +14 -0
- package/build/tools/doctor.js +123 -0
- package/build/tools/scan-host-config.d.ts +10 -0
- package/build/tools/scan-host-config.js +181 -0
- package/package.json +2 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,41 @@ All notable changes to GuardVibe are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.7.1] - 2026-04-04
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- `--output` flag without value no longer creates a file named "true" — now errors with clear message
|
|
12
|
+
- `--fail-on` default standardized to "critical" across all CLI commands (was inconsistently "high" in `guardvibe-scan`)
|
|
13
|
+
- `--format` with invalid value (e.g., "yaml") now errors explicitly instead of silently falling back to markdown
|
|
14
|
+
- `--output` with nested path (e.g., `--output reports/deep/out.json`) now auto-creates parent directories
|
|
15
|
+
- CLI error messages now consistently use `[ERR]` prefix across all commands
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Secret redaction expanded from 3 to 13 patterns — now covers AWS keys, GitHub tokens, Stripe keys, Google API keys, Slack tokens, SendGrid keys, private key headers, and DATABASE_URL
|
|
19
|
+
- `guardvibe-init` binary entry removed as part of early CLI surface cleanup. Use `guardvibe init` going forward. This simplifies the public CLI before wider adoption.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- 21 new stabilization tests covering all v2.7.1 fixes
|
|
23
|
+
|
|
24
|
+
## [2.7.0] - 2026-04-04
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- `npx guardvibe doctor` CLI command with `--scope`, `--format`, `--output`, `--fail-on` flags
|
|
28
|
+
- CLI modular decomposition: `src/cli/` with args, init, hook, ci, scan, doctor, remediation modules
|
|
29
|
+
- Host-specific remediation: findings now include platform-tailored fix steps for Claude, Cursor, VS Code, Gemini, Windsurf, Shell, and .env files
|
|
30
|
+
- 13 new doctor CLI tests
|
|
31
|
+
|
|
32
|
+
## [2.6.0] - 2026-04-04
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- Host security: `guardvibe_doctor` unified host hardening scanner
|
|
36
|
+
- `audit_mcp_config` — MCP configuration scanner (CVE-2025-59536 hook injection)
|
|
37
|
+
- `scan_host_config` — environment scanner (CVE-2026-21852 base URL hijack)
|
|
38
|
+
- Four-axis finding model: severity, trustState, verdict, confidence
|
|
39
|
+
- 14 new AI host security rules (VG880-VG895)
|
|
40
|
+
- Secret redaction in all output paths
|
|
41
|
+
- `.guardviberc` allowlist support for trusted servers, base URLs, registries
|
|
42
|
+
|
|
8
43
|
## [2.5.0] - 2026-04-04
|
|
9
44
|
|
|
10
45
|
### Added
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/guardvibe)
|
|
7
7
|
[](https://codecov.io/gh/goklab/guardvibe)
|
|
8
8
|
|
|
9
|
-
**The security MCP built for vibe coding.**
|
|
9
|
+
**The security MCP built for vibe coding.** 330 security rules, 29 tools covering the entire AI-generated code journey — from first line to production deployment.
|
|
10
10
|
|
|
11
11
|
Works with **Claude Code, Cursor, Gemini CLI, Codex, VS Code (Copilot), Windsurf**, and any MCP-compatible coding agent.
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ Works with **Claude Code, Cursor, Gemini CLI, Codex, VS Code (Copilot), Windsurf
|
|
|
14
14
|
|
|
15
15
|
Most security tools are built for enterprise security teams. GuardVibe is built for **you** — the developer using AI to build and ship web apps fast.
|
|
16
16
|
|
|
17
|
-
- **
|
|
17
|
+
- **330 security rules, 29 tools** purpose-built for the stacks AI agents generate
|
|
18
18
|
- **Zero setup friction** — `npx guardvibe` and you're scanning
|
|
19
19
|
- **No account required** — runs 100% locally, no API keys, no cloud
|
|
20
20
|
- **Understands your stack** — not generic SAST, but rules that know Next.js, Supabase, Stripe, Clerk, and the tools you actually use
|
|
@@ -35,12 +35,13 @@ GuardVibe is purpose-built for the AI coding workflow. Traditional tools are exc
|
|
|
35
35
|
| Runs inside AI agents (MCP) | Native | Not supported | Not supported |
|
|
36
36
|
| Zero config setup | `npx guardvibe` | Account + config required | Built-in (limited) |
|
|
37
37
|
| Vibecoding stack rules (Next.js, Supabase, Clerk, tRPC, Hono) | 100+ dedicated | Generic patterns | Not applicable |
|
|
38
|
-
| AI/LLM security (prompt injection, MCP, tool abuse) |
|
|
38
|
+
| AI/LLM security (prompt injection, MCP, tool abuse) | 30 rules | Experimental/None | None |
|
|
39
|
+
| AI host security (CVE-2025-59536, CVE-2026-21852) | `guardvibe doctor` | Not supported | Not supported |
|
|
39
40
|
| Auto-fix suggestions for AI agents | `fix_code` tool | CLI autofix | Not supported |
|
|
40
41
|
| CVE version detection | 21 packages | Extensive | Extensive |
|
|
41
42
|
| Compliance mapping (SOC2, PCI-DSS, HIPAA) | Built-in | Paid tier | None |
|
|
42
43
|
| SARIF CI/CD export | Yes | Yes | Limited |
|
|
43
|
-
| Rule count |
|
|
44
|
+
| Rule count | 330 (focused) | 5000+ (broad) | N/A |
|
|
44
45
|
|
|
45
46
|
**When to use GuardVibe:** You're building with AI agents and want security scanning integrated into your coding workflow — no dashboard, no account, no CI setup.
|
|
46
47
|
|
|
@@ -149,6 +150,9 @@ Resend (email HTML injection), Upstash Redis, Pinecone, PostHog, Google Analytic
|
|
|
149
150
|
### AI / LLM Security
|
|
150
151
|
Prompt injection detection, LLM output sinks, system prompt leaks, MCP server SSRF/path traversal/command injection, `dangerouslyAllowBrowser`, missing `maxTokens`, AI API key client exposure, indirect prompt injection via external data
|
|
151
152
|
|
|
153
|
+
### AI Host Security (NEW in v2.6.0+)
|
|
154
|
+
`guardvibe doctor` — unified host hardening scanner detecting CVE-2025-59536 (hook injection via `.claude/settings.json`), CVE-2026-21852 (API key exfiltration via `ANTHROPIC_BASE_URL` override), MCP config audit, environment scanner, permission analysis. Supports Claude, Cursor, VS Code, Gemini, Windsurf. Host-specific remediation with platform-tailored fix steps.
|
|
155
|
+
|
|
152
156
|
### OWASP API Security
|
|
153
157
|
BOLA/IDOR (Broken Object Level Authorization), mass assignment (spread request body, Object.assign), missing pagination, rate limiting, admin endpoint authorization, verbose error leaks
|
|
154
158
|
|
|
@@ -179,7 +183,7 @@ SOC2, PCI-DSS, HIPAA control mapping with compliance reports
|
|
|
179
183
|
### Supply Chain
|
|
180
184
|
Malicious postinstall scripts, unpinned GitHub Actions, typosquat detection
|
|
181
185
|
|
|
182
|
-
## Tools (
|
|
186
|
+
## Tools (29 MCP tools)
|
|
183
187
|
|
|
184
188
|
| Tool | What it does |
|
|
185
189
|
|------|-------------|
|
|
@@ -209,26 +213,31 @@ Malicious postinstall scripts, unpinned GitHub Actions, typosquat detection
|
|
|
209
213
|
| `scan_file` | Real-time single-file scan — designed for post-edit hooks |
|
|
210
214
|
| `scan_changed_files` | Scan only git-changed files — for PRs and incremental CI |
|
|
211
215
|
| `security_stats` | Cumulative security dashboard — scans, fixes, grade trend over time |
|
|
216
|
+
| `guardvibe_doctor` | **Host security audit** — CVE-2025-59536, CVE-2026-21852, MCP config, env scanner |
|
|
217
|
+
| `audit_mcp_config` | Audit MCP server configurations for hook injection, file:// abuse, sensitive paths |
|
|
218
|
+
| `scan_host_config` | Scan shell profiles, .env files for base URL hijack and credential sniffing |
|
|
212
219
|
|
|
213
220
|
All scanning tools support `format: "json"` for machine-readable output.
|
|
214
221
|
|
|
215
|
-
## Security Rules (
|
|
222
|
+
## Security Rules (330 rules across 25 modules)
|
|
216
223
|
|
|
217
224
|
| Category | Rules | Coverage |
|
|
218
225
|
|----------|-------|----------|
|
|
219
226
|
| Core OWASP | 38 | SQL injection, XSS, CSRF, command injection, CORS, SSRF, hardcoded secrets |
|
|
220
227
|
| Next.js App Router | 17 | Server Actions, secret exposure, auth bypass, CSP, redirects |
|
|
221
228
|
| Auth (Clerk / Auth.js / Supabase Auth) | 16 | Middleware, secret keys, session storage, role checks, SSR cookies |
|
|
222
|
-
| Database (Supabase / Prisma / Drizzle) |
|
|
229
|
+
| Database (Supabase / Prisma / Drizzle) | 11 | Raw queries, client exposure, service role leaks, NoSQL injection |
|
|
223
230
|
| OWASP API Security | 10 | BOLA/IDOR, mass assignment, pagination, rate limiting, error leaks |
|
|
224
|
-
| Modern Stack |
|
|
225
|
-
| Deployment Config |
|
|
231
|
+
| Modern Stack | 37 | Zod, tRPC, Hono, GraphQL, Uploadthing, Turso, Convex, OAuth, CSP, webhooks, AI SDK |
|
|
232
|
+
| Deployment Config | 21 | Vercel, Next.js config, Docker Compose, Fly, Render, Netlify, Cloudflare, K8s secrets |
|
|
226
233
|
| Payments (Stripe / Polar / Lemon) | 9 | Webhook signatures, key exposure, price manipulation |
|
|
227
234
|
| Services (Resend / Upstash / Pinecone / PostHog) | 11 | API key leaks, PII tracking, email injection |
|
|
228
235
|
| Web Security | 15 | Webhooks, CSP, .env safety, AI key exposure, cookie handling |
|
|
229
236
|
| React Native / Expo | 10 | AsyncStorage secrets, deep links, ATS, hardcoded URLs |
|
|
230
237
|
| Firebase | 7 | Firestore rules, admin SDK, storage, custom tokens |
|
|
231
238
|
| AI / LLM Security | 16 | Prompt injection, MCP SSRF, excessive agency, indirect injection |
|
|
239
|
+
| **AI Host Security** | **10** | **CVE-2025-59536 hook injection, CVE-2026-21852 base URL hijack, MCP config audit** |
|
|
240
|
+
| **AI Tool Runtime** | **4** | **MCP tool output sanitization, obfuscated descriptions, safety bypass** |
|
|
232
241
|
| CVE Version Intelligence | 21 | Known vulnerable versions in package.json (21 CVEs) |
|
|
233
242
|
| Shell / Bash | 5 | Pipe to bash, chmod 777, rm -rf, sudo password |
|
|
234
243
|
| SQL | 4 | DROP/DELETE without WHERE, stacked queries, GRANT ALL |
|
|
@@ -243,12 +252,32 @@ All scanning tools support `format: "json"` for machine-readable output.
|
|
|
243
252
|
## CLI Commands
|
|
244
253
|
|
|
245
254
|
```bash
|
|
246
|
-
|
|
247
|
-
npx guardvibe
|
|
248
|
-
npx guardvibe
|
|
249
|
-
npx guardvibe
|
|
250
|
-
npx guardvibe
|
|
255
|
+
# Scanning
|
|
256
|
+
npx guardvibe scan [path] # Scan a directory for security issues
|
|
257
|
+
npx guardvibe scan . --format json # JSON output for automation
|
|
258
|
+
npx guardvibe check <file> # Scan a single file
|
|
259
|
+
npx guardvibe diff [base] # Scan only changed files since git ref
|
|
260
|
+
|
|
261
|
+
# Host security audit
|
|
262
|
+
npx guardvibe doctor # Host hardening audit (project scope)
|
|
263
|
+
npx guardvibe doctor --scope host # + shell profiles, global MCP configs
|
|
264
|
+
npx guardvibe doctor --scope full # + home dir configs
|
|
265
|
+
npx guardvibe doctor --format json # JSON output
|
|
266
|
+
|
|
267
|
+
# Setup
|
|
268
|
+
npx guardvibe init <platform> # Setup MCP server (claude, cursor, gemini, all)
|
|
269
|
+
npx guardvibe hook install # Install pre-commit hook
|
|
270
|
+
npx guardvibe hook uninstall # Remove pre-commit hook
|
|
271
|
+
npx guardvibe ci github # Generate GitHub Actions workflow
|
|
272
|
+
|
|
273
|
+
# Pre-commit / CI
|
|
274
|
+
npx guardvibe-scan # Scan staged files (for pre-commit)
|
|
251
275
|
npx guardvibe-scan --format sarif --output results.sarif # CI mode
|
|
276
|
+
|
|
277
|
+
# Options (all scan commands)
|
|
278
|
+
# --format markdown|json|sarif|buddy
|
|
279
|
+
# --output <file> Write results to file
|
|
280
|
+
# --fail-on <level> Exit 1 on findings: critical|high|medium|low|none
|
|
252
281
|
```
|
|
253
282
|
|
|
254
283
|
## Plugin System
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI argument parsing utilities
|
|
3
|
+
*/
|
|
4
|
+
export declare function getStringFlag(flags: Record<string, string | true>, key: string): string | null;
|
|
5
|
+
export declare function validateFormat(flags: Record<string, string | true>): string;
|
|
6
|
+
export declare function getOutputPath(flags: Record<string, string | true>): string | null;
|
|
7
|
+
export declare function parseArgs(args: string[]): {
|
|
8
|
+
flags: Record<string, string | true>;
|
|
9
|
+
positional: string[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Check if scan results should cause a non-zero exit based on --fail-on flag.
|
|
13
|
+
* Default: "critical" — only exit 1 on critical findings.
|
|
14
|
+
*/
|
|
15
|
+
export declare function shouldFail(result: string, failOn: string): boolean;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI argument parsing utilities
|
|
3
|
+
*/
|
|
4
|
+
const VALID_FORMATS = new Set(["markdown", "json", "sarif", "buddy"]);
|
|
5
|
+
export function getStringFlag(flags, key) {
|
|
6
|
+
const val = flags[key];
|
|
7
|
+
if (val === undefined || val === true)
|
|
8
|
+
return null;
|
|
9
|
+
return val;
|
|
10
|
+
}
|
|
11
|
+
export function validateFormat(flags) {
|
|
12
|
+
const format = getStringFlag(flags, "format") ?? "markdown";
|
|
13
|
+
if (!VALID_FORMATS.has(format)) {
|
|
14
|
+
console.error(` [ERR] Invalid format "${format}". Use: markdown, json, sarif, or buddy`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
return format;
|
|
18
|
+
}
|
|
19
|
+
export function getOutputPath(flags) {
|
|
20
|
+
const val = flags.output;
|
|
21
|
+
if (val === undefined)
|
|
22
|
+
return null;
|
|
23
|
+
if (val === true) {
|
|
24
|
+
console.error(" [ERR] --output requires a file path. Usage: --output results.json");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
return val;
|
|
28
|
+
}
|
|
29
|
+
export function parseArgs(args) {
|
|
30
|
+
const flags = {};
|
|
31
|
+
const positional = [];
|
|
32
|
+
for (let i = 0; i < args.length; i++) {
|
|
33
|
+
if (args[i].startsWith("--")) {
|
|
34
|
+
const key = args[i].slice(2);
|
|
35
|
+
const next = args[i + 1];
|
|
36
|
+
if (next && !next.startsWith("--")) {
|
|
37
|
+
flags[key] = next;
|
|
38
|
+
i++;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
flags[key] = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
positional.push(args[i]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { flags, positional };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if scan results should cause a non-zero exit based on --fail-on flag.
|
|
52
|
+
* Default: "critical" — only exit 1 on critical findings.
|
|
53
|
+
*/
|
|
54
|
+
export function shouldFail(result, failOn) {
|
|
55
|
+
if (failOn === "none")
|
|
56
|
+
return false;
|
|
57
|
+
const levels = {
|
|
58
|
+
low: ["critical", "high", "medium", "low"],
|
|
59
|
+
medium: ["critical", "high", "medium"],
|
|
60
|
+
high: ["critical", "high"],
|
|
61
|
+
critical: ["critical"],
|
|
62
|
+
};
|
|
63
|
+
const failLevels = levels[failOn] || levels.critical;
|
|
64
|
+
// Try JSON format first
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(result);
|
|
67
|
+
if (parsed.summary) {
|
|
68
|
+
return failLevels.some(level => (parsed.summary[level] ?? 0) > 0);
|
|
69
|
+
}
|
|
70
|
+
if (parsed.findings) {
|
|
71
|
+
return parsed.findings.some((f) => failLevels.includes(f.severity));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch { /* not JSON, try markdown tags */ }
|
|
75
|
+
// Markdown format: check for [SEVERITY] tags
|
|
76
|
+
const tags = failLevels.map(l => `[${l.toUpperCase()}]`);
|
|
77
|
+
return tags.some(tag => result.includes(tag));
|
|
78
|
+
}
|
package/build/cli/ci.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI: guardvibe ci <provider>
|
|
3
|
+
* Generates CI/CD workflow configurations.
|
|
4
|
+
*/
|
|
5
|
+
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
const GITHUB_ACTIONS_WORKFLOW = `name: GuardVibe Security Scan
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
pull_request:
|
|
11
|
+
branches: [main, master]
|
|
12
|
+
push:
|
|
13
|
+
branches: [main, master]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
security-events: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
security-scan:
|
|
21
|
+
name: Security Scan
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
|
|
28
|
+
- uses: actions/setup-node@v4
|
|
29
|
+
with:
|
|
30
|
+
node-version: "22"
|
|
31
|
+
|
|
32
|
+
- name: Run GuardVibe security scan
|
|
33
|
+
run: npx -y guardvibe-scan --format sarif --output guardvibe-results.sarif
|
|
34
|
+
|
|
35
|
+
- name: Upload SARIF to GitHub Security
|
|
36
|
+
if: always()
|
|
37
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
38
|
+
with:
|
|
39
|
+
sarif_file: guardvibe-results.sarif
|
|
40
|
+
category: guardvibe
|
|
41
|
+
`;
|
|
42
|
+
function generateGitHubActions() {
|
|
43
|
+
const workflowDir = join(process.cwd(), ".github", "workflows");
|
|
44
|
+
if (!existsSync(workflowDir)) {
|
|
45
|
+
mkdirSync(workflowDir, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
const workflowPath = join(workflowDir, "guardvibe.yml");
|
|
48
|
+
if (existsSync(workflowPath)) {
|
|
49
|
+
console.log(" [OK] .github/workflows/guardvibe.yml already exists.");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
writeFileSync(workflowPath, GITHUB_ACTIONS_WORKFLOW, "utf-8");
|
|
53
|
+
console.log(" [OK] Created .github/workflows/guardvibe.yml");
|
|
54
|
+
console.log(" [OK] SARIF results will appear in GitHub Security tab.");
|
|
55
|
+
}
|
|
56
|
+
export function runCi(args) {
|
|
57
|
+
const provider = args[0]?.toLowerCase();
|
|
58
|
+
console.log(`\n GuardVibe CI/CD Setup\n`);
|
|
59
|
+
if (provider === "github") {
|
|
60
|
+
generateGitHubActions();
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.error(" [ERR] Unknown CI provider. Usage: npx guardvibe ci github");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
console.log();
|
|
67
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI: guardvibe doctor
|
|
3
|
+
* Host hardening audit from terminal with host-specific remediation.
|
|
4
|
+
*/
|
|
5
|
+
import { writeFileSync, existsSync, mkdirSync } from "fs";
|
|
6
|
+
import { resolve, dirname } from "path";
|
|
7
|
+
import { parseArgs, shouldFail, validateFormat, getOutputPath, getStringFlag } from "./args.js";
|
|
8
|
+
import { doctor } from "../tools/doctor.js";
|
|
9
|
+
export async function runDoctor(args) {
|
|
10
|
+
const { flags, positional } = parseArgs(args);
|
|
11
|
+
const targetPath = resolve(positional[0] ?? ".");
|
|
12
|
+
const scope = (getStringFlag(flags, "scope") ?? "project");
|
|
13
|
+
const format = validateFormat(flags);
|
|
14
|
+
const outputFile = getOutputPath(flags);
|
|
15
|
+
if (!["project", "host", "full"].includes(scope)) {
|
|
16
|
+
console.error(` [ERR] Invalid scope "${scope}". Use: project, host, or full`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const formatArg = format === "json" ? "json" : "markdown";
|
|
20
|
+
const result = doctor(targetPath, scope, formatArg);
|
|
21
|
+
if (outputFile) {
|
|
22
|
+
const dir = dirname(outputFile);
|
|
23
|
+
if (!existsSync(dir)) {
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
writeFileSync(outputFile, result, "utf-8");
|
|
27
|
+
console.log(` [OK] Results written to ${outputFile}`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(result);
|
|
31
|
+
}
|
|
32
|
+
const failOn = getStringFlag(flags, "fail-on") ?? "high";
|
|
33
|
+
if (shouldFail(result, failOn))
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI: guardvibe hook install|uninstall
|
|
3
|
+
* Manages pre-commit security hooks.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, unlinkSync } from "fs";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
const HOOK_SCRIPT = `#!/bin/sh
|
|
8
|
+
# GuardVibe pre-commit security hook
|
|
9
|
+
# Installed by: npx guardvibe hook install
|
|
10
|
+
|
|
11
|
+
echo "🔒 GuardVibe: scanning staged files..."
|
|
12
|
+
|
|
13
|
+
# Run guardvibe scan on staged files
|
|
14
|
+
RESULT=$(npx -y guardvibe-scan 2>&1)
|
|
15
|
+
EXIT_CODE=$?
|
|
16
|
+
|
|
17
|
+
if [ $EXIT_CODE -ne 0 ]; then
|
|
18
|
+
echo ""
|
|
19
|
+
echo "$RESULT"
|
|
20
|
+
echo ""
|
|
21
|
+
echo "❌ GuardVibe: security issues found. Fix them or commit with --no-verify to skip."
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
echo "✅ GuardVibe: all checks passed."
|
|
26
|
+
`;
|
|
27
|
+
function installHook() {
|
|
28
|
+
const gitDir = join(process.cwd(), ".git");
|
|
29
|
+
if (!existsSync(gitDir)) {
|
|
30
|
+
console.error(" [ERR] Not a git repository. Run this from your project root.");
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
const hooksDir = join(gitDir, "hooks");
|
|
34
|
+
if (!existsSync(hooksDir)) {
|
|
35
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
const hookPath = join(hooksDir, "pre-commit");
|
|
38
|
+
if (existsSync(hookPath)) {
|
|
39
|
+
const existing = readFileSync(hookPath, "utf-8");
|
|
40
|
+
if (existing.includes("GuardVibe")) {
|
|
41
|
+
console.log(" [OK] GuardVibe pre-commit hook already installed.");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
writeFileSync(hookPath, existing + "\n" + HOOK_SCRIPT, "utf-8");
|
|
45
|
+
console.log(" [OK] GuardVibe added to existing pre-commit hook.");
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
writeFileSync(hookPath, HOOK_SCRIPT, "utf-8");
|
|
49
|
+
chmodSync(hookPath, 0o755);
|
|
50
|
+
console.log(" [OK] Pre-commit hook installed at .git/hooks/pre-commit");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function uninstallHook() {
|
|
54
|
+
const hookPath = join(process.cwd(), ".git", "hooks", "pre-commit");
|
|
55
|
+
if (!existsSync(hookPath)) {
|
|
56
|
+
console.log(" [OK] No pre-commit hook found.");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const content = readFileSync(hookPath, "utf-8");
|
|
60
|
+
if (!content.includes("GuardVibe")) {
|
|
61
|
+
console.log(" [OK] Pre-commit hook exists but doesn't contain GuardVibe.");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const cleaned = content
|
|
65
|
+
.replace(/\n?# GuardVibe pre-commit security hook[\s\S]*?GuardVibe: all checks passed[."]*\n?/g, "")
|
|
66
|
+
.trim();
|
|
67
|
+
if (!cleaned || cleaned === "#!/bin/sh") {
|
|
68
|
+
unlinkSync(hookPath);
|
|
69
|
+
console.log(" [OK] Pre-commit hook removed.");
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
writeFileSync(hookPath, cleaned + "\n", "utf-8");
|
|
73
|
+
console.log(" [OK] GuardVibe removed from pre-commit hook (other hooks preserved).");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function runHook(args) {
|
|
77
|
+
const action = args[0]?.toLowerCase();
|
|
78
|
+
console.log(`\n GuardVibe Pre-Commit Hook\n`);
|
|
79
|
+
if (action === "install") {
|
|
80
|
+
installHook();
|
|
81
|
+
}
|
|
82
|
+
else if (action === "uninstall") {
|
|
83
|
+
uninstallHook();
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.error(" [ERR] Unknown action. Usage: npx guardvibe hook install|uninstall");
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
console.log();
|
|
90
|
+
}
|