snyk-docker-plugin
Version:
Snyk CLI docker plugin
227 lines (206 loc) • 6.67 kB
text/typescript
import * as Debug from "debug";
import { DockerFileAnalysis } from "../dockerfile";
import * as archiveExtractor from "../extractor";
import {
getGoModulesContentAction,
goModulesToScannedProjects,
} from "../go-parser";
import { getBufferContent, getElfFileContent, getFileContent } from "../inputs";
import {
getApkDbFileContent,
getApkDbFileContentAction,
} from "../inputs/apk/static";
import {
getAptDbFileContent,
getDpkgFileContentAction,
getExtFileContentAction,
} from "../inputs/apt/static";
import {
getBinariesHashes,
getNodeBinariesFileContentAction,
getOpenJDKBinariesFileContentAction,
} from "../inputs/binaries/static";
import {
getAptFiles,
getDpkgPackageFileContentAction,
} from "../inputs/distroless/static";
import * as filePatternStatic from "../inputs/file-pattern/static";
import { getJarFileContentAction } from "../inputs/java/static";
import { getNodeAppFileContentAction } from "../inputs/node/static";
import { getOsReleaseActions } from "../inputs/os-release/static";
import { getPhpAppFileContentAction } from "../inputs/php/static";
import {
getRpmDbFileContent,
getRpmDbFileContentAction,
getRpmSqliteDbFileContent,
getRpmSqliteDbFileContentAction,
} from "../inputs/rpm/static";
import { isTrue } from "../option-utils";
import { ImageType, ManifestFile, PluginOptions } from "../types";
import {
nodeFilesToScannedProjects,
phpFilesToScannedProjects,
} from "./applications";
import { jarFilesToScannedResults } from "./applications/java";
import { AppDepsScanResultWithoutTarget } from "./applications/types";
import * as osReleaseDetector from "./os-release";
import { analyze as apkAnalyze } from "./package-managers/apk";
import {
analyze as aptAnalyze,
analyzeDistroless as aptDistrolessAnalyze,
} from "./package-managers/apt";
import {
analyze as rpmAnalyze,
mapRpmSqlitePackages,
} from "./package-managers/rpm";
import { ImageAnalysis, OSRelease, StaticAnalysis } from "./types";
const debug = Debug("snyk");
export async function analyze(
targetImage: string,
dockerfileAnalysis: DockerFileAnalysis | undefined,
imageType: ImageType,
imagePath: string,
globsToFind: { include: string[]; exclude: string[] },
options: Partial<PluginOptions>,
): Promise<StaticAnalysis> {
const staticAnalysisActions = [
getApkDbFileContentAction,
getDpkgFileContentAction,
getExtFileContentAction,
getRpmDbFileContentAction,
getRpmSqliteDbFileContentAction,
...getOsReleaseActions,
getNodeBinariesFileContentAction,
getOpenJDKBinariesFileContentAction,
getDpkgPackageFileContentAction,
];
const checkForGlobs = shouldCheckForGlobs(globsToFind);
if (checkForGlobs) {
staticAnalysisActions.push(
filePatternStatic.generateExtractAction(
globsToFind.include,
globsToFind.exclude,
),
);
}
const appScan = !isTrue(options["exclude-app-vulns"]);
if (appScan) {
staticAnalysisActions.push(
...[
getNodeAppFileContentAction,
getPhpAppFileContentAction,
getJarFileContentAction,
getGoModulesContentAction,
],
);
}
const {
imageId,
manifestLayers,
extractedLayers,
rootFsLayers,
autoDetectedUserInstructions,
platform,
imageLabels,
imageCreationTime,
} = await archiveExtractor.extractImageContent(
imageType,
imagePath,
staticAnalysisActions,
);
const [
apkDbFileContent,
aptDbFileContent,
rpmDbFileContent,
rpmSqliteDbFileContent,
] = await Promise.all([
getApkDbFileContent(extractedLayers),
getAptDbFileContent(extractedLayers),
getRpmDbFileContent(extractedLayers),
getRpmSqliteDbFileContent(extractedLayers),
]);
const distrolessAptFiles = getAptFiles(extractedLayers);
const manifestFiles: ManifestFile[] = [];
if (checkForGlobs) {
const matchingFiles = filePatternStatic.getMatchingFiles(extractedLayers);
manifestFiles.push(...matchingFiles);
}
let osRelease: OSRelease;
try {
osRelease = await osReleaseDetector.detectStatically(
extractedLayers,
dockerfileAnalysis,
);
} catch (err) {
debug(`Could not detect OS release: ${JSON.stringify(err)}`);
throw new Error("Failed to detect OS release");
}
let results: ImageAnalysis[];
try {
results = await Promise.all([
apkAnalyze(targetImage, apkDbFileContent),
aptAnalyze(targetImage, aptDbFileContent),
rpmAnalyze(targetImage, rpmDbFileContent),
mapRpmSqlitePackages(targetImage, rpmSqliteDbFileContent),
aptDistrolessAnalyze(targetImage, distrolessAptFiles),
]);
} catch (err) {
debug(`Could not detect installed OS packages: ${JSON.stringify(err)}`);
throw new Error("Failed to detect installed OS packages");
}
const binaries = getBinariesHashes(extractedLayers);
const applicationDependenciesScanResults: AppDepsScanResultWithoutTarget[] = [];
if (appScan) {
const nodeDependenciesScanResults = await nodeFilesToScannedProjects(
getFileContent(extractedLayers, getNodeAppFileContentAction.actionName),
);
const phpDependenciesScanResults = await phpFilesToScannedProjects(
getFileContent(extractedLayers, getPhpAppFileContentAction.actionName),
);
const desiredLevelsOfUnpacking = getNestedJarsDesiredDepth(options);
const jarFingerprintScanResults = await jarFilesToScannedResults(
getBufferContent(extractedLayers, getJarFileContentAction.actionName),
targetImage,
desiredLevelsOfUnpacking,
);
const goModulesScanResult = await goModulesToScannedProjects(
getElfFileContent(extractedLayers, getGoModulesContentAction.actionName),
);
applicationDependenciesScanResults.push(
...nodeDependenciesScanResults,
...phpDependenciesScanResults,
...jarFingerprintScanResults,
...goModulesScanResult,
);
}
return {
imageId,
osRelease,
platform,
results,
binaries,
imageLayers: manifestLayers,
rootFsLayers,
applicationDependenciesScanResults,
manifestFiles,
autoDetectedUserInstructions,
imageLabels,
imageCreationTime,
};
}
function getNestedJarsDesiredDepth(options: Partial<PluginOptions>) {
const nestedJarsOption =
options["nested-jars-depth"] || options["shaded-jars-depth"];
let nestedJarsDepth = 1;
const depthNumber = Number(nestedJarsOption);
if (!isNaN(depthNumber) && depthNumber >= 0) {
nestedJarsDepth = depthNumber;
}
return nestedJarsDepth;
}
function shouldCheckForGlobs(globsToFind: {
include: string[];
exclude: string[];
}): boolean {
return globsToFind.include.length > 0;
}