@cyclonedx/cdxgen 8.0.0

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/docker.js ADDED
@@ -0,0 +1,769 @@
1
+ const isWin = require("os").platform() === "win32";
2
+ const got = require("got");
3
+ const glob = require("glob");
4
+ const url = require("url");
5
+ const util = require("util");
6
+ const stream = require("stream");
7
+ const fs = require("fs");
8
+ const path = require("path");
9
+ const os = require("os");
10
+ const tar = require("tar");
11
+ const { spawnSync } = require("child_process");
12
+
13
+ const pipeline = util.promisify(stream.pipeline);
14
+
15
+ let dockerConn = undefined;
16
+ let isPodman = false;
17
+ let isPodmanRootless = true;
18
+ let isDockerRootless = false;
19
+ const WIN_LOCAL_TLS = "http://localhost:2375";
20
+ let isWinLocalTLS = false;
21
+
22
+ // Debug mode flag
23
+ const DEBUG_MODE =
24
+ process.env.SCAN_DEBUG_MODE === "debug" ||
25
+ process.env.SHIFTLEFT_LOGGING_LEVEL === "debug";
26
+
27
+ /**
28
+ * Method to get all dirs matching a name
29
+ *
30
+ * @param {string} dirPath Root directory for search
31
+ * @param {string} dirName Directory name
32
+ */
33
+ const getDirs = (dirPath, dirName, hidden = false, recurse = true) => {
34
+ try {
35
+ return glob.sync(recurse ? "**/" : "" + dirName, {
36
+ cwd: dirPath,
37
+ silent: true,
38
+ absolute: true,
39
+ nocase: true,
40
+ nodir: false,
41
+ follow: false,
42
+ dot: hidden
43
+ });
44
+ } catch (err) {
45
+ return [];
46
+ }
47
+ };
48
+ exports.getDirs = getDirs;
49
+
50
+ function flatten(lists) {
51
+ return lists.reduce((a, b) => a.concat(b), []);
52
+ }
53
+
54
+ function getDirectories(srcpath) {
55
+ if (fs.existsSync(srcpath)) {
56
+ return fs
57
+ .readdirSync(srcpath)
58
+ .map((file) => path.join(srcpath, file))
59
+ .filter((path) => {
60
+ try {
61
+ return fs.statSync(path).isDirectory();
62
+ } catch (e) {
63
+ return false;
64
+ }
65
+ });
66
+ }
67
+ return [];
68
+ }
69
+
70
+ const getOnlyDirs = (srcpath, dirName) => {
71
+ return [
72
+ srcpath,
73
+ ...flatten(
74
+ getDirectories(srcpath)
75
+ .map((p) => {
76
+ try {
77
+ if (fs.existsSync(p)) {
78
+ if (fs.lstatSync(p).isDirectory()) {
79
+ return getOnlyDirs(p, dirName);
80
+ }
81
+ }
82
+ } catch (err) {
83
+ console.error(err);
84
+ }
85
+ })
86
+ .filter((p) => p !== undefined)
87
+ )
88
+ ].filter((d) => d.endsWith(dirName));
89
+ };
90
+ exports.getOnlyDirs = getOnlyDirs;
91
+
92
+ const getDefaultOptions = () => {
93
+ let opts = {
94
+ throwHttpErrors: true,
95
+ "hooks.beforeError": [],
96
+ method: "GET",
97
+ isPodman
98
+ };
99
+ const userInfo = os.userInfo();
100
+ opts.podmanPrefixUrl = isWin ? "" : `unix:/run/podman/podman.sock:`;
101
+ opts.podmanRootlessPrefixUrl = isWin
102
+ ? ""
103
+ : `unix:/run/user/${userInfo.uid}/podman/podman.sock:`;
104
+ if (!process.env.DOCKER_HOST) {
105
+ if (isPodman) {
106
+ opts.prefixUrl = isPodmanRootless
107
+ ? opts.podmanRootlessPrefixUrl
108
+ : opts.podmanPrefixUrl;
109
+ } else {
110
+ if (isWinLocalTLS) {
111
+ opts.prefixUrl = WIN_LOCAL_TLS;
112
+ } else {
113
+ // Named pipes syntax for Windows doesn't work with got
114
+ // See: https://github.com/sindresorhus/got/issues/2178
115
+ /*
116
+ opts.prefixUrl = isWin
117
+ ? "npipe//./pipe/docker_engine:"
118
+ : "unix:/var/run/docker.sock:";
119
+ */
120
+ opts.prefixUrl = isWin ? WIN_LOCAL_TLS : "unix:/var/run/docker.sock:";
121
+ }
122
+ }
123
+ } else {
124
+ let hostStr = process.env.DOCKER_HOST;
125
+ if (hostStr.startsWith("unix:///")) {
126
+ hostStr = hostStr.replace("unix:///", "unix:/");
127
+ if (hostStr.includes("docker.sock")) {
128
+ hostStr = hostStr.replace("docker.sock", "docker.sock:");
129
+ isDockerRootless = true;
130
+ }
131
+ }
132
+ opts.prefixUrl = hostStr;
133
+ if (process.env.DOCKER_CERT_PATH) {
134
+ opts.https = {
135
+ certificate: fs.readFileSync(
136
+ path.join(process.env.DOCKER_CERT_PATH, "cert.pem"),
137
+ "utf8"
138
+ ),
139
+ key: fs.readFileSync(
140
+ path.join(process.env.DOCKER_CERT_PATH, "key.pem"),
141
+ "utf8"
142
+ )
143
+ };
144
+ }
145
+ }
146
+
147
+ return opts;
148
+ };
149
+
150
+ const getConnection = async (options) => {
151
+ if (!dockerConn) {
152
+ const opts = Object.assign({}, getDefaultOptions(), options);
153
+ try {
154
+ await got.get("_ping", opts);
155
+ dockerConn = got.extend(opts);
156
+ if (DEBUG_MODE) {
157
+ if (isDockerRootless) {
158
+ console.log("Docker service in rootless mode detected!");
159
+ } else {
160
+ console.log("Docker service in root mode detected!");
161
+ }
162
+ }
163
+ } catch (err) {
164
+ // console.log(err, opts);
165
+ try {
166
+ if (isWin) {
167
+ opts.prefixUrl = WIN_LOCAL_TLS;
168
+ await got.get("_ping", opts);
169
+ dockerConn = got.extend(opts);
170
+ isWinLocalTLS = true;
171
+ if (DEBUG_MODE) {
172
+ console.log("Docker desktop on Windows detected!");
173
+ }
174
+ } else {
175
+ opts.prefixUrl = opts.podmanRootlessPrefixUrl;
176
+ await got.get("libpod/_ping", opts);
177
+ isPodman = true;
178
+ isPodmanRootless = true;
179
+ dockerConn = got.extend(opts);
180
+ if (DEBUG_MODE) {
181
+ console.log("Podman in rootless mode detected!");
182
+ }
183
+ }
184
+ } catch (err) {
185
+ // console.log(err);
186
+ try {
187
+ opts.prefixUrl = opts.podmanPrefixUrl;
188
+ await got.get("libpod/_ping", opts);
189
+ isPodman = true;
190
+ isPodmanRootless = false;
191
+ dockerConn = got.extend(opts);
192
+ console.log("Podman in root mode detected!");
193
+ } catch (err) {
194
+ if (os.platform() === "win32") {
195
+ console.warn(
196
+ "Ensure Docker for Desktop is running as an administrator with 'Exposing daemon on TCP without TLS' setting turned on.",
197
+ opts
198
+ );
199
+ } else {
200
+ console.warn(
201
+ "Ensure docker/podman service or Docker for Desktop is running.",
202
+ opts
203
+ );
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
209
+ return dockerConn;
210
+ };
211
+ exports.getConnection = getConnection;
212
+
213
+ const makeRequest = async (path, method = "GET") => {
214
+ let client = await getConnection();
215
+ if (!client) {
216
+ return undefined;
217
+ }
218
+ const extraOptions = {
219
+ responseType: method === "GET" ? "json" : "text",
220
+ resolveBodyOnly: true,
221
+ method
222
+ };
223
+ const opts = Object.assign({}, getDefaultOptions(), extraOptions);
224
+ return await client(path, opts);
225
+ };
226
+ exports.makeRequest = makeRequest;
227
+
228
+ /**
229
+ * Parse image name
230
+ *
231
+ * docker pull debian
232
+ * docker pull debian:jessie
233
+ * docker pull ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
234
+ * docker pull myregistry.local:5000/testing/test-image
235
+ */
236
+ const parseImageName = (fullImageName) => {
237
+ const nameObj = {
238
+ registry: "",
239
+ repo: "",
240
+ tag: "",
241
+ digest: "",
242
+ platform: ""
243
+ };
244
+ if (!fullImageName) {
245
+ return nameObj;
246
+ }
247
+ // Extract registry name
248
+ if (
249
+ fullImageName.includes("/") &&
250
+ (fullImageName.includes(".") || fullImageName.includes(":"))
251
+ ) {
252
+ const urlObj = url.parse(fullImageName);
253
+ const tmpA = fullImageName.split("/");
254
+ if (
255
+ urlObj.path !== fullImageName ||
256
+ tmpA[0].includes(".") ||
257
+ tmpA[0].includes(":")
258
+ ) {
259
+ nameObj.registry = tmpA[0];
260
+ fullImageName = fullImageName.replace(tmpA[0] + "/", "");
261
+ }
262
+ }
263
+ // Extract digest name
264
+ if (fullImageName.includes("@sha256:")) {
265
+ const tmpA = fullImageName.split("@sha256:");
266
+ if (tmpA.length > 1) {
267
+ nameObj.digest = tmpA[tmpA.length - 1];
268
+ fullImageName = fullImageName.replace("@sha256:" + nameObj.digest, "");
269
+ }
270
+ }
271
+ // Extract tag name
272
+ if (fullImageName.includes(":")) {
273
+ const tmpA = fullImageName.split(":");
274
+ if (tmpA.length > 1) {
275
+ nameObj.tag = tmpA[tmpA.length - 1];
276
+ fullImageName = fullImageName.replace(":" + nameObj.tag, "");
277
+ }
278
+ }
279
+ // The left over string is the repo name
280
+ nameObj.repo = fullImageName;
281
+ return nameObj;
282
+ };
283
+ exports.parseImageName = parseImageName;
284
+
285
+ /**
286
+ * Method to get image to the local registry by pulling from the remote if required
287
+ */
288
+ const getImage = async (fullImageName) => {
289
+ let localData = undefined;
290
+ const { repo, tag, digest } = parseImageName(fullImageName);
291
+ // Fetch only the latest tag if none is specified
292
+ if (tag === "" && digest === "") {
293
+ fullImageName = fullImageName + ":latest";
294
+ }
295
+ if (isWin) {
296
+ let result = spawnSync("docker", ["pull", fullImageName], {
297
+ encoding: "utf-8"
298
+ });
299
+ if (result.status !== 0 || result.error) {
300
+ return localData;
301
+ } else {
302
+ result = spawnSync("docker", ["inspect", fullImageName], {
303
+ encoding: "utf-8"
304
+ });
305
+ if (result.status !== 0 || result.error) {
306
+ return localData;
307
+ } else {
308
+ try {
309
+ const stdout = result.stdout;
310
+ if (stdout) {
311
+ const inspectData = JSON.parse(Buffer.from(stdout).toString());
312
+ if (inspectData && Array.isArray(inspectData)) {
313
+ return inspectData[0];
314
+ } else {
315
+ return inspectData;
316
+ }
317
+ }
318
+ } catch (err) {
319
+ // continue regardless of error
320
+ }
321
+ }
322
+ }
323
+ }
324
+ try {
325
+ localData = await makeRequest(`images/${repo}/json`);
326
+ if (DEBUG_MODE) {
327
+ console.log(localData);
328
+ }
329
+ } catch (err) {
330
+ if (DEBUG_MODE) {
331
+ console.log(
332
+ `Trying to pull the image ${fullImageName} from registry. This might take a while ...`
333
+ );
334
+ }
335
+ // If the data is not available locally
336
+ try {
337
+ const pullData = await makeRequest(
338
+ `images/create?fromImage=${fullImageName}`,
339
+ "POST"
340
+ );
341
+ if (
342
+ pullData &&
343
+ (pullData.includes("no match for platform in manifest") ||
344
+ pullData.includes("Error choosing an image from manifest list"))
345
+ ) {
346
+ console.warn(
347
+ "You may have to enable experimental settings in docker to support this platform!"
348
+ );
349
+ console.warn(
350
+ "To scan windows images, run cdxgen on a windows server with hyper-v and docker installed. Switch to windows containers in your docker settings."
351
+ );
352
+ return undefined;
353
+ }
354
+ } catch (err) {
355
+ // continue regardless of error
356
+ }
357
+ try {
358
+ if (DEBUG_MODE) {
359
+ console.log(`Trying with ${repo}`);
360
+ }
361
+ localData = await makeRequest(`images/${repo}/json`);
362
+ if (DEBUG_MODE) {
363
+ console.log(localData);
364
+ }
365
+ } catch (err) {
366
+ if (DEBUG_MODE) {
367
+ console.log(`Retrying with ${fullImageName} due to`, err);
368
+ }
369
+ try {
370
+ localData = await makeRequest(`images/${fullImageName}/json`);
371
+ if (DEBUG_MODE) {
372
+ console.log(localData);
373
+ }
374
+ } catch (err) {
375
+ // continue regardless of error
376
+ }
377
+ }
378
+ }
379
+ if (!localData) {
380
+ console.log(
381
+ `Unable to pull ${fullImageName}. Check if the name is valid. Perform any authentication prior to invoking cdxgen.`
382
+ );
383
+ console.log(
384
+ `Trying manually pulling this image using docker pull ${fullImageName}`
385
+ );
386
+ }
387
+ return localData;
388
+ };
389
+ exports.getImage = getImage;
390
+
391
+ const extractTar = async (fullImageName, dir) => {
392
+ try {
393
+ await pipeline(
394
+ fs.createReadStream(fullImageName),
395
+ tar.x({
396
+ sync: true,
397
+ preserveOwner: false,
398
+ noMtime: true,
399
+ noChmod: true,
400
+ strict: true,
401
+ C: dir,
402
+ portable: true,
403
+ onwarn: () => {}
404
+ })
405
+ );
406
+ return true;
407
+ } catch (err) {
408
+ if (DEBUG_MODE) {
409
+ console.log(err);
410
+ }
411
+ return false;
412
+ }
413
+ };
414
+ exports.extractTar = extractTar;
415
+
416
+ /**
417
+ * Method to export a container image archive.
418
+ * Returns the location of the layers with additional packages related metadata
419
+ */
420
+ const exportArchive = async (fullImageName) => {
421
+ if (!fs.existsSync(fullImageName)) {
422
+ console.log(`Unable to find container image archive ${fullImageName}`);
423
+ return undefined;
424
+ }
425
+ let manifest = {};
426
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "docker-images-"));
427
+ const allLayersExplodedDir = path.join(tempDir, "all-layers");
428
+ const blobsDir = path.join(tempDir, "blobs", "sha256");
429
+ fs.mkdirSync(allLayersExplodedDir);
430
+ const manifestFile = path.join(tempDir, "manifest.json");
431
+ try {
432
+ await extractTar(fullImageName, tempDir);
433
+ // podman use blobs dir
434
+ if (fs.existsSync(blobsDir)) {
435
+ if (DEBUG_MODE) {
436
+ console.log(
437
+ `Image archive ${fullImageName} successfully exported to directory ${tempDir}`
438
+ );
439
+ }
440
+ const allBlobs = getDirs(blobsDir, "*", false, true);
441
+ for (let ablob of allBlobs) {
442
+ if (DEBUG_MODE) {
443
+ console.log(`Extracting ${ablob} to ${allLayersExplodedDir}`);
444
+ }
445
+ await extractTar(ablob, allLayersExplodedDir);
446
+ }
447
+ let lastLayerConfig = {};
448
+ let lastWorkingDir = "";
449
+ const exportData = {
450
+ manifest,
451
+ allLayersDir: tempDir,
452
+ allLayersExplodedDir,
453
+ lastLayerConfig,
454
+ lastWorkingDir
455
+ };
456
+ exportData.pkgPathList = getPkgPathList(exportData, lastWorkingDir);
457
+ return exportData;
458
+ } else if (fs.existsSync(manifestFile)) {
459
+ // docker manifest file
460
+ return await extractFromManifest(
461
+ manifestFile,
462
+ {},
463
+ tempDir,
464
+ allLayersExplodedDir
465
+ );
466
+ } else {
467
+ console.log(`Unable to extract image archive to ${tempDir}`);
468
+ }
469
+ } catch (err) {
470
+ console.log(err);
471
+ }
472
+ return undefined;
473
+ };
474
+ exports.exportArchive = exportArchive;
475
+
476
+ const extractFromManifest = async (
477
+ manifestFile,
478
+ localData,
479
+ tempDir,
480
+ allLayersExplodedDir
481
+ ) => {
482
+ // Example of manifests
483
+ // [{"Config":"blobs/sha256/dedc100afa8d6718f5ac537730dd4a5ceea3563e695c90f1a8ac6df32c4cb291","RepoTags":["shiftleft/core:latest"],"Layers":["blobs/sha256/eaead16dc43bb8811d4ff450935d607f9ba4baffda4fc110cc402fa43f601d83","blobs/sha256/2039af03c0e17a3025b989335e9414149577fa09e7d0dcbee80155333639d11f"]}]
484
+ // {"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.docker.distribution.manifest.list.v2+json","digest":"sha256:7706ac20c7587081dc7a00e0ec65a6633b0bb3788e0048a3e971d3eae492db63","size":318,"annotations":{"io.containerd.image.name":"docker.io/shiftleft/scan-slim:latest","org.opencontainers.image.ref.name":"latest"}}]}
485
+ let manifest = JSON.parse(
486
+ fs.readFileSync(manifestFile, {
487
+ encoding: "utf-8"
488
+ })
489
+ );
490
+ let lastLayerConfig = {};
491
+ let lastLayerConfigFile = "";
492
+ let lastWorkingDir = "";
493
+ // Extract the manifest for the new containerd syntax
494
+ if (Object.keys(manifest).length !== 0 && manifest.manifests) {
495
+ manifest = manifest.manifests;
496
+ }
497
+ if (Array.isArray(manifest)) {
498
+ if (manifest.length !== 1) {
499
+ if (DEBUG_MODE) {
500
+ console.log(
501
+ "Multiple image tags was downloaded. Only the last one would be used"
502
+ );
503
+ console.log(manifest[manifest.length - 1]);
504
+ }
505
+ }
506
+ let layers = manifest[manifest.length - 1]["Layers"] || [];
507
+ if (!layers.length && fs.existsSync(tempDir)) {
508
+ const blobFiles = fs.readdirSync(path.join(tempDir, "blobs", "sha256"));
509
+ if (blobFiles && blobFiles.length) {
510
+ for (const blobf of blobFiles) {
511
+ layers.push(path.join("blobs", "sha256", blobf));
512
+ }
513
+ }
514
+ }
515
+ const lastLayer = layers[layers.length - 1];
516
+ for (let layer of layers) {
517
+ if (DEBUG_MODE) {
518
+ console.log(`Extracting layer ${layer} to ${allLayersExplodedDir}`);
519
+ }
520
+ try {
521
+ await extractTar(path.join(tempDir, layer), allLayersExplodedDir);
522
+ } catch (err) {
523
+ console.log(err);
524
+ }
525
+ }
526
+ if (manifest.Config) {
527
+ lastLayerConfigFile = path.join(tempDir, manifest.Config);
528
+ }
529
+ if (lastLayer.includes("layer.tar")) {
530
+ lastLayerConfigFile = path.join(
531
+ tempDir,
532
+ lastLayer.replace("layer.tar", "json")
533
+ );
534
+ }
535
+ if (lastLayerConfigFile && fs.existsSync(lastLayerConfigFile)) {
536
+ try {
537
+ lastLayerConfig = JSON.parse(
538
+ fs.readFileSync(lastLayerConfigFile, {
539
+ encoding: "utf-8"
540
+ })
541
+ );
542
+ lastWorkingDir =
543
+ lastLayerConfig.config && lastLayerConfig.config.WorkingDir
544
+ ? path.join(allLayersExplodedDir, lastLayerConfig.config.WorkingDir)
545
+ : "";
546
+ } catch (err) {
547
+ console.log(err);
548
+ }
549
+ }
550
+ }
551
+ const exportData = {
552
+ inspectData: localData,
553
+ manifest,
554
+ allLayersDir: tempDir,
555
+ allLayersExplodedDir,
556
+ lastLayerConfig,
557
+ lastWorkingDir
558
+ };
559
+ exportData.pkgPathList = getPkgPathList(exportData, lastWorkingDir);
560
+ return exportData;
561
+ };
562
+
563
+ /**
564
+ * Method to export a container image by using the export feature in docker or podman service.
565
+ * Returns the location of the layers with additional packages related metadata
566
+ */
567
+ const exportImage = async (fullImageName) => {
568
+ // Try to get the data locally first
569
+ const localData = await getImage(fullImageName);
570
+ if (!localData) {
571
+ return undefined;
572
+ }
573
+ const { tag, digest } = parseImageName(fullImageName);
574
+ // Fetch only the latest tag if none is specified
575
+ if (tag === "" && digest === "") {
576
+ fullImageName = fullImageName + ":latest";
577
+ }
578
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "docker-images-"));
579
+ const allLayersExplodedDir = path.join(tempDir, "all-layers");
580
+ let manifestFile = path.join(tempDir, "manifest.json");
581
+ // Windows containers use index.json
582
+ const manifestIndexFile = path.join(tempDir, "index.json");
583
+ // On Windows, fallback to invoking cli
584
+ if (isWin) {
585
+ const imageTarFile = path.join(tempDir, "image.tar");
586
+ console.log(
587
+ `About to export image ${fullImageName} to ${imageTarFile} using docker cli`
588
+ );
589
+ let result = spawnSync(
590
+ "docker",
591
+ ["save", "-o", imageTarFile, fullImageName],
592
+ {
593
+ encoding: "utf-8"
594
+ }
595
+ );
596
+ if (result.status !== 0 || result.error) {
597
+ if (result.stdout || result.stderr) {
598
+ console.log(result.stdout, result.stderr);
599
+ }
600
+ return localData;
601
+ } else {
602
+ await extractTar(imageTarFile, tempDir);
603
+ if (DEBUG_MODE) {
604
+ console.log(`Cleaning up ${imageTarFile}`);
605
+ }
606
+ fs.rmSync(imageTarFile, { force: true });
607
+ }
608
+ } else {
609
+ let client = await getConnection();
610
+ try {
611
+ if (DEBUG_MODE) {
612
+ console.log(`About to export image ${fullImageName} to ${tempDir}`);
613
+ }
614
+ await pipeline(
615
+ client.stream(`images/${fullImageName}/get`),
616
+ tar.x({
617
+ sync: true,
618
+ preserveOwner: false,
619
+ noMtime: true,
620
+ noChmod: true,
621
+ strict: true,
622
+ C: tempDir,
623
+ portable: true,
624
+ onwarn: () => {}
625
+ })
626
+ );
627
+ } catch (err) {
628
+ console.error(err);
629
+ }
630
+ }
631
+ // Continue with extracting the layers
632
+ if (fs.existsSync(tempDir)) {
633
+ if (fs.existsSync(manifestFile)) {
634
+ // This is fine
635
+ } else if (fs.existsSync(manifestIndexFile)) {
636
+ manifestFile = manifestIndexFile;
637
+ } else {
638
+ console.log(
639
+ `Manifest file ${manifestFile} was not found after export at ${tempDir}`
640
+ );
641
+ return undefined;
642
+ }
643
+ if (DEBUG_MODE) {
644
+ console.log(
645
+ `Image ${fullImageName} successfully exported to directory ${tempDir}`
646
+ );
647
+ }
648
+ fs.mkdirSync(allLayersExplodedDir);
649
+ return await extractFromManifest(
650
+ manifestFile,
651
+ localData,
652
+ tempDir,
653
+ allLayersExplodedDir
654
+ );
655
+ } else {
656
+ console.log(`Unable to export image to ${tempDir}`);
657
+ }
658
+ return undefined;
659
+ };
660
+ exports.exportImage = exportImage;
661
+
662
+ /**
663
+ * Method to retrieve path list for system-level packages
664
+ */
665
+ const getPkgPathList = (exportData, lastWorkingDir) => {
666
+ const allLayersExplodedDir = exportData.allLayersExplodedDir;
667
+ const allLayersDir = exportData.allLayersDir;
668
+ let pathList = [];
669
+ let knownSysPaths = [];
670
+ if (allLayersExplodedDir && allLayersExplodedDir !== "") {
671
+ knownSysPaths = [
672
+ path.join(allLayersExplodedDir, "/usr/local/go"),
673
+ path.join(allLayersExplodedDir, "/usr/local/lib"),
674
+ path.join(allLayersExplodedDir, "/usr/local/lib64"),
675
+ path.join(allLayersExplodedDir, "/opt"),
676
+ path.join(allLayersExplodedDir, "/home"),
677
+ path.join(allLayersExplodedDir, "/usr/share"),
678
+ path.join(allLayersExplodedDir, "/usr/src"),
679
+ path.join(allLayersExplodedDir, "/var/www/html"),
680
+ path.join(allLayersExplodedDir, "/var/lib"),
681
+ path.join(allLayersExplodedDir, "/mnt")
682
+ ];
683
+ } else if (allLayersExplodedDir === "") {
684
+ knownSysPaths = [
685
+ path.join(allLayersExplodedDir, "/usr/local/go"),
686
+ path.join(allLayersExplodedDir, "/usr/local/lib"),
687
+ path.join(allLayersExplodedDir, "/usr/local/lib64"),
688
+ path.join(allLayersExplodedDir, "/opt"),
689
+ path.join(allLayersExplodedDir, "/usr/share"),
690
+ path.join(allLayersExplodedDir, "/usr/src"),
691
+ path.join(allLayersExplodedDir, "/var/www/html"),
692
+ path.join(allLayersExplodedDir, "/var/lib")
693
+ ];
694
+ }
695
+ if (fs.existsSync(path.join(allLayersDir, "Files"))) {
696
+ knownSysPaths.push(path.join(allLayersDir, "Files"));
697
+ }
698
+ /*
699
+ // Too slow
700
+ if (fs.existsSync(path.join(allLayersDir, "Users"))) {
701
+ knownSysPaths.push(path.join(allLayersDir, "Users"));
702
+ }
703
+ */
704
+ if (fs.existsSync(path.join(allLayersDir, "ProgramData"))) {
705
+ knownSysPaths.push(path.join(allLayersDir, "ProgramData"));
706
+ }
707
+ const pyInstalls = getDirs(allLayersDir, "Python*/", false, false);
708
+ if (pyInstalls && pyInstalls.length) {
709
+ for (let pyiPath of pyInstalls) {
710
+ const pyDirs = getOnlyDirs(pyiPath, "site-packages");
711
+ if (pyDirs && pyDirs.length) {
712
+ pathList = pathList.concat(pyDirs);
713
+ }
714
+ }
715
+ }
716
+ if (lastWorkingDir && lastWorkingDir !== "") {
717
+ knownSysPaths.push(lastWorkingDir);
718
+ // Some more common app dirs
719
+ if (!lastWorkingDir.startsWith("/app")) {
720
+ knownSysPaths.push(path.join(allLayersExplodedDir, "/app"));
721
+ }
722
+ if (!lastWorkingDir.startsWith("/data")) {
723
+ knownSysPaths.push(path.join(allLayersExplodedDir, "/data"));
724
+ }
725
+ if (!lastWorkingDir.startsWith("/srv")) {
726
+ knownSysPaths.push(path.join(allLayersExplodedDir, "/srv"));
727
+ }
728
+ }
729
+ // Known to cause EACCESS error
730
+ knownSysPaths.push(path.join(allLayersExplodedDir, "/usr/lib"));
731
+ knownSysPaths.push(path.join(allLayersExplodedDir, "/usr/lib64"));
732
+ // Build path list
733
+ for (let wpath of knownSysPaths) {
734
+ pathList = pathList.concat(wpath);
735
+ const pyDirs = getOnlyDirs(wpath, "site-packages");
736
+ if (pyDirs && pyDirs.length) {
737
+ pathList = pathList.concat(pyDirs);
738
+ }
739
+ const gemsDirs = getOnlyDirs(wpath, "gems");
740
+ if (gemsDirs && gemsDirs.length) {
741
+ pathList = pathList.concat(gemsDirs);
742
+ }
743
+ const cargoDirs = getOnlyDirs(wpath, ".cargo");
744
+ if (cargoDirs && cargoDirs.length) {
745
+ pathList = pathList.concat(cargoDirs);
746
+ }
747
+ const composerDirs = getOnlyDirs(wpath, ".composer");
748
+ if (composerDirs && composerDirs.length) {
749
+ pathList = pathList.concat(composerDirs);
750
+ }
751
+ }
752
+ if (DEBUG_MODE) {
753
+ console.log("pathList", pathList);
754
+ }
755
+ return pathList;
756
+ };
757
+ exports.getPkgPathList = getPkgPathList;
758
+
759
+ const removeImage = async (fullImageName, force = false) => {
760
+ const removeData = await makeRequest(
761
+ `images/${fullImageName}?force=${force}`,
762
+ "DELETE"
763
+ );
764
+ if (DEBUG_MODE) {
765
+ console.log(removeData);
766
+ }
767
+ return removeData;
768
+ };
769
+ exports.removeImage = removeImage;