@trustify-da/trustify-da-javascript-client 0.3.0-ea.e645720 → 0.3.0-ea.ec6824b

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 (42) hide show
  1. package/dist/package.json +19 -19
  2. package/dist/src/analysis.d.ts +7 -7
  3. package/dist/src/cyclone_dx_sbom.js +11 -3
  4. package/dist/src/index.d.ts +9 -9
  5. package/dist/src/index.js +5 -2
  6. package/dist/src/license/index.d.ts +3 -3
  7. package/dist/src/license/index.js +4 -1
  8. package/dist/src/license/license_utils.js +4 -1
  9. package/dist/src/license/licenses_api.d.ts +2 -2
  10. package/dist/src/oci_image/images.d.ts +1 -1
  11. package/dist/src/oci_image/utils.d.ts +5 -5
  12. package/dist/src/provider.d.ts +4 -3
  13. package/dist/src/provider.js +3 -1
  14. package/dist/src/providers/base_java.d.ts +14 -3
  15. package/dist/src/providers/base_java.js +16 -6
  16. package/dist/src/providers/base_javascript.d.ts +20 -9
  17. package/dist/src/providers/base_javascript.js +10 -3
  18. package/dist/src/providers/base_pyproject.d.ts +10 -5
  19. package/dist/src/providers/base_pyproject.js +7 -0
  20. package/dist/src/providers/golang_gomodules.d.ts +2 -1
  21. package/dist/src/providers/golang_gomodules.js +1 -1
  22. package/dist/src/providers/java_gradle_groovy.d.ts +1 -1
  23. package/dist/src/providers/java_gradle_kotlin.d.ts +1 -1
  24. package/dist/src/providers/java_maven.d.ts +2 -2
  25. package/dist/src/providers/java_maven.js +72 -0
  26. package/dist/src/providers/javascript_bun.d.ts +10 -0
  27. package/dist/src/providers/javascript_bun.js +100 -0
  28. package/dist/src/providers/javascript_npm.d.ts +6 -1
  29. package/dist/src/providers/processors/yarn_berry_processor.d.ts +2 -2
  30. package/dist/src/providers/processors/yarn_classic_processor.d.ts +2 -2
  31. package/dist/src/providers/python_controller.js +57 -3
  32. package/dist/src/providers/python_pip.d.ts +1 -0
  33. package/dist/src/providers/python_pip.js +1 -1
  34. package/dist/src/providers/python_pip_pyproject.js +3 -1
  35. package/dist/src/providers/python_poetry.d.ts +31 -8
  36. package/dist/src/providers/python_poetry.js +62 -14
  37. package/dist/src/providers/python_uv.d.ts +5 -14
  38. package/dist/src/providers/python_uv.js +44 -2
  39. package/dist/src/providers/rust_cargo.d.ts +6 -2
  40. package/dist/src/providers/rust_cargo.js +31 -4
  41. package/dist/src/tools.d.ts +2 -2
  42. package/package.json +20 -20
