@trustify-da/trustify-da-javascript-client 0.2.4-ea-1

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 (57) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +482 -0
  3. package/config/config.properties +1 -0
  4. package/dist/package.json +110 -0
  5. package/dist/src/analysis.d.ts +43 -0
  6. package/dist/src/analysis.js +252 -0
  7. package/dist/src/cli.d.ts +2 -0
  8. package/dist/src/cli.js +102 -0
  9. package/dist/src/cyclone_dx_sbom.d.ts +77 -0
  10. package/dist/src/cyclone_dx_sbom.js +244 -0
  11. package/dist/src/index.d.ts +82 -0
  12. package/dist/src/index.js +194 -0
  13. package/dist/src/oci_image/images.d.ts +99 -0
  14. package/dist/src/oci_image/images.js +263 -0
  15. package/dist/src/oci_image/platform.d.ts +59 -0
  16. package/dist/src/oci_image/platform.js +138 -0
  17. package/dist/src/oci_image/utils.d.ts +42 -0
  18. package/dist/src/oci_image/utils.js +496 -0
  19. package/dist/src/provider.d.ts +29 -0
  20. package/dist/src/provider.js +47 -0
  21. package/dist/src/providers/base_java.d.ts +85 -0
  22. package/dist/src/providers/base_java.js +191 -0
  23. package/dist/src/providers/base_javascript.d.ts +127 -0
  24. package/dist/src/providers/base_javascript.js +350 -0
  25. package/dist/src/providers/golang_gomodules.d.ts +42 -0
  26. package/dist/src/providers/golang_gomodules.js +403 -0
  27. package/dist/src/providers/java_gradle.d.ts +35 -0
  28. package/dist/src/providers/java_gradle.js +399 -0
  29. package/dist/src/providers/java_gradle_groovy.d.ts +7 -0
  30. package/dist/src/providers/java_gradle_groovy.js +19 -0
  31. package/dist/src/providers/java_gradle_kotlin.d.ts +11 -0
  32. package/dist/src/providers/java_gradle_kotlin.js +23 -0
  33. package/dist/src/providers/java_maven.d.ts +52 -0
  34. package/dist/src/providers/java_maven.js +263 -0
  35. package/dist/src/providers/javascript_npm.d.ts +4 -0
  36. package/dist/src/providers/javascript_npm.js +15 -0
  37. package/dist/src/providers/javascript_pnpm.d.ts +5 -0
  38. package/dist/src/providers/javascript_pnpm.js +22 -0
  39. package/dist/src/providers/javascript_yarn.d.ts +11 -0
  40. package/dist/src/providers/javascript_yarn.js +39 -0
  41. package/dist/src/providers/manifest.d.ts +11 -0
  42. package/dist/src/providers/manifest.js +48 -0
  43. package/dist/src/providers/processors/yarn_berry_processor.d.ts +41 -0
  44. package/dist/src/providers/processors/yarn_berry_processor.js +130 -0
  45. package/dist/src/providers/processors/yarn_classic_processor.d.ts +37 -0
  46. package/dist/src/providers/processors/yarn_classic_processor.js +109 -0
  47. package/dist/src/providers/processors/yarn_processor.d.ts +9 -0
  48. package/dist/src/providers/processors/yarn_processor.js +20 -0
  49. package/dist/src/providers/python_controller.d.ts +31 -0
  50. package/dist/src/providers/python_controller.js +406 -0
  51. package/dist/src/providers/python_pip.d.ts +35 -0
  52. package/dist/src/providers/python_pip.js +227 -0
  53. package/dist/src/sbom.d.ts +59 -0
  54. package/dist/src/sbom.js +84 -0
  55. package/dist/src/tools.d.ts +74 -0
  56. package/dist/src/tools.js +159 -0
  57. package/package.json +110 -0
