@trustify-da/trustify-da-javascript-client 0.2.4-ea.13
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.
- package/LICENSE +201 -0
- package/README.md +482 -0
- package/config/config.properties +1 -0
- package/dist/package.json +106 -0
- package/dist/src/analysis.d.ts +43 -0
- package/dist/src/analysis.js +252 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +102 -0
- package/dist/src/cyclone_dx_sbom.d.ts +77 -0
- package/dist/src/cyclone_dx_sbom.js +244 -0
- package/dist/src/index.d.ts +82 -0
- package/dist/src/index.js +194 -0
- package/dist/src/oci_image/images.d.ts +99 -0
- package/dist/src/oci_image/images.js +263 -0
- package/dist/src/oci_image/platform.d.ts +59 -0
- package/dist/src/oci_image/platform.js +138 -0
- package/dist/src/oci_image/utils.d.ts +42 -0
- package/dist/src/oci_image/utils.js +496 -0
- package/dist/src/provider.d.ts +29 -0
- package/dist/src/provider.js +47 -0
- package/dist/src/providers/base_java.d.ts +85 -0
- package/dist/src/providers/base_java.js +191 -0
- package/dist/src/providers/base_javascript.d.ts +127 -0
- package/dist/src/providers/base_javascript.js +350 -0
- package/dist/src/providers/golang_gomodules.d.ts +42 -0
- package/dist/src/providers/golang_gomodules.js +403 -0
- package/dist/src/providers/java_gradle.d.ts +35 -0
- package/dist/src/providers/java_gradle.js +399 -0
- package/dist/src/providers/java_gradle_groovy.d.ts +7 -0
- package/dist/src/providers/java_gradle_groovy.js +19 -0
- package/dist/src/providers/java_gradle_kotlin.d.ts +11 -0
- package/dist/src/providers/java_gradle_kotlin.js +23 -0
- package/dist/src/providers/java_maven.d.ts +52 -0
- package/dist/src/providers/java_maven.js +263 -0
- package/dist/src/providers/javascript_npm.d.ts +4 -0
- package/dist/src/providers/javascript_npm.js +15 -0
- package/dist/src/providers/javascript_pnpm.d.ts +5 -0
- package/dist/src/providers/javascript_pnpm.js +22 -0
- package/dist/src/providers/javascript_yarn.d.ts +11 -0
- package/dist/src/providers/javascript_yarn.js +39 -0
- package/dist/src/providers/manifest.d.ts +11 -0
- package/dist/src/providers/manifest.js +48 -0
- package/dist/src/providers/processors/yarn_berry_processor.d.ts +41 -0
- package/dist/src/providers/processors/yarn_berry_processor.js +130 -0
- package/dist/src/providers/processors/yarn_classic_processor.d.ts +37 -0
- package/dist/src/providers/processors/yarn_classic_processor.js +109 -0
- package/dist/src/providers/processors/yarn_processor.d.ts +9 -0
- package/dist/src/providers/processors/yarn_processor.js +20 -0
- package/dist/src/providers/python_controller.d.ts +31 -0
- package/dist/src/providers/python_controller.js +406 -0
- package/dist/src/providers/python_pip.d.ts +35 -0
- package/dist/src/providers/python_pip.js +227 -0
- package/dist/src/sbom.d.ts +59 -0
- package/dist/src/sbom.js +84 -0
- package/dist/src/tools.d.ts +74 -0
- package/dist/src/tools.js +159 -0
- package/package.json +106 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { EOL } from 'os';
|
|
5
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
6
|
+
import Sbom from '../sbom.js';
|
|
7
|
+
import { getCustom } from '../tools.js';
|
|
8
|
+
import Base_java, { ecosystem_maven } from "./base_java.js";
|
|
9
|
+
/** @typedef {import('../provider').Provider} */
|
|
10
|
+
/** @typedef {import('../provider').Provided} Provided */
|
|
11
|
+
/** @typedef {{name: string, version: string}} Package */
|
|
12
|
+
/** @typedef {{groupId: string, artifactId: string, version: string, scope: string, ignore: boolean}} Dependency */
|
|
13
|
+
export default class Java_maven extends Base_java {
|
|
14
|
+
constructor() {
|
|
15
|
+
super('mvn', 'mvnw' + (process.platform === 'win32' ? '.cmd' : ''));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} manifestName - the subject manifest name-type
|
|
19
|
+
* @returns {boolean} - return true if `pom.xml` is the manifest name-type
|
|
20
|
+
*/
|
|
21
|
+
isSupported(manifestName) {
|
|
22
|
+
return 'pom.xml' === manifestName;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @param {string} manifestDir - the directory where the manifest lies
|
|
26
|
+
*/
|
|
27
|
+
validateLockFile() { return true; }
|
|
28
|
+
/**
|
|
29
|
+
* Provide content and content type for maven-maven stack analysis.
|
|
30
|
+
* @param {string} manifest - the manifest path or name
|
|
31
|
+
* @param {{}} [opts={}] - optional various options to pass along the application
|
|
32
|
+
* @returns {Provided}
|
|
33
|
+
*/
|
|
34
|
+
provideStack(manifest, opts = {}) {
|
|
35
|
+
return {
|
|
36
|
+
ecosystem: ecosystem_maven,
|
|
37
|
+
content: this.#createSbomStackAnalysis(manifest, opts),
|
|
38
|
+
contentType: 'application/vnd.cyclonedx+json'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Provide content and content type for maven-maven component analysis.
|
|
43
|
+
* @param {string} manifest - path to the manifest file
|
|
44
|
+
* @param {{}} [opts={}] - optional various options to pass along the application
|
|
45
|
+
* @returns {Provided}
|
|
46
|
+
*/
|
|
47
|
+
provideComponent(manifest, opts = {}) {
|
|
48
|
+
return {
|
|
49
|
+
ecosystem: ecosystem_maven,
|
|
50
|
+
content: this.#getSbomForComponentAnalysis(manifest, opts),
|
|
51
|
+
contentType: 'application/vnd.cyclonedx+json'
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create a Dot Graph dependency tree for a manifest path.
|
|
56
|
+
* @param {string} manifest - path for pom.xml
|
|
57
|
+
* @param {{}} [opts={}] - optional various options to pass along the application
|
|
58
|
+
* @returns {string} the Dot Graph content
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
#createSbomStackAnalysis(manifest, opts = {}) {
|
|
62
|
+
const manifestDir = path.dirname(manifest);
|
|
63
|
+
const mvn = this.selectToolBinary(manifest, opts);
|
|
64
|
+
const mvnArgs = JSON.parse(getCustom('TRUSTIFY_DA_MVN_ARGS', '[]', opts));
|
|
65
|
+
if (!Array.isArray(mvnArgs)) {
|
|
66
|
+
throw new Error(`configured maven args is not an array, is a ${typeof mvnArgs}`);
|
|
67
|
+
}
|
|
68
|
+
// clean maven target
|
|
69
|
+
try {
|
|
70
|
+
this._invokeCommand(mvn, ['-q', 'clean', ...mvnArgs], { cwd: manifestDir });
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
throw new Error(`failed to clean maven target`, { cause: error });
|
|
74
|
+
}
|
|
75
|
+
// create dependency graph in a temp file
|
|
76
|
+
let tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'exhort_'));
|
|
77
|
+
let tmpDepTree = path.join(tmpDir, 'mvn_deptree.txt');
|
|
78
|
+
// build initial command (dot outputType is not available for verbose mode)
|
|
79
|
+
let depTreeCmdArgs = ['-q', 'org.apache.maven.plugins:maven-dependency-plugin:3.6.0:tree',
|
|
80
|
+
'-Dscope=compile', '-Dverbose',
|
|
81
|
+
'-DoutputType=text', `-DoutputFile=${tmpDepTree}`];
|
|
82
|
+
// exclude ignored dependencies, exclude format is groupId:artifactId:scope:version.
|
|
83
|
+
// version and scope are marked as '*' if not specified (we do not use scope yet)
|
|
84
|
+
let ignoredDeps = new Array();
|
|
85
|
+
let ignoredArgs = new Array();
|
|
86
|
+
this.#getDependencies(manifest).forEach(dep => {
|
|
87
|
+
if (dep.ignore) {
|
|
88
|
+
ignoredArgs.push(`${dep['groupId']}:${dep['artifactId']}`);
|
|
89
|
+
//version is not reliable because we're not resolving the effective pom
|
|
90
|
+
ignoredDeps.push(this.toPurl(dep.groupId, dep.artifactId));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
if (ignoredArgs.length > 0) {
|
|
94
|
+
depTreeCmdArgs.push(`-Dexcludes=${ignoredArgs.join(',')}`);
|
|
95
|
+
}
|
|
96
|
+
// execute dependency tree command
|
|
97
|
+
try {
|
|
98
|
+
this._invokeCommand(mvn, [...depTreeCmdArgs, ...mvnArgs], { cwd: manifestDir });
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
throw new Error(`failed creating maven dependency tree`, { cause: error });
|
|
102
|
+
}
|
|
103
|
+
// read dependency tree from temp file
|
|
104
|
+
let content = fs.readFileSync(tmpDepTree);
|
|
105
|
+
if (process.env["TRUSTIFY_DA_DEBUG"] === "true") {
|
|
106
|
+
console.error("Dependency tree that will be used as input for creating the BOM =>" + EOL + EOL + content.toString());
|
|
107
|
+
}
|
|
108
|
+
let sbom = this.createSbomFileFromTextFormat(content.toString(), ignoredDeps, opts);
|
|
109
|
+
// delete temp file and directory
|
|
110
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
111
|
+
// return dependency graph as string
|
|
112
|
+
return sbom;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
*
|
|
116
|
+
* @param {String} textGraphList Text graph String of the manifest
|
|
117
|
+
* @param {[String]} ignoredDeps List of ignored dependencies to be omitted from sbom
|
|
118
|
+
* @return {String} formatted sbom Json String with all dependencies
|
|
119
|
+
*/
|
|
120
|
+
createSbomFileFromTextFormat(textGraphList, ignoredDeps, opts) {
|
|
121
|
+
let lines = textGraphList.split(EOL);
|
|
122
|
+
// get root component
|
|
123
|
+
let root = lines[0];
|
|
124
|
+
let rootPurl = this.parseDep(root);
|
|
125
|
+
let sbom = new Sbom();
|
|
126
|
+
sbom.addRoot(rootPurl);
|
|
127
|
+
this.parseDependencyTree(root, 0, lines.slice(1), sbom);
|
|
128
|
+
return sbom.filterIgnoredDeps(ignoredDeps).getAsJsonString(opts);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Create a dependency list for a manifest content.
|
|
132
|
+
* @param {{}} [opts={}] - optional various options to pass along the application
|
|
133
|
+
* @returns {[Dependency]} the Dot Graph content
|
|
134
|
+
* @private
|
|
135
|
+
*/
|
|
136
|
+
#getSbomForComponentAnalysis(manifestPath, opts = {}) {
|
|
137
|
+
const mvn = this.selectToolBinary(manifestPath, opts);
|
|
138
|
+
const mvnArgs = JSON.parse(getCustom('TRUSTIFY_DA_MVN_ARGS', '[]', opts));
|
|
139
|
+
if (!Array.isArray(mvnArgs)) {
|
|
140
|
+
throw new Error(`configured maven args is not an array, is a ${typeof mvnArgs}`);
|
|
141
|
+
}
|
|
142
|
+
const tmpEffectivePom = path.resolve(path.join(path.dirname(manifestPath), 'effective-pom.xml'));
|
|
143
|
+
// create effective pom and save to temp file
|
|
144
|
+
try {
|
|
145
|
+
this._invokeCommand(mvn, ['-q', 'help:effective-pom', `-Doutput=${tmpEffectivePom}`, ...mvnArgs], { cwd: path.dirname(manifestPath) });
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
throw new Error(`failed creating maven effective pom`, { cause: error });
|
|
149
|
+
}
|
|
150
|
+
// iterate over all dependencies in original pom and collect all ignored ones
|
|
151
|
+
let ignored = this.#getDependencies(manifestPath).filter(d => d.ignore);
|
|
152
|
+
// iterate over all dependencies and create a package for every non-ignored one
|
|
153
|
+
/** @type [Dependency] */
|
|
154
|
+
let dependencies = this.#getDependencies(tmpEffectivePom)
|
|
155
|
+
.filter(d => !this.#dependencyIn(d, ignored));
|
|
156
|
+
let sbom = new Sbom();
|
|
157
|
+
let rootDependency = this.#getRootFromPom(tmpEffectivePom, manifestPath);
|
|
158
|
+
let purlRoot = this.toPurl(rootDependency.groupId, rootDependency.artifactId, rootDependency.version);
|
|
159
|
+
sbom.addRoot(purlRoot);
|
|
160
|
+
dependencies.forEach(dep => {
|
|
161
|
+
let currentPurl = this.toPurl(dep.groupId, dep.artifactId, dep.version);
|
|
162
|
+
sbom.addDependency(purlRoot, currentPurl);
|
|
163
|
+
});
|
|
164
|
+
fs.rmSync(tmpEffectivePom);
|
|
165
|
+
// return dependencies list
|
|
166
|
+
return sbom.getAsJsonString(opts);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
*
|
|
170
|
+
* @param effectivePomManifest effective pom manifest path
|
|
171
|
+
* @param originalManifest pom.xml manifest path
|
|
172
|
+
* @return {Dependency} returns the root dependency for the pom
|
|
173
|
+
* @private
|
|
174
|
+
*/
|
|
175
|
+
#getRootFromPom(effectivePomManifest) {
|
|
176
|
+
let parser = new XMLParser();
|
|
177
|
+
let buf = fs.readFileSync(effectivePomManifest);
|
|
178
|
+
let effectivePomStruct = parser.parse(buf.toString());
|
|
179
|
+
let pomRoot;
|
|
180
|
+
if (effectivePomStruct['project']) {
|
|
181
|
+
pomRoot = effectivePomStruct['project'];
|
|
182
|
+
}
|
|
183
|
+
else { // if there is no project root tag, then it's a multi module/submodules aggregator parent POM
|
|
184
|
+
for (let proj of effectivePomStruct['projects']['project']) {
|
|
185
|
+
// need to choose the aggregate POM and not one of the modules.
|
|
186
|
+
if (proj.packaging && proj.packaging === 'pom') {
|
|
187
|
+
pomRoot = proj;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/** @type Dependency */
|
|
192
|
+
let rootDependency = {
|
|
193
|
+
groupId: pomRoot['groupId'],
|
|
194
|
+
artifactId: pomRoot['artifactId'],
|
|
195
|
+
version: pomRoot['version'],
|
|
196
|
+
scope: '*',
|
|
197
|
+
ignore: false
|
|
198
|
+
};
|
|
199
|
+
return rootDependency;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Get a list of dependencies with marking of dependencies commented with <!--exhortignore-->.
|
|
203
|
+
* @param {string} manifest - path for pom.xml
|
|
204
|
+
* @returns {[Dependency]} an array of dependencies
|
|
205
|
+
* @private
|
|
206
|
+
*/
|
|
207
|
+
#getDependencies(manifest) {
|
|
208
|
+
/** @type [Dependency] */
|
|
209
|
+
let ignored = [];
|
|
210
|
+
// build xml parser with options
|
|
211
|
+
let parser = new XMLParser({
|
|
212
|
+
commentPropName: '#comment',
|
|
213
|
+
isArray: (_, jpath) => 'project.dependencies.dependency' === jpath,
|
|
214
|
+
parseTagValue: false
|
|
215
|
+
});
|
|
216
|
+
// read manifest pom.xml file into buffer
|
|
217
|
+
let buf = fs.readFileSync(manifest);
|
|
218
|
+
// parse manifest pom.xml to json
|
|
219
|
+
let pomJson = parser.parse(buf.toString());
|
|
220
|
+
// iterate over all dependencies and chery pick dependencies with a exhortignore comment
|
|
221
|
+
let pomXml;
|
|
222
|
+
// project without modules
|
|
223
|
+
if (pomJson['project']) {
|
|
224
|
+
if (pomJson['project']['dependencies'] !== undefined) {
|
|
225
|
+
pomXml = pomJson['project']['dependencies']['dependency'];
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
pomXml = [];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else { // project with modules
|
|
232
|
+
pomXml = pomJson['projects']['project'].filter(project => project.dependencies !== undefined).flatMap(project => project.dependencies.dependency);
|
|
233
|
+
}
|
|
234
|
+
pomXml.forEach(dep => {
|
|
235
|
+
let ignore = false;
|
|
236
|
+
if (dep['#comment'] && dep['#comment'].includes('exhortignore')) { // #comment is an array or a string
|
|
237
|
+
ignore = true;
|
|
238
|
+
}
|
|
239
|
+
if (dep['scope'] !== 'test') {
|
|
240
|
+
ignored.push({
|
|
241
|
+
groupId: dep['groupId'],
|
|
242
|
+
artifactId: dep['artifactId'],
|
|
243
|
+
version: dep['version'] ? dep['version'].toString() : '*',
|
|
244
|
+
scope: '*',
|
|
245
|
+
ignore: ignore
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
// return list of dependencies
|
|
250
|
+
return ignored;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Utility function for looking up a dependency in a list of dependencies ignoring the "ignored"
|
|
254
|
+
* field
|
|
255
|
+
* @param dep {Dependency} dependency to look for
|
|
256
|
+
* @param deps {[Dependency]} list of dependencies to look in
|
|
257
|
+
* @returns boolean true if found dep in deps
|
|
258
|
+
* @private
|
|
259
|
+
*/
|
|
260
|
+
#dependencyIn(dep, deps) {
|
|
261
|
+
return deps.filter(d => dep.artifactId === d.artifactId && dep.groupId === d.groupId && dep.scope === d.scope).length > 0;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Base_javascript from './base_javascript.js';
|
|
2
|
+
export default class Javascript_npm extends Base_javascript {
|
|
3
|
+
_lockFileName() {
|
|
4
|
+
return "package-lock.json";
|
|
5
|
+
}
|
|
6
|
+
_cmdName() {
|
|
7
|
+
return "npm";
|
|
8
|
+
}
|
|
9
|
+
_listCmdArgs(includeTransitive) {
|
|
10
|
+
return ['ls', includeTransitive ? '--all' : '--depth=0', '--package-lock-only', '--omit=dev', '--json'];
|
|
11
|
+
}
|
|
12
|
+
_updateLockFileCmdArgs() {
|
|
13
|
+
return ['install', '--package-lock-only'];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import Base_javascript from './base_javascript.js';
|
|
2
|
+
export default class Javascript_pnpm extends Base_javascript {
|
|
3
|
+
_lockFileName() {
|
|
4
|
+
return "pnpm-lock.yaml";
|
|
5
|
+
}
|
|
6
|
+
_cmdName() {
|
|
7
|
+
return "pnpm";
|
|
8
|
+
}
|
|
9
|
+
_listCmdArgs(includeTransitive) {
|
|
10
|
+
return ['ls', includeTransitive ? '--depth=Infinity' : '--depth=0', '--prod', '--json'];
|
|
11
|
+
}
|
|
12
|
+
_updateLockFileCmdArgs() {
|
|
13
|
+
return ['install', '--frozen-lockfile'];
|
|
14
|
+
}
|
|
15
|
+
_buildDependencyTree(includeTransitive, manifest) {
|
|
16
|
+
const tree = super._buildDependencyTree(includeTransitive, manifest);
|
|
17
|
+
if (Array.isArray(tree) && tree.length > 0) {
|
|
18
|
+
return tree[0];
|
|
19
|
+
}
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export default class Javascript_yarn extends Base_javascript {
|
|
2
|
+
static VERSION_PATTERN: RegExp;
|
|
3
|
+
_listCmdArgs(includeTransitive: any, manifestDir: any): any;
|
|
4
|
+
_updateLockFileCmdArgs(manifestDir: any): any;
|
|
5
|
+
_setUp(manifestPath: any, opts: any): void;
|
|
6
|
+
_getRootDependencies(depTree: any): any;
|
|
7
|
+
_parseDepTreeOutput(output: any): any;
|
|
8
|
+
_addDependenciesToSbom(sbom: any, depTree: any): void;
|
|
9
|
+
#private;
|
|
10
|
+
}
|
|
11
|
+
import Base_javascript from './base_javascript.js';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import Base_javascript from './base_javascript.js';
|
|
2
|
+
import Yarn_berry_processor from './processors/yarn_berry_processor.js';
|
|
3
|
+
import Yarn_classic_processor from './processors/yarn_classic_processor.js';
|
|
4
|
+
export default class Javascript_yarn extends Base_javascript {
|
|
5
|
+
static VERSION_PATTERN = /^([0-9]+)\./;
|
|
6
|
+
#processor;
|
|
7
|
+
_lockFileName() {
|
|
8
|
+
return "yarn.lock";
|
|
9
|
+
}
|
|
10
|
+
_cmdName() {
|
|
11
|
+
return "yarn";
|
|
12
|
+
}
|
|
13
|
+
_listCmdArgs(includeTransitive, manifestDir) {
|
|
14
|
+
return this.#processor.listCmdArgs(includeTransitive, manifestDir);
|
|
15
|
+
}
|
|
16
|
+
_updateLockFileCmdArgs(manifestDir) {
|
|
17
|
+
return this.#processor.updateLockFileCmdArgs(manifestDir);
|
|
18
|
+
}
|
|
19
|
+
_setUp(manifestPath, opts) {
|
|
20
|
+
super._setUp(manifestPath, opts);
|
|
21
|
+
const version = this._version() ?? '';
|
|
22
|
+
const matches = Javascript_yarn.VERSION_PATTERN.exec(version);
|
|
23
|
+
if (matches?.length !== 2) {
|
|
24
|
+
throw new Error(`Invalid Yarn version format: ${version}`);
|
|
25
|
+
}
|
|
26
|
+
const isClassic = matches[1] === '1';
|
|
27
|
+
this._setEcosystem(isClassic ? 'yarn-classic' : 'yarn-berry');
|
|
28
|
+
this.#processor = isClassic ? new Yarn_classic_processor(this._getManifest()) : new Yarn_berry_processor(this._getManifest());
|
|
29
|
+
}
|
|
30
|
+
_getRootDependencies(depTree) {
|
|
31
|
+
return this.#processor.getRootDependencies(depTree);
|
|
32
|
+
}
|
|
33
|
+
_parseDepTreeOutput(output) {
|
|
34
|
+
return this.#processor.parseDepTreeOutput(output);
|
|
35
|
+
}
|
|
36
|
+
_addDependenciesToSbom(sbom, depTree) {
|
|
37
|
+
this.#processor.addDependenciesToSbom(sbom, depTree);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export default class Manifest {
|
|
2
|
+
constructor(manifestPath: any);
|
|
3
|
+
manifestPath: any;
|
|
4
|
+
dependencies: any[];
|
|
5
|
+
name: any;
|
|
6
|
+
version: any;
|
|
7
|
+
ignored: any[];
|
|
8
|
+
loadManifest(): any;
|
|
9
|
+
loadDependencies(content: any): any[];
|
|
10
|
+
loadIgnored(content: any): any[];
|
|
11
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { toPurl } from "../tools.js";
|
|
3
|
+
const DEFAULT_VERSION = 'v0.0.0';
|
|
4
|
+
export default class Manifest {
|
|
5
|
+
constructor(manifestPath) {
|
|
6
|
+
if (!manifestPath) {
|
|
7
|
+
throw new Error("Missing required manifest path");
|
|
8
|
+
}
|
|
9
|
+
this.manifestPath = manifestPath;
|
|
10
|
+
const content = this.loadManifest();
|
|
11
|
+
this.dependencies = this.loadDependencies(content);
|
|
12
|
+
this.name = content.name;
|
|
13
|
+
this.version = content.version || DEFAULT_VERSION;
|
|
14
|
+
this.ignored = this.loadIgnored(content);
|
|
15
|
+
}
|
|
16
|
+
loadManifest() {
|
|
17
|
+
try {
|
|
18
|
+
let manifest = JSON.parse(fs.readFileSync(this.manifestPath, 'utf-8'));
|
|
19
|
+
return manifest;
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
if (err.code === 'ENOENT') {
|
|
23
|
+
throw new Error("Missing manifest file: " + this.manifestPath, { cause: err });
|
|
24
|
+
}
|
|
25
|
+
throw new Error("Unable to parse manifest: " + this.manifestPath, { cause: err });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
loadDependencies(content) {
|
|
29
|
+
let deps = [];
|
|
30
|
+
if (!content.dependencies) {
|
|
31
|
+
return deps;
|
|
32
|
+
}
|
|
33
|
+
for (let dep in content.dependencies) {
|
|
34
|
+
deps.push(dep);
|
|
35
|
+
}
|
|
36
|
+
return deps;
|
|
37
|
+
}
|
|
38
|
+
loadIgnored(content) {
|
|
39
|
+
let deps = [];
|
|
40
|
+
if (!content.exhortignore) {
|
|
41
|
+
return deps;
|
|
42
|
+
}
|
|
43
|
+
for (let i = 0; i < content.exhortignore.length; i++) {
|
|
44
|
+
deps.push(toPurl("npm", content.exhortignore[i]));
|
|
45
|
+
}
|
|
46
|
+
return deps;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Processor for Yarn Berry package manager
|
|
3
|
+
* Handles parsing and processing of dependencies for Yarn Berry projects
|
|
4
|
+
*/
|
|
5
|
+
export default class Yarn_berry_processor extends Yarn_processor {
|
|
6
|
+
static LOCATOR_PATTERN: RegExp;
|
|
7
|
+
static VIRTUAL_LOCATOR_PATTERN: RegExp;
|
|
8
|
+
/**
|
|
9
|
+
* Returns the command arguments for listing dependencies
|
|
10
|
+
* @param {boolean} includeTransitive - Whether to include transitive dependencies
|
|
11
|
+
* @returns {string[]} Command arguments for listing dependencies
|
|
12
|
+
*/
|
|
13
|
+
listCmdArgs(includeTransitive: boolean): string[];
|
|
14
|
+
/**
|
|
15
|
+
* Returns the command arguments for updating the lock file
|
|
16
|
+
* @param {string} - Directory containing the manifest file
|
|
17
|
+
* @returns {string[]} Command arguments for updating the lock file
|
|
18
|
+
*/
|
|
19
|
+
updateLockFileCmdArgs(): string[];
|
|
20
|
+
/**
|
|
21
|
+
* Parses the dependency tree output from Yarn Berry
|
|
22
|
+
* Converts multiple JSON objects into a valid JSON array
|
|
23
|
+
* @param {string} output - The raw command output
|
|
24
|
+
* @returns {string} Properly formatted JSON string
|
|
25
|
+
*/
|
|
26
|
+
parseDepTreeOutput(output: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Extracts root dependencies from the dependency tree
|
|
29
|
+
* @param {Object} depTree - The dependency tree object
|
|
30
|
+
* @returns {Map<string, PackageURL>} Map of dependency names to their PackageURL objects
|
|
31
|
+
*/
|
|
32
|
+
getRootDependencies(depTree: any): Map<string, PackageURL>;
|
|
33
|
+
/**
|
|
34
|
+
* Adds dependencies to the SBOM
|
|
35
|
+
* @param {Sbom} sbom - The SBOM object to add dependencies to
|
|
36
|
+
* @param {Object} depTree - The dependency tree object
|
|
37
|
+
*/
|
|
38
|
+
addDependenciesToSbom(sbom: Sbom, depTree: any): void;
|
|
39
|
+
#private;
|
|
40
|
+
}
|
|
41
|
+
import Yarn_processor from "./yarn_processor.js";
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { EOL } from 'os';
|
|
2
|
+
import { toPurl, toPurlFromString } from "../../tools.js";
|
|
3
|
+
import { purlType } from "../base_javascript.js";
|
|
4
|
+
import Yarn_processor from "./yarn_processor.js";
|
|
5
|
+
/**
|
|
6
|
+
* Processor for Yarn Berry package manager
|
|
7
|
+
* Handles parsing and processing of dependencies for Yarn Berry projects
|
|
8
|
+
*/
|
|
9
|
+
export default class Yarn_berry_processor extends Yarn_processor {
|
|
10
|
+
static LOCATOR_PATTERN = /^(@?[^@]+(?:\/[^@]+)?)@npm:(.+)$/;
|
|
11
|
+
static VIRTUAL_LOCATOR_PATTERN = /^(@?[^@]+(?:\/[^@]+)?)@virtual:[^#]+#npm:(.+)$/;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the command arguments for listing dependencies
|
|
14
|
+
* @param {boolean} includeTransitive - Whether to include transitive dependencies
|
|
15
|
+
* @returns {string[]} Command arguments for listing dependencies
|
|
16
|
+
*/
|
|
17
|
+
listCmdArgs(includeTransitive) {
|
|
18
|
+
return ['info', includeTransitive ? '--recursive' : '--all', '--json'];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Returns the command arguments for updating the lock file
|
|
22
|
+
* @param {string} - Directory containing the manifest file
|
|
23
|
+
* @returns {string[]} Command arguments for updating the lock file
|
|
24
|
+
*/
|
|
25
|
+
updateLockFileCmdArgs() {
|
|
26
|
+
return ['install', '--immutable'];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parses the dependency tree output from Yarn Berry
|
|
30
|
+
* Converts multiple JSON objects into a valid JSON array
|
|
31
|
+
* @param {string} output - The raw command output
|
|
32
|
+
* @returns {string} Properly formatted JSON string
|
|
33
|
+
*/
|
|
34
|
+
parseDepTreeOutput(output) {
|
|
35
|
+
// Normalize line endings to EOL regardless of platform
|
|
36
|
+
const normalizedOutput = output.replace(/\r\n|\n/g, EOL);
|
|
37
|
+
const lines = normalizedOutput.split(EOL).filter(line => line.trim());
|
|
38
|
+
// Transform multiline JSON objects into a valid JSON array
|
|
39
|
+
const outputArray = lines.join('').replaceAll('}{', '},{');
|
|
40
|
+
return `[${outputArray}]`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Extracts root dependencies from the dependency tree
|
|
44
|
+
* @param {Object} depTree - The dependency tree object
|
|
45
|
+
* @returns {Map<string, PackageURL>} Map of dependency names to their PackageURL objects
|
|
46
|
+
*/
|
|
47
|
+
getRootDependencies(depTree) {
|
|
48
|
+
if (!depTree) {
|
|
49
|
+
return new Map();
|
|
50
|
+
}
|
|
51
|
+
return new Map(depTree.filter(dep => !this.#isRoot(dep.value)).map(dep => {
|
|
52
|
+
const depName = dep.value;
|
|
53
|
+
const idx = depName.lastIndexOf('@');
|
|
54
|
+
const name = depName.substring(0, idx);
|
|
55
|
+
const version = dep.children.Version;
|
|
56
|
+
return [name, toPurl(purlType, name, version)];
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Checks if a dependency is the root package
|
|
61
|
+
* @param {string} name - Name of the dependency
|
|
62
|
+
* @returns {boolean} True if the dependency is the root package
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
#isRoot(name) {
|
|
66
|
+
if (!name) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return name.endsWith("@workspace:.");
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Adds dependencies to the SBOM
|
|
73
|
+
* @param {Sbom} sbom - The SBOM object to add dependencies to
|
|
74
|
+
* @param {Object} depTree - The dependency tree object
|
|
75
|
+
*/
|
|
76
|
+
addDependenciesToSbom(sbom, depTree) {
|
|
77
|
+
if (!depTree) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
depTree.forEach(n => {
|
|
81
|
+
const depName = n.value;
|
|
82
|
+
const from = this.#isRoot(depName) ? toPurlFromString(sbom.getRoot().purl) : this.#purlFromNode(depName, n);
|
|
83
|
+
const deps = n.children?.Dependencies;
|
|
84
|
+
if (!deps) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
deps.forEach(d => {
|
|
88
|
+
const to = this.#purlFromLocator(d.locator);
|
|
89
|
+
if (to) {
|
|
90
|
+
sbom.addDependency(from, to);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Creates a PackageURL from a dependency locator
|
|
97
|
+
* @param {string} locator - The dependency locator
|
|
98
|
+
* @returns {PackageURL|undefined} The PackageURL or undefined if not valid
|
|
99
|
+
* @private
|
|
100
|
+
*/
|
|
101
|
+
#purlFromLocator(locator) {
|
|
102
|
+
if (!locator) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const matches = Yarn_berry_processor.LOCATOR_PATTERN.exec(locator);
|
|
106
|
+
if (matches) {
|
|
107
|
+
return toPurl(purlType, matches[1], matches[2]);
|
|
108
|
+
}
|
|
109
|
+
const virtualMatches = Yarn_berry_processor.VIRTUAL_LOCATOR_PATTERN.exec(locator);
|
|
110
|
+
if (virtualMatches) {
|
|
111
|
+
return toPurl(purlType, virtualMatches[1], virtualMatches[2]);
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Creates a PackageURL from a dependency node
|
|
117
|
+
* @param {string} depName - The dependency name
|
|
118
|
+
* @param {Object} node - The dependency node object
|
|
119
|
+
* @returns {PackageURL|undefined} The PackageURL or undefined if not valid
|
|
120
|
+
* @private
|
|
121
|
+
*/
|
|
122
|
+
#purlFromNode(depName, node) {
|
|
123
|
+
if (!node?.children?.Version) {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
const name = depName.substring(0, depName.lastIndexOf('@'));
|
|
127
|
+
const version = node.children.Version;
|
|
128
|
+
return toPurl(purlType, name, version);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Processor for Yarn Classic package manager
|
|
3
|
+
* Handles parsing and processing of dependencies for Yarn Classic projects
|
|
4
|
+
*/
|
|
5
|
+
export default class Yarn_classic_processor extends Yarn_processor {
|
|
6
|
+
/**
|
|
7
|
+
* Returns the command arguments for listing dependencies
|
|
8
|
+
* @param {boolean} includeTransitive - Whether to include transitive dependencies
|
|
9
|
+
* @returns {string[]} Command arguments for listing dependencies
|
|
10
|
+
*/
|
|
11
|
+
listCmdArgs(includeTransitive: boolean): string[];
|
|
12
|
+
/**
|
|
13
|
+
* Returns the command arguments for updating the lock file
|
|
14
|
+
* @returns {string[]} Command arguments for updating the lock file
|
|
15
|
+
*/
|
|
16
|
+
updateLockFileCmdArgs(): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Parses the dependency tree output from Yarn Classic
|
|
19
|
+
* @param {string} output - The raw command output
|
|
20
|
+
* @returns {string} Unchanged output as it's already in JSON format
|
|
21
|
+
*/
|
|
22
|
+
parseDepTreeOutput(output: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Extracts root dependencies from the dependency tree
|
|
25
|
+
* @param {Object} depTree - The dependency tree object
|
|
26
|
+
* @returns {Map<string, PackageURL>} Map of dependency names to their PackageURL objects
|
|
27
|
+
*/
|
|
28
|
+
getRootDependencies(depTree: any): Map<string, PackageURL>;
|
|
29
|
+
/**
|
|
30
|
+
* Adds dependencies to the SBOM
|
|
31
|
+
* @param {Sbom} sbom - The SBOM object to add dependencies to
|
|
32
|
+
* @param {Object} depTree - The dependency tree object
|
|
33
|
+
*/
|
|
34
|
+
addDependenciesToSbom(sbom: Sbom, depTree: any): void;
|
|
35
|
+
#private;
|
|
36
|
+
}
|
|
37
|
+
import Yarn_processor from "./yarn_processor.js";
|