@@ -13,7 +13,7 @@ export default class Base_pyproject {
13
13
  * @param {Object} [opts={}]
14
14
  * @returns {boolean}
15
15
  */
16
- validateLockFile(manifestDir: string, opts?: any): boolean;
16
+ validateLockFile(manifestDir: string, opts?: Object): boolean;
17
17
  /**
18
18
  * Walk up from manifestDir to find the directory containing the lock file.
19
19
  * Follows the same pattern as Base_javascript._findLockFileDir().
@@ -22,7 +22,7 @@ export default class Base_pyproject {
22
22
  * @returns {string|null}
23
23
  * @protected
24
24
  */
25
- protected _findLockFileDir(manifestDir: string, opts?: any): string | null;
25
+ protected _findLockFileDir(manifestDir: string, opts?: Object): string | null;
26
26
  /**
27
27
  * Detect workspace root boundaries.
28
28
  * Currently only uv has native workspace support ([tool.uv.workspace] in pyproject.toml).
@@ -44,13 +44,13 @@ export default class Base_pyproject {
44
44
  * @param {Object} [opts={}]
45
45
  * @returns {Promise<Provided>}
46
46
  */
47
- provideStack(manifest: string, opts?: any): Promise<Provided>;
47
+ provideStack(manifest: string, opts?: Object): Promise<Provided>;
48
48
  /**
49
49
  * @param {string} manifest - path to pyproject.toml
50
50
  * @param {Object} [opts={}]
51
51
  * @returns {Promise<Provided>}
52
52
  */
53
- provideComponent(manifest: string, opts?: any): Promise<Provided>;
53
+ provideComponent(manifest: string, opts?: Object): Promise<Provided>;
54
54
  /**
55
55
  * @returns {string}
56
56
  * @protected
@@ -61,6 +61,11 @@ export default class Base_pyproject {
61
61
  * @protected
62
62
  */
63
63
  protected _cmdName(): string;
64
+ /**
65
+ * Returns the package manager name (e.g. pip, poetry, uv)
66
+ * @returns {string}
67
+ */
68
+ packageManagerName(): string;
64
69
  /**
65
70
  * Resolve dependencies using the tool-specific command and parser.
66
71
  *
@@ -72,7 +77,7 @@ export default class Base_pyproject {
72
77
  * @returns {Promise<DependencyData>}
73
78
  * @protected
74
79
  */
75
- protected _getDependencyData(manifestDir: string, workspaceDir: string, parsed: object, opts: any): Promise<DependencyData>;
80
+ protected _getDependencyData(manifestDir: string, workspaceDir: string, parsed: object, opts: Object): Promise<DependencyData>;
76
81
  /**
77
82
  * Canonicalize a Python package name per PEP 503.
78
83
  * @param {string} name
@@ -145,6 +145,13 @@ export default class Base_pyproject {
145
145
  _cmdName() {
146
146
  throw new TypeError('_cmdName must be implemented');
147
147
  }
148
+ /**
149
+ * Returns the package manager name (e.g. pip, poetry, uv)
150
+ * @returns {string}
151
+ */
152
+ packageManagerName() {
153
+ return this._cmdName();
154
+ }
148
155
  /**
149
156
  * Resolve dependencies using the tool-specific command and parser.
150
157
  *
@@ -13,9 +13,10 @@ declare namespace _default {
13
13
  export { provideComponent };
14
14
  export { provideStack };
15
15
  export { readLicenseFromManifest };
16
+ export function packageManagerName(): string;
16
17
  }
17
18
  export default _default;
18
- export type Provided = import("../provider").Provided;
19
+ export type Provided = any;
19
20
  export type Package = {
20
21
  name: string;
21
22
  version: string;
@@ -6,7 +6,7 @@ import Sbom from '../sbom.js';
6
6
  import { getCustom, getCustomPath, invokeCommand } from "../tools.js";
7
7
  import { filterManifestPathsByDiscoveryIgnore, resolveWorkspaceDiscoveryIgnore } from '../workspace.js';
8
8
  import { getParser, getRequireQuery } from './gomod_parser.js';
9
- export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest };
9
+ export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest, packageManagerName() { return 'go'; } };
10
10
  /** @typedef {import('../provider').Provider} */
11
11
  /** @typedef {import('../provider').Provided} Provided */
12
12
  /** @typedef {{name: string, version: string}} Package */
@@ -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("../provider").Provided;
6
+ export type Provided = any;
7
7
  import Java_gradle from './java_gradle.js';
@@ -7,5 +7,5 @@ export default class Java_gradle_kotlin extends Java_gradle {
7
7
  _parseAliasForLibsNotation(alias: any): any;
8
8
  _extractDepToBeIgnored(dep: any): string | null;
9
9
  }
10
- export type Provided = import("../provider").Provided;
10
+ export type Provided = any;
11
11
  import Java_gradle from './java_gradle.js';
@@ -51,8 +51,8 @@ export default class Java_maven extends Base_java {
51
51
  createSbomFileFromTextFormat(textGraphList: string, ignoredDeps: [string], opts: any, manifestPath: string): string;
52
52
  #private;
53
53
  }
54
- export type Java_maven = import("../provider").Provider;
55
- export type Provided = import("../provider").Provided;
54
+ export type Java_maven = any;
55
+ export type Provided = any;
56
56
  export type Package = {
57
57
  name: string;
58
58
  version: string;
@@ -181,6 +181,7 @@ export default class Java_maven extends Base_java {
181
181
  /** @type [Dependency] */
182
182
  let dependencies = this.#getDependencies(tmpEffectivePom)
183
183
  .filter(d => !this.#dependencyIn(d, ignored));
184
+ dependencies = this.#resolveVersionRanges(dependencies, manifestPath, opts);
184
185
  let sbom = new Sbom();
185
186
  let rootDependency = this.#getRootFromPom(tmpEffectivePom, manifestPath);
186
187
  let purlRoot = this.toPurl(rootDependency.groupId, rootDependency.artifactId, rootDependency.version);
@@ -289,6 +290,77 @@ export default class Java_maven extends Base_java {
289
290
  #dependencyIn(dep, deps) {
290
291
  return deps.filter(d => dep.artifactId === d.artifactId && dep.groupId === d.groupId && dep.scope === d.scope).length > 0;
291
292
  }
293
+ /**
294
+ * Returns true if the given version string is a Maven version range
295
+ * (starts with '[' or '(').
296
+ * @param {string} version
297
+ * @returns {boolean}
298
+ * @private
299
+ */
300
+ #isVersionRange(version) {
301
+ return typeof version === 'string' && (version.startsWith('[') || version.startsWith('('));
302
+ }
303
+ /**
304
+ * Resolves Maven version ranges in the given dependency list by running
305
+ * maven-dependency-plugin:tree and reading the concrete versions it selects.
306
+ * If no dependency uses a version range, returns the list unchanged.
307
+ * @param {Dependency[]} dependencies
308
+ * @param {string} manifestPath
309
+ * @param {object} opts
310
+ * @returns {Dependency[]}
311
+ * @private
312
+ */
313
+ #resolveVersionRanges(dependencies, manifestPath, opts = {}) {
314
+ // short-circuit if no dependency has a version range
315
+ if (!dependencies.some(dep => this.#isVersionRange(dep.version))) {
316
+ return dependencies;
317
+ }
318
+ const mvn = this.selectToolBinary(manifestPath, opts);
319
+ const mvnArgs = JSON.parse(getCustom('TRUSTIFY_DA_MVN_ARGS', '[]', opts));
320
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'trustify_da_range_'));
321
+ const tmpDepTree = path.join(tmpDir, 'mvn_deptree_ranges.txt');
322
+ try {
323
+ this._invokeCommand(mvn, [
324
+ '-q',
325
+ 'org.apache.maven.plugins:maven-dependency-plugin:3.6.0:tree',
326
+ '-Dscope=compile',
327
+ '-DoutputType=text',
328
+ `-DoutputFile=${tmpDepTree}`,
329
+ ...mvnArgs
330
+ ], { cwd: path.dirname(manifestPath) });
331
+ const content = fs.readFileSync(tmpDepTree);
332
+ const lines = content.toString().split(EOL).filter(l => l.trim() !== '');
333
+ // Build a map of groupId:artifactId -> resolved version from depth-1 entries
334
+ /** @type {Map<string, string>} */
335
+ const resolvedVersions = new Map();
336
+ for (const line of lines) {
337
+ if (this._getDepth(line) === 1) {
338
+ const purl = this.parseDep(line);
339
+ resolvedVersions.set(`${purl.namespace}:${purl.name}`, purl.version);
340
+ }
341
+ }
342
+ // Replace version ranges with resolved concrete versions
343
+ return dependencies.map(dep => {
344
+ if (this.#isVersionRange(dep.version)) {
345
+ const key = `${dep.groupId}:${dep.artifactId}`;
346
+ const resolved = resolvedVersions.get(key);
347
+ if (resolved) {
348
+ return { ...dep, version: resolved };
349
+ }
350
+ }
351
+ return dep;
352
+ });
353
+ }
354
+ catch (error) {
355
+ if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
356
+ console.error("Failed to resolve Maven version ranges: " + error.message);
357
+ }
358
+ return dependencies;
359
+ }
360
+ finally {
361
+ fs.rmSync(tmpDir, { recursive: true, force: true });
362
+ }
363
+ }
292
364
  }
