@trustify-da/trustify-da-javascript-client 0.3.0-ea.b40d888 → 0.3.0-ea.c2f6c64

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 (65) hide show
  1. package/README.md +191 -11
  2. package/dist/package.json +23 -10
  3. package/dist/src/analysis.d.ts +21 -5
  4. package/dist/src/analysis.js +74 -80
  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 +241 -8
  8. package/dist/src/cyclone_dx_sbom.d.ts +10 -2
  9. package/dist/src/cyclone_dx_sbom.js +32 -5
  10. package/dist/src/index.d.ts +138 -11
  11. package/dist/src/index.js +288 -7
  12. package/dist/src/license/index.d.ts +28 -0
  13. package/dist/src/license/index.js +100 -0
  14. package/dist/src/license/license_utils.d.ts +40 -0
  15. package/dist/src/license/license_utils.js +134 -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 +4 -5
  21. package/dist/src/oci_image/utils.d.ts +4 -4
  22. package/dist/src/oci_image/utils.js +11 -2
  23. package/dist/src/provider.d.ts +17 -5
  24. package/dist/src/provider.js +27 -5
  25. package/dist/src/providers/base_java.d.ts +3 -5
  26. package/dist/src/providers/base_javascript.d.ts +29 -7
  27. package/dist/src/providers/base_javascript.js +129 -22
  28. package/dist/src/providers/base_pyproject.d.ts +147 -0
  29. package/dist/src/providers/base_pyproject.js +279 -0
  30. package/dist/src/providers/golang_gomodules.d.ts +20 -13
  31. package/dist/src/providers/golang_gomodules.js +112 -114
  32. package/dist/src/providers/gomod_parser.d.ts +4 -0
  33. package/dist/src/providers/gomod_parser.js +16 -0
  34. package/dist/src/providers/java_gradle.d.ts +9 -3
  35. package/dist/src/providers/java_gradle.js +12 -2
  36. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  37. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  38. package/dist/src/providers/java_maven.d.ts +12 -5
  39. package/dist/src/providers/java_maven.js +33 -5
  40. package/dist/src/providers/javascript_pnpm.d.ts +1 -1
  41. package/dist/src/providers/javascript_pnpm.js +2 -2
  42. package/dist/src/providers/manifest.d.ts +2 -0
  43. package/dist/src/providers/manifest.js +22 -4
  44. package/dist/src/providers/processors/yarn_berry_processor.js +82 -3
  45. package/dist/src/providers/python_controller.d.ts +5 -2
  46. package/dist/src/providers/python_controller.js +56 -58
  47. package/dist/src/providers/python_pip.d.ts +11 -4
  48. package/dist/src/providers/python_pip.js +47 -54
  49. package/dist/src/providers/python_poetry.d.ts +42 -0
  50. package/dist/src/providers/python_poetry.js +146 -0
  51. package/dist/src/providers/python_uv.d.ts +26 -0
  52. package/dist/src/providers/python_uv.js +118 -0
  53. package/dist/src/providers/requirements_parser.d.ts +6 -0
  54. package/dist/src/providers/requirements_parser.js +24 -0
  55. package/dist/src/providers/rust_cargo.d.ts +52 -0
  56. package/dist/src/providers/rust_cargo.js +614 -0
  57. package/dist/src/providers/tree-sitter-gomod.wasm +0 -0
  58. package/dist/src/providers/tree-sitter-requirements.wasm +0 -0
  59. package/dist/src/sbom.d.ts +10 -1
  60. package/dist/src/sbom.js +12 -2
  61. package/dist/src/tools.d.ts +22 -6
  62. package/dist/src/tools.js +56 -1
  63. package/dist/src/workspace.d.ts +61 -0
  64. package/dist/src/workspace.js +256 -0
  65. package/package.json +24 -11
