@trustify-da/trustify-da-javascript-client 0.3.0-ea.a109536 → 0.3.0-ea.a754fa8

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.
Files changed (92) hide show
  1. package/README.md +367 -13
  2. package/dist/package.json +39 -23
  3. package/dist/src/analysis.d.ts +28 -11
  4. package/dist/src/analysis.js +99 -79
  5. package/dist/src/batch_opts.d.ts +24 -0
  6. package/dist/src/batch_opts.js +35 -0
  7. package/dist/src/cli.js +346 -9
  8. package/dist/src/cyclone_dx_sbom.d.ts +17 -2
  9. package/dist/src/cyclone_dx_sbom.js +56 -8
  10. package/dist/src/index.d.ts +145 -7
  11. package/dist/src/index.js +358 -6
  12. package/dist/src/license/index.d.ts +28 -0
  13. package/dist/src/license/index.js +103 -0
  14. package/dist/src/license/license_utils.d.ts +40 -0
  15. package/dist/src/license/license_utils.js +137 -0
  16. package/dist/src/license/licenses_api.d.ts +34 -0
  17. package/dist/src/license/licenses_api.js +98 -0
  18. package/dist/src/license/project_license.d.ts +20 -0
  19. package/dist/src/license/project_license.js +62 -0
  20. package/dist/src/oci_image/images.d.ts +1 -1
  21. package/dist/src/oci_image/images.js +15 -1
  22. package/dist/src/oci_image/utils.d.ts +5 -5
  23. package/dist/src/oci_image/utils.js +11 -2
  24. package/dist/src/provider.d.ts +18 -5
  25. package/dist/src/provider.js +33 -5
  26. package/dist/src/providers/base_java.d.ts +14 -12
  27. package/dist/src/providers/base_java.js +18 -44
  28. package/dist/src/providers/base_javascript.d.ts +45 -12
  29. package/dist/src/providers/base_javascript.js +138 -24
  30. package/dist/src/providers/base_pyproject.d.ts +158 -0
  31. package/dist/src/providers/base_pyproject.js +322 -0
  32. package/dist/src/providers/containerfile_parser.d.ts +5 -0
  33. package/dist/src/providers/containerfile_parser.js +20 -0
  34. package/dist/src/providers/golang_gomodules.d.ts +30 -13
  35. package/dist/src/providers/golang_gomodules.js +218 -122
  36. package/dist/src/providers/gomod_parser.d.ts +4 -0
  37. package/dist/src/providers/gomod_parser.js +16 -0
  38. package/dist/src/providers/java_gradle.d.ts +25 -0
  39. package/dist/src/providers/java_gradle.js +128 -4
  40. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  41. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  42. package/dist/src/providers/java_maven.d.ts +18 -3
  43. package/dist/src/providers/java_maven.js +197 -5
  44. package/dist/src/providers/javascript_bun.d.ts +10 -0
  45. package/dist/src/providers/javascript_bun.js +100 -0
  46. package/dist/src/providers/javascript_npm.d.ts +6 -0
  47. package/dist/src/providers/javascript_npm.js +21 -0
  48. package/dist/src/providers/javascript_pnpm.d.ts +1 -1
  49. package/dist/src/providers/javascript_pnpm.js +8 -4
  50. package/dist/src/providers/manifest.d.ts +2 -0
  51. package/dist/src/providers/manifest.js +22 -4
  52. package/dist/src/providers/marker_evaluator.d.ts +14 -0
  53. package/dist/src/providers/marker_evaluator.js +191 -0
  54. package/dist/src/providers/oci_dockerfile.d.ts +51 -0
  55. package/dist/src/providers/oci_dockerfile.js +177 -0
  56. package/dist/src/providers/processors/yarn_berry_processor.d.ts +2 -2
  57. package/dist/src/providers/processors/yarn_berry_processor.js +88 -5
  58. package/dist/src/providers/processors/yarn_classic_processor.d.ts +2 -2
  59. package/dist/src/providers/python_controller.d.ts +10 -3
  60. package/dist/src/providers/python_controller.js +122 -63
  61. package/dist/src/providers/python_pip.d.ts +16 -4
  62. package/dist/src/providers/python_pip.js +51 -58
  63. package/dist/src/providers/python_pip_pyproject.d.ts +61 -0
  64. package/dist/src/providers/python_pip_pyproject.js +146 -0
  65. package/dist/src/providers/python_poetry.d.ts +98 -0
  66. package/dist/src/providers/python_poetry.js +286 -0
  67. package/dist/src/providers/python_uv.d.ts +46 -0
  68. package/dist/src/providers/python_uv.js +269 -0
  69. package/dist/src/providers/requirements_parser.d.ts +6 -0
  70. package/dist/src/providers/requirements_parser.js +24 -0
  71. package/dist/src/providers/rust_cargo.d.ts +56 -0
  72. package/dist/src/providers/rust_cargo.js +700 -0
  73. package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
  74. package/dist/src/providers/tree-sitter-gomod.wasm +0 -0
  75. package/dist/src/providers/tree-sitter-requirements.wasm +0 -0
  76. package/dist/src/remediate.d.ts +21 -0
  77. package/dist/src/remediate.js +149 -0
  78. package/dist/src/remediation.d.ts +57 -0
  79. package/dist/src/remediation.js +436 -0
  80. package/dist/src/remediation_report.d.ts +40 -0
  81. package/dist/src/remediation_report.js +159 -0
  82. package/dist/src/sbom.d.ts +17 -2
  83. package/dist/src/sbom.js +16 -4
  84. package/dist/src/tools.d.ts +44 -0
  85. package/dist/src/tools.js +113 -0
  86. package/dist/src/updaters/maven_updater.d.ts +26 -0
  87. package/dist/src/updaters/maven_updater.js +322 -0
  88. package/dist/src/updaters/toml_updater.d.ts +33 -0
  89. package/dist/src/updaters/toml_updater.js +244 -0
  90. package/dist/src/workspace.d.ts +70 -0
  91. package/dist/src/workspace.js +256 -0
  92. package/package.json +40 -24
