snyk-docker-plugin
Version:
Snyk CLI docker plugin
123 lines • 5.26 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractArchive = void 0;
const Debug = require("debug");
const fs_1 = require("fs");
const gunzip = require("gunzip-maybe");
const path_1 = require("path");
const stream_1 = require("stream");
const tar_stream_1 = require("tar-stream");
const stream_utils_1 = require("../../stream-utils");
const layer_1 = require("../layer");
const debug = Debug("snyk");
/**
* Retrieve the products of files content from the specified oci-archive.
* @param ociArchiveFilesystemPath Path to image file saved in oci-archive format.
* @param extractActions Array of pattern-callbacks pairs.
* @returns Array of extracted files products sorted by the reverse order of the layers from last to first.
*/
async function extractArchive(ociArchiveFilesystemPath, extractActions) {
return new Promise((resolve, reject) => {
const tarExtractor = (0, tar_stream_1.extract)();
const layers = {};
const manifests = {};
let imageConfig;
let imageIndex;
tarExtractor.on("entry", async (header, stream, next) => {
if (header.type === "file") {
const normalizedHeaderName = (0, path_1.normalize)(header.name);
if (isImageIndexFile(normalizedHeaderName)) {
imageIndex = await (0, stream_utils_1.streamToJson)(stream);
}
else {
const jsonStream = new stream_1.PassThrough();
const layerStream = new stream_1.PassThrough();
stream.pipe(jsonStream);
stream.pipe(layerStream);
const promises = [
(0, stream_utils_1.streamToJson)(jsonStream).catch(() => undefined),
(0, layer_1.extractImageLayer)(layerStream, extractActions).catch(() => undefined),
];
const [manifest, layer] = await Promise.all(promises);
// header format is /blobs/hash_name/hash_value
// we're extracting hash_name:hash_value format to match manifest digest
const headerParts = normalizedHeaderName.split(path_1.sep);
const hashName = headerParts[1];
const hashValue = headerParts[headerParts.length - 1];
const digest = `${hashName}:${hashValue}`;
if (isArchiveManifest(manifest)) {
manifests[digest] = manifest;
}
else if (isImageConfigFile(manifest)) {
imageConfig = manifest;
}
if (layer !== undefined) {
layers[digest] = layer;
}
}
}
stream.resume(); // auto drain the stream
next(); // ready for next entry
});
tarExtractor.on("finish", () => {
try {
resolve(getLayersContentAndArchiveManifest(imageIndex, manifests, imageConfig, layers));
}
catch (error) {
debug(`Error getting layers and manifest content from oci archive: '${JSON.stringify(error)}'`);
reject(new Error("Invalid OCI archive"));
}
});
tarExtractor.on("error", (error) => {
reject(error);
});
(0, fs_1.createReadStream)(ociArchiveFilesystemPath)
.pipe(gunzip())
.pipe(tarExtractor);
});
}
exports.extractArchive = extractArchive;
function getLayersContentAndArchiveManifest(imageIndex, manifestCollection, imageConfig, layers) {
// filter empty layers
// get the layers content without the name
// reverse layers order from last to first
// get manifest file first
const manifest = getManifest(imageIndex, manifestCollection);
const filteredLayers = manifest.layers
.filter((layer) => layers[layer.digest])
.map((layer) => layers[layer.digest])
.reverse();
if (filteredLayers.length === 0) {
throw new Error("We found no layers in the provided image");
}
if (imageConfig === undefined) {
throw new Error("Could not find the image config in the provided image");
}
return {
layers: filteredLayers,
manifest,
imageConfig,
};
}
function getManifest(imageIndex, manifestCollection) {
if (!imageIndex) {
return manifestCollection[Object.keys(manifestCollection)[0]];
}
const manifestInfo = imageIndex.manifests.find((item) => item.platform
? item.platform.architecture === "amd64" && item.platform.os === "linux"
: item);
if (manifestInfo === undefined) {
throw new Error("Unsupported type of CPU architecture or operating system");
}
return manifestCollection[manifestInfo.digest];
}
function isArchiveManifest(manifest) {
return (manifest !== undefined && manifest.layers && manifest.layers.length >= 0);
}
function isImageConfigFile(json) {
return json !== undefined && json.architecture && json.rootfs;
}
function isImageIndexFile(name) {
return name === "index.json";
}
//# sourceMappingURL=layer.js.map
;