293
365
  const DEFAULT_MAVEN_DISCOVERY_IGNORE = [
294
366
  '**/target/**',
@@ -0,0 +1,10 @@
1
+ export default class Javascript_bun extends Base_javascript {
2
+ _listCmdArgs(): void;
3
+ _buildDependencyTree(includeTransitive: any, opts?: {}): {
4
+ name: any;
5
+ version: any;
6
+ dependencies: {};
7
+ };
8
+ #private;
9
+ }
10
+ import Base_javascript from './base_javascript.js';
@@ -0,0 +1,100 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parse } from 'jsonc-parser';
4
+ import Base_javascript from './base_javascript.js';
5
+ export default class Javascript_bun extends Base_javascript {
6
+ _lockFileName() {
7
+ return "bun.lock";
8
+ }
9
+ _cmdName() {
10
+ return "bun";
11
+ }
12
+ _listCmdArgs() {
13
+ throw new Error("not supported by Bun");
14
+ }
15
+ _updateLockFileCmdArgs() {
16
+ return ['install', '--lockfile-only'];
17
+ }
18
+ _buildDependencyTree(includeTransitive, opts = {}) {
19
+ this._version();
20
+ const manifestDir = path.dirname(this._getManifest().manifestPath);
21
+ const lockDir = this._findLockFileDir(manifestDir, opts) || manifestDir;
22
+ this._createLockFile(lockDir);
23
+ const lockContent = fs.readFileSync(path.join(lockDir, 'bun.lock'), 'utf-8');
24
+ const lockData = parse(lockContent);
25
+ const packages = lockData.packages || {};
26
+ const memberName = this._getManifest().name;
27
+ const workspaceEntry = this.#findWorkspaceEntry(lockData, lockDir, manifestDir, memberName);
28
+ const directDeps = workspaceEntry?.dependencies || {};
29
+ const tree = { name: memberName, version: this._getManifest().version, dependencies: {} };
30
+ const visited = new Set();
31
+ for (const depName of Object.keys(directDeps)) {
32
+ const resolved = this.#resolvePackage(depName, '', packages);
33
+ if (resolved) {
34
+ tree.dependencies[depName] = this.#buildNode(depName, resolved, packages, includeTransitive, visited);
35
+ }
36
+ }
37
+ return tree;
38
+ }
39
+ #findWorkspaceEntry(lockData, lockDir, manifestDir, memberName) {
40
+ const workspaces = lockData.workspaces || {};
41
+ const relPath = path.relative(lockDir, path.resolve(manifestDir));
42
+ if (!relPath || relPath === '.') {
43
+ return workspaces[''] || {};
44
+ }
45
+ const normalised = relPath.split(path.sep).join('/');
46
+ if (workspaces[normalised]) {
47
+ return workspaces[normalised];
48
+ }
49
+ for (const [wsPath, entry] of Object.entries(workspaces)) {
50
+ if (entry.name === memberName && wsPath !== '') {
51
+ return entry;
52
+ }
53
+ }
54
+ return workspaces[''] || {};
55
+ }
56
+ #resolvePackage(depName, parentKey, packages) {
57
+ if (parentKey) {
58
+ const scopedKey = `${parentKey}/${depName}`;
59
+ if (packages[scopedKey]) {
60
+ return packages[scopedKey];
61
+ }
62
+ }
63
+ return packages[depName] || null;
64
+ }
65
+ #buildNode(depName, resolved, packages, includeTransitive, visited) {
66
+ const resolvedId = Array.isArray(resolved) ? resolved[0] : resolved;
67
+ const version = this.#extractVersion(resolvedId);
68
+ const node = { version };
69
+ if (!includeTransitive) {
70
+ return node;
71
+ }
72
+ const metadata = Array.isArray(resolved) ? (resolved[2] || {}) : {};
73
+ const subDeps = metadata.dependencies || {};
74
+ if (Object.keys(subDeps).length > 0) {
75
+ const visitKey = `${depName}@${version}`;
76
+ if (visited.has(visitKey)) {
77
+ return node;
78
+ }
79
+ visited.add(visitKey);
80
+ node.dependencies = {};
81
+ for (const subName of Object.keys(subDeps)) {
82
+ const subResolved = this.#resolvePackage(subName, depName, packages);
83
+ if (subResolved) {
84
+ node.dependencies[subName] = this.#buildNode(subName, subResolved, packages, true, visited);
85
+ }
86
+ }
87
+ }
88
+ return node;
89
+ }
90
+ #extractVersion(resolvedId) {
91
+ if (typeof resolvedId !== 'string') {
92
+ return '0.0.0';
93
+ }
94
+ const atIdx = resolvedId.lastIndexOf('@');
95
+ if (atIdx > 0) {
96
+ return resolvedId.substring(atIdx + 1);
97
+ }
98
+ return '0.0.0';
99
+ }
100
+ }
@@ -1,5 +1,10 @@
1
1
  export default class Javascript_npm extends Base_javascript {
2
2
  _listCmdArgs(includeTransitive: any): string[];
3
- _buildDependencyTree(includeTransitive: any, opts?: {}): any;
3
+ _buildDependencyTree(includeTransitive: any, opts?: {}): Object | {
4
+ name: any;
5
+ version: any;
6
+ dependencies: any;
7
+ optionalDependencies: any;
8
+ };
4
9
  }
