verimu 0.0.17 → 0.0.19
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 +15 -13
- package/dist/cli.mjs +1120 -95
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +1081 -83
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1075 -77
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -13057,9 +13057,9 @@ var require_traverse = __commonJS({
|
|
|
13057
13057
|
Object.defineProperty(exports2, "__esModule", {
|
|
13058
13058
|
value: true
|
|
13059
13059
|
});
|
|
13060
|
-
exports2.default =
|
|
13060
|
+
exports2.default = traverse;
|
|
13061
13061
|
var _index = require_definitions();
|
|
13062
|
-
function
|
|
13062
|
+
function traverse(node, handlers, state) {
|
|
13063
13063
|
if (typeof handlers === "function") {
|
|
13064
13064
|
handlers = {
|
|
13065
13065
|
enter: handlers
|
|
@@ -14374,8 +14374,8 @@ function sanitizeTagId(value) {
|
|
|
14374
14374
|
}
|
|
14375
14375
|
|
|
14376
14376
|
// src/scan.ts
|
|
14377
|
-
var
|
|
14378
|
-
var
|
|
14377
|
+
var import_promises16 = require("fs/promises");
|
|
14378
|
+
var import_path17 = require("path");
|
|
14379
14379
|
|
|
14380
14380
|
// src/scanners/npm/npm-scanner.ts
|
|
14381
14381
|
var import_promises = require("fs/promises");
|
|
@@ -16997,6 +16997,15 @@ var ConsoleReporter = class {
|
|
|
16997
16997
|
lines.push(
|
|
16998
16998
|
` Findings: direct_evidence=${directEvidence}, indirect_no_evidence=${indirectNoEvidence}, unsupported=${unsupported}, analysis_error=${analysisErrors}`
|
|
16999
16999
|
);
|
|
17000
|
+
if (result.usageContext.ecosystemStatus.length > 0) {
|
|
17001
|
+
lines.push(" Analyzer status:");
|
|
17002
|
+
for (const status of result.usageContext.ecosystemStatus) {
|
|
17003
|
+
const note = status.note ? ` (${status.note})` : "";
|
|
17004
|
+
lines.push(
|
|
17005
|
+
` ${status.ecosystem}: ${status.status} via ${status.analyzer}${note}`
|
|
17006
|
+
);
|
|
17007
|
+
}
|
|
17008
|
+
}
|
|
17000
17009
|
if (result.usageContext.artifactPath) {
|
|
17001
17010
|
lines.push(` Artifact: ${result.usageContext.artifactPath}`);
|
|
17002
17011
|
}
|
|
@@ -17278,6 +17287,24 @@ var JsAstAnalyzer = class {
|
|
|
17278
17287
|
return this.ecosystems.has(ecosystem);
|
|
17279
17288
|
}
|
|
17280
17289
|
async analyze(context) {
|
|
17290
|
+
const traverseFn = resolveTraverseFunction(import_traverse.default);
|
|
17291
|
+
if (!traverseFn) {
|
|
17292
|
+
return {
|
|
17293
|
+
packages: context.packages.map((pkg) => ({
|
|
17294
|
+
packageName: pkg.packageName,
|
|
17295
|
+
ecosystem: pkg.ecosystem,
|
|
17296
|
+
status: "analysis_error",
|
|
17297
|
+
snippets: [],
|
|
17298
|
+
notes: "Failed to resolve @babel/traverse runtime export"
|
|
17299
|
+
})),
|
|
17300
|
+
errors: [{
|
|
17301
|
+
analyzer: this.name,
|
|
17302
|
+
ecosystem: context.ecosystem,
|
|
17303
|
+
error: "Failed to resolve @babel/traverse runtime export"
|
|
17304
|
+
}],
|
|
17305
|
+
snippetsProduced: 0
|
|
17306
|
+
};
|
|
17307
|
+
}
|
|
17281
17308
|
const packageMap = this.buildPackageMaps(context.packages);
|
|
17282
17309
|
const resultMap = /* @__PURE__ */ new Map();
|
|
17283
17310
|
const snippetKeyMap = /* @__PURE__ */ new Map();
|
|
@@ -17341,13 +17368,13 @@ var JsAstAnalyzer = class {
|
|
|
17341
17368
|
const matchCandidates = [];
|
|
17342
17369
|
const matchSeen = /* @__PURE__ */ new Set();
|
|
17343
17370
|
const symbolToPackage = /* @__PURE__ */ new Map();
|
|
17344
|
-
const addMatch = (
|
|
17345
|
-
const candidateKey = `${
|
|
17371
|
+
const addMatch = (packageKey3, line, matchKind, calledSymbol, confidence = 0.8) => {
|
|
17372
|
+
const candidateKey = `${packageKey3}:${line}:${matchKind}:${calledSymbol ?? ""}`;
|
|
17346
17373
|
if (matchSeen.has(candidateKey)) return;
|
|
17347
17374
|
matchSeen.add(candidateKey);
|
|
17348
|
-
matchCandidates.push({ packageKey:
|
|
17375
|
+
matchCandidates.push({ packageKey: packageKey3, line, matchKind, calledSymbol, confidence });
|
|
17349
17376
|
};
|
|
17350
|
-
(
|
|
17377
|
+
traverseFn(ast, {
|
|
17351
17378
|
ImportDeclaration: (path14) => {
|
|
17352
17379
|
const source = path14.node.source;
|
|
17353
17380
|
if (!(0, import_types.isStringLiteral)(source)) return;
|
|
@@ -17469,6 +17496,18 @@ var JsAstAnalyzer = class {
|
|
|
17469
17496
|
return `${ecosystem}::${packageName}`;
|
|
17470
17497
|
}
|
|
17471
17498
|
};
|
|
17499
|
+
function resolveTraverseFunction(moduleValue) {
|
|
17500
|
+
if (typeof moduleValue === "function") {
|
|
17501
|
+
return moduleValue;
|
|
17502
|
+
}
|
|
17503
|
+
if (typeof moduleValue === "object" && moduleValue !== null && "default" in moduleValue) {
|
|
17504
|
+
const candidate = moduleValue.default;
|
|
17505
|
+
if (typeof candidate === "function") {
|
|
17506
|
+
return candidate;
|
|
17507
|
+
}
|
|
17508
|
+
}
|
|
17509
|
+
return null;
|
|
17510
|
+
}
|
|
17472
17511
|
async function collectSourceFiles(rootPath) {
|
|
17473
17512
|
const files = [];
|
|
17474
17513
|
async function walk(dirPath) {
|
|
@@ -17564,9 +17603,9 @@ function collectIdentifiers(pattern) {
|
|
|
17564
17603
|
function resolveCallMatch(callee, symbolToPackage) {
|
|
17565
17604
|
const normalized = unwrapExpression(callee);
|
|
17566
17605
|
if ((0, import_types.isIdentifier)(normalized)) {
|
|
17567
|
-
const
|
|
17568
|
-
if (!
|
|
17569
|
-
return { packageKey:
|
|
17606
|
+
const packageKey3 = symbolToPackage.get(normalized.name);
|
|
17607
|
+
if (!packageKey3) return null;
|
|
17608
|
+
return { packageKey: packageKey3, calledSymbol: normalized.name };
|
|
17570
17609
|
}
|
|
17571
17610
|
if ((0, import_types.isMemberExpression)(normalized) || (0, import_types.isOptionalMemberExpression)(normalized)) {
|
|
17572
17611
|
return resolveMemberCallMatch(normalized, symbolToPackage);
|
|
@@ -17583,13 +17622,13 @@ function unwrapExpression(expression) {
|
|
|
17583
17622
|
function resolveMemberCallMatch(memberExpression, symbolToPackage) {
|
|
17584
17623
|
const objectExpr = unwrapExpression(memberExpression.object);
|
|
17585
17624
|
if (!(0, import_types.isIdentifier)(objectExpr)) return null;
|
|
17586
|
-
const
|
|
17587
|
-
if (!
|
|
17625
|
+
const packageKey3 = symbolToPackage.get(objectExpr.name);
|
|
17626
|
+
if (!packageKey3) return null;
|
|
17588
17627
|
const propertyName = propertyNameOf(memberExpression);
|
|
17589
17628
|
if (!propertyName) {
|
|
17590
|
-
return { packageKey:
|
|
17629
|
+
return { packageKey: packageKey3, calledSymbol: objectExpr.name };
|
|
17591
17630
|
}
|
|
17592
|
-
return { packageKey:
|
|
17631
|
+
return { packageKey: packageKey3, calledSymbol: `${objectExpr.name}.${propertyName}` };
|
|
17593
17632
|
}
|
|
17594
17633
|
function propertyNameOf(memberExpression) {
|
|
17595
17634
|
if (memberExpression.computed) {
|
|
@@ -17606,34 +17645,1021 @@ function propertyNameOf(memberExpression) {
|
|
|
17606
17645
|
return null;
|
|
17607
17646
|
}
|
|
17608
17647
|
|
|
17609
|
-
// src/context/analyzers/
|
|
17610
|
-
var
|
|
17611
|
-
|
|
17612
|
-
|
|
17613
|
-
|
|
17614
|
-
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
17648
|
+
// src/context/analyzers/shared.ts
|
|
17649
|
+
var import_promises15 = require("fs/promises");
|
|
17650
|
+
var import_path16 = require("path");
|
|
17651
|
+
var DEFAULT_IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
17652
|
+
".git",
|
|
17653
|
+
".hg",
|
|
17654
|
+
".svn",
|
|
17655
|
+
"node_modules",
|
|
17656
|
+
"dist",
|
|
17657
|
+
"build",
|
|
17658
|
+
"coverage",
|
|
17659
|
+
".next",
|
|
17660
|
+
".nuxt",
|
|
17661
|
+
".turbo",
|
|
17662
|
+
"vendor",
|
|
17663
|
+
".venv",
|
|
17664
|
+
"venv",
|
|
17665
|
+
"target",
|
|
17666
|
+
"bin",
|
|
17667
|
+
"obj"
|
|
17668
|
+
]);
|
|
17669
|
+
function packageKey(ecosystem, packageName) {
|
|
17670
|
+
return `${ecosystem}::${packageName}`;
|
|
17671
|
+
}
|
|
17672
|
+
function initState(packages) {
|
|
17673
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
17674
|
+
const snippetKeyMap = /* @__PURE__ */ new Map();
|
|
17675
|
+
for (const pkg of packages) {
|
|
17676
|
+
const key = packageKey(pkg.ecosystem, pkg.packageName);
|
|
17677
|
+
resultMap.set(key, {
|
|
17678
|
+
packageName: pkg.packageName,
|
|
17679
|
+
ecosystem: pkg.ecosystem,
|
|
17680
|
+
status: "indirect_no_evidence",
|
|
17681
|
+
snippets: []
|
|
17682
|
+
});
|
|
17683
|
+
snippetKeyMap.set(key, /* @__PURE__ */ new Set());
|
|
17684
|
+
}
|
|
17685
|
+
return {
|
|
17686
|
+
resultMap,
|
|
17687
|
+
snippetKeyMap,
|
|
17688
|
+
errors: [],
|
|
17689
|
+
snippetsProduced: 0
|
|
17690
|
+
};
|
|
17691
|
+
}
|
|
17692
|
+
function errorResultFromMessage(context, analyzerName, message, notes) {
|
|
17693
|
+
return {
|
|
17694
|
+
packages: context.packages.map((pkg) => ({
|
|
17695
|
+
packageName: pkg.packageName,
|
|
17696
|
+
ecosystem: pkg.ecosystem,
|
|
17697
|
+
status: "analysis_error",
|
|
17698
|
+
snippets: [],
|
|
17699
|
+
notes
|
|
17700
|
+
})),
|
|
17701
|
+
errors: [{ analyzer: analyzerName, ecosystem: context.ecosystem, error: message }],
|
|
17702
|
+
snippetsProduced: 0
|
|
17703
|
+
};
|
|
17704
|
+
}
|
|
17705
|
+
function toAnalyzerResult(state) {
|
|
17706
|
+
for (const result of state.resultMap.values()) {
|
|
17707
|
+
result.snippets = dedupeSnippets(result.snippets);
|
|
17708
|
+
if (result.snippets.length > 0) {
|
|
17709
|
+
result.status = "direct_evidence";
|
|
17710
|
+
}
|
|
17711
|
+
}
|
|
17712
|
+
return {
|
|
17713
|
+
packages: Array.from(state.resultMap.values()),
|
|
17714
|
+
errors: state.errors,
|
|
17715
|
+
snippetsProduced: state.snippetsProduced
|
|
17716
|
+
};
|
|
17717
|
+
}
|
|
17718
|
+
async function collectSourceFiles2(rootPath, extensions, ignoredDirs = DEFAULT_IGNORED_DIRS) {
|
|
17719
|
+
const files = [];
|
|
17720
|
+
async function walk(dirPath) {
|
|
17721
|
+
const entries = await (0, import_promises15.readdir)(dirPath, { withFileTypes: true });
|
|
17722
|
+
for (const entry of entries) {
|
|
17723
|
+
const fullPath = (0, import_path16.join)(dirPath, entry.name);
|
|
17724
|
+
if (entry.isDirectory()) {
|
|
17725
|
+
if (ignoredDirs.has(entry.name)) continue;
|
|
17726
|
+
await walk(fullPath);
|
|
17727
|
+
continue;
|
|
17728
|
+
}
|
|
17729
|
+
if (!entry.isFile()) continue;
|
|
17730
|
+
if (!extensions.has(extensionOf2(entry.name))) continue;
|
|
17731
|
+
files.push(fullPath);
|
|
17732
|
+
}
|
|
17618
17733
|
}
|
|
17734
|
+
await walk(rootPath);
|
|
17735
|
+
return files;
|
|
17736
|
+
}
|
|
17737
|
+
async function readSourceFile(analyzerName, ecosystem, filePath, errors) {
|
|
17738
|
+
try {
|
|
17739
|
+
return await (0, import_promises15.readFile)(filePath, "utf-8");
|
|
17740
|
+
} catch (err) {
|
|
17741
|
+
errors.push({
|
|
17742
|
+
analyzer: analyzerName,
|
|
17743
|
+
ecosystem,
|
|
17744
|
+
error: `Failed to read ${filePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
17745
|
+
});
|
|
17746
|
+
return null;
|
|
17747
|
+
}
|
|
17748
|
+
}
|
|
17749
|
+
function addCandidate(context, state, filePath, sourceText, candidate) {
|
|
17750
|
+
if (state.snippetsProduced >= context.maxSnippetsTotal) return;
|
|
17751
|
+
const packageResult = state.resultMap.get(candidate.packageKey);
|
|
17752
|
+
if (!packageResult) return;
|
|
17753
|
+
if (packageResult.snippets.length >= context.maxSnippetsPerPackage) return;
|
|
17754
|
+
const snippet = buildSnippet({
|
|
17755
|
+
projectPath: context.projectPath,
|
|
17756
|
+
filePath,
|
|
17757
|
+
sourceText,
|
|
17758
|
+
line: candidate.line,
|
|
17759
|
+
numContextLines: context.numContextLines,
|
|
17760
|
+
matchKind: candidate.matchKind,
|
|
17761
|
+
calledSymbol: candidate.calledSymbol,
|
|
17762
|
+
confidence: candidate.confidence ?? 0.8
|
|
17763
|
+
});
|
|
17764
|
+
const dedupeKey = `${snippet.filePath}:${snippet.startLine}:${snippet.endLine}:${snippet.matchKind}:${snippet.calledSymbol ?? ""}`;
|
|
17765
|
+
const packageSnippetKeys = state.snippetKeyMap.get(candidate.packageKey);
|
|
17766
|
+
if (!packageSnippetKeys || packageSnippetKeys.has(dedupeKey)) return;
|
|
17767
|
+
packageSnippetKeys.add(dedupeKey);
|
|
17768
|
+
packageResult.snippets.push(snippet);
|
|
17769
|
+
state.snippetsProduced += 1;
|
|
17770
|
+
}
|
|
17771
|
+
function extensionOf2(fileName) {
|
|
17772
|
+
const index = fileName.lastIndexOf(".");
|
|
17773
|
+
return index === -1 ? "" : fileName.slice(index).toLowerCase();
|
|
17774
|
+
}
|
|
17775
|
+
function basePackageName(name) {
|
|
17776
|
+
const slash = name.includes("/") ? name.split("/").at(-1) ?? name : name;
|
|
17777
|
+
const colon = slash.includes(":") ? slash.split(":").at(-1) ?? slash : slash;
|
|
17778
|
+
return colon;
|
|
17779
|
+
}
|
|
17780
|
+
function toIdentifierToken(value) {
|
|
17781
|
+
return value.replace(/[^A-Za-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
17782
|
+
}
|
|
17783
|
+
function uniqueTokens(values) {
|
|
17784
|
+
const result = [];
|
|
17785
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17786
|
+
for (const value of values) {
|
|
17787
|
+
const trimmed = value.trim();
|
|
17788
|
+
if (!trimmed) continue;
|
|
17789
|
+
const key = trimmed.toLowerCase();
|
|
17790
|
+
if (seen.has(key)) continue;
|
|
17791
|
+
seen.add(key);
|
|
17792
|
+
result.push(trimmed);
|
|
17793
|
+
}
|
|
17794
|
+
return result;
|
|
17795
|
+
}
|
|
17796
|
+
|
|
17797
|
+
// src/context/analyzers/python-ast-analyzer.ts
|
|
17798
|
+
var PYTHON_EXTENSIONS = /* @__PURE__ */ new Set([".py"]);
|
|
17799
|
+
var PythonAstAnalyzer = class {
|
|
17800
|
+
name = "python-ast-analyzer";
|
|
17801
|
+
ecosystems = /* @__PURE__ */ new Set(["pip", "poetry", "uv"]);
|
|
17619
17802
|
supports(ecosystem) {
|
|
17620
17803
|
return this.ecosystems.has(ecosystem);
|
|
17621
17804
|
}
|
|
17622
17805
|
async analyze(context) {
|
|
17623
|
-
const
|
|
17624
|
-
|
|
17625
|
-
|
|
17626
|
-
|
|
17627
|
-
|
|
17628
|
-
|
|
17629
|
-
|
|
17806
|
+
const state = initState(context.packages);
|
|
17807
|
+
const packagePatterns = context.packages.map(
|
|
17808
|
+
(pkg) => this.patternForPackage(pkg.packageName, pkg.ecosystem)
|
|
17809
|
+
);
|
|
17810
|
+
let files;
|
|
17811
|
+
try {
|
|
17812
|
+
files = await collectSourceFiles2(context.projectPath, PYTHON_EXTENSIONS);
|
|
17813
|
+
} catch (err) {
|
|
17814
|
+
return errorResultFromMessage(
|
|
17815
|
+
context,
|
|
17816
|
+
this.name,
|
|
17817
|
+
err instanceof Error ? err.message : String(err),
|
|
17818
|
+
"Failed to enumerate Python source files"
|
|
17819
|
+
);
|
|
17820
|
+
}
|
|
17821
|
+
for (const filePath of files) {
|
|
17822
|
+
if (state.snippetsProduced >= context.maxSnippetsTotal) break;
|
|
17823
|
+
const sourceText = await readSourceFile(this.name, context.ecosystem, filePath, state.errors);
|
|
17824
|
+
if (sourceText === null) continue;
|
|
17825
|
+
const aliasToPackage = /* @__PURE__ */ new Map();
|
|
17826
|
+
const lines = sourceText.split(/\r?\n/);
|
|
17827
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
17828
|
+
const line = lines[idx];
|
|
17829
|
+
const trimmed = stripInlineComment(line).trim();
|
|
17830
|
+
const lineNumber = idx + 1;
|
|
17831
|
+
if (!trimmed) continue;
|
|
17832
|
+
const importMatch = trimmed.match(/^import\s+(.+)$/);
|
|
17833
|
+
if (importMatch) {
|
|
17834
|
+
const segments = importMatch[1].split(",").map((part) => part.trim()).filter(Boolean);
|
|
17835
|
+
for (const segment of segments) {
|
|
17836
|
+
const parsed = segment.match(/^([A-Za-z_][A-Za-z0-9_\.]*)(?:\s+as\s+([A-Za-z_][A-Za-z0-9_]*))?$/);
|
|
17837
|
+
if (!parsed) continue;
|
|
17838
|
+
const moduleName = parsed[1];
|
|
17839
|
+
const alias = parsed[2] ?? moduleName.split(".").at(0) ?? moduleName;
|
|
17840
|
+
this.addImportForModule(
|
|
17841
|
+
context,
|
|
17842
|
+
state,
|
|
17843
|
+
filePath,
|
|
17844
|
+
sourceText,
|
|
17845
|
+
packagePatterns,
|
|
17846
|
+
aliasToPackage,
|
|
17847
|
+
moduleName,
|
|
17848
|
+
alias,
|
|
17849
|
+
lineNumber
|
|
17850
|
+
);
|
|
17851
|
+
}
|
|
17852
|
+
continue;
|
|
17853
|
+
}
|
|
17854
|
+
const fromMatch = trimmed.match(
|
|
17855
|
+
/^from\s+([A-Za-z_][A-Za-z0-9_\.]*)\s+import\s+(.+)$/
|
|
17856
|
+
);
|
|
17857
|
+
if (fromMatch) {
|
|
17858
|
+
const moduleName = fromMatch[1];
|
|
17859
|
+
const imported = fromMatch[2].split(",").map((part) => part.trim()).filter(Boolean);
|
|
17860
|
+
for (const part of imported) {
|
|
17861
|
+
const parsed = part.match(/^([A-Za-z_][A-Za-z0-9_]*)(?:\s+as\s+([A-Za-z_][A-Za-z0-9_]*))?$/);
|
|
17862
|
+
if (!parsed) continue;
|
|
17863
|
+
const name = parsed[1];
|
|
17864
|
+
const alias = parsed[2] ?? name;
|
|
17865
|
+
this.addImportForModule(
|
|
17866
|
+
context,
|
|
17867
|
+
state,
|
|
17868
|
+
filePath,
|
|
17869
|
+
sourceText,
|
|
17870
|
+
packagePatterns,
|
|
17871
|
+
aliasToPackage,
|
|
17872
|
+
moduleName,
|
|
17873
|
+
alias,
|
|
17874
|
+
lineNumber
|
|
17875
|
+
);
|
|
17876
|
+
}
|
|
17877
|
+
continue;
|
|
17878
|
+
}
|
|
17879
|
+
for (const [alias, pkgKey] of aliasToPackage.entries()) {
|
|
17880
|
+
const escapedAlias = escapeRegex(alias);
|
|
17881
|
+
const directCall = new RegExp(`\\b${escapedAlias}\\s*\\(`);
|
|
17882
|
+
if (directCall.test(trimmed)) {
|
|
17883
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
17884
|
+
packageKey: pkgKey,
|
|
17885
|
+
line: lineNumber,
|
|
17886
|
+
matchKind: "call",
|
|
17887
|
+
calledSymbol: alias,
|
|
17888
|
+
confidence: 0.78
|
|
17889
|
+
});
|
|
17890
|
+
}
|
|
17891
|
+
const memberCall = new RegExp(`\\b${escapedAlias}\\s*\\.\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\(`);
|
|
17892
|
+
const match = trimmed.match(memberCall);
|
|
17893
|
+
if (match) {
|
|
17894
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
17895
|
+
packageKey: pkgKey,
|
|
17896
|
+
line: lineNumber,
|
|
17897
|
+
matchKind: "call",
|
|
17898
|
+
calledSymbol: `${alias}.${match[1]}`,
|
|
17899
|
+
confidence: 0.8
|
|
17900
|
+
});
|
|
17901
|
+
}
|
|
17902
|
+
}
|
|
17903
|
+
}
|
|
17904
|
+
}
|
|
17905
|
+
return toAnalyzerResult(state);
|
|
17906
|
+
}
|
|
17907
|
+
addImportForModule(context, state, filePath, sourceText, packagePatterns, aliasToPackage, moduleName, alias, lineNumber) {
|
|
17908
|
+
const normalizedModule = moduleName.toLowerCase();
|
|
17909
|
+
for (const pkg of packagePatterns) {
|
|
17910
|
+
const matched = pkg.modules.some(
|
|
17911
|
+
(candidate) => normalizedModule === candidate || normalizedModule.startsWith(`${candidate}.`)
|
|
17912
|
+
);
|
|
17913
|
+
if (!matched) continue;
|
|
17914
|
+
aliasToPackage.set(alias, pkg.key);
|
|
17915
|
+
aliasToPackage.set(moduleName.split(".").at(0) ?? moduleName, pkg.key);
|
|
17916
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
17917
|
+
packageKey: pkg.key,
|
|
17918
|
+
line: lineNumber,
|
|
17919
|
+
matchKind: "import",
|
|
17920
|
+
confidence: 0.95
|
|
17921
|
+
});
|
|
17922
|
+
}
|
|
17923
|
+
}
|
|
17924
|
+
patternForPackage(packageName, ecosystem) {
|
|
17925
|
+
const normalized = toIdentifierToken(packageName).replace(/_/g, "-");
|
|
17926
|
+
const base = toIdentifierToken(basePackageName(packageName));
|
|
17927
|
+
const packageModules = [
|
|
17928
|
+
normalized.replace(/-/g, "_"),
|
|
17929
|
+
base.replace(/-/g, "_"),
|
|
17930
|
+
base.replace(/_/g, "")
|
|
17931
|
+
];
|
|
17932
|
+
if (normalized === "pyyaml" || base === "pyyaml") {
|
|
17933
|
+
packageModules.push("yaml");
|
|
17934
|
+
}
|
|
17630
17935
|
return {
|
|
17631
|
-
|
|
17632
|
-
|
|
17633
|
-
snippetsProduced: 0
|
|
17936
|
+
key: packageKey(ecosystem, packageName),
|
|
17937
|
+
modules: uniqueTokens(packageModules.map((v) => v.toLowerCase()))
|
|
17634
17938
|
};
|
|
17635
17939
|
}
|
|
17636
17940
|
};
|
|
17941
|
+
function stripInlineComment(line) {
|
|
17942
|
+
const hashIndex = line.indexOf("#");
|
|
17943
|
+
return hashIndex === -1 ? line : line.slice(0, hashIndex);
|
|
17944
|
+
}
|
|
17945
|
+
function escapeRegex(value) {
|
|
17946
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
17947
|
+
}
|
|
17948
|
+
|
|
17949
|
+
// src/context/analyzers/java-ast-analyzer.ts
|
|
17950
|
+
var JAVA_EXTENSIONS = /* @__PURE__ */ new Set([".java"]);
|
|
17951
|
+
var JavaAstAnalyzer = class {
|
|
17952
|
+
name = "java-ast-analyzer";
|
|
17953
|
+
ecosystems = /* @__PURE__ */ new Set(["maven"]);
|
|
17954
|
+
supports(ecosystem) {
|
|
17955
|
+
return this.ecosystems.has(ecosystem);
|
|
17956
|
+
}
|
|
17957
|
+
async analyze(context) {
|
|
17958
|
+
const state = initState(context.packages);
|
|
17959
|
+
const packagePatterns = context.packages.map(
|
|
17960
|
+
(pkg) => this.patternForPackage(pkg.packageName, pkg.ecosystem)
|
|
17961
|
+
);
|
|
17962
|
+
let files;
|
|
17963
|
+
try {
|
|
17964
|
+
files = await collectSourceFiles2(context.projectPath, JAVA_EXTENSIONS);
|
|
17965
|
+
} catch (err) {
|
|
17966
|
+
return errorResultFromMessage(
|
|
17967
|
+
context,
|
|
17968
|
+
this.name,
|
|
17969
|
+
err instanceof Error ? err.message : String(err),
|
|
17970
|
+
"Failed to enumerate Java source files"
|
|
17971
|
+
);
|
|
17972
|
+
}
|
|
17973
|
+
for (const filePath of files) {
|
|
17974
|
+
if (state.snippetsProduced >= context.maxSnippetsTotal) break;
|
|
17975
|
+
const sourceText = await readSourceFile(this.name, context.ecosystem, filePath, state.errors);
|
|
17976
|
+
if (sourceText === null) continue;
|
|
17977
|
+
const lines = sourceText.split(/\r?\n/);
|
|
17978
|
+
const symbolToPackage = /* @__PURE__ */ new Map();
|
|
17979
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
17980
|
+
const line = stripLineComment(lines[idx]).trim();
|
|
17981
|
+
const lineNumber = idx + 1;
|
|
17982
|
+
if (!line) continue;
|
|
17983
|
+
const importMatch = line.match(/^import\s+(?:static\s+)?([A-Za-z0-9_.*]+)\s*;/);
|
|
17984
|
+
if (importMatch) {
|
|
17985
|
+
const importPath = importMatch[1].replace(/\.\*$/, "");
|
|
17986
|
+
const simpleName = importPath.split(".").at(-1) ?? importPath;
|
|
17987
|
+
for (const pkg of packagePatterns) {
|
|
17988
|
+
if (!pkg.candidates.some((candidate) => importPath.startsWith(candidate))) continue;
|
|
17989
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
17990
|
+
packageKey: pkg.key,
|
|
17991
|
+
line: lineNumber,
|
|
17992
|
+
matchKind: "import",
|
|
17993
|
+
confidence: 0.94
|
|
17994
|
+
});
|
|
17995
|
+
if (simpleName && /^[A-Za-z_][A-Za-z0-9_]*$/.test(simpleName)) {
|
|
17996
|
+
symbolToPackage.set(simpleName, pkg.key);
|
|
17997
|
+
}
|
|
17998
|
+
}
|
|
17999
|
+
continue;
|
|
18000
|
+
}
|
|
18001
|
+
const varDecl = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?:=|;)/);
|
|
18002
|
+
if (varDecl) {
|
|
18003
|
+
const typeName = varDecl[1];
|
|
18004
|
+
const variableName = varDecl[2];
|
|
18005
|
+
const pkgKey2 = symbolToPackage.get(typeName);
|
|
18006
|
+
if (pkgKey2) {
|
|
18007
|
+
symbolToPackage.set(variableName, pkgKey2);
|
|
18008
|
+
}
|
|
18009
|
+
}
|
|
18010
|
+
const callMatch = line.match(/\b([A-Za-z_][A-Za-z0-9_]*)\s*\.\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(/);
|
|
18011
|
+
if (!callMatch) continue;
|
|
18012
|
+
const lhs = callMatch[1];
|
|
18013
|
+
const member = callMatch[2];
|
|
18014
|
+
const pkgKey = symbolToPackage.get(lhs);
|
|
18015
|
+
if (!pkgKey) continue;
|
|
18016
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18017
|
+
packageKey: pkgKey,
|
|
18018
|
+
line: lineNumber,
|
|
18019
|
+
matchKind: "call",
|
|
18020
|
+
calledSymbol: `${lhs}.${member}`,
|
|
18021
|
+
confidence: 0.8
|
|
18022
|
+
});
|
|
18023
|
+
}
|
|
18024
|
+
}
|
|
18025
|
+
return toAnalyzerResult(state);
|
|
18026
|
+
}
|
|
18027
|
+
patternForPackage(packageName, ecosystem) {
|
|
18028
|
+
const [groupIdRaw, artifactIdRaw] = packageName.split(":");
|
|
18029
|
+
const groupId = (groupIdRaw ?? "").trim();
|
|
18030
|
+
const artifactId = (artifactIdRaw ?? "").trim();
|
|
18031
|
+
const candidates = uniqueTokens([
|
|
18032
|
+
groupId,
|
|
18033
|
+
groupId && artifactId ? `${groupId}.${artifactId.replace(/-/g, ".")}` : "",
|
|
18034
|
+
artifactId ? artifactId.replace(/-/g, ".") : "",
|
|
18035
|
+
artifactId ? toIdentifierToken(artifactId).replace(/_/g, ".") : ""
|
|
18036
|
+
]);
|
|
18037
|
+
return {
|
|
18038
|
+
key: packageKey(ecosystem, packageName),
|
|
18039
|
+
candidates
|
|
18040
|
+
};
|
|
18041
|
+
}
|
|
18042
|
+
};
|
|
18043
|
+
function stripLineComment(line) {
|
|
18044
|
+
const idx = line.indexOf("//");
|
|
18045
|
+
return idx === -1 ? line : line.slice(0, idx);
|
|
18046
|
+
}
|
|
18047
|
+
|
|
18048
|
+
// src/context/analyzers/dotnet-ast-analyzer.ts
|
|
18049
|
+
var CSHARP_EXTENSIONS = /* @__PURE__ */ new Set([".cs"]);
|
|
18050
|
+
var DotnetAstAnalyzer = class {
|
|
18051
|
+
name = "dotnet-ast-analyzer";
|
|
18052
|
+
ecosystems = /* @__PURE__ */ new Set(["nuget"]);
|
|
18053
|
+
supports(ecosystem) {
|
|
18054
|
+
return this.ecosystems.has(ecosystem);
|
|
18055
|
+
}
|
|
18056
|
+
async analyze(context) {
|
|
18057
|
+
const state = initState(context.packages);
|
|
18058
|
+
const packagePatterns = context.packages.map(
|
|
18059
|
+
(pkg) => this.patternForPackage(pkg.packageName, pkg.ecosystem)
|
|
18060
|
+
);
|
|
18061
|
+
let files;
|
|
18062
|
+
try {
|
|
18063
|
+
files = await collectSourceFiles2(context.projectPath, CSHARP_EXTENSIONS);
|
|
18064
|
+
} catch (err) {
|
|
18065
|
+
return errorResultFromMessage(
|
|
18066
|
+
context,
|
|
18067
|
+
this.name,
|
|
18068
|
+
err instanceof Error ? err.message : String(err),
|
|
18069
|
+
"Failed to enumerate C# source files"
|
|
18070
|
+
);
|
|
18071
|
+
}
|
|
18072
|
+
for (const filePath of files) {
|
|
18073
|
+
if (state.snippetsProduced >= context.maxSnippetsTotal) break;
|
|
18074
|
+
const sourceText = await readSourceFile(this.name, context.ecosystem, filePath, state.errors);
|
|
18075
|
+
if (sourceText === null) continue;
|
|
18076
|
+
const lines = sourceText.split(/\r?\n/);
|
|
18077
|
+
const symbolToPackage = /* @__PURE__ */ new Map();
|
|
18078
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
18079
|
+
const line = stripLineComment2(lines[idx]).trim();
|
|
18080
|
+
const lineNumber = idx + 1;
|
|
18081
|
+
if (!line) continue;
|
|
18082
|
+
const aliasUsing = line.match(/^using\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*([A-Za-z0-9_.]+)\s*;/);
|
|
18083
|
+
if (aliasUsing) {
|
|
18084
|
+
const alias = aliasUsing[1];
|
|
18085
|
+
const targetNamespace = aliasUsing[2];
|
|
18086
|
+
for (const pkg of packagePatterns) {
|
|
18087
|
+
if (!pkg.namespaceCandidates.some((candidate) => targetNamespace.startsWith(candidate))) continue;
|
|
18088
|
+
symbolToPackage.set(alias, pkg.key);
|
|
18089
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18090
|
+
packageKey: pkg.key,
|
|
18091
|
+
line: lineNumber,
|
|
18092
|
+
matchKind: "import",
|
|
18093
|
+
confidence: 0.93
|
|
18094
|
+
});
|
|
18095
|
+
}
|
|
18096
|
+
continue;
|
|
18097
|
+
}
|
|
18098
|
+
const usingMatch = line.match(/^using\s+([A-Za-z0-9_.]+)\s*;/);
|
|
18099
|
+
if (usingMatch) {
|
|
18100
|
+
const namespaceName = usingMatch[1];
|
|
18101
|
+
const tailSymbol = namespaceName.split(".").at(-1) ?? namespaceName;
|
|
18102
|
+
for (const pkg of packagePatterns) {
|
|
18103
|
+
if (!pkg.namespaceCandidates.some((candidate) => namespaceName.startsWith(candidate))) continue;
|
|
18104
|
+
symbolToPackage.set(tailSymbol, pkg.key);
|
|
18105
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18106
|
+
packageKey: pkg.key,
|
|
18107
|
+
line: lineNumber,
|
|
18108
|
+
matchKind: "import",
|
|
18109
|
+
confidence: 0.93
|
|
18110
|
+
});
|
|
18111
|
+
}
|
|
18112
|
+
continue;
|
|
18113
|
+
}
|
|
18114
|
+
const varDecl = line.match(/^([A-Za-z_][A-Za-z0-9_.]*)\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?:=|;)/);
|
|
18115
|
+
if (varDecl) {
|
|
18116
|
+
const typeName = varDecl[1].split(".").at(-1) ?? varDecl[1];
|
|
18117
|
+
const variableName = varDecl[2];
|
|
18118
|
+
const pkgKey = symbolToPackage.get(typeName);
|
|
18119
|
+
if (pkgKey) {
|
|
18120
|
+
symbolToPackage.set(variableName, pkgKey);
|
|
18121
|
+
}
|
|
18122
|
+
}
|
|
18123
|
+
const memberCall = line.match(/\b([A-Za-z_][A-Za-z0-9_]*)\s*\.\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(/);
|
|
18124
|
+
if (memberCall) {
|
|
18125
|
+
const lhs = memberCall[1];
|
|
18126
|
+
const method = memberCall[2];
|
|
18127
|
+
const pkgKey = symbolToPackage.get(lhs) ?? this.findSymbolCandidatePackage(lhs, packagePatterns);
|
|
18128
|
+
if (pkgKey) {
|
|
18129
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18130
|
+
packageKey: pkgKey,
|
|
18131
|
+
line: lineNumber,
|
|
18132
|
+
matchKind: "call",
|
|
18133
|
+
calledSymbol: `${lhs}.${method}`,
|
|
18134
|
+
confidence: 0.79
|
|
18135
|
+
});
|
|
18136
|
+
}
|
|
18137
|
+
}
|
|
18138
|
+
}
|
|
18139
|
+
}
|
|
18140
|
+
return toAnalyzerResult(state);
|
|
18141
|
+
}
|
|
18142
|
+
patternForPackage(packageName, ecosystem) {
|
|
18143
|
+
const dotted = packageName.replace(/-/g, ".");
|
|
18144
|
+
const segments = dotted.split(".");
|
|
18145
|
+
const symbolCandidates = uniqueTokens([
|
|
18146
|
+
segments.at(-1) ?? "",
|
|
18147
|
+
segments.at(-2) ?? "",
|
|
18148
|
+
packageName.split(".").at(-1) ?? ""
|
|
18149
|
+
]);
|
|
18150
|
+
const namespaceCandidates = uniqueTokens([
|
|
18151
|
+
dotted,
|
|
18152
|
+
segments.slice(0, -1).join("."),
|
|
18153
|
+
segments.slice(0, Math.min(2, segments.length)).join(".")
|
|
18154
|
+
]);
|
|
18155
|
+
return {
|
|
18156
|
+
key: packageKey(ecosystem, packageName),
|
|
18157
|
+
namespaceCandidates,
|
|
18158
|
+
symbolCandidates
|
|
18159
|
+
};
|
|
18160
|
+
}
|
|
18161
|
+
findSymbolCandidatePackage(symbol, packagePatterns) {
|
|
18162
|
+
for (const pkg of packagePatterns) {
|
|
18163
|
+
if (pkg.symbolCandidates.includes(symbol)) {
|
|
18164
|
+
return pkg.key;
|
|
18165
|
+
}
|
|
18166
|
+
}
|
|
18167
|
+
return null;
|
|
18168
|
+
}
|
|
18169
|
+
};
|
|
18170
|
+
function stripLineComment2(line) {
|
|
18171
|
+
const idx = line.indexOf("//");
|
|
18172
|
+
return idx === -1 ? line : line.slice(0, idx);
|
|
18173
|
+
}
|
|
18174
|
+
|
|
18175
|
+
// src/context/analyzers/rust-ast-analyzer.ts
|
|
18176
|
+
var RUST_EXTENSIONS = /* @__PURE__ */ new Set([".rs"]);
|
|
18177
|
+
var RustAstAnalyzer = class {
|
|
18178
|
+
name = "rust-ast-analyzer";
|
|
18179
|
+
ecosystems = /* @__PURE__ */ new Set(["cargo"]);
|
|
18180
|
+
supports(ecosystem) {
|
|
18181
|
+
return this.ecosystems.has(ecosystem);
|
|
18182
|
+
}
|
|
18183
|
+
async analyze(context) {
|
|
18184
|
+
const state = initState(context.packages);
|
|
18185
|
+
const packagePatterns = context.packages.map(
|
|
18186
|
+
(pkg) => this.patternForPackage(pkg.packageName, pkg.ecosystem)
|
|
18187
|
+
);
|
|
18188
|
+
let files;
|
|
18189
|
+
try {
|
|
18190
|
+
files = await collectSourceFiles2(context.projectPath, RUST_EXTENSIONS);
|
|
18191
|
+
} catch (err) {
|
|
18192
|
+
return errorResultFromMessage(
|
|
18193
|
+
context,
|
|
18194
|
+
this.name,
|
|
18195
|
+
err instanceof Error ? err.message : String(err),
|
|
18196
|
+
"Failed to enumerate Rust source files"
|
|
18197
|
+
);
|
|
18198
|
+
}
|
|
18199
|
+
for (const filePath of files) {
|
|
18200
|
+
if (state.snippetsProduced >= context.maxSnippetsTotal) break;
|
|
18201
|
+
const sourceText = await readSourceFile(this.name, context.ecosystem, filePath, state.errors);
|
|
18202
|
+
if (sourceText === null) continue;
|
|
18203
|
+
const lines = sourceText.split(/\r?\n/);
|
|
18204
|
+
const symbolToPackage = /* @__PURE__ */ new Map();
|
|
18205
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
18206
|
+
const line = stripLineComment3(lines[idx]).trim();
|
|
18207
|
+
const lineNumber = idx + 1;
|
|
18208
|
+
if (!line) continue;
|
|
18209
|
+
const useMatch = line.match(/^use\s+([A-Za-z_][A-Za-z0-9_:]*)(?:\s+as\s+([A-Za-z_][A-Za-z0-9_]*))?\s*;/);
|
|
18210
|
+
if (useMatch) {
|
|
18211
|
+
const pathExpr = useMatch[1];
|
|
18212
|
+
const alias = useMatch[2] ?? pathExpr.split("::").at(0) ?? pathExpr;
|
|
18213
|
+
const crate = pathExpr.split("::").at(0) ?? pathExpr;
|
|
18214
|
+
for (const pkg of packagePatterns) {
|
|
18215
|
+
if (!pkg.crateNames.includes(crate)) continue;
|
|
18216
|
+
symbolToPackage.set(alias, pkg.key);
|
|
18217
|
+
symbolToPackage.set(crate, pkg.key);
|
|
18218
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18219
|
+
packageKey: pkg.key,
|
|
18220
|
+
line: lineNumber,
|
|
18221
|
+
matchKind: "import",
|
|
18222
|
+
confidence: 0.94
|
|
18223
|
+
});
|
|
18224
|
+
}
|
|
18225
|
+
continue;
|
|
18226
|
+
}
|
|
18227
|
+
const externMatch = line.match(/^extern\s+crate\s+([A-Za-z_][A-Za-z0-9_]*)(?:\s+as\s+([A-Za-z_][A-Za-z0-9_]*))?\s*;/);
|
|
18228
|
+
if (externMatch) {
|
|
18229
|
+
const crate = externMatch[1];
|
|
18230
|
+
const alias = externMatch[2] ?? crate;
|
|
18231
|
+
for (const pkg of packagePatterns) {
|
|
18232
|
+
if (!pkg.crateNames.includes(crate)) continue;
|
|
18233
|
+
symbolToPackage.set(alias, pkg.key);
|
|
18234
|
+
symbolToPackage.set(crate, pkg.key);
|
|
18235
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18236
|
+
packageKey: pkg.key,
|
|
18237
|
+
line: lineNumber,
|
|
18238
|
+
matchKind: "import",
|
|
18239
|
+
confidence: 0.94
|
|
18240
|
+
});
|
|
18241
|
+
}
|
|
18242
|
+
continue;
|
|
18243
|
+
}
|
|
18244
|
+
const scopedCall = line.match(/\b([A-Za-z_][A-Za-z0-9_]*)::([A-Za-z_][A-Za-z0-9_]*)\s*\(/);
|
|
18245
|
+
if (scopedCall) {
|
|
18246
|
+
const lhs = scopedCall[1];
|
|
18247
|
+
const func = scopedCall[2];
|
|
18248
|
+
const pkgKey = symbolToPackage.get(lhs);
|
|
18249
|
+
if (pkgKey) {
|
|
18250
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18251
|
+
packageKey: pkgKey,
|
|
18252
|
+
line: lineNumber,
|
|
18253
|
+
matchKind: "call",
|
|
18254
|
+
calledSymbol: `${lhs}::${func}`,
|
|
18255
|
+
confidence: 0.8
|
|
18256
|
+
});
|
|
18257
|
+
}
|
|
18258
|
+
}
|
|
18259
|
+
const methodCall = line.match(/\b([A-Za-z_][A-Za-z0-9_]*)\s*\.\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(/);
|
|
18260
|
+
if (methodCall) {
|
|
18261
|
+
const lhs = methodCall[1];
|
|
18262
|
+
const method = methodCall[2];
|
|
18263
|
+
const pkgKey = symbolToPackage.get(lhs);
|
|
18264
|
+
if (pkgKey) {
|
|
18265
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18266
|
+
packageKey: pkgKey,
|
|
18267
|
+
line: lineNumber,
|
|
18268
|
+
matchKind: "call",
|
|
18269
|
+
calledSymbol: `${lhs}.${method}`,
|
|
18270
|
+
confidence: 0.78
|
|
18271
|
+
});
|
|
18272
|
+
}
|
|
18273
|
+
}
|
|
18274
|
+
}
|
|
18275
|
+
}
|
|
18276
|
+
return toAnalyzerResult(state);
|
|
18277
|
+
}
|
|
18278
|
+
patternForPackage(packageName, ecosystem) {
|
|
18279
|
+
const crateName = packageName.replace(/-/g, "_");
|
|
18280
|
+
return {
|
|
18281
|
+
key: packageKey(ecosystem, packageName),
|
|
18282
|
+
crateNames: uniqueTokens([crateName, packageName])
|
|
18283
|
+
};
|
|
18284
|
+
}
|
|
18285
|
+
};
|
|
18286
|
+
function stripLineComment3(line) {
|
|
18287
|
+
const idx = line.indexOf("//");
|
|
18288
|
+
return idx === -1 ? line : line.slice(0, idx);
|
|
18289
|
+
}
|
|
18290
|
+
|
|
18291
|
+
// src/context/analyzers/go-ast-analyzer.ts
|
|
18292
|
+
var GO_EXTENSIONS = /* @__PURE__ */ new Set([".go"]);
|
|
18293
|
+
var GoAstAnalyzer = class {
|
|
18294
|
+
name = "go-ast-analyzer";
|
|
18295
|
+
ecosystems = /* @__PURE__ */ new Set(["go"]);
|
|
18296
|
+
supports(ecosystem) {
|
|
18297
|
+
return this.ecosystems.has(ecosystem);
|
|
18298
|
+
}
|
|
18299
|
+
async analyze(context) {
|
|
18300
|
+
const state = initState(context.packages);
|
|
18301
|
+
const packagePatterns = context.packages.map((pkg) => this.patternForPackage(pkg.packageName, pkg.ecosystem));
|
|
18302
|
+
let files;
|
|
18303
|
+
try {
|
|
18304
|
+
files = await collectSourceFiles2(context.projectPath, GO_EXTENSIONS);
|
|
18305
|
+
} catch (err) {
|
|
18306
|
+
return errorResultFromMessage(
|
|
18307
|
+
context,
|
|
18308
|
+
this.name,
|
|
18309
|
+
err instanceof Error ? err.message : String(err),
|
|
18310
|
+
"Failed to enumerate Go source files"
|
|
18311
|
+
);
|
|
18312
|
+
}
|
|
18313
|
+
for (const filePath of files) {
|
|
18314
|
+
if (state.snippetsProduced >= context.maxSnippetsTotal) break;
|
|
18315
|
+
const sourceText = await readSourceFile(this.name, context.ecosystem, filePath, state.errors);
|
|
18316
|
+
if (sourceText === null) continue;
|
|
18317
|
+
const aliasesByPackage = /* @__PURE__ */ new Map();
|
|
18318
|
+
for (const pkg of packagePatterns) {
|
|
18319
|
+
aliasesByPackage.set(pkg.key, new Set(pkg.aliases));
|
|
18320
|
+
}
|
|
18321
|
+
const lines = sourceText.split(/\r?\n/);
|
|
18322
|
+
let inImportBlock = false;
|
|
18323
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
18324
|
+
const lineNumber = idx + 1;
|
|
18325
|
+
const line = lines[idx];
|
|
18326
|
+
const trimmed = line.trim();
|
|
18327
|
+
if (trimmed.startsWith("import (")) {
|
|
18328
|
+
inImportBlock = true;
|
|
18329
|
+
continue;
|
|
18330
|
+
}
|
|
18331
|
+
if (inImportBlock && trimmed === ")") {
|
|
18332
|
+
inImportBlock = false;
|
|
18333
|
+
continue;
|
|
18334
|
+
}
|
|
18335
|
+
const match = this.extractImport(trimmed, inImportBlock);
|
|
18336
|
+
if (match) {
|
|
18337
|
+
for (const pkg of packagePatterns) {
|
|
18338
|
+
if (match.importPath !== pkg.packageName) continue;
|
|
18339
|
+
const alias = match.alias && match.alias !== "_" && match.alias !== "." ? toIdentifierToken(match.alias) : pkg.aliases[0];
|
|
18340
|
+
if (alias) {
|
|
18341
|
+
aliasesByPackage.get(pkg.key)?.add(alias);
|
|
18342
|
+
}
|
|
18343
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18344
|
+
packageKey: pkg.key,
|
|
18345
|
+
line: lineNumber,
|
|
18346
|
+
matchKind: "import",
|
|
18347
|
+
confidence: 0.95
|
|
18348
|
+
});
|
|
18349
|
+
}
|
|
18350
|
+
}
|
|
18351
|
+
for (const pkg of packagePatterns) {
|
|
18352
|
+
const aliases = aliasesByPackage.get(pkg.key);
|
|
18353
|
+
if (!aliases) continue;
|
|
18354
|
+
for (const alias of aliases) {
|
|
18355
|
+
if (!alias) continue;
|
|
18356
|
+
const escapedAlias = escapeRegex2(alias);
|
|
18357
|
+
const callRegex = new RegExp(`\\b${escapedAlias}\\s*\\.\\s*([A-Za-z_][A-Za-z0-9_]*)\\s*\\(`);
|
|
18358
|
+
const callMatch = line.match(callRegex);
|
|
18359
|
+
if (!callMatch) continue;
|
|
18360
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18361
|
+
packageKey: pkg.key,
|
|
18362
|
+
line: lineNumber,
|
|
18363
|
+
matchKind: "call",
|
|
18364
|
+
calledSymbol: `${alias}.${callMatch[1]}`,
|
|
18365
|
+
confidence: 0.8
|
|
18366
|
+
});
|
|
18367
|
+
}
|
|
18368
|
+
}
|
|
18369
|
+
}
|
|
18370
|
+
}
|
|
18371
|
+
return toAnalyzerResult(state);
|
|
18372
|
+
}
|
|
18373
|
+
patternForPackage(packageName, ecosystem) {
|
|
18374
|
+
const baseName = basePackageName(packageName);
|
|
18375
|
+
const identifierBase = toIdentifierToken(baseName.replace(/^v[0-9]+$/, ""));
|
|
18376
|
+
const shortened = identifierBase.replace(/go$/i, "") || identifierBase;
|
|
18377
|
+
return {
|
|
18378
|
+
key: packageKey(ecosystem, packageName),
|
|
18379
|
+
packageName,
|
|
18380
|
+
aliases: uniqueTokens([identifierBase, shortened])
|
|
18381
|
+
};
|
|
18382
|
+
}
|
|
18383
|
+
extractImport(trimmedLine, inImportBlock) {
|
|
18384
|
+
if (!inImportBlock && !trimmedLine.startsWith("import ")) return null;
|
|
18385
|
+
if (inImportBlock) {
|
|
18386
|
+
const blockMatch = trimmedLine.match(/^(?:(\.|_|[A-Za-z_][A-Za-z0-9_]*)\s+)?\"([^\"]+)\"/);
|
|
18387
|
+
if (!blockMatch) return null;
|
|
18388
|
+
return {
|
|
18389
|
+
alias: blockMatch[1] ?? null,
|
|
18390
|
+
importPath: blockMatch[2]
|
|
18391
|
+
};
|
|
18392
|
+
}
|
|
18393
|
+
const singleMatch = trimmedLine.match(/^import\s+(?:(\.|_|[A-Za-z_][A-Za-z0-9_]*)\s+)?\"([^\"]+)\"/);
|
|
18394
|
+
if (!singleMatch) return null;
|
|
18395
|
+
return {
|
|
18396
|
+
alias: singleMatch[1] ?? null,
|
|
18397
|
+
importPath: singleMatch[2]
|
|
18398
|
+
};
|
|
18399
|
+
}
|
|
18400
|
+
};
|
|
18401
|
+
function escapeRegex2(value) {
|
|
18402
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18403
|
+
}
|
|
18404
|
+
|
|
18405
|
+
// src/context/analyzers/ruby-ast-analyzer.ts
|
|
18406
|
+
var RUBY_EXTENSIONS = /* @__PURE__ */ new Set([".rb"]);
|
|
18407
|
+
var RubyAstAnalyzer = class {
|
|
18408
|
+
name = "ruby-ast-analyzer";
|
|
18409
|
+
ecosystems = /* @__PURE__ */ new Set(["ruby"]);
|
|
18410
|
+
supports(ecosystem) {
|
|
18411
|
+
return this.ecosystems.has(ecosystem);
|
|
18412
|
+
}
|
|
18413
|
+
async analyze(context) {
|
|
18414
|
+
const state = initState(context.packages);
|
|
18415
|
+
const packagePatterns = context.packages.map(
|
|
18416
|
+
(pkg) => this.patternForPackage(pkg.packageName, pkg.ecosystem)
|
|
18417
|
+
);
|
|
18418
|
+
let files;
|
|
18419
|
+
try {
|
|
18420
|
+
files = await collectSourceFiles2(context.projectPath, RUBY_EXTENSIONS);
|
|
18421
|
+
} catch (err) {
|
|
18422
|
+
return errorResultFromMessage(
|
|
18423
|
+
context,
|
|
18424
|
+
this.name,
|
|
18425
|
+
err instanceof Error ? err.message : String(err),
|
|
18426
|
+
"Failed to enumerate Ruby source files"
|
|
18427
|
+
);
|
|
18428
|
+
}
|
|
18429
|
+
for (const filePath of files) {
|
|
18430
|
+
if (state.snippetsProduced >= context.maxSnippetsTotal) break;
|
|
18431
|
+
const sourceText = await readSourceFile(this.name, context.ecosystem, filePath, state.errors);
|
|
18432
|
+
if (sourceText === null) continue;
|
|
18433
|
+
const lines = sourceText.split(/\r?\n/);
|
|
18434
|
+
const symbolToPackage = /* @__PURE__ */ new Map();
|
|
18435
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
18436
|
+
const line = stripInlineComment2(lines[idx]).trim();
|
|
18437
|
+
const lineNumber = idx + 1;
|
|
18438
|
+
if (!line) continue;
|
|
18439
|
+
const requireMatch = line.match(/^require(?:_relative)?\s+['"]([^'"]+)['"]/);
|
|
18440
|
+
if (requireMatch) {
|
|
18441
|
+
const requiredPath = requireMatch[1];
|
|
18442
|
+
for (const pkg of packagePatterns) {
|
|
18443
|
+
if (!pkg.requireCandidates.some((candidate) => requiredPath.includes(candidate))) continue;
|
|
18444
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18445
|
+
packageKey: pkg.key,
|
|
18446
|
+
line: lineNumber,
|
|
18447
|
+
matchKind: "require",
|
|
18448
|
+
confidence: 0.93
|
|
18449
|
+
});
|
|
18450
|
+
for (const constant of pkg.constantCandidates) {
|
|
18451
|
+
symbolToPackage.set(constant, pkg.key);
|
|
18452
|
+
}
|
|
18453
|
+
}
|
|
18454
|
+
continue;
|
|
18455
|
+
}
|
|
18456
|
+
const includeMatch = line.match(/^include\s+([A-Za-z_][A-Za-z0-9_:]*)/);
|
|
18457
|
+
if (includeMatch) {
|
|
18458
|
+
const moduleName = includeMatch[1];
|
|
18459
|
+
for (const pkg of packagePatterns) {
|
|
18460
|
+
if (!pkg.constantCandidates.some((candidate) => moduleName.startsWith(candidate))) continue;
|
|
18461
|
+
symbolToPackage.set(moduleName.split("::").at(0) ?? moduleName, pkg.key);
|
|
18462
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18463
|
+
packageKey: pkg.key,
|
|
18464
|
+
line: lineNumber,
|
|
18465
|
+
matchKind: "import",
|
|
18466
|
+
confidence: 0.9
|
|
18467
|
+
});
|
|
18468
|
+
}
|
|
18469
|
+
continue;
|
|
18470
|
+
}
|
|
18471
|
+
const namespacedCall = line.match(/\b([A-Z][A-Za-z0-9_:]*)\s*(?:\.|::)\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(/);
|
|
18472
|
+
if (namespacedCall) {
|
|
18473
|
+
const lhs = namespacedCall[1].split("::").at(0) ?? namespacedCall[1];
|
|
18474
|
+
const method = namespacedCall[2];
|
|
18475
|
+
const pkgKey = symbolToPackage.get(lhs) ?? this.findConstantCandidatePackage(lhs, packagePatterns);
|
|
18476
|
+
if (pkgKey) {
|
|
18477
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18478
|
+
packageKey: pkgKey,
|
|
18479
|
+
line: lineNumber,
|
|
18480
|
+
matchKind: "call",
|
|
18481
|
+
calledSymbol: `${lhs}.${method}`,
|
|
18482
|
+
confidence: 0.78
|
|
18483
|
+
});
|
|
18484
|
+
}
|
|
18485
|
+
}
|
|
18486
|
+
}
|
|
18487
|
+
}
|
|
18488
|
+
return toAnalyzerResult(state);
|
|
18489
|
+
}
|
|
18490
|
+
patternForPackage(packageName, ecosystem) {
|
|
18491
|
+
const base = basePackageName(packageName);
|
|
18492
|
+
const normalized = base.toLowerCase();
|
|
18493
|
+
const requireCandidates = uniqueTokens([
|
|
18494
|
+
normalized,
|
|
18495
|
+
normalized.replace(/-/g, "/"),
|
|
18496
|
+
normalized.replace(/-/g, "_")
|
|
18497
|
+
]);
|
|
18498
|
+
const constantParts = normalized.replace(/[^a-z0-9_/-]/g, "").split(/[\/_-]+/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1));
|
|
18499
|
+
const constant = constantParts.join("::");
|
|
18500
|
+
const collapsedConstant = constantParts.join("");
|
|
18501
|
+
return {
|
|
18502
|
+
key: packageKey(ecosystem, packageName),
|
|
18503
|
+
requireCandidates,
|
|
18504
|
+
constantCandidates: uniqueTokens([constant, collapsedConstant, constantParts.at(0) ?? ""])
|
|
18505
|
+
};
|
|
18506
|
+
}
|
|
18507
|
+
findConstantCandidatePackage(constant, packagePatterns) {
|
|
18508
|
+
for (const pkg of packagePatterns) {
|
|
18509
|
+
if (pkg.constantCandidates.includes(constant)) {
|
|
18510
|
+
return pkg.key;
|
|
18511
|
+
}
|
|
18512
|
+
}
|
|
18513
|
+
return null;
|
|
18514
|
+
}
|
|
18515
|
+
};
|
|
18516
|
+
function stripInlineComment2(line) {
|
|
18517
|
+
const idx = line.indexOf("#");
|
|
18518
|
+
return idx === -1 ? line : line.slice(0, idx);
|
|
18519
|
+
}
|
|
18520
|
+
|
|
18521
|
+
// src/context/analyzers/php-ast-analyzer.ts
|
|
18522
|
+
var PHP_EXTENSIONS = /* @__PURE__ */ new Set([".php"]);
|
|
18523
|
+
var PhpAstAnalyzer = class {
|
|
18524
|
+
name = "php-ast-analyzer";
|
|
18525
|
+
ecosystems = /* @__PURE__ */ new Set(["composer"]);
|
|
18526
|
+
supports(ecosystem) {
|
|
18527
|
+
return this.ecosystems.has(ecosystem);
|
|
18528
|
+
}
|
|
18529
|
+
async analyze(context) {
|
|
18530
|
+
const state = initState(context.packages);
|
|
18531
|
+
const packagePatterns = context.packages.map(
|
|
18532
|
+
(pkg) => this.patternForPackage(pkg.packageName, pkg.ecosystem)
|
|
18533
|
+
);
|
|
18534
|
+
let files;
|
|
18535
|
+
try {
|
|
18536
|
+
files = await collectSourceFiles2(context.projectPath, PHP_EXTENSIONS);
|
|
18537
|
+
} catch (err) {
|
|
18538
|
+
return errorResultFromMessage(
|
|
18539
|
+
context,
|
|
18540
|
+
this.name,
|
|
18541
|
+
err instanceof Error ? err.message : String(err),
|
|
18542
|
+
"Failed to enumerate PHP source files"
|
|
18543
|
+
);
|
|
18544
|
+
}
|
|
18545
|
+
for (const filePath of files) {
|
|
18546
|
+
if (state.snippetsProduced >= context.maxSnippetsTotal) break;
|
|
18547
|
+
const sourceText = await readSourceFile(this.name, context.ecosystem, filePath, state.errors);
|
|
18548
|
+
if (sourceText === null) continue;
|
|
18549
|
+
const lines = sourceText.split(/\r?\n/);
|
|
18550
|
+
const symbolToPackage = /* @__PURE__ */ new Map();
|
|
18551
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
18552
|
+
const line = stripLineComment4(lines[idx]).trim();
|
|
18553
|
+
const lineNumber = idx + 1;
|
|
18554
|
+
if (!line) continue;
|
|
18555
|
+
const useMatch = line.match(/^use\s+([A-Za-z0-9_\\]+)(?:\s+as\s+([A-Za-z_][A-Za-z0-9_]*))?\s*;/i);
|
|
18556
|
+
if (useMatch) {
|
|
18557
|
+
const namespacePath = useMatch[1];
|
|
18558
|
+
const alias = useMatch[2] ?? namespacePath.split("\\").at(-1) ?? namespacePath;
|
|
18559
|
+
for (const pkg of packagePatterns) {
|
|
18560
|
+
const normalizedNamespace = namespacePath.toLowerCase();
|
|
18561
|
+
if (!pkg.namespaceCandidates.some((candidate) => normalizedNamespace.startsWith(candidate.toLowerCase()))) continue;
|
|
18562
|
+
symbolToPackage.set(alias, pkg.key);
|
|
18563
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18564
|
+
packageKey: pkg.key,
|
|
18565
|
+
line: lineNumber,
|
|
18566
|
+
matchKind: "import",
|
|
18567
|
+
confidence: 0.94
|
|
18568
|
+
});
|
|
18569
|
+
}
|
|
18570
|
+
continue;
|
|
18571
|
+
}
|
|
18572
|
+
const requireMatch = line.match(/^require(?:_once)?\s*\(?\s*['"]([^'"]+)['"]\s*\)?\s*;/i);
|
|
18573
|
+
if (requireMatch) {
|
|
18574
|
+
const pathExpr = requireMatch[1].toLowerCase();
|
|
18575
|
+
for (const pkg of packagePatterns) {
|
|
18576
|
+
const hasVendor = pathExpr.includes(pkg.vendor.toLowerCase());
|
|
18577
|
+
const hasPackage = pathExpr.includes(pkg.package.toLowerCase());
|
|
18578
|
+
if (!hasVendor && !hasPackage) continue;
|
|
18579
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18580
|
+
packageKey: pkg.key,
|
|
18581
|
+
line: lineNumber,
|
|
18582
|
+
matchKind: "require",
|
|
18583
|
+
confidence: 0.9
|
|
18584
|
+
});
|
|
18585
|
+
}
|
|
18586
|
+
continue;
|
|
18587
|
+
}
|
|
18588
|
+
const staticCall = line.match(/\b([A-Za-z_][A-Za-z0-9_]*)::([A-Za-z_][A-Za-z0-9_]*)\s*\(/);
|
|
18589
|
+
if (staticCall) {
|
|
18590
|
+
const lhs = staticCall[1];
|
|
18591
|
+
const method = staticCall[2];
|
|
18592
|
+
const pkgKey = symbolToPackage.get(lhs) ?? this.findSymbolCandidatePackage(lhs, packagePatterns);
|
|
18593
|
+
if (pkgKey) {
|
|
18594
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18595
|
+
packageKey: pkgKey,
|
|
18596
|
+
line: lineNumber,
|
|
18597
|
+
matchKind: "call",
|
|
18598
|
+
calledSymbol: `${lhs}::${method}`,
|
|
18599
|
+
confidence: 0.8
|
|
18600
|
+
});
|
|
18601
|
+
}
|
|
18602
|
+
}
|
|
18603
|
+
const constructorMatch = line.match(/new\s+([A-Za-z_][A-Za-z0-9_]*)\b/);
|
|
18604
|
+
if (constructorMatch) {
|
|
18605
|
+
const className = constructorMatch[1];
|
|
18606
|
+
const pkgKey = symbolToPackage.get(className) ?? this.findSymbolCandidatePackage(className, packagePatterns);
|
|
18607
|
+
if (pkgKey) {
|
|
18608
|
+
addCandidate(context, state, filePath, sourceText, {
|
|
18609
|
+
packageKey: pkgKey,
|
|
18610
|
+
line: lineNumber,
|
|
18611
|
+
matchKind: "call",
|
|
18612
|
+
calledSymbol: `new ${className}`,
|
|
18613
|
+
confidence: 0.76
|
|
18614
|
+
});
|
|
18615
|
+
}
|
|
18616
|
+
}
|
|
18617
|
+
}
|
|
18618
|
+
}
|
|
18619
|
+
return toAnalyzerResult(state);
|
|
18620
|
+
}
|
|
18621
|
+
patternForPackage(packageName, ecosystem) {
|
|
18622
|
+
const [vendorRaw, packageRaw] = packageName.split("/");
|
|
18623
|
+
const vendor = vendorRaw ?? packageName;
|
|
18624
|
+
const packagePart = packageRaw ?? packageName;
|
|
18625
|
+
const namespaceCandidates = uniqueTokens([
|
|
18626
|
+
pascalize(vendor),
|
|
18627
|
+
pascalize(packagePart),
|
|
18628
|
+
`${pascalize(vendor)}\\${pascalize(packagePart)}`,
|
|
18629
|
+
`${pascalize(vendor)}\\${pascalize(packagePart.replace(/-/g, "_"))}`
|
|
18630
|
+
]);
|
|
18631
|
+
const symbolCandidates = uniqueTokens([
|
|
18632
|
+
pascalize(packagePart),
|
|
18633
|
+
pascalize(vendor),
|
|
18634
|
+
pascalize(packagePart).split("\\").at(-1) ?? ""
|
|
18635
|
+
]);
|
|
18636
|
+
return {
|
|
18637
|
+
key: packageKey(ecosystem, packageName),
|
|
18638
|
+
vendor,
|
|
18639
|
+
package: packagePart,
|
|
18640
|
+
namespaceCandidates,
|
|
18641
|
+
symbolCandidates
|
|
18642
|
+
};
|
|
18643
|
+
}
|
|
18644
|
+
findSymbolCandidatePackage(symbol, packagePatterns) {
|
|
18645
|
+
const normalized = symbol.toLowerCase();
|
|
18646
|
+
for (const pkg of packagePatterns) {
|
|
18647
|
+
if (pkg.symbolCandidates.some((candidate) => candidate.toLowerCase() === normalized)) {
|
|
18648
|
+
return pkg.key;
|
|
18649
|
+
}
|
|
18650
|
+
}
|
|
18651
|
+
return null;
|
|
18652
|
+
}
|
|
18653
|
+
};
|
|
18654
|
+
function pascalize(input) {
|
|
18655
|
+
return input.split(/[\/_.-]+/).filter(Boolean).map((part) => part[0]?.toUpperCase() + part.slice(1)).join("\\");
|
|
18656
|
+
}
|
|
18657
|
+
function stripLineComment4(line) {
|
|
18658
|
+
const slashIdx = line.indexOf("//");
|
|
18659
|
+
if (slashIdx !== -1) return line.slice(0, slashIdx);
|
|
18660
|
+
const hashIdx = line.indexOf("#");
|
|
18661
|
+
return hashIdx === -1 ? line : line.slice(0, hashIdx);
|
|
18662
|
+
}
|
|
17637
18663
|
|
|
17638
18664
|
// src/context/usage-context-engine.ts
|
|
17639
18665
|
var DEFAULT_MAX_SNIPPETS_PER_PACKAGE = 20;
|
|
@@ -17643,41 +18669,13 @@ var UsageContextEngine = class {
|
|
|
17643
18669
|
constructor(analyzers) {
|
|
17644
18670
|
this.analyzers = analyzers ?? [
|
|
17645
18671
|
new JsAstAnalyzer(),
|
|
17646
|
-
new
|
|
17647
|
-
|
|
17648
|
-
|
|
17649
|
-
|
|
17650
|
-
),
|
|
17651
|
-
new
|
|
17652
|
-
|
|
17653
|
-
["maven"],
|
|
17654
|
-
"Java AST analyzer is not yet implemented in this release"
|
|
17655
|
-
),
|
|
17656
|
-
new UnsupportedAnalyzer(
|
|
17657
|
-
"dotnet-ast-analyzer",
|
|
17658
|
-
["nuget"],
|
|
17659
|
-
"NuGet/C# AST analyzer is not yet implemented in this release"
|
|
17660
|
-
),
|
|
17661
|
-
new UnsupportedAnalyzer(
|
|
17662
|
-
"rust-ast-analyzer",
|
|
17663
|
-
["cargo"],
|
|
17664
|
-
"Rust AST analyzer is not yet implemented in this release"
|
|
17665
|
-
),
|
|
17666
|
-
new UnsupportedAnalyzer(
|
|
17667
|
-
"go-ast-analyzer",
|
|
17668
|
-
["go"],
|
|
17669
|
-
"Go AST analyzer is not yet implemented in this release"
|
|
17670
|
-
),
|
|
17671
|
-
new UnsupportedAnalyzer(
|
|
17672
|
-
"ruby-ast-analyzer",
|
|
17673
|
-
["ruby"],
|
|
17674
|
-
"Ruby AST analyzer is not yet implemented in this release"
|
|
17675
|
-
),
|
|
17676
|
-
new UnsupportedAnalyzer(
|
|
17677
|
-
"php-ast-analyzer",
|
|
17678
|
-
["composer"],
|
|
17679
|
-
"PHP AST analyzer is not yet implemented in this release"
|
|
17680
|
-
)
|
|
18672
|
+
new PythonAstAnalyzer(),
|
|
18673
|
+
new JavaAstAnalyzer(),
|
|
18674
|
+
new DotnetAstAnalyzer(),
|
|
18675
|
+
new RustAstAnalyzer(),
|
|
18676
|
+
new GoAstAnalyzer(),
|
|
18677
|
+
new RubyAstAnalyzer(),
|
|
18678
|
+
new PhpAstAnalyzer()
|
|
17681
18679
|
];
|
|
17682
18680
|
}
|
|
17683
18681
|
async analyze(input) {
|
|
@@ -17737,7 +18735,7 @@ var UsageContextEngine = class {
|
|
|
17737
18735
|
try {
|
|
17738
18736
|
const result = await analyzer.analyze(runContext);
|
|
17739
18737
|
const resultByKey = new Map(
|
|
17740
|
-
result.packages.map((pkg) => [
|
|
18738
|
+
result.packages.map((pkg) => [packageKey2(pkg.ecosystem, pkg.packageName), pkg])
|
|
17741
18739
|
);
|
|
17742
18740
|
errors.push(...result.errors);
|
|
17743
18741
|
remainingSnippets = Math.max(0, remainingSnippets - result.snippetsProduced);
|
|
@@ -17745,7 +18743,7 @@ var UsageContextEngine = class {
|
|
|
17745
18743
|
let unsupportedCount = 0;
|
|
17746
18744
|
let analysisErrorCount = 0;
|
|
17747
18745
|
for (const pkg of packages) {
|
|
17748
|
-
const analyzed = resultByKey.get(
|
|
18746
|
+
const analyzed = resultByKey.get(packageKey2(pkg.ecosystem, pkg.packageName)) ?? {
|
|
17749
18747
|
packageName: pkg.packageName,
|
|
17750
18748
|
ecosystem: pkg.ecosystem,
|
|
17751
18749
|
status: "analysis_error",
|
|
@@ -17850,13 +18848,13 @@ var UsageContextEngine = class {
|
|
|
17850
18848
|
buildVulnerablePackages(vulnerabilities, dependencies) {
|
|
17851
18849
|
const directMap = /* @__PURE__ */ new Map();
|
|
17852
18850
|
for (const dependency of dependencies) {
|
|
17853
|
-
const key =
|
|
18851
|
+
const key = packageKey2(dependency.ecosystem, dependency.name);
|
|
17854
18852
|
const existing = directMap.get(key) ?? false;
|
|
17855
18853
|
directMap.set(key, existing || dependency.direct);
|
|
17856
18854
|
}
|
|
17857
18855
|
const grouped = /* @__PURE__ */ new Map();
|
|
17858
18856
|
for (const vulnerability of vulnerabilities) {
|
|
17859
|
-
const key =
|
|
18857
|
+
const key = packageKey2(vulnerability.ecosystem, vulnerability.packageName);
|
|
17860
18858
|
const existing = grouped.get(key);
|
|
17861
18859
|
if (existing) {
|
|
17862
18860
|
existing.vulnerabilities.push(vulnerability);
|
|
@@ -17880,7 +18878,7 @@ var UsageContextEngine = class {
|
|
|
17880
18878
|
return n > 0 ? n : fallback;
|
|
17881
18879
|
}
|
|
17882
18880
|
};
|
|
17883
|
-
function
|
|
18881
|
+
function packageKey2(ecosystem, packageName) {
|
|
17884
18882
|
return `${ecosystem}::${packageName}`;
|
|
17885
18883
|
}
|
|
17886
18884
|
function groupByEcosystem(packages) {
|
|
@@ -17907,9 +18905,9 @@ async function scan(config) {
|
|
|
17907
18905
|
const sbom = artifacts.cyclonedx;
|
|
17908
18906
|
const outputPaths = deriveArtifactOutputPaths(sbomOutput);
|
|
17909
18907
|
await Promise.all([
|
|
17910
|
-
(0,
|
|
17911
|
-
(0,
|
|
17912
|
-
(0,
|
|
18908
|
+
(0, import_promises16.writeFile)(outputPaths.cyclonedx, artifacts.cyclonedx.content, "utf-8"),
|
|
18909
|
+
(0, import_promises16.writeFile)(outputPaths.spdx, artifacts.spdx.content, "utf-8"),
|
|
18910
|
+
(0, import_promises16.writeFile)(outputPaths.swid, artifacts.swid.content, "utf-8")
|
|
17913
18911
|
]);
|
|
17914
18912
|
let cveCheck;
|
|
17915
18913
|
if (skipCveCheck) {
|
|
@@ -17951,7 +18949,7 @@ async function scan(config) {
|
|
|
17951
18949
|
};
|
|
17952
18950
|
}
|
|
17953
18951
|
usageContext.artifactPath = outputPaths.usageContext;
|
|
17954
|
-
await (0,
|
|
18952
|
+
await (0, import_promises16.writeFile)(outputPaths.usageContext, JSON.stringify(usageContext, null, 2), "utf-8");
|
|
17955
18953
|
}
|
|
17956
18954
|
const summary = {
|
|
17957
18955
|
totalDependencies: scanResult.dependencies.length,
|
|
@@ -17989,7 +18987,7 @@ async function uploadToVerimu(report, config) {
|
|
|
17989
18987
|
throw new Error("API key required for upload");
|
|
17990
18988
|
}
|
|
17991
18989
|
const client = new VerimuApiClient(config.apiKey, config.apiBaseUrl);
|
|
17992
|
-
const projectName = (0,
|
|
18990
|
+
const projectName = (0, import_path17.basename)(config.projectPath);
|
|
17993
18991
|
const upsertRes = await client.upsertProject({
|
|
17994
18992
|
name: projectName,
|
|
17995
18993
|
ecosystem: report.project.ecosystem
|
|
@@ -18023,16 +19021,16 @@ function printReport(report) {
|
|
|
18023
19021
|
console.log(reporter.report(report));
|
|
18024
19022
|
}
|
|
18025
19023
|
function deriveArtifactOutputPaths(cycloneDxOutput) {
|
|
18026
|
-
const parsed = (0,
|
|
19024
|
+
const parsed = (0, import_path17.parse)(cycloneDxOutput);
|
|
18027
19025
|
let baseName = parsed.name;
|
|
18028
19026
|
if (parsed.ext === ".json" && baseName.endsWith(".cdx")) {
|
|
18029
19027
|
baseName = baseName.slice(0, -4);
|
|
18030
19028
|
}
|
|
18031
19029
|
return {
|
|
18032
19030
|
cyclonedx: cycloneDxOutput,
|
|
18033
|
-
spdx: (0,
|
|
18034
|
-
swid: (0,
|
|
18035
|
-
usageContext: (0,
|
|
19031
|
+
spdx: (0, import_path17.join)(parsed.dir, `${baseName}.spdx.json`),
|
|
19032
|
+
swid: (0, import_path17.join)(parsed.dir, `${baseName}.swid.xml`),
|
|
19033
|
+
usageContext: (0, import_path17.join)(parsed.dir, `${baseName}.usage-context.json`)
|
|
18036
19034
|
};
|
|
18037
19035
|
}
|
|
18038
19036
|
function buildUploadPayload(report) {
|