@trustify-da/trustify-da-javascript-client 0.3.0-ea.6549d2a → 0.3.0-ea.68905d3

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 (98) hide show
  1. package/README.md +368 -14
  2. package/dist/package.json +39 -26
  3. package/dist/src/analysis.d.ts +28 -5
  4. package/dist/src/analysis.js +78 -13
  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 +287 -5
  8. package/dist/src/cyclone_dx_sbom.d.ts +27 -1
  9. package/dist/src/cyclone_dx_sbom.js +84 -10
  10. package/dist/src/index.d.ts +145 -8
  11. package/dist/src/index.js +362 -28
  12. package/dist/src/license/index.d.ts +5 -5
  13. package/dist/src/license/index.js +8 -5
  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 +2 -2
  17. package/dist/src/license/licenses_api.js +9 -2
  18. package/dist/src/license/project_license.d.ts +1 -6
  19. package/dist/src/license/project_license.js +4 -81
  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/package_version.d.ts +8 -0
  25. package/dist/src/package_version.js +31 -0
  26. package/dist/src/provider.d.ts +8 -4
  27. package/dist/src/provider.js +18 -5
  28. package/dist/src/providers/base_java.d.ts +67 -12
  29. package/dist/src/providers/base_java.js +82 -61
  30. package/dist/src/providers/base_javascript.d.ts +91 -10
  31. package/dist/src/providers/base_javascript.js +220 -28
  32. package/dist/src/providers/base_pyproject.d.ts +158 -0
  33. package/dist/src/providers/base_pyproject.js +322 -0
  34. package/dist/src/providers/containerfile_parser.d.ts +5 -0
  35. package/dist/src/providers/containerfile_parser.js +20 -0
  36. package/dist/src/providers/golang_gomodules.d.ts +23 -13
  37. package/dist/src/providers/golang_gomodules.js +210 -121
  38. package/dist/src/providers/gomod_parser.d.ts +4 -0
  39. package/dist/src/providers/gomod_parser.js +16 -0
  40. package/dist/src/providers/java_gradle.d.ts +67 -0
  41. package/dist/src/providers/java_gradle.js +314 -21
  42. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  43. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  44. package/dist/src/providers/java_maven.d.ts +38 -10
  45. package/dist/src/providers/java_maven.js +279 -20
  46. package/dist/src/providers/javascript_bun.d.ts +22 -0
  47. package/dist/src/providers/javascript_bun.js +141 -0
  48. package/dist/src/providers/javascript_npm.d.ts +19 -0
  49. package/dist/src/providers/javascript_npm.js +61 -1
  50. package/dist/src/providers/javascript_pnpm.d.ts +14 -1
  51. package/dist/src/providers/javascript_pnpm.js +55 -5
  52. package/dist/src/providers/javascript_yarn.d.ts +12 -0
  53. package/dist/src/providers/javascript_yarn.js +75 -2
  54. package/dist/src/providers/manifest.d.ts +2 -0
  55. package/dist/src/providers/manifest.js +28 -7
  56. package/dist/src/providers/marker_evaluator.d.ts +14 -0
  57. package/dist/src/providers/marker_evaluator.js +191 -0
  58. package/dist/src/providers/oci_dockerfile.d.ts +51 -0
  59. package/dist/src/providers/oci_dockerfile.js +177 -0
  60. package/dist/src/providers/processors/yarn_berry_processor.d.ts +6 -2
  61. package/dist/src/providers/processors/yarn_berry_processor.js +91 -7
  62. package/dist/src/providers/processors/yarn_classic_processor.d.ts +6 -2
  63. package/dist/src/providers/processors/yarn_classic_processor.js +8 -6
  64. package/dist/src/providers/python_controller.d.ts +5 -1
  65. package/dist/src/providers/python_controller.js +69 -8
  66. package/dist/src/providers/python_pip.d.ts +5 -0
  67. package/dist/src/providers/python_pip.js +8 -7
  68. package/dist/src/providers/python_pip_pyproject.d.ts +61 -0
  69. package/dist/src/providers/python_pip_pyproject.js +146 -0
  70. package/dist/src/providers/python_poetry.d.ts +98 -0
  71. package/dist/src/providers/python_poetry.js +286 -0
  72. package/dist/src/providers/python_uv.d.ts +46 -0
  73. package/dist/src/providers/python_uv.js +269 -0
  74. package/dist/src/providers/requirements_parser.js +5 -4
  75. package/dist/src/providers/rust_cargo.d.ts +56 -0
  76. package/dist/src/providers/rust_cargo.js +682 -0
  77. package/dist/src/providers/tree-sitter-containerfile.wasm +0 -0
  78. package/dist/src/providers/tree-sitter-gomod.wasm +0 -0
  79. package/dist/src/providers/tree-sitter-requirements.wasm +0 -0
  80. package/dist/src/remediate.d.ts +117 -0
  81. package/dist/src/remediate.js +249 -0
  82. package/dist/src/remediation.d.ts +57 -0
  83. package/dist/src/remediation.js +436 -0
  84. package/dist/src/remediation_report.d.ts +40 -0
  85. package/dist/src/remediation_report.js +159 -0
  86. package/dist/src/sbom.d.ts +25 -1
  87. package/dist/src/sbom.js +23 -2
  88. package/dist/src/tools.d.ts +28 -2
  89. package/dist/src/tools.js +82 -1
  90. package/dist/src/updaters/maven_updater.d.ts +58 -0
  91. package/dist/src/updaters/maven_updater.js +345 -0
  92. package/dist/src/updaters/toml_updater.d.ts +62 -0
  93. package/dist/src/updaters/toml_updater.js +276 -0
  94. package/dist/src/workspace.d.ts +70 -0
  95. package/dist/src/workspace.js +256 -0
  96. package/package.json +40 -27
  97. package/dist/src/license/compatibility.d.ts +0 -18
  98. package/dist/src/license/compatibility.js +0 -45
