@ship-safe/cli 1.1.5 → 1.1.7

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.
Files changed (2) hide show
  1. package/dist/index.js +37 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4211,7 +4211,7 @@ var NEXTJS_RULES = [
4211
4211
  ],
4212
4212
  excludePatterns: [
4213
4213
  { regex: "(?:auth|session|token|clerk|getAuth|currentUser|requireAuth|protect|webhook|public|health)", type: "context_line" },
4214
- { regex: "(?:webhook|callback|health|cron|stripe|checkout|unsubscribe|contact|export|badge)", type: "file_path" }
4214
+ { regex: "(?:webhook|callback|health|cron|stripe|checkout|unsubscribe|contact|export|badge|cli\\/)", type: "file_path" }
4215
4215
  ],
4216
4216
  fix: { description: "Add authentication to your API route. Use middleware or check the session at the start of each handler.", suggestion: "const { userId } = auth();\nif (!userId) return new Response('Unauthorized', { status: 401 });" }
4217
4217
  },
@@ -4715,7 +4715,10 @@ var SECRET_RULES = [
4715
4715
  owasp: "A07:2021",
4716
4716
  languages: ["*"],
4717
4717
  patterns: [{ regex: `(?:password|passwd|pwd)\\s*[:=]\\s*["']([^\\s"']{8,})["']`, type: "match" }],
4718
- excludePatterns: [{ regex: "(?:example|test|fake|dummy|placeholder|password123|changeme|process\\.env|import\\.meta\\.env|type|interface|schema|validation|zod|yup)", type: "context_line" }],
4718
+ excludePatterns: [
4719
+ { regex: "(?:example|test|fake|dummy|placeholder|password123|changeme|process\\.env|import\\.meta\\.env|type|interface|schema|validation|zod|yup)", type: "context_line" },
4720
+ { regex: "(?:admin123|admin|default|sample|secret123|qwerty|letmein|welcome|monkey|dragon|master|1234|abcd)", type: "context_line" }
4721
+ ],
4719
4722
  fix: { description: "Remove hardcoded passwords. Use environment variables or a secrets manager." }
4720
4723
  },
