@vibedrift/cli 0.4.2 → 0.4.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 +92 -20
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2735,10 +2735,13 @@ var importsAnalyzer = {
|
|
|
2735
2735
|
);
|
|
2736
2736
|
const esmFiles = [];
|
|
2737
2737
|
const cjsFiles = [];
|
|
2738
|
+
const SKIP_PATH = /(?:fixtures?|testdata|__fixtures__|__mocks__)[/\\]/i;
|
|
2738
2739
|
for (const file of jsFiles) {
|
|
2739
2740
|
if (CONFIG_FILE_PATTERN.test(file.relativePath)) continue;
|
|
2741
|
+
if (SKIP_PATH.test(file.relativePath)) continue;
|
|
2742
|
+
const stripped = file.content.replace(/\/[^/\n]+\/[gimsuvy]*/g, '""').replace(/`[^`]*`/g, '""');
|
|
2740
2743
|
const hasESM = ESM_PATTERN.test(file.content);
|
|
2741
|
-
const hasCJS = CJS_PATTERN.test(
|
|
2744
|
+
const hasCJS = CJS_PATTERN.test(stripped);
|
|
2742
2745
|
if (hasESM && hasCJS) {
|
|
2743
2746
|
findings.push({
|
|
2744
2747
|
analyzerId: "imports",
|
|
@@ -2908,8 +2911,11 @@ var NODE_BUILTINS = /* @__PURE__ */ new Set([
|
|
|
2908
2911
|
]);
|
|
2909
2912
|
var JS_IMPORT_PATTERNS = [
|
|
2910
2913
|
/(?:import|from)\s+['"]([^./][^'"]*)['"]/g,
|
|
2911
|
-
/require\(\s*['"]([^./][^'"]*)['"]\s*\)/g
|
|
2914
|
+
/require\(\s*['"]([^./][^'"]*)['"]\s*\)/g,
|
|
2915
|
+
// Dynamic imports: await import("pkg") or import("pkg")
|
|
2916
|
+
/import\(\s*['"]([^./][^'"]*)['"]\s*\)/g
|
|
2912
2917
|
];
|
|
2918
|
+
var FIXTURE_PATH_PATTERN = /(?:fixtures?|testdata|__fixtures__|__mocks__)[/\\]/i;
|
|
2913
2919
|
function extractGoImports(content) {
|
|
2914
2920
|
const imports = [];
|
|
2915
2921
|
const singlePattern = /^import\s+"([^"]+)"/gm;
|
|
@@ -2998,7 +3004,7 @@ function analyzeJsDeps(ctx) {
|
|
|
2998
3004
|
const imported = /* @__PURE__ */ new Set();
|
|
2999
3005
|
const importLocations = /* @__PURE__ */ new Map();
|
|
3000
3006
|
const jsFiles = ctx.files.filter(
|
|
3001
|
-
(f) => f.language === "javascript" || f.language === "typescript"
|
|
3007
|
+
(f) => (f.language === "javascript" || f.language === "typescript") && !FIXTURE_PATH_PATTERN.test(f.relativePath)
|
|
3002
3008
|
);
|
|
3003
3009
|
for (const file of jsFiles) {
|
|
3004
3010
|
for (const pattern of JS_IMPORT_PATTERNS) {
|
|
@@ -3856,11 +3862,12 @@ var SECURITY_PATTERNS = [
|
|
|
3856
3862
|
id: "ssrf-risk",
|
|
3857
3863
|
name: "SSRF risk",
|
|
3858
3864
|
pattern: /(?:fetch|axios\.get|http\.get|requests\.get|httpClient)\s*\(\s*(?:[`'"].*\$\{|[^'"]*\+)/g,
|
|
3859
|
-
severity: "
|
|
3860
|
-
confidence: 0.
|
|
3861
|
-
message: "
|
|
3865
|
+
severity: "info",
|
|
3866
|
+
confidence: 0.4,
|
|
3867
|
+
message: "URL constructed from variable \u2014 review if the source is user-controlled",
|
|
3862
3868
|
languages: "all",
|
|
3863
|
-
tags: ["security", "ssrf"]
|
|
3869
|
+
tags: ["security", "ssrf"],
|
|
3870
|
+
negativeFilter: /(?:API_URL|BASE_URL|apiUrl|baseUrl|base\s*\+|endpoint|config\.|process\.env)/i
|
|
3864
3871
|
},
|
|
3865
3872
|
// === Python-specific ===
|
|
3866
3873
|
{
|
|
@@ -3916,7 +3923,9 @@ var securityAnalyzer = {
|
|
|
3916
3923
|
async analyze(ctx) {
|
|
3917
3924
|
const findings = [];
|
|
3918
3925
|
const projectLanguages = [...ctx.languageBreakdown.keys()];
|
|
3926
|
+
const PATTERN_DEF = /(?:pattern\s*:|regex\s*:|RegExp\s*\(|name\s*:|message\s*:|id\s*:|label\s*:)/;
|
|
3919
3927
|
for (const file of ctx.files) {
|
|
3928
|
+
if (/(?:fixtures?|testdata|__fixtures__|__mocks__)[/\\]/i.test(file.relativePath)) continue;
|
|
3920
3929
|
for (const pattern of SECURITY_PATTERNS) {
|
|
3921
3930
|
if (pattern.languages !== "all") {
|
|
3922
3931
|
if (!file.language || !pattern.languages.includes(file.language)) continue;
|
|
@@ -3924,10 +3933,11 @@ var securityAnalyzer = {
|
|
|
3924
3933
|
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
|
|
3925
3934
|
let match;
|
|
3926
3935
|
while ((match = regex.exec(file.content)) !== null) {
|
|
3936
|
+
const lineStart = file.content.lastIndexOf("\n", match.index) + 1;
|
|
3937
|
+
const lineEnd = file.content.indexOf("\n", match.index);
|
|
3938
|
+
const line = file.content.slice(lineStart, lineEnd === -1 ? void 0 : lineEnd);
|
|
3939
|
+
if (PATTERN_DEF.test(line)) continue;
|
|
3927
3940
|
if (pattern.negativeFilter) {
|
|
3928
|
-
const lineStart2 = file.content.lastIndexOf("\n", match.index) + 1;
|
|
3929
|
-
const lineEnd2 = file.content.indexOf("\n", match.index);
|
|
3930
|
-
const line = file.content.slice(lineStart2, lineEnd2 === -1 ? void 0 : lineEnd2);
|
|
3931
3941
|
if (pattern.negativeFilter.test(line)) continue;
|
|
3932
3942
|
}
|
|
3933
3943
|
if (pattern.contextRequired) {
|
|
@@ -3939,9 +3949,7 @@ var securityAnalyzer = {
|
|
|
3939
3949
|
if (!pattern.contextRequired.test(context)) continue;
|
|
3940
3950
|
}
|
|
3941
3951
|
const lineNum = getLineNumber(file.content, match.index);
|
|
3942
|
-
const
|
|
3943
|
-
const lineEnd = file.content.indexOf("\n", match.index);
|
|
3944
|
-
const snippet = file.content.slice(lineStart, lineEnd === -1 ? void 0 : lineEnd).trim();
|
|
3952
|
+
const snippet = line.trim();
|
|
3945
3953
|
findings.push({
|
|
3946
3954
|
analyzerId: "security",
|
|
3947
3955
|
severity: pattern.severity,
|
|
@@ -6802,7 +6810,7 @@ function buildAiSummaryWidget(result) {
|
|
|
6802
6810
|
return `<section class="section" style="margin-bottom:32px">
|
|
6803
6811
|
<div style="background:rgba(255,208,0,0.03);border:1px solid var(--border);border-radius:0;padding:24px 28px;position:relative;overflow:hidden">
|
|
6804
6812
|
<div style="position:absolute;top:0;left:0;width:3px;height:100%;background:var(--border)"></div>
|
|
6805
|
-
<div style="display:flex;align-items:flex-start;gap:20px;flex-wrap:wrap">
|
|
6813
|
+
<div class="score-layout" style="display:flex;align-items:flex-start;gap:20px;flex-wrap:wrap">
|
|
6806
6814
|
<div style="flex:1;min-width:280px">
|
|
6807
6815
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:10px">
|
|
6808
6816
|
<span style="font-size:16px">🤖</span>
|
|
@@ -6844,7 +6852,7 @@ function buildScoreSection(result) {
|
|
|
6844
6852
|
}).join("");
|
|
6845
6853
|
return `<section class="section">
|
|
6846
6854
|
<div class="label">SCORE OVERVIEW</div>
|
|
6847
|
-
<div style="display:flex;gap:40px;align-items:flex-start;flex-wrap:wrap">
|
|
6855
|
+
<div class="score-layout" style="display:flex;gap:40px;align-items:flex-start;flex-wrap:wrap">
|
|
6848
6856
|
<div style="text-align:center;min-width:120px">
|
|
6849
6857
|
<div class="mono" style="font-size:72px;font-weight:800;color:${color};line-height:1">${compositeScore}</div>
|
|
6850
6858
|
<div class="mono" style="font-size:16px;color:var(--text-tertiary)">/ ${maxCompositeScore}</div>
|
|
@@ -6858,7 +6866,7 @@ function buildRadarSection(result) {
|
|
|
6858
6866
|
const cats = getDriftCats(result);
|
|
6859
6867
|
const n = cats.length;
|
|
6860
6868
|
if (n === 0) return "";
|
|
6861
|
-
const cx =
|
|
6869
|
+
const cx = 260, cy = 200, r = 130, step = 2 * Math.PI / n;
|
|
6862
6870
|
let grid = "", dots = "", labels = "";
|
|
6863
6871
|
const pts = [];
|
|
6864
6872
|
for (const ring of [0.25, 0.5, 0.75, 1]) {
|
|
@@ -6875,14 +6883,14 @@ function buildRadarSection(result) {
|
|
|
6875
6883
|
const px = cx + r * ratio * Math.cos(a), py = cy + r * ratio * Math.sin(a);
|
|
6876
6884
|
pts.push(`${px},${py}`);
|
|
6877
6885
|
dots += `<circle cx="${px}" cy="${py}" r="5" fill="var(--text-secondary)" stroke="var(--bg-page)" stroke-width="2"/>`;
|
|
6878
|
-
const labelDist = r +
|
|
6886
|
+
const labelDist = r + 35;
|
|
6879
6887
|
const lx = cx + labelDist * Math.cos(a), ly = cy + labelDist * Math.sin(a);
|
|
6880
6888
|
const anchor = Math.abs(Math.cos(a)) < 0.3 ? "middle" : Math.cos(a) > 0 ? "start" : "end";
|
|
6881
6889
|
const { color: catColor } = gradeFor(cats[i].score, cats[i].max);
|
|
6882
6890
|
labels += `<text x="${lx}" y="${ly - 7}" fill="${catColor}" font-size="12" font-weight="600" font-family="-apple-system,sans-serif" text-anchor="${anchor}" dominant-baseline="middle">${esc(cats[i].shortName)}</text>`;
|
|
6883
6891
|
labels += `<text x="${lx}" y="${ly + 8}" fill="#5A6A7E" font-size="11" font-weight="500" font-family="'SF Mono',Consolas,monospace" text-anchor="${anchor}" dominant-baseline="middle">${cats[i].score}/${cats[i].max} ${cats[i].findings > 0 ? "(" + cats[i].findings + " issues)" : ""}</text>`;
|
|
6884
6892
|
}
|
|
6885
|
-
const radar = `<svg viewBox="0 0
|
|
6893
|
+
const radar = `<svg viewBox="0 0 520 440" style="width:100%;max-width:560px;height:auto;margin:0 auto;display:block">${grid}<polygon points="${pts.join(" ")}" fill="rgba(255,208,0,0.04)" stroke="rgba(255,208,0,0.3)" stroke-width="1.5"/>${dots}${labels}</svg>`;
|
|
6886
6894
|
const weakest = [...cats].sort((a, b) => {
|
|
6887
6895
|
const pctA = a.max > 0 ? a.score / a.max : 1;
|
|
6888
6896
|
const pctB = b.max > 0 ? b.score / b.max : 1;
|
|
@@ -7475,9 +7483,73 @@ th[data-sort]:hover { color: var(--text-primary); }
|
|
|
7475
7483
|
::-webkit-scrollbar-thumb { background: #3A3A3D; }
|
|
7476
7484
|
::-webkit-scrollbar-thumb:hover { background: #555; }
|
|
7477
7485
|
@media (max-width: 768px) {
|
|
7478
|
-
.page { padding:
|
|
7479
|
-
.
|
|
7486
|
+
.page { padding: 16px 10px; }
|
|
7487
|
+
.section { margin-bottom: 28px; }
|
|
7488
|
+
.label { font-size: 10px; letter-spacing: 1.5px; margin-bottom: 10px; }
|
|
7489
|
+
|
|
7490
|
+
/* Score hero: stack vertically */
|
|
7491
|
+
.score-layout { flex-direction: column !important; gap: 16px !important; }
|
|
7492
|
+
.score-layout > div { min-width: 0 !important; width: 100% !important; }
|
|
7493
|
+
|
|
7494
|
+
/* Category bars: stack, remove min-widths */
|
|
7495
|
+
.score-layout [style*="min-width:300px"],
|
|
7496
|
+
.score-layout [style*="min-width:280px"],
|
|
7497
|
+
.score-layout [style*="min-width:200px"],
|
|
7498
|
+
.score-layout [style*="min-width:120px"] {
|
|
7499
|
+
min-width: 0 !important; width: 100% !important;
|
|
7500
|
+
}
|
|
7501
|
+
|
|
7502
|
+
/* Radar chart: constrain to viewport */
|
|
7503
|
+
svg[viewBox] { max-width: 100% !important; }
|
|
7504
|
+
|
|
7505
|
+
/* Strongest/Weakest cards: 2-col, tighter */
|
|
7506
|
+
.section > div[style*="display:flex"][style*="gap:12px"] {
|
|
7507
|
+
gap: 8px !important;
|
|
7508
|
+
}
|
|
7509
|
+
.section > div[style*="display:flex"] > div[style*="min-width:160px"] {
|
|
7510
|
+
min-width: 0 !important; flex: 1 !important;
|
|
7511
|
+
}
|
|
7512
|
+
|
|
7513
|
+
/* Findings list: tighter padding */
|
|
7514
|
+
details > div { padding: 8px 10px !important; }
|
|
7515
|
+
details summary { padding: 10px 12px !important; font-size: 13px !important; }
|
|
7516
|
+
|
|
7517
|
+
/* File ranking table: horizontal scroll */
|
|
7518
|
+
table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; white-space: nowrap; }
|
|
7519
|
+
table th, table td { padding: 5px 8px !important; font-size: 11px !important; }
|
|
7520
|
+
|
|
7521
|
+
/* Code snippets: wrap text */
|
|
7522
|
+
pre, code { white-space: pre-wrap !important; word-break: break-word !important; font-size: 12px !important; }
|
|
7523
|
+
|
|
7524
|
+
/* Drift coherence bars: full width */
|
|
7525
|
+
.section div[style*="display:flex"][style*="gap:40px"] {
|
|
7526
|
+
flex-direction: column !important; gap: 16px !important;
|
|
7527
|
+
}
|
|
7528
|
+
|
|
7529
|
+
/* Summary + AI section: stack */
|
|
7530
|
+
.section div[style*="display:flex"][style*="gap:20px"] {
|
|
7531
|
+
flex-direction: column !important; gap: 12px !important;
|
|
7532
|
+
}
|
|
7533
|
+
|
|
7534
|
+
/* Sticky header: tighter padding */
|
|
7535
|
+
.sticky-header { padding: 0 12px; font-size: 12px; }
|
|
7480
7536
|
.sticky-project { display: none !important; }
|
|
7537
|
+
|
|
7538
|
+
/* Export buttons: smaller */
|
|
7539
|
+
.export-btn { padding: 5px 10px; font-size: 11px; }
|
|
7540
|
+
|
|
7541
|
+
/* Code DNA / ML sections: reduce gap */
|
|
7542
|
+
.section div[style*="gap:10px"] { gap: 6px !important; }
|
|
7543
|
+
|
|
7544
|
+
/* Hide long file paths, truncate */
|
|
7545
|
+
td[data-scroll-to] { max-width: 140px !important; }
|
|
7546
|
+
}
|
|
7547
|
+
@media (max-width: 480px) {
|
|
7548
|
+
.page { padding: 12px 8px; }
|
|
7549
|
+
body { font-size: 13px; }
|
|
7550
|
+
.label { font-size: 9px; }
|
|
7551
|
+
.sticky-header { height: 38px; }
|
|
7552
|
+
table th, table td { padding: 4px 6px !important; font-size: 10px !important; }
|
|
7481
7553
|
}
|
|
7482
7554
|
@media print {
|
|
7483
7555
|
body { background: #fff; color: #111; font-size: 11px; line-height: 1.4; }
|