guardvibe 3.1.2 → 3.1.4
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/README.md +4 -3
- package/build/cli/ci.js +38 -6
- package/build/cli/hook.js +55 -9
- package/build/data/compliance-metadata.js +4 -4
- package/build/data/rules/dockerfile.js +4 -1
- package/build/data/rules/nextjs.js +4 -1
- package/build/tools/check-code.js +39 -0
- package/build/tools/explain-remediation.js +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -450,9 +450,10 @@ If your AI agent cannot connect to GuardVibe:
|
|
|
450
450
|
|
|
451
451
|
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.
|
|
452
452
|
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).
|
|
453
|
-
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.
|
|
454
|
-
4. **
|
|
455
|
-
5. **
|
|
453
|
+
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.3`) at init time for fast deterministic startup. As of v3.1.2 the re-run also rewrites stale pins automatically (`Upgraded GuardVibe pin (3.0.55 → 3.1.3)`). 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.
|
|
454
|
+
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.
|
|
455
|
+
5. **Verify Node.js version.** GuardVibe requires Node.js >= 18.0.0. Check with `node --version`.
|
|
456
|
+
6. **Check npx cache.** If you upgraded GuardVibe and the old version is cached, run `npx -y guardvibe@latest` to force the latest version.
|
|
456
457
|
|
|
457
458
|
### Node.js version requirements
|
|
458
459
|
|
package/build/cli/ci.js
CHANGED
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
* CLI: guardvibe ci <provider>
|
|
3
3
|
* Generates CI/CD workflow configurations.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { createRequire } from "module";
|
|
6
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
6
7
|
import { join } from "path";
|
|
7
|
-
const
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const pkg = require("../../package.json");
|
|
10
|
+
function buildGithubActionsWorkflow(version) {
|
|
11
|
+
return `name: GuardVibe Security Scan
|
|
12
|
+
# Pinned to guardvibe@${version} for reproducible CI builds. Re-run \`npx guardvibe ci github\` to upgrade.
|
|
8
13
|
|
|
9
14
|
on:
|
|
10
15
|
pull_request:
|
|
@@ -30,7 +35,7 @@ jobs:
|
|
|
30
35
|
node-version: "22"
|
|
31
36
|
|
|
32
37
|
- name: Run GuardVibe security scan
|
|
33
|
-
run: npx -y guardvibe
|
|
38
|
+
run: npx -y guardvibe@${version} scan --format sarif --output guardvibe-results.sarif
|
|
34
39
|
|
|
35
40
|
- name: Upload SARIF to GitHub Security
|
|
36
41
|
if: always()
|
|
@@ -39,18 +44,45 @@ jobs:
|
|
|
39
44
|
sarif_file: guardvibe-results.sarif
|
|
40
45
|
category: guardvibe
|
|
41
46
|
`;
|
|
47
|
+
}
|
|
48
|
+
/** Extract a pinned guardvibe version from a generated workflow YAML, or "latest"/null for legacy/unrecognized forms. */
|
|
49
|
+
function extractPinnedVersionFromWorkflow(content) {
|
|
50
|
+
const pinned = content.match(/guardvibe@(\d+\.\d+\.\d+(?:-[\w.]+)?)/);
|
|
51
|
+
if (pinned)
|
|
52
|
+
return pinned[1];
|
|
53
|
+
if (/guardvibe-scan|guardvibe@latest/.test(content))
|
|
54
|
+
return "latest";
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
42
57
|
function generateGitHubActions() {
|
|
43
58
|
const workflowDir = join(process.cwd(), ".github", "workflows");
|
|
44
59
|
if (!existsSync(workflowDir)) {
|
|
45
60
|
mkdirSync(workflowDir, { recursive: true });
|
|
46
61
|
}
|
|
47
62
|
const workflowPath = join(workflowDir, "guardvibe.yml");
|
|
63
|
+
const fresh = buildGithubActionsWorkflow(pkg.version);
|
|
48
64
|
if (existsSync(workflowPath)) {
|
|
49
|
-
|
|
65
|
+
const existing = readFileSync(workflowPath, "utf-8");
|
|
66
|
+
const existingPin = extractPinnedVersionFromWorkflow(existing);
|
|
67
|
+
if (existingPin === pkg.version) {
|
|
68
|
+
console.log(` [OK] .github/workflows/guardvibe.yml already up-to-date (pinned to v${pkg.version}).`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (existingPin && existingPin !== "latest") {
|
|
72
|
+
writeFileSync(workflowPath, fresh, "utf-8");
|
|
73
|
+
console.log(` [OK] Upgraded .github/workflows/guardvibe.yml (${existingPin} → ${pkg.version}).`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (existingPin === "latest") {
|
|
77
|
+
writeFileSync(workflowPath, fresh, "utf-8");
|
|
78
|
+
console.log(` [OK] Pinned .github/workflows/guardvibe.yml (was unpinned → ${pkg.version}).`);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
console.log(" [OK] .github/workflows/guardvibe.yml exists with custom contents — leaving as-is.");
|
|
50
82
|
return;
|
|
51
83
|
}
|
|
52
|
-
writeFileSync(workflowPath,
|
|
53
|
-
console.log(
|
|
84
|
+
writeFileSync(workflowPath, fresh, "utf-8");
|
|
85
|
+
console.log(` [OK] Created .github/workflows/guardvibe.yml (pinned to v${pkg.version}).`);
|
|
54
86
|
console.log(" [OK] SARIF results will appear in GitHub Security tab.");
|
|
55
87
|
}
|
|
56
88
|
export function runCi(args) {
|
package/build/cli/hook.js
CHANGED
|
@@ -2,16 +2,23 @@
|
|
|
2
2
|
* CLI: guardvibe hook install|uninstall
|
|
3
3
|
* Manages pre-commit security hooks.
|
|
4
4
|
*/
|
|
5
|
+
import { createRequire } from "module";
|
|
5
6
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, unlinkSync } from "fs";
|
|
6
7
|
import { join } from "path";
|
|
7
|
-
const
|
|
8
|
-
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
const pkg = require("../../package.json");
|
|
10
|
+
const GUARDVIBE_BLOCK_START = "# GuardVibe pre-commit security hook";
|
|
11
|
+
const GUARDVIBE_BLOCK_END = "✅ GuardVibe: all checks passed.";
|
|
12
|
+
function buildHookScript(version) {
|
|
13
|
+
return `#!/bin/sh
|
|
14
|
+
${GUARDVIBE_BLOCK_START}
|
|
9
15
|
# Installed by: npx guardvibe hook install
|
|
16
|
+
# Pinned to v${version} for reproducible CI/local behavior. Re-run install to upgrade.
|
|
10
17
|
|
|
11
18
|
echo "🔒 GuardVibe: scanning staged files..."
|
|
12
19
|
|
|
13
20
|
# Run guardvibe scan on staged files
|
|
14
|
-
RESULT=$(npx -y guardvibe
|
|
21
|
+
RESULT=$(npx -y guardvibe@${version} scan --staged 2>&1)
|
|
15
22
|
EXIT_CODE=$?
|
|
16
23
|
|
|
17
24
|
if [ $EXIT_CODE -ne 0 ]; then
|
|
@@ -22,8 +29,33 @@ if [ $EXIT_CODE -ne 0 ]; then
|
|
|
22
29
|
exit 1
|
|
23
30
|
fi
|
|
24
31
|
|
|
25
|
-
echo "
|
|
32
|
+
echo "${GUARDVIBE_BLOCK_END}"
|
|
26
33
|
`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extract the pinned GuardVibe version from an existing pre-commit hook.
|
|
37
|
+
* Returns the version string, "latest" for legacy unpinned hooks, or null if no GuardVibe block found.
|
|
38
|
+
*/
|
|
39
|
+
function extractPinnedVersionFromHook(content) {
|
|
40
|
+
const pinnedMatch = content.match(/guardvibe@(\d+\.\d+\.\d+(?:-[\w.]+)?)/);
|
|
41
|
+
if (pinnedMatch)
|
|
42
|
+
return pinnedMatch[1];
|
|
43
|
+
if (/guardvibe@latest|npx\s+-y\s+guardvibe(?:\s|\b)/.test(content) && content.includes("GuardVibe")) {
|
|
44
|
+
return "latest";
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function replaceGuardVibeBlock(existing, fresh) {
|
|
49
|
+
// Strip any prior GuardVibe block (with or without leading shebang) and append the fresh one.
|
|
50
|
+
const cleaned = existing
|
|
51
|
+
.replace(/\n?# GuardVibe pre-commit security hook[\s\S]*?GuardVibe: all checks passed[."]*\n?/g, "")
|
|
52
|
+
.trimEnd();
|
|
53
|
+
if (!cleaned || cleaned === "#!/bin/sh")
|
|
54
|
+
return fresh;
|
|
55
|
+
// Splice the GuardVibe section in (without its shebang) onto the existing hook.
|
|
56
|
+
const freshNoShebang = fresh.replace(/^#!\/bin\/sh\n/, "");
|
|
57
|
+
return cleaned + "\n\n" + freshNoShebang;
|
|
58
|
+
}
|
|
27
59
|
function installHook() {
|
|
28
60
|
const gitDir = join(process.cwd(), ".git");
|
|
29
61
|
if (!existsSync(gitDir)) {
|
|
@@ -35,19 +67,33 @@ function installHook() {
|
|
|
35
67
|
mkdirSync(hooksDir, { recursive: true });
|
|
36
68
|
}
|
|
37
69
|
const hookPath = join(hooksDir, "pre-commit");
|
|
70
|
+
const freshScript = buildHookScript(pkg.version);
|
|
38
71
|
if (existsSync(hookPath)) {
|
|
39
72
|
const existing = readFileSync(hookPath, "utf-8");
|
|
40
|
-
|
|
41
|
-
|
|
73
|
+
const existingPin = extractPinnedVersionFromHook(existing);
|
|
74
|
+
if (existingPin === pkg.version) {
|
|
75
|
+
console.log(` [OK] GuardVibe pre-commit hook already up-to-date (v${pkg.version}).`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (existingPin && existingPin !== "latest") {
|
|
79
|
+
writeFileSync(hookPath, replaceGuardVibeBlock(existing, freshScript), "utf-8");
|
|
80
|
+
chmodSync(hookPath, 0o755);
|
|
81
|
+
console.log(` [OK] Upgraded GuardVibe pre-commit hook (${existingPin} → ${pkg.version}).`);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (existingPin === "latest") {
|
|
85
|
+
writeFileSync(hookPath, replaceGuardVibeBlock(existing, freshScript), "utf-8");
|
|
86
|
+
chmodSync(hookPath, 0o755);
|
|
87
|
+
console.log(` [OK] Pinned GuardVibe pre-commit hook (was unpinned → ${pkg.version}).`);
|
|
42
88
|
return;
|
|
43
89
|
}
|
|
44
|
-
writeFileSync(hookPath, existing + "\n" +
|
|
90
|
+
writeFileSync(hookPath, existing.trimEnd() + "\n\n" + freshScript, "utf-8");
|
|
45
91
|
console.log(" [OK] GuardVibe added to existing pre-commit hook.");
|
|
46
92
|
}
|
|
47
93
|
else {
|
|
48
|
-
writeFileSync(hookPath,
|
|
94
|
+
writeFileSync(hookPath, freshScript, "utf-8");
|
|
49
95
|
chmodSync(hookPath, 0o755);
|
|
50
|
-
console.log(
|
|
96
|
+
console.log(` [OK] Pre-commit hook installed at .git/hooks/pre-commit (pinned to v${pkg.version}).`);
|
|
51
97
|
}
|
|
52
98
|
}
|
|
53
99
|
function uninstallHook() {
|
|
@@ -15,8 +15,8 @@ export const complianceMetadata = {
|
|
|
15
15
|
VG002: {
|
|
16
16
|
gdpr: ["GDPR:Art32(1)(b)"],
|
|
17
17
|
iso27001: ["ISO27001:A.8.3", "ISO27001:A.8.24"],
|
|
18
|
-
exploit: "Attacker
|
|
19
|
-
audit: "
|
|
18
|
+
exploit: "Attacker accesses API endpoints or resources without authentication, reading or modifying data belonging to other users.",
|
|
19
|
+
audit: "Show middleware/auth layer that protects all sensitive endpoints. Demonstrate that unauthenticated requests return 401/403.",
|
|
20
20
|
},
|
|
21
21
|
VG003: {
|
|
22
22
|
gdpr: ["GDPR:Art32(1)(a)"],
|
|
@@ -27,8 +27,8 @@ export const complianceMetadata = {
|
|
|
27
27
|
VG010: {
|
|
28
28
|
gdpr: ["GDPR:Art32(1)(b)", "GDPR:Art25"],
|
|
29
29
|
iso27001: ["ISO27001:A.8.3", "ISO27001:A.5.15"],
|
|
30
|
-
exploit: "Attacker
|
|
31
|
-
audit: "
|
|
30
|
+
exploit: "Attacker sends crafted SQL input (e.g., ' OR 1=1--, UNION SELECT, ;DROP TABLE) through unvalidated form fields, URL parameters, or JSON body to extract, modify, or delete database records.",
|
|
31
|
+
audit: "Demonstrate that all database queries use parameterized statements or ORM methods. Show code review checklist that includes SQL injection testing.",
|
|
32
32
|
},
|
|
33
33
|
VG042: {
|
|
34
34
|
gdpr: ["GDPR:Art32(1)(a)"],
|
|
@@ -53,7 +53,10 @@ export const dockerfileRules = [
|
|
|
53
53
|
severity: "medium",
|
|
54
54
|
owasp: "A05:2025 Security Misconfiguration",
|
|
55
55
|
description: "ADD has extra features (URL fetch, tar extraction) that can introduce unexpected behavior. Use COPY for local files.",
|
|
56
|
-
|
|
56
|
+
// Anchor to start of line + case-sensitive: Docker `ADD` instruction is uppercase and
|
|
57
|
+
// begins a line. Matching `add` case-insensitive caught `RUN pnpm add`, `apk add`,
|
|
58
|
+
// `yarn add`, etc. — package-manager subcommands inside RUN, not Docker instructions.
|
|
59
|
+
pattern: /^ADD\s+(?!https?:\/\/)\S+\s+\S+/gm,
|
|
57
60
|
languages: ["dockerfile"],
|
|
58
61
|
fix: "Use COPY instead of ADD for local files. Only use ADD for URLs or tar extraction.",
|
|
59
62
|
fixCode: "# Use COPY for local files\nCOPY ./src /app/src\n# Only use ADD for remote files or tar extraction\n# ADD https://example.com/file.tar.gz /app/",
|
|
@@ -90,7 +90,10 @@ export const nextjsRules = [
|
|
|
90
90
|
severity: "high",
|
|
91
91
|
owasp: "A01:2025 Broken Access Control",
|
|
92
92
|
description: "Sensitive data (tokens, secrets, internal IDs) appears to be passed from server to client component as props.",
|
|
93
|
-
|
|
93
|
+
// Require no-space `=` to match JSX prop syntax (`token={value}`) and exclude JS object
|
|
94
|
+
// literal assignments (`apiKey = { ... }`, `token = { ... }`) common in test helpers,
|
|
95
|
+
// default-param destructuring, and config objects.
|
|
96
|
+
pattern: /(?:(?:^|[^a-zA-Z])(?:secret|token|password|apiKey|api_key|privateKey|private_key|internalId|ssn|creditCard|credit_card))=\{[\s\S]*?\}/g,
|
|
94
97
|
languages: ["javascript", "typescript"],
|
|
95
98
|
fix: "Never pass sensitive data as props to client components. Keep secrets server-side.",
|
|
96
99
|
fixCode: "// Keep sensitive data server-side\nexport default async function Page() {\n const secret = process.env.API_SECRET;\n const publicData = await fetchData(secret);\n return <ClientComponent data={publicData} />;\n}",
|
|
@@ -471,6 +471,33 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
471
471
|
// Skip destructive DDL rules (VG540-VG542) and view rules (VG439) in migration directories
|
|
472
472
|
if ((rule.id.startsWith("VG54") || rule.id === "VG439") && isMigrationFile)
|
|
473
473
|
continue;
|
|
474
|
+
// VG146 (Unquoted .env Value): only fire on `.env` / `.env.local` / `.env.production` etc.
|
|
475
|
+
// Bash scripts use `${VAR:-default}` and similar expansions that legitimately contain
|
|
476
|
+
// `{`, `}`, `:` characters; matching them as "unquoted env values" is a FP class.
|
|
477
|
+
if (rule.id === "VG146" && filePath && !/(?:^|\/)\.env(?:\.[\w.-]+)?$/.test(filePath))
|
|
478
|
+
continue;
|
|
479
|
+
// VG200 (Container running as root): skip when a USER directive exists anywhere in the file.
|
|
480
|
+
// The rule's regex with `(?:(?!^USER)[\s\S])*` is unreliable across multi-stage builds; a
|
|
481
|
+
// file-level check is more robust.
|
|
482
|
+
if (rule.id === "VG200" && /^USER\s+\S+/m.test(code))
|
|
483
|
+
continue;
|
|
484
|
+
// VG206 (Missing HEALTHCHECK): skip when a HEALTHCHECK directive exists anywhere. The
|
|
485
|
+
// rule's regex requires HEALTHCHECK *after* CMD/ENTRYPOINT, but Dockerfiles commonly place
|
|
486
|
+
// HEALTHCHECK before CMD (nginx production stage), producing FPs.
|
|
487
|
+
if (rule.id === "VG206" && /^HEALTHCHECK\s+/m.test(code))
|
|
488
|
+
continue;
|
|
489
|
+
// VG407 (Server Data Leaked to Client Component): skip files that ARE client components.
|
|
490
|
+
// Signals: the `"use client"` directive (Next.js App Router) OR usage of React state/effect
|
|
491
|
+
// hooks (universal client-render signal — Remix, Vite-React, Pages Router, etc.). The rule
|
|
492
|
+
// targets server→client prop-boundary leaks; intra-client passing of local form state to
|
|
493
|
+
// a child component (e.g. PasswordStrengthIndicator) is not the same boundary.
|
|
494
|
+
if (rule.id === "VG407") {
|
|
495
|
+
const head = code.slice(0, 1000);
|
|
496
|
+
const hasUseClient = /^\s*['"]use client['"];?\s*$/m.test(head);
|
|
497
|
+
const hasReactStateHooks = /\b(?:useState|useReducer|useEffect|useLayoutEffect|useRef|useMemo|useCallback|useContext|useTransition|useSyncExternalStore)\s*\(/.test(code);
|
|
498
|
+
if (hasUseClient || hasReactStateHooks)
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
474
501
|
// Skip SQL injection rules in schema/migration .sql files (DDL, not user input)
|
|
475
502
|
if (rule.id === "VG543" && (isMigrationFile || isSqlSchemaFile))
|
|
476
503
|
continue;
|
|
@@ -651,12 +678,24 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
|
|
|
651
678
|
effectiveRule = { ...effectiveRule, severity: "low" };
|
|
652
679
|
}
|
|
653
680
|
}
|
|
681
|
+
// VG202 (latest/untagged image): pre-compute `AS <alias>` names from the file so
|
|
682
|
+
// matches against intermediate-stage references (`FROM base AS builder`) can be
|
|
683
|
+
// filtered out at match time. The set is null for other rules to avoid wasted work.
|
|
684
|
+
const dockerStageAliases = rule.id === "VG202"
|
|
685
|
+
? new Set(Array.from(code.matchAll(/^FROM\s+\S+\s+AS\s+(\w[\w.-]*)/gim)).map(m => m[1].toLowerCase()))
|
|
686
|
+
: null;
|
|
654
687
|
let match;
|
|
655
688
|
while ((match = rule.pattern.exec(code)) !== null) {
|
|
656
689
|
const beforeMatch = code.substring(0, match.index);
|
|
657
690
|
const lineNumber = beforeMatch.split("\n").length;
|
|
658
691
|
if (isLineSuppressed(suppressions, lineNumber, rule.id))
|
|
659
692
|
continue;
|
|
693
|
+
// VG202: skip when the FROM target matches a previous AS-alias in the same file.
|
|
694
|
+
if (dockerStageAliases) {
|
|
695
|
+
const target = match[0].replace(/^FROM\s+/i, "").split(/[:@\s]/)[0].toLowerCase();
|
|
696
|
+
if (dockerStageAliases.has(target))
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
660
699
|
// Skip matches on comment lines and inside string literals.
|
|
661
700
|
// CVE version-pin rules (VG900-VG931) are exempt — they scan package.json
|
|
662
701
|
// dependency declarations where these contexts don't apply.
|
|
@@ -66,8 +66,10 @@ function getBreakingRisk(rule) {
|
|
|
66
66
|
const id = rule.id;
|
|
67
67
|
if (["VG001", "VG062", "VG060"].includes(id))
|
|
68
68
|
return "LOW — Moving to env vars requires .env setup but no code logic changes.";
|
|
69
|
-
if (["VG402", "
|
|
69
|
+
if (["VG402", "VG952"].includes(id))
|
|
70
70
|
return "MEDIUM — Adding auth checks may break unauthenticated flows that were working. Test all affected endpoints.";
|
|
71
|
+
if (["VG010", "VG011", "VG013", "VG014"].includes(id))
|
|
72
|
+
return "LOW — Parameterized queries are drop-in replacements for most drivers/ORMs. Manual string-concat call sites may need light refactoring; run query coverage after the swap.";
|
|
71
73
|
if (["VG401", "VG960"].includes(id))
|
|
72
74
|
return "MEDIUM — Adding schema validation will reject previously accepted invalid input. Test with real user data.";
|
|
73
75
|
if (["VG403", "VG500", "VG510"].includes(id))
|
|
@@ -88,8 +90,10 @@ function getTestStrategy(rule) {
|
|
|
88
90
|
const id = rule.id;
|
|
89
91
|
if (["VG001", "VG062", "VG060"].includes(id))
|
|
90
92
|
return "1. Move value to .env\n2. Verify app still reads from env\n3. Confirm old hardcoded value removed from git history";
|
|
91
|
-
if (["VG402"
|
|
93
|
+
if (["VG402"].includes(id))
|
|
92
94
|
return "1. Call endpoint without auth token → expect 401\n2. Call with valid token → expect success\n3. Call with expired token → expect 401";
|
|
95
|
+
if (["VG010", "VG011", "VG013", "VG014"].includes(id))
|
|
96
|
+
return "1. Replace concatenated SQL with parameterized query / prepared statement\n2. Submit a malicious payload (`' OR 1=1--`, `;DROP TABLE--`, `UNION SELECT`) → expect literal match, no row exposure, no execution\n3. Re-run normal-input regression tests to confirm queries still return correct results";
|
|
93
97
|
if (["VG401", "VG960"].includes(id))
|
|
94
98
|
return "1. Submit valid data → expect success\n2. Submit empty/malformed data → expect 400 with validation error\n3. Submit oversized data → expect rejection";
|
|
95
99
|
if (["VG403", "VG500"].includes(id))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "guardvibe",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.4",
|
|
4
4
|
"mcpName": "io.github.goklab/guardvibe",
|
|
5
5
|
"description": "Security MCP for vibe coding. 390 rules, 36 tools, CLI + doctor. Host security, auth coverage mapping, LLM-powered deep scan (IDOR/business logic), taint analysis, +25 AI-native rules (MCP supply-chain, RAG/vector poisoning, agent loop DoS, public-prefix LLM keys, sandbox bypass). Plus Next.js, Supabase, Clerk, Stripe, Prisma, tRPC, Hono, GraphQL, Convex, Turso, Uploadthing, AI SDK, and the full AI-generated stack.",
|
|
6
6
|
"type": "module",
|