snyk
Version:
snyk library and cli utility
1,194 lines (1,126 loc) • 4.19 MB
JavaScript
exports.id = 917;
exports.ids = [917];
exports.modules = {
/***/ 68214:
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.formatTestError = void 0;
function formatTestError(error) {
// Possible error cases:
// - the test found some vulns. `error.message` is a
// JSON-stringified
// test result.
// - the flow failed, `error` is a real Error object.
// - the flow failed, `error` is a number or string
// describing the problem.
//
// To standardise this, make sure we use the best _object_ to
// describe the error.
let errorResponse;
if (error instanceof Error) {
errorResponse = error;
}
else if (typeof error !== 'object') {
errorResponse = new Error(error);
}
else {
try {
errorResponse = JSON.parse(error.message);
}
catch (unused) {
errorResponse = error;
}
}
return errorResponse;
}
exports.formatTestError = formatTestError;
/***/ }),
/***/ 22716:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.performanceAnalyticsObject = exports.addIacAnalytics = void 0;
const types_1 = __webpack_require__(42258);
const analytics = __webpack_require__(82744);
const math_utils_1 = __webpack_require__(48537);
const file_utils_1 = __webpack_require__(58486);
function addIacAnalytics(formattedResults, ignoredIssuesCount, isLocalCustomRules) {
let totalIssuesCount = 0;
const customRulesIdsFoundInIssues = {};
let issuesFromCustomRulesCount = 0;
const issuesByType = {};
const packageManagers = Array();
formattedResults.forEach((res) => {
totalIssuesCount =
(totalIssuesCount || 0) + res.result.cloudConfigResults.length;
const packageManagerConfig = res.packageManager;
packageManagers.push(packageManagerConfig);
res.result.cloudConfigResults.forEach((policy) => {
var _a;
issuesByType[packageManagerConfig] = (_a = issuesByType[packageManagerConfig]) !== null && _a !== void 0 ? _a : {};
issuesByType[packageManagerConfig][policy.severity] =
(issuesByType[packageManagerConfig][policy.severity] || 0) + 1;
if (policy.isGeneratedByCustomRule) {
issuesFromCustomRulesCount++;
customRulesIdsFoundInIssues[policy.publicId] = true;
}
});
});
const uniqueCustomRulesCount = Object.keys(customRulesIdsFoundInIssues).length;
analytics.add('packageManager', Array.from(new Set(packageManagers)));
analytics.add('iac-issues-count', totalIssuesCount);
analytics.add('iac-ignored-issues-count', ignoredIssuesCount);
analytics.add('iac-type', issuesByType);
analytics.add('iac-metrics', exports.performanceAnalyticsObject);
analytics.add('iac-test-count', formattedResults.length);
analytics.add('iac-custom-rules-issues-count', issuesFromCustomRulesCount);
analytics.add('iac-custom-rules-issues-percentage', math_utils_1.calculatePercentage(issuesFromCustomRulesCount, totalIssuesCount));
if (!isLocalCustomRules) {
analytics.add('iac-custom-rules-checksum', file_utils_1.computeCustomRulesBundleChecksum());
}
analytics.add('iac-custom-rules-coverage-count', uniqueCustomRulesCount);
}
exports.addIacAnalytics = addIacAnalytics;
exports.performanceAnalyticsObject = {
[types_1.PerformanceAnalyticsKey.InitLocalCache]: null,
[types_1.PerformanceAnalyticsKey.FileLoading]: null,
[types_1.PerformanceAnalyticsKey.FileParsing]: null,
[types_1.PerformanceAnalyticsKey.FileScanning]: null,
[types_1.PerformanceAnalyticsKey.OrgSettings]: null,
[types_1.PerformanceAnalyticsKey.CustomSeverities]: null,
[types_1.PerformanceAnalyticsKey.ResultFormatting]: null,
[types_1.PerformanceAnalyticsKey.UsageTracking]: null,
[types_1.PerformanceAnalyticsKey.CacheCleanup]: null,
[types_1.PerformanceAnalyticsKey.Total]: null,
};
/***/ }),
/***/ 68590:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.assertIaCOptionsFlags = exports.UnsupportedEntitlementFlagError = exports.FlagValueError = exports.FlagError = void 0;
const errors_1 = __webpack_require__(55191);
const args_1 = __webpack_require__(94765);
const error_utils_1 = __webpack_require__(23872);
const types_1 = __webpack_require__(42258);
const keys = [
'org',
'debug',
'insecure',
'detectionDepth',
'severityThreshold',
'rules',
'json',
'sarif',
'json-file-output',
'sarif-file-output',
'v',
'version',
'h',
'help',
'q',
'quiet',
'scan',
'legacy',
// PolicyOptions
'ignore-policy',
'policy-path',
];
const allowed = new Set(keys);
function camelcaseToDash(key) {
return key.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase());
}
function getFlagName(key) {
const dashes = key.length === 1 ? '-' : '--';
const flag = camelcaseToDash(key);
return `${dashes}${flag}`;
}
class FlagError extends errors_1.CustomError {
constructor(key, featureFlag) {
const flag = getFlagName(key);
let msg;
if (featureFlag) {
msg = `Flag "${flag}" is only supported if feature flag '${featureFlag}' is enabled. The feature flag can be enabled via Snyk Preview if you are on the Enterprise Plan`;
}
else {
msg = `Unsupported flag "${flag}" provided. Run snyk iac test --help for supported flags`;
}
super(msg);
this.code = types_1.IaCErrorCodes.FlagError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = msg;
}
}
exports.FlagError = FlagError;
class FlagValueError extends errors_1.CustomError {
constructor(key, value) {
const flag = getFlagName(key);
const msg = `Unsupported value "${value}" provided to flag "${flag}".\nSupported values are: ${SUPPORTED_TF_PLAN_SCAN_MODES.join(', ')}`;
super(msg);
this.code = types_1.IaCErrorCodes.FlagValueError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = msg;
}
}
exports.FlagValueError = FlagValueError;
class UnsupportedEntitlementFlagError extends errors_1.CustomError {
constructor(key, entitlementName) {
const flag = getFlagName(key);
super(`Unsupported flag: ${flag} - Missing the ${entitlementName} entitlement`);
this.code = types_1.IaCErrorCodes.UnsupportedEntitlementFlagError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = `Flag "${flag}" is currently not supported for this org. To enable it, please contact snyk support.`;
}
}
exports.UnsupportedEntitlementFlagError = UnsupportedEntitlementFlagError;
/**
* Validates the command line flags passed to the snyk iac test
* command. The current argument parsing is very permissive and
* allows unknown flags to be provided without valdiation.
*
* For snyk iac we need to explictly validate the flags to avoid
* misconfigurations and typos. For example, if the --experimental
* flag were to be mis-spelled we would end up sending the client
* data to our backend rather than running it locally as intended.
* @param argv command line args passed to the process
*/
function assertIaCOptionsFlags(argv) {
// We process the process.argv so we don't get default values.
const parsed = args_1.args(argv);
for (const key of Object.keys(parsed.options)) {
// The _ property is a special case that contains non
// flag strings passed to the command line (usually files)
// and `iac` is the command provided.
if (key !== '_' && key !== 'iac' && !allowed.has(key)) {
throw new FlagError(key, '');
}
}
if (parsed.options.scan) {
assertTerraformPlanModes(parsed.options.scan);
}
}
exports.assertIaCOptionsFlags = assertIaCOptionsFlags;
const SUPPORTED_TF_PLAN_SCAN_MODES = [
types_1.TerraformPlanScanMode.DeltaScan,
types_1.TerraformPlanScanMode.FullScan,
];
function assertTerraformPlanModes(scanModeArgValue) {
if (!SUPPORTED_TF_PLAN_SCAN_MODES.includes(scanModeArgValue)) {
throw new FlagValueError('scan', scanModeArgValue);
}
}
/***/ }),
/***/ 23872:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getErrorStringCode = void 0;
const types_1 = __webpack_require__(42258);
function getErrorStringCode(code) {
const errorName = types_1.IaCErrorCodes[code];
if (!errorName) {
return 'INVALID_IAC_ERROR';
}
let result = errorName.replace(/([A-Z])/g, '_$1');
if (result.charAt(0) === '_') {
result = result.substring(1);
}
return result.toUpperCase();
}
exports.getErrorStringCode = getErrorStringCode;
/***/ }),
/***/ 26747:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.extractLineNumber = exports.getFileTypeForParser = void 0;
const types_1 = __webpack_require__(42258);
const errors_1 = __webpack_require__(55191);
const cloud_config_parser_1 = __webpack_require__(98611);
const file_parser_1 = __webpack_require__(2314);
const analytics = __webpack_require__(82744);
const Debug = __webpack_require__(15158);
const error_utils_1 = __webpack_require__(23872);
const debug = Debug('iac-extract-line-number');
function getFileTypeForParser(fileType) {
switch (fileType) {
case 'yaml':
case 'yml':
return cloud_config_parser_1.CloudConfigFileTypes.YAML;
case 'json':
return cloud_config_parser_1.CloudConfigFileTypes.JSON;
case 'tf':
return cloud_config_parser_1.CloudConfigFileTypes.TF;
default:
throw new file_parser_1.UnsupportedFileTypeError(fileType);
}
}
exports.getFileTypeForParser = getFileTypeForParser;
function extractLineNumber(cloudConfigPath, fileType, treeByDocId) {
try {
return cloud_config_parser_1.getLineNumber(cloudConfigPath, fileType, treeByDocId);
}
catch (_a) {
const err = new FailedToExtractLineNumberError();
analytics.add('error-code', err.code);
debug('Parser library failed. Could not assign lineNumber to issue');
return -1;
}
}
exports.extractLineNumber = extractLineNumber;
class FailedToExtractLineNumberError extends errors_1.CustomError {
constructor(message) {
super(message || 'Parser library failed. Could not assign lineNumber to issue');
this.code = types_1.IaCErrorCodes.FailedToExtractLineNumberError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = ''; // Not a user facing error.
}
}
/***/ }),
/***/ 13552:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.FailedToLoadFileError = exports.NoFilesToScanError = exports.tryLoadFileData = exports.loadFiles = void 0;
const makeDirectoryIterator_1 = __webpack_require__(87491);
const fs_1 = __webpack_require__(35747);
const types_1 = __webpack_require__(42258);
const iac_parser_1 = __webpack_require__(43458);
const detect_1 = __webpack_require__(45318);
const errors_1 = __webpack_require__(55191);
const error_utils_1 = __webpack_require__(23872);
const DEFAULT_ENCODING = 'utf-8';
async function loadFiles(pathToScan, options = {}) {
let filePaths = [pathToScan];
if (detect_1.isLocalFolder(pathToScan)) {
filePaths = getFilePathsFromDirectory(pathToScan, {
maxDepth: options.detectionDepth,
});
}
const filesToScan = [];
for (const filePath of filePaths) {
try {
const fileData = await tryLoadFileData(filePath);
if (fileData) {
filesToScan.push(fileData);
}
}
catch (e) {
throw new FailedToLoadFileError(filePath);
}
}
if (filesToScan.length === 0) {
throw new NoFilesToScanError();
}
return filesToScan.filter((file) => file.fileContent !== '');
}
exports.loadFiles = loadFiles;
function getFilePathsFromDirectory(pathToScan, options = {}) {
const directoryPaths = makeDirectoryIterator_1.makeDirectoryIterator(pathToScan, {
maxDepth: options.maxDepth,
});
const directoryFilePaths = [];
for (const filePath of directoryPaths) {
directoryFilePaths.push(filePath);
}
return directoryFilePaths;
}
async function tryLoadFileData(pathToScan) {
const fileType = iac_parser_1.getFileType(pathToScan);
if (!types_1.VALID_FILE_TYPES.includes(fileType)) {
return null;
}
const fileContent = (await fs_1.promises.readFile(pathToScan, DEFAULT_ENCODING)).toString();
return {
filePath: pathToScan,
fileType: fileType,
fileContent,
};
}
exports.tryLoadFileData = tryLoadFileData;
class NoFilesToScanError extends errors_1.CustomError {
constructor(message) {
super(message || 'Could not find any valid IaC files');
this.code = types_1.IaCErrorCodes.NoFilesToScanError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage =
'Could not find any valid infrastructure as code files. Supported file extensions are tf, yml, yaml & json.\nMore information can be found by running `snyk iac test --help` or through our documentation:\nhttps://support.snyk.io/hc/en-us/articles/360012429477-Test-your-Kubernetes-files-with-our-CLI-tool\nhttps://support.snyk.io/hc/en-us/articles/360013723877-Test-your-Terraform-files-with-our-CLI-tool';
}
}
exports.NoFilesToScanError = NoFilesToScanError;
class FailedToLoadFileError extends errors_1.CustomError {
constructor(filename) {
super('Failed to load file content');
this.code = types_1.IaCErrorCodes.FailedToLoadFileError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = `We were unable to read file "${filename}" for scanning. Please ensure that it is readable.`;
}
}
exports.FailedToLoadFileError = FailedToLoadFileError;
/***/ }),
/***/ 2314:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.UnsupportedFileTypeError = exports.tryParseIacFile = exports.parseFiles = void 0;
const config_type_detection_1 = __webpack_require__(93669);
const terraform_file_parser_1 = __webpack_require__(29263);
const file_loader_1 = __webpack_require__(13552);
const terraform_plan_parser_1 = __webpack_require__(81893);
const types_1 = __webpack_require__(42258);
const analytics = __webpack_require__(82744);
const errors_1 = __webpack_require__(55191);
const error_utils_1 = __webpack_require__(23872);
const yaml_parser_1 = __webpack_require__(91993);
async function parseFiles(filesData, options = {}) {
const parsedFiles = [];
const failedFiles = [];
for (const fileData of filesData) {
try {
parsedFiles.push(...tryParseIacFile(fileData, options));
}
catch (err) {
if (filesData.length === 1) {
throw err;
}
failedFiles.push(generateFailedParsedFile(fileData, err));
}
}
if (parsedFiles.length === 0) {
throw new file_loader_1.NoFilesToScanError();
}
return {
parsedFiles,
failedFiles,
};
}
exports.parseFiles = parseFiles;
function generateFailedParsedFile({ fileType, filePath, fileContent }, err) {
return {
err,
failureReason: err.message,
fileType,
filePath,
fileContent,
engineType: null,
jsonContent: null,
};
}
function tryParseIacFile(fileData, options = {}) {
analytics.add('iac-terraform-plan', false);
switch (fileData.fileType) {
case 'yaml':
case 'yml': {
const parsedIacFile = yaml_parser_1.parseYAMLOrJSONFileData(fileData);
return config_type_detection_1.detectConfigType(fileData, parsedIacFile);
}
case 'json': {
const parsedIacFile = yaml_parser_1.parseYAMLOrJSONFileData(fileData);
// the Kubernetes file can have more than one JSON object in it
// but the Terraform plan can only have one
if (parsedIacFile.length === 1 && terraform_plan_parser_1.isTerraformPlan(parsedIacFile[0])) {
analytics.add('iac-terraform-plan', true);
return terraform_plan_parser_1.tryParsingTerraformPlan(fileData, parsedIacFile[0], {
isFullScan: options.scan === types_1.TerraformPlanScanMode.FullScan,
});
}
else {
return config_type_detection_1.detectConfigType(fileData, parsedIacFile);
}
}
case 'tf':
return terraform_file_parser_1.tryParsingTerraformFile(fileData);
default:
throw new UnsupportedFileTypeError(fileData.fileType);
}
}
exports.tryParseIacFile = tryParseIacFile;
class UnsupportedFileTypeError extends errors_1.CustomError {
constructor(fileType) {
super('Unsupported file extension');
this.code = types_1.IaCErrorCodes.UnsupportedFileTypeError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = `Unable to process the file with extension ${fileType}. Supported file extensions are tf, yml, yaml & json.\nMore information can be found by running \`snyk iac test --help\` or through our documentation:\nhttps://support.snyk.io/hc/en-us/articles/360012429477-Test-your-Kubernetes-files-with-our-CLI-tool\nhttps://support.snyk.io/hc/en-us/articles/360013723877-Test-your-Terraform-files-with-our-CLI-tool`;
}
}
exports.UnsupportedFileTypeError = UnsupportedFileTypeError;
/***/ }),
/***/ 16879:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.FailedToExecutePolicyEngine = exports.FailedToBuildPolicyEngine = exports.clearPolicyEngineCache = exports.scanFiles = void 0;
const types_1 = __webpack_require__(42258);
const opa_wasm_1 = __webpack_require__(39026);
const fs = __webpack_require__(35747);
const local_cache_1 = __webpack_require__(6255);
const errors_1 = __webpack_require__(55191);
const error_utils_1 = __webpack_require__(23872);
async function scanFiles(parsedFiles) {
// TODO: gracefully handle failed scans
const scanResults = [];
for (const parsedFile of parsedFiles) {
const policyEngine = await getPolicyEngine(parsedFile.engineType);
const result = policyEngine.scanFile(parsedFile);
scanResults.push(result);
}
return scanResults;
}
exports.scanFiles = scanFiles;
async function getPolicyEngine(engineType) {
if (policyEngineCache[engineType]) {
return policyEngineCache[engineType];
}
policyEngineCache[engineType] = await buildPolicyEngine(engineType);
return policyEngineCache[engineType];
}
// used in tests only
function clearPolicyEngineCache() {
policyEngineCache = {
[types_1.EngineType.Kubernetes]: null,
[types_1.EngineType.Terraform]: null,
[types_1.EngineType.CloudFormation]: null,
[types_1.EngineType.ARM]: null,
[types_1.EngineType.Custom]: null,
};
}
exports.clearPolicyEngineCache = clearPolicyEngineCache;
let policyEngineCache = {
[types_1.EngineType.Kubernetes]: null,
[types_1.EngineType.Terraform]: null,
[types_1.EngineType.CloudFormation]: null,
[types_1.EngineType.ARM]: null,
[types_1.EngineType.Custom]: null,
};
async function buildPolicyEngine(engineType) {
const [policyEngineCoreDataPath, policyEngineMetaDataPath,] = local_cache_1.getLocalCachePath(engineType);
try {
const wasmFile = fs.readFileSync(policyEngineCoreDataPath);
const policyMetaData = fs.readFileSync(policyEngineMetaDataPath);
const policyMetadataAsJson = JSON.parse(policyMetaData.toString());
const opaWasmInstance = await opa_wasm_1.loadPolicy(Buffer.from(wasmFile));
opaWasmInstance.setData(policyMetadataAsJson);
return new PolicyEngine(opaWasmInstance);
}
catch (err) {
throw new FailedToBuildPolicyEngine();
}
}
class PolicyEngine {
constructor(opaWasmInstance) {
this.opaWasmInstance = opaWasmInstance;
this.opaWasmInstance = opaWasmInstance;
}
evaluate(data) {
return this.opaWasmInstance.evaluate(data)[0].result;
}
scanFile(iacFile) {
try {
const violatedPolicies = this.evaluate(iacFile.jsonContent);
return {
...iacFile,
violatedPolicies,
};
}
catch (err) {
// TODO: to distinguish between different failure reasons
throw new FailedToExecutePolicyEngine();
}
}
}
class FailedToBuildPolicyEngine extends errors_1.CustomError {
constructor(message) {
super(message || 'Failed to build policy engine');
this.code = types_1.IaCErrorCodes.FailedToBuildPolicyEngine;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage =
'We were unable to run the test. Please run the command again with the `-d` flag and contact support@snyk.io with the contents of the output';
}
}
exports.FailedToBuildPolicyEngine = FailedToBuildPolicyEngine;
class FailedToExecutePolicyEngine extends errors_1.CustomError {
constructor(message) {
super(message || 'Failed to execute policy engine');
this.code = types_1.IaCErrorCodes.FailedToExecutePolicyEngine;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage =
'We were unable to run the test. Please run the command again with the `-d` flag and contact support@snyk.io with the contents of the output';
}
}
exports.FailedToExecutePolicyEngine = FailedToExecutePolicyEngine;
/***/ }),
/***/ 58486:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.computeCustomRulesBundleChecksum = exports.isValidBundle = exports.extractBundle = exports.createIacDir = void 0;
const fs = __webpack_require__(35747);
const tar = __webpack_require__(97998);
const path = __webpack_require__(85622);
const crypto = __webpack_require__(76417);
const local_cache_1 = __webpack_require__(6255);
const oci_pull_1 = __webpack_require__(5029);
function hashData(s) {
const hashedData = crypto
.createHash('sha1')
.update(s)
.digest('hex');
return hashedData;
}
function createIacDir() {
// this path will be able to be customised by the user in the future
const iacPath = path.join(local_cache_1.LOCAL_POLICY_ENGINE_DIR);
try {
if (!fs.existsSync(iacPath)) {
fs.mkdirSync(iacPath, '700');
}
fs.accessSync(iacPath, fs.constants.W_OK);
}
catch (_a) {
throw new local_cache_1.FailedToInitLocalCacheError();
}
}
exports.createIacDir = createIacDir;
function extractBundle(response) {
return new Promise((resolve, reject) => {
response
.on('error', reject)
.pipe(tar.x({
C: path.join(local_cache_1.LOCAL_POLICY_ENGINE_DIR),
}))
.on('finish', resolve)
.on('error', reject);
});
}
exports.extractBundle = extractBundle;
function isValidBundle(wasmPath, dataPath) {
try {
// verify that the correct files were generated, since this is user input
return !(!fs.existsSync(wasmPath) || !fs.existsSync(dataPath));
}
catch (_a) {
return false;
}
}
exports.isValidBundle = isValidBundle;
function computeCustomRulesBundleChecksum() {
try {
const customRulesPolicyWasmPath = path.join(local_cache_1.LOCAL_POLICY_ENGINE_DIR, oci_pull_1.CUSTOM_RULES_TARBALL);
// if bundle is not configured we don't want to include the checksum
if (!fs.existsSync(customRulesPolicyWasmPath)) {
return;
}
const policyWasm = fs.readFileSync(customRulesPolicyWasmPath, 'utf8');
return hashData(policyWasm);
}
catch (err) {
return;
}
}
exports.computeCustomRulesBundleChecksum = computeCustomRulesBundleChecksum;
/***/ }),
/***/ 55409:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.FailedToExecuteCustomRulesError = exports.FailedToPullCustomBundleError = exports.pullIaCCustomRules = exports.removeFileContent = exports.test = void 0;
const detect_1 = __webpack_require__(45318);
const types_1 = __webpack_require__(42258);
const analytics_1 = __webpack_require__(22716);
const usage_tracking_1 = __webpack_require__(43156);
const policy_1 = __webpack_require__(25476);
const measurable_methods_1 = __webpack_require__(78272);
const feature_flags_1 = __webpack_require__(63011);
const assert_iac_options_flag_1 = __webpack_require__(68590);
const user_config_1 = __webpack_require__(28137);
const config_1 = __webpack_require__(22541);
const policy_2 = __webpack_require__(32615);
const errors_1 = __webpack_require__(55191);
const error_utils_1 = __webpack_require__(23872);
const oci_pull_1 = __webpack_require__(5029);
const unsupported_entitlement_error_1 = __webpack_require__(78673);
const url_utils_1 = __webpack_require__(80256);
// this method executes the local processing engine and then formats the results to adapt with the CLI output.
// this flow is the default GA flow for IAC scanning.
async function test(pathToScan, options) {
var _a;
try {
let customRulesPath;
const orgPublicId = (_a = options.org) !== null && _a !== void 0 ? _a : config_1.default.org;
const iacOrgSettings = await measurable_methods_1.getIacOrgSettings(orgPublicId);
if (options.rules) {
await assertRulesFlagAvailable(orgPublicId, iacOrgSettings);
customRulesPath = options.rules;
}
const isOCIRegistryURLProvided = checkOCIRegistryURLProvided(iacOrgSettings);
if (isOCIRegistryURLProvided && customRulesPath) {
throw new FailedToExecuteCustomRulesError();
}
if (isOCIRegistryURLProvided) {
await assertPullAvailable(orgPublicId, iacOrgSettings);
await pullIaCCustomRules(iacOrgSettings);
}
else {
await measurable_methods_1.initLocalCache({ customRulesPath });
}
const policy = await policy_2.findAndLoadPolicy(pathToScan, 'iac', options);
const filesToParse = await measurable_methods_1.loadFiles(pathToScan, options);
const { parsedFiles, failedFiles } = await measurable_methods_1.parseFiles(filesToParse, options);
// Duplicate all the files and run them through the custom engine.
if (customRulesPath || isOCIRegistryURLProvided) {
parsedFiles.push(...parsedFiles.map((file) => ({
...file,
engineType: types_1.EngineType.Custom,
})));
}
const scannedFiles = await measurable_methods_1.scanFiles(parsedFiles);
const resultsWithCustomSeverities = await measurable_methods_1.applyCustomSeverities(scannedFiles, iacOrgSettings.customPolicies);
const formattedResults = measurable_methods_1.formatScanResults(resultsWithCustomSeverities, options, iacOrgSettings.meta);
const { filteredIssues, ignoreCount } = policy_1.filterIgnoredIssues(policy, formattedResults);
try {
await measurable_methods_1.trackUsage(filteredIssues);
}
catch (e) {
if (e instanceof usage_tracking_1.TestLimitReachedError) {
throw e;
}
// If something has gone wrong, err on the side of allowing the user to
// run their tests by squashing the error.
}
analytics_1.addIacAnalytics(filteredIssues, ignoreCount, !!customRulesPath);
// TODO: add support for proper typing of old TestResult interface.
return {
results: filteredIssues,
// NOTE: No file or parsed file data should leave this function.
failures: detect_1.isLocalFolder(pathToScan)
? failedFiles.map(removeFileContent)
: undefined,
};
}
finally {
measurable_methods_1.cleanLocalCache();
}
}
exports.test = test;
function removeFileContent({ filePath, fileType, failureReason, projectType, }) {
return {
filePath,
fileType,
failureReason,
projectType,
};
}
exports.removeFileContent = removeFileContent;
/**
* Checks if the OCI registry URL has been provided.
*/
function checkOCIRegistryURLProvided(iacOrgSettings) {
return (checkOCIRegistryURLExistsInSettings(iacOrgSettings) ||
!!user_config_1.config.get('oci-registry-url'));
}
/**
* Checks if the OCI registry URL was provided in the org's IaC settings.
*/
function checkOCIRegistryURLExistsInSettings(iacOrgSettings) {
var _a, _b;
return (!!((_a = iacOrgSettings.customRules) === null || _a === void 0 ? void 0 : _a.isEnabled) &&
!!((_b = iacOrgSettings.customRules) === null || _b === void 0 ? void 0 : _b.ociRegistryURL));
}
/**
* Extracts the OCI registry URL components from the org's IaC settings.
*/
function getOCIRegistryURLComponentsFromSettings(iacOrgSettings) {
const settingsOCIRegistryURL = iacOrgSettings.customRules.ociRegistryURL;
return {
...oci_pull_1.extractOCIRegistryURLComponents(settingsOCIRegistryURL),
tag: iacOrgSettings.customRules.ociRegistryTag || 'latest',
};
}
/**
* Extracts the OCI registry URL components from the environment variables.
*/
function getOCIRegistryURLComponentsFromEnv() {
const envOCIRegistryURL = user_config_1.config.get('oci-registry-url');
if (!url_utils_1.isValidUrl(envOCIRegistryURL)) {
throw new oci_pull_1.InvalidRemoteRegistryURLError();
}
return oci_pull_1.extractOCIRegistryURLComponents(envOCIRegistryURL);
}
/**
* Gets the OCI registry URL components from either the env variables or the IaC org settings.
*/
function getOCIRegistryURLComponents(iacOrgSettings) {
if (checkOCIRegistryURLExistsInSettings(iacOrgSettings)) {
return getOCIRegistryURLComponentsFromSettings(iacOrgSettings);
}
// Default is to get the URL from env variables.
return getOCIRegistryURLComponentsFromEnv();
}
/**
* Pull and store the IaC custom-rules bundle from the remote OCI Registry.
*/
async function pullIaCCustomRules(iacOrgSettings) {
const ociRegistryURLComponents = getOCIRegistryURLComponents(iacOrgSettings);
const username = user_config_1.config.get('oci-registry-username');
const password = user_config_1.config.get('oci-registry-password');
const opt = {
username,
password,
reqOptions: {
acceptManifest: types_1.manifestContentType,
acceptLayer: types_1.layerContentType,
indexContentType: '',
},
};
try {
await measurable_methods_1.pull(ociRegistryURLComponents, opt);
}
catch (err) {
if (err.statusCode === 401) {
throw new FailedToPullCustomBundleError('There was an authentication error. Incorrect credentials provided.');
}
else if (err.statusCode === 404) {
throw new FailedToPullCustomBundleError('The remote repository could not be found. Please check the provided URL.');
}
else if (err instanceof oci_pull_1.InvalidManifestSchemaVersionError) {
throw new FailedToPullCustomBundleError(err.message);
}
else if (err instanceof oci_pull_1.FailedToBuildOCIArtifactError) {
throw new oci_pull_1.FailedToBuildOCIArtifactError();
}
else if (err instanceof oci_pull_1.InvalidRemoteRegistryURLError) {
throw new oci_pull_1.InvalidRemoteRegistryURLError();
}
else {
throw new FailedToPullCustomBundleError();
}
}
}
exports.pullIaCCustomRules = pullIaCCustomRules;
/**
* Asserts the custom-rules feature is available for the provided org.
*/
async function assertCustomRulesAvailable(orgPublicId, iacOrgSettings) {
var _a;
const isCustomRulesEnabled = !!(await feature_flags_1.isFeatureFlagSupportedForOrg('iacCustomRules', orgPublicId)).ok;
if (!isCustomRulesEnabled) {
throw new errors_1.UnsupportedFeatureFlagError('iacCustomRules');
}
const isEntitledToCustomRules = !!((_a = iacOrgSettings.entitlements) === null || _a === void 0 ? void 0 : _a.iacCustomRulesEntitlement);
if (!isEntitledToCustomRules) {
throw new unsupported_entitlement_error_1.UnsupportedEntitlementError('iacCustomRulesEntitlement');
}
}
/**
* Asserts the --rules flag is available for the provided org.
*/
async function assertRulesFlagAvailable(orgPublicId, iacOrgSettings) {
try {
await assertCustomRulesAvailable(orgPublicId, iacOrgSettings);
}
catch (err) {
if (err instanceof unsupported_entitlement_error_1.UnsupportedEntitlementError) {
throw new assert_iac_options_flag_1.UnsupportedEntitlementFlagError('rules', err.entitlement);
}
else if (err instanceof errors_1.UnsupportedFeatureFlagError) {
throw new assert_iac_options_flag_1.FlagError('rules', err.featureFlag);
}
else {
throw err;
}
}
}
/**
* Asserts the feature custom-rules bundles pulling feature is available for the provided org.
*/
async function assertPullAvailable(orgPublicId, iacOrgSettings) {
try {
await assertCustomRulesAvailable(orgPublicId, iacOrgSettings);
}
catch (err) {
if (err instanceof unsupported_entitlement_error_1.UnsupportedEntitlementError) {
throw new oci_pull_1.UnsupportedEntitlementPullError(err.entitlement);
}
else if (err instanceof errors_1.UnsupportedFeatureFlagError) {
throw new oci_pull_1.UnsupportedFeatureFlagPullError(err.featureFlag);
}
}
}
class FailedToPullCustomBundleError extends errors_1.CustomError {
constructor(message) {
super(message || 'Could not pull custom bundle');
this.code = types_1.IaCErrorCodes.FailedToPullCustomBundleError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = `${message ? message + ' ' : ''}
We were unable to download the custom bundle to the disk. Please ensure access to the remote Registry and validate you have provided all the right parameters.`;
}
}
exports.FailedToPullCustomBundleError = FailedToPullCustomBundleError;
class FailedToExecuteCustomRulesError extends errors_1.CustomError {
constructor(message) {
super(message || 'Could not execute custom rules mode');
this.code = types_1.IaCErrorCodes.FailedToExecuteCustomRulesError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = `
Remote and local custom rules bundle can not be used at the same time.
Please provide a registry URL for the remote bundle, or specify local path location by using the --rules flag for the local bundle.`;
}
}
exports.FailedToExecuteCustomRulesError = FailedToExecuteCustomRulesError;
/***/ }),
/***/ 6255:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.InvalidCustomRulesPath = exports.InvalidCustomRules = exports.FailedToExtractCustomRulesError = exports.FailedToDownloadRulesError = exports.FailedToInitLocalCacheError = exports.cleanLocalCache = exports.initLocalCache = exports.getLocalCachePath = exports.assertNever = exports.CUSTOM_POLICY_ENGINE_WASM_PATH = exports.LOCAL_POLICY_ENGINE_DIR = void 0;
const path = __webpack_require__(85622);
const fs = __webpack_require__(35747);
const types_1 = __webpack_require__(42258);
const needle = __webpack_require__(64484);
const rimraf = __webpack_require__(50984);
const file_utils_1 = __webpack_require__(58486);
const Debug = __webpack_require__(15158);
const errors_1 = __webpack_require__(55191);
const analytics = __webpack_require__(82744);
const error_utils_1 = __webpack_require__(23872);
const debug = Debug('iac-local-cache');
exports.LOCAL_POLICY_ENGINE_DIR = '.iac-data';
const KUBERNETES_POLICY_ENGINE_WASM_PATH = path.join(exports.LOCAL_POLICY_ENGINE_DIR, 'k8s_policy.wasm');
const KUBERNETES_POLICY_ENGINE_DATA_PATH = path.join(exports.LOCAL_POLICY_ENGINE_DIR, 'k8s_data.json');
const TERRAFORM_POLICY_ENGINE_WASM_PATH = path.join(exports.LOCAL_POLICY_ENGINE_DIR, 'tf_policy.wasm');
const TERRAFORM_POLICY_ENGINE_DATA_PATH = path.join(exports.LOCAL_POLICY_ENGINE_DIR, 'tf_data.json');
const CLOUDFORMATION_POLICY_ENGINE_WASM_PATH = path.join(exports.LOCAL_POLICY_ENGINE_DIR, 'cloudformation_policy.wasm');
const CLOUDFORMATION_POLICY_ENGINE_DATA_PATH = path.join(exports.LOCAL_POLICY_ENGINE_DIR, 'cloudformation_data.json');
const ARM_POLICY_ENGINE_WASM_PATH = path.join(exports.LOCAL_POLICY_ENGINE_DIR, 'arm_policy.wasm');
const ARM_POLICY_ENGINE_DATA_PATH = path.join(exports.LOCAL_POLICY_ENGINE_DIR, 'arm_data.json');
// NOTE: The filenames used for the custom policy bundles match those output
// by the `opa` CLI tool, which is why they are very generic.
exports.CUSTOM_POLICY_ENGINE_WASM_PATH = path.join(exports.LOCAL_POLICY_ENGINE_DIR, 'policy.wasm');
const CUSTOM_POLICY_ENGINE_DATA_PATH = path.join(exports.LOCAL_POLICY_ENGINE_DIR, 'data.json');
function assertNever(value) {
throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`);
}
exports.assertNever = assertNever;
function getLocalCachePath(engineType) {
switch (engineType) {
case types_1.EngineType.Kubernetes:
return [
`${process.cwd()}/${KUBERNETES_POLICY_ENGINE_WASM_PATH}`,
`${process.cwd()}/${KUBERNETES_POLICY_ENGINE_DATA_PATH}`,
];
case types_1.EngineType.Terraform:
return [
`${process.cwd()}/${TERRAFORM_POLICY_ENGINE_WASM_PATH}`,
`${process.cwd()}/${TERRAFORM_POLICY_ENGINE_DATA_PATH}`,
];
case types_1.EngineType.CloudFormation:
return [
`${process.cwd()}/${CLOUDFORMATION_POLICY_ENGINE_WASM_PATH}`,
`${process.cwd()}/${CLOUDFORMATION_POLICY_ENGINE_DATA_PATH}`,
];
case types_1.EngineType.ARM:
return [
`${process.cwd()}/${ARM_POLICY_ENGINE_WASM_PATH}`,
`${process.cwd()}/${ARM_POLICY_ENGINE_DATA_PATH}`,
];
case types_1.EngineType.Custom:
return [
`${process.cwd()}/${exports.CUSTOM_POLICY_ENGINE_WASM_PATH}`,
`${process.cwd()}/${CUSTOM_POLICY_ENGINE_DATA_PATH}`,
];
default:
assertNever(engineType);
}
}
exports.getLocalCachePath = getLocalCachePath;
async function initLocalCache({ customRulesPath, } = {}) {
try {
file_utils_1.createIacDir();
}
catch (e) {
throw new FailedToInitLocalCacheError();
}
// Attempt to extract the custom rules from the path provided.
if (customRulesPath) {
if (!fs.existsSync(customRulesPath)) {
throw new InvalidCustomRulesPath(customRulesPath);
}
try {
const response = fs.createReadStream(customRulesPath);
await file_utils_1.extractBundle(response);
}
catch (e) {
throw new FailedToExtractCustomRulesError(customRulesPath);
}
if (!file_utils_1.isValidBundle(exports.CUSTOM_POLICY_ENGINE_WASM_PATH, CUSTOM_POLICY_ENGINE_DATA_PATH)) {
throw new InvalidCustomRules(customRulesPath);
}
}
// We extract the Snyk rules after the custom rules to ensure our files
// always overwrite whatever might be there.
try {
const BUNDLE_URL = 'https://static.snyk.io/cli/wasm/bundle.tar.gz';
const response = needle.get(BUNDLE_URL);
await file_utils_1.extractBundle(response);
}
catch (e) {
throw new FailedToDownloadRulesError();
}
}
exports.initLocalCache = initLocalCache;
function cleanLocalCache() {
// path to delete is hardcoded for now
const iacPath = path.join(`${process.cwd()}`, exports.LOCAL_POLICY_ENGINE_DIR);
try {
// when we support Node version >= 12.10.0 , we can replace rimraf
// with the native fs.rmdirSync(path, {recursive: true})
rimraf.sync(iacPath);
}
catch (e) {
const err = new FailedToCleanLocalCacheError();
analytics.add('error-code', err.code);
debug('The local cache directory could not be deleted');
}
}
exports.cleanLocalCache = cleanLocalCache;
class FailedToInitLocalCacheError extends errors_1.CustomError {
constructor(message) {
super(message || 'Failed to initialize local cache');
this.code = types_1.IaCErrorCodes.FailedToInitLocalCacheError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage =
'We were unable to create a local directory to store the test assets, please ensure that the current working directory is writable';
}
}
exports.FailedToInitLocalCacheError = FailedToInitLocalCacheError;
class FailedToDownloadRulesError extends errors_1.CustomError {
constructor(message) {
super(message || 'Failed to download policies');
this.code = types_1.IaCErrorCodes.FailedToDownloadRulesError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage =
'We were unable to download the security rules, please ensure the network can access https://static.snyk.io';
}
}
exports.FailedToDownloadRulesError = FailedToDownloadRulesError;
class FailedToExtractCustomRulesError extends errors_1.CustomError {
constructor(path, message) {
super(message || 'Failed to download policies');
this.code = types_1.IaCErrorCodes.FailedToExtractCustomRulesError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = `We were unable to extract the rules provided at: ${path}. The provided bundle may be corrupted or invalid. Please ensure it was generated using the 'snyk-iac-rules' SDK`;
}
}
exports.FailedToExtractCustomRulesError = FailedToExtractCustomRulesError;
class InvalidCustomRules extends errors_1.CustomError {
constructor(path, message) {
super(message || 'Invalid custom rules bundle');
this.code = types_1.IaCErrorCodes.InvalidCustomRules;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = `We were unable to extract the rules provided at: ${path}. The provided bundle does not match the required structure. Please ensure it was generated using the 'snyk-iac-rules' SDK`;
}
}
exports.InvalidCustomRules = InvalidCustomRules;
class InvalidCustomRulesPath extends errors_1.CustomError {
constructor(path, message) {
super(message || 'Invalid path to custom rules bundle');
this.code = types_1.IaCErrorCodes.InvalidCustomRulesPath;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = `We were unable to extract the rules provided at: ${path}. The bundle at the provided path does not exist`;
}
}
exports.InvalidCustomRulesPath = InvalidCustomRulesPath;
class FailedToCleanLocalCacheError extends errors_1.CustomError {
constructor(message) {
super(message || 'Failed to clean local cache');
this.code = types_1.IaCErrorCodes.FailedToCleanLocalCacheError;
this.strCode = error_utils_1.getErrorStringCode(this.code);
this.userMessage = ''; // Not a user facing error.
}
}
/***/ }),
/***/ 48537:
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.calculatePercentage = void 0;
/**
* Calculate percentage from relative and total amounts.
* @param relativeValue The relative amount.
* @param totalValue The total amount.
* @returns The calculated precentage.
*/
exports.calculatePercentage = (relativeValue, totalValue) => +(totalValue ? (relativeValue / totalValue) * 100 : 0).toFixed(2);
/***/ }),
/***/ 78272:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.pull = exports.localTest = exports.cleanLocalCache = exports.trackUsage = exports.formatScanResults = exports.applyCustomSeverities = exports.getIacOrgSettings = exports.scanFiles = exports.parseFiles = exports.loadFiles = exports.initLocalCache = exports.performanceAnalyticsDecorator = exports.asyncPerformanceAnalyticsDecorator = void 0;
const file_loader_1 = __webpack_require__(13552);
const file_parser_1 = __webpack_require__(2314);
const file_scanner_1 = __webpack_require__(16879);
const results_formatter_1 = __webpack_require__(98425);
const usage_tracking_1 = __webpack_require__(43156);
const local_cache_1 = __webpack_require__(6255);
const apply_custom_severities_1 = __webpack_require__(91470);
const get_iac_org_settings_1 = __webpack_require__(1802);
const index_1 = __webpack_require__(55409);
const oci_pull_1 = __webpack_require__(5029);
const analytics_1 = __webpack_require__(22716);
const types_1 = __webpack_require__(42258);
// Note: The return type of the returned async function needs to be Promise<Val> for
// the compiler to be happy, so we need to unwrap it with the messy
// Awaiter<ReturnType<T>> rather than just using ReturnType<T> directly.
function asyncPerformanceAnalyticsDecorator(measurableMethod, analyticsKey) {
return async function (...args) {
const startTime = Date.now();
const returnValue = await measurableMethod(...args);
const durationMs = Date.now() - startTime;
analytics_1.performanceAnalyticsObject[analyticsKey] = durationMs;
return returnValue;
};
}
exports.asyncPerformanceAnalyticsDecorator = asyncPerformanceAnalyticsDecorator;
function performanceAnalyticsDecorator(measurableMethod, analyticsKey) {
return function (...args) {
const startTime = Date.now();
const returnValue = measurableMethod(...args);
const durationMs = Date.now() - startTime;
analytics_1.performanceAnalyticsObject[analyticsKey] = durationMs;
return returnValue;
};
}
exports.performanceAnalyticsDecorator = performanceAnalyticsDecorator;
const measurableInitLocalCache = asyncPerformanceAnalyticsDecorator(local_cache_1.initLocalCache, types_1.PerformanceAnalyticsKey.InitLocalCache);
exports.initLocalCache = measurableInitLocalCache;
const measurableLoadFiles = asyncPerformanceAnalyticsDecorator(file_loader_1.loadFiles, types_1.PerformanceAnalyticsKey.FileLoading);
exports.loadFiles = measurableLoadFiles;
const measurableParseFiles = asyncPerformanceAnalyticsDecorator(file_parser_1.parseFiles, types_1.PerformanceAnalyticsKey.FileParsing);
exports.parseFiles = measurableParseFiles;
const measurableScanFiles = asyncPerformanceAnalyticsDecorator(file_scanner_1.scanFiles, types_1.PerformanceAnalyticsKey.FileScanning);
exports.scanFiles = measurableScanFiles;
const measurableGetIacOrgSettings = asyncPerformanceAnalyticsDecorator(get_iac_org_settings_1.getIacOrgSettings, types_1.PerformanceAnalyticsKey.OrgSettings);
exports.getIacOrgSettings = measurableGetIacOrgSettings;
const measurableApplyCustomSeverities = asyncPerformanceAnalyticsDecorator(apply_custom_severities_1.applyCustomSeverities, types_1.PerformanceAnalyticsKey.CustomSeverities);
exports.applyCustomSeverities = measurableApplyCustomSeverities;
const measurableCleanLocalCache = performanceAnalyticsDecorator(local_cache_1.cleanLocalCache, types_1.PerformanceAnalyticsKey.CacheCleanup);
exports.cleanLocalCache = measurableCleanLocalCache;
const measurableFormatScanResults = performanceAnalyticsDecorator(results_formatter_1.formatScanResults, types_1.PerformanceAnalyticsKey.ResultFormatting);
exports.formatScanResults = measurableFormatScanResults;
const measurableTrackUsage = asyncPerformanceAnalyticsDecorator(usage_tracking_1.trackUsage, types_1.PerformanceAnalyticsKey.UsageTracking);
exports.trackUsage = measurableTrackUsage;
const measurableLocalTest = asyncPerformanceAnalyticsDecorator(index_1.test, types_1.PerformanceAnalyticsKey.Total);
exports.localTest = measurableLocalTest;
const measurableOciPull = asyncPerformanceAnalyticsDecorator(oci_pull_1.pull, types_1.PerformanceAnalyticsKey.Total);
exports.pull = measurableOciPull;
/***/ }),
/***/ 5029:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.UnsupportedEntitlementPullError = exports.UnsupportedFeatureFlagPullError = exports.InvalidRemoteRegistryURLError = exports.InvalidManifestSchemaVersionError = exports.FailedToBuildOCIArtifactError = exports.pull = exports.extractOCIRegistryURLComponents = exports.CUSTOM_RULES_TARBALL = void 0;
const registryClient = __webpack_require__(28310);
const fs_1 = __webpack_require__(35747);
const path = __webpa