guardvibe 3.1.34 → 3.1.36
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 +21 -0
- package/README.md +5 -5
- package/build/data/rules/api-security.js +1 -1
- package/build/data/rules/modern-stack.js +1 -1
- package/build/data/rules/payments.js +1 -1
- package/build/data/rules/web-security.js +24 -0
- package/build/tools/check-code.js +21 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ 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
|
+
## [3.1.36] - 2026-06-07
|
|
9
|
+
|
|
10
|
+
### Added — high-value recall rules (436 → 438, 36 tools)
|
|
11
|
+
- **VG1083** JWT verification bypass — flags `jwt.decode()` of a request-supplied token used without a real signature check, and `jwt.verify(..., { algorithms: ['none'] })` (algorithm-confusion / signature stripping). The decode branch is suppressed when the same file also verifies the token (decode-then-verify is legitimate).
|
|
12
|
+
- **VG1084** DOM XSS via jQuery HTML insertion — `.html()/.append()/.prepend()/.after()/.before()/.replaceWith()` with user-controlled or concatenated/interpolated content (skips `.text()` and static literals).
|
|
13
|
+
|
|
14
|
+
Both validated against the real-world corpus: zero false positives (the one borderline juice-shop `jwt.decode`-then-`verify` hit is correctly suppressed). The ReDoS guard now re-measures any over-budget pattern and uses the minimum across runs, so CPU/GC load spikes can no longer cause a flaky failure while genuine backtracking (consistently slow) is still caught.
|
|
15
|
+
|
|
16
|
+
## [3.1.35] - 2026-06-07
|
|
17
|
+
|
|
18
|
+
### Fixed — false-positive precision on real production apps (no rule-count change, 436 / 36)
|
|
19
|
+
Surfaced by the precision half of the quality sweep; each narrowing was verified against the cited code and confirmed (via an old-vs-new diff over the corpus) to remove only false positives, with zero true-positive loss.
|
|
20
|
+
- **VG123 / VG010** no longer flag a parameterized IN-clause built from placeholder generation (`id IN (${ids.map(() => '?').join(',')})` with values passed as the params array).
|
|
21
|
+
- **VG137** (debug endpoint) no longer fires on build/test config files (`vite.config`, `jest-e2e`, `playwright.config`, `vitest`, etc.) where a `/test` path string sits near `process.env`.
|
|
22
|
+
- **VG1005** (Supabase `.or()` filter injection) now requires actual Supabase usage in the file, ending the collision with Zod's `.or()` schema combinator.
|
|
23
|
+
- **VG968** (cron `CRON_SECRET`) recognizes Vercel/QStash signature verification (`verifyVercelSignature`, `verifyQstashSignature`, `Receiver`) as valid cron auth.
|
|
24
|
+
- **VG951** (BOLA) recognizes tenant compound-where ownership (`where: { id, projectId | workspaceId | teamId }`).
|
|
25
|
+
- **VG601** (Stripe webhook) recognizes non-Stripe signature verification (QStash/Vercel/generic `verifySignature`).
|
|
26
|
+
|
|
27
|
+
Self-audit PASS / A / 0, gate green.
|
|
28
|
+
|
|
8
29
|
## [3.1.34] - 2026-06-07
|
|
9
30
|
|
|
10
31
|
### Added — recall (false-negative) improvements (433 → 436 rules, 36 tools)
|
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.** 438 security rules, 36 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
|
+
- **438 security rules, 36 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
|
|
@@ -52,7 +52,7 @@ GuardVibe is purpose-built for the AI coding workflow. Traditional tools are exc
|
|
|
52
52
|
| CVE version detection | 67 packages, refreshed daily | Extensive | Extensive |
|
|
53
53
|
| Compliance mapping (SOC2, PCI-DSS, HIPAA) | Built-in | Paid tier | None |
|
|
54
54
|
| SARIF CI/CD export | Yes | Yes | Limited |
|
|
55
|
-
| Rule count |
|
|
55
|
+
| Rule count | 438 (focused, 68 AI-native) | 5000+ (broad) | N/A |
|
|
56
56
|
|
|
57
57
|
**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.
|
|
58
58
|
|
|
@@ -242,7 +242,7 @@ Malicious postinstall scripts, unpinned GitHub Actions, CI `npm` provenance / `-
|
|
|
242
242
|
|
|
243
243
|
All scanning tools support `format: "json"` for machine-readable output.
|
|
244
244
|
|
|
245
|
-
## Security Rules (
|
|
245
|
+
## Security Rules (438 rules across 25 modules)
|
|
246
246
|
|
|
247
247
|
| Category | Rules | Coverage |
|
|
248
248
|
|----------|-------|----------|
|
|
@@ -457,7 +457,7 @@ If your AI agent cannot connect to GuardVibe:
|
|
|
457
457
|
|
|
458
458
|
1. **Restart your IDE/agent.** MCP servers are started by the host application. After running `npx guardvibe init`, restart Claude Code, Cursor, or Gemini CLI for the config to take effect.
|
|
459
459
|
2. **Check the config path.** Run `npx guardvibe init claude` again and verify the output shows the correct config file location (`.mcp.json` in your project root for Claude Code, `.cursor/mcp.json` for Cursor).
|
|
460
|
-
3. **Re-run `init` to upgrade.** When upgrading GuardVibe, re-run `npx guardvibe init claude` — `.mcp.json` is pinned to a specific version (e.g. `guardvibe@3.1.
|
|
460
|
+
3. **Re-run `init` to upgrade.** When upgrading GuardVibe, re-run `npx guardvibe init claude` — `.mcp.json` is pinned to a specific version (e.g. `guardvibe@3.1.36`) at init time for fast deterministic startup. As of v3.1.2 the re-run also rewrites stale pins automatically (`Upgraded GuardVibe pin (3.1.27 → 3.1.28)`); since v3.1.27 the PostToolUse hook command is pinned to the same version (was `@latest`) and re-run upgrades a stale hook too. The same applies to `npx guardvibe hook install` and `npx guardvibe ci github` (since v3.1.3) — both are version-pinned at install/generate time and re-run to upgrade.
|
|
461
461
|
4. **Pre-3.1.1 users won't see the auto-update banner.** GuardVibe started writing a once-per-day "newer version available" notice to stderr in v3.1.1. If your install predates that, you'll never see it — run `npx -y guardvibe@latest init <host>` once to bake in the latest pin and start receiving banners on subsequent sessions.
|
|
462
462
|
5. **Verify Node.js version.** GuardVibe requires Node.js >= 18.0.0. Check with `node --version`.
|
|
463
463
|
6. **Check npx cache.** If you upgraded GuardVibe and the old version is cached, run `npx -y guardvibe@latest` to force the latest version.
|
|
@@ -20,7 +20,7 @@ export const apiSecurityRules = [
|
|
|
20
20
|
severity: "critical",
|
|
21
21
|
owasp: "API1:2023 Broken Object Level Authorization",
|
|
22
22
|
description: "Delete or update operation uses user-supplied ID without verifying resource ownership. Any authenticated user can modify or delete other users' resources.",
|
|
23
|
-
pattern: /(?:delete|update|destroy|remove)\s*\(\s*\{?\s*(?:where\s*:\s*\{)?\s*(?:id|_id)\s*:\s*(?:req\.(?:params|query|body)|params\.|args\.|input\.)(?:(?!userId|user_id|ownerId|owner_id|createdBy|created_by|author|authorId|author_id|email|userEmail|accountId|account_id|tenantId|tenant_id|orgId|org_id|organizationId)[\s\S]){0,200}?\}/gi,
|
|
23
|
+
pattern: /(?:delete|update|destroy|remove)\s*\(\s*\{?\s*(?:where\s*:\s*\{)?\s*(?:id|_id)\s*:\s*(?:req\.(?:params|query|body)|params\.|args\.|input\.)(?:(?!userId|user_id|ownerId|owner_id|createdBy|created_by|author|authorId|author_id|email|userEmail|accountId|account_id|tenantId|tenant_id|orgId|org_id|organizationId|projectId|project_id|workspaceId|workspace_id|teamId|team_id)[\s\S]){0,200}?\}/gi,
|
|
24
24
|
languages: ["javascript", "typescript"],
|
|
25
25
|
fix: "Include the authenticated user's ID in the where clause to prevent unauthorized modifications.",
|
|
26
26
|
fixCode: '// Scope mutations to the authenticated user\nconst { userId } = await auth();\nawait prisma.post.delete({\n where: { id: params.id, userId }, // ownership!\n});',
|
|
@@ -122,7 +122,7 @@ export const modernStackRules = [
|
|
|
122
122
|
severity: "high",
|
|
123
123
|
owasp: "A01:2025 Broken Access Control",
|
|
124
124
|
description: "Vercel cron job endpoint does not verify the CRON_SECRET header. Anyone can trigger the cron job by calling the endpoint directly.",
|
|
125
|
-
pattern: /(?:\/api\/cron|cron)[\s\S]*?export\s+(?:async\s+)?function\s+GET\s*\([^)]*\)\s*\{(?:(?!CRON_SECRET|authorization|Bearer|verifySignature|x-vercel-cron)[\s\S]){10,}?(?:prisma|db|supabase|fetch|sql|resend|stripe)\.\w+/g,
|
|
125
|
+
pattern: /(?:\/api\/cron|cron)[\s\S]*?export\s+(?:async\s+)?function\s+GET\s*\([^)]*\)\s*\{(?:(?!CRON_SECRET|authorization|Bearer|verifySignature|x-vercel-cron|verifyVercelSignature|verifyQstash|verifyQstashSignature|verifySignatureAppRouter|Receiver|verifyCron)[\s\S]){10,}?(?:prisma|db|supabase|fetch|sql|resend|stripe)\.\w+/g,
|
|
126
126
|
languages: ["javascript", "typescript"],
|
|
127
127
|
fix: "Verify the CRON_SECRET header at the start of every cron endpoint.",
|
|
128
128
|
fixCode: 'export async function GET(request: Request) {\n const authHeader = request.headers.get("authorization");\n if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {\n return new Response("Unauthorized", { status: 401 });\n }\n // ... cron job logic\n}',
|
|
@@ -18,7 +18,7 @@ export const paymentRules = [
|
|
|
18
18
|
severity: "critical",
|
|
19
19
|
owasp: "A01:2025 Broken Access Control",
|
|
20
20
|
description: "Stripe webhook endpoint processes events without verifying the webhook signature. Anyone can send fake payment events.",
|
|
21
|
-
pattern: /(?:\/api\/webhook|\/api\/stripe|webhook.*stripe)[\s\S]*?(?:req\.body|request\.json|JSON\.parse)[\s\S]{0,300}?(?![\s\S]{0,300}?(?:constructEvent|verifyHeader|stripe\.webhooks|svix\.verify|webhook\.verify|verify\w*Webhook|verifyWebhookSignature|wh\.verify|crypto\.timingSafeEqual))/g,
|
|
21
|
+
pattern: /(?:\/api\/webhook|\/api\/stripe|webhook.*stripe)[\s\S]*?(?:req\.body|request\.json|JSON\.parse)[\s\S]{0,300}?(?![\s\S]{0,300}?(?:constructEvent|verifyHeader|stripe\.webhooks|svix\.verify|webhook\.verify|verify\w*Webhook|verifyWebhookSignature|wh\.verify|crypto\.timingSafeEqual|verifyQstashSignature|verifyQstash|verifySignature|verifyVercelSignature|Receiver\b|new\s+Webhook\b))/g,
|
|
22
22
|
languages: ["javascript", "typescript"],
|
|
23
23
|
fix: "Always verify Stripe webhook signatures using stripe.webhooks.constructEvent().",
|
|
24
24
|
fixCode: "// Verify webhook signature\nconst sig = request.headers.get('stripe-signature')!;\nconst event = stripe.webhooks.constructEvent(\n body, sig, process.env.STRIPE_WEBHOOK_SECRET!\n);",
|
|
@@ -221,4 +221,28 @@ export const webSecurityRules = [
|
|
|
221
221
|
fixCode: "// BAD: ejs.render(req.body.template, data)\n// GOOD: fixed template, user value as data only\nconst tpl = ejs.compile(STATIC_TEMPLATE);\nres.send(tpl({ name: req.body.name }));",
|
|
222
222
|
compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.1"],
|
|
223
223
|
},
|
|
224
|
+
{
|
|
225
|
+
id: "VG1083",
|
|
226
|
+
name: "JWT Verification Bypass (decode/none-algorithm)",
|
|
227
|
+
severity: "critical",
|
|
228
|
+
owasp: "A07:2025 Auth Failures",
|
|
229
|
+
description: "A JWT is trusted without a real signature check: jwt.decode() of a request-supplied token returns the payload WITHOUT verifying the signature (any forged token is accepted), or jwt.verify() is called with algorithms including 'none' (algorithm-confusion / signature-stripping). Either lets an attacker mint arbitrary identities/claims.",
|
|
230
|
+
pattern: /(?:jwt\.verify\s*\([^;]{0,200}?algorithms\s*:\s*\[[^\]]*["']none["']|(?:jwt|jsonwebtoken|jose)\s*\.\s*decode\s*\(\s*(?:req\.|request\.|token\b|authToken|bearerToken|accessToken|authorization\b|headers\b))/gi,
|
|
231
|
+
languages: ["javascript", "typescript"],
|
|
232
|
+
fix: "Always verify the signature with an explicit algorithm allowlist: jwt.verify(token, secret, { algorithms: ['HS256'] }). Never use jwt.decode() for authentication/authorization, and never include 'none' in the algorithms list.",
|
|
233
|
+
fixCode: "// BAD: const user = jwt.decode(req.headers.authorization);\n// GOOD:\nconst user = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'] });",
|
|
234
|
+
compliance: ["SOC2:CC6.6", "PCI-DSS:Req6.5.10", "HIPAA:§164.312(d)"],
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
id: "VG1084",
|
|
238
|
+
name: "DOM XSS via jQuery HTML insertion",
|
|
239
|
+
severity: "high",
|
|
240
|
+
owasp: "A03:2025 Injection",
|
|
241
|
+
description: "jQuery DOM-insertion methods (.html(), .append(), .prepend(), .after(), .before(), .replaceWith(), .wrap*) parse their argument as HTML. Passing user-controlled or concatenated/interpolated content (location, query params, .val(), .data()) into them causes DOM-based cross-site scripting.",
|
|
242
|
+
pattern: /\$\([^)]*\)(?:\.\w+\([^)]*\))*?\.(?:html|append|prepend|after|before|replaceWith|wrap|wrapAll|wrapInner)\s*\(\s*(?:[^)]*?(?:location|document\.(?:URL|cookie|referrer)|searchParams|req\.|request\.|params\.|query\.|window\.name|\.val\s*\(\s*\)|\.data\s*\()|`[^`]*\$\{|["'][^"']*["']\s*\+)/gi,
|
|
243
|
+
languages: ["javascript", "typescript"],
|
|
244
|
+
fix: "Use .text() instead of .html() for untrusted content, or sanitize with DOMPurify before insertion. Build elements with $('<div>').text(value) rather than concatenating HTML strings.",
|
|
245
|
+
fixCode: "// BAD: $('#out').html(location.hash)\n// GOOD:\n$('#out').text(userValue); // auto-escaped\n// or: $('#out').html(DOMPurify.sanitize(html));",
|
|
246
|
+
compliance: ["SOC2:CC7.1", "PCI-DSS:Req6.5.7"],
|
|
247
|
+
},
|
|
224
248
|
];
|
|
@@ -451,7 +451,17 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
451
451
|
// agent.get('/?q=' + sqlPayload) which match the regex but aren't database calls
|
|
452
452
|
// - VG042/VG678: HTTP-response/security-header rules (tests don't serve to real users)
|
|
453
453
|
const isTestFile = filePath && /(?:\.(?:[\w-]+-)?(?:spec|test|e2e|stories|cy)\.(?:ts|tsx|js|jsx|mjs|cjs)$|_test\.go$|\/__tests__\/|\/__mocks__\/|\/tests?\/|\/cypress\/|\/playwright\/|\/dockertest\/|\/testutil\/|\/testhelpers?\/|\/testfixtures?\/)/i.test(filePath);
|
|
454
|
-
if (isTestFile && ["VG001", "VG003", "VG062", "VG010", "VG011", "VG012", "VG013", "VG014", "VG042", "VG100", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409", "VG148", "VG424"].includes(rule.id))
|
|
454
|
+
if (isTestFile && ["VG001", "VG003", "VG062", "VG010", "VG011", "VG012", "VG013", "VG014", "VG042", "VG100", "VG130", "VG678", "VG955", "VG133", "VG1021", "VG409", "VG148", "VG424", "VG137"].includes(rule.id))
|
|
455
|
+
continue;
|
|
456
|
+
// VG137 (Debug Endpoint Exposes System Information) also misfires on build/test config
|
|
457
|
+
// files: a `<rootDir>/test/` mapper or a `/test` path string near `process.env` in
|
|
458
|
+
// vite/jest/playwright/vitest/rollup/webpack/react-router config is not an exposed
|
|
459
|
+
// debug HTTP endpoint. Skip those config files.
|
|
460
|
+
if (rule.id === "VG137" && filePath && /(?:\.config\.[cm]?[jt]sx?$|(?:^|\/)(?:vite|vitest|jest|playwright|cypress|rollup|webpack|esbuild|tsup|react-router|svelte|astro|nuxt|babel|tailwind|postcss|drizzle)[.-][\w.-]*\.[cm]?[jt]sx?$)/i.test(filePath))
|
|
461
|
+
continue;
|
|
462
|
+
// VG1005 (Supabase .or() Filter Injection) collides with Zod's `.or()` schema combinator
|
|
463
|
+
// and any other `.or(` method. Only fire when the file actually uses Supabase.
|
|
464
|
+
if (rule.id === "VG1005" && !/\bsupabase\b|createClient\s*\(|from\s+["']@supabase/i.test(code))
|
|
455
465
|
continue;
|
|
456
466
|
// VG955 (Missing Pagination on List Endpoint): only fire on actual request-handling
|
|
457
467
|
// surfaces — API routes, App Router `route.{ts,tsx}`, pages/api, or Server Actions.
|
|
@@ -948,6 +958,11 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
948
958
|
&& /^[A-Za-z][A-Za-z .,!?'’()-]*\s[A-Za-z .,!?'’()-]+$/.test(msgPair[2]))
|
|
949
959
|
continue;
|
|
950
960
|
}
|
|
961
|
+
// VG1083 (JWT verification bypass): jwt.decode() is fine when used only to peek at a
|
|
962
|
+
// token that is ALSO verified (decode-then-verify). Skip the decode branch when a real
|
|
963
|
+
// signature verification exists in the file. (The none-algorithm branch always fires.)
|
|
964
|
+
if (rule.id === "VG1083" && /\.decode\s*\(/.test(match[0]) && /jwt\.verify\s*\(|jwtVerify\s*\(|jose[\s\S]{0,60}?(?:jwtVerify|verify)/i.test(code))
|
|
965
|
+
continue;
|
|
951
966
|
// VG138 (Plaintext Password Comparison): skip benign non-credential comparisons.
|
|
952
967
|
// (1) Confirm-password match: `req.body.password == req.body.cpassword` compares two
|
|
953
968
|
// user inputs from the same form, not a submission against a stored secret.
|
|
@@ -1000,7 +1015,11 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
1000
1015
|
const isParameterized = /\b(?:bind|replacements)\s*:/.test(callCtx) || /[=\s](?:\$\d+|:[a-zA-Z_]\w*)\b/.test(tpl);
|
|
1001
1016
|
const interps = tpl.match(/\$\{[^}]*\}/g) || [];
|
|
1002
1017
|
const allSafe = interps.length > 0 && interps.every(s => /\$\{\s*[\w$.]*(?:hash|sha\d*|md5|bcrypt|argon2?|hmac|digest|encode|escape|encodeURIComponent|toString|String|Number|parseInt|parseFloat)\b/i.test(s));
|
|
1003
|
-
|
|
1018
|
+
// Placeholder generation for a parameterized IN-clause: `id IN (${ids.map(()=>'?').join(',')})`
|
|
1019
|
+
// — the interpolation produces only `?` positional placeholders, values pass via the
|
|
1020
|
+
// params array. Inherently parameterized; not injectable.
|
|
1021
|
+
const allPlaceholderGen = interps.length > 0 && interps.every(s => /\.map\s*\(\s*\(?[\w,\s]*\)?\s*=>\s*["']\?["']/.test(s) || /^\$\{\s*["']\?["']\s*\}$/.test(s));
|
|
1022
|
+
if ((isParameterized && allSafe) || allPlaceholderGen)
|
|
1004
1023
|
continue;
|
|
1005
1024
|
}
|
|
1006
1025
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guardvibe",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.36",
|
|
4
4
|
"mcpName": "io.github.goklab/guardvibe",
|
|
5
|
-
"description": "Security MCP for vibe coding.
|
|
5
|
+
"description": "Security MCP for vibe coding. 438 rules, 36 tools, CLI + doctor. Host security, auth coverage mapping, LLM-powered deep scan (IDOR/business logic), taint analysis. 67 CVE rules refreshed daily from GHSA/OSV/CISA KEV — Miasma @redhat-cloud-services compromise, Next.js May 2026 13-advisory cluster, Drizzle/MikroORM/Kysely SQL injection, Axios proxy-auth redirect leak, Hono setCookie attribute injection, Clerk SSRF, tRPC prototype pollution, @tanstack supply-chain, node-ipc protestware, OpenClaude sandbox bypass, plus the full AI-generated stack (Supabase, Stripe, Prisma, Hono, GraphQL, Convex, Turso, Uploadthing, AI SDK). 68 AI-native rules including OWASP MCP Top 10 tool-description prompt injection (VG1068), model-controlled sandbox-disable flag detection (VG1063), Session messenger exfil endpoint IOC (VG1075), and CI/CD supply-chain hardening (VG1070 npm --expect-provenance / --ignore-scripts enforcement).",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"guardvibe": "build/cli.js",
|