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/README.md +5 -5
- package/dist/{api-4UFZFIDO.js → api-MFCSQQ2Y.js} +2 -2
- package/dist/{chunk-KE2IZNH6.js → chunk-4OFR3SIS.js} +279 -26
- package/dist/{chunk-KE2IZNH6.js.map → chunk-4OFR3SIS.js.map} +1 -1
- package/dist/index.js +39 -12
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- /package/dist/{api-4UFZFIDO.js.map → api-MFCSQQ2Y.js.map} +0 -0
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-
|
|
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
|
|
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 (
|
|
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 (!
|
|
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
|
|
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) =>
|
|
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 (
|
|
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 (
|
|
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.
|
|
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-
|
|
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) {
|