@@ -0,0 +1,109 @@
1
+ import { toPurl, toPurlFromString } from "../../tools.js";
2
+ import { purlType } from "../base_javascript.js";
3
+ import Yarn_processor from "./yarn_processor.js";
4
+ /**
5
+ * Processor for Yarn Classic package manager
6
+ * Handles parsing and processing of dependencies for Yarn Classic projects
7
+ */
8
+ export default class Yarn_classic_processor extends Yarn_processor {
9
+ /**
10
+ * Returns the command arguments for listing dependencies
11
+ * @param {boolean} includeTransitive - Whether to include transitive dependencies
12
+ * @returns {string[]} Command arguments for listing dependencies
13
+ */
14
+ listCmdArgs(includeTransitive) {
15
+ return ['list', includeTransitive ? '--depth=Infinity' : '--depth=0', '--prod', '--frozen-lockfile', '--json'];
16
+ }
17
+ /**
18
+ * Returns the command arguments for updating the lock file
19
+ * @returns {string[]} Command arguments for updating the lock file
20
+ */
21
+ updateLockFileCmdArgs() {
22
+ return ['install', '--frozen-lockfile'];
23
+ }
24
+ /**
25
+ * Parses the dependency tree output from Yarn Classic
26
+ * @param {string} output - The raw command output
27
+ * @returns {string} Unchanged output as it's already in JSON format
28
+ */
29
+ parseDepTreeOutput(output) {
30
+ return output;
31
+ }
32
+ /**
33
+ * Extracts root dependencies from the dependency tree
34
+ * @param {Object} depTree - The dependency tree object
35
+ * @returns {Map<string, PackageURL>} Map of dependency names to their PackageURL objects
36
+ */
37
+ getRootDependencies(depTree) {
38
+ if (!depTree?.data?.trees) {
39
+ return new Map();
40
+ }
41
+ return new Map(depTree.data.trees.map(dep => {
42
+ const depName = dep.name;
43
+ const idx = depName.lastIndexOf('@');
44
+ const name = depName.substring(0, idx);
45
+ const version = idx !== -1 ? depName.substring(idx + 1) : '';
46
+ return [name, toPurl(purlType, name, version)];
47
+ }));
48
+ }
49
+ /**
50
+ * Adds dependencies to the SBOM
51
+ * @param {Sbom} sbom - The SBOM object to add dependencies to
52
+ * @param {Object} depTree - The dependency tree object
53
+ */
54
+ addDependenciesToSbom(sbom, depTree) {
55
+ if (!depTree?.data?.trees) {
56
+ return;
57
+ }
58
+ const rootPurl = toPurlFromString(sbom.getRoot().purl);
59
+ const purls = new Map();
60
+ depTree.data.trees.forEach(n => {
61
+ const dep = new NodeMetaData(n);
62
+ if (this._manifest.dependencies.includes(dep.name)) {
63
+ sbom.addDependency(rootPurl, dep.purl);
64
+ }
65
+ purls.set(dep.name, dep.purl);
66
+ });
67
+ depTree.data.trees.forEach(n => {
68
+ this.#addChildrenToSbom(sbom, n, purls);
69
+ });
70
+ }
71
+ /**
72
+ * Recursively adds child dependencies to the SBOM
73
+ * @param {Sbom} sbom - The SBOM object to add dependencies to
74
+ * @param {Object} node - The current dependency node
75
+ * @param {Map<string, PackageURL>} purls - Map of dependency names to their PackageURL objects
76
+ * @private
77
+ */
78
+ #addChildrenToSbom(sbom, node, purls) {
79
+ const dep = new NodeMetaData(node);
80
+ const children = node.children ? node.children : [];
81
+ children.forEach(c => {
82
+ const child = new NodeMetaData(c);
83
+ const from = dep.shadow ? purls.get(dep.name) : dep.purl;
84
+ const to = child.shadow ? purls.get(child.name) : child.purl;
85
+ if (from && to) {
86
+ sbom.addDependency(from, to);
87
+ }
88
+ this.#addChildrenToSbom(sbom, c, purls);
89
+ });
90
+ }
91
+ }
92
+ /**
93
+ * Helper class to extract and store metadata from a dependency node
94
+ */
95
+ class NodeMetaData {
96
+ /**
97
+ * Creates a new NodeMetaData instance
98
+ * @param {Object} node - The dependency node
99
+ */
100
+ constructor(node) {
101
+ this.nodeName = node.name;
102
+ const idx = this.nodeName.lastIndexOf('@');
103
+ this.name = this.nodeName.substring(0, idx);
104
+ this.version = idx !== -1 ? this.nodeName.substring(idx + 1) : '';
105
+ this.purl = toPurl(purlType, this.name, this.version);
106
+ const shadowNode = node.shadow;
107
+ this.shadow = shadowNode ? shadowNode : false;
108
+ }
109
+ }
@@ -0,0 +1,9 @@
1
+ export default class Yarn_processor {
2
+ constructor(manifest: any);
3
+ _manifest: any;
4
+ installCmd(): void;
5
+ listDepsCmd(): void;
6
+ getRootDependencies(): void;
7
+ addDependenciesToSbom(): void;
8
+ parseDepTreeOutput(): void;
9
+ }
@@ -0,0 +1,20 @@
1
+ export default class Yarn_processor {
2
+ constructor(manifest) {
3
+ this._manifest = manifest;
4
+ }
5
+ installCmd() {
6
+ throw new Error('Method "installCmd" must be implemented.');
7
+ }
8
+ listDepsCmd() {
9
+ throw new Error('Method "listDepsCmd" must be implemented.');
10
+ }
11
+ getRootDependencies() {
12
+ throw new Error('Method "getRootDependencies" must be implemented.');
13
+ }
14
+ addDependenciesToSbom() {
15
+ throw new Error('Method "addDependenciesToSbom" must be implemented.');
16
+ }
17
+ parseDepTreeOutput() {
18
+ throw new Error('Method "parseDepTreeOutput" must be implemented.');
19
+ }
20
+ }
@@ -0,0 +1,31 @@
1
+ /** @typedef {{name: string, version: string, dependencies: DependencyEntry[]}} DependencyEntry */
2
+ export default class Python_controller {
3
+ /**
4
+ * Constructor to create new python controller instance to interact with pip package manager
5
+ * @param {boolean} realEnvironment - whether to use real environment supplied by client or to create virtual environment
6
+ * @param {string} pathToPip - path to pip package manager
7
+ * @param {string} pathToPython - path to python binary
8
+ * @param {string} pathToRequirements
9
+ * @
10
+ */
11
+ constructor(realEnvironment: boolean, pathToPip: string, pathToPython: string, pathToRequirements: string, options?: {});
12
+ pythonEnvDir: any;
13
+ pathToPipBin: string;
14
+ pathToPythonBin: string;
15
+ realEnvironment: boolean;
16
+ pathToRequirements: string;
17
+ options: {};
18
+ prepareEnvironment(): void;
19
+ /**
20
+ *
21
+ * @param {boolean} includeTransitive - whether to return include in returned object transitive dependencies or not
22
+ * @return {[DependencyEntry]}
23
+ */
24
+ getDependencies(includeTransitive: boolean): [DependencyEntry];
25
+ #private;
26
+ }
27
+ export type DependencyEntry = {
28
+ name: string;
29
+ version: string;
30
+ dependencies: DependencyEntry[];
31
+ };
@@ -0,0 +1,406 @@
1
+ import fs from "node:fs";
2
+ import path from 'node:path';
3
+ import os, { EOL } from "os";
4
+ import { environmentVariableIsPopulated, getCustom, invokeCommand } from "../tools.js";
5
+ function getPipFreezeOutput() {
6
+ try {
7
+ return environmentVariableIsPopulated("TRUSTIFY_DA_PIP_FREEZE") ? new Buffer.from(process.env["TRUSTIFY_DA_PIP_FREEZE"], 'base64').toString('ascii') : invokeCommand(this.pathToPipBin, ['freeze', '--all']).toString();
8
+ }
9
+ catch (error) {
10
+ throw new Error('Failed invoking \'pip freeze\' to list all installed packages in environment', { cause: error });
11
+ }
12
+ }
13
+ function getPipShowOutput(depNames) {
14
+ try {
15
+ return environmentVariableIsPopulated("TRUSTIFY_DA_PIP_SHOW") ? new Buffer.from(process.env["TRUSTIFY_DA_PIP_SHOW"], 'base64').toString('ascii') : invokeCommand(this.pathToPipBin, ['show', ...depNames]).toString();
16
+ }
17
+ catch (error) {
18
+ throw new Error('fail invoking \'pip show\' to fetch metadata for all installed packages in environment', { cause: error });
19
+ }
20
+ }
21
+ /** @typedef {{name: string, version: string, dependencies: DependencyEntry[]}} DependencyEntry */
22
+ export default class Python_controller {
23
+ pythonEnvDir;
24
+ pathToPipBin;
25
+ pathToPythonBin;
26
+ realEnvironment;
27
+ pathToRequirements;
28
+ options;
29
+ /**
30
+ * Constructor to create new python controller instance to interact with pip package manager
31
+ * @param {boolean} realEnvironment - whether to use real environment supplied by client or to create virtual environment
32
+ * @param {string} pathToPip - path to pip package manager
33
+ * @param {string} pathToPython - path to python binary
34
+ * @param {string} pathToRequirements
35
+ * @
36
+ */
37
+ constructor(realEnvironment, pathToPip, pathToPython, pathToRequirements, options = {}) {
38
+ this.pathToPythonBin = pathToPython;
39
+ this.pathToPipBin = pathToPip;
40
+ this.realEnvironment = realEnvironment;
41
+ this.prepareEnvironment();
42
+ this.pathToRequirements = pathToRequirements;
43
+ this.options = options;
44
+ }
45
+ prepareEnvironment() {
46
+ if (!this.realEnvironment) {
47
+ this.pythonEnvDir = path.join(path.sep, "tmp", "exhort_env_js");
48
+ try {
49
+ invokeCommand(this.pathToPythonBin, ['-m', 'venv', this.pythonEnvDir]);
50
+ }
51
+ catch (error) {
52
+ throw new Error('Failed creating virtual python environment', { cause: error });
53
+ }
54
+ if (this.pathToPythonBin.includes("python3")) {
55
+ this.pathToPipBin = path.join(path.sep, this.pythonEnvDir, os.platform() === 'win32' ? "Scripts" : "bin", this.#decideIfWindowsOrLinuxPath("pip3"));
56
+ this.pathToPythonBin = path.join(path.sep, this.pythonEnvDir, os.platform() === 'win32' ? "Scripts" : "bin", this.#decideIfWindowsOrLinuxPath("python3"));
57
+ if (os.platform() === 'win32') {
58
+ let driveLetter = path.parse(process.cwd()).root;
59
+ this.pathToPythonBin = `${driveLetter}${this.pathToPythonBin.substring(1)}`;
60
+ this.pathToPipBin = `${driveLetter}${this.pathToPipBin.substring(1)}`;
61
+ }
62
+ }
63
+ else {
64
+ this.pathToPipBin = path.join(path.sep, this.pythonEnvDir, os.platform() === 'win32' ? "Scripts" : "bin", this.#decideIfWindowsOrLinuxPath("pip"));
65
+ this.pathToPythonBin = path.join(path.sep, this.pythonEnvDir, os.platform() === 'win32' ? "Scripts" : "bin", this.#decideIfWindowsOrLinuxPath("python"));
66
+ if (os.platform() === 'win32') {
67
+ let driveLetter = path.parse(process.cwd()).root;
68
+ this.pathToPythonBin = `${driveLetter}${this.pathToPythonBin.substring(1)}`;
69
+ this.pathToPipBin = `${driveLetter}${this.pathToPipBin.substring(1)}`;
70
+ }
71
+ }
72
+ // upgrade pip version to latest
73
+ try {
74
+ invokeCommand(this.pathToPythonBin, ['-m', 'pip', 'install', '--upgrade', 'pip']);
75
+ }
76
+ catch (error) {
77
+ throw new Error('Failed upgrading pip version in virtual python environment', { cause: error });
78
+ }
79
+ }
80
+ else {
81
+ if (this.pathToPythonBin.startsWith("python")) {
82
+ this.pythonEnvDir = process.cwd();
83
+ }
84
+ else {
85
+ this.pythonEnvDir = path.dirname(this.pathToPythonBin);
86
+ }
87
+ }
88
+ }
89
+ #decideIfWindowsOrLinuxPath(fileName) {
90
+ if (os.platform() === "win32") {
91
+ return fileName + ".exe";
92
+ }
93
+ else {
94
+ return fileName;
95
+ }
96
+ }
97
+ /**
98
+ *
99
+ * @param {boolean} includeTransitive - whether to return include in returned object transitive dependencies or not
100
+ * @return {[DependencyEntry]}
101
+ */
102
+ getDependencies(includeTransitive) {
103
+ let startingTime;
104
+ let endingTime;
105
+ if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
106
+ startingTime = new Date();
107
+ console.log("Starting time to get requirements.txt dependency tree = " + startingTime);
108
+ }
109
+ if (!this.realEnvironment) {
110
+ let installBestEfforts = getCustom("TRUSTIFY_DA_PYTHON_INSTALL_BEST_EFFORTS", "false", this.options);
111
+ if (installBestEfforts === "false") {
112
+ try {
113
+ invokeCommand(this.pathToPipBin, ['install', '-r', this.pathToRequirements]);
114
+ }
115
+ catch (error) {
116
+ throw new Error('Failed installing requirements.txt manifest in virtual python environment', { cause: error });
117
+ }
118
+ }
119
+ // make best efforts to install the requirements.txt on the virtual environment created from the python3 passed in.
120
+ // that means that it will install the packages without referring to the versions, but will let pip choose the version
121
+ // tailored for version of the python environment( and of pip package manager) for each package.
122
+ else {
123
+ let matchManifestVersions = getCustom("MATCH_MANIFEST_VERSIONS", "true", this.options);
124
+ if (matchManifestVersions === "true") {
125
+ throw new Error("Conflicting settings, TRUSTIFY_DA_PYTHON_INSTALL_BEST_EFFORTS=true can only work with MATCH_MANIFEST_VERSIONS=false");
126
+ }
127
+ this.#installingRequirementsOneByOne();
128
+ }
129
+ }
130
+ let dependencies = this.#getDependenciesImpl(includeTransitive);
131
+ this.#cleanEnvironment();
132
+ if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
133
+ endingTime = new Date();
134
+ console.log("Ending time to get requirements.txt dependency tree = " + endingTime);
135
+ let time = (endingTime - startingTime) / 1000;
136
+ console.log("total time to get requirements.txt dependency tree = " + time);
137
+ }
138
+ return dependencies;
139
+ }
140
+ #installingRequirementsOneByOne() {
141
+ let requirementsContent = fs.readFileSync(this.pathToRequirements);
142
+ let requirementsRows = requirementsContent.toString().split(EOL);
143
+ requirementsRows.filter((line) => !line.trim().startsWith("#")).filter((line) => line.trim() !== "").forEach((dependency) => {
144
+ let dependencyName = getDependencyName(dependency);
145
+ try {
146
+ invokeCommand(this.pathToPipBin, ['install', dependencyName]);
147
+ }
148
+ catch (error) {
149
+ throw new Error(`Failed in best-effort installing ${dependencyName} in virtual python environment`, { cause: error });
150
+ }
151
+ });
152
+ }
153
+ /**
154
+ * @private
155
+ */
156
+ #cleanEnvironment() {
157
+ if (!this.realEnvironment) {
158
+ try {
159
+ invokeCommand(this.pathToPipBin, ['uninstall', '-y', '-r', this.pathToRequirements]);
160
+ }
161
+ catch (error) {
162
+ throw new Error('Failed uninstalling requirements.txt in virtual python environment', { cause: error });
163
+ }
164
+ }
165
+ }
166
+ #getDependenciesImpl(includeTransitive) {
167
+ let dependencies = new Array();
168
+ let usePipDepTree = getCustom("TRUSTIFY_DA_PIP_USE_DEP_TREE", "false", this.options);
169
+ let freezeOutput;
170
+ let lines;
171
+ let depNames;
172
+ let pipShowOutput;
173
+ let allPipShowDeps;
174
+ let pipDepTreeJsonArrayOutput;
175
+ if (usePipDepTree !== "true") {
176
+ freezeOutput = getPipFreezeOutput.call(this);
177
+ lines = freezeOutput.split(EOL);
178
+ depNames = lines.map(line => getDependencyName(line));
179
+ }
180
+ else {
181
+ pipDepTreeJsonArrayOutput = getDependencyTreeJsonFromPipDepTree(this.pathToPipBin, this.pathToPythonBin);
182
+ }
183
+ if (usePipDepTree !== "true") {
184
+ pipShowOutput = getPipShowOutput.call(this, depNames);
185
+ allPipShowDeps = pipShowOutput.split(EOL + "---" + EOL);
186
+ }
187
+ //debug
188
+ // pipShowOutput = "alternative pip show output goes here for debugging"
189
+ let matchManifestVersions = getCustom("MATCH_MANIFEST_VERSIONS", "true", this.options);
190
+ let linesOfRequirements = fs.readFileSync(this.pathToRequirements).toString().split(EOL).filter((line) => !line.trim().startsWith("#")).map(line => line.trim());
191
+ let CachedEnvironmentDeps = {};
192
+ if (usePipDepTree !== "true") {
193
+ allPipShowDeps.forEach((record) => {
194
+ let dependencyName = getDependencyNameShow(record).toLowerCase();
195
+ CachedEnvironmentDeps[dependencyName] = record;
196
+ CachedEnvironmentDeps[dependencyName.replace("-", "_")] = record;
197
+ CachedEnvironmentDeps[dependencyName.replace("_", "-")] = record;
198
+ });
199
+ }
200
+ else {
201
+ pipDepTreeJsonArrayOutput.forEach(depTreeEntry => {
202
+ let packageName = depTreeEntry["package"]["package_name"].toLowerCase();
203
+ let pipDepTreeEntryForCache = {
204
+ name: packageName,
205
+ version: depTreeEntry["package"]["installed_version"],
206
+ dependencies: depTreeEntry["dependencies"].map(dep => dep["package_name"])
207
+ };
208
+ CachedEnvironmentDeps[packageName] = pipDepTreeEntryForCache;
209
+ CachedEnvironmentDeps[packageName.replace("-", "_")] = pipDepTreeEntryForCache;
210
+ CachedEnvironmentDeps[packageName.replace("_", "-")] = pipDepTreeEntryForCache;
211
+ });
212
+ }
213
+ linesOfRequirements.forEach((dep) => {
214
+ // if matchManifestVersions setting is turned on , then
215
+ if (matchManifestVersions === "true") {
216
+ let dependencyName;
217
+ let manifestVersion;
218
+ let installedVersion;
219
+ let doubleEqualSignPosition;
220
+ if (dep.includes("==")) {
221
+ doubleEqualSignPosition = dep.indexOf("==");
222
+ manifestVersion = dep.substring(doubleEqualSignPosition + 2).trim();
223
+ if (manifestVersion.includes("#")) {
224
+ let hashCharIndex = manifestVersion.indexOf("#");
225
+ manifestVersion = manifestVersion.substring(0, hashCharIndex);
226
+ }
227
+ dependencyName = getDependencyName(dep);
228
+ // only compare between declared version in manifest to installed version , if the package is installed.
229
+ if (CachedEnvironmentDeps[dependencyName.toLowerCase()] !== undefined) {
230
+ if (usePipDepTree !== "true") {
231
+ installedVersion = getDependencyVersion(CachedEnvironmentDeps[dependencyName.toLowerCase()]);
232
+ }
233
+ else {
234
+ installedVersion = CachedEnvironmentDeps[dependencyName.toLowerCase()].version;
235
+ }
236
+ }
237
+ if (installedVersion) {
238
+ if (manifestVersion.trim() !== installedVersion.trim()) {
239
+ throw new Error(`Can't continue with analysis - versions mismatch for dependency name ${dependencyName} (manifest version=${manifestVersion}, installed version=${installedVersion}).If you want to allow version mismatch for analysis between installed and requested packages, set environment variable/setting MATCH_MANIFEST_VERSIONS=false`);
240
+ }
241
+ }
242
+ }
243
+ }
244
+ let path = new Array();
245
+ let depName = getDependencyName(dep);
246
+ //array to track a path for each branch in the dependency tree
247
+ path.push(depName.toLowerCase());
248
+ bringAllDependencies(dependencies, depName, CachedEnvironmentDeps, includeTransitive, path, usePipDepTree);
249
+ });
250
+ dependencies.sort((dep1, dep2) => {
251
+ const DEP1 = dep1.name.toLowerCase();
252
+ const DEP2 = dep2.name.toLowerCase();
253
+ if (DEP1 < DEP2) {
254
+ return -1;
255
+ }
256
+ if (DEP1 > DEP2) {
257
+ return 1;
258
+ }
259
+ return 0;
260
+ });
261
+ return dependencies;
262
+ }
263
+ }
264
+ /**
265
+ *
266
+ * @param {string} record - a record block from pip show
267
+ * @return {string} the name of the dependency of the pip show record.
268
+ */
269
+ function getDependencyNameShow(record) {
270
+ let versionKeyIndex = record.indexOf("Name:");
271
+ let versionToken = record.substring(versionKeyIndex + 5);
272
+ let endOfLine = versionToken.indexOf(EOL);
273
+ return versionToken.substring(0, endOfLine).trim();
274
+ }
275
+ /**
276
+ *
277
+ * @param {string} record - a record block from pip show
278
+ * @return {string} the name of the dependency of the pip show record.
279
+ */
280
+ function getDependencyVersion(record) {
281
+ let versionKeyIndex = record.indexOf("Version:");
282
+ let versionToken = record.substring(versionKeyIndex + 8);
283
+ let endOfLine = versionToken.indexOf(EOL);
284
+ return versionToken.substring(0, endOfLine).trim();
285
+ }
286
+ /**
287
+ *
288
+ * @param depLine the dependency with version/ version requirement as shown in requirements.txt
289
+ * @return {string} the name of dependency
290
+ */
291
+ function getDependencyName(depLine) {
292
+ const regex = /[^\w\s-_.]/g;
293
+ let endIndex = depLine.search(regex);
294
+ let result = depLine.substring(0, endIndex);
295
+ // In case package in requirements text only contain package name without version
296
+ if (result.trim() === "") {
297
+ const regex = /[\w\s-_.]+/g;
298
+ if (depLine.match(regex)) {
299
+ result = depLine.match(regex)[0];
300
+ }
301
+ else {
302
+ result = depLine;
303
+ }
304
+ }
305
+ return result.trim();
306
+ }
307
+ /**
308
+ *
309
+ * @param record - a dependency record block from pip show
310
+ * @return {[string]} array of all direct deps names of that dependency
311
+ */
312
+ function getDepsList(record) {
313
+ let requiresKeyIndex = record.indexOf("Requires:");
314
+ let requiresToken = record.substring(requiresKeyIndex + 9);
315
+ let endOfLine = requiresToken.indexOf(EOL);
316
+ let listOfDepsString = requiresToken.substring(0, endOfLine);
317
+ let list = listOfDepsString.split(",").filter(line => line.trim() !== "").map(line => line.trim());
318
+ return list;
319
+ }
320
+ /**
321
+ *
322
+ * @param {[DependencyEntry]} dependencies
323
+ * @param dependencyName
324
+ * @param cachedEnvironmentDeps
325
+ * @param includeTransitive
326
+ * @param usePipDepTree
327
+ * @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
328
+ */
329
+ function bringAllDependencies(dependencies, dependencyName, cachedEnvironmentDeps, includeTransitive, path, usePipDepTree) {
330
+ if (dependencyName?.trim() === "") {
331
+ return;
332
+ }
333
+ let record = cachedEnvironmentDeps[dependencyName.toLowerCase()];
334
+ if (record == null) {
335
+ throw new Error(`Package ${dependencyName} is not installed in your python environment, either install it (better to install requirements.txt altogether) or set the setting TRUSTIFY_DA_PYTHON_VIRTUAL_ENV=true to automatically install it in virtual environment (please note that this may slow down the analysis)`);
336
+ }
337
+ let depName;
338
+ let version;
339
+ let directDeps;
340
+ if (usePipDepTree !== "true") {
341
+ depName = getDependencyNameShow(record);
342
+ version = getDependencyVersion(record);
343
+ directDeps = getDepsList(record);
344
+ }
345
+ else {
346
+ depName = record.name;
347
+ version = record.version;
348
+ directDeps = record.dependencies;
349
+ }
350
+ let targetDeps = new Array();
351
+ let entry = { "name": depName, "version": version, "dependencies": [] };
352
+ dependencies.push(entry);
353
+ directDeps.forEach((dep) => {
354
+ let depArray = new Array();
355
+ // to avoid infinite loop, check if the dependency not already on current path, before going recursively resolving its dependencies.
356
+ if (!path.includes(dep.toLowerCase())) {
357
+ // send to recurrsion the path + the current dep
358
+ depArray.push(dep.toLowerCase());
359
+ if (includeTransitive) {
360
+ // send to recurrsion the array of all deps in path + the current dependency name which is not on the path.
361
+ bringAllDependencies(targetDeps, dep, cachedEnvironmentDeps, includeTransitive, path.concat(depArray), usePipDepTree);
362
+ }
363
+ }
364
+ // sort ra
365
+ targetDeps.sort((dep1, dep2) => {
366
+ const DEP1 = dep1.name.toLowerCase();
367
+ const DEP2 = dep2.name.toLowerCase();
368
+ if (DEP1 < DEP2) {
369
+ return -1;
370
+ }
371
+ if (DEP1 > DEP2) {
372
+ return 1;
373
+ }
374
+ return 0;
375
+ });
376
+ entry["dependencies"] = targetDeps;
377
+ });
378
+ }
379
+ /**
380
+ * This function install tiny pipdeptree tool using pip ( if it's not already installed on python environment), and use it to fetch the dependency tree in json format.
381
+ * @param {string }pipPath - the filesystem path location of pip binary
382
+ * @param {string }pythonPath - the filesystem path location of python binary
383
+ * @return {Object[] } json array containing objects with the packages and their dependencies from pipdeptree utility
384
+ * @private
385
+ */
386
+ function getDependencyTreeJsonFromPipDepTree(pipPath, pythonPath) {
387
+ let dependencyTree;
388
+ try {
389
+ invokeCommand(pipPath, ['install', 'pipdeptree']);
390
+ }
391
+ catch (error) {
392
+ throw new Error(`Failed installing pipdeptree utility`, { cause: error });
393
+ }
394
+ try {
395
+ if (pythonPath.startsWith("python")) {
396
+ dependencyTree = invokeCommand('pipdeptree', ['--json']).toString();
397
+ }
398
+ else {
399
+ dependencyTree = invokeCommand('pipdeptree', ['--json', '--python', pythonPath]).toString();
400
+ }
401
+ }
402
+ catch (error) {
403
+ throw new Error(`Failed building dependency tree using pipdeptree tool, stopping analysis`, { cause: error });
404
+ }
405
+ return JSON.parse(dependencyTree);
406
+ }
@@ -0,0 +1,35 @@
1
+ declare namespace _default {
2
+ export { isSupported };
3
+ export { validateLockFile };
4
+ export { provideComponent };
5
+ export { provideStack };
6
+ }
7
+ export default _default;
8
+ export type DependencyEntry = {
9
+ name: string;
10
+ version: string;
11
+ dependencies: DependencyEntry[];
12
+ };
13
+ /**
14
+ * @param {string} manifestName - the subject manifest name-type
15
+ * @returns {boolean} - return true if `requirements.txt` is the manifest name-type
16
+ */
17
+ declare function isSupported(manifestName: string): boolean;
18
+ /**
19
+ * @param {string} manifestDir - the directory where the manifest lies
20
+ */
21
+ declare function validateLockFile(): boolean;
22
+ /**
23
+ * Provide content and content type for python-pip component analysis.
24
+ * @param {string} manifest - path to requirements.txt for component report
25
+ * @param {{}} [opts={}] - optional various options to pass along the application
26
+ * @returns {Provided}
27
+ */
28
+ declare function provideComponent(manifest: string, opts?: {} | undefined): Provided;
29
+ /**
30
+ * Provide content and content type for python-pip 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}
34
+ */
35
+ declare function provideStack(manifest: string, opts?: {} | undefined): Provided;