5
10
  import Base_javascript from './base_javascript.js';
@@ -29,13 +29,13 @@ export default class Yarn_berry_processor extends Yarn_processor {
29
29
  * @param {Object} depTree - The dependency tree object
30
30
  * @returns {Map<string, PackageURL>} Map of dependency names to their PackageURL objects
31
31
  */
32
- getRootDependencies(depTree: any): Map<string, PackageURL>;
32
+ getRootDependencies(depTree: Object): Map<string, PackageURL>;
33
33
  /**
34
34
  * Adds dependencies to the SBOM
35
35
  * @param {Sbom} sbom - The SBOM object to add dependencies to
36
36
  * @param {Object} depTree - The dependency tree object
37
37
  */
38
- addDependenciesToSbom(sbom: Sbom, depTree: any): void;
38
+ addDependenciesToSbom(sbom: Sbom, depTree: Object): void;
39
39
  #private;
40
40
  }
41
41
  import Yarn_processor from "./yarn_processor.js";
@@ -25,13 +25,13 @@ export default class Yarn_classic_processor extends Yarn_processor {
25
25
  * @param {Object} depTree - The dependency tree object
26
26
  * @returns {Map<string, PackageURL>} Map of dependency names to their PackageURL objects
27
27
  */
28
- getRootDependencies(depTree: any): Map<string, PackageURL>;
28
+ getRootDependencies(depTree: Object): Map<string, PackageURL>;
29
29
  /**
30
30
  * Adds dependencies to the SBOM
31
31
  * @param {Sbom} sbom - The SBOM object to add dependencies to
32
32
  * @param {Object} depTree - The dependency tree object
33
33
  */
34
- addDependenciesToSbom(sbom: Sbom, depTree: any): void;
34
+ addDependenciesToSbom(sbom: Sbom, depTree: Object): void;
35
35
  #private;
36
36
  }
