xploitscan 1.0.20 → 1.1.0

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 CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  **Security scanner for AI-generated code.** Find vulnerabilities before attackers do.
7
7
 
8
- Built for developers shipping code via Cursor, Lovable, Bolt, Replit, and Claude Code. 158 security rules. Plain-English results. Copy-paste fixes.
8
+ Built for developers shipping code via Cursor, Lovable, Bolt, Replit, and Claude Code. 206 security rules. Plain-English results. Copy-paste fixes.
9
9
 
10
10
  ## Quick Start
11
11
 
@@ -17,7 +17,7 @@ No install, no config, no account required. Your code stays 100% local.
17
17
 
18
18
  ## What It Catches
19
19
 
20
- 158 rules across 15+ categories:
20
+ 206 rules across 15+ categories:
21
21
 
22
22
  | Category | Examples | Rules |
23
23
  |----------|---------|-------|
@@ -32,6 +32,18 @@ No install, no config, no account required. Your code stays 100% local.
32
32
 
33
33
  Every finding includes OWASP Top 10 and CWE compliance mappings.
34
34
 
35
+ ## Detection Quality
36
+
37
+ Detection is scored publicly on a labeled fixture corpus that's refreshed on every commit. Current numbers live at **[xploitscan.com/benchmark](https://xploitscan.com/benchmark)**:
38
+
39
+ - **100% precision** (zero false positives) across 151 labeled fixtures covering 25+ vulnerability classes
40
+ - **80%+ recall** on rules with active test coverage
41
+ - Side-by-side comparison with Semgrep (community rulesets) and Bearer on the same corpus
42
+
43
+ The scanner uses a two-layer architecture: a fast regex pre-filter for pattern-based rules (secrets, missing headers, container misconfigs), and a Babel-parsed AST layer with local taint tracking for data-flow rules (SSRF, prototype pollution, mass assignment, SSTI, command injection from user input). Recognized taint sources: Express / Fastify / Koa / Next.js App Router / Web Fetch API / AWS Lambda.
44
+
45
+ Methodology, fixture format, and reproducibility instructions: **[xploitscan.com/docs/detection-methodology](https://xploitscan.com/docs/detection-methodology)**.
46
+
35
47
  ## Installation
36
48
 
37
49
  ```bash
@@ -130,7 +142,7 @@ Scan via the web at [xploitscan.com](https://xploitscan.com):
130
142
  - SOC2/ISO27001 compliance mapping
131
143
  - Slack and Discord webhook notifications
132
144
 
133
- **Free**: 5 scans/day, 30 core rules. **Pro** ($29/mo): unlimited scans, all 158 rules, and all dashboard features. **Team** ($99/mo): everything in Pro plus 5 seats, shared scan history, RBAC, and portfolio reports. Annual plans save 20%.
145
+ **Free**: 5 scans/day, 30 core rules. **Indie** ($9/mo): 500 scans/month, all 206 rules, scan history. **Pro** ($19/mo): unlimited scans, all 206 rules, PDF reports, compliance mapping, webhooks, AI false-positive filter. **Team** ($99/mo): everything in Pro plus 5 seats, shared scan history, RBAC, and portfolio reports. Annual plans save 40%.
134
146
 
135
147
  ## Supported Languages
136
148
 
@@ -12,7 +12,7 @@ import {
12
12
  storeToken,
13
13
  syncUser,
14
14
  uploadScanResults
15
- } from "./chunk-RJUUWD2F.js";
15
+ } from "./chunk-HHYGPI7J.js";
16
16
  export {
17
17
  checkUsage,
18
18
  clearProRulesCache,
@@ -27,4 +27,4 @@ export {
27
27
  syncUser,
28
28
  uploadScanResults
29
29
  };
30
- //# sourceMappingURL=api-HTHCG6QE.js.map
30
+ //# sourceMappingURL=api-UURQGULS.js.map
@@ -1,10 +1,35 @@
1
1
  #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
2
8
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
9
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
10
  }) : x)(function(x) {
5
11
  if (typeof require !== "undefined") return require.apply(this, arguments);
6
12
  throw Error('Dynamic require of "' + x + '" is not supported');
7
13
  });
14
+ var __commonJS = (cb, mod) => function __require2() {
15
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
8
33
 
9
34
  // src/utils/api.ts
10
35
  import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from "fs";
@@ -17,6 +42,13 @@ var PRO_RULES_FILE = join(CONFIG_DIR, "pro-rules.cjs");
17
42
  var PRO_RULES_META = join(CONFIG_DIR, "pro-rules-meta.json");
18
43
  var API_BASE = process.env.XPLOITSCAN_API_URL ?? "https://api.xploitscan.com";
19
44
  var WEB_BASE = process.env.XPLOITSCAN_WEB_URL ?? "https://xploitscan.com";
45
+ function getApiKey() {
46
+ const fromEnv = process.env.XPLOITSCAN_API_KEY;
47
+ if (fromEnv && fromEnv.startsWith("xpls_")) return fromEnv;
48
+ const stored = getStoredToken();
49
+ if (stored && stored.token.startsWith("xpls_")) return stored.token;
50
+ return null;
51
+ }
20
52
  if (API_BASE.startsWith("http://") && !API_BASE.includes("localhost") && !API_BASE.includes("127.0.0.1")) {
21
53
  throw new Error("API URL must use HTTPS for security. Set XPLOITSCAN_API_URL to an https:// URL.");
22
54
  }
@@ -66,13 +98,13 @@ async function apiRequest(path, options = {}) {
66
98
  });
67
99
  }
68
100
  async function checkUsage() {
69
- const token = getStoredToken();
70
- if (!token) {
101
+ const apiKey = getApiKey();
102
+ if (!apiKey) {
71
103
  return { allowed: true, plan: "anonymous", remaining: -1, limit: -1 };
72
104
  }
73
105
  try {
74
106
  const res = await fetch(`${WEB_BASE}/api/cli/usage`, {
75
- headers: { Authorization: `Bearer ${token.token}` }
107
+ headers: { Authorization: `Bearer ${apiKey}` }
76
108
  });
77
109
  if (!res.ok) {
78
110
  return { allowed: true, plan: "unknown", remaining: -1, limit: -1 };
@@ -103,10 +135,11 @@ async function uploadScanResults(result) {
103
135
  }
104
136
  async function syncUser() {
105
137
  const token = getStoredToken();
106
- if (!token) return null;
138
+ const apiKey = getApiKey();
139
+ if (!apiKey || !token) return null;
107
140
  try {
108
141
  const res = await fetch(`${WEB_BASE}/api/cli/usage`, {
109
- headers: { Authorization: `Bearer ${token.token}` }
142
+ headers: { Authorization: `Bearer ${apiKey}` }
110
143
  });
111
144
  if (!res.ok) return null;
112
145
  const data = await res.json();
@@ -127,11 +160,11 @@ async function getCheckoutUrl() {
127
160
  }
128
161
  var PRO_RULES_CACHE_HOURS = 24;
129
162
  async function downloadProRulesBundle() {
130
- const token = getStoredToken();
131
- if (!token) return false;
163
+ const apiKey = getApiKey();
164
+ if (!apiKey) return false;
132
165
  try {
133
166
  const res = await fetch(`${WEB_BASE}/api/cli/rules-bundle`, {
134
- headers: { Authorization: `Bearer ${token.token}` }
167
+ headers: { Authorization: `Bearer ${apiKey}` }
135
168
  });
136
169
  if (!res.ok) return false;
137
170
  const bundle = await res.text();
@@ -183,6 +216,9 @@ function clearProRulesCache() {
183
216
  }
184
217
 
185
218
  export {
219
+ __require,
220
+ __commonJS,
221
+ __toESM,
186
222
  getStoredToken,
187
223
  storeToken,
188
224
  clearToken,
@@ -196,4 +232,4 @@ export {
196
232
  loadCachedProRules,
197
233
  clearProRulesCache
198
234
  };
199
- //# sourceMappingURL=chunk-RJUUWD2F.js.map
235
+ //# sourceMappingURL=chunk-HHYGPI7J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/api.ts"],"sourcesContent":["import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createHash } from \"node:crypto\";\n\nconst CONFIG_DIR = join(homedir(), \".xploitscan\");\nconst TOKEN_FILE = join(CONFIG_DIR, \"token.json\");\nconst PRO_RULES_FILE = join(CONFIG_DIR, \"pro-rules.cjs\");\nconst PRO_RULES_META = join(CONFIG_DIR, \"pro-rules-meta.json\");\n\nconst API_BASE = process.env.XPLOITSCAN_API_URL ?? \"https://api.xploitscan.com\";\nconst WEB_BASE = process.env.XPLOITSCAN_WEB_URL ?? \"https://xploitscan.com\";\n\n/**\n * Resolve the credential to send to xploitscan.com endpoints that require\n * an `xpls_` API key (currently `/api/cli/usage` and `/api/cli/rules-bundle`).\n *\n * Order of precedence:\n * 1. `XPLOITSCAN_API_KEY` env var — how the GitHub Action and CI flows\n * pass a long-lived key (matches the variable name documented in\n * `packages/action/action.yml` and the Settings → API Keys docs).\n * 2. The locally stored token from `xploitscan auth login` — only useful\n * if the user pasted an `xpls_` key during login. The legacy Clerk\n * JWT path is no longer accepted by the server (security fix C-1).\n *\n * Returns null when no `xpls_`-shaped credential is available.\n */\nfunction getApiKey(): string | null {\n const fromEnv = process.env.XPLOITSCAN_API_KEY;\n if (fromEnv && fromEnv.startsWith(\"xpls_\")) return fromEnv;\n const stored = getStoredToken();\n if (stored && stored.token.startsWith(\"xpls_\")) return stored.token;\n return null;\n}\n\nif (API_BASE.startsWith(\"http://\") && !API_BASE.includes(\"localhost\") && !API_BASE.includes(\"127.0.0.1\")) {\n throw new Error(\"API URL must use HTTPS for security. Set XPLOITSCAN_API_URL to an https:// URL.\");\n}\nif (WEB_BASE.startsWith(\"http://\") && !WEB_BASE.includes(\"localhost\") && !WEB_BASE.includes(\"127.0.0.1\")) {\n throw new Error(\"Web URL must use HTTPS for security. Set XPLOITSCAN_WEB_URL to an https:// URL.\");\n}\n\ninterface TokenData {\n token: string;\n userId: string;\n email: string;\n expiresAt?: number;\n}\n\nexport function getStoredToken(): TokenData | null {\n try {\n if (!existsSync(TOKEN_FILE)) return null;\n const data = JSON.parse(readFileSync(TOKEN_FILE, \"utf-8\"));\n if (data.expiresAt && Date.now() > data.expiresAt) {\n // Token expired\n unlinkSync(TOKEN_FILE);\n return null;\n }\n return data;\n } catch {\n return null;\n }\n}\n\nexport function storeToken(data: TokenData): void {\n mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });\n writeFileSync(TOKEN_FILE, JSON.stringify(data, null, 2), { mode: 0o600 });\n}\n\nexport function clearToken(): void {\n try {\n if (existsSync(TOKEN_FILE)) {\n unlinkSync(TOKEN_FILE);\n }\n } catch {\n // ignore\n }\n}\n\nexport function isAuthenticated(): boolean {\n return getStoredToken() !== null;\n}\n\nasync function apiRequest(\n path: string,\n options: RequestInit = {},\n): Promise<Response> {\n const token = getStoredToken();\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...(options.headers as Record<string, string>),\n };\n\n if (token) {\n headers.Authorization = `Bearer ${token.token}`;\n }\n\n return fetch(`${API_BASE}${path}`, {\n ...options,\n headers,\n });\n}\n\nexport async function checkUsage(): Promise<{\n allowed: boolean;\n plan: string;\n // Set to \"indie\" when the server wants the CLI to message a monthly\n // (rather than daily) cap. Other tiers don't set this field.\n tier?: string;\n remaining: number;\n limit: number;\n}> {\n const apiKey = getApiKey();\n if (!apiKey) {\n // No xpls_ key available — anonymous local-scan path (or a stale\n // Clerk-JWT login that the server no longer accepts).\n return { allowed: true, plan: \"anonymous\", remaining: -1, limit: -1 };\n }\n\n try {\n // Use web app API for real subscription lookup\n const res = await fetch(`${WEB_BASE}/api/cli/usage`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n });\n if (!res.ok) {\n return { allowed: true, plan: \"unknown\", remaining: -1, limit: -1 };\n }\n return await res.json();\n } catch {\n // Network error — allow local scan\n return { allowed: true, plan: \"offline\", remaining: -1, limit: -1 };\n }\n}\n\nexport async function incrementUsage(): Promise<void> {\n const token = getStoredToken();\n if (!token) return;\n\n try {\n await apiRequest(\"/api/usage/increment\", { method: \"POST\" });\n } catch {\n // Silent fail — don't block scan\n }\n}\n\nexport async function uploadScanResults(result: {\n directory: string;\n filesScanned: number;\n findings: unknown[];\n duration: number;\n}): Promise<void> {\n const token = getStoredToken();\n if (!token) return;\n\n try {\n await apiRequest(\"/api/scans\", {\n method: \"POST\",\n body: JSON.stringify(result),\n });\n } catch {\n // Silent fail\n }\n}\n\nexport async function syncUser(): Promise<{ plan: string; email: string } | null> {\n const token = getStoredToken();\n const apiKey = getApiKey();\n if (!apiKey || !token) return null;\n try {\n const res = await fetch(`${WEB_BASE}/api/cli/usage`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n });\n if (!res.ok) return null;\n const data = await res.json();\n return { plan: data.plan, email: token.email };\n } catch {\n return null;\n }\n}\n\nexport async function getCheckoutUrl(): Promise<string | null> {\n try {\n const res = await apiRequest(\"/api/billing/checkout\", { method: \"POST\" });\n if (!res.ok) return null;\n const data = await res.json();\n return data.url;\n } catch {\n return null;\n }\n}\n\n// ── Pro Rules Bundle (Server-Side Delivery) ──────────────────────────────\n\nconst PRO_RULES_CACHE_HOURS = 24;\n\nexport async function downloadProRulesBundle(): Promise<boolean> {\n const apiKey = getApiKey();\n if (!apiKey) return false;\n\n try {\n const res = await fetch(`${WEB_BASE}/api/cli/rules-bundle`, {\n headers: { Authorization: `Bearer ${apiKey}` },\n });\n if (!res.ok) return false;\n\n const bundle = await res.text();\n if (!bundle || bundle.length < 100) return false;\n\n // Basic integrity check: verify bundle contains expected exports\n if (!bundle.includes(\"proOnlyRules\") || !bundle.includes(\"exports\")) {\n return false;\n }\n\n const hash = createHash(\"sha256\").update(bundle).digest(\"hex\");\n mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });\n writeFileSync(PRO_RULES_FILE, bundle, { mode: 0o600 });\n writeFileSync(PRO_RULES_META, JSON.stringify({\n cachedAt: Date.now(),\n expiresAt: Date.now() + PRO_RULES_CACHE_HOURS * 60 * 60 * 1000,\n sha256: hash,\n }), { mode: 0o600 });\n return true;\n } catch {\n return false;\n }\n}\n\nexport function loadCachedProRules(): unknown[] | null {\n try {\n if (!existsSync(PRO_RULES_FILE) || !existsSync(PRO_RULES_META)) return null;\n\n const meta = JSON.parse(readFileSync(PRO_RULES_META, \"utf-8\"));\n if (Date.now() > meta.expiresAt) {\n clearProRulesCache();\n return null;\n }\n\n // Verify integrity before loading\n if (meta.sha256) {\n const fileContent = readFileSync(PRO_RULES_FILE, \"utf-8\");\n const currentHash = createHash(\"sha256\").update(fileContent).digest(\"hex\");\n if (currentHash !== meta.sha256) {\n clearProRulesCache();\n return null;\n }\n }\n\n // Dynamic require of the cached CJS bundle\n const mod = require(PRO_RULES_FILE);\n return mod.proOnlyRules || null;\n } catch {\n return null;\n }\n}\n\nexport function clearProRulesCache(): void {\n try {\n if (existsSync(PRO_RULES_FILE)) unlinkSync(PRO_RULES_FILE);\n if (existsSync(PRO_RULES_META)) unlinkSync(PRO_RULES_META);\n } catch { /* ignore */ }\n}\n"],"mappings":"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","names":[]}