UNPKG

snyk-docker-plugin

Version:
156 lines 7.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.goModulesToScannedProjects = exports.getGoModulesContentAction = exports.DEP_GRAPH_TYPE = void 0; const Debug = require("debug"); const elf = require("elfy"); const event_loop_spinner_1 = require("event-loop-spinner"); // NOTE: Paths will always be normalized to POSIX even on Windows. // This makes it easier to ignore differences between Linux and Windows. const path_1 = require("path"); const go_binary_1 = require("./go-binary"); const debug = Debug("snyk"); const ignoredPaths = [ path_1.posix.normalize("/boot"), path_1.posix.normalize("/dev"), path_1.posix.normalize("/etc"), path_1.posix.normalize("/home"), path_1.posix.normalize("/media"), path_1.posix.normalize("/mnt"), path_1.posix.normalize("/proc"), path_1.posix.normalize("/root"), path_1.posix.normalize("/run"), path_1.posix.normalize("/sbin"), path_1.posix.normalize("/sys"), path_1.posix.normalize("/tmp"), path_1.posix.normalize("/var"), ]; exports.DEP_GRAPH_TYPE = "gomodules"; function filePathMatches(filePath) { const normalizedPath = path_1.posix.normalize(filePath); const dirName = path_1.posix.dirname(normalizedPath); return (!path_1.posix.parse(normalizedPath).ext && !ignoredPaths.some((ignorePath) => dirName.startsWith(ignorePath))); } exports.getGoModulesContentAction = { actionName: "gomodules", filePathMatches, callback: findGoBinaries, }; async function findGoBinaries(stream, streamSize) { return new Promise((resolve, reject) => { const encoding = "binary"; const buildIdMagic = "Go"; const elfHeaderMagic = "\x7FELF"; const buildInfoMagic = "\xff Go buildinf:"; // ELF section headers and so ".go.buildinfo" & ".note.go.buildid" blobs are available in the first 64kb const elfBuildInfoSize = 64 * 1024; const buffer = Buffer.alloc(streamSize !== null && streamSize !== void 0 ? streamSize : elfBuildInfoSize); let bytesWritten = 0; stream.on("end", () => { try { // Discard if (bytesWritten === 0) { return resolve(undefined); } const binaryFile = elf.parse(buffer); const goBuildInfo = binaryFile.body.sections.find((section) => section.name === ".go.buildinfo"); // Could be found in file headers const goBuildId = binaryFile.body.sections.find((section) => section.name === ".note.go.buildid"); if (!goBuildInfo && !goBuildId) { return resolve(undefined); } else if (goBuildInfo) { const info = goBuildInfo.data .slice(0, buildInfoMagic.length) .toString(encoding); if (info === buildInfoMagic) { // to make sure we got a Go binary with module support, we try // reading it. Will throw an error if not. (0, go_binary_1.readRawBuildInfo)(binaryFile); return resolve(binaryFile); } return resolve(undefined); } else if (goBuildId) { const strings = goBuildId.data .toString() .split(/\0+/g) .filter(Boolean); const go = strings[strings.length - 2]; const buildIdParts = strings[strings.length - 1].split(path_1.posix.sep); // Build ID's precise form is actionID/[.../]contentID. // Usually the buildID is simply actionID/contentID, but with exceptions. // https://github.com/golang/go/blob/master/src/cmd/go/internal/work/buildid.go#L23 if (go === buildIdMagic && buildIdParts.length >= 2) { // to make sure we got a Go binary with module support, we try // reading it. Will throw an error if not. (0, go_binary_1.readRawBuildInfo)(binaryFile); return resolve(binaryFile); } return resolve(undefined); } } catch (error) { // catching exception during elf file parse shouldn't fail the archive iteration // it either we recognize file as binary or not return resolve(undefined); } }); stream.on("error", (error) => { reject(error); }); stream.once("data", (chunk) => { const first4Bytes = chunk.toString(encoding, 0, 4); if (first4Bytes === elfHeaderMagic) { Buffer.from(chunk).copy(buffer, bytesWritten, 0); bytesWritten += chunk.length; // Listen to next chunks only if it's an ELF executable stream.addListener("data", (chunk) => { Buffer.from(chunk).copy(buffer, bytesWritten, 0); bytesWritten += chunk.length; }); } }); }); } /** * Build depGraphs for each Go executable * @param filePathToContent */ async function goModulesToScannedProjects(filePathToContent) { const scanResults = []; for (const [filePath, goBinary] of Object.entries(filePathToContent)) { if (event_loop_spinner_1.eventLoopSpinner.isStarving()) { await event_loop_spinner_1.eventLoopSpinner.spin(); } try { const depGraph = await new go_binary_1.GoBinary(goBinary).depGraph(); if (!depGraph) { continue; } const depGraphFact = { type: "depGraph", data: depGraph, }; scanResults.push({ facts: [depGraphFact], identity: { type: exports.DEP_GRAPH_TYPE, // TODO: The path will contain forward slashes on Linux or backslashes on Windows. // So if you scanned the exact same image but from two different machines, // we'd generate two different identities. // These two identities would create two different Projects if monitored... so is this a bug? // If we enforce forward-slashes in every case, would that create duplicate Projects // for existing users who are using the current "backslashes on Windows" behaviour? targetFile: filePath, }, }); } catch (err) { debug(`Go binary scan for file ${filePath} failed: ${err.message}`); } } return scanResults; } exports.goModulesToScannedProjects = goModulesToScannedProjects; //# sourceMappingURL=index.js.map