37
37
  import Yarn_processor from "./yarn_processor.js";
@@ -19,6 +19,53 @@ function getPipShowOutput(depNames) {
19
19
  throw new Error('fail invoking \'pip show\' to fetch metadata for all installed packages in environment', { cause: error });
20
20
  }
21
21
  }
22
+ /**
23
+ * Get pip inspect output from env var override or by invoking pip inspect.
24
+ * Returns null if pip inspect is unavailable (pip < 22.2) or if freeze/show
25
+ * env vars are set without an inspect override (to avoid inconsistent data).
26
+ * @return {string|null} pip inspect JSON output, or null on failure
27
+ */
28
+ function getPipInspectOutput() {
29
+ if (environmentVariableIsPopulated("TRUSTIFY_DA_PIP_INSPECT")) {
30
+ return new Buffer.from(process.env["TRUSTIFY_DA_PIP_INSPECT"], 'base64').toString('ascii');
31
+ }
32
+ if (environmentVariableIsPopulated("TRUSTIFY_DA_PIP_FREEZE") || environmentVariableIsPopulated("TRUSTIFY_DA_PIP_SHOW")) {
33
+ return null;
34
+ }
35
+ try {
36
+ return invokeCommand(this.pathToPipBin, ['inspect']).toString();
37
+ }
38
+ catch (error) {
39
+ console.warn('pip inspect is not available (requires pip 22.2+), SBOM will be generated without hashes');
40
+ return null;
41
+ }
42
+ }
43
+ /**
44
+ * Parse pip inspect JSON output and build a hash lookup map.
45
+ * @param {string|null} inspectOutput - raw pip inspect JSON string
46
+ * @return {Map<string, Array<{alg: string, content: string}>>} map of lowercase package name to hashes
47
+ */
48
+ function parsePipInspectHashes(inspectOutput) {
49
+ if (!inspectOutput) {
50
+ return new Map();
51
+ }
52
+ try {
53
+ let inspectData = JSON.parse(inspectOutput);
54
+ let hashMap = new Map();
55
+ for (let pkg of (inspectData.installed || [])) {
56
+ let name = pkg.metadata?.name;
57
+ let sha256 = pkg.download_info?.archive_info?.hashes?.sha256;
58
+ if (name && sha256) {
59
+ hashMap.set(name.toLowerCase(), [{ alg: "SHA-256", content: sha256 }]);
60
+ }
61
+ }
62
+ return hashMap;
63
+ }
64
+ catch (error) {
65
+ console.warn('Failed to parse pip inspect output, SBOM will be generated without hashes');
66
+ return new Map();
67
+ }
68
+ }
22
69
  /** @typedef {{name: string, version: string, dependencies: DependencyEntry[], hashes?: Array<{alg: string, content: string}>}} DependencyEntry */