@@ -0,0 +1,279 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { PackageURL } from 'packageurl-js';
4
+ import { parse as parseToml } from 'smol-toml';
5
+ import { getLicense } from '../license/license_utils.js';
6
+ import Sbom from '../sbom.js';
7
+ const ecosystem = 'pip';
8
+ const IGNORE_MARKERS = ['exhortignore', 'trustify-da-ignore'];
9
+ const DEFAULT_ROOT_NAME = 'default-pip-root';
10
+ const DEFAULT_ROOT_VERSION = '0.0.0';
11
+ /** @typedef {{name: string, version: string, children: string[]}} GraphEntry */
12
+ /** @typedef {{name: string, version: string, dependencies: DepTreeEntry[]}} DepTreeEntry */
13
+ /** @typedef {{directDeps: string[], graph: Map<string, GraphEntry>}} DependencyData */
14
+ /** @typedef {{ecosystem: string, content: string, contentType: string}} Provided */
15
+ export default class Base_pyproject {
16
+ /**
17
+ * @param {string} manifestName
18
+ * @returns {boolean}
19
+ */
20
+ isSupported(manifestName) {
21
+ return 'pyproject.toml' === manifestName;
22
+ }
23
+ /**
24
+ * @param {string} manifestDir
25
+ * @returns {boolean}
26
+ */
27
+ validateLockFile(manifestDir) {
28
+ return fs.existsSync(path.join(manifestDir, this._lockFileName()));
29
+ }
30
+ /**
31
+ * Read project license from pyproject.toml, with fallback to LICENSE file.
32
+ * @param {string} manifestPath
33
+ * @returns {string|null}
34
+ */
35
+ readLicenseFromManifest(manifestPath) {
36
+ let fromManifest = null;
37
+ try {
38
+ let content = fs.readFileSync(manifestPath, 'utf-8');
39
+ let parsed = parseToml(content);
40
+ fromManifest = parsed.project?.license;
41
+ if (typeof fromManifest === 'object' && fromManifest != null) {
42
+ fromManifest = fromManifest.text || null;
43
+ }
44
+ if (!fromManifest) {
45
+ fromManifest = parsed.tool?.poetry?.license || null;
46
+ }
47
+ }
48
+ catch (_) {
49
+ // leave fromManifest as null
50
+ }
51
+ return getLicense(fromManifest, manifestPath);
52
+ }
53
+ /**
54
+ * @param {string} manifest - path to pyproject.toml
55
+ * @param {Object} [opts={}]
56
+ * @returns {Promise<Provided>}
57
+ */
58
+ async provideStack(manifest, opts = {}) {
59
+ return {
60
+ ecosystem,
61
+ content: await this._createSbom(manifest, opts, true),
62
+ contentType: 'application/vnd.cyclonedx+json'
63
+ };
64
+ }
65
+ /**
66
+ * @param {string} manifest - path to pyproject.toml
67
+ * @param {Object} [opts={}]
68
+ * @returns {Promise<Provided>}
69
+ */
70
+ async provideComponent(manifest, opts = {}) {
71
+ return {
72
+ ecosystem,
73
+ content: await this._createSbom(manifest, opts, false),
74
+ contentType: 'application/vnd.cyclonedx+json'
75
+ };
76
+ }
77
+ // --- abstract methods (subclasses must override) ---
78
+ /**
79
+ * @returns {string}
80
+ * @protected
81
+ */
82
+ _lockFileName() {
83
+ throw new TypeError('_lockFileName must be implemented');
84
+ }
85
+ /**
86
+ * @returns {string}
87
+ * @protected
88
+ */
89
+ _cmdName() {
90
+ throw new TypeError('_cmdName must be implemented');
91
+ }
92
+ /**
93
+ * Resolve dependencies using the tool-specific command and parser.
94
+ * @param {string} manifestDir
95
+ * @param {object} parsed - parsed pyproject.toml
96
+ * @param {Object} opts
97
+ * @returns {Promise<DependencyData>}
98
+ * @protected
99
+ */
100
+ // eslint-disable-next-line no-unused-vars
101
+ async _getDependencyData(manifestDir, parsed, opts) {
102
+ throw new TypeError('_getDependencyData must be implemented');
103
+ }
104
+ // --- shared helpers ---
105
+ /**
106
+ * Canonicalize a Python package name per PEP 503.
107
+ * @param {string} name
108
+ * @returns {string}
109
+ * @protected
110
+ */
111
+ _canonicalize(name) {
112
+ return name.toLowerCase().replace(/[-_.]+/g, '-');
113
+ }
114
+ /**
115
+ * Get the project name from pyproject.toml.
116
+ * @param {object} parsed
117
+ * @returns {string|null}
118
+ * @protected
119
+ */
120
+ _getProjectName(parsed) {
121
+ return parsed.project?.name || parsed.tool?.poetry?.name || null;
122
+ }
123
+ /**
124
+ * Get the project version from pyproject.toml.
125
+ * @param {object} parsed
126
+ * @returns {string|null}
127
+ * @protected
128
+ */
129
+ _getProjectVersion(parsed) {
130
+ return parsed.project?.version || parsed.tool?.poetry?.version || null;
131
+ }
132
+ /**
133
+ * Scan raw pyproject.toml text for dependencies with ignore markers.
134
+ * @param {string} manifestPath
135
+ * @returns {Set<string>}
136
+ * @protected
137
+ */
138
+ _getIgnoredDeps(manifestPath) {
139
+ let ignored = new Set();
140
+ let content = fs.readFileSync(manifestPath, 'utf-8');
141
+ let lines = content.split(/\r?\n/);
142
+ for (let line of lines) {
143
+ if (!IGNORE_MARKERS.some(m => line.includes(m))) {
144
+ continue;
145
+ }
146
+ // PEP 621 style: "requests>=2.25" #exhortignore
147
+ let pep621Match = line.match(/^\s*"([^"]+)"/);
148
+ if (pep621Match) {
149
+ let reqStr = pep621Match[1];
150
+ let nameMatch = reqStr.match(/^([A-Za-z0-9]([A-Za-z0-9._-]*[A-Za-z0-9])?)/);
151
+ if (nameMatch) {
152
+ ignored.add(this._canonicalize(nameMatch[1]));
153
+ }
154
+ continue;
155
+ }
156
+ // Poetry style: requests = "^2.25" #exhortignore
157
+ let poetryMatch = line.match(/^\s*([A-Za-z0-9][A-Za-z0-9._-]*)\s*=/);
158
+ if (poetryMatch) {
159
+ ignored.add(this._canonicalize(poetryMatch[1]));
160
+ }
161
+ }
162
+ return ignored;
163
+ }
164
+ /**
165
+ * Build dependency tree from graph, starting from direct deps.
166
+ * @param {Map<string, GraphEntry>} graph
167
+ * @param {string[]} directDeps - canonical names of direct deps
168
+ * @param {Set<string>} ignoredDeps
169
+ * @param {boolean} includeTransitive
170
+ * @returns {DepTreeEntry[]}
171
+ * @protected
172
+ */
173
+ _buildDependencyTree(graph, directDeps, ignoredDeps, includeTransitive) {
174
+ let result = [];
175
+ for (let key of directDeps) {
176
+ if (ignoredDeps.has(key)) {
177
+ continue;
178
+ }
179
+ let entry = graph.get(key);
180
+ if (!entry) {
181
+ continue;
182
+ }
183
+ let depTree = [];
184
+ if (includeTransitive) {
185
+ let visited = new Set();
186
+ visited.add(key);
187
+ this._collectTransitive(graph, entry.children, depTree, ignoredDeps, visited);
188
+ }
189
+ result.push({ name: entry.name, version: entry.version, dependencies: depTree });
190
+ }
191
+ result.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
192
+ return result;
193
+ }
194
+ /**
195
+ * Recursively collect transitive dependencies.
196
+ * @param {Map<string, GraphEntry>} graph
197
+ * @param {string[]} childKeys
198
+ * @param {DepTreeEntry[]} result - mutated in place
199
+ * @param {Set<string>} ignoredDeps
200
+ * @param {Set<string>} visited
201
+ * @returns {void}
202
+ * @protected
203
+ */
204
+ _collectTransitive(graph, childKeys, result, ignoredDeps, visited) {
205
+ for (let childKey of childKeys) {
206
+ let canonKey = this._canonicalize(childKey);
207
+ if (ignoredDeps.has(canonKey)) {
208
+ continue;
209
+ }
210
+ if (visited.has(canonKey)) {
211
+ continue;
212
+ }
213
+ visited.add(canonKey);
214
+ let entry = graph.get(canonKey);
215
+ if (!entry) {
216
+ continue;
217
+ }
218
+ let childDeps = [];
219
+ this._collectTransitive(graph, entry.children, childDeps, ignoredDeps, visited);
220
+ result.push({ name: entry.name, version: entry.version, dependencies: childDeps });
221
+ }
222
+ result.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
223
+ }
224
+ /**
225
+ * @param {string} name
226
+ * @param {string} version
227
+ * @returns {PackageURL}
228
+ * @protected
229
+ */
230
+ _toPurl(name, version) {
231
+ return new PackageURL('pypi', undefined, name, version, undefined, undefined);
232
+ }
233
+ /**
234
+ * Recursively add a dependency and its transitive deps to the SBOM.
235
+ * @param {PackageURL} source
236
+ * @param {DepTreeEntry} dep
237
+ * @param {Sbom} sbom
238
+ * @returns {void}
239
+ * @private
240
+ */
241
+ _addAllDependencies(source, dep, sbom) {
242
+ let targetPurl = this._toPurl(dep.name, dep.version);
243
+ sbom.addDependency(source, targetPurl);
244
+ if (dep.dependencies && dep.dependencies.length > 0) {
245
+ dep.dependencies.forEach(child => this._addAllDependencies(this._toPurl(dep.name, dep.version), child, sbom));
246
+ }
247
+ }
248
+ /**
249
+ * Create SBOM json string for a pyproject.toml project.
250
+ * @param {string} manifest - path to pyproject.toml
251
+ * @param {Object} opts
252
+ * @param {boolean} includeTransitive
253
+ * @returns {Promise<string>}
254
+ * @private
255
+ */
256
+ async _createSbom(manifest, opts, includeTransitive) {
257
+ let manifestDir = path.dirname(manifest);
258
+ let content = fs.readFileSync(manifest, 'utf-8');
259
+ let parsed = parseToml(content);
260
+ let { directDeps, graph } = await this._getDependencyData(manifestDir, parsed, opts);
261
+ let ignoredDeps = this._getIgnoredDeps(manifest);
262
+ let dependencies = this._buildDependencyTree(graph, directDeps, ignoredDeps, includeTransitive);
263
+ let sbom = new Sbom();
264
+ let rootName = this._getProjectName(parsed) || DEFAULT_ROOT_NAME;
265
+ let rootVersion = this._getProjectVersion(parsed) || DEFAULT_ROOT_VERSION;
266
+ let rootPurl = this._toPurl(rootName, rootVersion);
267
+ let license = this.readLicenseFromManifest(manifest);
268
+ sbom.addRoot(rootPurl, license);
269
+ dependencies.forEach(dep => {
270
+ if (includeTransitive) {
271
+ this._addAllDependencies(rootPurl, dep, sbom);
272
+ }
273
+ else {
274
+ sbom.addDependency(rootPurl, this._toPurl(dep.name, dep.version));
275
+ }
276
+ });
277
+ return sbom.getAsJsonString(opts);
278
+ }
279
+ }
@@ -3,9 +3,10 @@ declare namespace _default {
3
3
  export { validateLockFile };
4
4
  export { provideComponent };
5
5
  export { provideStack };
6
+ export { readLicenseFromManifest };
6
7
  }
