xploitscan 1.2.0 → 1.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/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  clearProRulesCache,
8
8
  clearToken,
9
9
  detectFramework,
10
+ detectPlatform,
10
11
  downloadProRulesBundle,
11
12
  filterFalsePositives,
12
13
  getStoredToken,
@@ -18,14 +19,14 @@ import {
18
19
  storeToken,
19
20
  syncUser,
20
21
  uploadScanResults
21
- } from "./chunk-KE2IZNH6.js";
22
+ } from "./chunk-4OFR3SIS.js";
22
23
 
23
24
  // src/index.ts
24
25
  import { Command } from "commander";
25
26
 
26
27
  // src/commands/scan.ts
27
28
  import { resolve as resolve2, join as join6 } from "path";
28
- import { watch as fsWatch } from "fs";
29
+ import { watch as fsWatch, existsSync as existsSync5 } from "fs";
29
30
  import ora from "ora";
30
31
  import chalk2 from "chalk";
31
32
 
@@ -1769,6 +1770,21 @@ function renderTerminalReport(result, files) {
1769
1770
  if (frameworks.length > 0 && frameworks[0] !== "unknown") {
1770
1771
  console.log(chalk.gray(" Frameworks: ") + chalk.white(frameworks.join(", ")));
1771
1772
  }
1773
+ const platforms = detectPlatform(files);
1774
+ if (platforms.length > 0) {
1775
+ const PLATFORM_LABELS = {
1776
+ lovable: "Lovable",
1777
+ bolt: "Bolt",
1778
+ replit: "Replit",
1779
+ cursor: "Cursor",
1780
+ v0: "v0",
1781
+ "claude-code": "Claude Code"
1782
+ };
1783
+ const labels = platforms.map((p) => PLATFORM_LABELS[p] ?? p);
1784
+ console.log(
1785
+ chalk.gray(" Detected: ") + chalk.cyan(labels.join(" + ") + " project") + chalk.gray(" \u2014 rules tuned for what these tools ship by default")
1786
+ );
1787
+ }
1772
1788
  }
1773
1789
  const GRADE_PERCENTILES = { "A+": 98, "A": 90, "B": 70, "C": 45, "D": 20, "F": 5 };
1774
1790
  const { grade, score, summary } = calculateGrade(findings, filesScanned);
@@ -2327,6 +2343,17 @@ ${isMonthlyCap ? "Monthly" : "Daily"} scan limit reached.`));
2327
2343
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2328
2344
  directory: dir
2329
2345
  };
2346
+ const PLATFORM_MARKERS = [
2347
+ ".cursorrules",
2348
+ ".cursor/",
2349
+ ".replit",
2350
+ "replit.nix",
2351
+ ".bolt/",
2352
+ ".stackblitzrc",
2353
+ "CLAUDE.md",
2354
+ ".claude/"
2355
+ ];
2356
+ const platformMarkerFiles = PLATFORM_MARKERS.filter((m) => existsSync5(join6(dir, m.replace(/\/$/, "")))).map((m) => ({ path: m, content: "" }));
2330
2357
  switch (format) {
2331
2358
  case "json":
2332
2359
  renderJsonReport(result);
@@ -2344,7 +2371,7 @@ ${isMonthlyCap ? "Monthly" : "Daily"} scan limit reached.`));
2344
2371
  renderDatadogReport(result);
2345
2372
  break;
2346
2373
  default:
2347
- renderTerminalReport(result, fileContentsForAnalysis);
2374
+ renderTerminalReport(result, [...fileContentsForAnalysis, ...platformMarkerFiles]);
2348
2375
  break;
2349
2376
  }
