shipwarden 0.2.0 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipwarden",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Free security scanner and launch checklist for apps built with Lovable, Bolt, Cursor and v0: Supabase RLS, Firebase rules, leaked keys, unprotected endpoints, missing privacy policy and 404 pages.",
5
5
  "homepage": "https://github.com/patrickballard/shipwarden#readme",
6
6
  "repository": {
@@ -87,6 +87,8 @@ export const publicEnvSecret = {
87
87
  for (const m of file.content.matchAll(PUBLIC_VAR)) {
88
88
  const [name, prefix, rest] = m;
89
89
  if (seen.has(name) || /PUBLISHABLE|ANON/.test(rest)) continue;
90
+ const lineStart = file.content.lastIndexOf('\n', m.index) + 1;
91
+ if (/^\s*(#|\/\/|\*)/.test(file.content.slice(lineStart, m.index))) continue; // commented out
90
92
  const critical = CRITICAL_NAME.test(rest);
91
93
  if (!critical && !AI_KEY_NAME.test(rest)) continue;
92
94
  // The ai-key-in-browser finding already covers an AI key used from browser code.
package/src/rules/web.js CHANGED
@@ -1,4 +1,4 @@
1
- import { EDGE_FUNCTION, ROUTE_HANDLER, hasDirective, isServerEndpoint, lineAt, makeFinding } from '../util.js';
1
+ import { EDGE_FUNCTION, ROUTE_HANDLER, hasDirective, isServerEndpoint, lineAt, looksLikePlaceholder, makeFinding } from '../util.js';
2
2
 
3
3
  const MUTATING_EXPORT = /export\s+(?:async\s+)?function\s+(POST|PUT|PATCH|DELETE)\b|export\s+const\s+(POST|PUT|PATCH|DELETE)\s*=|\bas\s+(POST|PUT|PATCH|DELETE)\b/;
4
4
 
@@ -177,8 +177,11 @@ export const committedEnvFile = {
177
177
  severity: 'high',
178
178
  check(file, ctx) {
179
179
  if (!ctx.tracked || !ENV_FILE.test(file.path) || ENV_TEMPLATE.test(file.path)) return [];
180
- const names = [...file.content.matchAll(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*['"]?[^\s'"#]/gm)].map((m) => m[1]);
181
- const secrets = names.filter((n) => !PUBLIC_PREFIX.test(n));
180
+ const vars = [...file.content.matchAll(/^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*['"]?([^\s'"#][^\n'"]*)/gm)];
181
+ // Public values and local-only dev settings (localhost databases, "password") aren't leaks.
182
+ const secrets = vars
183
+ .filter(([, name, value]) => !PUBLIC_PREFIX.test(name) && !/localhost|127\.0\.0\.1|0\.0\.0\.0|host\.docker\.internal/.test(value) && !looksLikePlaceholder(value) && !/^(password|secret|changeme|true|false|\d+|development|production|test)$/i.test(value))
184
+ .map(([, name]) => name);
182
185
  if (!secrets.length) return [];
183
186
  const shown = secrets.slice(0, 5).map((n) => `\`${n}\``).join(', ');
184
187
  return [