xploitscan 1.1.2 → 1.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/dist/index.js CHANGED
@@ -18,9 +18,9 @@ import {
18
18
  uploadScanResults
19
19
  } from "./chunk-WGENFXRJ.js";
20
20
 
21
- // ../../node_modules/.pnpm/@babel+parser@7.29.2/node_modules/@babel/parser/lib/index.js
21
+ // ../../node_modules/.pnpm/@babel+parser@7.29.3/node_modules/@babel/parser/lib/index.js
22
22
  var require_lib = __commonJS({
23
- "../../node_modules/.pnpm/@babel+parser@7.29.2/node_modules/@babel/parser/lib/index.js"(exports) {
23
+ "../../node_modules/.pnpm/@babel+parser@7.29.3/node_modules/@babel/parser/lib/index.js"(exports) {
24
24
  "use strict";
25
25
  Object.defineProperty(exports, "__esModule", {
26
26
  value: true
@@ -1971,7 +1971,7 @@ var require_lib = __commonJS({
1971
1971
  }
1972
1972
  flowParseDeclareVariable(node) {
1973
1973
  this.next();
1974
- node.id = this.flowParseTypeAnnotatableIdentifier(true);
1974
+ node.id = this.flowParseTypeAnnotatableIdentifier();
1975
1975
  this.scope.declareName(node.id.name, 5, node.id.loc.start);
1976
1976
  this.semicolon();
1977
1977
  return this.finishNode(node, "DeclareVariable");
@@ -2145,9 +2145,14 @@ var require_lib = __commonJS({
2145
2145
  reservedType: word
2146
2146
  });
2147
2147
  }
2148
- flowParseRestrictedIdentifier(liberal, declaration) {
2148
+ flowParseRestrictedIdentifierName(liberal, declaration) {
2149
2149
  this.checkReservedType(this.state.value, this.state.startLoc, declaration);
2150
- return this.parseIdentifier(liberal);
2150
+ return this.parseIdentifierName(liberal);
2151
+ }
2152
+ flowParseRestrictedIdentifier(liberal, declaration) {
2153
+ const node = this.startNode();
2154
+ const name = this.flowParseRestrictedIdentifierName(liberal, declaration);
2155
+ return this.createIdentifier(node, name);
2151
2156
  }
2152
2157
  flowParseTypeAlias(node) {
2153
2158
  node.id = this.flowParseRestrictedIdentifier(false, true);
@@ -2181,14 +2186,21 @@ var require_lib = __commonJS({
2181
2186
  this.semicolon();
2182
2187
  return this.finishNode(node, "OpaqueType");
2183
2188
  }
2189
+ flowParseTypeParameterBound() {
2190
+ if (this.match(14) || this.isContextual(81)) {
2191
+ const node = this.startNode();
2192
+ this.next();
2193
+ node.typeAnnotation = this.flowParseType();
2194
+ return this.finishNode(node, "TypeAnnotation");
2195
+ }
2196
+ }
2184
2197
  flowParseTypeParameter(requireDefault = false) {
2185
2198
  const nodeStartLoc = this.state.startLoc;
2186
2199
  const node = this.startNode();
2187
2200
  const variance = this.flowParseVariance();
2188
- const ident = this.flowParseTypeAnnotatableIdentifier();
2189
- node.name = ident.name;
2201
+ node.name = this.flowParseRestrictedIdentifierName();
2190
2202
  node.variance = variance;
2191
- node.bound = ident.typeAnnotation;
2203
+ node.bound = this.flowParseTypeParameterBound();
2192
2204
  if (this.match(29)) {
2193
2205
  this.eat(29);
2194
2206
  node.default = this.flowParseType();
@@ -2883,13 +2895,13 @@ var require_lib = __commonJS({
2883
2895
  node.typeAnnotation = this.flowParseTypeInitialiser();
2884
2896
  return this.finishNode(node, "TypeAnnotation");
2885
2897
  }
2886
- flowParseTypeAnnotatableIdentifier(allowPrimitiveOverride) {
2887
- const ident = allowPrimitiveOverride ? this.parseIdentifier() : this.flowParseRestrictedIdentifier();
2898
+ flowParseTypeAnnotatableIdentifier() {
2899
+ const node = this.startNode();
2900
+ const name = this.parseIdentifierName();
2888
2901
  if (this.match(14)) {
2889
- ident.typeAnnotation = this.flowParseTypeAnnotation();
2890
- this.resetEndLocation(ident);
2902
+ node.typeAnnotation = this.flowParseTypeAnnotation();
2891
2903
  }
2892
- return ident;
2904
+ return this.createIdentifier(node, name);
2893
2905
  }
2894
2906
  typeCastToParameter(node) {
2895
2907
  node.expression.typeAnnotation = node.typeAnnotation;
@@ -5106,6 +5118,7 @@ var require_lib = __commonJS({
5106
5118
  adjustInnerComments(node, node.properties, commentWS);
5107
5119
  break;
5108
5120
  case "CallExpression":
5121
+ case "NewExpression":
5109
5122
  case "OptionalCallExpression":
5110
5123
  adjustInnerComments(node, node.arguments, commentWS);
5111
5124
  break;
@@ -5118,6 +5131,7 @@ var require_lib = __commonJS({
5118
5131
  case "ObjectMethod":
5119
5132
  case "ClassMethod":
5120
5133
  case "ClassPrivateMethod":
5134
+ case "TSTypeParameterDeclaration":
5121
5135
  adjustInnerComments(node, node.params, commentWS);
5122
5136
  break;
5123
5137
  case "ArrayExpression":
@@ -5134,6 +5148,9 @@ var require_lib = __commonJS({
5134
5148
  case "TSEnumBody":
5135
5149
  adjustInnerComments(node, node.members, commentWS);
5136
5150
  break;
5151
+ case "TSInterfaceBody":
5152
+ adjustInnerComments(node, node.body, commentWS);
5153
+ break;
5137
5154
  default: {
5138
5155
  if (node.type === "RecordExpression") {
5139
5156
  adjustInnerComments(node, node.properties, commentWS);
@@ -43495,7 +43512,25 @@ var ALWAYS_IGNORE = [
43495
43512
  "*.map",
43496
43513
  "package-lock.json",
43497
43514
  "pnpm-lock.yaml",
43498
- "yarn.lock"
43515
+ "yarn.lock",
43516
+ // Intentionally-vulnerable test corpora. Security tools (XploitScan,
43517
+ // Semgrep, Bearer, Gitleaks, Snyk Code) all maintain directories of
43518
+ // known-bad code samples used to verify that rules fire. Scanning
43519
+ // them produces a flood of "critical" findings on the scanner's own
43520
+ // dogfood, drowning the signal from real product code. Skip by
43521
+ // default. Users who genuinely want to scan a fixture directory can
43522
+ // override via .xploitscanignore (the negation syntax `!test-
43523
+ // fixtures/` un-ignores it).
43524
+ //
43525
+ // We only match these as directory names anywhere in the tree, so a
43526
+ // project whose actual source happens to live under a path called
43527
+ // `fixtures-prod/` is unaffected.
43528
+ "test-fixtures",
43529
+ "test-fixtures/**",
43530
+ "__fixtures__",
43531
+ "__fixtures__/**",
43532
+ "vulnerable-samples",
43533
+ "vulnerable-samples/**"
43499
43534
  ];
43500
43535
  async function collectFiles(directory) {
43501
43536
  const ig = ignore.default();
@@ -45514,7 +45549,21 @@ For each finding, respond ONLY with a JSON array. No other text.
45514
45549
  Each element: {"index": <number>, "verdict": "real" or "fp", "reason": "<1 sentence>"}`;
45515
45550
  var MAX_FINDINGS_PER_BATCH = 15;
45516
45551
  var MAX_CONTEXT_LINES = 10;
45517
- var MAX_TOTAL_FINDINGS = 50;
45552
+ var DEFAULT_MAX_TOTAL_FINDINGS = 200;
45553
+ var MAX_TOTAL_FINDINGS = (() => {
45554
+ const raw = process.env.XPLOITSCAN_AI_FILTER_MAX;
45555
+ if (!raw) return DEFAULT_MAX_TOTAL_FINDINGS;
45556
+ const n = parseInt(raw, 10);
45557
+ if (!Number.isFinite(n) || n < 1) return DEFAULT_MAX_TOTAL_FINDINGS;
45558
+ return Math.min(n, 1e3);
45559
+ })();
45560
+ var SEVERITY_PRIORITY = {
45561
+ critical: 0,
45562
+ high: 1,
45563
+ medium: 2,
45564
+ low: 3,
45565
+ info: 4
45566
+ };
45518
45567
  function getExpandedContext(content, line, contextLines = MAX_CONTEXT_LINES) {
45519
45568
  const lines = content.split("\n");
45520
45569
  const start = Math.max(0, line - 1 - contextLines);
@@ -45562,8 +45611,13 @@ async function filterFalsePositives(findings, fileContents) {
45562
45611
  const empty = { findings, filteredFindings: [], aiReviewed: false, removedCount: 0, totalBefore: findings.length };
45563
45612
  if (!process.env.ANTHROPIC_API_KEY) return empty;
45564
45613
  if (findings.length === 0) return empty;
45565
- const toReview = findings.slice(0, MAX_TOTAL_FINDINGS);
45566
- const overflow = findings.slice(MAX_TOTAL_FINDINGS);
45614
+ const prioritized = [...findings].sort((a, b) => {
45615
+ const pa = SEVERITY_PRIORITY[(a.severity || "").toLowerCase()] ?? 5;
45616
+ const pb = SEVERITY_PRIORITY[(b.severity || "").toLowerCase()] ?? 5;
45617
+ return pa - pb;
45618
+ });
45619
+ const toReview = prioritized.slice(0, MAX_TOTAL_FINDINGS);
45620
+ const overflow = prioritized.slice(MAX_TOTAL_FINDINGS);
45567
45621
  const totalBefore = findings.length;
45568
45622
  const byFile = /* @__PURE__ */ new Map();
45569
45623
  for (const f of toReview) {
@@ -47843,6 +47897,7 @@ import { execFile as execFile3 } from "child_process";
47843
47897
  import chalk3 from "chalk";
47844
47898
  import ora2 from "ora";
47845
47899
  var CLERK_PUBLISHABLE_KEY = process.env.CLERK_PUBLISHABLE_KEY ?? "";
47900
+ var WEB_BASE = process.env.XPLOITSCAN_WEB_BASE ?? "https://xploitscan.com";
47846
47901
  async function loginCommand() {
47847
47902
  const existing = getStoredToken();
47848
47903
  if (existing) {
@@ -47931,19 +47986,28 @@ async function waitForBrowserLogin() {
47931
47986
  res.end("Missing parameters");
47932
47987
  }
47933
47988
  } else if (url.pathname === "/login") {
47934
- const callbackUrl = `http://localhost:${server.address().port}/callback`;
47935
- res.writeHead(200, { "Content-Type": "text/html" });
47936
- res.end(`
47937
- <html>
47938
- <body style="font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0a0a0f; color: #e0e0e0;">
47939
- <div style="text-align: center;">
47940
- <h1 style="color: #00d4ff;">xploitscan</h1>
47941
- <p>Redirecting to login...</p>
47942
- <p style="color: #888; font-size: 0.85rem;">If not redirected, <a href="#" style="color: #00d4ff;">click here</a>.</p>
47943
- </div>
47944
- </body>
47945
- </html>
47946
- `);
47989
+ const port = server.address().port;
47990
+ const callbackUrl = `http://localhost:${port}/callback`;
47991
+ const cliLoginUrl = `${WEB_BASE}/auth/cli-login?callback=${encodeURIComponent(callbackUrl)}&state=${encodeURIComponent(expectedState)}`;
47992
+ res.writeHead(302, { Location: cliLoginUrl, "Content-Type": "text/html" });
47993
+ res.end(`<!doctype html>
47994
+ <html>
47995
+ <head>
47996
+ <meta charset="utf-8" />
47997
+ <meta http-equiv="refresh" content="0; url=${cliLoginUrl}" />
47998
+ <title>xploitscan login</title>
47999
+ </head>
48000
+ <body style="font-family: system-ui; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: #0a0a0f; color: #e0e0e0;">
48001
+ <div style="text-align: center;">
48002
+ <h1 style="color: #00d4ff;">xploitscan</h1>
48003
+ <p>Redirecting to login...</p>
48004
+ <p style="color: #888; font-size: 0.85rem;">
48005
+ If not redirected, <a href="${cliLoginUrl}" style="color: #00d4ff;">click here</a>.
48006
+ </p>
48007
+ </div>
48008
+ <script>window.location.replace(${JSON.stringify(cliLoginUrl)});</script>
48009
+ </body>
48010
+ </html>`);
47947
48011
  } else {
47948
48012
  res.writeHead(302, { Location: "/login" });
47949
48013
  res.end();
@@ -48155,7 +48219,7 @@ async function cursorInstallCommand(opts = {}) {
48155
48219
  var program = new Command();
48156
48220
  program.name("xploitscan").description(
48157
48221
  "AI security scanner for vibe-coded apps. Find vulnerabilities before attackers do."
48158
- ).version("1.1.2");
48222
+ ).version("1.1.4");
48159
48223
  program.command("scan").description("Scan a directory for security vulnerabilities").argument("[directory]", "Directory to scan", ".").option("--no-ai", "Skip AI-powered analysis").option(
48160
48224
  "-f, --format <format>",
48161
48225
  "Output format: terminal, json, sarif, splunk-hec, elastic-ecs, datadog-logs",