@@ -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
- export default { isSupported, validateLockFile, provideComponent, provideStack };
7
+ import { filterManifestPathsByDiscoveryIgnore, resolveWorkspaceDiscoveryIgnore } from '../workspace.js';
8
+ import { getParser, getRequireQuery } from './gomod_parser.js';
9
+ export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest, packageManagerName() { return 'go'; } };
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 - the subject manifest name-type
20
- * @returns {boolean} - return true if `pom.xml` is the manifest name-type
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
- * @param {string} manifestDir - the directory where the manifest lies
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 - the manifest path or name
32
- * @param {{}} [opts={}] - optional various options to pass along the application
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 - path to go.mod for component report
45
- * @param {{}} [opts={}] - optional various options to pass along the application
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
- * @param line one row from go.mod file
74
- * @return {boolean} whether line from go.mod should be considered as ignored or not
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 ignoredLine(line) {
77
- let result = false;
78
- if (line.match(".*exhortignore.*")) {
79
- if (line.match(".+//\\s*exhortignore") || line.match(".+//\\sindirect (//)?\\s*exhortignore")) {
80
- let trimmedRow = line.trim();
81
- if (!trimmedRow.startsWith("module ") && !trimmedRow.startsWith("go ") && !trimmedRow.startsWith("require (") && !trimmedRow.startsWith("require(")
82
- && !trimmedRow.startsWith("exclude ") && !trimmedRow.startsWith("replace ") && !trimmedRow.startsWith("retract ") && !trimmedRow.startsWith("use ")
83
- && !trimmedRow.includes("=>")) {
84
- if (trimmedRow.startsWith("require ") || trimmedRow.match("^[a-z.0-9/-]+\\s{1,2}[vV][0-9]\\.[0-9](\\.[0-9]){0,2}.*")) {
85
- result = true;
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 result;
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 } manifest - path to manifest
106
- * @return {[PackageURL]} list of ignored dependencies d
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(manifest) {
109
- let goMod = fs.readFileSync(manifest).toString().trim();
110
- let lines = goMod.split(getLineSeparatorGolang());
111
- return lines.filter(line => ignoredLine(line)).map(line => extractPackageName(line)).map(dep => toPurl(dep, /[ ]{1,3}/));
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 {[PackageURL]}allIgnoredDeps - list of purls of all dependencies that should be ignored
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 {[string]} lines - array of lines of go.mod manifest
135
- * @param {string} goMod - content of go.mod manifest
136
- * @return {[string]} all dependencies from go.mod file as array
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(lines, goMod) {
139
- let result;
140
- // collect all deps that starts with require keyword
141
- result = lines.filter((line) => line.trim().startsWith("require") && !line.includes("(")).map((dep) => dep.substring("require".length).trim());
142
- // collect all deps that are inside `require` blocks
143
- let currentSegmentOfGoMod = goMod;
144
- let requirePositionObject = decideRequireBlockIndex(currentSegmentOfGoMod);
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{[string]} goModGraphOutputRows the goModGraphOutputRows from go mod graph' output
179
- * @param {string }manifest path to go.mod manifest on file system
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, manifest) {
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(lines, goMod);
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(`versions mismatch for dependency name ${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`);
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
  });
@@ -206,15 +187,45 @@ function performManifestVersionsCheck(rootElementName, goModGraphOutputRows, man
206
187
  throw error;
207
188
  }
208
189
  }
190
+ /** Parses go.sum to build a hash map of module@version to CycloneDX hashes. */
191
+ function parseGoSumHashes(manifestDir) {
192
+ let goSumPath = path.join(manifestDir, 'go.sum');
193
+ if (!fs.existsSync(goSumPath)) {
194
+ return new Map();
195
+ }
196
+ let hashMap = new Map();
197
+ let content = fs.readFileSync(goSumPath, 'utf-8');
198
+ for (let line of content.split('\n')) {
199
+ line = line.trim();
200
+ if (!line) {
201
+ continue;
202
+ }
203
+ let parts = line.split(/\s+/);
204
+ if (parts.length < 3) {
205
+ continue;
206
+ }
207
+ let [mod, version, hash] = parts;
208
+ if (version.endsWith('/go.mod')) {
209
+ continue;
210
+ }
211
+ if (!hash.startsWith('h1:')) {
212
+ continue;
213
+ }
214
+ let base64Part = hash.slice(3);
215
+ let hexDigest = Buffer.from(base64Part, 'base64').toString('hex');
216
+ hashMap.set(`${mod}@${version}`, [{ alg: "SHA-256", content: hexDigest }]);
217
+ }
218
+ return hashMap;
219
+ }
209
220
  /**
210
221
  * Create SBOM json string for go Module.
211
222
  * @param {string} manifest - path for go.mod
212
223
  * @param {{}} [opts={}] - optional various options to pass along the application
213
224
  * @param {boolean} includeTransitive - whether the sbom should contain transitive dependencies of the main module or not.
214
- * @returns {string} the SBOM json content
225
+ * @returns {Promise<string>} the SBOM json content
215
226
  * @private
216
227
  */
217
- function getSBOM(manifest, opts = {}, includeTransitive) {
228
+ async function getSBOM(manifest, opts = {}, includeTransitive) {
218
229
  // get custom goBin path
219
230
  let goBin = getCustomPath('go', opts);
220
231
  // verify goBin is accessible
@@ -240,17 +251,30 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
240
251
  catch (error) {
241
252
  throw new Error('failed to determine root module name', { cause: error });
242
253
  }
243
- let ignoredDeps = getIgnoredDeps(manifest);
254
+ let manifestContent = fs.readFileSync(manifest).toString();
255
+ let [parser, requireQuery] = await Promise.all([getParser(), getRequireQuery()]);
256
+ let ignoredDeps = getIgnoredDeps(manifestContent, parser, requireQuery);
244
257
  let allIgnoredDeps = ignoredDeps.map((dep) => dep.toString());
245
258
  let sbom = new Sbom();
246
259
  let rows = goGraphOutput.split(getLineSeparatorGolang()).filter(line => !line.includes(' go@'));
247
- let root = getParentVertexFromEdge(goModEditOutput['Module']['Path']);
260
+ let root = goModEditOutput['Module']['Path'];
261
+ // Build set of direct dependency paths from go mod edit -json
262
+ let directDepPaths = new Set();
263
+ if (goModEditOutput['Require']) {
264
+ goModEditOutput['Require'].forEach(req => {
265
+ if (!req['Indirect']) {
266
+ directDepPaths.add(req['Path']);
267
+ }
268
+ });
269
+ }
248
270
  let matchManifestVersions = getCustom("MATCH_MANIFEST_VERSIONS", "false", opts);
249
271
  if (matchManifestVersions === "true") {
250
- performManifestVersionsCheck(root, rows, manifest);
272
+ performManifestVersionsCheck(root, rows, manifestContent, parser, requireQuery);
251
273
  }
274
+ let goSumHashes = parseGoSumHashes(manifestDir);
252
275
  const mainModule = toPurl(root, "@");
253
- sbom.addRoot(mainModule);
276
+ const license = readLicenseFromManifest(manifest);
277
+ sbom.addRoot(mainModule, license);
254
278
  const exhortGoMvsLogicEnabled = getCustom("TRUSTIFY_DA_GO_MVS_LOGIC_ENABLED", "true", opts);
255
279
  if (includeTransitive && exhortGoMvsLogicEnabled === "true") {
256
280
  rows = getFinalPackagesVersionsForModule(rows, manifest, goBin);
@@ -264,8 +288,12 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
264
288
  currentParent = getParentVertexFromEdge(row);
265
289
  source = toPurl(currentParent, "@");
266
290
  }
267
- let target = toPurl(getChildVertexFromEdge(row), "@");
268
- sbom.addDependency(source, target);
291
+ let child = getChildVertexFromEdge(row);
292
+ let target = toPurl(child, "@");
293
+ if (getParentVertexFromEdge(row) === root && !directDepPaths.has(getPackageName(child))) {
294
+ return;
295
+ }
296
+ sbom.addDependency(source, target, undefined, goSumHashes.get(child));
269
297
  });
270
298
  // at the end, filter out all ignored dependencies including versions.
271
299
  sbom.filterIgnoredDepsIncludingVersion(allIgnoredDeps);
@@ -274,20 +302,32 @@ function getSBOM(manifest, opts = {}, includeTransitive) {
274
302
  else {
275
303
  let directDependencies = rows.filter(row => row.startsWith(root));
276
304
  directDependencies.forEach(pair => {
277
- let dependency = getChildVertexFromEdge(pair);
278
- let depPurl = toPurl(dependency, "@");
279
- if (dependencyNotIgnored(ignoredDeps, depPurl)) {
280
- sbom.addDependency(mainModule, depPurl);
305
+ let child = getChildVertexFromEdge(pair);
306
+ let target = toPurl(child, "@");
307
+ if (dependencyNotIgnored(ignoredDeps, target)) {
308
+ if (directDepPaths.has(getPackageName(child))) {
309
+ sbom.addDependency(mainModule, target, undefined, goSumHashes.get(child));
310
+ }
281
311
  }
282
312
  });
283
313
  enforceRemovingIgnoredDepsInCaseOfAutomaticVersionUpdate(ignoredDeps, sbom);
284
314
  }
315
+ // Backfill hashes for components that were only seen as dependency sources
316
+ for (const comp of sbom.sbomModel.components) {
317
+ if (!comp.hashes && comp.purl) {
318
+ let key = decodeURIComponent(comp.purl.replace('pkg:golang/', ''));
319
+ let hashes = goSumHashes.get(key);
320
+ if (hashes) {
321
+ comp.hashes = hashes;
322
+ }
323
+ }
324
+ }
285
325
  return sbom.getAsJsonString(opts);
286
326
  }
287
327
  /**
288
328
  * Utility function for creating Purl String
289
329
 
290
- * @param {string }dependency the name of the artifact, can include a namespace(group) or not - namespace/artifactName.
330
+ * @param {string} dependency the name of the artifact, can include a namespace(group) or not - namespace/artifactName.
291
331
  * @param {RegExp} delimiter delimiter between name of dependency and version
292
332
  * @private
293
333
  * @returns {PackageURL|null} PackageUrl Object ready to be used in SBOM
@@ -312,12 +352,12 @@ function toPurl(dependency, delimiter) {
312
352
  }
313
353
  return pkg;
314
354
  }
315
- /** This function gets rows from go mod graph , and go.mod graph, and selecting for all
355
+ /** This function gets rows from go mod graph, and go.mod graph, and selecting for all
316
356
  * packages the has more than one minor the final versions as selected by golang MVS algorithm.
317
- * @param {[string]}rows all the rows from go modules dependency tree
357
+ * @param {string[]} rows all the rows from go modules dependency tree
318
358
  * @param {string} manifestPath the path of the go.mod file
319
359
  * @param {string} path to go binary
320
- * @return {[string]} rows that contains final versions.
360
+ * @return {string[]} rows that contains final versions.
321
361
  */
322
362
  function getFinalPackagesVersionsForModule(rows, manifestPath, goBin) {
323
363
  let manifestDir = path.dirname(manifestPath);
@@ -330,13 +370,7 @@ function getFinalPackagesVersionsForModule(rows, manifestPath, goBin) {
330
370
  catch (error) {
331
371
  throw new Error('failed to list all modules', { cause: error });
332
372
  }
333
- let finalVersionModules = new Map();
334
- finalVersionsForAllModules.split(getLineSeparatorGolang()).filter(string => string.trim() !== "")
335
- .filter(string => string.trim().split(" ").length === 2)
336
- .forEach((dependency) => {
337
- let dep = dependency.split(" ");
338
- finalVersionModules[dep[0]] = dep[1];
339
- });
373
+ let finalVersionModules = parseModuleVersions(finalVersionsForAllModules);
340
374
  let finalVersionModulesArray = new Array();
341
375
  rows.filter(string => string.trim() !== "").forEach(module => {
342
376
  let child = getChildVertexFromEdge(module);
@@ -372,7 +406,7 @@ function getFinalPackagesVersionsForModule(rows, manifestPath, goBin) {
372
406
  /**
373
407
  *
374
408
  * @param {string} fullPackage - full package with its name and version
375
- * @return -{string} package name only
409
+ * @return {string} package name only
376
410
  * @private
377
411
  */
378
412
  function getPackageName(fullPackage) {
@@ -390,14 +424,76 @@ function isSpecialGoModule(moduleName) {
390
424
  /**
391
425
  *
392
426
  * @param {string} fullPackage - full package with its name and version
393
- * @return -{string} package version only
427
+ * @return {string|undefined} package version only
394
428
  * @private
395
429
  */
396
430
  function getVersionOfPackage(fullPackage) {
397
431
  let parts = fullPackage.split("@");
398
432
  return parts.length > 1 ? parts[1] : undefined;
399
433
  }
434
+ function parseModuleVersions(goListOutput) {
435
+ let modules = {};
436
+ goListOutput.split(getLineSeparatorGolang()).filter(string => string.trim() !== "")
437
+ .forEach((line) => {
438
+ let parts = line.trim().split(" ");
439
+ if (parts.length === 2) {
440
+ modules[parts[0]] = parts[1];
441
+ }
442
+ else if (parts.length >= 4 && parts[2] === "=>") {
443
+ modules[parts[0]] = parts[parts.length - 1];
444
+ }
445
+ });
446
+ return modules;
447
+ }
400
448
  function getLineSeparatorGolang() {
401
449
  let reg = /\n|\r\n/;
402
450
  return reg;
403
451
  }
452
+ /**
453
+ * Discover all go.mod manifest paths in a Go workspace.
454
+ * Uses `go work edit -json` to get workspace members.
455
+ *
456
+ * @param {string} workspaceRoot - Absolute or relative path to workspace root (must contain go.work)
457
+ * @param {import('../index.js').Options} [opts={}]
458
+ * @returns {Promise<string[]>} Paths to go.mod files (absolute)
459
+ */
460
+ export async function discoverGoWorkspaceModules(workspaceRoot, opts = {}) {
461
+ const root = path.resolve(workspaceRoot);
462
+ const goWork = path.join(root, 'go.work');
463
+ if (!fs.existsSync(goWork)) {
464
+ return [];
465
+ }
466
+ const goBin = getCustomPath('go', opts);
467
+ let output;
468
+ try {
469
+ output = invokeCommand(goBin, ['work', 'edit', '-json', goWork], { cwd: root });
470
+ }
471
+ catch {
472
+ return [];
473
+ }
474
+ let workspace;
475
+ try {
476
+ workspace = JSON.parse(output.toString().trim());
477
+ }
478
+ catch {
479
+ return [];
480
+ }
481
+ const useEntries = workspace.Use || [];
482
+ if (useEntries.length === 0) {
483
+ return [];
484
+ }
485
+ const manifestPaths = [];
486
+ for (const entry of useEntries) {
487
+ const diskPath = entry.DiskPath;
488
+ if (!diskPath) {
489
+ continue;
490
+ }
491
+ const moduleDir = path.resolve(root, diskPath);
492
+ const goMod = path.join(moduleDir, 'go.mod');
493
+ if (fs.existsSync(goMod)) {
494
+ manifestPaths.push(goMod);
495
+ }
496
+ }
497
+ const ignorePatterns = resolveWorkspaceDiscoveryIgnore(opts);
498
+ return filterManifestPathsByDiscoveryIgnore(manifestPaths, root, ignorePatterns);
499
+ }
@@ -0,0 +1,4 @@
1
+ export function getParser(): Promise<Parser>;
2
+ export function getRequireQuery(): Promise<Query>;
3
+ import { Parser } from 'web-tree-sitter';
4
+ import { Query } from 'web-tree-sitter';
@@ -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,6 +34,12 @@ 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