2350
2377
  if (aiReviewed && !isSilent) {
@@ -2573,7 +2600,7 @@ Open this URL in your browser to log in:`));
2573
2600
  // src/commands/hook.ts
2574
2601
  import { execSync } from "child_process";
2575
2602
  import {
2576
- existsSync as existsSync5,
2603
+ existsSync as existsSync6,
2577
2604
  readFileSync as readFileSync4,
2578
2605
  writeFileSync,
2579
2606
  chmodSync,
@@ -2636,7 +2663,7 @@ async function installHookCommand(options) {
2636
2663
  const hookPath = join7(hooksDir, "pre-commit");
2637
2664
  let existingContent = "";
2638
2665
  let hadExisting = false;
2639
- if (existsSync5(hookPath)) {
2666
+ if (existsSync6(hookPath)) {
2640
2667
  existingContent = readFileSync4(hookPath, "utf-8");
2641
2668
  hadExisting = true;
2642
2669
  const alreadyInstalled = existingContent.includes(HOOK_START_MARKER) && existingContent.includes(HOOK_END_MARKER);
@@ -2683,7 +2710,7 @@ async function uninstallHookCommand() {
2683
2710
  process.exit(1);
2684
2711
  }
2685
2712
  const hookPath = join7(gitRoot, ".git", "hooks", "pre-commit");
2686
- if (!existsSync5(hookPath)) {
2713
+ if (!existsSync6(hookPath)) {
2687
2714
  console.log(chalk4.yellow("\u26A0 No pre-commit hook found. Nothing to uninstall."));
2688
2715
  return;
2689
2716
  }
@@ -2713,7 +2740,7 @@ async function uninstallHookCommand() {
2713
2740
 
2714
2741
  // src/commands/cursor.ts
2715
2742
  import chalk5 from "chalk";
2716
- import { mkdirSync, existsSync as existsSync6, writeFileSync as writeFileSync2, readFileSync as readFileSync5 } from "fs";
2743
+ import { mkdirSync, existsSync as existsSync7, writeFileSync as writeFileSync2, readFileSync as readFileSync5 } from "fs";
2717
2744
  import { join as join8, dirname } from "path";
2718
2745
  import { fileURLToPath } from "url";
2719
2746
  async function cursorInstallCommand(opts = {}) {
@@ -2724,7 +2751,7 @@ async function cursorInstallCommand(opts = {}) {
2724
2751
  join8(here, "..", "templates"),
2725
2752
  join8(here, "..", "src", "templates")
2726
2753
  ];
2727
- const templatesDir = candidates.find((p) => existsSync6(join8(p, "cursor-security.mdc")));
2754
+ const templatesDir = candidates.find((p) => existsSync7(join8(p, "cursor-security.mdc")));
2728
2755
  if (!templatesDir) {
2729
2756
  console.error(chalk5.red("Could not locate XploitScan rule templates. Try reinstalling: npm i -g xploitscan@latest"));
2730
2757
  process.exit(1);
@@ -2734,7 +2761,7 @@ async function cursorInstallCommand(opts = {}) {
2734
2761
  if (!opts.legacyOnly) {
2735
2762
  const mdcDir = join8(cwd, ".cursor", "rules");
2736
2763
  const mdcPath = join8(mdcDir, "xploitscan-security.mdc");
2737
- if (existsSync6(mdcPath) && !opts.force) {
2764
+ if (existsSync7(mdcPath) && !opts.force) {
2738
2765
  console.log(chalk5.yellow(`Skipping ${mdcPath} (already exists, use --force to overwrite)`));
2739
2766
  } else {
2740
2767
  mkdirSync(mdcDir, { recursive: true });
@@ -2743,7 +2770,7 @@ async function cursorInstallCommand(opts = {}) {
2743
2770
  }
2744
2771
  }
2745
2772
  const legacyPath = join8(cwd, ".cursorrules");
2746
- if (existsSync6(legacyPath) && !opts.force) {
2773
+ if (existsSync7(legacyPath) && !opts.force) {
2747
2774
  console.log(chalk5.yellow(`Skipping ${legacyPath} (already exists, use --force to overwrite)`));
2748
2775
  } else {
2749
2776
  writeFileSync2(legacyPath, legacySrc);
@@ -2758,7 +2785,7 @@ async function cursorInstallCommand(opts = {}) {
2758
2785
  var program = new Command();
2759
2786
  program.name("xploitscan").description(
2760
2787
  "AI security scanner for vibe-coded apps. Find vulnerabilities before attackers do."
2761
- ).version("1.2.0");
2788
+ ).version("1.2.1");
2762
2789
  program.command("scan").description("Scan a directory for security vulnerabilities").argument("[directory]", "Directory to scan", ".").option("--no-ai", "Skip AI-powered analysis").option(
2763
2790
  "-f, --format <format>",
2764
2791
  "Output format: terminal, json, sarif, splunk-hec, elastic-ecs, datadog-logs",
@@ -2787,7 +2814,7 @@ cursor.command("install").description("Drop XploitScan security rules into .curs
2787
2814
  await cursorInstallCommand({ force: opts.force, legacyOnly: opts.legacyOnly });
2788
2815
  });
2789
2816
  program.command("upgrade").description("Upgrade to XploitScan Pro for unlimited scans").action(async () => {
2790
- const { getStoredToken: getStoredToken2, getCheckoutUrl } = await import("./api-4UFZFIDO.js");
2817
+ const { getStoredToken: getStoredToken2, getCheckoutUrl } = await import("./api-MFCSQQ2Y.js");
2791
2818
  const chalk6 = (await import("chalk")).default;
2792
2819
  const token = getStoredToken2();
2793
2820
  if (!token) {