guardvibe 3.1.9 → 3.1.10

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.
@@ -775,6 +775,11 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
775
775
  // (`forgot_password: "forgot_password_clicked"`).
776
776
  if (filePath && /(?:\/i18n\/|\/locales?\/|\/translations?\/|\/event[-_]tracker\/|\/analytics\/events\/|\/messages\/[a-z]{2}(?:[-_][A-Z]{2})?\.[jt]sx?$)/i.test(filePath))
777
777
  continue;
778
+ // Seed scripts and shared test-fixture builders deliberately use placeholder
779
+ // credentials (`password: "delete-me"`, `password: "MOCK_PASSWORD"`). These
780
+ // populate dev/CI databases and never run against production.
781
+ if (filePath && /(?:^|\/)(?:scripts|seeds?|fixtures?|__fixtures__|bookingScenario|setupAndTeardown)\b|(?:^|\/)seed[-_.][\w-]*\.[jt]sx?$/i.test(filePath))
782
+ continue;
778
783
  }
779
784
  // Skip credential rules when the variable name signals test/example/mock intent.
780
785
  // e.g. `testingPassword`, `examplePassword`, `mockApiKey`, `placeholderSecret`.
@@ -787,6 +792,16 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
787
792
  // Covers both SCREAMING_SNAKE (TS string enums) and snake_case (event-tracker key maps).
788
793
  if (/\b([A-Za-z_][A-Za-z0-9_]*)\s*[:=]\s*["']\1["']/.test(matchedLine))
789
794
  continue;
795
+ // Skip when the value is just a re-casing of the identifier — covers
796
+ // `IncorrectEmailPassword = "incorrect-email-password"` (TS string enum kebab) and
797
+ // `X_CAL_SECRET_KEY = "x-cal-secret-key"` (HTTP header constant). Both forms reduce
798
+ // to the same lowercase letters; no real credential is its own name re-cased.
799
+ const idValuePair = matchedLine.match(/\b([A-Za-z_][A-Za-z0-9_]*)\s*[:=]\s*["']([\w-]+)["']/);
800
+ if (idValuePair) {
801
+ const canonical = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, "");
802
+ if (canonical(idValuePair[1]) === canonical(idValuePair[2]))
803
+ continue;
804
+ }
790
805
  // Skip SCREAMING_SNAKE error/status codes whose value is digits-only.
791
806
  // e.g. `INVALID_PASSWORD = "5020"` — error code, not a credential.
792
807
  if (/\b[A-Z][A-Z0-9_]*\s*=\s*["']\d+["']/.test(matchedLine))
@@ -922,10 +937,20 @@ export function analyzeCode(code, language, framework, filePath, configDir, rule
922
937
  const varName = match[0].split(/\s*(?:===|!==|==|!=)/)[0].trim();
923
938
  if (/(?:Count|Length|Balance|Map|List|Array|Index|Size|Total|Num|Id|Type|Name|Status|Data|Info|Error|Result|Response|Config|Option|Url|Path|Provider|Model|Limit|Quota|Rate|Max|Min)/i.test(varName))
924
939
  continue;
925
- // Look at what comes after the operator. If it's a string literal, null, undefined,
926
- // or a number — this is an emptiness/type check, not a secret comparison.
940
+ // Look at what comes after the operator. If it's a string/template literal, null,
941
+ // undefined, true/false, or a number — this is an emptiness/type check, not a
942
+ // secret comparison. The earlier shape only matched empty literals (`''`/`""`),
943
+ // missing the common `typeof x === "object"` / `=== "string"` shapes.
927
944
  const afterOp = code.substring(match.index + match[0].length).trimStart();
928
- if (/^(?:''|""|``|null\b|undefined\b|\d|0x|true\b|false\b)/.test(afterOp))
945
+ if (/^(?:'[^']*'|"[^"]*"|`[^`]*`|null\b|undefined\b|\d|0x|true\b|false\b)/.test(afterOp))
946
+ continue;
947
+ // Client-side React code is not exposed to remote timing attacks: the comparison
948
+ // runs in the user's own browser, where the attacker already has full control of
949
+ // execution timing (network jitter doesn't help them, and a same-machine attacker
950
+ // has easier paths than timing). Skip when the file is a client component.
951
+ const isClientFile = /^['"]use client['"]/.test(code.trimStart()) ||
952
+ /\b(?:useState|useEffect|useReducer|useRef|useMemo|useCallback|useContext|useTransition|useSyncExternalStore|useLayoutEffect)\s*\(/.test(code);
953
+ if (isClientFile)
929
954
  continue;
930
955
  }
931
956
  // Skip VG1005 (.or() filter injection) when all interpolated variables are
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "guardvibe",
3
- "version": "3.1.9",
3
+ "version": "3.1.10",
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",