@socketsecurity/cli-with-sentry 1.6.2 → 1.8.0
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/CHANGELOG.md +17 -0
- package/README.md +5 -5
- package/dist/cli.js +350 -268
- package/dist/cli.js.map +1 -1
- package/dist/constants.js +4 -4
- package/dist/constants.js.map +1 -1
- package/dist/manifest-scripts/maven-extension/coana-maven-extension.jar +0 -0
- package/dist/manifest-scripts/socket-facts.init.gradle +2 -65
- package/dist/manifest-scripts/socket-facts.plugin.scala +75 -41
- package/dist/tsconfig.dts.tsbuildinfo +1 -1
- package/dist/types/commands/fix/coana-fix.d.mts.map +1 -1
- package/dist/types/commands/fix/generated-socket-facts.d.mts +4 -2
- package/dist/types/commands/fix/generated-socket-facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/cmd-manifest-dynamic-sbom-inference.d.mts.map +1 -1
- package/dist/types/commands/manifest/convert-gradle-to-facts.d.mts +2 -1
- package/dist/types/commands/manifest/convert-gradle-to-facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/convert-maven-to-facts.d.mts +2 -1
- package/dist/types/commands/manifest/convert-maven-to-facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/convert-sbt-to-facts.d.mts +1 -1
- package/dist/types/commands/manifest/convert-sbt-to-facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/run-manifest-facts.d.mts +3 -2
- package/dist/types/commands/manifest/run-manifest-facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/assemble.d.mts +0 -1
- package/dist/types/commands/manifest/scripts/assemble.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/build-tool.d.mts +2 -0
- package/dist/types/commands/manifest/scripts/build-tool.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/facts.d.mts +15 -9
- package/dist/types/commands/manifest/scripts/facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/records.d.mts +6 -7
- package/dist/types/commands/manifest/scripts/records.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/run.d.mts +2 -0
- package/dist/types/commands/manifest/scripts/run.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/sidecar.d.mts +3 -1
- package/dist/types/commands/manifest/scripts/sidecar.d.mts.map +1 -1
- package/dist/types/commands/scan/handle-create-new-scan.d.mts.map +1 -1
- package/dist/types/commands/scan/perform-reachability-analysis.d.mts.map +1 -1
- package/dist/types/utils/coana.d.mts +7 -0
- package/dist/types/utils/coana.d.mts.map +1 -1
- package/dist/utils.js +16 -1
- package/dist/utils.js.map +1 -1
- package/external/blessed/vendor/tng.js +1 -1
- package/external/blessed-contrib/lib/widget/charts/bar.js +2 -2
- package/external/blessed-contrib/lib/widget/charts/line.js +2 -2
- package/external/blessed-contrib/lib/widget/table.js +3 -3
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1857,8 +1857,12 @@ withPaths = true) {
|
|
|
1857
1857
|
};
|
|
1858
1858
|
acc.set(factsFile, {
|
|
1859
1859
|
components: facts.components.map(paths),
|
|
1860
|
-
projects: (facts.projects ?? []).map(
|
|
1860
|
+
projects: (facts.projects ?? []).map(({
|
|
1861
|
+
children: _children,
|
|
1862
|
+
...proj
|
|
1863
|
+
}) => ({
|
|
1861
1864
|
...paths(proj),
|
|
1865
|
+
dependencies: [...(artifactPaths.directDependenciesByProject.get(proj.id) ?? [])],
|
|
1862
1866
|
classpath: [...(artifactPaths.classpathByProject.get(proj.id) ?? [])]
|
|
1863
1867
|
}))
|
|
1864
1868
|
});
|
|
@@ -1955,15 +1959,13 @@ async function performReachabilityAnalysis(options) {
|
|
|
1955
1959
|
}
|
|
1956
1960
|
const sockSdk = sockSdkCResult.data;
|
|
1957
1961
|
spinner?.start('Uploading manifests for reachability analysis...');
|
|
1962
|
+
const outputFilePath = outputPath || constants.default.DOT_SOCKET_DOT_FACTS_JSON;
|
|
1958
1963
|
|
|
1959
1964
|
// Ensure uploaded manifest files are relative to analysis target as coana resolves SBOM manifest files relative to this path
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
// facts files are cleaned up downstream — see the post-success
|
|
1965
|
-
// deletion in handle-create-new-scan.mts.
|
|
1966
|
-
const uploadCResult = await utils.handleApiCall(sockSdk.uploadManifestFiles(orgSlug, packagePaths, {
|
|
1965
|
+
const uploadCResult = await utils.handleApiCall(sockSdk.uploadManifestFiles(orgSlug, packagePaths.filter(p => !utils.isReachabilityReportPath(p, {
|
|
1966
|
+
cwd,
|
|
1967
|
+
outputPath: outputFilePath
|
|
1968
|
+
})), {
|
|
1967
1969
|
pathsRelativeTo: path.resolve(cwd, analysisTarget)
|
|
1968
1970
|
}), {
|
|
1969
1971
|
description: 'upload manifests',
|
|
@@ -1991,7 +1993,6 @@ async function performReachabilityAnalysis(options) {
|
|
|
1991
1993
|
spinner?.success(`Manifests uploaded successfully. Tar hash: ${tarHash}`);
|
|
1992
1994
|
spinner?.start();
|
|
1993
1995
|
spinner?.infoAndStop('Running reachability analysis with Coana...');
|
|
1994
|
-
const outputFilePath = outputPath || constants.default.DOT_SOCKET_DOT_FACTS_JSON;
|
|
1995
1996
|
|
|
1996
1997
|
// Temp file for --compute-artifacts-sidecar, removed in the finally below.
|
|
1997
1998
|
// Written even when empty under dynamicSbomInference, since the
|
|
@@ -2282,6 +2283,15 @@ const BUILD_TOOL_WRAPPER = {
|
|
|
2282
2283
|
maven: 'mvnw'
|
|
2283
2284
|
};
|
|
2284
2285
|
|
|
2286
|
+
// Gradle (8+) and sbt hold one build per directory; Maven builds are addressed
|
|
2287
|
+
// by POM file, so `mvn -f other-pom.xml` puts a second build in the directory.
|
|
2288
|
+
const ADDRESSED_BY_FILE = {
|
|
2289
|
+
__proto__: null,
|
|
2290
|
+
gradle: false,
|
|
2291
|
+
maven: true,
|
|
2292
|
+
sbt: false
|
|
2293
|
+
};
|
|
2294
|
+
|
|
2285
2295
|
// sbt happily runs in any directory, synthesizing a default project from its
|
|
2286
2296
|
// name, so an sbt run outside a build yields a plausible but bogus SBOM. Maven
|
|
2287
2297
|
// and Gradle refuse such a directory themselves.
|
|
@@ -2299,6 +2309,14 @@ function resolveBuildToolBin(tool, projectDir, bin) {
|
|
|
2299
2309
|
return DEFAULT_BUILD_TOOL_BIN[tool];
|
|
2300
2310
|
}
|
|
2301
2311
|
|
|
2312
|
+
// Distinct for every build sharing a directory.
|
|
2313
|
+
function socketFactsFileName(tool, entryFile) {
|
|
2314
|
+
if (!ADDRESSED_BY_FILE[tool]) {
|
|
2315
|
+
return `${tool}${constants.default.DOT_SOCKET_DOT_FACTS_JSON}`;
|
|
2316
|
+
}
|
|
2317
|
+
return entryFile ? `${path.basename(entryFile)}${constants.default.DOT_SOCKET_DOT_FACTS_JSON}` : undefined;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2302
2320
|
// Gradle's variant-aware resolver: distinct exceptions give mutually-exclusive
|
|
2303
2321
|
// phrasing, so most-specific-first substring checks classify reliably.
|
|
2304
2322
|
function classifyGradleFailure(detail) {
|
|
@@ -2659,13 +2677,14 @@ const PURL_TYPE_MAVEN = 'maven';
|
|
|
2659
2677
|
function assembleFacts(parsed, opts = {}) {
|
|
2660
2678
|
const fileExists = opts.fileExists ?? fs$1.existsSync;
|
|
2661
2679
|
const perRoot = buildPerRoot(parsed);
|
|
2662
|
-
const
|
|
2663
|
-
|
|
2664
|
-
finalNodes
|
|
2665
|
-
} = mergeById(perRoot);
|
|
2680
|
+
const finalNodes = mergeById(perRoot);
|
|
2681
|
+
const graphs = buildSubprojectGraphs(perRoot);
|
|
2666
2682
|
const tool = parsed.tool || 'gradle';
|
|
2667
|
-
const components = buildComponents(finalNodes
|
|
2668
|
-
const
|
|
2683
|
+
const components = buildComponents(finalNodes);
|
|
2684
|
+
const {
|
|
2685
|
+
dependencies,
|
|
2686
|
+
projects
|
|
2687
|
+
} = buildDependencyGraph(parsed, graphs, components);
|
|
2669
2688
|
const metadata = {
|
|
2670
2689
|
format: 'socket-facts-sbom',
|
|
2671
2690
|
tool,
|
|
@@ -2677,7 +2696,8 @@ function assembleFacts(parsed, opts = {}) {
|
|
|
2677
2696
|
const facts = projects.length ? {
|
|
2678
2697
|
metadata,
|
|
2679
2698
|
projects,
|
|
2680
|
-
components
|
|
2699
|
+
components,
|
|
2700
|
+
dependencies
|
|
2681
2701
|
} : {
|
|
2682
2702
|
metadata,
|
|
2683
2703
|
components
|
|
@@ -2685,7 +2705,7 @@ function assembleFacts(parsed, opts = {}) {
|
|
|
2685
2705
|
return {
|
|
2686
2706
|
facts,
|
|
2687
2707
|
report: buildReport(parsed),
|
|
2688
|
-
artifactPaths: buildArtifactPaths(finalNodes, [...parsed.projects.values()],
|
|
2708
|
+
artifactPaths: buildArtifactPaths(finalNodes, [...parsed.projects.values()], graphs, fileExists)
|
|
2689
2709
|
};
|
|
2690
2710
|
}
|
|
2691
2711
|
|
|
@@ -2744,17 +2764,15 @@ function buildPerRoot(parsed) {
|
|
|
2744
2764
|
// reachability, which needs each subproject's exact classpath.
|
|
2745
2765
|
function mergeById(perRoot) {
|
|
2746
2766
|
const finalNodes = new Map();
|
|
2747
|
-
const
|
|
2748
|
-
for (const [rootId, {
|
|
2767
|
+
for (const {
|
|
2749
2768
|
nodes,
|
|
2750
2769
|
prod
|
|
2751
|
-
}
|
|
2770
|
+
} of perRoot.values()) {
|
|
2752
2771
|
for (const [coordId, node] of nodes) {
|
|
2753
2772
|
let fn = finalNodes.get(coordId);
|
|
2754
2773
|
if (!fn) {
|
|
2755
2774
|
fn = {
|
|
2756
2775
|
coord: node.coord,
|
|
2757
|
-
children: new Set(),
|
|
2758
2776
|
prod: false,
|
|
2759
2777
|
direct: false,
|
|
2760
2778
|
project: node.project,
|
|
@@ -2768,56 +2786,14 @@ function mergeById(perRoot) {
|
|
|
2768
2786
|
if (node.direct) {
|
|
2769
2787
|
fn.direct = true;
|
|
2770
2788
|
}
|
|
2771
|
-
for (const c of node.children) {
|
|
2772
|
-
fn.children.add(c);
|
|
2773
|
-
}
|
|
2774
2789
|
for (const t of node.targets) {
|
|
2775
2790
|
fn.targets.add(t);
|
|
2776
2791
|
}
|
|
2777
|
-
if (node.direct) {
|
|
2778
|
-
let d = directByRoot.get(rootId);
|
|
2779
|
-
if (!d) {
|
|
2780
|
-
d = new Set();
|
|
2781
|
-
directByRoot.set(rootId, d);
|
|
2782
|
-
}
|
|
2783
|
-
d.add(coordId);
|
|
2784
|
-
}
|
|
2785
2792
|
}
|
|
2786
2793
|
}
|
|
2787
|
-
return
|
|
2788
|
-
finalNodes,
|
|
2789
|
-
directByRoot
|
|
2790
|
-
};
|
|
2791
|
-
}
|
|
2792
|
-
function buildManifestFilesById(parsed, directByRoot, perRoot) {
|
|
2793
|
-
const buildFilesByCoord = new Map();
|
|
2794
|
-
for (const [rootId, ids] of directByRoot) {
|
|
2795
|
-
const root = perRoot.get(rootId);
|
|
2796
|
-
const project = parsed.projects.get(root?.projectKey ?? '');
|
|
2797
|
-
for (const id of ids) {
|
|
2798
|
-
let set = buildFilesByCoord.get(id);
|
|
2799
|
-
if (!set) {
|
|
2800
|
-
set = new Set();
|
|
2801
|
-
buildFilesByCoord.set(id, set);
|
|
2802
|
-
}
|
|
2803
|
-
const coord = root?.nodes.get(id)?.coord;
|
|
2804
|
-
// Without a known declaring script, the project's build file, even one
|
|
2805
|
-
// absent on disk: its presence marks the dependency direct here.
|
|
2806
|
-
const declared = coord && project?.declaredIn.get(`${coord.group}:${coord.name}`);
|
|
2807
|
-
const files = declared ?? [...(project?.buildFiles ?? []), ...(project?.missingBuildFiles ?? [])];
|
|
2808
|
-
for (const f of files) {
|
|
2809
|
-
set.add(f);
|
|
2810
|
-
}
|
|
2811
|
-
}
|
|
2812
|
-
}
|
|
2813
|
-
return new Map([...buildFilesByCoord].map(({
|
|
2814
|
-
0: id,
|
|
2815
|
-
1: buildFiles
|
|
2816
|
-
}) => [id, [constants.default.DOT_SOCKET_DOT_FACTS_JSON, ...[...buildFiles].sort()].map(file => ({
|
|
2817
|
-
file
|
|
2818
|
-
}))]));
|
|
2794
|
+
return finalNodes;
|
|
2819
2795
|
}
|
|
2820
|
-
function buildComponents(finalNodes
|
|
2796
|
+
function buildComponents(finalNodes) {
|
|
2821
2797
|
return [...finalNodes.keys()].sort().map(id => {
|
|
2822
2798
|
const fn = finalNodes.get(id);
|
|
2823
2799
|
const c = fn.coord;
|
|
@@ -2851,92 +2827,188 @@ function buildComponents(finalNodes, manifestFilesById) {
|
|
|
2851
2827
|
if (fn.project) {
|
|
2852
2828
|
comp.firstParty = true;
|
|
2853
2829
|
}
|
|
2854
|
-
if (fn.children.size) {
|
|
2855
|
-
comp.dependencies = [...fn.children].sort();
|
|
2856
|
-
}
|
|
2857
|
-
const manifestFiles = manifestFilesById.get(id);
|
|
2858
|
-
if (manifestFiles) {
|
|
2859
|
-
comp.manifestFiles = manifestFiles;
|
|
2860
|
-
}
|
|
2861
2830
|
return comp;
|
|
2862
2831
|
});
|
|
2863
2832
|
}
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2833
|
+
|
|
2834
|
+
// One graph per subproject, its configurations merged.
|
|
2835
|
+
function buildSubprojectGraphs(perRoot) {
|
|
2836
|
+
const graphs = new Map();
|
|
2837
|
+
for (const {
|
|
2838
|
+
nodes,
|
|
2839
|
+
projectKey
|
|
2840
|
+
} of perRoot.values()) {
|
|
2841
|
+
let g = graphs.get(projectKey);
|
|
2842
|
+
if (!g) {
|
|
2843
|
+
g = {
|
|
2844
|
+
children: new Map(),
|
|
2845
|
+
direct: new Set()
|
|
2846
|
+
};
|
|
2847
|
+
graphs.set(projectKey, g);
|
|
2872
2848
|
}
|
|
2873
|
-
for (const
|
|
2874
|
-
set.
|
|
2849
|
+
for (const [coordId, node] of nodes) {
|
|
2850
|
+
let set = g.children.get(coordId);
|
|
2851
|
+
if (!set) {
|
|
2852
|
+
set = new Set();
|
|
2853
|
+
g.children.set(coordId, set);
|
|
2854
|
+
}
|
|
2855
|
+
for (const c of node.children) {
|
|
2856
|
+
set.add(c);
|
|
2857
|
+
}
|
|
2858
|
+
if (node.direct) {
|
|
2859
|
+
g.direct.add(coordId);
|
|
2860
|
+
}
|
|
2875
2861
|
}
|
|
2876
2862
|
}
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
}
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2863
|
+
return graphs;
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
// Equal subtrees share an entry; partition refinement (coarsest bisimulation)
|
|
2867
|
+
// keeps that exact on cycles too.
|
|
2868
|
+
function buildDependencyGraph(parsed, graphs, components) {
|
|
2869
|
+
const componentIndex = new Map(components.map((c, i) => [c.id, i]));
|
|
2870
|
+
const vertexIds = new Map();
|
|
2871
|
+
const labels = [];
|
|
2872
|
+
const edges = [];
|
|
2873
|
+
const vertex = (projectKey, coordId) => {
|
|
2874
|
+
const key = `${projectKey}\t${coordId}`;
|
|
2875
|
+
let v = vertexIds.get(key);
|
|
2876
|
+
if (v === undefined) {
|
|
2877
|
+
v = labels.length;
|
|
2878
|
+
vertexIds.set(key, v);
|
|
2879
|
+
labels.push(componentIndex.get(coordId));
|
|
2880
|
+
edges.push([]);
|
|
2881
|
+
}
|
|
2882
|
+
return v;
|
|
2883
|
+
};
|
|
2884
|
+
for (const [projectKey, {
|
|
2885
|
+
children
|
|
2886
|
+
}] of graphs) {
|
|
2887
|
+
for (const [coordId, kids] of children) {
|
|
2888
|
+
const v = vertex(projectKey, coordId);
|
|
2889
|
+
for (const c of kids) {
|
|
2890
|
+
edges[v].push(vertex(projectKey, c));
|
|
2891
|
+
}
|
|
2897
2892
|
}
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2893
|
+
}
|
|
2894
|
+
|
|
2895
|
+
// Each round splits blocks by their children's blocks; a round that splits
|
|
2896
|
+
// nothing is stable.
|
|
2897
|
+
let block = [...labels];
|
|
2898
|
+
let blockCount = new Set(block).size;
|
|
2899
|
+
for (;;) {
|
|
2900
|
+
const ids = new Map();
|
|
2901
|
+
block = block.map((b, v) => {
|
|
2902
|
+
const childBlocks = [...new Set(edges[v].map(c => block[c]))].sort((x, y) => x - y);
|
|
2903
|
+
const sig = `${b}:${childBlocks.join(',')}`;
|
|
2904
|
+
let id = ids.get(sig);
|
|
2905
|
+
if (id === undefined) {
|
|
2906
|
+
id = ids.size;
|
|
2907
|
+
ids.set(sig, id);
|
|
2908
|
+
}
|
|
2909
|
+
return id;
|
|
2910
|
+
});
|
|
2911
|
+
if (ids.size === blockCount) {
|
|
2912
|
+
break;
|
|
2913
|
+
}
|
|
2914
|
+
blockCount = ids.size;
|
|
2915
|
+
}
|
|
2916
|
+
const byComponent = (a, b) => componentIndex.get(a) - componentIndex.get(b);
|
|
2917
|
+
const projects = [...parsed.projects.values()].map(p => ({
|
|
2918
|
+
p,
|
|
2919
|
+
roots: treeRoots(p.projectKey, graphs.get(p.projectKey), byComponent)
|
|
2920
|
+
})).sort((a, b) => {
|
|
2921
|
+
const ka = `${a.p.dir} ${a.p.group}:${a.p.name}`;
|
|
2922
|
+
const kb = `${b.p.dir} ${b.p.group}:${b.p.name}`;
|
|
2903
2923
|
return ka < kb ? -1 : ka > kb ? 1 : 0;
|
|
2904
2924
|
});
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
const
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
if (!set) {
|
|
2915
|
-
set = new Set();
|
|
2916
|
-
idsByProjectKey.set(projectKey, set);
|
|
2925
|
+
|
|
2926
|
+
// Post-order over the sorted projects, so the numbering is stable.
|
|
2927
|
+
const indexOfBlock = new Map();
|
|
2928
|
+
const vertexOfBlock = new Map();
|
|
2929
|
+
const dependencies = [];
|
|
2930
|
+
const visit = v => {
|
|
2931
|
+
const b = block[v];
|
|
2932
|
+
if (vertexOfBlock.has(b)) {
|
|
2933
|
+
return;
|
|
2917
2934
|
}
|
|
2918
|
-
|
|
2919
|
-
|
|
2935
|
+
vertexOfBlock.set(b, v);
|
|
2936
|
+
for (const c of [...edges[v]].sort((x, y) => labels[x] - labels[y])) {
|
|
2937
|
+
visit(c);
|
|
2938
|
+
}
|
|
2939
|
+
indexOfBlock.set(b, dependencies.length);
|
|
2940
|
+
dependencies.push({
|
|
2941
|
+
component: labels[v]
|
|
2942
|
+
});
|
|
2943
|
+
};
|
|
2944
|
+
const indexOf = v => indexOfBlock.get(block[v]);
|
|
2945
|
+
for (const {
|
|
2946
|
+
p,
|
|
2947
|
+
roots
|
|
2948
|
+
} of projects) {
|
|
2949
|
+
for (const coordId of roots) {
|
|
2950
|
+
visit(vertex(p.projectKey, coordId));
|
|
2920
2951
|
}
|
|
2921
2952
|
}
|
|
2922
|
-
const
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
if (!set) {
|
|
2927
|
-
set = new Set();
|
|
2928
|
-
classpathByProject.set(key, set);
|
|
2953
|
+
for (const [b, v] of vertexOfBlock) {
|
|
2954
|
+
const children = [...new Set(edges[v].map(indexOf))].sort((x, y) => x - y);
|
|
2955
|
+
if (children.length) {
|
|
2956
|
+
dependencies[indexOfBlock.get(b)].children = children;
|
|
2929
2957
|
}
|
|
2930
|
-
|
|
2931
|
-
|
|
2958
|
+
}
|
|
2959
|
+
return {
|
|
2960
|
+
dependencies,
|
|
2961
|
+
projects: projects.map(({
|
|
2962
|
+
p,
|
|
2963
|
+
roots
|
|
2964
|
+
}) => {
|
|
2965
|
+
const entry = {
|
|
2966
|
+
id: p.projectKey,
|
|
2967
|
+
type: PURL_TYPE_MAVEN,
|
|
2968
|
+
namespace: p.group,
|
|
2969
|
+
name: p.name,
|
|
2970
|
+
...(p.version ? {
|
|
2971
|
+
version: p.version
|
|
2972
|
+
} : {}),
|
|
2973
|
+
subprojectDir: p.dir,
|
|
2974
|
+
children: roots.map(coordId => indexOf(vertex(p.projectKey, coordId))).sort((x, y) => x - y)
|
|
2975
|
+
};
|
|
2976
|
+
if (p.buildFiles.length) {
|
|
2977
|
+
entry.manifestFiles = [...new Set(p.buildFiles)].sort().map(file => ({
|
|
2978
|
+
file
|
|
2979
|
+
}));
|
|
2980
|
+
}
|
|
2981
|
+
return entry;
|
|
2982
|
+
})
|
|
2983
|
+
};
|
|
2984
|
+
}
|
|
2985
|
+
function treeRoots(projectKey, graph, byComponent) {
|
|
2986
|
+
if (!graph) {
|
|
2987
|
+
return [];
|
|
2988
|
+
}
|
|
2989
|
+
const roots = [...graph.direct].sort(byComponent);
|
|
2990
|
+
const reached = new Set();
|
|
2991
|
+
const stack = [...roots];
|
|
2992
|
+
while (stack.length) {
|
|
2993
|
+
const id = stack.pop();
|
|
2994
|
+
if (!reached.has(id)) {
|
|
2995
|
+
reached.add(id);
|
|
2996
|
+
stack.push(...graph.children.get(id));
|
|
2932
2997
|
}
|
|
2933
2998
|
}
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
}
|
|
2999
|
+
const unreached = [...graph.children.keys()].filter(id => !reached.has(id));
|
|
3000
|
+
if (unreached.length) {
|
|
3001
|
+
throw new Error(`Resolved dependencies of ${projectKey} are not reachable from its direct dependencies: ${unreached.sort(byComponent).join(', ')}`);
|
|
3002
|
+
}
|
|
3003
|
+
return roots;
|
|
3004
|
+
}
|
|
3005
|
+
function byProject(projects, graphs, ids) {
|
|
3006
|
+
return new Map(projects.map(p => {
|
|
3007
|
+
const g = graphs.get(p.projectKey);
|
|
3008
|
+
return [p.projectKey, g ? [...ids(g)].sort() : []];
|
|
3009
|
+
}));
|
|
2938
3010
|
}
|
|
2939
|
-
function buildArtifactPaths(finalNodes, projects,
|
|
3011
|
+
function buildArtifactPaths(finalNodes, projects, graphs, fileExists) {
|
|
2940
3012
|
const pathsById = new Map();
|
|
2941
3013
|
for (const [id, fn] of finalNodes) {
|
|
2942
3014
|
if (!fn.project) {
|
|
@@ -2956,7 +3028,8 @@ function buildArtifactPaths(finalNodes, projects, perRoot, fileExists) {
|
|
|
2956
3028
|
}
|
|
2957
3029
|
return {
|
|
2958
3030
|
pathsById,
|
|
2959
|
-
classpathByProject:
|
|
3031
|
+
classpathByProject: byProject(projects, graphs, g => g.children.keys()),
|
|
3032
|
+
directDependenciesByProject: byProject(projects, graphs, g => g.direct)
|
|
2960
3033
|
};
|
|
2961
3034
|
}
|
|
2962
3035
|
function buildReport(parsed) {
|
|
@@ -3128,11 +3201,11 @@ function serializeExcludePathPatterns(paths) {
|
|
|
3128
3201
|
//
|
|
3129
3202
|
// meta tool toolVersion javaVersion
|
|
3130
3203
|
// buildRoot path (absolute; the facts file's directory)
|
|
3204
|
+
// entry path (file-addressed builds; build-root-relative)
|
|
3131
3205
|
// project projectKey group name version dir
|
|
3132
3206
|
// projectSrc projectKey path (--with-files only)
|
|
3133
3207
|
// projectTgt projectKey path (--with-files only)
|
|
3134
|
-
// projectBuild projectKey path
|
|
3135
|
-
// declared projectKey group name path (script declaring the dependency; build-root-relative)
|
|
3208
|
+
// projectBuild projectKey path (build-root-relative; may be absent on disk)
|
|
3136
3209
|
// root rootId projectKey config prod(0|1)
|
|
3137
3210
|
// node rootId coordId group name version ext classifier direct(0|1) project
|
|
3138
3211
|
// edge rootId parentCoordId childCoordId
|
|
@@ -3173,6 +3246,7 @@ function parseRecords(text) {
|
|
|
3173
3246
|
toolVersion: '',
|
|
3174
3247
|
javaVersion: '',
|
|
3175
3248
|
buildRoot: '',
|
|
3249
|
+
entry: '',
|
|
3176
3250
|
projects: new Map(),
|
|
3177
3251
|
roots: new Map(),
|
|
3178
3252
|
scannedConfigs: [],
|
|
@@ -3206,9 +3280,7 @@ function parseRecords(text) {
|
|
|
3206
3280
|
dir: '',
|
|
3207
3281
|
sources: [],
|
|
3208
3282
|
targets: [],
|
|
3209
|
-
buildFiles: []
|
|
3210
|
-
missingBuildFiles: [],
|
|
3211
|
-
declaredIn: new Map()
|
|
3283
|
+
buildFiles: []
|
|
3212
3284
|
};
|
|
3213
3285
|
result.projects.set(key, p);
|
|
3214
3286
|
}
|
|
@@ -3228,6 +3300,9 @@ function parseRecords(text) {
|
|
|
3228
3300
|
case 'buildRoot':
|
|
3229
3301
|
result.buildRoot = f[1] ?? '';
|
|
3230
3302
|
break;
|
|
3303
|
+
case 'entry':
|
|
3304
|
+
result.entry = f[1] ?? '';
|
|
3305
|
+
break;
|
|
3231
3306
|
case 'project':
|
|
3232
3307
|
{
|
|
3233
3308
|
const p = project(f[1] ?? '');
|
|
@@ -3249,17 +3324,7 @@ function parseRecords(text) {
|
|
|
3249
3324
|
break;
|
|
3250
3325
|
case 'projectBuild':
|
|
3251
3326
|
if (f[2]) {
|
|
3252
|
-
|
|
3253
|
-
(f[3] === 'missing' ? p.missingBuildFiles : p.buildFiles).push(f[2]);
|
|
3254
|
-
}
|
|
3255
|
-
break;
|
|
3256
|
-
case 'declared':
|
|
3257
|
-
if (f[4]) {
|
|
3258
|
-
const {
|
|
3259
|
-
declaredIn
|
|
3260
|
-
} = project(f[1] ?? '');
|
|
3261
|
-
const ga = `${f[2] ?? ''}:${f[3] ?? ''}`;
|
|
3262
|
-
declaredIn.set(ga, [...(declaredIn.get(ga) ?? []), f[4]]);
|
|
3327
|
+
project(f[1] ?? '').buildFiles.push(f[2]);
|
|
3263
3328
|
}
|
|
3264
3329
|
break;
|
|
3265
3330
|
case 'root':
|
|
@@ -3392,9 +3457,10 @@ async function writeSbtPlugin(globalBase, sourceFilename, destFilename) {
|
|
|
3392
3457
|
});
|
|
3393
3458
|
await fs$1.promises.writeFile(path.join(pluginsDir, destFilename), src);
|
|
3394
3459
|
}
|
|
3395
|
-
async function assembleFromRecords(out, recordsFile) {
|
|
3460
|
+
async function assembleFromRecords(tool, out, recordsFile) {
|
|
3396
3461
|
const text = fs$1.existsSync(recordsFile) ? await fs$1.promises.readFile(recordsFile, 'utf8') : '';
|
|
3397
3462
|
const parsed = parseRecords(text);
|
|
3463
|
+
const factsFileName = socketFactsFileName(tool, parsed.entry || undefined);
|
|
3398
3464
|
const {
|
|
3399
3465
|
artifactPaths,
|
|
3400
3466
|
facts,
|
|
@@ -3404,6 +3470,7 @@ async function assembleFromRecords(out, recordsFile) {
|
|
|
3404
3470
|
buildRoot: parsed.buildRoot || undefined,
|
|
3405
3471
|
code: out.code,
|
|
3406
3472
|
facts,
|
|
3473
|
+
factsFileName,
|
|
3407
3474
|
report,
|
|
3408
3475
|
artifactPaths,
|
|
3409
3476
|
stderr: out.stderr,
|
|
@@ -3476,7 +3543,7 @@ async function invokeGradle(opts, initScriptFilename, task, tmpDirPrefix) {
|
|
|
3476
3543
|
// resolvedConfiguration API and shared accumulator aren't cache-safe.
|
|
3477
3544
|
const args = ['--init-script', initScript, '-Dorg.gradle.configuration-cache=false', `-Psocket.recordsFile=${recordsFile}`, ...commonProps(opts, '-P'), ...(opts.toolOpts ?? []), task, '--no-daemon', '--console=plain'];
|
|
3478
3545
|
const out = await runNeverThrow(bin, args, opts);
|
|
3479
|
-
return await assembleFromRecords(out, recordsFile);
|
|
3546
|
+
return await assembleFromRecords('gradle', out, recordsFile);
|
|
3480
3547
|
});
|
|
3481
3548
|
}
|
|
3482
3549
|
async function runGradle(opts) {
|
|
@@ -3502,7 +3569,7 @@ async function invokeSbtIn(globalBase, opts, pluginSourceFilename, pluginDestFil
|
|
|
3502
3569
|
const javaHomeOpt = javaHome && !(opts.toolOpts ?? []).includes('--java-home') ? ['--java-home', javaHome] : [];
|
|
3503
3570
|
const args = [...javaHomeOpt, ...props, ...(opts.toolOpts ?? []), '--batch', task];
|
|
3504
3571
|
const out = await runNeverThrow(bin, args, opts);
|
|
3505
|
-
return await assembleFromRecords(out, recordsFile);
|
|
3572
|
+
return await assembleFromRecords('sbt', out, recordsFile);
|
|
3506
3573
|
}
|
|
3507
3574
|
async function runSbt(opts) {
|
|
3508
3575
|
const run = globalBase => invokeSbtIn(globalBase, opts, 'socket-facts.plugin.scala', SBT_FACTS_PLUGIN_FILENAME, FACTS_TASK);
|
|
@@ -3529,7 +3596,7 @@ async function invokeMaven(opts, coanaTask, tmpDirPrefix) {
|
|
|
3529
3596
|
const props = [`-Dmaven.ext.class.path=${jarPath}`, `-Dcoana.task=${coanaTask}`, `-Dsocket.recordsFile=${recordsFile}`, ...commonProps(opts, '-D')];
|
|
3530
3597
|
const args = [...props, ...(opts.toolOpts ?? []), '--batch-mode', 'validate'];
|
|
3531
3598
|
const out = await runNeverThrow(bin, args, opts);
|
|
3532
|
-
return await assembleFromRecords(out, recordsFile);
|
|
3599
|
+
return await assembleFromRecords('maven', out, recordsFile);
|
|
3533
3600
|
});
|
|
3534
3601
|
}
|
|
3535
3602
|
async function runMaven(opts) {
|
|
@@ -3552,8 +3619,9 @@ function tailBuildOutput(stdout, stderr) {
|
|
|
3552
3619
|
// `process.exitCode`, which can already be non-zero for an unrelated reason.
|
|
3553
3620
|
|
|
3554
3621
|
// Runs the bundled build-tool resolution script for a JVM project and writes
|
|
3555
|
-
//
|
|
3556
|
-
// resolved artifact paths into
|
|
3622
|
+
// its `<entry>.socket.facts.json` (see socketFactsFileName). `withFiles`
|
|
3623
|
+
// (reachability only) additionally folds resolved artifact paths into
|
|
3624
|
+
// `sidecarAcc`. A blocking resolution failure sets
|
|
3557
3625
|
// a non-zero exit code and returns (matching the `--pom` generator) unless
|
|
3558
3626
|
// `ignoreUnresolved`; a crashed build — a process failure, not an unresolved
|
|
3559
3627
|
// dependency — always fails.
|
|
@@ -3639,6 +3707,7 @@ async function runManifestFacts({
|
|
|
3639
3707
|
buildRoot,
|
|
3640
3708
|
code,
|
|
3641
3709
|
facts,
|
|
3710
|
+
factsFileName,
|
|
3642
3711
|
report,
|
|
3643
3712
|
stderr,
|
|
3644
3713
|
stdout
|
|
@@ -3697,14 +3766,20 @@ async function runManifestFacts({
|
|
|
3697
3766
|
logger.logger.warn(`No resolvable ${ecosystem} dependencies found; nothing to upload.`);
|
|
3698
3767
|
return;
|
|
3699
3768
|
}
|
|
3700
|
-
if (!buildRoot) {
|
|
3769
|
+
if (!buildRoot || !factsFileName) {
|
|
3701
3770
|
process.exitCode = 1;
|
|
3702
|
-
logger.logger.fail(`The ${ecosystem} build did not report its root directory, so its Socket facts file cannot be placed.`);
|
|
3771
|
+
logger.logger.fail(`The ${ecosystem} build did not report its ${buildRoot ? 'entry build file' : 'root directory'}, so its Socket facts file cannot be placed.`);
|
|
3703
3772
|
return null;
|
|
3704
3773
|
}
|
|
3705
3774
|
// Every path in the facts is relative to the build root, which `-f`/`-p`
|
|
3706
3775
|
// can move away from cwd.
|
|
3707
|
-
const factsPath = path.join(buildRoot,
|
|
3776
|
+
const factsPath = path.join(buildRoot, factsFileName);
|
|
3777
|
+
// Not a name producers write, so a copy here is stale and would be uploaded
|
|
3778
|
+
// alongside the new file.
|
|
3779
|
+
const legacyFactsPath = path.join(buildRoot, constants.default.DOT_SOCKET_DOT_FACTS_JSON);
|
|
3780
|
+
if (fs$1.existsSync(legacyFactsPath)) {
|
|
3781
|
+
logger.logger.warn(`Found \`${legacyFactsPath}\`, which is uploaded alongside \`${factsFileName}\`. Delete it if an earlier \`socket manifest\` run left it behind.`);
|
|
3782
|
+
}
|
|
3708
3783
|
const socketCliVersion = constants.default.ENV.INLINED_SOCKET_CLI_VERSION;
|
|
3709
3784
|
if (facts.metadata && socketCliVersion) {
|
|
3710
3785
|
facts.metadata.socketCliVersion = socketCliVersion;
|
|
@@ -6385,7 +6460,8 @@ async function extractBazelToMaven(opts) {
|
|
|
6385
6460
|
}
|
|
6386
6461
|
}
|
|
6387
6462
|
|
|
6388
|
-
// Generates
|
|
6463
|
+
// Generates `gradle.socket.facts.json` for a Gradle project via the bundled
|
|
6464
|
+
// init script.
|
|
6389
6465
|
async function convertGradleToFacts({
|
|
6390
6466
|
bin,
|
|
6391
6467
|
cwd,
|
|
@@ -6415,7 +6491,8 @@ async function convertGradleToFacts({
|
|
|
6415
6491
|
});
|
|
6416
6492
|
}
|
|
6417
6493
|
|
|
6418
|
-
// Generates
|
|
6494
|
+
// Generates `pom.xml.socket.facts.json` (named after the POM Maven runs on)
|
|
6495
|
+
// for a Maven project via the bundled extension.
|
|
6419
6496
|
async function convertMavenToFacts({
|
|
6420
6497
|
bin,
|
|
6421
6498
|
cwd,
|
|
@@ -6445,7 +6522,7 @@ async function convertMavenToFacts({
|
|
|
6445
6522
|
});
|
|
6446
6523
|
}
|
|
6447
6524
|
|
|
6448
|
-
// Generates
|
|
6525
|
+
// Generates `sbt.socket.facts.json` for an sbt project via the bundled sbt plugin.
|
|
6449
6526
|
// sbt 0.13/early 1.x can't run on modern JDKs — pass a compatible JDK via
|
|
6450
6527
|
// `--sbt-opts "--java-home <path>"` or `JAVA_HOME`.
|
|
6451
6528
|
async function convertSbtToFacts({
|
|
@@ -7342,12 +7419,12 @@ async function createNewScan({
|
|
|
7342
7419
|
logger.logger.success('Reachability analysis completed successfully');
|
|
7343
7420
|
reachabilityReport = reachResult.data?.reachabilityReport;
|
|
7344
7421
|
|
|
7345
|
-
//
|
|
7346
|
-
//
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7422
|
+
// Mirror the SBOM inputs Coana analyzed; otherwise its fresh report
|
|
7423
|
+
// (appended below) supersedes every facts file.
|
|
7424
|
+
const pathsForScan = reach.reachUseOnlyPregeneratedSboms ? filterToPregeneratedSboms(packagePaths, supportedFiles).filter(p => !utils.isReachabilityReportPath(p, {
|
|
7425
|
+
cwd,
|
|
7426
|
+
outputPath: constants.default.DOT_SOCKET_DOT_FACTS_JSON
|
|
7427
|
+
})) : packagePaths.filter(p => !utils.isSocketFactsFile(p));
|
|
7351
7428
|
|
|
7352
7429
|
// Append coana's reachability report, but not twice: a pre-generated facts
|
|
7353
7430
|
// input can resolve to the same path coana wrote its report to.
|
|
@@ -7396,17 +7473,8 @@ async function createNewScan({
|
|
|
7396
7473
|
logger.logger.warn('Reachability analysis ran but no full application reachability scan ID was found; skipping reachability finalize. The scan was created but its reachability report was not linked.');
|
|
7397
7474
|
}
|
|
7398
7475
|
|
|
7399
|
-
//
|
|
7400
|
-
//
|
|
7401
|
-
// scans leave the file in place for debugging. Producer-written files
|
|
7402
|
-
// (e.g. from `socket manifest gradle --facts`) are NOT touched here —
|
|
7403
|
-
// those are user-owned input that the user can clean up themselves; in
|
|
7404
|
-
// the --reach path coana overwrites that file with its enriched output
|
|
7405
|
-
// anyway, so it's the same path that gets removed. `--reach-retain-facts-file`
|
|
7406
|
-
// opts out of this cleanup so the report can be inspected; the user is then
|
|
7407
|
-
// responsible for deleting it before the next full application reachability
|
|
7408
|
-
// scan (a stale file is picked up as pre-generated input and would make those
|
|
7409
|
-
// results unreliable).
|
|
7476
|
+
// A scan without --reach would upload a leftover report as an SBOM with
|
|
7477
|
+
// stale reachability results; a failed scan keeps it for debugging.
|
|
7410
7478
|
if (fullScanCResult.ok && scanId && reachabilityReport && !reach.reachRetainFactsFile) {
|
|
7411
7479
|
try {
|
|
7412
7480
|
await fs.unlink(path.resolve(cwd, reachabilityReport));
|
|
@@ -8844,49 +8912,75 @@ async function getFixEnv() {
|
|
|
8844
8912
|
};
|
|
8845
8913
|
}
|
|
8846
8914
|
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
excludePaths,
|
|
8851
|
-
tmpDir
|
|
8852
|
-
}) {
|
|
8853
|
-
const {
|
|
8854
|
-
factsPaths,
|
|
8855
|
-
resolvedPathsSidecar
|
|
8856
|
-
} = await runDynamicSbomInference({
|
|
8857
|
-
cwd,
|
|
8858
|
-
excludePaths,
|
|
8859
|
-
sbtTmpDir: undefined,
|
|
8860
|
-
sidecar: true,
|
|
8861
|
-
withFiles: false
|
|
8915
|
+
async function backUp(sources, dir) {
|
|
8916
|
+
await fs.mkdir(dir, {
|
|
8917
|
+
recursive: true
|
|
8862
8918
|
});
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
await fs.writeFile(sidecarFile, JSON.stringify(resolvedPathsSidecar));
|
|
8866
|
-
}
|
|
8867
|
-
const paths = factsPaths.map(p => path.resolve(cwd, p));
|
|
8868
|
-
const backups = await Promise.all(paths.map(async (source, index) => {
|
|
8869
|
-
const backup = path.join(tmpDir, `${index}.json`);
|
|
8919
|
+
return await Promise.all(sources.map(async (source, index) => {
|
|
8920
|
+
const backup = path.join(dir, `${index}.json`);
|
|
8870
8921
|
await fs.copyFile(source, backup);
|
|
8871
8922
|
return {
|
|
8872
8923
|
backup,
|
|
8873
8924
|
source
|
|
8874
8925
|
};
|
|
8875
8926
|
}));
|
|
8927
|
+
}
|
|
8928
|
+
async function restoreAll(backups) {
|
|
8929
|
+
await Promise.all(backups.map(({
|
|
8930
|
+
backup,
|
|
8931
|
+
source
|
|
8932
|
+
}) => fs.copyFile(backup, source)));
|
|
8933
|
+
}
|
|
8934
|
+
|
|
8935
|
+
// Backed up so each fix attempt sees the pre-fix build after `git clean`, and
|
|
8936
|
+
// facts files already present are left as they were found.
|
|
8937
|
+
async function generateSocketFactsForFix({
|
|
8938
|
+
cwd,
|
|
8939
|
+
excludePaths,
|
|
8940
|
+
findFactsPaths,
|
|
8941
|
+
tmpDir
|
|
8942
|
+
}) {
|
|
8943
|
+
const existingPaths = await findFactsPaths();
|
|
8944
|
+
const originals = await backUp(existingPaths, path.join(tmpDir, 'existing'));
|
|
8945
|
+
const existing = new Set(existingPaths);
|
|
8946
|
+
const reset = async written => {
|
|
8947
|
+
await Promise.all(written.filter(p => !existing.has(p)).map(p => fs.rm(p, {
|
|
8948
|
+
force: true
|
|
8949
|
+
})));
|
|
8950
|
+
await restoreAll(originals);
|
|
8951
|
+
};
|
|
8952
|
+
let paths;
|
|
8953
|
+
let sidecarFile;
|
|
8954
|
+
try {
|
|
8955
|
+
const {
|
|
8956
|
+
factsPaths,
|
|
8957
|
+
resolvedPathsSidecar
|
|
8958
|
+
} = await runDynamicSbomInference({
|
|
8959
|
+
cwd,
|
|
8960
|
+
excludePaths,
|
|
8961
|
+
sbtTmpDir: undefined,
|
|
8962
|
+
sidecar: true,
|
|
8963
|
+
withFiles: false
|
|
8964
|
+
});
|
|
8965
|
+
paths = factsPaths.map(p => path.resolve(cwd, p));
|
|
8966
|
+
if (resolvedPathsSidecar) {
|
|
8967
|
+
sidecarFile = path.join(tmpDir, 'sidecar.json');
|
|
8968
|
+
await fs.writeFile(sidecarFile, JSON.stringify(resolvedPathsSidecar));
|
|
8969
|
+
}
|
|
8970
|
+
} catch (e) {
|
|
8971
|
+
// A failed build root aborts inference after others wrote their facts.
|
|
8972
|
+
await reset(await findFactsPaths());
|
|
8973
|
+
throw e;
|
|
8974
|
+
}
|
|
8975
|
+
const generated = new Set(paths);
|
|
8976
|
+
const backups = [...originals.filter(({
|
|
8977
|
+
source
|
|
8978
|
+
}) => !generated.has(source)), ...(await backUp(paths, path.join(tmpDir, 'generated')))];
|
|
8876
8979
|
return {
|
|
8877
8980
|
paths,
|
|
8878
8981
|
sidecarFile,
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
force: true
|
|
8882
|
-
})));
|
|
8883
|
-
},
|
|
8884
|
-
async restore() {
|
|
8885
|
-
await Promise.all(backups.map(({
|
|
8886
|
-
backup,
|
|
8887
|
-
source
|
|
8888
|
-
}) => fs.copyFile(backup, source)));
|
|
8889
|
-
}
|
|
8982
|
+
remove: () => reset(paths),
|
|
8983
|
+
restore: () => restoreAll(backups)
|
|
8890
8984
|
};
|
|
8891
8985
|
}
|
|
8892
8986
|
|
|
@@ -9097,51 +9191,36 @@ async function coanaFixWithFacts(fixConfig, factsSlot) {
|
|
|
9097
9191
|
// sibling manifest's references). --exclude stays separate as a hidden
|
|
9098
9192
|
// legacy escape hatch for the narrower "fix-application only" semantic.
|
|
9099
9193
|
const coanaExcludePatterns = [...exclude, ...excludePaths];
|
|
9100
|
-
const findScanFilepaths = () => utils.getPackageFilesForScan(['.'], supportedFiles, {
|
|
9194
|
+
const findScanFilepaths = async () => (await utils.getPackageFilesForScan(['.'], supportedFiles, {
|
|
9101
9195
|
additionalIgnores,
|
|
9102
9196
|
config: socketConfig,
|
|
9103
9197
|
cwd
|
|
9104
|
-
})
|
|
9198
|
+
})).filter(p => !utils.isReachabilityReportPath(p, {
|
|
9199
|
+
cwd,
|
|
9200
|
+
outputPath: constants.DOT_SOCKET_DOT_FACTS_JSON
|
|
9201
|
+
}));
|
|
9105
9202
|
const scanFilepaths = await findScanFilepaths();
|
|
9106
|
-
// Facts files are analysis artifacts and must be removed before re-running fix.
|
|
9107
|
-
const factsFiles = scanFilepaths.filter(utils.isSocketFactsFile);
|
|
9108
|
-
if (factsFiles.length) {
|
|
9109
|
-
if (!silence) {
|
|
9110
|
-
spinner?.stop();
|
|
9111
|
-
}
|
|
9112
|
-
return {
|
|
9113
|
-
ok: false,
|
|
9114
|
-
message: 'Found Socket facts files in manifest files',
|
|
9115
|
-
cause: `Delete the following ${words.pluralize('file', factsFiles.length)} before running socket fix again:\n` + factsFiles.map(p => ` - ${p}`).join('\n')
|
|
9116
|
-
};
|
|
9117
|
-
}
|
|
9118
9203
|
if (factsSlot) {
|
|
9119
9204
|
if (!silence) {
|
|
9120
9205
|
spinner?.stop();
|
|
9121
9206
|
logger.logger.info('Generating Socket facts for Gradle, sbt and Maven builds ...');
|
|
9122
9207
|
}
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
const partial = (await findScanFilepaths()).filter(utils.isSocketFactsFile);
|
|
9132
|
-
await Promise.all(partial.map(p => fs$1.promises.rm(p, {
|
|
9133
|
-
force: true
|
|
9134
|
-
})));
|
|
9135
|
-
throw e;
|
|
9136
|
-
}
|
|
9137
|
-
scanFilepaths.push(...factsSlot.generated.paths);
|
|
9208
|
+
factsSlot.generated = await generateSocketFactsForFix({
|
|
9209
|
+
cwd,
|
|
9210
|
+
excludePaths,
|
|
9211
|
+
findFactsPaths: async () => (await findScanFilepaths()).filter(utils.isSocketFactsFile),
|
|
9212
|
+
tmpDir: factsSlot.tmpDir
|
|
9213
|
+
});
|
|
9214
|
+
const known = new Set(scanFilepaths);
|
|
9215
|
+
scanFilepaths.push(...factsSlot.generated.paths.filter(p => !known.has(p)));
|
|
9138
9216
|
if (!silence) {
|
|
9139
9217
|
spinner?.start();
|
|
9140
9218
|
}
|
|
9141
9219
|
}
|
|
9142
9220
|
const sidecarFile = factsSlot?.generated?.sidecarFile;
|
|
9143
|
-
// Discovery only needs which artifacts the facts files resolve
|
|
9144
|
-
// fixes
|
|
9221
|
+
// Discovery only needs which artifacts the facts files resolve. Applying
|
|
9222
|
+
// fixes reads each project's tree; a Coana predating facts trees can only
|
|
9223
|
+
// tell from the sidecar's classpaths which projects resolve an artifact.
|
|
9145
9224
|
const discoveryFlags = factsSlot ? ['--maven-use-only-socket-facts'] : [];
|
|
9146
9225
|
const factsFlags = [...discoveryFlags, ...(sidecarFile ? ['--compute-artifacts-sidecar', sidecarFile] : [])];
|
|
9147
9226
|
const uploadCResult = await utils.handleApiCall(sockSdk.uploadManifestFiles(orgSlug, scanFilepaths, {
|
|
@@ -9897,7 +9976,7 @@ const reachabilityFlags = {
|
|
|
9897
9976
|
reachRetainFactsFile: {
|
|
9898
9977
|
type: 'boolean',
|
|
9899
9978
|
default: false,
|
|
9900
|
-
description: 'Keep the `.socket.facts.json` reachability report that the analysis writes to the scan directory instead of deleting it after a successful scan.
|
|
9979
|
+
description: 'Keep the `.socket.facts.json` reachability report that the analysis writes to the scan directory instead of deleting it after a successful scan. Delete it once inspected: later reachability scans ignore it, but a scan without --reach uploads it as an SBOM carrying stale reachability results.'
|
|
9901
9980
|
},
|
|
9902
9981
|
reachSkipCache: {
|
|
9903
9982
|
type: 'boolean',
|
|
@@ -12904,7 +12983,10 @@ const config$d = {
|
|
|
12904
12983
|
$ ${command} [options] [CWD=.]
|
|
12905
12984
|
|
|
12906
12985
|
Recursively walks CWD, discovers independent gradle, sbt, and maven build
|
|
12907
|
-
roots, and generates a Socket facts SBOM
|
|
12986
|
+
roots, and generates a Socket facts SBOM for each
|
|
12987
|
+
(pom.xml.socket.facts.json, gradle.socket.facts.json, or
|
|
12988
|
+
sbt.socket.facts.json, so builds sharing a directory never overwrite each
|
|
12989
|
+
other),
|
|
12908
12990
|
skipping subproject/reactor-module directories a parent build root already
|
|
12909
12991
|
covers. Unlike \`socket manifest auto\`, this looks beyond CWD itself.
|
|
12910
12992
|
|
|
@@ -12993,11 +13075,11 @@ const config$c = {
|
|
|
12993
13075
|
},
|
|
12994
13076
|
facts: {
|
|
12995
13077
|
type: 'boolean',
|
|
12996
|
-
description: 'Emit a Socket facts JSON file (
|
|
13078
|
+
description: 'Emit a Socket facts JSON file (`gradle.socket.facts.json`) describing the resolved dependency graph. This is the default; pass `--pom` to generate `pom.xml` files instead'
|
|
12997
13079
|
},
|
|
12998
13080
|
pom: {
|
|
12999
13081
|
type: 'boolean',
|
|
13000
|
-
description: 'Generate `pom.xml` manifest file(s) instead of the default Socket facts file (
|
|
13082
|
+
description: 'Generate `pom.xml` manifest file(s) instead of the default Socket facts file (`gradle.socket.facts.json`)'
|
|
13001
13083
|
},
|
|
13002
13084
|
includeConfigs: {
|
|
13003
13085
|
type: 'string',
|
|
@@ -13028,9 +13110,9 @@ const config$c = {
|
|
|
13028
13110
|
Options
|
|
13029
13111
|
${utils.getFlagListOutput(config.flags)}
|
|
13030
13112
|
|
|
13031
|
-
By default, emits a single
|
|
13032
|
-
dependency graph of the whole build, using gradle (preferably your
|
|
13033
|
-
\`gradlew\`). An unresolved dependency is a fatal error. You can pass
|
|
13113
|
+
By default, emits a single \`gradle.socket.facts.json\` describing the
|
|
13114
|
+
resolved dependency graph of the whole build, using gradle (preferably your
|
|
13115
|
+
local \`gradlew\`). An unresolved dependency is a fatal error. You can pass
|
|
13034
13116
|
--include-configs / --exclude-configs (comma-separated glob patterns) to
|
|
13035
13117
|
control which configurations are resolved (e.g.
|
|
13036
13118
|
--include-configs=\`*CompileClasspath,*RuntimeClasspath\`), and
|
|
@@ -13247,11 +13329,11 @@ const config$b = {
|
|
|
13247
13329
|
},
|
|
13248
13330
|
facts: {
|
|
13249
13331
|
type: 'boolean',
|
|
13250
|
-
description: 'Emit a Socket facts JSON file (
|
|
13332
|
+
description: 'Emit a Socket facts JSON file (`gradle.socket.facts.json`) describing the resolved dependency graph. This is the default; pass `--pom` to generate `pom.xml` files instead'
|
|
13251
13333
|
},
|
|
13252
13334
|
pom: {
|
|
13253
13335
|
type: 'boolean',
|
|
13254
|
-
description: 'Generate `pom.xml` manifest file(s) instead of the default Socket facts file (
|
|
13336
|
+
description: 'Generate `pom.xml` manifest file(s) instead of the default Socket facts file (`gradle.socket.facts.json`)'
|
|
13255
13337
|
},
|
|
13256
13338
|
includeConfigs: {
|
|
13257
13339
|
type: 'string',
|
|
@@ -13282,9 +13364,9 @@ const config$b = {
|
|
|
13282
13364
|
Options
|
|
13283
13365
|
${utils.getFlagListOutput(config.flags)}
|
|
13284
13366
|
|
|
13285
|
-
By default, emits a single
|
|
13286
|
-
dependency graph of the whole build, using gradle (preferably your
|
|
13287
|
-
\`gradlew\`). An unresolved dependency is a fatal error. You can pass
|
|
13367
|
+
By default, emits a single \`gradle.socket.facts.json\` describing the
|
|
13368
|
+
resolved dependency graph of the whole build, using gradle (preferably your
|
|
13369
|
+
local \`gradlew\`). An unresolved dependency is a fatal error. You can pass
|
|
13288
13370
|
--include-configs / --exclude-configs (comma-separated glob patterns) to
|
|
13289
13371
|
control which configurations are resolved (e.g.
|
|
13290
13372
|
--include-configs=\`*CompileClasspath,*RuntimeClasspath\`), and
|
|
@@ -13521,8 +13603,8 @@ const config$a = {
|
|
|
13521
13603
|
Options
|
|
13522
13604
|
${utils.getFlagListOutput(config.flags)}
|
|
13523
13605
|
|
|
13524
|
-
Emits a single
|
|
13525
|
-
graph of your Maven project, using maven (\`mvn\` on PATH by default). It
|
|
13606
|
+
Emits a single \`pom.xml.socket.facts.json\` (named after the POM Maven
|
|
13607
|
+
runs on) describing the resolved dependency graph of your Maven project, using maven (\`mvn\` on PATH by default). It
|
|
13526
13608
|
reads dependency metadata only and never downloads artifacts; an unresolved
|
|
13527
13609
|
dependency is a fatal error. You can pass --include-configs /
|
|
13528
13610
|
--exclude-configs (comma-separated glob patterns) to control which Maven
|
|
@@ -13685,11 +13767,11 @@ const config$9 = {
|
|
|
13685
13767
|
},
|
|
13686
13768
|
facts: {
|
|
13687
13769
|
type: 'boolean',
|
|
13688
|
-
description: 'Emit a Socket facts JSON file (
|
|
13770
|
+
description: 'Emit a Socket facts JSON file (`sbt.socket.facts.json`) describing the resolved dependency graph. This is the default; pass `--pom` to generate `pom.xml` files instead'
|
|
13689
13771
|
},
|
|
13690
13772
|
pom: {
|
|
13691
13773
|
type: 'boolean',
|
|
13692
|
-
description: 'Generate `pom.xml` manifest file(s) instead of the default Socket facts file (
|
|
13774
|
+
description: 'Generate `pom.xml` manifest file(s) instead of the default Socket facts file (`sbt.socket.facts.json`)'
|
|
13693
13775
|
},
|
|
13694
13776
|
includeConfigs: {
|
|
13695
13777
|
type: 'string',
|
|
@@ -13706,7 +13788,7 @@ const config$9 = {
|
|
|
13706
13788
|
},
|
|
13707
13789
|
out: {
|
|
13708
13790
|
type: 'string',
|
|
13709
|
-
description: 'Only with --pom: path of the output `pom.xml`, see also --stdout. Does not apply when generating Socket facts (always written to the project root as
|
|
13791
|
+
description: 'Only with --pom: path of the output `pom.xml`, see also --stdout. Does not apply when generating Socket facts (always written to the project root as `sbt.socket.facts.json`)'
|
|
13710
13792
|
},
|
|
13711
13793
|
stdout: {
|
|
13712
13794
|
type: 'boolean',
|
|
@@ -13728,8 +13810,8 @@ const config$9 = {
|
|
|
13728
13810
|
Options
|
|
13729
13811
|
${utils.getFlagListOutput(config.flags)}
|
|
13730
13812
|
|
|
13731
|
-
By default, emits a single
|
|
13732
|
-
dependency graph of the whole build. It reads dependency metadata only and
|
|
13813
|
+
By default, emits a single \`sbt.socket.facts.json\` describing the
|
|
13814
|
+
resolved dependency graph of the whole build. It reads dependency metadata only and
|
|
13733
13815
|
never downloads artifacts; an unresolved dependency is a fatal error. You
|
|
13734
13816
|
can pass --include-configs / --exclude-configs (comma-separated glob
|
|
13735
13817
|
patterns) to control which sbt configurations are resolved (e.g.
|
|
@@ -13910,7 +13992,7 @@ async function run$A(argv, importMeta, {
|
|
|
13910
13992
|
// would the file name be?
|
|
13911
13993
|
|
|
13912
13994
|
// --out / --stdout only affect the pom path. Socket facts are always written
|
|
13913
|
-
// to the project root as
|
|
13995
|
+
// to the project root as `sbt.socket.facts.json` so that `socket scan create`
|
|
13914
13996
|
// picks them up, so reject these flags in facts mode rather than silently
|
|
13915
13997
|
// ignoring an explicitly-passed output location.
|
|
13916
13998
|
const wasValidInput = utils.checkCommandInput(outputKind, {
|
|
@@ -13921,7 +14003,7 @@ async function run$A(argv, importMeta, {
|
|
|
13921
14003
|
}, {
|
|
13922
14004
|
nook: true,
|
|
13923
14005
|
test: !(facts && (cli.flags['out'] !== undefined || cli.flags['stdout'] !== undefined)),
|
|
13924
|
-
message: 'The `--out` and `--stdout` options only apply with `--pom`; Socket facts are always written to the project root as
|
|
14006
|
+
message: 'The `--out` and `--stdout` options only apply with `--pom`; Socket facts are always written to the project root as `sbt.socket.facts.json`',
|
|
13925
14007
|
fail: 'remove --out/--stdout, or pass --pom'
|
|
13926
14008
|
});
|
|
13927
14009
|
if (!wasValidInput) {
|
|
@@ -14514,7 +14596,7 @@ async function askForFactsFlag(current) {
|
|
|
14514
14596
|
choices: [{
|
|
14515
14597
|
name: 'Socket facts (default)',
|
|
14516
14598
|
value: 'yes',
|
|
14517
|
-
description: 'Generate a
|
|
14599
|
+
description: 'Generate a Socket facts file (*.socket.facts.json) describing the resolved dependency graph'
|
|
14518
14600
|
}, {
|
|
14519
14601
|
name: 'pom.xml',
|
|
14520
14602
|
value: 'no',
|
|
@@ -24478,5 +24560,5 @@ process.on('unhandledRejection', async (reason, promise) => {
|
|
|
24478
24560
|
// eslint-disable-next-line n/no-process-exit
|
|
24479
24561
|
process.exit(1);
|
|
24480
24562
|
});
|
|
24481
|
-
//# debugId=
|
|
24563
|
+
//# debugId=e5b43908-d002-4c63-b2f7-4c7336aacdb4
|
|
24482
24564
|
//# sourceMappingURL=cli.js.map
|