@vibgrate/cli 2026.710.1 → 2026.711.2
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/DOCS.md +23 -10
- package/README.md +14 -10
- package/dist/baseline-SRVG4ZAD.js +6 -0
- package/dist/{baseline-EONSXRN7.js.map → baseline-SRVG4ZAD.js.map} +1 -1
- package/dist/{chunk-OGRPKYRW.js → chunk-4IHO6VUL.js} +3 -3
- package/dist/{chunk-OGRPKYRW.js.map → chunk-4IHO6VUL.js.map} +1 -1
- package/dist/{chunk-NOHVVB7K.js → chunk-75ZJYYJE.js} +69 -8
- package/dist/chunk-75ZJYYJE.js.map +1 -0
- package/dist/{chunk-ZNCJZVNT.js → chunk-NNU2PW2H.js} +74 -24
- package/dist/chunk-NNU2PW2H.js.map +1 -0
- package/dist/cli.js +321 -10
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/skills/README.md +2 -2
- package/dist/baseline-EONSXRN7.js +0 -6
- package/dist/chunk-NOHVVB7K.js.map +0 -1
- package/dist/chunk-ZNCJZVNT.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { resolveDsn, resolveIngestHost, dashHostForIngestHost, resolveCliInvocation, availableRegionIds, createWorkspaceDsn, writeStoredCredentials, credentialsPath, findGitRoot, ensureGitignored, gitignoreEntryForCredentials, clearStoredCredentials, inventory, uploadScanArtifact, ingestHostForRegionId, dsnCommand, pushCommand, CliError, ExitCode, NPX_INVOCATION, refreshIfStale, driftCount, embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, queryGraphSemantic, queryGraph, unavailableMessage, recordCliCall, CLI_TOOL_ALIASES, countPending, resolveOne, indexFor, countTokens, shortestPath, renderReport, impactOf, testsToRun, detectRunner, defaultGraphPath, serveStdio, ASSISTANTS, usageError, loadGraph, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, coveringTests, FREE_PACK, readScanArtifact, enrichOnline, ECOSYSTEMS, discoverModels, readSavings, readUsage, exportGraph, catalogPath, libDir, stableStringify, buildGraph, writeArtifacts, writeSnapshot, UsageError, ResourceLimitError, epistemicBreakdown, cacheDir, probeFreshness, discover, vibgrateDir, modelCacheInfo, clearModelCache, GraphSource, createServer, assistantById, serializeGraph, resolveLimits, loadCatalog, driftFor, addLibrary, parseDsn as parseDsn$1, publishPrivateLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface, assessDocQuality, symbolsFromApi,
|
|
2
|
+
import { resolveDsn, resolveIngestHost, dashHostForIngestHost, resolveCliInvocation, availableRegionIds, createWorkspaceDsn, writeStoredCredentials, credentialsPath, findGitRoot, ensureGitignored, gitignoreEntryForCredentials, clearStoredCredentials, inventory, uploadScanArtifact, ingestHostForRegionId, dsnCommand, pushCommand, CliError, ExitCode, NPX_INVOCATION, refreshIfStale, driftCount, embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, queryGraphSemantic, queryGraph, unavailableMessage, recordCliCall, CLI_TOOL_ALIASES, countPending, resolveOne, indexFor, countTokens, shortestPath, renderReport, impactOf, testsToRun, detectRunner, defaultGraphPath, serveStdio, ASSISTANTS, usageError, loadGraph, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, coveringTests, FREE_PACK, readScanArtifact, enrichOnline, ECOSYSTEMS, discoverModels, readSavings, readUsage, exportGraph, catalogPath, libDir, stableStringify, buildGraph, writeArtifacts, writeSnapshot, UsageError, ResourceLimitError, epistemicBreakdown, cacheDir, probeFreshness, discover, vibgrateDir, modelCacheInfo, clearModelCache, GraphSource, createServer, assistantById, serializeGraph, resolveLimits, loadCatalog, driftFor, addLibrary, parseDsn as parseDsn$1, publishPrivateLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface, assessDocQuality, symbolsFromApi, fetchHostedDocsCached, selectForBudget, formatForExt, verifyDeterminism, renderHtml, isModelReady } from './chunk-75ZJYYJE.js';
|
|
3
3
|
import { resolvedGrammarFiles, grammarSetVersion, canonicalize } from './chunk-X5YT263H.js';
|
|
4
|
-
import { loadAdvancedScanHook, baselineCommand } from './chunk-
|
|
5
|
-
import { ensureDir, pathExists, writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, VERSION, readJsonFile, writeTextFile, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, brandProgressBar, resolveHead, workingTreeDirty } from './chunk-
|
|
4
|
+
import { loadAdvancedScanHook, baselineCommand } from './chunk-4IHO6VUL.js';
|
|
5
|
+
import { ensureDir, pathExists, writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, VERSION, readJsonFile, writeTextFile, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, PROJECT_TYPE_TO_OSV_ECOSYSTEM, fetchRiskySymbols, brandProgressBar, resolveHead, workingTreeDirty } from './chunk-NNU2PW2H.js';
|
|
6
6
|
import './chunk-RXP66R2E.js';
|
|
7
7
|
import { pathExists as pathExists$1, parseExcludePatterns } from './chunk-GI6W53LM.js';
|
|
8
8
|
import * as fs6 from 'fs';
|
|
@@ -16,8 +16,8 @@ import * as crypto from 'crypto';
|
|
|
16
16
|
import { randomUUID } from 'crypto';
|
|
17
17
|
import chalk8 from 'chalk';
|
|
18
18
|
import * as os from 'os';
|
|
19
|
+
import * as fs12 from 'fs/promises';
|
|
19
20
|
import * as readline from 'readline';
|
|
20
|
-
import * as fs15 from 'fs/promises';
|
|
21
21
|
import semver from 'semver';
|
|
22
22
|
|
|
23
23
|
var DSSE_PAYLOAD_TYPE = "application/vnd.in-toto+json";
|
|
@@ -2535,7 +2535,8 @@ async function showCmd(root, name, budget, asJson, opts = {}) {
|
|
|
2535
2535
|
if (online && !quality.sufficient) {
|
|
2536
2536
|
const dsn = resolveDsn();
|
|
2537
2537
|
const parsed = dsn ? parseDsn$1(dsn) : null;
|
|
2538
|
-
hosted = await
|
|
2538
|
+
hosted = await fetchHostedDocsCached(
|
|
2539
|
+
root,
|
|
2539
2540
|
{ name: displayName, query: name, maxTokens: budget },
|
|
2540
2541
|
{ region: opts.region, ingest: opts.ingest, auth: parsed ? { keyId: parsed.keyId, secret: parsed.secret } : void 0 }
|
|
2541
2542
|
);
|
|
@@ -2674,7 +2675,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
2674
2675
|
console.log(chalk8.green("\u2714") + ` Created ${chalk8.bold("vibgrate.config.ts")}`);
|
|
2675
2676
|
}
|
|
2676
2677
|
if (opts.baseline) {
|
|
2677
|
-
const { runBaseline } = await import('./baseline-
|
|
2678
|
+
const { runBaseline } = await import('./baseline-SRVG4ZAD.js');
|
|
2678
2679
|
await runBaseline(rootDir);
|
|
2679
2680
|
}
|
|
2680
2681
|
console.log("");
|
|
@@ -2683,6 +2684,270 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
2683
2684
|
console.log(` ${chalk8.cyan("vibgrate baseline")} Create a drift baseline`);
|
|
2684
2685
|
console.log("");
|
|
2685
2686
|
});
|
|
2687
|
+
var REACHABILITY_ANALYZER_VERSION = "vg-reach-1.0";
|
|
2688
|
+
var ECOSYSTEM_MATCH_CONFIDENCE = {
|
|
2689
|
+
npm: 0.8,
|
|
2690
|
+
Go: 0.8,
|
|
2691
|
+
"crates.io": 0.7,
|
|
2692
|
+
PyPI: 0.65,
|
|
2693
|
+
RubyGems: 0.6
|
|
2694
|
+
};
|
|
2695
|
+
var GENERIC_SYMBOL_NAMES = /* @__PURE__ */ new Set([
|
|
2696
|
+
"get",
|
|
2697
|
+
"set",
|
|
2698
|
+
"run",
|
|
2699
|
+
"main",
|
|
2700
|
+
"init",
|
|
2701
|
+
"call",
|
|
2702
|
+
"apply",
|
|
2703
|
+
"exec",
|
|
2704
|
+
"send",
|
|
2705
|
+
"open",
|
|
2706
|
+
"read",
|
|
2707
|
+
"write",
|
|
2708
|
+
"parse",
|
|
2709
|
+
"load",
|
|
2710
|
+
"save",
|
|
2711
|
+
"start",
|
|
2712
|
+
"stop",
|
|
2713
|
+
"new",
|
|
2714
|
+
"create",
|
|
2715
|
+
"update",
|
|
2716
|
+
"delete",
|
|
2717
|
+
"handle",
|
|
2718
|
+
"process",
|
|
2719
|
+
"render",
|
|
2720
|
+
"value",
|
|
2721
|
+
"data",
|
|
2722
|
+
"next"
|
|
2723
|
+
]);
|
|
2724
|
+
var MAX_FILES_PER_PACKAGE = 25;
|
|
2725
|
+
var MAX_FILE_BYTES = 256 * 1024;
|
|
2726
|
+
var MAX_MODULES_PER_PACKAGE = 50;
|
|
2727
|
+
var MAX_IMPORTED_MODULE_PACKAGES = 500;
|
|
2728
|
+
var MAX_FINDINGS = 1e3;
|
|
2729
|
+
function normalizePython(name) {
|
|
2730
|
+
return name.toLowerCase().replace(/[-.]/g, "_");
|
|
2731
|
+
}
|
|
2732
|
+
function specifierMatchesPackage(ecosystem, pkg, spec) {
|
|
2733
|
+
switch (ecosystem) {
|
|
2734
|
+
case "npm":
|
|
2735
|
+
case "Go":
|
|
2736
|
+
case "RubyGems":
|
|
2737
|
+
return spec === pkg || spec.startsWith(`${pkg}/`);
|
|
2738
|
+
case "PyPI": {
|
|
2739
|
+
const top = spec.split(".")[0] ?? spec;
|
|
2740
|
+
return normalizePython(top) === normalizePython(pkg);
|
|
2741
|
+
}
|
|
2742
|
+
case "crates.io": {
|
|
2743
|
+
const top = (spec.split("::")[0] ?? spec).toLowerCase().replace(/-/g, "_");
|
|
2744
|
+
return top === pkg.toLowerCase().replace(/-/g, "_");
|
|
2745
|
+
}
|
|
2746
|
+
default:
|
|
2747
|
+
return false;
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
function symbolIdentifier(symbol) {
|
|
2751
|
+
const cleaned = symbol.replace(/\(.*\)$/, "");
|
|
2752
|
+
const segments = cleaned.split(/[./:#]|::/).filter(Boolean);
|
|
2753
|
+
return segments[segments.length - 1] ?? cleaned;
|
|
2754
|
+
}
|
|
2755
|
+
function isMatchableIdentifier(identifier) {
|
|
2756
|
+
return identifier.length >= 3 && !GENERIC_SYMBOL_NAMES.has(identifier.toLowerCase());
|
|
2757
|
+
}
|
|
2758
|
+
function collectPackageImports(graph, ecosystem, pkg) {
|
|
2759
|
+
if (!(ecosystem in ECOSYSTEM_MATCH_CONFIDENCE)) {
|
|
2760
|
+
return { supported: false, modules: [], importingFiles: [] };
|
|
2761
|
+
}
|
|
2762
|
+
const idx = indexFor(graph);
|
|
2763
|
+
const modules = /* @__PURE__ */ new Set();
|
|
2764
|
+
const files = /* @__PURE__ */ new Set();
|
|
2765
|
+
for (const node of graph.nodes) {
|
|
2766
|
+
if (node.kind !== "external") continue;
|
|
2767
|
+
if (!specifierMatchesPackage(ecosystem, pkg, node.name)) continue;
|
|
2768
|
+
modules.add(node.name);
|
|
2769
|
+
for (const edge of idx.in(node.id, "import")) {
|
|
2770
|
+
const src = idx.nodeById.get(edge.src);
|
|
2771
|
+
if (src?.file) files.add(src.file);
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
return {
|
|
2775
|
+
supported: true,
|
|
2776
|
+
modules: [...modules].sort(),
|
|
2777
|
+
importingFiles: [...files].sort()
|
|
2778
|
+
};
|
|
2779
|
+
}
|
|
2780
|
+
async function fileReferencesIdentifier(rootDir, relFile, identifier, readFile3) {
|
|
2781
|
+
const content = await readFile3(path.join(rootDir, relFile));
|
|
2782
|
+
if (!content) return false;
|
|
2783
|
+
const escaped = identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2784
|
+
return new RegExp(`\\b${escaped}\\b`).test(content);
|
|
2785
|
+
}
|
|
2786
|
+
async function defaultReadFile(absPath) {
|
|
2787
|
+
try {
|
|
2788
|
+
const stat2 = await fs12.stat(absPath);
|
|
2789
|
+
if (!stat2.isFile() || stat2.size > MAX_FILE_BYTES) return null;
|
|
2790
|
+
return await fs12.readFile(absPath, "utf8");
|
|
2791
|
+
} catch {
|
|
2792
|
+
return null;
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
async function analyzeReachability(input) {
|
|
2796
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2797
|
+
const readFile3 = input.readFile ?? defaultReadFile;
|
|
2798
|
+
if (!input.graph) {
|
|
2799
|
+
return {
|
|
2800
|
+
analyzerVersion: REACHABILITY_ANALYZER_VERSION,
|
|
2801
|
+
source: "none",
|
|
2802
|
+
generatedAt,
|
|
2803
|
+
manifestAdvisoryCount: input.manifest.length,
|
|
2804
|
+
findings: input.manifest.slice(0, MAX_FINDINGS).map((entry) => ({
|
|
2805
|
+
advisoryId: entry.advisoryId,
|
|
2806
|
+
ecosystem: entry.ecosystem,
|
|
2807
|
+
package: entry.package,
|
|
2808
|
+
version: entry.version,
|
|
2809
|
+
tier: "unknown",
|
|
2810
|
+
evidence: "no code map available for this scan",
|
|
2811
|
+
graphConfidence: 0
|
|
2812
|
+
}))
|
|
2813
|
+
};
|
|
2814
|
+
}
|
|
2815
|
+
const evidenceByPackage = /* @__PURE__ */ new Map();
|
|
2816
|
+
const importedModules = [];
|
|
2817
|
+
const seenPackages = /* @__PURE__ */ new Set();
|
|
2818
|
+
for (const dep of input.dependencies) {
|
|
2819
|
+
const key = `${dep.ecosystem}|${dep.package}`;
|
|
2820
|
+
if (seenPackages.has(key)) continue;
|
|
2821
|
+
seenPackages.add(key);
|
|
2822
|
+
const evidence = collectPackageImports(input.graph, dep.ecosystem, dep.package);
|
|
2823
|
+
evidenceByPackage.set(key, evidence);
|
|
2824
|
+
if (evidence.supported && importedModules.length < MAX_IMPORTED_MODULE_PACKAGES) {
|
|
2825
|
+
importedModules.push({
|
|
2826
|
+
ecosystem: dep.ecosystem,
|
|
2827
|
+
package: dep.package,
|
|
2828
|
+
modules: evidence.modules.slice(0, MAX_MODULES_PER_PACKAGE),
|
|
2829
|
+
importingFiles: evidence.importingFiles.length
|
|
2830
|
+
});
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
importedModules.sort(
|
|
2834
|
+
(a, b) => a.ecosystem.localeCompare(b.ecosystem) || a.package.localeCompare(b.package)
|
|
2835
|
+
);
|
|
2836
|
+
const findings = [];
|
|
2837
|
+
for (const entry of input.manifest) {
|
|
2838
|
+
const key = `${entry.ecosystem}|${entry.package}`;
|
|
2839
|
+
const evidence = evidenceByPackage.get(key) ?? collectPackageImports(input.graph, entry.ecosystem, entry.package);
|
|
2840
|
+
const baseConfidence = ECOSYSTEM_MATCH_CONFIDENCE[entry.ecosystem] ?? 0;
|
|
2841
|
+
if (!evidence.supported) {
|
|
2842
|
+
findings.push({
|
|
2843
|
+
advisoryId: entry.advisoryId,
|
|
2844
|
+
ecosystem: entry.ecosystem,
|
|
2845
|
+
package: entry.package,
|
|
2846
|
+
version: entry.version,
|
|
2847
|
+
tier: "unknown",
|
|
2848
|
+
evidence: `import matching is not supported for the ${entry.ecosystem} ecosystem yet`,
|
|
2849
|
+
graphConfidence: 0
|
|
2850
|
+
});
|
|
2851
|
+
continue;
|
|
2852
|
+
}
|
|
2853
|
+
if (evidence.importingFiles.length === 0) {
|
|
2854
|
+
findings.push({
|
|
2855
|
+
advisoryId: entry.advisoryId,
|
|
2856
|
+
ecosystem: entry.ecosystem,
|
|
2857
|
+
package: entry.package,
|
|
2858
|
+
version: entry.version,
|
|
2859
|
+
tier: "not_reached",
|
|
2860
|
+
evidence: `no import of ${entry.package} was found in the code map`,
|
|
2861
|
+
graphConfidence: baseConfidence
|
|
2862
|
+
});
|
|
2863
|
+
continue;
|
|
2864
|
+
}
|
|
2865
|
+
const matchableSymbols = entry.symbols.map((s) => ({ symbol: s.symbol, identifier: symbolIdentifier(s.symbol) })).filter((s) => isMatchableIdentifier(s.identifier));
|
|
2866
|
+
if (entry.symbolCoverage !== "function" || matchableSymbols.length === 0) {
|
|
2867
|
+
findings.push({
|
|
2868
|
+
advisoryId: entry.advisoryId,
|
|
2869
|
+
ecosystem: entry.ecosystem,
|
|
2870
|
+
package: entry.package,
|
|
2871
|
+
version: entry.version,
|
|
2872
|
+
tier: "potentially_reachable",
|
|
2873
|
+
evidence: `${entry.package} is imported by ${evidence.importingFiles.length} file${evidence.importingFiles.length === 1 ? "" : "s"}; vulnerable symbols unresolved (coverage: ${entry.symbolCoverage})`,
|
|
2874
|
+
callPath: evidence.importingFiles.slice(0, 10),
|
|
2875
|
+
graphConfidence: Math.min(baseConfidence, 0.6)
|
|
2876
|
+
});
|
|
2877
|
+
continue;
|
|
2878
|
+
}
|
|
2879
|
+
const filesToCheck = evidence.importingFiles.slice(0, MAX_FILES_PER_PACKAGE);
|
|
2880
|
+
let anySymbolFound = false;
|
|
2881
|
+
for (const { symbol, identifier } of matchableSymbols) {
|
|
2882
|
+
const referencingFiles = [];
|
|
2883
|
+
for (const relFile of filesToCheck) {
|
|
2884
|
+
if (await fileReferencesIdentifier(input.rootDir, relFile, identifier, readFile3)) {
|
|
2885
|
+
referencingFiles.push(relFile);
|
|
2886
|
+
if (referencingFiles.length >= 5) break;
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
if (referencingFiles.length > 0) {
|
|
2890
|
+
anySymbolFound = true;
|
|
2891
|
+
findings.push({
|
|
2892
|
+
advisoryId: entry.advisoryId,
|
|
2893
|
+
ecosystem: entry.ecosystem,
|
|
2894
|
+
package: entry.package,
|
|
2895
|
+
version: entry.version,
|
|
2896
|
+
symbol,
|
|
2897
|
+
tier: "reachable",
|
|
2898
|
+
evidence: `vulnerable symbol \`${identifier}\` is referenced in ${referencingFiles.length} importing file${referencingFiles.length === 1 ? "" : "s"}`,
|
|
2899
|
+
callPath: referencingFiles,
|
|
2900
|
+
graphConfidence: Math.min(baseConfidence, 0.75)
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
if (!anySymbolFound) {
|
|
2905
|
+
findings.push({
|
|
2906
|
+
advisoryId: entry.advisoryId,
|
|
2907
|
+
ecosystem: entry.ecosystem,
|
|
2908
|
+
package: entry.package,
|
|
2909
|
+
version: entry.version,
|
|
2910
|
+
tier: "not_reached",
|
|
2911
|
+
evidence: `${entry.package} is imported, but none of its ${matchableSymbols.length} vulnerable symbol${matchableSymbols.length === 1 ? "" : "s"} is referenced in the ${filesToCheck.length} importing file${filesToCheck.length === 1 ? "" : "s"} checked`,
|
|
2912
|
+
callPath: evidence.importingFiles.slice(0, 10),
|
|
2913
|
+
graphConfidence: Math.min(baseConfidence, 0.55)
|
|
2914
|
+
});
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
findings.sort(
|
|
2918
|
+
(a, b) => a.ecosystem.localeCompare(b.ecosystem) || a.package.localeCompare(b.package) || a.advisoryId.localeCompare(b.advisoryId) || (a.symbol ?? "").localeCompare(b.symbol ?? "")
|
|
2919
|
+
);
|
|
2920
|
+
return {
|
|
2921
|
+
analyzerVersion: REACHABILITY_ANALYZER_VERSION,
|
|
2922
|
+
source: "graph",
|
|
2923
|
+
generatedAt,
|
|
2924
|
+
manifestAdvisoryCount: input.manifest.length,
|
|
2925
|
+
importedModules,
|
|
2926
|
+
findings: findings.slice(0, MAX_FINDINGS)
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2929
|
+
function collectPreflightDependencies(projects, projectTypeToEcosystem) {
|
|
2930
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
2931
|
+
for (const project of projects) {
|
|
2932
|
+
const ecosystem = projectTypeToEcosystem[project.type];
|
|
2933
|
+
if (!ecosystem) continue;
|
|
2934
|
+
for (const dep of project.dependencies) {
|
|
2935
|
+
const version = dep.resolvedVersion || normalizeVersionSpec(dep.currentSpec);
|
|
2936
|
+
if (!version) continue;
|
|
2937
|
+
const key = `${ecosystem}:${dep.package}:${version}`;
|
|
2938
|
+
if (!deduped.has(key)) {
|
|
2939
|
+
deduped.set(key, { ecosystem, package: dep.package, version });
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
return [...deduped.values()].sort(
|
|
2944
|
+
(a, b) => a.ecosystem.localeCompare(b.ecosystem) || a.package.localeCompare(b.package) || a.version.localeCompare(b.version)
|
|
2945
|
+
);
|
|
2946
|
+
}
|
|
2947
|
+
function normalizeVersionSpec(spec) {
|
|
2948
|
+
const match = spec.trim().match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/);
|
|
2949
|
+
return match?.[0] ?? null;
|
|
2950
|
+
}
|
|
2686
2951
|
|
|
2687
2952
|
// src/reporting/utils/ingest-id-output.ts
|
|
2688
2953
|
function emitIngestIdLine(ingestId, options) {
|
|
@@ -2738,6 +3003,47 @@ function printAiContextPrompt(detectedAssistant) {
|
|
|
2738
3003
|
function shouldBuildCodeMap(opts) {
|
|
2739
3004
|
return opts.graph !== false && !opts.maxPrivacy && !opts.noLocalArtifacts;
|
|
2740
3005
|
}
|
|
3006
|
+
async function attachReachability(artifact, rootDir, opts, graph) {
|
|
3007
|
+
try {
|
|
3008
|
+
const dsn = resolveDsn(opts.dsn);
|
|
3009
|
+
if (!dsn) return;
|
|
3010
|
+
const parsed = parseDsn(dsn);
|
|
3011
|
+
if (!parsed) return;
|
|
3012
|
+
const ingestHost = opts.region ? resolveIngestHost(opts.region) : parsed.host;
|
|
3013
|
+
const dependencies = collectPreflightDependencies(artifact.projects, PROJECT_TYPE_TO_OSV_ECOSYSTEM);
|
|
3014
|
+
if (dependencies.length === 0) return;
|
|
3015
|
+
const manifest = await fetchRiskySymbols(parsed, ingestHost, dependencies);
|
|
3016
|
+
if (manifest.status !== "ok" || !manifest.advisories) return;
|
|
3017
|
+
artifact.reachability = await analyzeReachability({
|
|
3018
|
+
graph,
|
|
3019
|
+
rootDir,
|
|
3020
|
+
manifest: manifest.advisories,
|
|
3021
|
+
dependencies
|
|
3022
|
+
});
|
|
3023
|
+
if (!opts.quiet && manifest.advisories.length > 0) {
|
|
3024
|
+
const tiers = artifact.reachability.findings.reduce((acc, f) => {
|
|
3025
|
+
acc[f.tier] = (acc[f.tier] ?? 0) + 1;
|
|
3026
|
+
return acc;
|
|
3027
|
+
}, {});
|
|
3028
|
+
const parts = [
|
|
3029
|
+
tiers.reachable ? `${tiers.reachable} reachable` : null,
|
|
3030
|
+
tiers.potentially_reachable ? `${tiers.potentially_reachable} potentially reachable` : null,
|
|
3031
|
+
tiers.not_reached ? `${tiers.not_reached} not reached` : null,
|
|
3032
|
+
tiers.unknown ? `${tiers.unknown} unknown` : null
|
|
3033
|
+
].filter(Boolean);
|
|
3034
|
+
console.log(
|
|
3035
|
+
chalk8.dim(
|
|
3036
|
+
`Reachability: ${manifest.advisories.length} advisor${manifest.advisories.length === 1 ? "y" : "ies"} checked against the code map \u2014 ${parts.join(", ")}`
|
|
3037
|
+
)
|
|
3038
|
+
);
|
|
3039
|
+
}
|
|
3040
|
+
} catch (e) {
|
|
3041
|
+
if (!opts.quiet) {
|
|
3042
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
3043
|
+
console.log(chalk8.dim(`Reachability check skipped: ${msg}`));
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
2741
3047
|
async function autoPush(artifact, rootDir, opts) {
|
|
2742
3048
|
const dsn = resolveDsn(opts.dsn);
|
|
2743
3049
|
if (!dsn) {
|
|
@@ -2987,6 +3293,7 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
|
|
|
2987
3293
|
invocation: resolveCliInvocation()
|
|
2988
3294
|
};
|
|
2989
3295
|
const wantGraph = shouldBuildCodeMap({ graph: opts.graph, maxPrivacy: opts.maxPrivacy, noLocalArtifacts });
|
|
3296
|
+
let builtGraph = null;
|
|
2990
3297
|
if (wantGraph) {
|
|
2991
3298
|
scanOpts.postScan = async (report) => {
|
|
2992
3299
|
const result = await buildGraph({
|
|
@@ -2994,6 +3301,7 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
|
|
|
2994
3301
|
exclude: opts.exclude,
|
|
2995
3302
|
onParseProgress: (done, total) => report(done, total, "parsing")
|
|
2996
3303
|
});
|
|
3304
|
+
builtGraph = result.graph;
|
|
2997
3305
|
writeArtifacts(result.graph, { root: rootDir });
|
|
2998
3306
|
writeSnapshot(rootDir, result.graph.provenance.corpusHash, result.fileStats, {
|
|
2999
3307
|
exclude: opts.exclude
|
|
@@ -3043,6 +3351,9 @@ Failing fitness function: drift worsened by ${worseningPercent.toFixed(2)}% (thr
|
|
|
3043
3351
|
}
|
|
3044
3352
|
}
|
|
3045
3353
|
}
|
|
3354
|
+
if (willPush && hasDsn && builtGraph) {
|
|
3355
|
+
await attachReachability(artifact, rootDir, scanOpts, builtGraph);
|
|
3356
|
+
}
|
|
3046
3357
|
if (willPush) {
|
|
3047
3358
|
await autoPush(artifact, rootDir, scanOpts);
|
|
3048
3359
|
}
|
|
@@ -3229,7 +3540,7 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
|
3229
3540
|
]);
|
|
3230
3541
|
var MAX_FILES = 8e3;
|
|
3231
3542
|
var MAX_DEPTH = 12;
|
|
3232
|
-
var
|
|
3543
|
+
var MAX_FILE_BYTES2 = 1048576;
|
|
3233
3544
|
var EXT_ECOSYSTEM = {
|
|
3234
3545
|
".ts": "npm",
|
|
3235
3546
|
".tsx": "npm",
|
|
@@ -3294,7 +3605,7 @@ function analyzeTree(root, packages) {
|
|
|
3294
3605
|
const full = path.join(dir, entry.name);
|
|
3295
3606
|
let content;
|
|
3296
3607
|
try {
|
|
3297
|
-
if (fs6.statSync(full).size >
|
|
3608
|
+
if (fs6.statSync(full).size > MAX_FILE_BYTES2) continue;
|
|
3298
3609
|
content = fs6.readFileSync(full, "utf8");
|
|
3299
3610
|
} catch {
|
|
3300
3611
|
continue;
|
|
@@ -3629,8 +3940,8 @@ async function fetchLatestVersion() {
|
|
|
3629
3940
|
}
|
|
3630
3941
|
async function writeCache(data) {
|
|
3631
3942
|
try {
|
|
3632
|
-
await
|
|
3633
|
-
await
|
|
3943
|
+
await fs12.mkdir(CACHE_DIR, { recursive: true });
|
|
3944
|
+
await fs12.writeFile(CACHE_FILE, JSON.stringify(data), "utf-8");
|
|
3634
3945
|
} catch {
|
|
3635
3946
|
}
|
|
3636
3947
|
}
|