@trustify-da/trustify-da-javascript-client 0.3.0-ea.8178c04 → 0.3.0-ea.848421d
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 +191 -11
- package/dist/package.json +23 -10
- package/dist/src/analysis.d.ts +21 -5
- package/dist/src/analysis.js +74 -80
- package/dist/src/batch_opts.d.ts +24 -0
- package/dist/src/batch_opts.js +35 -0
- package/dist/src/cli.js +241 -8
- package/dist/src/cyclone_dx_sbom.d.ts +17 -3
- package/dist/src/cyclone_dx_sbom.js +48 -8
- package/dist/src/index.d.ts +196 -11
- package/dist/src/index.js +345 -7
- package/dist/src/license/index.d.ts +28 -0
- package/dist/src/license/index.js +100 -0
- package/dist/src/license/license_utils.d.ts +40 -0
- package/dist/src/license/license_utils.js +134 -0
- package/dist/src/license/licenses_api.d.ts +34 -0
- package/dist/src/license/licenses_api.js +98 -0
- package/dist/src/license/project_license.d.ts +20 -0
- package/dist/src/license/project_license.js +62 -0
- package/dist/src/oci_image/images.d.ts +4 -5
- package/dist/src/oci_image/utils.d.ts +4 -4
- package/dist/src/oci_image/utils.js +11 -2
- package/dist/src/provider.d.ts +17 -5
- package/dist/src/provider.js +29 -5
- package/dist/src/providers/base_java.d.ts +3 -14
- package/dist/src/providers/base_java.js +2 -38
- package/dist/src/providers/base_javascript.d.ts +29 -7
- package/dist/src/providers/base_javascript.js +129 -22
- package/dist/src/providers/base_pyproject.d.ts +153 -0
- package/dist/src/providers/base_pyproject.js +315 -0
- package/dist/src/providers/golang_gomodules.d.ts +29 -13
- package/dist/src/providers/golang_gomodules.js +161 -114
- package/dist/src/providers/gomod_parser.d.ts +4 -0
- package/dist/src/providers/gomod_parser.js +16 -0
- package/dist/src/providers/java_gradle.d.ts +28 -3
- package/dist/src/providers/java_gradle.js +126 -2
- package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
- package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
- package/dist/src/providers/java_maven.d.ts +20 -5
- package/dist/src/providers/java_maven.js +126 -6
- package/dist/src/providers/javascript_npm.d.ts +1 -0
- package/dist/src/providers/javascript_npm.js +21 -0
- package/dist/src/providers/javascript_pnpm.d.ts +1 -1
- package/dist/src/providers/javascript_pnpm.js +8 -4
- package/dist/src/providers/manifest.d.ts +2 -0
- package/dist/src/providers/manifest.js +22 -4
- package/dist/src/providers/processors/yarn_berry_processor.js +88 -5
- package/dist/src/providers/python_controller.d.ts +10 -3
- package/dist/src/providers/python_controller.js +61 -59
- package/dist/src/providers/python_pip.d.ts +15 -4
- package/dist/src/providers/python_pip.js +51 -58
- package/dist/src/providers/python_pip_pyproject.d.ts +61 -0
- package/dist/src/providers/python_pip_pyproject.js +144 -0
- package/dist/src/providers/python_poetry.d.ts +58 -0
- package/dist/src/providers/python_poetry.js +175 -0
- package/dist/src/providers/python_uv.d.ts +42 -0
- package/dist/src/providers/python_uv.js +149 -0
- package/dist/src/providers/requirements_parser.d.ts +6 -0
- package/dist/src/providers/requirements_parser.js +24 -0
- package/dist/src/providers/rust_cargo.d.ts +52 -0
- package/dist/src/providers/rust_cargo.js +614 -0
- package/dist/src/providers/tree-sitter-gomod.wasm +0 -0
- package/dist/src/providers/tree-sitter-requirements.wasm +0 -0
- package/dist/src/sbom.d.ts +17 -2
- package/dist/src/sbom.js +16 -4
- package/dist/src/tools.d.ts +48 -6
- package/dist/src/tools.js +114 -1
- package/dist/src/workspace.d.ts +61 -0
- package/dist/src/workspace.js +256 -0
- package/package.json +24 -11
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { EOL } from "os";
|
|
4
3
|
import { PackageURL } from 'packageurl-js';
|
|
4
|
+
import { readLicenseFile } from '../license/license_utils.js';
|
|
5
5
|
import Sbom from '../sbom.js';
|
|
6
6
|
import { getCustom, getCustomPath, invokeCommand } from "../tools.js";
|
|
7
|
-
|
|
7
|
+
import { filterManifestPathsByDiscoveryIgnore, resolveWorkspaceDiscoveryIgnore } from '../workspace.js';
|
|
8
|
+
import { getParser, getRequireQuery } from './gomod_parser.js';
|
|
9
|
+
export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest };
|
|
8
10
|
/** @typedef {import('../provider').Provider} */
|
|
9
11
|
/** @typedef {import('../provider').Provided} Provided */
|
|
10
12
|
/** @typedef {{name: string, version: string}} Package */
|
|
@@ -12,43 +14,50 @@ export default { isSupported, validateLockFile, provideComponent, provideStack }
|
|
|
12
14
|
/**
|
|
13
15
|
* @type {string} ecosystem for npm-npm is 'maven'
|
|
14
16
|
* @private
|
|
15
|
-
|
|
17
|
+
*/
|
|
16
18
|
const ecosystem = 'golang';
|
|
17
19
|
const defaultMainModuleVersion = "v0.0.0";
|
|
18
20
|
/**
|
|
19
|
-
* @param {string} manifestName
|
|
20
|
-
* @returns {boolean}
|
|
21
|
-
|
|
21
|
+
* @param {string} manifestName the subject manifest name-type
|
|
22
|
+
* @returns {boolean} return true if `pom.xml` is the manifest name-type
|
|
23
|
+
*/
|
|
22
24
|
function isSupported(manifestName) {
|
|
23
25
|
return 'go.mod' === manifestName;
|
|
24
26
|
}
|
|
25
27
|
/**
|
|
26
|
-
*
|
|
28
|
+
* Go modules have no standard license field in go.mod
|
|
29
|
+
* @param {string} manifestPath path to go.mod
|
|
30
|
+
* @returns {string|null}
|
|
31
|
+
*/
|
|
32
|
+
// eslint-disable-next-line no-unused-vars
|
|
33
|
+
function readLicenseFromManifest(manifestPath) { return readLicenseFile(manifestPath); }
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} manifestDir the directory where the manifest lies
|
|
27
36
|
*/
|
|
28
37
|
function validateLockFile() { return true; }
|
|
29
38
|
/**
|
|
30
39
|
* Provide content and content type for maven-maven stack analysis.
|
|
31
|
-
* @param {string} manifest
|
|
32
|
-
* @param {{}} [opts={}]
|
|
33
|
-
* @returns {Provided}
|
|
40
|
+
* @param {string} manifest the manifest path or name
|
|
41
|
+
* @param {{}} [opts={}] optional various options to pass along the application
|
|
42
|
+
* @returns {Promise<Provided>}
|
|
34
43
|
*/
|
|
35
|
-
function provideStack(manifest, opts = {}) {
|
|
44
|
+
async function provideStack(manifest, opts = {}) {
|
|
36
45
|
return {
|
|
37
46
|
ecosystem,
|
|
38
|
-
content: getSBOM(manifest, opts, true),
|
|
47
|
+
content: await getSBOM(manifest, opts, true),
|
|
39
48
|
contentType: 'application/vnd.cyclonedx+json'
|
|
40
49
|
};
|
|
41
50
|
}
|
|
42
51
|
/**
|
|
43
52
|
* Provide content and content type for maven-maven component analysis.
|
|
44
|
-
* @param {string} manifest
|
|
45
|
-
* @param {{}} [opts={}]
|
|
46
|
-
* @returns {Provided}
|
|
53
|
+
* @param {string} manifest path to go.mod for component report
|
|
54
|
+
* @param {{}} [opts={}] optional various options to pass along the application
|
|
55
|
+
* @returns {Promise<Provided>}
|
|
47
56
|
*/
|
|
48
|
-
function provideComponent(manifest, opts = {}) {
|
|
57
|
+
async function provideComponent(manifest, opts = {}) {
|
|
49
58
|
return {
|
|
50
59
|
ecosystem,
|
|
51
|
-
content: getSBOM(manifest, opts, false),
|
|
60
|
+
content: await getSBOM(manifest, opts, false),
|
|
52
61
|
contentType: 'application/vnd.cyclonedx+json'
|
|
53
62
|
};
|
|
54
63
|
}
|
|
@@ -69,50 +78,52 @@ function getChildVertexFromEdge(edge) {
|
|
|
69
78
|
return edge.split(" ")[1];
|
|
70
79
|
}
|
|
71
80
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
81
|
+
* Check whether a require_spec has a valid exhortignore marker.
|
|
82
|
+
* For direct dependencies: `//exhortignore` or `// exhortignore`
|
|
83
|
+
* For indirect dependencies: `// indirect; exhortignore` (semicolon-separated)
|
|
84
|
+
* @param {import('web-tree-sitter').SyntaxNode} specNode
|
|
85
|
+
* @return {boolean}
|
|
75
86
|
*/
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
function hasExhortIgnore(specNode) {
|
|
88
|
+
// Ideally this would be the following tree-sitter query instead, but for some
|
|
89
|
+
// reason it throws an error here but not in the playground.
|
|
90
|
+
// (require_spec) ((module_path) @path (version) (comment) @comment (#match? @comment "^//.*exhortignore"))
|
|
91
|
+
// QueryError: Bad pattern structure at offset 53: '(comment) @comment (#match? @comment "^//.*exhortignore")) @spec'...
|
|
92
|
+
let comments = specNode.children.filter(c => c.type === 'comment');
|
|
93
|
+
for (let comment of comments) {
|
|
94
|
+
let text = comment.text;
|
|
95
|
+
if (/^\/\/\s*indirect;\s*exhortignore/.test(text)) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
if (/^\/\/\s*exhortignore/.test(text)) {
|
|
99
|
+
return true;
|
|
88
100
|
}
|
|
89
101
|
}
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* extract package name from go.mod line that contains exhortignore comment.
|
|
94
|
-
* @param line a row contains exhortignore as part of a comment
|
|
95
|
-
* @return {string} the full package name + group/namespace + version
|
|
96
|
-
* @private
|
|
97
|
-
*/
|
|
98
|
-
function extractPackageName(line) {
|
|
99
|
-
let trimmedRow = line.trim();
|
|
100
|
-
let firstRemarkNotationOccurrence = trimmedRow.indexOf("//");
|
|
101
|
-
return trimmedRow.substring(0, firstRemarkNotationOccurrence).trim();
|
|
102
|
+
return false;
|
|
102
103
|
}
|
|
103
104
|
/**
|
|
104
105
|
*
|
|
105
|
-
* @param {string
|
|
106
|
-
* @
|
|
106
|
+
* @param {string} manifestContent go.mod file contents
|
|
107
|
+
* @param {import('web-tree-sitter').Parser} parser
|
|
108
|
+
* @param {import('web-tree-sitter').Query} requireQuery
|
|
109
|
+
* @return {PackageURL[]} list of ignored dependencies
|
|
107
110
|
*/
|
|
108
|
-
function getIgnoredDeps(
|
|
109
|
-
let
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
function getIgnoredDeps(manifestContent, parser, requireQuery) {
|
|
112
|
+
let tree = parser.parse(manifestContent);
|
|
113
|
+
return requireQuery.matches(tree.rootNode)
|
|
114
|
+
.filter(match => {
|
|
115
|
+
let specNode = match.captures.find(c => c.name === 'spec').node;
|
|
116
|
+
return hasExhortIgnore(specNode);
|
|
117
|
+
})
|
|
118
|
+
.map(match => {
|
|
119
|
+
let name = match.captures.find(c => c.name === 'name').node.text;
|
|
120
|
+
let version = match.captures.find(c => c.name === 'version').node.text;
|
|
121
|
+
return toPurl(`${name} ${version}`, /[ ]{1,3}/);
|
|
122
|
+
});
|
|
112
123
|
}
|
|
113
124
|
/**
|
|
114
125
|
*
|
|
115
|
-
* @param {[
|
|
126
|
+
* @param {PackageURL[]} allIgnoredDeps list of purls of all dependencies that should be ignored
|
|
116
127
|
* @param {PackageURL} purl object to be checked if needed to be ignored
|
|
117
128
|
* @return {boolean}
|
|
118
129
|
*/
|
|
@@ -131,59 +142,29 @@ function enforceRemovingIgnoredDepsInCaseOfAutomaticVersionUpdate(ignoredDeps, s
|
|
|
131
142
|
}
|
|
132
143
|
/**
|
|
133
144
|
*
|
|
134
|
-
* @param {
|
|
135
|
-
* @param {
|
|
136
|
-
* @
|
|
145
|
+
* @param {string} manifestContent go.mod file contents
|
|
146
|
+
* @param {import('web-tree-sitter').Parser} parser
|
|
147
|
+
* @param {import('web-tree-sitter').Query} requireQuery
|
|
148
|
+
* @return {string[]} all dependencies from go.mod file as "name version" strings
|
|
137
149
|
*/
|
|
138
|
-
function collectAllDepsFromManifest(
|
|
139
|
-
let
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
while (requirePositionObject.index > -1) {
|
|
146
|
-
let depsInsideRequirementsBlock = currentSegmentOfGoMod.substring(requirePositionObject.index + requirePositionObject.startingOffeset).trim();
|
|
147
|
-
let endOfBlockIndex = depsInsideRequirementsBlock.indexOf(")");
|
|
148
|
-
let currentIndex = 0;
|
|
149
|
-
while (currentIndex < endOfBlockIndex) {
|
|
150
|
-
let endOfLinePosition = depsInsideRequirementsBlock.indexOf(EOL, currentIndex);
|
|
151
|
-
let dependency = depsInsideRequirementsBlock.substring(currentIndex, endOfLinePosition);
|
|
152
|
-
result.push(dependency.trim());
|
|
153
|
-
currentIndex = endOfLinePosition + 1;
|
|
154
|
-
}
|
|
155
|
-
currentSegmentOfGoMod = currentSegmentOfGoMod.substring(endOfBlockIndex + 1).trim();
|
|
156
|
-
requirePositionObject = decideRequireBlockIndex(currentSegmentOfGoMod);
|
|
157
|
-
}
|
|
158
|
-
function decideRequireBlockIndex(goMod) {
|
|
159
|
-
let object = {};
|
|
160
|
-
let index = goMod.indexOf("require(");
|
|
161
|
-
object.startingOffeset = "require(".length;
|
|
162
|
-
if (index === -1) {
|
|
163
|
-
index = goMod.indexOf("require (");
|
|
164
|
-
object.startingOffeset = "require (".length;
|
|
165
|
-
if (index === -1) {
|
|
166
|
-
index = goMod.indexOf("require (");
|
|
167
|
-
object.startingOffeset = "require (".length;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
object.index = index;
|
|
171
|
-
return object;
|
|
172
|
-
}
|
|
173
|
-
return result;
|
|
150
|
+
function collectAllDepsFromManifest(manifestContent, parser, requireQuery) {
|
|
151
|
+
let tree = parser.parse(manifestContent);
|
|
152
|
+
return requireQuery.matches(tree.rootNode).map(match => {
|
|
153
|
+
let name = match.captures.find(c => c.name === 'name').node.text;
|
|
154
|
+
let version = match.captures.find(c => c.name === 'version').node.text;
|
|
155
|
+
return `${name} ${version}`;
|
|
156
|
+
});
|
|
174
157
|
}
|
|
175
158
|
/**
|
|
176
159
|
*
|
|
177
160
|
* @param {string} rootElementName the rootElementName element of go mod graph, to compare only direct deps from go mod graph against go.mod manifest
|
|
178
|
-
* @param{[
|
|
179
|
-
* @param {string
|
|
161
|
+
* @param {string[]} goModGraphOutputRows the goModGraphOutputRows from go mod graph' output
|
|
162
|
+
* @param {string} manifestContent go.mod file contents
|
|
180
163
|
* @private
|
|
181
164
|
*/
|
|
182
|
-
function performManifestVersionsCheck(rootElementName, goModGraphOutputRows,
|
|
183
|
-
let goMod = fs.readFileSync(manifest).toString().trim();
|
|
184
|
-
let lines = goMod.split(getLineSeparatorGolang());
|
|
165
|
+
function performManifestVersionsCheck(rootElementName, goModGraphOutputRows, manifestContent, parser, requireQuery) {
|
|
185
166
|
let comparisonLines = goModGraphOutputRows.filter((line) => line.startsWith(rootElementName)).map((line) => getChildVertexFromEdge(line));
|
|
186
|
-
let manifestDeps = collectAllDepsFromManifest(
|
|
167
|
+
let manifestDeps = collectAllDepsFromManifest(manifestContent, parser, requireQuery);
|
|
187
168
|
try {
|
|
188
169
|
comparisonLines.forEach((dependency) => {
|
|
189
170
|
let parts = dependency.split("@");
|
|
@@ -195,7 +176,7 @@ function performManifestVersionsCheck(rootElementName, goModGraphOutputRows, man
|
|
|
195
176
|
let currentVersion = components[1];
|
|
196
177
|
if (currentDepName === depName) {
|
|
197
178
|
if (currentVersion !== version) {
|
|
198
|
-
throw new Error(`
|
|
179
|
+
throw new Error(`version mismatch for dependency "${depName}", manifest version=${currentVersion}, installed version=${version}, if you want to allow version mismatch for analysis between installed and requested packages, set environment variable/setting MATCH_MANIFEST_VERSIONS=false`);
|
|
199
180
|
}
|
|
200
181
|
}
|
|
201
182
|
});
|
|
@@ -211,10 +192,10 @@ function performManifestVersionsCheck(rootElementName, goModGraphOutputRows, man
|
|
|
211
192
|
* @param {string} manifest - path for go.mod
|
|
212
193
|
* @param {{}} [opts={}] - optional various options to pass along the application
|
|
213
194
|
* @param {boolean} includeTransitive - whether the sbom should contain transitive dependencies of the main module or not.
|
|
214
|
-
* @returns {string} the SBOM json content
|
|
195
|
+
* @returns {Promise<string>} the SBOM json content
|
|
215
196
|
* @private
|
|
216
197
|
*/
|
|
217
|
-
function getSBOM(manifest, opts = {}, includeTransitive) {
|
|
198
|
+
async function getSBOM(manifest, opts = {}, includeTransitive) {
|
|
218
199
|
// get custom goBin path
|
|
219
200
|
let goBin = getCustomPath('go', opts);
|
|
220
201
|
// verify goBin is accessible
|
|
@@ -240,17 +221,29 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
|
|
|
240
221
|
catch (error) {
|
|
241
222
|
throw new Error('failed to determine root module name', { cause: error });
|
|
242
223
|
}
|
|
243
|
-
let
|
|
224
|
+
let manifestContent = fs.readFileSync(manifest).toString();
|
|
225
|
+
let [parser, requireQuery] = await Promise.all([getParser(), getRequireQuery()]);
|
|
226
|
+
let ignoredDeps = getIgnoredDeps(manifestContent, parser, requireQuery);
|
|
244
227
|
let allIgnoredDeps = ignoredDeps.map((dep) => dep.toString());
|
|
245
228
|
let sbom = new Sbom();
|
|
246
229
|
let rows = goGraphOutput.split(getLineSeparatorGolang()).filter(line => !line.includes(' go@'));
|
|
247
|
-
let root =
|
|
230
|
+
let root = goModEditOutput['Module']['Path'];
|
|
231
|
+
// Build set of direct dependency paths from go mod edit -json
|
|
232
|
+
let directDepPaths = new Set();
|
|
233
|
+
if (goModEditOutput['Require']) {
|
|
234
|
+
goModEditOutput['Require'].forEach(req => {
|
|
235
|
+
if (!req['Indirect']) {
|
|
236
|
+
directDepPaths.add(req['Path']);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
248
240
|
let matchManifestVersions = getCustom("MATCH_MANIFEST_VERSIONS", "false", opts);
|
|
249
241
|
if (matchManifestVersions === "true") {
|
|
250
|
-
performManifestVersionsCheck(root, rows,
|
|
242
|
+
performManifestVersionsCheck(root, rows, manifestContent, parser, requireQuery);
|
|
251
243
|
}
|
|
252
244
|
const mainModule = toPurl(root, "@");
|
|
253
|
-
|
|
245
|
+
const license = readLicenseFromManifest(manifest);
|
|
246
|
+
sbom.addRoot(mainModule, license);
|
|
254
247
|
const exhortGoMvsLogicEnabled = getCustom("TRUSTIFY_DA_GO_MVS_LOGIC_ENABLED", "true", opts);
|
|
255
248
|
if (includeTransitive && exhortGoMvsLogicEnabled === "true") {
|
|
256
249
|
rows = getFinalPackagesVersionsForModule(rows, manifest, goBin);
|
|
@@ -264,7 +257,11 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
|
|
|
264
257
|
currentParent = getParentVertexFromEdge(row);
|
|
265
258
|
source = toPurl(currentParent, "@");
|
|
266
259
|
}
|
|
267
|
-
let
|
|
260
|
+
let child = getChildVertexFromEdge(row);
|
|
261
|
+
let target = toPurl(child, "@");
|
|
262
|
+
if (getParentVertexFromEdge(row) === root && !directDepPaths.has(getPackageName(child))) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
268
265
|
sbom.addDependency(source, target);
|
|
269
266
|
});
|
|
270
267
|
// at the end, filter out all ignored dependencies including versions.
|
|
@@ -274,10 +271,12 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
|
|
|
274
271
|
else {
|
|
275
272
|
let directDependencies = rows.filter(row => row.startsWith(root));
|
|
276
273
|
directDependencies.forEach(pair => {
|
|
277
|
-
let
|
|
278
|
-
let
|
|
279
|
-
if (dependencyNotIgnored(ignoredDeps,
|
|
280
|
-
|
|
274
|
+
let child = getChildVertexFromEdge(pair);
|
|
275
|
+
let target = toPurl(child, "@");
|
|
276
|
+
if (dependencyNotIgnored(ignoredDeps, target)) {
|
|
277
|
+
if (directDepPaths.has(getPackageName(child))) {
|
|
278
|
+
sbom.addDependency(mainModule, target);
|
|
279
|
+
}
|
|
281
280
|
}
|
|
282
281
|
});
|
|
283
282
|
enforceRemovingIgnoredDepsInCaseOfAutomaticVersionUpdate(ignoredDeps, sbom);
|
|
@@ -287,7 +286,7 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
|
|
|
287
286
|
/**
|
|
288
287
|
* Utility function for creating Purl String
|
|
289
288
|
|
|
290
|
-
* @param {string
|
|
289
|
+
* @param {string} dependency the name of the artifact, can include a namespace(group) or not - namespace/artifactName.
|
|
291
290
|
* @param {RegExp} delimiter delimiter between name of dependency and version
|
|
292
291
|
* @private
|
|
293
292
|
* @returns {PackageURL|null} PackageUrl Object ready to be used in SBOM
|
|
@@ -312,12 +311,12 @@ function toPurl(dependency, delimiter) {
|
|
|
312
311
|
}
|
|
313
312
|
return pkg;
|
|
314
313
|
}
|
|
315
|
-
/** This function gets rows from go mod graph
|
|
314
|
+
/** This function gets rows from go mod graph, and go.mod graph, and selecting for all
|
|
316
315
|
* packages the has more than one minor the final versions as selected by golang MVS algorithm.
|
|
317
|
-
* @param {[
|
|
316
|
+
* @param {string[]} rows all the rows from go modules dependency tree
|
|
318
317
|
* @param {string} manifestPath the path of the go.mod file
|
|
319
318
|
* @param {string} path to go binary
|
|
320
|
-
* @return {[
|
|
319
|
+
* @return {string[]} rows that contains final versions.
|
|
321
320
|
*/
|
|
322
321
|
function getFinalPackagesVersionsForModule(rows, manifestPath, goBin) {
|
|
323
322
|
let manifestDir = path.dirname(manifestPath);
|
|
@@ -372,7 +371,7 @@ function getFinalPackagesVersionsForModule(rows, manifestPath, goBin) {
|
|
|
372
371
|
/**
|
|
373
372
|
*
|
|
374
373
|
* @param {string} fullPackage - full package with its name and version
|
|
375
|
-
* @return
|
|
374
|
+
* @return {string} package name only
|
|
376
375
|
* @private
|
|
377
376
|
*/
|
|
378
377
|
function getPackageName(fullPackage) {
|
|
@@ -390,7 +389,7 @@ function isSpecialGoModule(moduleName) {
|
|
|
390
389
|
/**
|
|
391
390
|
*
|
|
392
391
|
* @param {string} fullPackage - full package with its name and version
|
|
393
|
-
* @return
|
|
392
|
+
* @return {string|undefined} package version only
|
|
394
393
|
* @private
|
|
395
394
|
*/
|
|
396
395
|
function getVersionOfPackage(fullPackage) {
|
|
@@ -401,3 +400,51 @@ function getLineSeparatorGolang() {
|
|
|
401
400
|
let reg = /\n|\r\n/;
|
|
402
401
|
return reg;
|
|
403
402
|
}
|
|
403
|
+
/**
|
|
404
|
+
* Discover all go.mod manifest paths in a Go workspace.
|
|
405
|
+
* Uses `go work edit -json` to get workspace members.
|
|
406
|
+
*
|
|
407
|
+
* @param {string} workspaceRoot - Absolute or relative path to workspace root (must contain go.work)
|
|
408
|
+
* @param {import('../index.js').Options} [opts={}]
|
|
409
|
+
* @returns {Promise<string[]>} Paths to go.mod files (absolute)
|
|
410
|
+
*/
|
|
411
|
+
export async function discoverGoWorkspaceModules(workspaceRoot, opts = {}) {
|
|
412
|
+
const root = path.resolve(workspaceRoot);
|
|
413
|
+
const goWork = path.join(root, 'go.work');
|
|
414
|
+
if (!fs.existsSync(goWork)) {
|
|
415
|
+
return [];
|
|
416
|
+
}
|
|
417
|
+
const goBin = getCustomPath('go', opts);
|
|
418
|
+
let output;
|
|
419
|
+
try {
|
|
420
|
+
output = invokeCommand(goBin, ['work', 'edit', '-json', goWork], { cwd: root });
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
return [];
|
|
424
|
+
}
|
|
425
|
+
let workspace;
|
|
426
|
+
try {
|
|
427
|
+
workspace = JSON.parse(output.toString().trim());
|
|
428
|
+
}
|
|
429
|
+
catch {
|
|
430
|
+
return [];
|
|
431
|
+
}
|
|
432
|
+
const useEntries = workspace.Use || [];
|
|
433
|
+
if (useEntries.length === 0) {
|
|
434
|
+
return [];
|
|
435
|
+
}
|
|
436
|
+
const manifestPaths = [];
|
|
437
|
+
for (const entry of useEntries) {
|
|
438
|
+
const diskPath = entry.DiskPath;
|
|
439
|
+
if (!diskPath) {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
const moduleDir = path.resolve(root, diskPath);
|
|
443
|
+
const goMod = path.join(moduleDir, 'go.mod');
|
|
444
|
+
if (fs.existsSync(goMod)) {
|
|
445
|
+
manifestPaths.push(goMod);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
const ignorePatterns = resolveWorkspaceDiscoveryIgnore(opts);
|
|
449
|
+
return filterManifestPathsByDiscoveryIgnore(manifestPaths, root, ignorePatterns);
|
|
450
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { Language, Parser, Query } from 'web-tree-sitter';
|
|
3
|
+
const wasmUrl = new URL('./tree-sitter-gomod.wasm', import.meta.url);
|
|
4
|
+
async function init() {
|
|
5
|
+
await Parser.init();
|
|
6
|
+
const wasmBytes = new Uint8Array(await readFile(wasmUrl));
|
|
7
|
+
return await Language.load(wasmBytes);
|
|
8
|
+
}
|
|
9
|
+
export async function getParser() {
|
|
10
|
+
const language = await init();
|
|
11
|
+
return new Parser().setLanguage(language);
|
|
12
|
+
}
|
|
13
|
+
export async function getRequireQuery() {
|
|
14
|
+
const language = await init();
|
|
15
|
+
return new Query(language, '(require_spec (module_path) @name (version) @version) @spec');
|
|
16
|
+
}
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discover all build.gradle[.kts] manifest paths in a Gradle multi-project build.
|
|
3
|
+
* Uses a custom init script to get structured project listing.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} workspaceRoot - Absolute or relative path to workspace root (must contain settings.gradle[.kts])
|
|
6
|
+
* @param {import('../index.js').Options} [opts={}]
|
|
7
|
+
* @returns {Promise<string[]>} Paths to build.gradle[.kts] files (absolute)
|
|
8
|
+
*/
|
|
9
|
+
export function discoverGradleSubprojects(workspaceRoot: string, opts?: import("../index.js").Options): Promise<string[]>;
|
|
10
|
+
/**
|
|
11
|
+
* Parse the structured output from the Gradle init script.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} raw - Raw stdout from gradle
|
|
14
|
+
* @returns {{ path: string, dir: string }[]}
|
|
15
|
+
*/
|
|
16
|
+
export function parseGradleInitScriptOutput(raw: string): {
|
|
17
|
+
path: string;
|
|
18
|
+
dir: string;
|
|
19
|
+
}[];
|
|
1
20
|
/**
|
|
2
21
|
* This class provides common functionality for Groovy and Kotlin DSL files.
|
|
3
22
|
*/
|
|
@@ -15,21 +34,27 @@ export default class Java_gradle extends Base_java {
|
|
|
15
34
|
* @param {string} manifestDir - the directory where the manifest lies
|
|
16
35
|
*/
|
|
17
36
|
validateLockFile(): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Gradle manifests (build.gradle, build.gradle.kts) have no standard license field.
|
|
39
|
+
* @param {string} manifestPath - path to manifest
|
|
40
|
+
* @returns {null}
|
|
41
|
+
*/
|
|
42
|
+
readLicenseFromManifest(manifestPath: string): null;
|
|
18
43
|
/**
|
|
19
44
|
* Provide content and content type for stack analysis.
|
|
20
45
|
* @param {string} manifest - the manifest path or name
|
|
21
46
|
* @param {{}} [opts={}] - optional various options to pass along the application
|
|
22
47
|
* @returns {Provided}
|
|
23
48
|
*/
|
|
24
|
-
provideStack(manifest: string, opts?: {}
|
|
49
|
+
provideStack(manifest: string, opts?: {}): Provided;
|
|
25
50
|
/**
|
|
26
51
|
* Provide content and content type for maven-maven component analysis.
|
|
27
52
|
* @param {string} manifest - path to pom.xml for component report
|
|
28
53
|
* @param {{}} [opts={}] - optional various options to pass along the application
|
|
29
54
|
* @returns {Provided}
|
|
30
55
|
*/
|
|
31
|
-
provideComponent(manifest: string, opts?: {}
|
|
56
|
+
provideComponent(manifest: string, opts?: {}): Provided;
|
|
32
57
|
#private;
|
|
33
58
|
}
|
|
34
|
-
export type Provided = import(
|
|
59
|
+
export type Provided = import("../provider.js").Provided;
|
|
35
60
|
import Base_java from "./base_java.js";
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
1
2
|
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
2
4
|
import path from 'node:path';
|
|
3
5
|
import { EOL } from 'os';
|
|
4
6
|
import TOML from 'fast-toml';
|
|
7
|
+
import { readLicenseFile } from '../license/license_utils.js';
|
|
5
8
|
import Sbom from '../sbom.js';
|
|
9
|
+
import { invokeCommand } from '../tools.js';
|
|
10
|
+
import { filterManifestPathsByDiscoveryIgnore, resolveWorkspaceDiscoveryIgnore } from '../workspace.js';
|
|
6
11
|
import Base_java, { ecosystem_gradle } from "./base_java.js";
|
|
7
12
|
/** @typedef {import('../provider.js').Provider} */
|
|
8
13
|
/** @typedef {import('../provider.js').Provided} Provided */
|
|
@@ -49,6 +54,13 @@ export default class Java_gradle extends Base_java {
|
|
|
49
54
|
* @param {string} manifestDir - the directory where the manifest lies
|
|
50
55
|
*/
|
|
51
56
|
validateLockFile() { return true; }
|
|
57
|
+
/**
|
|
58
|
+
* Gradle manifests (build.gradle, build.gradle.kts) have no standard license field.
|
|
59
|
+
* @param {string} manifestPath - path to manifest
|
|
60
|
+
* @returns {null}
|
|
61
|
+
*/
|
|
62
|
+
// eslint-disable-next-line no-unused-vars
|
|
63
|
+
readLicenseFromManifest(manifestPath) { return readLicenseFile(manifestPath); }
|
|
52
64
|
/**
|
|
53
65
|
* Provide content and content type for stack analysis.
|
|
54
66
|
* @param {string} manifest - the manifest path or name
|
|
@@ -158,7 +170,8 @@ export default class Java_gradle extends Base_java {
|
|
|
158
170
|
let sbom = new Sbom();
|
|
159
171
|
let root = `${properties.group}:${properties[ROOT_PROJECT_KEY_NAME].match(/Root project '(.+)'/)[1]}:jar:${properties.version}`;
|
|
160
172
|
let rootPurl = this.parseDep(root);
|
|
161
|
-
|
|
173
|
+
const license = this.readLicenseFromManifest(manifestPath);
|
|
174
|
+
sbom.addRoot(rootPurl, license);
|
|
162
175
|
let ignoredDeps = this.#getIgnoredDeps(manifestPath);
|
|
163
176
|
const [runtimeConfig, compileConfig] = this.#extractConfigurations(content);
|
|
164
177
|
const processedDeps = new Set();
|
|
@@ -298,7 +311,8 @@ export default class Java_gradle extends Base_java {
|
|
|
298
311
|
let sbom = new Sbom();
|
|
299
312
|
let root = `${properties.group}:${properties[ROOT_PROJECT_KEY_NAME].match(/Root project '(.+)'/)[1]}:jar:${properties.version}`;
|
|
300
313
|
let rootPurl = this.parseDep(root);
|
|
301
|
-
|
|
314
|
+
const license = this.readLicenseFromManifest(manifestPath);
|
|
315
|
+
sbom.addRoot(rootPurl, license);
|
|
302
316
|
let ignoredDeps = this.#getIgnoredDeps(manifestPath);
|
|
303
317
|
const [runtimeConfig, compileConfig] = this.#extractConfigurations(content);
|
|
304
318
|
let directDependencies = new Map();
|
|
@@ -397,3 +411,113 @@ export default class Java_gradle extends Base_java {
|
|
|
397
411
|
return undefined;
|
|
398
412
|
}
|
|
399
413
|
}
|
|
414
|
+
const DEFAULT_GRADLE_DISCOVERY_IGNORE = [
|
|
415
|
+
'**/build/**',
|
|
416
|
+
'**/.gradle/**',
|
|
417
|
+
];
|
|
418
|
+
/** Gradle init script that emits structured project listing. */
|
|
419
|
+
const GRADLE_INIT_SCRIPT = `allprojects {
|
|
420
|
+
task daListProjects {
|
|
421
|
+
doLast {
|
|
422
|
+
println "::DA_PROJECT::\${project.path}::\${project.projectDir}"
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
`;
|
|
427
|
+
/**
|
|
428
|
+
* Discover all build.gradle[.kts] manifest paths in a Gradle multi-project build.
|
|
429
|
+
* Uses a custom init script to get structured project listing.
|
|
430
|
+
*
|
|
431
|
+
* @param {string} workspaceRoot - Absolute or relative path to workspace root (must contain settings.gradle[.kts])
|
|
432
|
+
* @param {import('../index.js').Options} [opts={}]
|
|
433
|
+
* @returns {Promise<string[]>} Paths to build.gradle[.kts] files (absolute)
|
|
434
|
+
*/
|
|
435
|
+
export async function discoverGradleSubprojects(workspaceRoot, opts = {}) {
|
|
436
|
+
const root = path.resolve(workspaceRoot);
|
|
437
|
+
const hasSettings = fs.existsSync(path.join(root, 'settings.gradle'))
|
|
438
|
+
|| fs.existsSync(path.join(root, 'settings.gradle.kts'));
|
|
439
|
+
if (!hasSettings) {
|
|
440
|
+
return [];
|
|
441
|
+
}
|
|
442
|
+
const manifestPaths = [];
|
|
443
|
+
const rootBuildKts = path.join(root, 'build.gradle.kts');
|
|
444
|
+
const rootBuild = path.join(root, 'build.gradle');
|
|
445
|
+
const rootManifest = fs.existsSync(rootBuildKts) ? rootBuildKts : fs.existsSync(rootBuild) ? rootBuild : null;
|
|
446
|
+
if (rootManifest) {
|
|
447
|
+
manifestPaths.push(rootManifest);
|
|
448
|
+
}
|
|
449
|
+
let gradleBin;
|
|
450
|
+
try {
|
|
451
|
+
gradleBin = new Java_gradle().selectToolBinary(rootManifest || rootBuild, opts);
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
const ignorePatterns = [...resolveWorkspaceDiscoveryIgnore(opts), ...DEFAULT_GRADLE_DISCOVERY_IGNORE];
|
|
455
|
+
return filterManifestPathsByDiscoveryIgnore(manifestPaths, root, ignorePatterns);
|
|
456
|
+
}
|
|
457
|
+
const initScriptPath = path.join(os.tmpdir(), `da-list-projects-${crypto.randomUUID()}.gradle`);
|
|
458
|
+
try {
|
|
459
|
+
fs.writeFileSync(initScriptPath, GRADLE_INIT_SCRIPT);
|
|
460
|
+
let output;
|
|
461
|
+
try {
|
|
462
|
+
output = invokeCommand(gradleBin, [
|
|
463
|
+
'-q', '--no-daemon',
|
|
464
|
+
'--init-script', initScriptPath,
|
|
465
|
+
'daListProjects',
|
|
466
|
+
], { cwd: root });
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
const ignorePatterns = [...resolveWorkspaceDiscoveryIgnore(opts), ...DEFAULT_GRADLE_DISCOVERY_IGNORE];
|
|
470
|
+
return filterManifestPathsByDiscoveryIgnore(manifestPaths, root, ignorePatterns);
|
|
471
|
+
}
|
|
472
|
+
const projects = parseGradleInitScriptOutput(output.toString());
|
|
473
|
+
for (const proj of projects) {
|
|
474
|
+
if (proj.path === ':') {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
const projDir = path.resolve(proj.dir);
|
|
478
|
+
const buildKts = path.join(projDir, 'build.gradle.kts');
|
|
479
|
+
const buildGroovy = path.join(projDir, 'build.gradle');
|
|
480
|
+
if (fs.existsSync(buildKts)) {
|
|
481
|
+
manifestPaths.push(buildKts);
|
|
482
|
+
}
|
|
483
|
+
else if (fs.existsSync(buildGroovy)) {
|
|
484
|
+
manifestPaths.push(buildGroovy);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
finally {
|
|
489
|
+
try {
|
|
490
|
+
fs.unlinkSync(initScriptPath);
|
|
491
|
+
}
|
|
492
|
+
catch { /* ignore */ }
|
|
493
|
+
}
|
|
494
|
+
const ignorePatterns = [...resolveWorkspaceDiscoveryIgnore(opts), ...DEFAULT_GRADLE_DISCOVERY_IGNORE];
|
|
495
|
+
return filterManifestPathsByDiscoveryIgnore(manifestPaths, root, ignorePatterns);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Parse the structured output from the Gradle init script.
|
|
499
|
+
*
|
|
500
|
+
* @param {string} raw - Raw stdout from gradle
|
|
501
|
+
* @returns {{ path: string, dir: string }[]}
|
|
502
|
+
*/
|
|
503
|
+
export function parseGradleInitScriptOutput(raw) {
|
|
504
|
+
const projects = [];
|
|
505
|
+
for (const rawLine of raw.split('\n')) {
|
|
506
|
+
const line = rawLine.trimEnd();
|
|
507
|
+
if (!line.startsWith('::DA_PROJECT::')) {
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
const prefix = '::DA_PROJECT::';
|
|
511
|
+
const remainder = line.substring(prefix.length);
|
|
512
|
+
const lastSep = remainder.lastIndexOf('::');
|
|
513
|
+
if (lastSep < 0) {
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
const projPath = remainder.substring(0, lastSep);
|
|
517
|
+
const dir = remainder.substring(lastSep + 2);
|
|
518
|
+
if (projPath && dir) {
|
|
519
|
+
projects.push({ path: projPath, dir });
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return projects;
|
|
523
|
+
}
|
|
@@ -3,5 +3,5 @@ export default class Java_gradle_groovy extends Java_gradle {
|
|
|
3
3
|
_parseAliasForLibsNotation(alias: any): any;
|
|
4
4
|
_extractDepToBeIgnored(dep: any): any;
|
|
5
5
|
}
|
|
6
|
-
export type Provided = import(
|
|
6
|
+
export type Provided = import("../provider").Provided;
|
|
7
7
|
import Java_gradle from './java_gradle.js';
|