7
8
  export default _default;
8
- export type Provided = import('../provider').Provided;
9
+ export type Provided = import("../provider").Provided;
9
10
  export type Package = {
10
11
  name: string;
11
12
  version: string;
@@ -18,25 +19,31 @@ export type Dependency = {
18
19
  ignore: boolean;
19
20
  };
20
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
+ * @param {string} manifestName the subject manifest name-type
23
+ * @returns {boolean} return true if `pom.xml` is the manifest name-type
24
+ */
24
25
  declare function isSupported(manifestName: string): boolean;
25
26
  /**
26
- * @param {string} manifestDir - the directory where the manifest lies
27
+ * @param {string} manifestDir the directory where the manifest lies
27
28
  */
28
29
  declare function validateLockFile(): boolean;
29
30
  /**
30
31
  * Provide content and content type for maven-maven component analysis.
31
- * @param {string} manifest - path to go.mod for component report
32
- * @param {{}} [opts={}] - optional various options to pass along the application
33
- * @returns {Provided}
32
+ * @param {string} manifest path to go.mod for component report
33
+ * @param {{}} [opts={}] optional various options to pass along the application
34
+ * @returns {Promise<Provided>}
34
35
  */
35
- declare function provideComponent(manifest: string, opts?: {} | undefined): Provided;
36
+ declare function provideComponent(manifest: string, opts?: {}): Promise<Provided>;
36
37
  /**
37
38
  * Provide content and content type for maven-maven stack analysis.
38
- * @param {string} manifest - the manifest path or name
39
- * @param {{}} [opts={}] - optional various options to pass along the application
40
- * @returns {Provided}
39
+ * @param {string} manifest the manifest path or name
40
+ * @param {{}} [opts={}] optional various options to pass along the application
41
+ * @returns {Promise<Provided>}
41
42
  */
42
- declare function provideStack(manifest: string, opts?: {} | undefined): Provided;
43
+ declare function provideStack(manifest: string, opts?: {}): Promise<Provided>;
44
+ /**
45
+ * Go modules have no standard license field in go.mod
46
+ * @param {string} manifestPath path to go.mod
47
+ * @returns {string|null}
48
+ */
49
+ declare function readLicenseFromManifest(manifestPath: string): string | null;