@socketsecurity/cli-with-sentry 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +5 -5
- package/dist/cli.js +184 -173
- 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 +100 -18
- package/dist/manifest-scripts/socket-facts.plugin.scala +30 -3
- package/dist/tsconfig.dts.tsbuildinfo +1 -1
- package/dist/types/commands/fix/coana-fix.d.mts.map +1 -1
- package/dist/types/commands/manifest/generate-recursive-manifests.d.mts +1 -1
- package/dist/types/commands/manifest/generate-recursive-manifests.d.mts.map +1 -1
- package/dist/types/commands/manifest/run-manifest-facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/assemble.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/facts.d.mts +18 -16
- package/dist/types/commands/manifest/scripts/facts.d.mts.map +1 -1
- package/dist/types/commands/manifest/scripts/records.d.mts +16 -2
- 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 +6 -8
- package/dist/types/commands/manifest/scripts/sidecar.d.mts.map +1 -1
- package/dist/types/commands/scan/cmd-scan-create.d.mts.map +1 -1
- package/dist/types/commands/scan/handle-create-new-scan.d.mts.map +1 -1
- package/dist/types/utils/coana.d.mts +3 -0
- package/dist/types/utils/coana.d.mts.map +1 -1
- package/dist/utils.js +10 -1
- package/dist/utils.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -1814,28 +1814,9 @@ function reachMemoryLimitToMb(value) {
|
|
|
1814
1814
|
return match[2]?.toLowerCase() === 'gb' ? amount * 1024 : amount;
|
|
1815
1815
|
}
|
|
1816
1816
|
|
|
1817
|
-
// No sources/targets here: those are local absolute paths, returned in-memory
|
|
1818
|
-
// as ResolvedArtifactPaths, never serialized into the SBOM.
|
|
1819
|
-
|
|
1820
|
-
// Relative to the facts file's directory.
|
|
1821
|
-
|
|
1822
|
-
// Resolved on-disk paths for a --with-files run, keyed by coordinate. `targets`
|
|
1823
|
-
// = classpath entries (jars / module output dirs); `sources` = module source
|
|
1824
|
-
// roots.
|
|
1825
|
-
|
|
1826
|
-
function projectClasspathKey(project) {
|
|
1827
|
-
return `${project.subprojectDir} ${project.namespace ?? ''}:${project.name}`;
|
|
1828
|
-
}
|
|
1829
|
-
|
|
1830
|
-
// Coordinate-based (not `id`-based) so it also matches foreign SBOMs like
|
|
1831
|
-
// CycloneDX. Empty segments dropped.
|
|
1832
|
-
function mavenCoordinateKey(groupId, artifactId, type, classifier, version) {
|
|
1833
|
-
return [groupId, artifactId, type, classifier, version].filter(Boolean).join(':');
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
1817
|
// Frozen contract with `coana run --compute-artifacts-sidecar`; change only
|
|
1837
1818
|
// in sync with the coana consumer. Keyed by the absolute path of the
|
|
1838
|
-
//
|
|
1819
|
+
// `*.socket.facts.json` file whose own projects[]/components[] these entries
|
|
1839
1820
|
// describe - the key IS the scope, so two independent reactors that happen to
|
|
1840
1821
|
// emit the same purl identity (e.g. a shared internal module name) can never
|
|
1841
1822
|
// collide: each is only ever looked up within its own key. No cross-reactor
|
|
@@ -1843,24 +1824,13 @@ function mavenCoordinateKey(groupId, artifactId, type, classifier, version) {
|
|
|
1843
1824
|
// independent reactors is intentionally duplicated across all of their
|
|
1844
1825
|
// components[].
|
|
1845
1826
|
|
|
1846
|
-
// `
|
|
1847
|
-
// for this coordinate - an empty array is a successful resolve that found
|
|
1848
|
-
// nothing (e.g. a pom/BOM with no artifact), not a failure. Both fields
|
|
1849
|
-
// omitted (undefined) means resolution couldn't even be attempted - the only
|
|
1850
|
-
// case here is a degenerate entry with no computable coordinate at all, since
|
|
1851
|
-
// every entry reaching this function already came from a resolved graph node
|
|
1852
|
-
// (an unresolved dependency lives in the resolution report, not here).
|
|
1827
|
+
// `[]` means resolved and found nothing (e.g. a pom/BOM with no artifact).
|
|
1853
1828
|
function attachPaths(entry, artifactPaths) {
|
|
1854
|
-
const
|
|
1855
|
-
if (!coordKey) {
|
|
1856
|
-
return {
|
|
1857
|
-
...entry
|
|
1858
|
-
};
|
|
1859
|
-
}
|
|
1829
|
+
const paths = artifactPaths.pathsById.get(entry.id);
|
|
1860
1830
|
return {
|
|
1861
1831
|
...entry,
|
|
1862
|
-
targets: [...(
|
|
1863
|
-
sources: [...(
|
|
1832
|
+
targets: [...(paths?.targets ?? [])],
|
|
1833
|
+
sources: [...(paths?.sources ?? [])]
|
|
1864
1834
|
};
|
|
1865
1835
|
}
|
|
1866
1836
|
function purlSortKey(entry) {
|
|
@@ -1877,9 +1847,8 @@ function sortByPurl(entries) {
|
|
|
1877
1847
|
// Emit an entry for every SBOM component AND every first-party project: a
|
|
1878
1848
|
// top-level module is a project, not a dependency component, yet its source
|
|
1879
1849
|
// roots are where reachability starts, so the sidecar must carry them.
|
|
1880
|
-
//
|
|
1881
|
-
//
|
|
1882
|
-
// existing last-writer-wins convention for that case.
|
|
1850
|
+
// Every build writes its own facts file, so a key is accumulated once; a
|
|
1851
|
+
// repeated call for the same factsFile (the same build run again) overwrites.
|
|
1883
1852
|
function accumulateSidecar(acc, facts, artifactPaths, factsFile,
|
|
1884
1853
|
// Off when artifact paths were not resolved; entries then omit `targets` and `sources`.
|
|
1885
1854
|
withPaths = true) {
|
|
@@ -1890,7 +1859,7 @@ withPaths = true) {
|
|
|
1890
1859
|
components: facts.components.map(paths),
|
|
1891
1860
|
projects: (facts.projects ?? []).map(proj => ({
|
|
1892
1861
|
...paths(proj),
|
|
1893
|
-
classpath: [...(artifactPaths.classpathByProject.get(
|
|
1862
|
+
classpath: [...(artifactPaths.classpathByProject.get(proj.id) ?? [])]
|
|
1894
1863
|
}))
|
|
1895
1864
|
});
|
|
1896
1865
|
}
|
|
@@ -2693,13 +2662,9 @@ function assembleFacts(parsed, opts = {}) {
|
|
|
2693
2662
|
const {
|
|
2694
2663
|
directByRoot,
|
|
2695
2664
|
finalNodes
|
|
2696
|
-
} =
|
|
2665
|
+
} = mergeById(perRoot);
|
|
2697
2666
|
const tool = parsed.tool || 'gradle';
|
|
2698
|
-
const
|
|
2699
|
-
for (const p of parsed.projects.values()) {
|
|
2700
|
-
projectsByGav.set(gav(p.group, p.name, p.version), p);
|
|
2701
|
-
}
|
|
2702
|
-
const components = buildComponents(finalNodes, projectsByGav, buildManifestFilesByCoord(parsed, directByRoot, perRoot));
|
|
2667
|
+
const components = buildComponents(finalNodes, buildManifestFilesById(parsed, directByRoot, perRoot));
|
|
2703
2668
|
const projects = opts.emitProjects === false ? [] : buildProjects(parsed, directByRoot, perRoot);
|
|
2704
2669
|
const metadata = {
|
|
2705
2670
|
format: 'socket-facts-sbom',
|
|
@@ -2720,35 +2685,50 @@ function assembleFacts(parsed, opts = {}) {
|
|
|
2720
2685
|
return {
|
|
2721
2686
|
facts,
|
|
2722
2687
|
report: buildReport(parsed),
|
|
2723
|
-
artifactPaths: buildArtifactPaths(finalNodes, [...parsed.projects.values()],
|
|
2688
|
+
artifactPaths: buildArtifactPaths(finalNodes, [...parsed.projects.values()], perRoot, fileExists)
|
|
2724
2689
|
};
|
|
2725
2690
|
}
|
|
2726
|
-
|
|
2727
|
-
|
|
2691
|
+
|
|
2692
|
+
// A node resolving to a build project takes that project's id, so projects
|
|
2693
|
+
// sharing a coordinate stay apart and the project's variants collapse into it.
|
|
2694
|
+
function componentId(coordId, project) {
|
|
2695
|
+
return project || coordId;
|
|
2728
2696
|
}
|
|
2729
2697
|
function buildPerRoot(parsed) {
|
|
2730
2698
|
const out = new Map();
|
|
2731
2699
|
for (const [rootId, r] of parsed.roots) {
|
|
2732
|
-
const
|
|
2700
|
+
const idOf = coordId => componentId(coordId, r.nodes.get(coordId)?.project ?? '');
|
|
2701
|
+
const nodes = new Map();
|
|
2702
|
+
for (const [coordId, n] of r.nodes) {
|
|
2703
|
+
const id = idOf(coordId);
|
|
2704
|
+
let node = nodes.get(id);
|
|
2705
|
+
if (!node) {
|
|
2706
|
+
node = {
|
|
2707
|
+
coord: n.project ? {
|
|
2708
|
+
...n.coord,
|
|
2709
|
+
classifier: '',
|
|
2710
|
+
ext: ''
|
|
2711
|
+
} : n.coord,
|
|
2712
|
+
children: [],
|
|
2713
|
+
direct: false,
|
|
2714
|
+
project: n.project,
|
|
2715
|
+
targets: []
|
|
2716
|
+
};
|
|
2717
|
+
nodes.set(id, node);
|
|
2718
|
+
}
|
|
2719
|
+
node.direct ||= n.direct;
|
|
2720
|
+
node.targets.push(...n.targets);
|
|
2721
|
+
}
|
|
2733
2722
|
for (const [p, c] of r.edges) {
|
|
2734
2723
|
if (!r.nodes.has(p) || !r.nodes.has(c)) {
|
|
2735
2724
|
continue;
|
|
2736
2725
|
}
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2726
|
+
const parentId = idOf(p);
|
|
2727
|
+
const childId = idOf(c);
|
|
2728
|
+
const parent = nodes.get(parentId);
|
|
2729
|
+
if (childId !== parentId && !parent.children.includes(childId)) {
|
|
2730
|
+
parent.children.push(childId);
|
|
2741
2731
|
}
|
|
2742
|
-
set.add(c);
|
|
2743
|
-
}
|
|
2744
|
-
const nodes = new Map();
|
|
2745
|
-
for (const [coordId, n] of r.nodes) {
|
|
2746
|
-
nodes.set(coordId, {
|
|
2747
|
-
coord: n.coord,
|
|
2748
|
-
children: [...(childrenByParent.get(coordId) ?? [])],
|
|
2749
|
-
direct: n.direct,
|
|
2750
|
-
targets: n.targets
|
|
2751
|
-
});
|
|
2752
2732
|
}
|
|
2753
2733
|
out.set(rootId, {
|
|
2754
2734
|
projectKey: r.projectKey,
|
|
@@ -2759,10 +2739,10 @@ function buildPerRoot(parsed) {
|
|
|
2759
2739
|
return out;
|
|
2760
2740
|
}
|
|
2761
2741
|
|
|
2762
|
-
// Components are merged by
|
|
2763
|
-
//
|
|
2764
|
-
//
|
|
2765
|
-
function
|
|
2742
|
+
// Components are merged by id across every resolution root; which ids belong
|
|
2743
|
+
// to which subproject is kept separately (classpathByProject) for
|
|
2744
|
+
// reachability, which needs each subproject's exact classpath.
|
|
2745
|
+
function mergeById(perRoot) {
|
|
2766
2746
|
const finalNodes = new Map();
|
|
2767
2747
|
const directByRoot = new Map();
|
|
2768
2748
|
for (const [rootId, {
|
|
@@ -2777,6 +2757,7 @@ function mergeByCoordinate(perRoot) {
|
|
|
2777
2757
|
children: new Set(),
|
|
2778
2758
|
prod: false,
|
|
2779
2759
|
direct: false,
|
|
2760
|
+
project: node.project,
|
|
2780
2761
|
targets: new Set()
|
|
2781
2762
|
};
|
|
2782
2763
|
finalNodes.set(coordId, fn);
|
|
@@ -2808,18 +2789,23 @@ function mergeByCoordinate(perRoot) {
|
|
|
2808
2789
|
directByRoot
|
|
2809
2790
|
};
|
|
2810
2791
|
}
|
|
2811
|
-
function
|
|
2792
|
+
function buildManifestFilesById(parsed, directByRoot, perRoot) {
|
|
2812
2793
|
const buildFilesByCoord = new Map();
|
|
2813
2794
|
for (const [rootId, ids] of directByRoot) {
|
|
2814
|
-
const
|
|
2815
|
-
const
|
|
2795
|
+
const root = perRoot.get(rootId);
|
|
2796
|
+
const project = parsed.projects.get(root?.projectKey ?? '');
|
|
2816
2797
|
for (const id of ids) {
|
|
2817
2798
|
let set = buildFilesByCoord.get(id);
|
|
2818
2799
|
if (!set) {
|
|
2819
2800
|
set = new Set();
|
|
2820
2801
|
buildFilesByCoord.set(id, set);
|
|
2821
2802
|
}
|
|
2822
|
-
|
|
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) {
|
|
2823
2809
|
set.add(f);
|
|
2824
2810
|
}
|
|
2825
2811
|
}
|
|
@@ -2831,7 +2817,7 @@ function buildManifestFilesByCoord(parsed, directByRoot, perRoot) {
|
|
|
2831
2817
|
file
|
|
2832
2818
|
}))]));
|
|
2833
2819
|
}
|
|
2834
|
-
function buildComponents(finalNodes,
|
|
2820
|
+
function buildComponents(finalNodes, manifestFilesById) {
|
|
2835
2821
|
return [...finalNodes.keys()].sort().map(id => {
|
|
2836
2822
|
const fn = finalNodes.get(id);
|
|
2837
2823
|
const c = fn.coord;
|
|
@@ -2862,13 +2848,13 @@ function buildComponents(finalNodes, projectsByGav, manifestFilesByCoord) {
|
|
|
2862
2848
|
if (!fn.prod) {
|
|
2863
2849
|
comp.dev = true;
|
|
2864
2850
|
}
|
|
2865
|
-
if (
|
|
2851
|
+
if (fn.project) {
|
|
2866
2852
|
comp.firstParty = true;
|
|
2867
2853
|
}
|
|
2868
2854
|
if (fn.children.size) {
|
|
2869
2855
|
comp.dependencies = [...fn.children].sort();
|
|
2870
2856
|
}
|
|
2871
|
-
const manifestFiles =
|
|
2857
|
+
const manifestFiles = manifestFilesById.get(id);
|
|
2872
2858
|
if (manifestFiles) {
|
|
2873
2859
|
comp.manifestFiles = manifestFiles;
|
|
2874
2860
|
}
|
|
@@ -2890,6 +2876,7 @@ function buildProjects(parsed, directByRoot, perRoot) {
|
|
|
2890
2876
|
}
|
|
2891
2877
|
const projects = [...parsed.projects.values()].map(p => {
|
|
2892
2878
|
const entry = {
|
|
2879
|
+
id: p.projectKey,
|
|
2893
2880
|
type: PURL_TYPE_MAVEN,
|
|
2894
2881
|
namespace: p.group,
|
|
2895
2882
|
name: p.name,
|
|
@@ -2899,6 +2886,15 @@ function buildProjects(parsed, directByRoot, perRoot) {
|
|
|
2899
2886
|
subprojectDir: p.dir,
|
|
2900
2887
|
dependencies: [...(directByProject.get(p.projectKey) ?? [])].sort()
|
|
2901
2888
|
};
|
|
2889
|
+
// A project without a build file of its own is defined by the scripts
|
|
2890
|
+
// declaring its dependencies, e.g. the root build script.
|
|
2891
|
+
const declared = [...new Set([...p.declaredIn.values()].flat())];
|
|
2892
|
+
const files = p.buildFiles.length ? p.buildFiles : declared.length ? declared : p.missingBuildFiles;
|
|
2893
|
+
if (files.length) {
|
|
2894
|
+
entry.manifestFiles = [...files].sort().map(file => ({
|
|
2895
|
+
file
|
|
2896
|
+
}));
|
|
2897
|
+
}
|
|
2902
2898
|
return entry;
|
|
2903
2899
|
});
|
|
2904
2900
|
projects.sort((a, b) => {
|
|
@@ -2908,21 +2904,6 @@ function buildProjects(parsed, directByRoot, perRoot) {
|
|
|
2908
2904
|
});
|
|
2909
2905
|
return projects;
|
|
2910
2906
|
}
|
|
2911
|
-
function unionInto(map, key, add) {
|
|
2912
|
-
if (!add.length) {
|
|
2913
|
-
return;
|
|
2914
|
-
}
|
|
2915
|
-
const acc = map.get(key);
|
|
2916
|
-
if (acc) {
|
|
2917
|
-
for (const f of add) {
|
|
2918
|
-
if (!acc.includes(f)) {
|
|
2919
|
-
acc.push(f);
|
|
2920
|
-
}
|
|
2921
|
-
}
|
|
2922
|
-
} else {
|
|
2923
|
-
map.set(key, [...add]);
|
|
2924
|
-
}
|
|
2925
|
-
}
|
|
2926
2907
|
function buildClasspathByProject(projects, perRoot) {
|
|
2927
2908
|
const idsByProjectKey = new Map();
|
|
2928
2909
|
for (const {
|
|
@@ -2940,11 +2921,7 @@ function buildClasspathByProject(projects, perRoot) {
|
|
|
2940
2921
|
}
|
|
2941
2922
|
const classpathByProject = new Map();
|
|
2942
2923
|
for (const p of projects) {
|
|
2943
|
-
const key =
|
|
2944
|
-
name: p.name,
|
|
2945
|
-
namespace: p.group,
|
|
2946
|
-
subprojectDir: p.dir
|
|
2947
|
-
});
|
|
2924
|
+
const key = p.projectKey;
|
|
2948
2925
|
let set = classpathByProject.get(key);
|
|
2949
2926
|
if (!set) {
|
|
2950
2927
|
set = new Set();
|
|
@@ -2959,61 +2936,26 @@ function buildClasspathByProject(projects, perRoot) {
|
|
|
2959
2936
|
1: ids
|
|
2960
2937
|
}) => [key, [...ids].sort()]));
|
|
2961
2938
|
}
|
|
2962
|
-
function buildArtifactPaths(finalNodes, projects,
|
|
2963
|
-
const
|
|
2964
|
-
const
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
if (!coordKey) {
|
|
2971
|
-
continue;
|
|
2972
|
-
}
|
|
2973
|
-
coords.add(coordKey);
|
|
2974
|
-
const pi = projectsByGav.get(gav(c.group, c.name, c.version ?? ''));
|
|
2975
|
-
const sources = (pi?.sources ?? []).filter(fileExists).sort();
|
|
2976
|
-
const targets = [...new Set(pi ? pi.targets : fn.targets)].filter(fileExists).sort();
|
|
2977
|
-
if (sources.length) {
|
|
2978
|
-
sourcesByCoord.set(coordKey, sources);
|
|
2979
|
-
}
|
|
2980
|
-
if (!targets.length) {
|
|
2981
|
-
continue;
|
|
2982
|
-
}
|
|
2983
|
-
targetsByCoord.set(coordKey, targets);
|
|
2984
|
-
const gavKey = mavenCoordinateKey(c.group, c.name, undefined, undefined, c.version);
|
|
2985
|
-
if (gavKey) {
|
|
2986
|
-
const acc = targetsByGav.get(gavKey);
|
|
2987
|
-
if (acc) {
|
|
2988
|
-
for (const f of targets) {
|
|
2989
|
-
if (!acc.includes(f)) {
|
|
2990
|
-
acc.push(f);
|
|
2991
|
-
}
|
|
2992
|
-
}
|
|
2993
|
-
} else {
|
|
2994
|
-
targetsByGav.set(gavKey, [...targets]);
|
|
2995
|
-
}
|
|
2939
|
+
function buildArtifactPaths(finalNodes, projects, perRoot, fileExists) {
|
|
2940
|
+
const pathsById = new Map();
|
|
2941
|
+
for (const [id, fn] of finalNodes) {
|
|
2942
|
+
if (!fn.project) {
|
|
2943
|
+
pathsById.set(id, {
|
|
2944
|
+
sources: [],
|
|
2945
|
+
targets: [...fn.targets].filter(fileExists).sort()
|
|
2946
|
+
});
|
|
2996
2947
|
}
|
|
2997
2948
|
}
|
|
2998
|
-
// A
|
|
2999
|
-
//
|
|
3000
|
-
// emit first-party module paths here.
|
|
2949
|
+
// A project's own component shares its id, so this also covers dependency
|
|
2950
|
+
// edges onto a sibling project.
|
|
3001
2951
|
for (const p of projects) {
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
}
|
|
3006
|
-
coords.add(coordKey);
|
|
3007
|
-
unionInto(sourcesByCoord, coordKey, p.sources.filter(fileExists));
|
|
3008
|
-
const targets = p.targets.filter(fileExists);
|
|
3009
|
-
unionInto(targetsByCoord, coordKey, targets);
|
|
3010
|
-
unionInto(targetsByGav, coordKey, targets);
|
|
2952
|
+
pathsById.set(p.projectKey, {
|
|
2953
|
+
sources: [...new Set(p.sources)].filter(fileExists).sort(),
|
|
2954
|
+
targets: [...new Set(p.targets)].filter(fileExists).sort()
|
|
2955
|
+
});
|
|
3011
2956
|
}
|
|
3012
2957
|
return {
|
|
3013
|
-
|
|
3014
|
-
targetsByGav,
|
|
3015
|
-
sourcesByCoord,
|
|
3016
|
-
coords,
|
|
2958
|
+
pathsById,
|
|
3017
2959
|
classpathByProject: buildClasspathByProject(projects, perRoot)
|
|
3018
2960
|
};
|
|
3019
2961
|
}
|
|
@@ -3185,18 +3127,23 @@ function serializeExcludePathPatterns(paths) {
|
|
|
3185
3127
|
// <tag>\t<field>\t<field>...
|
|
3186
3128
|
//
|
|
3187
3129
|
// meta tool toolVersion javaVersion
|
|
3130
|
+
// buildRoot path (absolute; the facts file's directory)
|
|
3188
3131
|
// project projectKey group name version dir
|
|
3189
3132
|
// projectSrc projectKey path (--with-files only)
|
|
3190
3133
|
// projectTgt projectKey path (--with-files only)
|
|
3191
|
-
// projectBuild projectKey path
|
|
3134
|
+
// projectBuild projectKey path [missing] (build-root-relative; `missing`: configured, absent on disk)
|
|
3135
|
+
// declared projectKey group name path (script declaring the dependency; build-root-relative)
|
|
3192
3136
|
// root rootId projectKey config prod(0|1)
|
|
3193
|
-
// node rootId coordId group name version ext classifier direct(0|1)
|
|
3137
|
+
// node rootId coordId group name version ext classifier direct(0|1) project
|
|
3194
3138
|
// edge rootId parentCoordId childCoordId
|
|
3195
3139
|
// file rootId coordId path (--with-files only)
|
|
3196
3140
|
// scanned config
|
|
3197
3141
|
// failure coord detail config
|
|
3198
3142
|
// unscannable config detail
|
|
3199
3143
|
//
|
|
3144
|
+
// `projectKey` is the tool's unique project identity (Maven: GAV; Gradle:
|
|
3145
|
+
// project path; sbt: project id), emitted as the project id; a `node` whose
|
|
3146
|
+
// `project` names one resolves to that build project.
|
|
3200
3147
|
// A `root` is one (subproject, configuration) resolution root; `coordId` is the
|
|
3201
3148
|
// coordinate key (`group:name:ext:classifier:version`, empty segments dropped),
|
|
3202
3149
|
// used opaquely as the per-root node key. Unknown tags are ignored.
|
|
@@ -3225,6 +3172,7 @@ function parseRecords(text) {
|
|
|
3225
3172
|
tool: '',
|
|
3226
3173
|
toolVersion: '',
|
|
3227
3174
|
javaVersion: '',
|
|
3175
|
+
buildRoot: '',
|
|
3228
3176
|
projects: new Map(),
|
|
3229
3177
|
roots: new Map(),
|
|
3230
3178
|
scannedConfigs: [],
|
|
@@ -3258,7 +3206,9 @@ function parseRecords(text) {
|
|
|
3258
3206
|
dir: '',
|
|
3259
3207
|
sources: [],
|
|
3260
3208
|
targets: [],
|
|
3261
|
-
buildFiles: []
|
|
3209
|
+
buildFiles: [],
|
|
3210
|
+
missingBuildFiles: [],
|
|
3211
|
+
declaredIn: new Map()
|
|
3262
3212
|
};
|
|
3263
3213
|
result.projects.set(key, p);
|
|
3264
3214
|
}
|
|
@@ -3275,6 +3225,9 @@ function parseRecords(text) {
|
|
|
3275
3225
|
result.toolVersion = f[2] ?? '';
|
|
3276
3226
|
result.javaVersion = f[3] ?? '';
|
|
3277
3227
|
break;
|
|
3228
|
+
case 'buildRoot':
|
|
3229
|
+
result.buildRoot = f[1] ?? '';
|
|
3230
|
+
break;
|
|
3278
3231
|
case 'project':
|
|
3279
3232
|
{
|
|
3280
3233
|
const p = project(f[1] ?? '');
|
|
@@ -3296,7 +3249,17 @@ function parseRecords(text) {
|
|
|
3296
3249
|
break;
|
|
3297
3250
|
case 'projectBuild':
|
|
3298
3251
|
if (f[2]) {
|
|
3299
|
-
project(f[1] ?? '')
|
|
3252
|
+
const p = project(f[1] ?? '');
|
|
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]]);
|
|
3300
3263
|
}
|
|
3301
3264
|
break;
|
|
3302
3265
|
case 'root':
|
|
@@ -3321,6 +3284,7 @@ function parseRecords(text) {
|
|
|
3321
3284
|
classifier: f[7] ?? ''
|
|
3322
3285
|
},
|
|
3323
3286
|
direct: bool(f[8]),
|
|
3287
|
+
project: f[9] ?? '',
|
|
3324
3288
|
targets: []
|
|
3325
3289
|
});
|
|
3326
3290
|
break;
|
|
@@ -3430,12 +3394,14 @@ async function writeSbtPlugin(globalBase, sourceFilename, destFilename) {
|
|
|
3430
3394
|
}
|
|
3431
3395
|
async function assembleFromRecords(out, recordsFile) {
|
|
3432
3396
|
const text = fs$1.existsSync(recordsFile) ? await fs$1.promises.readFile(recordsFile, 'utf8') : '';
|
|
3397
|
+
const parsed = parseRecords(text);
|
|
3433
3398
|
const {
|
|
3434
3399
|
artifactPaths,
|
|
3435
3400
|
facts,
|
|
3436
3401
|
report
|
|
3437
|
-
} = assembleFacts(
|
|
3402
|
+
} = assembleFacts(parsed);
|
|
3438
3403
|
return {
|
|
3404
|
+
buildRoot: parsed.buildRoot || undefined,
|
|
3439
3405
|
code: out.code,
|
|
3440
3406
|
facts,
|
|
3441
3407
|
report,
|
|
@@ -3606,7 +3572,6 @@ async function runManifestFacts({
|
|
|
3606
3572
|
verbose,
|
|
3607
3573
|
withFiles
|
|
3608
3574
|
}) {
|
|
3609
|
-
const factsPath = path.join(cwd, constants.default.DOT_SOCKET_DOT_FACTS_JSON);
|
|
3610
3575
|
let resolvedJavaHome;
|
|
3611
3576
|
if (javaHome) {
|
|
3612
3577
|
const expanded = expandEnvVarRefs(javaHome);
|
|
@@ -3671,6 +3636,7 @@ async function runManifestFacts({
|
|
|
3671
3636
|
}
|
|
3672
3637
|
const {
|
|
3673
3638
|
artifactPaths,
|
|
3639
|
+
buildRoot,
|
|
3674
3640
|
code,
|
|
3675
3641
|
facts,
|
|
3676
3642
|
report,
|
|
@@ -3731,6 +3697,14 @@ async function runManifestFacts({
|
|
|
3731
3697
|
logger.logger.warn(`No resolvable ${ecosystem} dependencies found; nothing to upload.`);
|
|
3732
3698
|
return;
|
|
3733
3699
|
}
|
|
3700
|
+
if (!buildRoot) {
|
|
3701
|
+
process.exitCode = 1;
|
|
3702
|
+
logger.logger.fail(`The ${ecosystem} build did not report its root directory, so its Socket facts file cannot be placed.`);
|
|
3703
|
+
return null;
|
|
3704
|
+
}
|
|
3705
|
+
// Every path in the facts is relative to the build root, which `-f`/`-p`
|
|
3706
|
+
// can move away from cwd.
|
|
3707
|
+
const factsPath = path.join(buildRoot, constants.default.DOT_SOCKET_DOT_FACTS_JSON);
|
|
3734
3708
|
const socketCliVersion = constants.default.ENV.INLINED_SOCKET_CLI_VERSION;
|
|
3735
3709
|
if (facts.metadata && socketCliVersion) {
|
|
3736
3710
|
facts.metadata.socketCliVersion = socketCliVersion;
|
|
@@ -3876,8 +3850,11 @@ async function runEcosystemCandidates({
|
|
|
3876
3850
|
continue;
|
|
3877
3851
|
}
|
|
3878
3852
|
covered.add(dir);
|
|
3853
|
+
// `-f`/`-p` can root the reactor away from `dir`.
|
|
3854
|
+
// eslint-disable-next-line no-await-in-loop
|
|
3855
|
+
const buildRoot = await utils.realpathOrResolved(path.dirname(result.factsPath));
|
|
3879
3856
|
// eslint-disable-next-line no-await-in-loop
|
|
3880
|
-
const resolvedSubprojectDirs = await Promise.all(result.projects.map(project => utils.realpathOrResolved(path.resolve(
|
|
3857
|
+
const resolvedSubprojectDirs = await Promise.all(result.projects.map(project => utils.realpathOrResolved(path.resolve(buildRoot, project.subprojectDir))));
|
|
3881
3858
|
for (const subprojectDir of resolvedSubprojectDirs) {
|
|
3882
3859
|
// Only a genuinely nested member (a descendant of this reactor's own
|
|
3883
3860
|
// directory) has no independent existence worth its own standalone
|
|
@@ -3890,7 +3867,7 @@ async function runEcosystemCandidates({
|
|
|
3890
3867
|
// meaningful data point, not a redundant one. Never suppress its own
|
|
3891
3868
|
// build-root invocation, regardless of which reactor(s) also
|
|
3892
3869
|
// incorporate it or the order candidates happen to be discovered in.
|
|
3893
|
-
if (subprojectDir.startsWith(`${
|
|
3870
|
+
if (subprojectDir === buildRoot || subprojectDir.startsWith(`${buildRoot}${path.sep}`)) {
|
|
3894
3871
|
covered.add(subprojectDir);
|
|
3895
3872
|
}
|
|
3896
3873
|
}
|
|
@@ -3905,7 +3882,7 @@ async function runEcosystemCandidates({
|
|
|
3905
3882
|
return outcomes;
|
|
3906
3883
|
}
|
|
3907
3884
|
|
|
3908
|
-
// Generates one
|
|
3885
|
+
// Generates one Socket facts file per independent gradle/sbt/maven build
|
|
3909
3886
|
// root under `cwd`. Coverage is tracked per ecosystem via the facts SBOM's
|
|
3910
3887
|
// own projects[].subprojectDir, not by pruning the whole discovered subtree,
|
|
3911
3888
|
// so an unrelated nested project a reactor doesn't declare still gets its
|
|
@@ -7231,7 +7208,7 @@ async function createNewScan({
|
|
|
7231
7208
|
});
|
|
7232
7209
|
if (reach.dynamicSbomInference) {
|
|
7233
7210
|
// Already generated recursively above; resolving cwd's own build
|
|
7234
|
-
// root a second time would race on the same
|
|
7211
|
+
// root a second time would race on the same facts file.
|
|
7235
7212
|
detected.gradle = false;
|
|
7236
7213
|
detected.sbt = false;
|
|
7237
7214
|
detected.maven = false;
|
|
@@ -7366,11 +7343,11 @@ async function createNewScan({
|
|
|
7366
7343
|
reachabilityReport = reachResult.data?.reachabilityReport;
|
|
7367
7344
|
|
|
7368
7345
|
// When using only pre-generated SBOMs, build the scan from those inputs —
|
|
7369
|
-
// CycloneDX, SPDX, and Socket facts
|
|
7370
|
-
//
|
|
7371
|
-
//
|
|
7372
|
-
//
|
|
7373
|
-
const pathsForScan = reach.reachUseOnlyPregeneratedSboms ? filterToPregeneratedSboms(packagePaths, supportedFiles) : packagePaths.filter(p =>
|
|
7346
|
+
// CycloneDX, SPDX, and Socket facts — matching Coana's
|
|
7347
|
+
// `--use-only-pregenerated-sboms` selection. Otherwise drop every facts
|
|
7348
|
+
// file; coana's fresh reachability report (appended below) is the
|
|
7349
|
+
// authoritative facts file for the scan.
|
|
7350
|
+
const pathsForScan = reach.reachUseOnlyPregeneratedSboms ? filterToPregeneratedSboms(packagePaths, supportedFiles) : packagePaths.filter(p => !utils.isSocketFactsFile(p));
|
|
7374
7351
|
|
|
7375
7352
|
// Append coana's reachability report, but not twice: a pre-generated facts
|
|
7376
7353
|
// input can resolve to the same path coana wrote its report to.
|
|
@@ -9006,9 +8983,6 @@ async function discoverGhsaIds(orgSlug, tarHash, options) {
|
|
|
9006
8983
|
}).catch(() => {});
|
|
9007
8984
|
}
|
|
9008
8985
|
}
|
|
9009
|
-
function isFactsFile(filepath) {
|
|
9010
|
-
return path.basename(filepath).toLowerCase() === constants.DOT_SOCKET_DOT_FACTS_JSON;
|
|
9011
|
-
}
|
|
9012
8986
|
async function gitWorkingTreeChanges(cwd) {
|
|
9013
8987
|
const {
|
|
9014
8988
|
0: modifiedCResult,
|
|
@@ -9034,6 +9008,18 @@ function readWrittenFiles(outputFile) {
|
|
|
9034
9008
|
const files = result?.modifiedFiles;
|
|
9035
9009
|
return Array.isArray(files) && files.every(f => typeof f === 'string') ? new Set(files) : undefined;
|
|
9036
9010
|
}
|
|
9011
|
+
// Coana exits with this code after writing an --output-file that lists computed fixes it left unapplied.
|
|
9012
|
+
const COANA_FIXES_NOT_APPLIED_EXIT_CODE = 5;
|
|
9013
|
+
function isFixesNotAppliedExit(result) {
|
|
9014
|
+
return !result.ok && result.data?.code === COANA_FIXES_NOT_APPLIED_EXIT_CODE;
|
|
9015
|
+
}
|
|
9016
|
+
function readNotAppliedFixes(fixesResultJson) {
|
|
9017
|
+
const notApplied = fixesResultJson?.notApplied;
|
|
9018
|
+
return Array.isArray(notApplied) ? notApplied : [];
|
|
9019
|
+
}
|
|
9020
|
+
function formatNotAppliedFixes(notApplied) {
|
|
9021
|
+
return notApplied.map(f => ` ${f.ghsa}: ${f.purl} -> ${f.fixedVersion}: ${f.reason}`).join('\n');
|
|
9022
|
+
}
|
|
9037
9023
|
async function coanaFix(fixConfig) {
|
|
9038
9024
|
if (!fixConfig.dynamicSbomInference) {
|
|
9039
9025
|
return await coanaFixWithFacts(fixConfig, undefined);
|
|
@@ -9117,16 +9103,15 @@ async function coanaFixWithFacts(fixConfig, factsSlot) {
|
|
|
9117
9103
|
cwd
|
|
9118
9104
|
});
|
|
9119
9105
|
const scanFilepaths = await findScanFilepaths();
|
|
9120
|
-
//
|
|
9121
|
-
|
|
9122
|
-
const factsFiles = scanFilepaths.filter(isFactsFile);
|
|
9106
|
+
// Facts files are analysis artifacts and must be removed before re-running fix.
|
|
9107
|
+
const factsFiles = scanFilepaths.filter(utils.isSocketFactsFile);
|
|
9123
9108
|
if (factsFiles.length) {
|
|
9124
9109
|
if (!silence) {
|
|
9125
9110
|
spinner?.stop();
|
|
9126
9111
|
}
|
|
9127
9112
|
return {
|
|
9128
9113
|
ok: false,
|
|
9129
|
-
message:
|
|
9114
|
+
message: 'Found Socket facts files in manifest files',
|
|
9130
9115
|
cause: `Delete the following ${words.pluralize('file', factsFiles.length)} before running socket fix again:\n` + factsFiles.map(p => ` - ${p}`).join('\n')
|
|
9131
9116
|
};
|
|
9132
9117
|
}
|
|
@@ -9143,7 +9128,7 @@ async function coanaFixWithFacts(fixConfig, factsSlot) {
|
|
|
9143
9128
|
});
|
|
9144
9129
|
} catch (e) {
|
|
9145
9130
|
// A failed build root aborts inference after others wrote their facts.
|
|
9146
|
-
const partial = (await findScanFilepaths()).filter(
|
|
9131
|
+
const partial = (await findScanFilepaths()).filter(utils.isSocketFactsFile);
|
|
9147
9132
|
await Promise.all(partial.map(p => fs$1.promises.rm(p, {
|
|
9148
9133
|
force: true
|
|
9149
9134
|
})));
|
|
@@ -9250,7 +9235,7 @@ async function coanaFixWithFacts(fixConfig, factsSlot) {
|
|
|
9250
9235
|
if (!silence) {
|
|
9251
9236
|
spinner?.stop();
|
|
9252
9237
|
}
|
|
9253
|
-
if (!fixCResult.ok) {
|
|
9238
|
+
if (!fixCResult.ok && !isFixesNotAppliedExit(fixCResult)) {
|
|
9254
9239
|
return fixCResult;
|
|
9255
9240
|
}
|
|
9256
9241
|
|
|
@@ -9267,11 +9252,24 @@ async function coanaFixWithFacts(fixConfig, factsSlot) {
|
|
|
9267
9252
|
const tmpContent = await fs$1.promises.readFile(tmpFile, 'utf8');
|
|
9268
9253
|
await fs$1.promises.writeFile(outputFile, tmpContent, 'utf8');
|
|
9269
9254
|
}
|
|
9255
|
+
const ghsaDetails = fixesResultJson ? [fixesResultJson] : [];
|
|
9256
|
+
const notApplied = readNotAppliedFixes(fixesResultJson);
|
|
9257
|
+
if (!fixCResult.ok || notApplied.length) {
|
|
9258
|
+
return {
|
|
9259
|
+
ok: false,
|
|
9260
|
+
message: fixesResultJson?.type === 'no-fixes-applied' ? 'No computed fixes were applied' : 'Some computed fixes were not applied',
|
|
9261
|
+
cause: formatNotAppliedFixes(notApplied),
|
|
9262
|
+
data: {
|
|
9263
|
+
fixedAll: false,
|
|
9264
|
+
ghsaDetails
|
|
9265
|
+
}
|
|
9266
|
+
};
|
|
9267
|
+
}
|
|
9270
9268
|
return {
|
|
9271
9269
|
ok: true,
|
|
9272
9270
|
data: {
|
|
9273
9271
|
fixedAll: true,
|
|
9274
|
-
ghsaDetails
|
|
9272
|
+
ghsaDetails
|
|
9275
9273
|
}
|
|
9276
9274
|
};
|
|
9277
9275
|
} finally {
|
|
@@ -9378,7 +9376,13 @@ async function coanaFixWithFacts(fixConfig, factsSlot) {
|
|
|
9378
9376
|
spinner: silence ? undefined : spinner,
|
|
9379
9377
|
stdio: silence ? 'pipe' : 'inherit'
|
|
9380
9378
|
});
|
|
9381
|
-
if (
|
|
9379
|
+
if (isFixesNotAppliedExit(fixCResult)) {
|
|
9380
|
+
if (!silence) {
|
|
9381
|
+
logger.logger.warn(`Not every fix for ${ghsaId} was applied:\n${formatNotAppliedFixes(readNotAppliedFixes(fs$2.readJsonSync(tmpFile, {
|
|
9382
|
+
throws: false
|
|
9383
|
+
})))}`);
|
|
9384
|
+
}
|
|
9385
|
+
} else if (!fixCResult.ok) {
|
|
9382
9386
|
if (!silence) {
|
|
9383
9387
|
logger.logger.error(`Update failed for ${ghsaId}: ${utils.getErrorCause(fixCResult)}`);
|
|
9384
9388
|
}
|
|
@@ -19922,6 +19926,13 @@ const generalFlags$1 = {
|
|
|
19922
19926
|
description: 'Scan each TARGET directory as a uv project, using the versions pinned in its uv.lock or its workspace root uv.lock. Uploads a CycloneDX dependency graph per TARGET in place of manifest discovery, including all extras and dependency groups. Requires uv on PATH.'
|
|
19923
19927
|
}
|
|
19924
19928
|
};
|
|
19929
|
+
function hasSocketFactsFileIn(dir) {
|
|
19930
|
+
try {
|
|
19931
|
+
return fs$1.readdirSync(dir).some(utils.isSocketFactsFile);
|
|
19932
|
+
} catch {
|
|
19933
|
+
return false;
|
|
19934
|
+
}
|
|
19935
|
+
}
|
|
19925
19936
|
const cmdScanCreate = {
|
|
19926
19937
|
description: description$c,
|
|
19927
19938
|
hidden: hidden$a,
|
|
@@ -20143,12 +20154,12 @@ async function run$d(argv, importMeta, {
|
|
|
20143
20154
|
}
|
|
20144
20155
|
}
|
|
20145
20156
|
const detected = await detectManifestActions(sockJson, cwd);
|
|
20146
|
-
// Suppress the --auto-manifest suggestion when a
|
|
20157
|
+
// Suppress the --auto-manifest suggestion when a Socket facts file is
|
|
20147
20158
|
// already present at cwd. That file is the output of `socket manifest auto`
|
|
20148
20159
|
// (and `--facts` mode of the per-ecosystem manifest commands), so suggesting
|
|
20149
20160
|
// to regenerate it would be misleading; the manifest data is already there
|
|
20150
20161
|
// and will be picked up by the scan.
|
|
20151
|
-
const hasFactsFile =
|
|
20162
|
+
const hasFactsFile = hasSocketFactsFileIn(cwd);
|
|
20152
20163
|
if (detected.count > 0 && !autoManifest && !dynamicSbomInference && !uvMembers && !hasFactsFile) {
|
|
20153
20164
|
logger.logger.info(`Detected ${detected.count} manifest targets we could try to generate. Please set the --auto-manifest flag if you want to include languages covered by \`socket manifest auto\` in the Scan.`);
|
|
20154
20165
|
}
|
|
@@ -24467,5 +24478,5 @@ process.on('unhandledRejection', async (reason, promise) => {
|
|
|
24467
24478
|
// eslint-disable-next-line n/no-process-exit
|
|
24468
24479
|
process.exit(1);
|
|
24469
24480
|
});
|
|
24470
|
-
//# debugId=
|
|
24481
|
+
//# debugId=40113ed0-ae58-43eb-a197-d1810801160e
|
|
24471
24482
|
//# sourceMappingURL=cli.js.map
|