snyk-docker-plugin
Version:
Snyk CLI docker plugin
100 lines • 4.1 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 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 docker-archive.
* @param dockerArchiveFilesystemPath Path to image file saved in docker-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(dockerArchiveFilesystemPath, extractActions) {
return new Promise((resolve, reject) => {
const tarExtractor = (0, tar_stream_1.extract)();
const layers = {};
let manifest;
let imageConfig;
tarExtractor.on("entry", async (header, stream, next) => {
if (header.type === "file") {
const normalizedName = (0, path_1.normalize)(header.name);
if (isTarFile(normalizedName)) {
try {
layers[normalizedName] = await (0, layer_1.extractImageLayer)(stream, extractActions);
}
catch (error) {
debug(`Error extracting layer content from: '${error}'`);
reject(new Error("Error reading tar archive"));
}
}
else if (isManifestFile(normalizedName)) {
const manifestArray = await getManifestFile(stream);
manifest = manifestArray[0];
}
else if (isImageConfigFile(normalizedName)) {
imageConfig = await getManifestFile(stream);
}
}
stream.resume(); // auto drain the stream
next(); // ready for next entry
});
tarExtractor.on("finish", () => {
try {
resolve(getLayersContentAndArchiveManifest(manifest, imageConfig, layers));
}
catch (error) {
debug(`Error getting layers and manifest content from docker archive: '${JSON.stringify(error)}'`);
reject(new Error("Invalid Docker archive"));
}
});
tarExtractor.on("error", (error) => reject(error));
(0, fs_1.createReadStream)(dockerArchiveFilesystemPath)
.pipe(gunzip())
.pipe(tarExtractor);
});
}
exports.extractArchive = extractArchive;
function getLayersContentAndArchiveManifest(manifest, imageConfig, layers) {
// skip (ignore) non-existent layers
// get the layers content without the name
// reverse layers order from last to first
const layersWithNormalizedNames = manifest.Layers.map((layersName) => (0, path_1.normalize)(layersName));
const filteredLayers = layersWithNormalizedNames
.filter((layersName) => layers[layersName])
.map((layerName) => layers[layerName])
.reverse();
if (filteredLayers.length === 0) {
throw new Error("We found no layers in the provided image");
}
return {
layers: filteredLayers,
manifest,
imageConfig,
};
}
/**
* Note: consumes the stream.
*/
async function getManifestFile(stream) {
return (0, stream_utils_1.streamToJson)(stream);
}
function isManifestFile(name) {
return name === "manifest.json";
}
function isImageConfigFile(name) {
const configRegex = new RegExp("[A-Fa-f0-9]{64}\\.json");
return configRegex.test(name);
}
function isTarFile(name) {
// For both "docker save" and "skopeo copy" style archives the
// layers are represented as tar archives whose names end in .tar.
// For Docker this is "layer.tar", for Skopeo - "<sha256ofLayer>.tar".
return (0, path_1.basename)(name).endsWith(".tar");
}
//# sourceMappingURL=layer.js.map
;