23
70
  export default class Python_controller {
24
71
  pythonEnvDir;
@@ -225,6 +272,8 @@ export default class Python_controller {
225
272
  CachedEnvironmentDeps[packageName.replace("_", "-")] = pipDepTreeEntryForCache;
226
273
  });
227
274
  }
275
+ const inspectOutput = getPipInspectOutput.call(this);
276
+ const hashMap = parsePipInspectHashes(inspectOutput);
228
277
  parsedRequirements.forEach(({ name: depName, version: manifestVersion, hasMarker }) => {
229
278
  if (hasMarker && CachedEnvironmentDeps[depName.toLowerCase()] === undefined) {
230
279
  return;
@@ -247,7 +296,7 @@ export default class Python_controller {
247
296
  }
248
297
  let path = [];
249
298
  path.push(depName.toLowerCase());
250
- bringAllDependencies(dependencies, depName, CachedEnvironmentDeps, includeTransitive, path, usePipDepTree);
299
+ bringAllDependencies(dependencies, depName, CachedEnvironmentDeps, includeTransitive, path, usePipDepTree, hashMap);
251
300
  });
252
301
  dependencies.sort((dep1, dep2) => {
253
302
  const DEP1 = dep1.name.toLowerCase();
@@ -327,8 +376,9 @@ function getDepsList(record) {
327
376
  * @param includeTransitive
328
377
  * @param usePipDepTree
329
378
  * @param {[string]}path array representing the path of the current branch in dependency tree, starting with a root dependency - that is - a given dependency in requirements.txt
379
+ * @param {Map<string, Array<{alg: string, content: string}>>} hashMap - map of lowercase package name to hashes
330
380
  */
331
- function bringAllDependencies(dependencies, dependencyName, cachedEnvironmentDeps, includeTransitive, path, usePipDepTree) {
381
+ function bringAllDependencies(dependencies, dependencyName, cachedEnvironmentDeps, includeTransitive, path, usePipDepTree, hashMap) {
332
382
  if (dependencyName?.trim() === "") {
333
383
  return;
334
384
  }
@@ -350,7 +400,11 @@ function bringAllDependencies(dependencies, dependencyName, cachedEnvironmentDep
350
400
  directDeps = record.dependencies;
351
401
  }
352
402
  let targetDeps = [];
403
+ let hashes = hashMap?.get(depName.toLowerCase());
353
404
  let entry = { "name": depName, "version": version, "dependencies": [] };
405
+ if (hashes) {
406
+ entry.hashes = hashes;
407
+ }
354
408
  dependencies.push(entry);
355
409
  directDeps.forEach((dep) => {
356
410
  let depArray = [];
@@ -360,7 +414,7 @@ function bringAllDependencies(dependencies, dependencyName, cachedEnvironmentDep
360
414
  depArray.push(dep.toLowerCase());
361
415
  if (includeTransitive) {
362
416
  // send to recurrsion the array of all deps in path + the current dependency name which is not on the path.
363
- bringAllDependencies(targetDeps, dep, cachedEnvironmentDeps, includeTransitive, path.concat(depArray), usePipDepTree);
417
+ bringAllDependencies(targetDeps, dep, cachedEnvironmentDeps, includeTransitive, path.concat(depArray), usePipDepTree, hashMap);
364
418
  }
365
419
  }
366
420
  // sort ra
@@ -4,6 +4,7 @@ declare namespace _default {
4
4
  export { provideComponent };
5
5
  export { provideStack };
6
6
  export { readLicenseFromManifest };
7
+ export function packageManagerName(): string;
7
8
  }
8
9
  export default _default;
9
10
  export type DependencyEntry = {
@@ -5,7 +5,7 @@ import Sbom from '../sbom.js';
5
5
  import { environmentVariableIsPopulated, getCustom, getCustomPath, invokeCommand } from "../tools.js";
6
6
  import Python_controller from './python_controller.js';
7
7
  import { getParser, getIgnoreQuery, getPinnedVersionQuery } from './requirements_parser.js';
8
- export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest };
8
+ export default { isSupported, validateLockFile, provideComponent, provideStack, readLicenseFromManifest, packageManagerName() { return 'pip'; } };
9
9
  /** @typedef {{name: string, version: string, dependencies: DependencyEntry[], hashes?: Array<{alg: string, content: string}>}} DependencyEntry */
10
10
  /**
11
11
  * @type {string} ecosystem for python-pip is 'pip'
@@ -76,7 +76,9 @@ export default class Python_pip_pyproject extends Base_pyproject {
76
76
  let name = pkg.metadata.name;
77
77
  let version = pkg.metadata.version;
78
78
  let key = this._canonicalize(name);
79
- graph.set(key, { name, version, children: [] });
79
+ let sha256 = pkg.download_info?.archive_info?.hashes?.sha256;
80
+ let hashes = sha256 ? [{ alg: "SHA-256", content: sha256 }] : undefined;
81
+ graph.set(key, { name, version, children: [], hashes });
80
82
  }
81
83
  for (let pkg of nonRootPackages) {
82
84
  let key = this._canonicalize(pkg.metadata.name);
@@ -1,4 +1,5 @@
1
1
  export default class Python_poetry extends Base_pyproject {
2
+ _verifyPoetryAccessible(poetryBin: any): void;
2
3
  /**
3
4
  * @param {string} manifestDir
4
5
  * @param {string} _workspaceDir - unused (poetry has no workspace support)
@@ -6,7 +7,7 @@ export default class Python_poetry extends Base_pyproject {
6
7
  * @param {Object} opts
7
8
  * @returns {Promise<{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[]}>}>}
8
9
  */
9
- _getDependencyData(manifestDir: string, _workspaceDir: string, parsed: object, opts: any): Promise<{
10
+ _getDependencyData(manifestDir: string, _workspaceDir: string, parsed: object, opts: Object): Promise<{
10
11
  directDeps: string[];
11
12
  graph: Map<string, {
12
13
  name: string;
@@ -18,17 +19,17 @@ export default class Python_poetry extends Base_pyproject {
18
19
  * Get poetry show --tree output.
19
20
  * @param {string} manifestDir
20
21
  * @param {boolean} hasDevGroup
21
- * @param {Object} opts
22
+ * @param {string} poetryBin
22
23
  * @returns {string}
23
24
  */
24
- _getPoetryShowTreeOutput(manifestDir: string, hasDevGroup: boolean, opts: any): string;
25
+ _getPoetryShowTreeOutput(manifestDir: string, hasDevGroup: boolean, poetryBin: string): string;
25
26
  /**
26
27
  * Get poetry show --all output (flat list with resolved versions).
27
28
  * @param {string} manifestDir
28
- * @param {Object} opts
29
+ * @param {string} poetryBin
29
30
  * @returns {string}
30
31
  */
31
- _getPoetryShowAllOutput(manifestDir: string, opts: any): string;
32
+ _getPoetryShowAllOutput(manifestDir: string, poetryBin: string): string;
32
33
  /**
33
34
  * Parse poetry show --all output into a version map.
34
35
  * Lines look like: "name (!) 1.2.3 Description text..."
@@ -46,29 +47,51 @@ export default class Python_poetry extends Base_pyproject {
46
47
  * → transitiveMarkers['click']['colorama'] = "sys_platform == 'win32'"
47
48
  * @param {string|null} lockDir
48
49
  * @param {object} parsed - parsed pyproject.toml
49
- * @returns {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>}}
50
+ * @returns {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>, hashMap: Map<string, Array<{alg: string, content: string}>>}}
50
51
  */
51
52
  _extractMarkerData(lockDir: string | null, parsed: object): {
52
53
  directMarkers: Map<string, string>;
53
54
  transitiveMarkers: Map<string, Map<string, string>>;
55
+ hashMap: Map<string, Array<{
56
+ alg: string;
57
+ content: string;
58
+ }>>;
54
59
  };
60
+ /**
61
+ * Extract a SHA-256 hex digest from poetry.lock files array.
62
+ * Prefers the sdist (.tar.gz) hash; falls back to the first wheel hash.
63
+ * @param {Array<{file: string, hash: string}>} [files]
64
+ * @returns {string|null}
65
+ */
66
+ _extractSha256FromFiles(files?: Array<{
67
+ file: string;
68
+ hash: string;
69
+ }>): string | null;
55
70
  /**
56
71
  * Parse poetry show --tree output into a dependency graph structure.
57
72
  *
58
73
  * @param {string} treeOutput
59
74
  * @param {Map<string, string>} versionMap - canonical name -> resolved version
60
- * @param {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>}} markerData
61
- * @returns {{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[]}>}}
75
+ * @param {{directMarkers: Map<string, string>, transitiveMarkers: Map<string, Map<string, string>>, hashMap: Map<string, Array<{alg: string, content: string}>>}} markerData
76
+ * @returns {{directDeps: string[], graph: Map<string, {name: string, version: string, children: string[], hashes?: Array<{alg: string, content: string}>}>}}
62
77
  */
63
78
  _parsePoetryTree(treeOutput: string, versionMap: Map<string, string>, markerData: {
64
79
  directMarkers: Map<string, string>;
65
80
  transitiveMarkers: Map<string, Map<string, string>>;
81
+ hashMap: Map<string, Array<{
82
+ alg: string;
83
+ content: string;
84
+ }>>;
66
85
  }): {
67
86
  directDeps: string[];
68
87
  graph: Map<string, {
69
88
  name: string;
70
89
  version: string;
71
90
  children: string[];
91
+ hashes?: Array<{
92
+ alg: string;
93
+ content: string;
94
+ }>;
72
95
  }>;
73
96
  };
74
97
  }