UNPKG

snyk

Version:

snyk library and cli utility

1,194 lines (1,126 loc) • 4.19 MB
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