@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,496 @@
|
|
|
1
|
+
import { delimiter, sep } from 'path';
|
|
2
|
+
import { getCustom, getCustomPath, invokeCommand } from '../tools.js';
|
|
3
|
+
import { ImageRef } from './images.js';
|
|
4
|
+
import { Platform } from './platform.js';
|
|
5
|
+
// Constants
|
|
6
|
+
const TRUSTIFY_DA_SYFT_CONFIG_PATH = "TRUSTIFY_DA_SYFT_CONFIG_PATH";
|
|
7
|
+
const TRUSTIFY_DA_SYFT_IMAGE_SOURCE = "TRUSTIFY_DA_SYFT_IMAGE_SOURCE";
|
|
8
|
+
const TRUSTIFY_DA_IMAGE_PLATFORM = "TRUSTIFY_DA_IMAGE_PLATFORM";
|
|
9
|
+
const TRUSTIFY_DA_IMAGE_OS = "TRUSTIFY_DA_IMAGE_OS";
|
|
10
|
+
const TRUSTIFY_DA_IMAGE_ARCH = "TRUSTIFY_DA_IMAGE_ARCH";
|
|
11
|
+
const TRUSTIFY_DA_IMAGE_VARIANT = "TRUSTIFY_DA_IMAGE_VARIANT";
|
|
12
|
+
const TRUSTIFY_DA_SKOPEO_CONFIG_PATH = "TRUSTIFY_DA_SKOPEO_CONFIG_PATH";
|
|
13
|
+
const TRUSTIFY_DA_IMAGE_SERVICE_ENDPOINT = "TRUSTIFY_DA_IMAGE_SERVICE_ENDPOINT";
|
|
14
|
+
const MEDIA_TYPE_DOCKER2_MANIFEST = "application/vnd.docker.distribution.manifest.v2+json";
|
|
15
|
+
const MEDIA_TYPE_DOCKER2_MANIFEST_LIST = "application/vnd.docker.distribution.manifest.list.v2+json";
|
|
16
|
+
const MEDIA_TYPE_OCI1_MANIFEST = "application/vnd.oci.image.manifest.v1+json";
|
|
17
|
+
const MEDIA_TYPE_OCI1_MANIFEST_LIST = "application/vnd.oci.image.index.v1+json";
|
|
18
|
+
const archMapping = {
|
|
19
|
+
"amd64": "amd64",
|
|
20
|
+
"x86_64": "amd64",
|
|
21
|
+
"armv5tl": "arm",
|
|
22
|
+
"armv5tel": "arm",
|
|
23
|
+
"armv5tejl": "arm",
|
|
24
|
+
"armv6l": "arm",
|
|
25
|
+
"armv7l": "arm",
|
|
26
|
+
"armv7ml": "arm",
|
|
27
|
+
"arm64": "arm64",
|
|
28
|
+
"aarch64": "arm64",
|
|
29
|
+
"i386": "386",
|
|
30
|
+
"i486": "386",
|
|
31
|
+
"i586": "386",
|
|
32
|
+
"i686": "386",
|
|
33
|
+
"mips64le": "mips64le",
|
|
34
|
+
"ppc64le": "ppc64le",
|
|
35
|
+
"riscv64": "riscv64",
|
|
36
|
+
"s390x": "s390x"
|
|
37
|
+
};
|
|
38
|
+
const variantMapping = {
|
|
39
|
+
"armv5tl": "v5",
|
|
40
|
+
"armv5tel": "v5",
|
|
41
|
+
"armv5tejl": "v5",
|
|
42
|
+
"armv6l": "v6",
|
|
43
|
+
"armv7l": "v7",
|
|
44
|
+
"armv7ml": "v7",
|
|
45
|
+
"arm64": "v8",
|
|
46
|
+
"aarch64": "v8"
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param {import('./images').ImageRef} imageRef
|
|
51
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
52
|
+
* @returns {{}}
|
|
53
|
+
*/
|
|
54
|
+
export function generateImageSBOM(imageRef, opts = {}) {
|
|
55
|
+
const output = execSyft(imageRef, opts);
|
|
56
|
+
const node = JSON.parse(output);
|
|
57
|
+
if (node['metadata'] != null) {
|
|
58
|
+
const metadata = node['metadata'];
|
|
59
|
+
if (metadata['component'] != null && typeof metadata['component'] === 'object') {
|
|
60
|
+
const imagePurl = imageRef.getPackageURL().toString();
|
|
61
|
+
metadata['component']['purl'] = imagePurl;
|
|
62
|
+
return node;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
*
|
|
68
|
+
* @param {string} image
|
|
69
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
70
|
+
* @returns {ImageRef}
|
|
71
|
+
*/
|
|
72
|
+
export function parseImageRef(image, opts) {
|
|
73
|
+
const parts = image.split('^^');
|
|
74
|
+
if (parts[0].trim() === image) {
|
|
75
|
+
return new ImageRef(image, null, opts);
|
|
76
|
+
}
|
|
77
|
+
else if (parts.length === 2) {
|
|
78
|
+
return new ImageRef(parts[0], parts[1], opts);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
throw new Error(`Failed to parse OCI image ref "${image}", should be in the format "image^^architecture" or "image"`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Executes Syft to generate SBOM
|
|
86
|
+
* @param {import('./images').ImageRef} imageRef - The image reference
|
|
87
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
88
|
+
*/
|
|
89
|
+
function execSyft(imageRef, opts = {}) {
|
|
90
|
+
const syft = getCustomPath("syft", opts);
|
|
91
|
+
const docker = getCustomPath("docker", opts);
|
|
92
|
+
const podman = getCustomPath("podman", opts);
|
|
93
|
+
const syftConfigPath = getCustom(TRUSTIFY_DA_SYFT_CONFIG_PATH, "", opts);
|
|
94
|
+
const imageSource = getCustom(TRUSTIFY_DA_SYFT_IMAGE_SOURCE, "", opts);
|
|
95
|
+
// Confirm image source exists, this will throw an error if not
|
|
96
|
+
getImageSource(imageSource);
|
|
97
|
+
const dockerPath = docker?.includes(sep)
|
|
98
|
+
? docker.substring(0, docker.lastIndexOf(sep) + 1)
|
|
99
|
+
: "";
|
|
100
|
+
const podmanPath = podman?.includes(sep)
|
|
101
|
+
? podman.substring(0, podman.lastIndexOf(sep) + 1)
|
|
102
|
+
: "";
|
|
103
|
+
const envs = getSyftEnvs(dockerPath, podmanPath);
|
|
104
|
+
const scheme = imageRef.image.getFullName();
|
|
105
|
+
const args = [
|
|
106
|
+
scheme,
|
|
107
|
+
"-s",
|
|
108
|
+
"all-layers",
|
|
109
|
+
"-o",
|
|
110
|
+
"cyclonedx-json@1.5",
|
|
111
|
+
"-q",
|
|
112
|
+
...(syftConfigPath ? ["-c", syftConfigPath] : []),
|
|
113
|
+
...(imageSource ? ["--from", imageSource] : []),
|
|
114
|
+
];
|
|
115
|
+
try {
|
|
116
|
+
return invokeCommand(syft, args, {
|
|
117
|
+
env: { ...process.env, ...envs },
|
|
118
|
+
// 10MB, this output can be large so we need more than default
|
|
119
|
+
maxBuffer: 1024 * 1024 * 10
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (error.code === 'ENOENT') {
|
|
124
|
+
throw new Error(`syft binary not accessible at ${syft}`);
|
|
125
|
+
}
|
|
126
|
+
throw new Error(`failed to invoke syft to generate OCI image SBOM`, { cause: error });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Gets the environment variables for Syft
|
|
131
|
+
* @param {string} dockerPath - The Docker path
|
|
132
|
+
* @param {string} podmanPath - The Podman path
|
|
133
|
+
* @returns {Array<string>} - The environment variables
|
|
134
|
+
*/
|
|
135
|
+
function getSyftEnvs(dockerPath, podmanPath) {
|
|
136
|
+
let path = null;
|
|
137
|
+
if (dockerPath && podmanPath) {
|
|
138
|
+
path = `${dockerPath}${File.pathSeparator}${podmanPath}`;
|
|
139
|
+
}
|
|
140
|
+
else if (dockerPath) {
|
|
141
|
+
path = dockerPath;
|
|
142
|
+
}
|
|
143
|
+
else if (podmanPath) {
|
|
144
|
+
path = podmanPath;
|
|
145
|
+
}
|
|
146
|
+
const prependPath = () => {
|
|
147
|
+
const systemPath = process.env["PATH"];
|
|
148
|
+
if (systemPath) {
|
|
149
|
+
return `${systemPath}${delimiter}${path}`;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
return `${path}`;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
return path ? { 'PATH': prependPath() } : {};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Gets the platform information for an image
|
|
159
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
160
|
+
* @returns {Platform|null} - The platform information or null
|
|
161
|
+
*/
|
|
162
|
+
export function getImagePlatform(opts = {}) {
|
|
163
|
+
const platform = getCustom(TRUSTIFY_DA_IMAGE_PLATFORM, null, opts);
|
|
164
|
+
if (platform) {
|
|
165
|
+
return Platform.fromString(platform);
|
|
166
|
+
}
|
|
167
|
+
const imageSource = getCustom(TRUSTIFY_DA_SYFT_IMAGE_SOURCE, "", opts);
|
|
168
|
+
const source = getImageSource(imageSource);
|
|
169
|
+
let os = getCustom(TRUSTIFY_DA_IMAGE_OS, null, opts);
|
|
170
|
+
if (!os) {
|
|
171
|
+
os = source.getOs(opts);
|
|
172
|
+
}
|
|
173
|
+
let arch = getCustom(TRUSTIFY_DA_IMAGE_ARCH, null, opts);
|
|
174
|
+
if (!arch) {
|
|
175
|
+
arch = source.getArch(opts);
|
|
176
|
+
}
|
|
177
|
+
if (os && arch) {
|
|
178
|
+
if (!Platform.isVariantRequired(os, arch)) {
|
|
179
|
+
return Platform.fromComponents(os, arch, null);
|
|
180
|
+
}
|
|
181
|
+
let variant = getCustom(TRUSTIFY_DA_IMAGE_VARIANT, null, opts);
|
|
182
|
+
if (!variant) {
|
|
183
|
+
variant = source.getVariant(opts);
|
|
184
|
+
}
|
|
185
|
+
if (variant) {
|
|
186
|
+
return Platform.fromComponents(os, arch, variant);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Gets information about a host from a container engine
|
|
193
|
+
* @param {string} engine - The container engine name
|
|
194
|
+
* @param {string} info - The information to retrieve
|
|
195
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
196
|
+
* @returns {string} - The host information
|
|
197
|
+
*/
|
|
198
|
+
function hostInfo(engine, info, opts = {}) {
|
|
199
|
+
const exec = getCustomPath(engine, opts);
|
|
200
|
+
let output;
|
|
201
|
+
try {
|
|
202
|
+
output = invokeCommand(exec, ["info"]);
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
if (error.code === 'ENOENT') {
|
|
206
|
+
throw new Error(`${engine} binary not accessible at ${exec}`);
|
|
207
|
+
}
|
|
208
|
+
throw new Error(`failed to invoke ${engine} to fetch host info`, { cause: error });
|
|
209
|
+
}
|
|
210
|
+
const lines = output.split("\n");
|
|
211
|
+
for (const line of lines) {
|
|
212
|
+
const trimmedLine = line.trimStart();
|
|
213
|
+
if (trimmedLine.startsWith(`${info}:`)) {
|
|
214
|
+
return line.trim().substring(info.length + 1).trim();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return "";
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Gets the OS information from Docker
|
|
221
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
222
|
+
* @returns {string} - The OS information
|
|
223
|
+
*/
|
|
224
|
+
function dockerGetOs(opts = {}) {
|
|
225
|
+
return hostInfo("docker", "OSType", opts);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Gets the architecture information from Docker
|
|
229
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
230
|
+
* @returns {string} - The architecture information
|
|
231
|
+
*/
|
|
232
|
+
function dockerGetArch(opts = {}) {
|
|
233
|
+
let arch = hostInfo("docker", "Architecture", opts);
|
|
234
|
+
arch = archMapping[arch];
|
|
235
|
+
return arch || "";
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Gets the variant information from Docker
|
|
239
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
240
|
+
* @returns {string} - The variant information
|
|
241
|
+
*/
|
|
242
|
+
function dockerGetVariant(opts = {}) {
|
|
243
|
+
let variant = hostInfo("docker", "Architecture", opts);
|
|
244
|
+
variant = variantMapping[variant];
|
|
245
|
+
return variant || "";
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Gets the OS information from Podman
|
|
249
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
250
|
+
* @returns {string} - The OS information
|
|
251
|
+
*/
|
|
252
|
+
function podmanGetOs(opts = {}) {
|
|
253
|
+
return hostInfo("podman", "os", opts);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Gets the architecture information from Podman
|
|
257
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
258
|
+
* @returns {string} - The architecture information
|
|
259
|
+
*/
|
|
260
|
+
function podmanGetArch(opts = {}) {
|
|
261
|
+
return hostInfo("podman", "arch", opts);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Gets the variant information from Podman
|
|
265
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
266
|
+
* @returns {string} - The variant information
|
|
267
|
+
*/
|
|
268
|
+
function podmanGetVariant(opts = {}) {
|
|
269
|
+
return hostInfo("podman", "variant", opts);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Gets information from Docker or Podman
|
|
273
|
+
* @param {function(Object): string} dockerSupplier - function to get information from Docker
|
|
274
|
+
* @param {function(Object): string} podmanSupplier - function to get information from Podman
|
|
275
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
276
|
+
* @returns {string} - The information
|
|
277
|
+
*/
|
|
278
|
+
function dockerPodmanInfo(dockerSupplier, podmanSupplier, opts = {}) {
|
|
279
|
+
return dockerSupplier(opts) || podmanSupplier(opts);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Gets the digests for an image
|
|
283
|
+
* @param {import('./images').ImageRef} imageRef - The image reference
|
|
284
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
285
|
+
* @returns {Object.<string, string>} - The image digests
|
|
286
|
+
* @throws {Error} If the image info is invalid
|
|
287
|
+
*/
|
|
288
|
+
export function getImageDigests(imageRef, opts = {}) {
|
|
289
|
+
const output = execSkopeoInspect(imageRef, true, opts);
|
|
290
|
+
const node = JSON.parse(output);
|
|
291
|
+
if (node.mediaType) {
|
|
292
|
+
const mediaType = node.mediaType;
|
|
293
|
+
if (typeof mediaType === "string") {
|
|
294
|
+
switch (mediaType) {
|
|
295
|
+
case MEDIA_TYPE_OCI1_MANIFEST:
|
|
296
|
+
case MEDIA_TYPE_DOCKER2_MANIFEST:
|
|
297
|
+
return getSingleImageDigest(imageRef, opts);
|
|
298
|
+
case MEDIA_TYPE_OCI1_MANIFEST_LIST:
|
|
299
|
+
case MEDIA_TYPE_DOCKER2_MANIFEST_LIST:
|
|
300
|
+
return getMultiImageDigests(node);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
throw new Error(`The image info is invalid: ${output}`);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Gets digests for multiple images
|
|
308
|
+
* @param {Object} node - The JSON node
|
|
309
|
+
* @returns {Object.<string, string>} - The image digests
|
|
310
|
+
*/
|
|
311
|
+
function getMultiImageDigests(node) {
|
|
312
|
+
if (node.manifests && Array.isArray(node.manifests)) {
|
|
313
|
+
return node.manifests
|
|
314
|
+
.filter(filterMediaType)
|
|
315
|
+
.filter(filterDigest)
|
|
316
|
+
.filter(filterPlatform)
|
|
317
|
+
.reduce((result, manifestNode) => {
|
|
318
|
+
const platformNode = manifestNode.platform;
|
|
319
|
+
const arch = platformNode.architecture;
|
|
320
|
+
const os = platformNode.os;
|
|
321
|
+
let platform;
|
|
322
|
+
if (platformNode.variant) {
|
|
323
|
+
platform = Platform.fromComponents(os, arch, platformNode.variant);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
platform = Platform.fromComponents(os, arch);
|
|
327
|
+
}
|
|
328
|
+
result[platform.toString()] = manifestNode.digest;
|
|
329
|
+
return result;
|
|
330
|
+
}, {});
|
|
331
|
+
}
|
|
332
|
+
return {};
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Filters manifest nodes by media type
|
|
336
|
+
* @param {Object} manifestNode - The manifest node
|
|
337
|
+
* @returns {boolean} - Whether the node passes the filter
|
|
338
|
+
*/
|
|
339
|
+
function filterMediaType(manifestNode) {
|
|
340
|
+
if (manifestNode.mediaType) {
|
|
341
|
+
const mediaType = manifestNode.mediaType;
|
|
342
|
+
if (typeof mediaType === 'string') {
|
|
343
|
+
return mediaType === MEDIA_TYPE_OCI1_MANIFEST ||
|
|
344
|
+
mediaType === MEDIA_TYPE_DOCKER2_MANIFEST;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Filters manifest nodes by digest
|
|
351
|
+
* @param {Object} manifestNode - The manifest node
|
|
352
|
+
* @returns {boolean} - Whether the node passes the filter
|
|
353
|
+
*/
|
|
354
|
+
function filterDigest(manifestNode) {
|
|
355
|
+
return manifestNode.digest && typeof manifestNode.digest === 'string';
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Filters manifest nodes by platform
|
|
359
|
+
* @param {Object} manifestNode - The manifest node
|
|
360
|
+
* @returns {boolean} - Whether the node passes the filter
|
|
361
|
+
*/
|
|
362
|
+
function filterPlatform(manifestNode) {
|
|
363
|
+
if (manifestNode.platform && typeof manifestNode.platform === 'object') {
|
|
364
|
+
const platformNode = manifestNode.platform;
|
|
365
|
+
if (platformNode.architecture && platformNode.os &&
|
|
366
|
+
typeof platformNode.architecture === 'string' &&
|
|
367
|
+
typeof platformNode.os === 'string') {
|
|
368
|
+
try {
|
|
369
|
+
if (platformNode.variant && typeof platformNode.variant === 'string') {
|
|
370
|
+
Platform.fromComponents(platformNode.os, platformNode.architecture, platformNode.variant);
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
Platform.fromComponents(platformNode.os, platformNode.architecture);
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Gets digest for a single image
|
|
385
|
+
* @param {import('./images').ImageRef} imageRef - The image reference
|
|
386
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
387
|
+
* @returns {Object.<string, string>} - The image digest
|
|
388
|
+
*/
|
|
389
|
+
function getSingleImageDigest(imageRef, opts = {}) {
|
|
390
|
+
const output = execSkopeoInspect(imageRef, false, opts);
|
|
391
|
+
const node = JSON.parse(output);
|
|
392
|
+
if (node.Digest && typeof node.Digest === 'string') {
|
|
393
|
+
const result = {};
|
|
394
|
+
result[Platform.EMPTY.toString()] = node.Digest;
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
return {};
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Executes Skopeo inspect
|
|
401
|
+
* @param {import('./images').ImageRef} imageRef - The image reference
|
|
402
|
+
* @param {boolean} raw - Whether to use raw output
|
|
403
|
+
* @param {import("../index.js").Options} [opts={}] - optional various options to pass along the application
|
|
404
|
+
*/
|
|
405
|
+
function execSkopeoInspect(imageRef, raw, opts = {}) {
|
|
406
|
+
const skopeo = getCustomPath("skopeo", opts);
|
|
407
|
+
const configPath = getCustom(TRUSTIFY_DA_SKOPEO_CONFIG_PATH, null, opts);
|
|
408
|
+
const daemonHost = getCustom(TRUSTIFY_DA_IMAGE_SERVICE_ENDPOINT, null, opts);
|
|
409
|
+
const args = [
|
|
410
|
+
"inspect",
|
|
411
|
+
raw ? "--raw" : "",
|
|
412
|
+
`docker://${imageRef.image.getFullName()}`,
|
|
413
|
+
...(configPath ? ["--authfile", configPath] : []),
|
|
414
|
+
...(daemonHost ? ["--daemon-host", daemonHost] : []),
|
|
415
|
+
];
|
|
416
|
+
try {
|
|
417
|
+
return invokeCommand(skopeo, args);
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
if (error.code === 'ENOENT') {
|
|
421
|
+
throw new Error(`skopeo binary not accessible at ${skopeo}`);
|
|
422
|
+
}
|
|
423
|
+
throw new Error(`failed to invoke skopeo to inspect OCI image(s)`, { cause: error });
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* @typedef SyftImageSource
|
|
428
|
+
* @type {object}
|
|
429
|
+
* @property {function(): string} getOs
|
|
430
|
+
* @property {function(): string} getArch
|
|
431
|
+
* @property {function(): string} getVariant
|
|
432
|
+
*/
|
|
433
|
+
/**
|
|
434
|
+
* @typedef SyftImageSourceType
|
|
435
|
+
* @type {object}
|
|
436
|
+
* @property {function(Object): string} getOs
|
|
437
|
+
* @property {function(Object): string} getArch
|
|
438
|
+
* @property {function(Object): string} getVariant
|
|
439
|
+
*/
|
|
440
|
+
/** @type {function(string): SyftImageSourceType} */
|
|
441
|
+
const SyftImageSource = (name) => {
|
|
442
|
+
switch (name) {
|
|
443
|
+
case "":
|
|
444
|
+
case "registry":
|
|
445
|
+
return {
|
|
446
|
+
getOs(opts = {}) {
|
|
447
|
+
return dockerPodmanInfo(dockerGetOs, podmanGetOs, opts);
|
|
448
|
+
},
|
|
449
|
+
getArch(opts = {}) {
|
|
450
|
+
return dockerPodmanInfo(dockerGetArch, podmanGetArch, opts);
|
|
451
|
+
},
|
|
452
|
+
getVariant(opts = {}) {
|
|
453
|
+
return dockerPodmanInfo(dockerGetVariant, podmanGetVariant, opts);
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
case "docker":
|
|
457
|
+
return {
|
|
458
|
+
getOs(opts = {}) {
|
|
459
|
+
return dockerGetOs(opts);
|
|
460
|
+
},
|
|
461
|
+
getArch(opts = {}) {
|
|
462
|
+
return dockerGetArch(opts);
|
|
463
|
+
},
|
|
464
|
+
getVariant(opts = {}) {
|
|
465
|
+
return dockerGetVariant(opts);
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
case "podman":
|
|
469
|
+
return {
|
|
470
|
+
getOs(opts = {}) {
|
|
471
|
+
return podmanGetOs(opts);
|
|
472
|
+
},
|
|
473
|
+
getArch(opts = {}) {
|
|
474
|
+
return podmanGetArch(opts);
|
|
475
|
+
},
|
|
476
|
+
getVariant(opts = {}) {
|
|
477
|
+
return podmanGetVariant(opts);
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
default:
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
/**
|
|
485
|
+
* Gets an image source by name
|
|
486
|
+
* @param {string} name - The image source name
|
|
487
|
+
* @returns {Object} - The image source
|
|
488
|
+
* @throws {Error} If the image source is not valid
|
|
489
|
+
*/
|
|
490
|
+
function getImageSource(name) {
|
|
491
|
+
const source = SyftImageSource(name);
|
|
492
|
+
if (!source) {
|
|
493
|
+
throw new Error(`The image source for syft is not valid: ${name}`);
|
|
494
|
+
}
|
|
495
|
+
return source;
|
|
496
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Match a provider from a list or providers based on file type.
|
|
3
|
+
* Each provider MUST export 'isSupported' taking a file name-type and returning true if supported.
|
|
4
|
+
* Each provider MUST export 'provideComponent' taking manifest data returning a {@link Provided}.
|
|
5
|
+
* Each provider MUST export 'provideStack' taking manifest path returning a {@link Provided}.
|
|
6
|
+
* @param {string} manifest - the name-type or path of the manifest
|
|
7
|
+
* @param {[Provider]} providers - list of providers to iterate over
|
|
8
|
+
* @returns {Provider}
|
|
9
|
+
* @throws {Error} when the manifest is not supported and no provider was matched
|
|
10
|
+
*/
|
|
11
|
+
export function match(manifest: string, providers: [Provider]): Provider;
|
|
12
|
+
/** @typedef {{ecosystem: string, contentType: string, content: string}} Provided */
|
|
13
|
+
/** @typedef {{isSupported: function(string): boolean, validateLockFile: function(string): void, provideComponent: function(string, {}): Provided, provideStack: function(string, {}): Provided}} Provider */
|
|
14
|
+
/**
|
|
15
|
+
* MUST include all providers here.
|
|
16
|
+
* @type {[Provider]}
|
|
17
|
+
*/
|
|
18
|
+
export const availableProviders: [Provider];
|
|
19
|
+
export type Provided = {
|
|
20
|
+
ecosystem: string;
|
|
21
|
+
contentType: string;
|
|
22
|
+
content: string;
|
|
23
|
+
};
|
|
24
|
+
export type Provider = {
|
|
25
|
+
isSupported: (arg0: string) => boolean;
|
|
26
|
+
validateLockFile: (arg0: string) => void;
|
|
27
|
+
provideComponent: (arg0: string, arg1: {}) => Provided;
|
|
28
|
+
provideStack: (arg0: string, arg1: {}) => Provided;
|
|
29
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import golangGomodulesProvider from './providers/golang_gomodules.js';
|
|
3
|
+
import Java_gradle_groovy from "./providers/java_gradle_groovy.js";
|
|
4
|
+
import Java_gradle_kotlin from "./providers/java_gradle_kotlin.js";
|
|
5
|
+
import Java_maven from "./providers/java_maven.js";
|
|
6
|
+
import Javascript_npm from './providers/javascript_npm.js';
|
|
7
|
+
import Javascript_pnpm from './providers/javascript_pnpm.js';
|
|
8
|
+
import Javascript_yarn from './providers/javascript_yarn.js';
|
|
9
|
+
import pythonPipProvider from './providers/python_pip.js';
|
|
10
|
+
/** @typedef {{ecosystem: string, contentType: string, content: string}} Provided */
|
|
11
|
+
/** @typedef {{isSupported: function(string): boolean, validateLockFile: function(string): void, provideComponent: function(string, {}): Provided, provideStack: function(string, {}): Provided}} Provider */
|
|
12
|
+
/**
|
|
13
|
+
* MUST include all providers here.
|
|
14
|
+
* @type {[Provider]}
|
|
15
|
+
*/
|
|
16
|
+
export const availableProviders = [
|
|
17
|
+
new Java_maven(),
|
|
18
|
+
new Java_gradle_groovy(),
|
|
19
|
+
new Java_gradle_kotlin(),
|
|
20
|
+
new Javascript_npm(),
|
|
21
|
+
new Javascript_pnpm(),
|
|
22
|
+
new Javascript_yarn(),
|
|
23
|
+
golangGomodulesProvider,
|
|
24
|
+
pythonPipProvider
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* Match a provider from a list or providers based on file type.
|
|
28
|
+
* Each provider MUST export 'isSupported' taking a file name-type and returning true if supported.
|
|
29
|
+
* Each provider MUST export 'provideComponent' taking manifest data returning a {@link Provided}.
|
|
30
|
+
* Each provider MUST export 'provideStack' taking manifest path returning a {@link Provided}.
|
|
31
|
+
* @param {string} manifest - the name-type or path of the manifest
|
|
32
|
+
* @param {[Provider]} providers - list of providers to iterate over
|
|
33
|
+
* @returns {Provider}
|
|
34
|
+
* @throws {Error} when the manifest is not supported and no provider was matched
|
|
35
|
+
*/
|
|
36
|
+
export function match(manifest, providers) {
|
|
37
|
+
const manifestPath = path.parse(manifest);
|
|
38
|
+
const supported = providers.filter(prov => prov.isSupported(manifestPath.base));
|
|
39
|
+
if (supported.length === 0) {
|
|
40
|
+
throw new Error(`${manifestPath.base} is not supported`);
|
|
41
|
+
}
|
|
42
|
+
const provider = supported.find(prov => prov.validateLockFile(manifestPath.dir));
|
|
43
|
+
if (!provider) {
|
|
44
|
+
throw new Error(`${manifestPath.base} requires a lock file. Use your preferred package manager to generate the lock file.`);
|
|
45
|
+
}
|
|
46
|
+
return provider;
|
|
47
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="packageurl-js/src/package-url.js" />
|
|
3
|
+
export type ecosystem_maven = import('../provider').Provider;
|
|
4
|
+
/** @typedef {import('../provider').Provider} */
|
|
5
|
+
/** @typedef {import('../provider').Provided} Provided */
|
|
6
|
+
/** @typedef {{name: string, version: string}} Package */
|
|
7
|
+
/** @typedef {{groupId: string, artifactId: string, version: string, scope: string, ignore: boolean}} Dependency */
|
|
8
|
+
/**
|
|
9
|
+
* @type {string} ecosystem for java maven packages.
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
export const ecosystem_maven: string;
|
|
13
|
+
export const ecosystem_gradle: "gradle";
|
|
14
|
+
export default class Base_Java {
|
|
15
|
+
/**
|
|
16
|
+
*
|
|
17
|
+
* @param {string} globalBinary name of the global binary
|
|
18
|
+
* @param {string} localWrapper name of the local wrapper filename
|
|
19
|
+
*/
|
|
20
|
+
constructor(globalBinary: string, localWrapper: string);
|
|
21
|
+
DEP_REGEX: RegExp;
|
|
22
|
+
CONFLICT_REGEX: RegExp;
|
|
23
|
+
globalBinary: string;
|
|
24
|
+
localWrapper: string;
|
|
25
|
+
/**
|
|
26
|
+
* Recursively populates the SBOM instance with the parsed graph
|
|
27
|
+
* @param {string} src - Source dependency to start the calculations from
|
|
28
|
+
* @param {number} srcDepth - Current depth in the graph for the given source
|
|
29
|
+
* @param {Array} lines - Array containing the text files being parsed
|
|
30
|
+
* @param {Sbom} sbom - The SBOM where the dependencies are being added
|
|
31
|
+
*/
|
|
32
|
+
parseDependencyTree(src: string, srcDepth: number, lines: any[], sbom: Sbom): void;
|
|
33
|
+
/**
|
|
34
|
+
* Create a PackageURL from any line in a Text Graph dependency tree for a manifest path.
|
|
35
|
+
* @param {string} line - line to parse from a dependencies.txt file
|
|
36
|
+
* @returns {PackageURL} The parsed packageURL
|
|
37
|
+
*/
|
|
38
|
+
parseDep(line: string): PackageURL;
|
|
39
|
+
/**
|
|
40
|
+
* Returns a PackageUrl For Java maven dependencies
|
|
41
|
+
* @param group
|
|
42
|
+
* @param artifact
|
|
43
|
+
* @param version
|
|
44
|
+
* @return {PackageURL}
|
|
45
|
+
*/
|
|
46
|
+
toPurl(group: any, artifact: any, version: any): PackageURL;
|
|
47
|
+
/** This method invokes command string in a process in a synchronous way.
|
|
48
|
+
* Exists for stubbing in tests.
|
|
49
|
+
* @param bin - the command to be invoked
|
|
50
|
+
* @param args - the args to pass to the binary
|
|
51
|
+
* @param {import('child_process').ExecFileOptionsWithStringEncoding} [opts={}]
|
|
52
|
+
* @protected
|
|
53
|
+
*/
|
|
54
|
+
protected _invokeCommand(bin: any, args: any, opts?: import("child_process").ExecFileOptionsWithStringEncoding | undefined): string;
|
|
55
|
+
/**
|
|
56
|
+
*
|
|
57
|
+
* @param {string} manifestPath
|
|
58
|
+
* @param {{}} opts
|
|
59
|
+
* @returns string
|
|
60
|
+
*/
|
|
61
|
+
selectToolBinary(manifestPath: string, opts: {}): string | null;
|
|
62
|
+
/**
|
|
63
|
+
*
|
|
64
|
+
* @param {string} startingManifest - the path of the manifest from which to start searching for the wrapper
|
|
65
|
+
* @param {string} repoRoot - the root of the repository at which point to stop searching for mvnw, derived via git if unset and then fallsback
|
|
66
|
+
* to the root of the drive the manifest is on (assumes absolute path is given)
|
|
67
|
+
* @returns {string|undefined}
|
|
68
|
+
*/
|
|
69
|
+
traverseForWrapper(startingManifest: string, repoRoot?: string): string | undefined;
|
|
70
|
+
normalizePath(thePath: any): string;
|
|
71
|
+
#private;
|
|
72
|
+
}
|
|
73
|
+
export type Provided = import('../provider').Provided;
|
|
74
|
+
export type Package = {
|
|
75
|
+
name: string;
|
|
76
|
+
version: string;
|
|
77
|
+
};
|
|
78
|
+
export type Dependency = {
|
|
79
|
+
groupId: string;
|
|
80
|
+
artifactId: string;
|
|
81
|
+
version: string;
|
|
82
|
+
scope: string;
|
|
83
|
+
ignore: boolean;
|
|
84
|
+
};
|
|
85
|
+
import { PackageURL } from 'packageurl-js';
|