4721
4724
  {
@@ -4896,7 +4899,7 @@ var XSS_RULES = [
4896
4899
  patterns: [{ regex: "dangerouslySetInnerHTML", type: "match" }],
4897
4900
  excludePatterns: [
4898
4901
  { regex: "(?:json-ld|jsonLd|structured-data|schema\\.org|ld\\+json|localStorage|theme|nonce|suppressHydration|DOMPurify|sanitize)", type: "context_line" },
4899
- { regex: "(?:json-ld|jsonld|structured-data)", type: "file_path" }
4902
+ { regex: "(?:json-ld|jsonld|structured-data|layout\\.tsx|layout\\.jsx|_document\\.tsx|_document\\.jsx)", type: "file_path" }
4900
4903
  ],
4901
4904
  fix: { description: "Avoid dangerouslySetInnerHTML. If you must use it, sanitize the HTML with DOMPurify or a similar library first." }
4902
4905
  },
@@ -4910,6 +4913,10 @@ var XSS_RULES = [
4910
4913
  owasp: "A03:2021",
4911
4914
  languages: ["javascript", "typescript"],
4912
4915
  patterns: [{ regex: "document\\.write\\s*\\(", type: "match" }],
4916
+ excludePatterns: [
4917
+ { regex: "(?:pdf|print|export|report|window\\.open)", type: "context_line" },
4918
+ { regex: "(?:pdf|print|export)", type: "file_path" }
4919
+ ],
4913
4920
  fix: { description: "Replace document.write() with DOM manipulation methods like createElement() and appendChild()." }
4914
4921
  },
4915
4922
  {
@@ -5039,6 +5046,10 @@ var PII_RULES = [
5039
5046
  owasp: "A09:2021",
5040
5047
  languages: ["javascript", "typescript"],
5041
5048
  patterns: [{ regex: "console\\.log\\s*\\(.*(?:email|user\\.email|userEmail)", type: "match" }],
5049
+ excludePatterns: [
5050
+ { regex: "(?:chalk|ora|spinner|ink|inquirer|readline|prompt|display|print|show)", type: "context_line" },
5051
+ { regex: "(?:cli\\/|commands\\/|bin\\/)", type: "file_path" }
5052
+ ],
5042
5053
  fix: { description: "Avoid logging email addresses or other PII. If needed for debugging, mask the email." }
5043
5054
  },
5044
5055
  {
@@ -5051,6 +5062,10 @@ var PII_RULES = [
5051
5062
  owasp: "A09:2021",
5052
5063
  languages: ["javascript", "typescript"],
5053
5064
  patterns: [{ regex: "console\\.(?:log|info|debug|warn)\\s*\\(.*(?:password|token|secret|creditCard|ssn|socialSecurity)", type: "match" }],
5065
+ excludePatterns: [
5066
+ { regex: "(?:not set|missing|undefined|required|invalid|expired|failed|error|skipping)", type: "context_line" },
5067
+ { regex: "(?:cli\\/|commands\\/|bin\\/)", type: "file_path" }
5068
+ ],
5054
5069
  fix: { description: "Remove console logging of sensitive data before deploying to production." }
5055
5070
  },
5056
5071
  {
@@ -5096,7 +5111,10 @@ var AUTHZ_RULES = [
5096
5111
  owasp: "A01:2021",
5097
5112
  languages: ["javascript", "typescript"],
5098
5113
  patterns: [{ regex: `(?:isAdmin|role\\s*===?\\s*["']admin|user\\.role|hasPermission|isAuthorized)\\s*(?:\\)|&&|;|\\?)`, type: "match" }],
5099
- excludePatterns: [{ regex: "(?:middleware|server|api|route\\.ts|route\\.js|\\bGET\\b|\\bPOST\\b|\\bPUT\\b|\\bDELETE\\b)", type: "file_path" }],
5114
+ excludePatterns: [
5115
+ { regex: "(?:middleware|server|api|route\\.ts|route\\.js|\\bGET\\b|\\bPOST\\b|\\bPUT\\b|\\bDELETE\\b)", type: "file_path" },
5116
+ { regex: "(?:useQuery|useConvex|useMutation|convex|trpc|graphql|useSWR|useSession|getServerSession|className|disabled|hidden|opacity|hidden|display|render|show|visible)", type: "context_line" }
5117
+ ],
5100
5118
  fix: { description: "Always enforce admin/role checks on the server side (API routes or middleware), not just in the frontend. Client-side checks are for UI only." }
5101
5119
  },
5102
5120
  {
@@ -5566,7 +5584,10 @@ var HEADERS_RULES = [
5566
5584
  { regex: "(?:login|signin|sign-in|signup|sign-up|register|reset-password|forgot-password|verify-email).*(?:POST|post|handler|action)", type: "match" },
5567
5585
  { regex: "(?:POST|post|handler|action).*(?:login|signin|sign-in|signup|sign-up|register|reset-password|forgot-password)", type: "match" }
5568
5586
  ],
5569
- excludePatterns: [{ regex: "(?:rateLimit|rateLimiter|throttle|limiter|upstash|slowDown|brute|attempts)", type: "context_line" }],
5587
+ excludePatterns: [
5588
+ { regex: "(?:rateLimit|rateLimiter|throttle|limiter|upstash|slowDown|brute|attempts)", type: "context_line" },
5589
+ { regex: "(?:cli\\/|commands\\/|bin\\/|desktop\\/|electron\\/)", type: "file_path" }
5590
+ ],
5570
5591
  fix: { description: "Add rate limiting to all authentication endpoints. Limit login attempts per IP and per account to prevent brute force attacks.", suggestion: "const limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5 })" }
5571
5592
  },
5572
5593
  {
@@ -5723,7 +5744,10 @@ var CLIENT_RULES = [
5723
5744
  { regex: "(?:res|response)\\.(?:json|send|status)\\s*\\([^)]*(?:error\\.stack|error\\.message|err\\.stack|err\\.message|e\\.stack)", type: "match" },
5724
5745
  { regex: "NextResponse\\.json\\s*\\(\\s*\\{[^}]*(?:error\\.stack|error\\.message|err\\.stack|err\\.message)", type: "match" }
5725
5746
  ],
5726
- excludePatterns: [{ regex: "(?:development|dev|NODE_ENV|process\\.env)", type: "context_line" }],
5747
+ excludePatterns: [
5748
+ { regex: "(?:development|dev|NODE_ENV|process\\.env)", type: "context_line" },
5749
+ { regex: "(?:startsWith|includes|===|!==|Quota|limit|Unauthorized|Invalid)", type: "context_line" }
5750
+ ],
5727
5751
  fix: { description: "Return a generic error message to users (e.g. 'Something went wrong'). Log the full error server-side with console.error() for debugging. Never send stack traces or internal error messages in API responses." }
5728
5752
  },
5729
5753
  {
@@ -5756,7 +5780,7 @@ var CLIENT_RULES = [
5756
5780
  ],
5757
5781
  excludePatterns: [
5758
5782
  { regex: "(?:csrf|xsrf|_token|csrfToken|validateToken|SameSite|clerk|auth\\(\\)|getAuth|currentUser|getSession)", type: "context_line" },
5759
- { regex: "(?:api/webhook|api/stripe|api/clerk|api/cron|api/internal)", type: "file_path" }
5783
+ { regex: "(?:api/webhook|api/stripe|api/clerk|api/cron|api/internal|api/cli|api/checkout|api/contact|api/unsubscribe)", type: "file_path" }
5760
5784
  ],
5761
5785
  fix: { description: "Add CSRF protection: use SameSite=Strict cookies, verify a CSRF token header, or use a framework that handles this automatically (e.g. Next.js Server Actions have built-in CSRF protection)." }
5762
5786
  }
@@ -5846,6 +5870,10 @@ function matchRule(rule, file) {
5846
5870
  return [];
5847
5871
  }
5848
5872
  const isSecretRule = rule.id.startsWith("secrets/");
5873
+ if (!isSecretRule) {
5874
+ const docPaths = /(?:\/docs\/|\/examples\/|\/fixtures\/|__tests__\/fixtures)/i;
5875
+ if (docPaths.test(file.path)) return [];
5876
+ }
5849
5877
  const findings = [];
5850
5878
  const lines = file.content.split("\n");
5851
5879
  for (const pattern of rule.patterns) {
@@ -7500,10 +7528,10 @@ import { homedir as homedir2 } from "os";
7500
7528
  import { join as join3 } from "path";
7501
7529
  var HISTORY_DIR = join3(homedir2(), ".shipsafe", "scans");
7502
7530
  function fingerprint(f) {
7503
- return createHash2("md5").update(`${f.ruleId}:${f.file}`).digest("hex");
7531
+ return createHash2("sha256").update(`${f.ruleId}:${f.file}`).digest("hex");
7504
7532
  }
7505
7533
  function historyPath(scanPath) {
7506
- const hash = createHash2("md5").update(scanPath).digest("hex");
7534
+ const hash = createHash2("sha256").update(scanPath).digest("hex");
7507
7535
  return join3(HISTORY_DIR, `${hash}.json`);
7508
7536
  }
7509
7537
  function diffWithPrevious(scanPath, currentFindings) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ship-safe/cli",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Security scanner for AI-generated code — find vulnerabilities before you ship",
5
5
  "type": "module",
6
6
  "license": "MIT",