@@ -0,0 +1,137 @@
1
+ /**
2
+ * License utilities: file reading, SPDX detection, normalization, compatibility.
3
+ * This module has NO dependencies on providers or backend to avoid circular dependencies.
4
+ */
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ const LICENSE_FILES = ['LICENSE', 'LICENSE.md', 'LICENSE.txt'];
8
+ /**
9
+ * Find LICENSE file path in the same directory as the manifest.
10
+ * @param {string} manifestPath
11
+ * @returns {string|null} - path to LICENSE file or null if not found
12
+ */
13
+ export function findLicenseFilePath(manifestPath) {
14
+ const manifestDir = path.dirname(path.resolve(manifestPath));
15
+ for (const name of LICENSE_FILES) {
16
+ const filePath = path.join(manifestDir, name);
17
+ try {
18
+ if (fs.statSync(filePath).isFile()) {
19
+ return filePath;
20
+ }
21
+ }
22
+ catch {
23
+ // skip
24
+ }
25
+ }
26
+ return null;
27
+ }
28
+ /**
29
+ * Very simple SPDX detection from common license text (first ~500 chars).
30
+ * @param {string} text
31
+ * @returns {string|null}
32
+ */
33
+ export function detectSpdxFromText(text) {
34
+ const head = text.slice(0, 500);
35
+ if (/Apache License,?\s*Version 2\.0/i.test(head)) {
36
+ return 'Apache-2.0';
37
+ }
38
+ if (/MIT License/i.test(head) && /Permission is hereby granted/i.test(head)) {
39
+ return 'MIT';
40
+ }
41
+ if (/GNU AFFERO GENERAL PUBLIC LICENSE\s+Version 3/i.test(head)) {
42
+ return 'AGPL-3.0-only';
43
+ }
44
+ if (/GNU LESSER GENERAL PUBLIC LICENSE\s+Version 3/i.test(head)) {
45
+ return 'LGPL-3.0-only';
46
+ }
47
+ if (/GNU LESSER GENERAL PUBLIC LICENSE\s+Version 2\.1/i.test(head)) {
48
+ return 'LGPL-2.1-only';
49
+ }
50
+ if (/GNU GENERAL PUBLIC LICENSE\s+Version 2/i.test(head)) {
51
+ return 'GPL-2.0-only';
52
+ }
53
+ if (/GNU GENERAL PUBLIC LICENSE\s+Version 3/i.test(head)) {
54
+ return 'GPL-3.0-only';
55
+ }
56
+ if (/BSD 2-Clause/i.test(head)) {
57
+ return 'BSD-2-Clause';
58
+ }
59
+ if (/BSD 3-Clause/i.test(head)) {
60
+ return 'BSD-3-Clause';
61
+ }
62
+ return null;
63
+ }
64
+ /**
65
+ * Read LICENSE file and detect SPDX identifier.
66
+ * @param {string} manifestPath - path to manifest
67
+ * @returns {string|null} - SPDX identifier from LICENSE file or null
68
+ */
69
+ export function readLicenseFile(manifestPath) {
70
+ const licenseFilePath = findLicenseFilePath(manifestPath);
71
+ if (!licenseFilePath) {
72
+ return null;
73
+ }
74
+ try {
75
+ const content = fs.readFileSync(licenseFilePath, 'utf-8');
76
+ return detectSpdxFromText(content) || content.split('\n')[0]?.trim() || null;
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ }
82
+ /**
83
+ * Get project license from manifest or LICENSE file.
84
+ * Returns manifestLicense if provided, otherwise tries LICENSE file.
85
+ * @param {string|null} manifestLicense - license from manifest (or null)
86
+ * @param {string} manifestPath - path to manifest
87
+ * @returns {string|null} - SPDX identifier or null
88
+ */
89
+ export function getLicense(manifestLicense, manifestPath) {
90
+ return manifestLicense || readLicenseFile(manifestPath) || null;
91
+ }
92
+ /**
93
+ * Normalize SPDX identifier for comparison (lowercase, strip common suffixes).
94
+ * @param {string} spdxOrName
95
+ * @returns {string}
96
+ */
97
+ export function normalizeSpdx(spdxOrName) {
98
+ const s = String(spdxOrName).trim().toLowerCase();
99
+ if (s.endsWith(' license')) {
100
+ return s.slice(0, -8);
101
+ }
102
+ return s;
103
+ }
104
+ /**
105
+ * Check if a dependency's license is compatible with the project license based on backend categories.
106
+ *
107
+ * @param {string} [projectCategory] - backend category for project license: PERMISSIVE | WEAK_COPYLEFT | STRONG_COPYLEFT | UNKNOWN
108
+ * @param {string} [dependencyCategory] - backend category for dependency license: PERMISSIVE | WEAK_COPYLEFT | STRONG_COPYLEFT | UNKNOWN
109
+ * @returns {'compatible'|'incompatible'|'unknown'}
110
+ */
111
+ export function getCompatibility(projectCategory, dependencyCategory) {
112
+ if (!projectCategory || !dependencyCategory) {
113
+ return 'unknown';
114
+ }
115
+ const proj = projectCategory.toUpperCase();
116
+ const dep = dependencyCategory.toUpperCase();
117
+ if (proj === 'UNKNOWN') {
118
+ return 'unknown';
119
+ }
120
+ if (dep === 'UNKNOWN') {
121
+ return 'incompatible';
122
+ }
123
+ const restrictiveness = {
124
+ 'PERMISSIVE': 1,
125
+ 'WEAK_COPYLEFT': 2,
126
+ 'STRONG_COPYLEFT': 3
127
+ };
128
+ const projLevel = restrictiveness[proj];
129
+ const depLevel = restrictiveness[dep];
130
+ if (projLevel === undefined || depLevel === undefined) {
131
+ return 'unknown';
132
+ }
133
+ if (depLevel > projLevel) {
134
+ return 'incompatible';
135
+ }
136
+ return 'compatible';
137
+ }
@@ -6,7 +6,7 @@
6
6
  * @param {import('../index.js').Options} [opts={}] - options (proxy, token, TRUSTIFY_DA_BACKEND_URL, etc.)
7
7
  * @returns {Promise<Object|null>} License details or null if not found
8
8
  */
9
- export function getLicenseDetails(spdxId: string, opts?: import("../index.js").Options): Promise<any | null>;
9
+ export function getLicenseDetails(spdxId: string, opts?: import("../index.js").Options): Promise<Object | null>;
10
10
  /**
11
11
  * Normalize the LicensesResponse shape (array of LicenseProviderResult) into a map of purl -> license info.
12
12
  * Each provider result has { status, summary, packages } where packages is { [purl]: { concluded, evidence } }.
@@ -28,7 +28,7 @@ export function normalizeLicensesResponse(data: unknown, purls?: string[]): Map<
28
28
  * @param {string[]} [purls] - optional list of purls to restrict to
29
29
  * @returns {Map<string, { licenses: string[], category?: string }>}
30
30
  */
31
- export function licensesFromReport(analysisReport: import("@trustify-da/trustify-da-api-model/model/v5/AnalysisReport").AnalysisReport, purls?: string[]): Map<string, {
31
+ export function licensesFromReport(analysisReport: any, purls?: string[]): Map<string, {
32
32
  licenses: string[];
33
33
  category?: string;
34
34
  }>;
@@ -4,6 +4,7 @@
4
4
  * @see https://github.com/guacsec/trustify-dependency-analytics#license-analysis-apiv5licenses
5
5
  * @see https://github.com/guacsec/trustify-da-api-spec/blob/main/api/v5/openapi.yaml
6
6
  */
7
+ import { PackageURL } from 'packageurl-js';
7
8
  import { selectTrustifyDABackend } from '../index.js';
8
9
  import { addProxyAgent, getTokenHeaders } from '../tools.js';
9
10
  /**
@@ -39,6 +40,10 @@ export async function getLicenseDetails(spdxId, opts = {}) {
39
40
  throw new Error(`Failed to fetch license details: ${err.message}`);
40
41
  }
41
42
  }
43
+ function normalizePurlString(purl) {
44
+ const parsed = PackageURL.fromString(purl);
45
+ return new PackageURL(parsed.type, parsed.namespace, parsed.name, parsed.version, null, null).toString();
46
+ }
42
47
  /**
43
48
  * Normalize the LicensesResponse shape (array of LicenseProviderResult) into a map of purl -> license info.
44
49
  * Each provider result has { status, summary, packages } where packages is { [purl]: { concluded, evidence } }.
@@ -53,6 +58,7 @@ export function normalizeLicensesResponse(data, purls = []) {
53
58
  if (!data || !Array.isArray(data)) {
54
59
  return map;
55
60
  }
61
+ const normalizedPurlsSet = purls.length > 0 ? new Set(purls.map(normalizePurlString)) : null;
56
62
  for (const providerResult of data) {
57
63
  const packages = providerResult?.packages;
58
64
  if (!packages || typeof packages !== 'object') {
@@ -64,8 +70,9 @@ export function normalizeLicensesResponse(data, purls = []) {
64
70
  const expression = concluded?.expression;
65
71
  const licenses = identifiers.length > 0 ? identifiers : (expression ? [expression] : []);
66
72
  const category = concluded?.category; // PERMISSIVE | WEAK_COPYLEFT | STRONG_COPYLEFT | UNKNOWN
67
- if (purls.length === 0 || purls.includes(purl)) {
68
- map.set(purl, { licenses: licenses.filter(Boolean), category });
73
+ const normalizedPurl = normalizePurlString(purl);
74
+ if (normalizedPurlsSet === null || normalizedPurlsSet.has(normalizedPurl)) {
75
+ map.set(normalizedPurl, { licenses: licenses.filter(Boolean), category });
69
76
  }
70
77
  }
71
78
  // Use first provider that has packages; backend may return multiple (e.g. deps.dev)
@@ -10,12 +10,6 @@ export function getProjectLicense(manifestPath: string): {
10
10
  fromFile: string | null;
11
11
  mismatch: boolean;
12
12
  };
13
- /**
14
- * Find LICENSE file path in the same directory as the manifest.
15
- * @param {string} manifestPath
16
- * @returns {string|null} - path to LICENSE file or null if not found
17
- */
18
- export function findLicenseFilePath(manifestPath: string): string | null;
19
13
  /**
20
14
  * Call backend /licenses/identify endpoint to identify license from file.
21
15
  * @param {string} licenseFilePath - path to LICENSE file
@@ -23,3 +17,4 @@ export function findLicenseFilePath(manifestPath: string): string | null;
23
17
  * @returns {Promise<string|null>} - SPDX identifier or null
24
18
  */
25
19
  export function identifyLicense(licenseFilePath: string, opts?: {}): Promise<string | null>;
20
+ export { findLicenseFilePath, readLicenseFile } from "./license_utils.js";
@@ -7,7 +7,7 @@ import path from 'node:path';
7
7
  import { selectTrustifyDABackend } from '../index.js';
8
8
  import { matchForLicense, availableProviders } from '../provider.js';
9
9
  import { addProxyAgent, getTokenHeaders } from '../tools.js';
10
- const LICENSE_FILES = ['LICENSE', 'LICENSE.md', 'LICENSE.txt'];
10
+ import { normalizeSpdx, readLicenseFile } from './license_utils.js';
11
11
  /**
12
12
  * Resolve project license from manifest and from LICENSE / LICENSE.md in manifest dir or git root.
13
13
  * Uses local pattern matching for LICENSE file identification (synchronous).
@@ -19,7 +19,7 @@ export function getProjectLicense(manifestPath) {
19
19
  const resolved = path.resolve(manifestPath);
20
20
  const provider = matchForLicense(resolved, availableProviders);
21
21
  const fromManifest = provider.readLicenseFromManifest(resolved);
22
- const fromFile = readLicenseFromFile(resolved);
22
+ const fromFile = readLicenseFile(resolved);
23
23
  const mismatch = Boolean(fromManifest && fromFile && normalizeSpdx(fromManifest) !== normalizeSpdx(fromFile));
24
24
  return {
25
25
  fromManifest: fromManifest || null,
@@ -27,26 +27,7 @@ export function getProjectLicense(manifestPath) {
27
27
  mismatch
28
28
  };
29
29
  }
30
- /**
31
- * Find LICENSE file path in the same directory as the manifest.
32
- * @param {string} manifestPath
33
- * @returns {string|null} - path to LICENSE file or null if not found
34
- */
35
- export function findLicenseFilePath(manifestPath) {
36
- const manifestDir = path.dirname(path.resolve(manifestPath));
37
- for (const name of LICENSE_FILES) {
38
- const filePath = path.join(manifestDir, name);
39
- try {
40
- if (fs.statSync(filePath).isFile()) {
41
- return filePath;
42
- }
43
- }
44
- catch {
45
- // skip
46
- }
47
- }
48
- return null;
49
- }
30
+ export { findLicenseFilePath, readLicenseFile } from './license_utils.js';
50
31
  /**
51
32
  * Call backend /licenses/identify endpoint to identify license from file.
52
33
  * @param {string} licenseFilePath - path to LICENSE file
@@ -57,7 +38,7 @@ export async function identifyLicense(licenseFilePath, opts = {}) {
57
38
  try {
58
39
  const fileContent = fs.readFileSync(licenseFilePath);
59
40
  const backendUrl = selectTrustifyDABackend(opts);
60
- const url = new URL(`${backendUrl}/licenses/identify`);
41
+ const url = new URL(`${backendUrl}/api/v5/licenses/identify`);
61
42
  const tokenHeaders = getTokenHeaders(opts);
62
43
  const fetchOptions = addProxyAgent({
63
44
  method: 'POST',
@@ -79,61 +60,3 @@ export async function identifyLicense(licenseFilePath, opts = {}) {
79
60
  return null; // Fallback to local detection on error
80
61
  }
81
62
  }
82
- /**
83
- * Find and read LICENSE or LICENSE.md; use local pattern matching for identification.
84
- * @param {string} manifestPath
85
- * @returns {string|null}
86
- */
87
- function readLicenseFromFile(manifestPath) {
88
- const licenseFilePath = findLicenseFilePath(manifestPath);
89
- if (!licenseFilePath) {
90
- return null;
91
- }
92
- try {
93
- const content = fs.readFileSync(licenseFilePath, 'utf-8');
94
- return detectSpdxFromText(content) || content.split('\n')[0]?.trim() || null;
95
- }
96
- catch {
97
- return null;
98
- }
99
- }
100
- /**
101
- * Very simple SPDX detection from common license text (first ~500 chars).
102
- * @param {string} text
103
- * @returns {string|null}
104
- */
105
- function detectSpdxFromText(text) {
106
- const head = text.slice(0, 500);
107
- if (/Apache License,?\s*Version 2\.0/i.test(head)) {
108
- return 'Apache-2.0';
109
- }
110
- if (/MIT License/i.test(head) && /Permission is hereby granted/i.test(head)) {
111
- return 'MIT';
112
- }
113
- if (/GNU GENERAL PUBLIC LICENSE\s+Version 2/i.test(head)) {
114
- return 'GPL-2.0-only';
115
- }
116
- if (/GNU GENERAL PUBLIC LICENSE\s+Version 3/i.test(head)) {
117
- return 'GPL-3.0-only';
118
- }
119
- if (/BSD 2-Clause/i.test(head)) {
120
- return 'BSD-2-Clause';
121
- }
122
- if (/BSD 3-Clause/i.test(head)) {
123
- return 'BSD-3-Clause';
124
- }
125
- return null;
126
- }
127
- /**
128
- * Normalize for comparison (lowercase, strip common suffixes).
129
- * @param {string} spdxOrName
130
- * @returns {string}
131
- */
132
- function normalizeSpdx(spdxOrName) {
133
- const s = String(spdxOrName).trim().toLowerCase();
134
- // e.g. "MIT" vs "MIT License"
135
- if (s.endsWith(' license')) {
136
- return s.slice(0, -8);
137
- }
138
- return s;
139
- }
@@ -78,7 +78,7 @@ export class ImageRef {
78
78
  * @param {string} [platform]
79
79
  * @param {import("index.js").Options} [opts={}]
80
80
  */
81
- constructor(image: string, platform?: string, opts?: import("index.js").Options);
81
+ constructor(image: string, platform?: string, opts?: any);
82
82
  /** @type {Image} */
83
83
  image: Image;
84
84
  /** @type {Platform} */
@@ -242,8 +242,22 @@ export class ImageRef {
242
242
  getPackageURL() {
243
243
  /** @type {Object.<string, string>} */
244
244
  const qualifiers = {};
245
- const repositoryUrl = this.image.getNameWithoutTag();
245
+ let repositoryUrl = this.image.getNameWithoutTag();
246
246
  const simpleName = this.image.getSimpleName();
247
+ // Normalize Docker Hub image references so all forms produce the same PURL:
248
+ // node → docker.io/node
249
+ // docker.io/library/node → docker.io/node
250
+ if (repositoryUrl != null) {
251
+ const lower = repositoryUrl.toLowerCase();
252
+ if (lower === simpleName.toLowerCase()) {
253
+ // Bare name (e.g. "node") — default to docker.io
254
+ repositoryUrl = `docker.io/${simpleName}`;
255
+ }
256
+ else if (lower.startsWith('docker.io/library/')) {
257
+ // Official image with library/ prefix — strip it
258
+ repositoryUrl = `docker.io/${lower.substring('docker.io/library/'.length)}`;
259
+ }
260
+ }
247
261
  if (repositoryUrl != null && repositoryUrl.toLowerCase() !== simpleName.toLowerCase()) {
248
262
  qualifiers[ImageRef.REPOSITORY_QUALIFIER] = repositoryUrl.toLowerCase();
249
263
  }
@@ -4,7 +4,7 @@
4
4
  * @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
5
5
  * @returns {{}}
6
6
  */
7
- export function generateImageSBOM(imageRef: import("./images").ImageRef, opts?: import("../index.js").Options): {};
7
+ export function generateImageSBOM(imageRef: any, opts?: import("../index.js").Options): {};
8
8
  /**
9
9
  *
10
10
  * @param {string} image
@@ -25,7 +25,7 @@ export function getImagePlatform(opts?: import("../index.js").Options): Platform
25
25
  * @returns {Object.<string, string>} - The image digests
26
26
  * @throws {Error} If the image info is invalid
27
27
  */
28
- export function getImageDigests(imageRef: import("./images").ImageRef, opts?: import("../index.js").Options): {
28
+ export function getImageDigests(imageRef: any, opts?: import("../index.js").Options): {
29
29
  [x: string]: string;
30
30
  };
31
31
  export type SyftImageSource = {
@@ -34,9 +34,9 @@ export type SyftImageSource = {
34
34
  getVariant: () => string;
35
35
  };
36
36
  export type SyftImageSourceType = {
37
- getOs: (arg0: any) => string;
38
- getArch: (arg0: any) => string;
39
- getVariant: (arg0: any) => string;
37
+ getOs: (arg0: Object) => string;
38
+ getArch: (arg0: Object) => string;
39
+ getVariant: (arg0: Object) => string;
40
40
  };
41
41
  import { ImageRef } from './images.js';
42
42
  import { Platform } from './platform.js';
@@ -135,7 +135,7 @@ function execSyft(imageRef, opts = {}) {
135
135
  function getSyftEnvs(dockerPath, podmanPath) {
136
136
  let path = null;
137
137
  if (dockerPath && podmanPath) {
138
- path = `${dockerPath}${File.pathSeparator}${podmanPath}`;
138
+ path = `${dockerPath}${delimiter}${podmanPath}`;
139
139
  }
140
140
  else if (dockerPath) {
141
141
  path = dockerPath;
@@ -276,7 +276,16 @@ function podmanGetVariant(opts = {}) {
276
276
  * @returns {string} - The information
277
277
  */
278
278
  function dockerPodmanInfo(dockerSupplier, podmanSupplier, opts = {}) {
279
- return dockerSupplier(opts) || podmanSupplier(opts);
279
+ try {
280
+ const result = dockerSupplier(opts);
281
+ if (result) {
282
+ return result;
283
+ }
284
+ }
285
+ catch (_) {
286
+ // docker not available, fall through to podman
287
+ }
288
+ return podmanSupplier(opts);
280
289
  }
281
290
  /**
282
291
  * Gets the digests for an image
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Reads this package's version from package.json, resolving the path relative
3
+ * to this module. Works under ESM (node >= 22 import.meta.dirname, older via
4
+ * import.meta.url) and CommonJS (__dirname). Returns undefined if the file
5
+ * cannot be read, so callers never fail on a missing version.
6
+ * @return {string|undefined} the package version, or undefined
7
+ */
8
+ export function getPackageVersion(): string | undefined;
@@ -0,0 +1,31 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import * as url from "url";
4
+ /**
5
+ * Reads this package's version from package.json, resolving the path relative
6
+ * to this module. Works under ESM (node >= 22 import.meta.dirname, older via
7
+ * import.meta.url) and CommonJS (__dirname). Returns undefined if the file
8
+ * cannot be read, so callers never fail on a missing version.
9
+ * @return {string|undefined} the package version, or undefined
10
+ */
11
+ export function getPackageVersion() {
12
+ try {
13
+ let dirName = import.meta.dirname;
14
+ if (!dirName) {
15
+ dirName = url.fileURLToPath(new URL('.', import.meta.url));
16
+ }
17
+ try {
18
+ if (__dirname) {
19
+ dirName = __dirname;
20
+ }
21
+ }
22
+ catch {
23
+ // __dirname not defined under ESM, continue with fileURLToPath
24
+ }
25
+ const packageJson = JSON.parse(fs.readFileSync(path.join(dirName, "..", "package.json")).toString());
26
+ return packageJson.version;
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ }
@@ -13,12 +13,15 @@ export function matchForLicense(manifestPath: string, providers: [Provider]): Pr
13
13
  * Each provider MUST export 'provideStack' taking manifest path returning a {@link Provided}.
14
14
  * @param {string} manifest - the name-type or path of the manifest
15
15
  * @param {[Provider]} providers - list of providers to iterate over
16
+ * @param {{TRUSTIFY_DA_WORKSPACE_DIR?: string}} [opts={}] - optional; TRUSTIFY_DA_WORKSPACE_DIR overrides lock file location for workspaces
16
17
  * @returns {Provider}
17
18
  * @throws {Error} when the manifest is not supported and no provider was matched
18
19
  */
19
- export function match(manifest: string, providers: [Provider]): Provider;
20
+ export function match(manifest: string, providers: [Provider], opts?: {
21
+ TRUSTIFY_DA_WORKSPACE_DIR?: string;
22
+ }): Provider;
20
23
  /** @typedef {{ecosystem: string, contentType: string, content: string}} Provided */
21
- /** @typedef {{isSupported: function(string): boolean, validateLockFile: function(string): void, provideComponent: function(string, {}): Provided | Promise<Provided>, provideStack: function(string, {}): Provided | Promise<Provided>, readLicenseFromManifest: function(string): string | null}} Provider */
24
+ /** @typedef {{isSupported: function(string): boolean, validateLockFile: function(string, Object): void, provideComponent: function(string, {}): Provided | Promise<Provided>, provideStack: function(string, {}): Provided | Promise<Provided>, readLicenseFromManifest: function(string, Object=): string | null, packageManagerName: function(): string}} Provider */
22
25
  /**
23
26
  * MUST include all providers here.
24
27
  * @type {[Provider]}
@@ -31,8 +34,9 @@ export type Provided = {
31
34
  };
32
35
  export type Provider = {
33
36
  isSupported: (arg0: string) => boolean;
34
- validateLockFile: (arg0: string) => void;
37
+ validateLockFile: (arg0: string, arg1: Object) => void;
35
38
  provideComponent: (arg0: string, arg1: {}) => Provided | Promise<Provided>;
36
39
  provideStack: (arg0: string, arg1: {}) => Provided | Promise<Provided>;
37
- readLicenseFromManifest: (arg0: string) => string | null;
40
+ readLicenseFromManifest: (arg0: string, arg1: Object | undefined) => string | null;
41
+ packageManagerName: () => string;
38
42
  };
@@ -3,12 +3,18 @@ import golangGomodulesProvider from './providers/golang_gomodules.js';
3
3
  import Java_gradle_groovy from "./providers/java_gradle_groovy.js";
4
4
  import Java_gradle_kotlin from "./providers/java_gradle_kotlin.js";
5
5
  import Java_maven from "./providers/java_maven.js";
6
+ import Javascript_bun from './providers/javascript_bun.js';
6
7
  import Javascript_npm from './providers/javascript_npm.js';
7
8
  import Javascript_pnpm from './providers/javascript_pnpm.js';
8
9
  import Javascript_yarn from './providers/javascript_yarn.js';
10
+ import dockerfileProvider from './providers/oci_dockerfile.js';
9
11
  import pythonPipProvider from './providers/python_pip.js';
12
+ import Python_pip_pyproject from './providers/python_pip_pyproject.js';
13
+ import Python_poetry from './providers/python_poetry.js';
14
+ import Python_uv from './providers/python_uv.js';
15
+ import rustCargoProvider from './providers/rust_cargo.js';
10
16
  /** @typedef {{ecosystem: string, contentType: string, content: string}} Provided */
11
- /** @typedef {{isSupported: function(string): boolean, validateLockFile: function(string): void, provideComponent: function(string, {}): Provided | Promise<Provided>, provideStack: function(string, {}): Provided | Promise<Provided>, readLicenseFromManifest: function(string): string | null}} Provider */
17
+ /** @typedef {{isSupported: function(string): boolean, validateLockFile: function(string, Object): void, provideComponent: function(string, {}): Provided | Promise<Provided>, provideStack: function(string, {}): Provided | Promise<Provided>, readLicenseFromManifest: function(string, Object=): string | null, packageManagerName: function(): string}} Provider */
12
18
  /**
13
19
  * MUST include all providers here.
14
20
  * @type {[Provider]}
@@ -17,11 +23,17 @@ export const availableProviders = [
17
23
  new Java_maven(),
18
24
  new Java_gradle_groovy(),
19
25
  new Java_gradle_kotlin(),
20
- new Javascript_npm(),
26
+ new Javascript_bun(),
21
27
  new Javascript_pnpm(),
22
28
  new Javascript_yarn(),
29
+ new Javascript_npm(),
23
30
  golangGomodulesProvider,
24
- pythonPipProvider
31
+ pythonPipProvider,
32
+ new Python_poetry(),
33
+ new Python_uv(),
34
+ new Python_pip_pyproject(),
35
+ rustCargoProvider,
36
+ dockerfileProvider
25
37
  ];
26
38
  /**
27
39
  * Match a provider by manifest type only (no lock file check). Used for license reading.
@@ -45,16 +57,17 @@ export function matchForLicense(manifestPath, providers) {
45
57
  * Each provider MUST export 'provideStack' taking manifest path returning a {@link Provided}.
46
58
  * @param {string} manifest - the name-type or path of the manifest
47
59
  * @param {[Provider]} providers - list of providers to iterate over
60
+ * @param {{TRUSTIFY_DA_WORKSPACE_DIR?: string}} [opts={}] - optional; TRUSTIFY_DA_WORKSPACE_DIR overrides lock file location for workspaces
48
61
  * @returns {Provider}
49
62
  * @throws {Error} when the manifest is not supported and no provider was matched
50
63
  */
51
- export function match(manifest, providers) {
64
+ export function match(manifest, providers, opts = {}) {
52
65
  const manifestPath = path.parse(manifest);
53
66
  const supported = providers.filter(prov => prov.isSupported(manifestPath.base));
54
67
  if (supported.length === 0) {
55
68
  throw new Error(`${manifestPath.base} is not supported`);
56
69
  }
57
- const provider = supported.find(prov => prov.validateLockFile(manifestPath.dir));
70
+ const provider = supported.find(prov => prov.validateLockFile(manifestPath.dir, opts));
58
71
  if (!provider) {
59
72
  throw new Error(`${manifestPath.base} requires a lock file. Use your preferred package manager to generate the lock file.`);
60
73
  }
@@ -1,4 +1,4 @@
1
- export type ecosystem_maven = import("../provider").Provider;
1
+ export type ecosystem_maven = any;
2
2
  /** @typedef {import('../provider').Provider} */
3
3
  /** @typedef {import('../provider').Provided} Provided */
4
4
  /** @typedef {{name: string, version: string}} Package */
@@ -10,6 +10,13 @@ export type ecosystem_maven = import("../provider").Provider;
10
10
  export const ecosystem_maven: string;
11
11
  export const ecosystem_gradle: "gradle";
12
12
  export default class Base_Java {
13
+ /**
14
+ * Maven dependency scopes. Used to detect whether the trailing column of a
15
+ * dependency-tree line is a scope keyword — which in turn signals that an
16
+ * optional classifier column is present between the packaging and version.
17
+ * @type {string[]}
18
+ */
19
+ static MAVEN_SCOPES: string[];
13
20
  /**
14
21
  *
15
22
  * @param {string} globalBinary name of the global binary
@@ -20,6 +27,11 @@ export default class Base_Java {
20
27
  CONFLICT_REGEX: RegExp;
21
28
  globalBinary: string;
22
29
  localWrapper: string;
30
+ /**
31
+ * Returns the package manager name (e.g. mvn, gradle)
32
+ * @returns {string}
33
+ */
34
+ packageManagerName(): string;
23
35
  /**
24
36
  * Recursively populates the SBOM instance with the parsed graph
25
37
  * @param {string} src - Source dependency to start the calculations from
@@ -28,6 +40,59 @@ export default class Base_Java {
28
40
  * @param {Sbom} sbom - The SBOM where the dependencies are being added
29
41
  */
30
42
  parseDependencyTree(src: string, srcDepth: number, lines: any[], sbom: Sbom): void;
43
+ /**
44
+ * Calculates how deep in the graph is the given line
45
+ * @param {string} line - line to calculate the depth from
46
+ * @returns {number} The calculated depth
47
+ * @protected
48
+ */
49
+ protected _getDepth(line: string): number;
50
+ /**
51
+ * Parse a single dependency-tree line into its Maven coordinate parts.
52
+ *
53
+ * This is the single source of truth for interpreting a dependency-tree
54
+ * line. Both {@link parseDep} (used to build SBOM component PURLs) and the
55
+ * Maven hash-map builder rely on it, so the PURL key and the artifact file
56
+ * path can never drift from one another.
57
+ *
58
+ * A line has the shape `groupId:artifactId:packaging[:classifier]:version[:scope]`.
59
+ * The classifier column is only present when a sixth column holds a known
60
+ * Maven scope keyword; otherwise the fourth column is the version.
61
+ *
62
+ * @param {string} line - line to parse from a dependency tree
63
+ * @returns {{groupId: string, artifactId: string, packaging: string, classifier: (string|null), version: string, scope: (string|null), overridden: boolean}}
64
+ * Parsed coordinate. `version` is the resolved concrete version — when the
65
+ * line carries a conflict override the override version replaces it and
66
+ * `overridden` is set, mirroring how Maven records the winning version.
67
+ */
68
+ parseCoordinate(line: string): {
69
+ groupId: string;
70
+ artifactId: string;
71
+ packaging: string;
72
+ classifier: (string | null);
73
+ version: string;
74
+ scope: (string | null);
75
+ overridden: boolean;
76
+ };
77
+ /**
78
+ * Build the canonical PackageURL for a parsed coordinate.
79
+ *
80
+ * The classifier is folded into the version component (e.g.
81
+ * `4.1.0-linux-x86_64`) except when the version came from a conflict
82
+ * override, in which case the override version stands alone — matching the
83
+ * historical behavior of {@link parseDep}.
84
+ *
85
+ * @param {{groupId: string, artifactId: string, classifier: (string|null), version: string, overridden: boolean}} coord
86
+ * @returns {PackageURL} The canonical packageURL for the coordinate
87
+ * @protected
88
+ */
89
+ protected _coordinateToPurl(coord: {
90
+ groupId: string;
91
+ artifactId: string;
92
+ classifier: (string | null);
93
+ version: string;
94
+ overridden: boolean;
95
+ }): PackageURL;
31
96
  /**
32
97
  * Create a PackageURL from any line in a Text Graph dependency tree for a manifest path.
33
98
  * @param {string} line - line to parse from a dependencies.txt file
@@ -57,18 +122,8 @@ export default class Base_Java {
57
122
  * @returns string
58
123
  */
59
124
  selectToolBinary(manifestPath: string, opts: {}): string | null;
60
- /**
61
- *
62
- * @param {string} startingManifest - the path of the manifest from which to start searching for the wrapper
63
- * @param {string} repoRoot - the root of the repository at which point to stop searching for mvnw, derived via git if unset and then fallsback
64
- * to the root of the drive the manifest is on (assumes absolute path is given)
65
- * @returns {string|undefined}
66
- */
67
- traverseForWrapper(startingManifest: string, repoRoot?: string): string | undefined;
68
- normalizePath(thePath: any): string;
69
- #private;
70
125
  }
71
- export type Provided = import("../provider").Provided;
126
+ export type Provided = any;
72
127
  export type Package = {
73
128
  name: string;
74
129
  version: string;