diff --git a/src/lib/iac/test/v2/json-old.ts b/src/lib/iac/test/v2/json-old.ts new file mode 100644 index 0000000000..71bc746b94 --- /dev/null +++ b/src/lib/iac/test/v2/json-old.ts @@ -0,0 +1,313 @@ +// Some of the types below specify fields constrained to a single value. Those +// fields must be produced in the JSON output, and they must have those values +// to keep backwards compatibility. + +import { Resource, TestOutput, Vulnerability } from './scan/results'; +import * as path from 'path'; +import { IacProjectType, iacRemediationTypes } from '../../constants'; +import { State } from './scan/policy-engine'; +import { + IacTestError, + mapIacTestError, +} from '../../../snyk-test/iac-test-result'; + +export interface Result { + meta: Meta; + filesystemPolicy: false; + vulnerabilities: []; + dependencyCount: 0; + licensesPolicy: null; + ignoreSettings: IgnoreSettings; + targetFile: string; + projectName: string; + org: string; + policy: string; + isPrivate: boolean; + targetFilePath: string; + packageManager: IacProjectType | State.InputTypeEnum; + path: string; + projectType: IacProjectType | State.InputTypeEnum; + ok: boolean; + infrastructureAsCodeIssues: IacIssue[]; + error?: string; +} + +export interface IgnoreSettings { + adminOnly: boolean; + reasonRequired: boolean; + disregardFilesystemIgnores: boolean; +} + +export interface Meta { + isPrivate: boolean; + isLicensesEnabled: boolean; + ignoreSettings: IgnoreSettings; + org: string; + policy: string; +} + +export interface IgnoreSettings { + adminOnly: boolean; + reasonRequired: boolean; + disregardFilesystemIgnores: boolean; +} + +export interface IacIssue { + severity: string; + resolve: string; + impact: string; + msg: string; + remediation?: Remediation; + subType: string; + issue: string; + publicId: string; + title: string; + references: string[]; + id: string; + isIgnored: boolean; + iacDescription: IacDescription; + lineNumber: number; + documentation?: string; + isGeneratedByCustomRule: boolean; + path: string[]; + policyEngineType?: string; + type?: IacProjectType | State.InputTypeEnum; + compliance?: string[][]; + description: string; +} + +export interface Remediation { + cloudformation?: string; + terraform?: string; + arm?: string; + kubernetes?: string; +} + +export interface IacDescription { + issue: string; + impact: string; + resolve: string; +} + +export function convertEngineToJsonResults({ + results, + projectName, +}: { + results: TestOutput; + projectName: string; +}): Array { + const vulnerabilityGroups = groupVulnerabilitiesByFile(results); // all vulns groups by file + const resourceGroups = groupResourcesByFile(results); // all resources grouped by file + const filesWithoutIssues = findFilesWithoutIssues( + resourceGroups, + vulnerabilityGroups, + ); // all resources without issues grouped by file + + const output: Array = []; + + if (results.errors) { + output.push(...results.errors.map((e) => mapIacTestError(e))); + } + + for (const [file, resources] of Object.entries(filesWithoutIssues)) { + output.push(resourcesToResult(results, projectName, file, resources)); + } + + for (const [file, vulnerabilities] of Object.entries(vulnerabilityGroups)) { + output.push( + vulnerabilitiesToResult(results, projectName, file, vulnerabilities), + ); + } + + return output; +} + +function groupResourcesByFile(results: TestOutput) { + const groups: Record = {}; + + if (results.results?.resources) { + for (const resource of results.results.resources) { + if (resource.file) { + const resources = groups[resource.file] || []; + resources.push(resource); + groups[resource.file] = resources; + } + } + } + + return groups; +} + +function groupVulnerabilitiesByFile(results: TestOutput) { + const groups: Record = {}; + + if (results.results?.vulnerabilities) { + for (const vulnerability of results.results.vulnerabilities) { + if (vulnerability.resource.file) { + const vulnerabilities = groups[vulnerability.resource.file] || []; + vulnerabilities.push(vulnerability); + groups[vulnerability.resource.file] = vulnerabilities; + } + } + } + + return groups; +} + +function findFilesWithoutIssues( + resourceGroups: Record, + vulnerabilityGroups: Record, +) { + const groups: Record = {}; + + for (const [file, resources] of Object.entries(resourceGroups)) { + if (!(file in vulnerabilityGroups)) { + groups[file] = resources; + } + } + + return groups; +} + +function resourcesToResult( + testOutput: TestOutput, + projectName: string, + file: string, + resources: Resource[], +): Result { + const kind = resourcesToKind(resources); + const ignoreSettings = testOutput.settings.ignoreSettings; + const meta = orgSettingsToMeta(testOutput, ignoreSettings); + + return { + meta, + filesystemPolicy: false, + vulnerabilities: [], + dependencyCount: 0, + licensesPolicy: null, + ignoreSettings, + targetFile: file, + projectName, + org: testOutput.settings.org, + policy: '', + isPrivate: true, + targetFilePath: path.resolve(file), + packageManager: kind, + path: process.cwd(), + projectType: kind, + ok: true, + infrastructureAsCodeIssues: [], + }; +} + +function vulnerabilitiesToResult( + testOutput: TestOutput, + projectName: string, + file: string, + vulnerabilities: Vulnerability[], +): Result { + const kind = vulnerabilitiesToKind(vulnerabilities); + const ignoreSettings = testOutput.settings.ignoreSettings; + const meta = orgSettingsToMeta(testOutput, ignoreSettings); + const infrastructureAsCodeIssues = vulnerabilitiesToIacIssues( + vulnerabilities, + ); + + return { + meta, + filesystemPolicy: false, + vulnerabilities: [], + dependencyCount: 0, + licensesPolicy: null, + ignoreSettings, + targetFile: file, + projectName, + org: testOutput.settings.org, + policy: '', + isPrivate: true, + targetFilePath: path.resolve(file), + packageManager: kind, + path: process.cwd(), + projectType: kind, + ok: false, + infrastructureAsCodeIssues, + }; +} + +function vulnerabilitiesToIacIssues( + vulnerabilities: Vulnerability[], +): IacIssue[] { + return vulnerabilities.map((v) => { + const resolve = extractResolve(v); + + return { + severity: v.severity, + resolve, + impact: v.rule.description, + msg: v.resource.formattedPath, + remediation: { + [iacRemediationTypes[v.resource.kind] as string]: resolve, + }, + type: v.resource.kind, + subType: v.resource.type, + issue: v.rule.title, + publicId: v.rule.id, + title: v.rule.title, + references: v.rule.references ? [v.rule.references] : [], // TODO: `references` expects a list of URLs, but `v.references` is a markdown string with URLs. When makrdown parsing is added, extract the URLs in `v.references` + id: v.rule.id, + isIgnored: v.ignored, + iacDescription: { + issue: v.rule.title, + impact: v.rule.description, + resolve, + }, + lineNumber: v.resource.line || -1, + documentation: v.rule.documentation, // only works for rules available on snyk.io + isGeneratedByCustomRule: !!v.rule.isGeneratedByCustomRule, + path: v?.resource?.formattedPath.split('.') || [], + compliance: [], + description: v.rule.description, + }; + }); +} + +function extractResolve(vulnerability: Vulnerability): string { + const newLineIdx = vulnerability.remediation.search(/\r?\n|\r/g); + return newLineIdx < 0 + ? vulnerability.remediation + : vulnerability.remediation.substring(0, newLineIdx); +} + +// TODO: add correct mapping to our packageManger name (will probably be done in `snyk-iac-test`) +function resourcesToKind( + resources: Resource[], +): IacProjectType | State.InputTypeEnum { + for (const r of resources) { + return r.kind; + } + return '' as IacProjectType | State.InputTypeEnum; +} + +function vulnerabilitiesToKind( + vulnerabilities: Vulnerability[], +): IacProjectType | State.InputTypeEnum { + for (const v of vulnerabilities) { + return v.resource.kind; + } + return '' as IacProjectType | State.InputTypeEnum; +} + +function orgSettingsToMeta( + testOutput: TestOutput, + ignoreSettings: IgnoreSettings, +): Meta { + const org = testOutput.settings.org; + + return { + isPrivate: true, + isLicensesEnabled: false, + org, + policy: '', + ignoreSettings, + }; +} diff --git a/src/lib/iac/test/v2/json.ts b/src/lib/iac/test/v2/json.ts index 71bc746b94..5724b1e3d3 100644 --- a/src/lib/iac/test/v2/json.ts +++ b/src/lib/iac/test/v2/json.ts @@ -29,6 +29,7 @@ export interface Result { projectType: IacProjectType | State.InputTypeEnum; ok: boolean; infrastructureAsCodeIssues: IacIssue[]; + infrastructureAsCodeSuccesses?: IacSuccess[]; error?: string; } @@ -75,6 +76,15 @@ export interface IacIssue { compliance?: string[][]; description: string; } +export interface IacSuccess { + id: string; + severity: string; + type: IacProjectType | State.InputTypeEnum; + subType: string; + path: string[]; + msg: string; + isIgnored: boolean; +} export interface Remediation { cloudformation?: string; @@ -113,9 +123,18 @@ export function convertEngineToJsonResults({ output.push(resourcesToResult(results, projectName, file, resources)); } - for (const [file, vulnerabilities] of Object.entries(vulnerabilityGroups)) { + for (const [ + file, + { vulnerabilities, passedVulnerabilities }, + ] of Object.entries(vulnerabilityGroups)) { output.push( - vulnerabilitiesToResult(results, projectName, file, vulnerabilities), + vulnerabilitiesToResult( + results, + projectName, + file, + vulnerabilities, + passedVulnerabilities, + ), ); } @@ -139,14 +158,36 @@ function groupResourcesByFile(results: TestOutput) { } function groupVulnerabilitiesByFile(results: TestOutput) { - const groups: Record = {}; + const groups: Record< + string, + { + vulnerabilities: Vulnerability[]; + passedVulnerabilities: Vulnerability[]; + } + > = {}; if (results.results?.vulnerabilities) { for (const vulnerability of results.results.vulnerabilities) { if (vulnerability.resource.file) { - const vulnerabilities = groups[vulnerability.resource.file] || []; - vulnerabilities.push(vulnerability); - groups[vulnerability.resource.file] = vulnerabilities; + const vulnerabilityInfo = groups[vulnerability.resource.file] || { + vulnerabilities: [], + passedVulnerabilities: [], + }; + vulnerabilityInfo.vulnerabilities.push(vulnerability); + groups[vulnerability.resource.file] = vulnerabilityInfo; + } + } + } + + if (results.results?.passedVulnerabilities) { + for (const passedVulnerability of results.results.passedVulnerabilities) { + if (passedVulnerability.resource.file) { + const vulnerabilityInfo = groups[passedVulnerability.resource.file] || { + vulnerabilities: [], + passedVulnerabilities: [], + }; + vulnerabilityInfo.passedVulnerabilities.push(passedVulnerability); + groups[passedVulnerability.resource.file] = vulnerabilityInfo; } } } @@ -156,7 +197,7 @@ function groupVulnerabilitiesByFile(results: TestOutput) { function findFilesWithoutIssues( resourceGroups: Record, - vulnerabilityGroups: Record, + vulnerabilityGroups: Record, ) { const groups: Record = {}; @@ -197,6 +238,7 @@ function resourcesToResult( projectType: kind, ok: true, infrastructureAsCodeIssues: [], + infrastructureAsCodeSuccesses: [], }; } @@ -205,13 +247,19 @@ function vulnerabilitiesToResult( projectName: string, file: string, vulnerabilities: Vulnerability[], + passedVulnerabilities: Vulnerability[], ): Result { - const kind = vulnerabilitiesToKind(vulnerabilities); + const kind = + vulnerabilitiesToKind(vulnerabilities) || + vulnerabilitiesToKind(passedVulnerabilities); const ignoreSettings = testOutput.settings.ignoreSettings; const meta = orgSettingsToMeta(testOutput, ignoreSettings); const infrastructureAsCodeIssues = vulnerabilitiesToIacIssues( vulnerabilities, ); + const infrastructureAsCodeSuccesses = passedVulnerabilitiesToIacSuccesses( + passedVulnerabilities, + ); return { meta, @@ -229,8 +277,9 @@ function vulnerabilitiesToResult( packageManager: kind, path: process.cwd(), projectType: kind, - ok: false, + ok: infrastructureAsCodeIssues.length === 0, infrastructureAsCodeIssues, + infrastructureAsCodeSuccesses, }; } @@ -271,6 +320,23 @@ function vulnerabilitiesToIacIssues( }); } +function passedVulnerabilitiesToIacSuccesses( + vulnerabilities: Vulnerability[], +): IacSuccess[] { + return vulnerabilities.map((v) => { + return { + id: v.rule.id, + severity: v.severity, + type: v.resource.kind, + subType: v.resource.type, + path: v?.resource?.formattedPath.split('.') || [], + // IAC-2962: This field is included in IacIssue, so adding it here as well + msg: v.resource.formattedPath, + isIgnored: v.ignored, + }; + }); +} + function extractResolve(vulnerability: Vulnerability): string { const newLineIdx = vulnerability.remediation.search(/\r?\n|\r/g); return newLineIdx < 0 diff --git a/src/lib/iac/test/v2/local-cache/policy-engine/constants/utils.ts b/src/lib/iac/test/v2/local-cache/policy-engine/constants/utils.ts index 4ae18c75a0..4476a28cc0 100644 --- a/src/lib/iac/test/v2/local-cache/policy-engine/constants/utils.ts +++ b/src/lib/iac/test/v2/local-cache/policy-engine/constants/utils.ts @@ -1,11 +1,11 @@ import * as os from 'os'; const policyEngineChecksums = ` -44bc8c547e26f0960b95333a273fc6f7154f4919d51f2ee323c4133e29f322fc snyk-iac-test_0.51.3_Linux_arm64 -955d9416588379dff0ca128d4a26675a5e9575f6bd702fceeab58224f355d007 snyk-iac-test_0.51.3_Darwin_arm64 -9bb87bff18af15fd50a1e518f78d649373a607a0a3d09e2d88c420649fae020f snyk-iac-test_0.51.3_Windows_x86_64.exe -a2d91e709a8908a0df56fd0d05a029ecc75cbcfef6b2c5b80a621472a552952c snyk-iac-test_0.51.3_Linux_x86_64 -de5676c97e9f9080b2ebcc87bce2bcc9dc17961eadb81816489ea7130ab73ebc snyk-iac-test_0.51.3_Darwin_x86_64 +0438c67095e46c8560c302c10588df2ea4a643bf14000f2dfc672d8a7be732de snyk-iac-test_0.53.0_Windows_x86_64.exe +0a915fdaffb003d7595efe0e14f65813ea09d01d9425d8546b8a12ec81602088 snyk-iac-test_0.53.0_Linux_x86_64 +11106c5317f324538fa6881b254885bfe02f0efc30213415887e9224fc38ec9d snyk-iac-test_0.53.0_Darwin_x86_64 +3d286b4328e54387311f317ad3055b0ebb8dbcbc2e3694996699d15da8b8cbd3 snyk-iac-test_0.53.0_Linux_arm64 +47e364f3889ee8cee588045190b457b817b5d391d74978dbb5ab23e9e5e79c46 snyk-iac-test_0.53.0_Darwin_arm64 `; export const policyEngineVersion = getPolicyEngineVersion(); diff --git a/src/lib/iac/test/v2/output.ts b/src/lib/iac/test/v2/output.ts index e6463e92c3..431fff7f63 100644 --- a/src/lib/iac/test/v2/output.ts +++ b/src/lib/iac/test/v2/output.ts @@ -1,8 +1,9 @@ import { Ora } from 'ora'; import { EOL } from 'os'; import { convertEngineToJsonResults } from './json'; +import { convertEngineToJsonResults as oldConvertEngineToJsonResults } from './json-old'; import { TestOutput } from './scan/results'; - +import { previewFeaturesEnabled } from '../../../preview-features-enabled'; import { TestCommandResult } from '../../../../cli/commands/types'; import { formatIacTestFailures, @@ -83,10 +84,15 @@ function buildTestCommandResultData({ scanResult.results?.metadata?.projectName ?? path.basename(process.cwd()); const jsonData = jsonStringifyLargeObject( - convertEngineToJsonResults({ - results: scanResult, - projectName, - }), + previewFeaturesEnabled() + ? convertEngineToJsonResults({ + results: scanResult, + projectName, + }) + : oldConvertEngineToJsonResults({ + results: scanResult, + projectName, + }), ); const sarifData = jsonStringifyLargeObject( diff --git a/src/lib/iac/test/v2/scan/index.ts b/src/lib/iac/test/v2/scan/index.ts index ae1db82dc3..362747b5f6 100644 --- a/src/lib/iac/test/v2/scan/index.ts +++ b/src/lib/iac/test/v2/scan/index.ts @@ -13,6 +13,7 @@ import config from '../../../../config'; import { api, getOAuthToken } from '../../../../api-token'; import envPaths from 'env-paths'; import { restoreEnvProxy } from '../../../env-utils'; +import { previewFeaturesEnabled } from '../../../../preview-features-enabled'; const debug = newDebug('snyk-iac'); const debugOutput = newDebug('snyk-iac:output'); @@ -124,6 +125,14 @@ function processFlags( flags.push('-output', outputPath); + // TODO: IAC-2962 - Remove condition after the feature is GA + if (previewFeaturesEnabled()) { + // to reduce the size of the output + flags.push('-exclude-raw-results'); + // required for infrastructureAsCodeSuccesses to be populated + flags.push('-include-passed-vulnerabilities'); + } + if (options.severityThreshold) { flags.push('-severity-threshold', options.severityThreshold); } diff --git a/src/lib/iac/test/v2/scan/results.ts b/src/lib/iac/test/v2/scan/results.ts index 7bcc43f559..ee614d40e4 100644 --- a/src/lib/iac/test/v2/scan/results.ts +++ b/src/lib/iac/test/v2/scan/results.ts @@ -64,6 +64,7 @@ export interface SnykIacTestOutput { export interface Results { resources?: Resource[]; vulnerabilities?: Vulnerability[]; + passedVulnerabilities?: Vulnerability[]; metadata: Metadata; scanAnalytics: ScanAnalytics; } diff --git a/test/jest/unit/iac/process-results/fixtures/integrated-json-output-v2.json b/test/jest/unit/iac/process-results/fixtures/integrated-json-output-v2.json new file mode 100644 index 0000000000..803373849e --- /dev/null +++ b/test/jest/unit/iac/process-results/fixtures/integrated-json-output-v2.json @@ -0,0 +1,330 @@ +[ + { + "meta": { + "isPrivate": true, + "isLicensesEnabled": false, + "org": "org-name", + "policy": "", + "ignoreSettings": { + "adminOnly": false, + "disregardFilesystemIgnores": false, + "reasonRequired": false + } + }, + "filesystemPolicy": false, + "vulnerabilities": [], + "dependencyCount": 0, + "licensesPolicy": null, + "ignoreSettings": { + "adminOnly": false, + "disregardFilesystemIgnores": false, + "reasonRequired": false + }, + "targetFile": "no_rules_detected.json", + "projectName": "org-name", + "org": "org-name", + "policy": "", + "isPrivate": true, + "targetFilePath": "/Users/sergiub/snyk/cli/no_rules_detected.json", + "packageManager": "terraformconfig", + "path": "/Users/sergiub/snyk/cli", + "projectType": "terraformconfig", + "ok": true, + "infrastructureAsCodeIssues": [], + "infrastructureAsCodeSuccesses": [] + }, + { + "meta": { + "isPrivate": true, + "isLicensesEnabled": false, + "org": "org-name", + "policy": "", + "ignoreSettings": { + "adminOnly": false, + "disregardFilesystemIgnores": false, + "reasonRequired": false + } + }, + "filesystemPolicy": false, + "vulnerabilities": [], + "dependencyCount": 0, + "licensesPolicy": null, + "ignoreSettings": { + "adminOnly": false, + "disregardFilesystemIgnores": false, + "reasonRequired": false + }, + "targetFile": "plan.json", + "projectName": "org-name", + "org": "org-name", + "policy": "", + "isPrivate": true, + "targetFilePath": "/Users/sergiub/snyk/cli/plan.json", + "packageManager": "terraformconfig", + "path": "/Users/sergiub/snyk/cli", + "projectType": "terraformconfig", + "ok": false, + "infrastructureAsCodeIssues": [ + { + "severity": "medium", + "resolve": "Remove any invalid `ingress` block from the `aws_security_group` or `aws_default_security_group`.", + "impact": "Configuring all VPC default security groups to restrict all inbound traffic encourages least privilege security group development and mindful placement of AWS resources into security groups, which in turn reduces the exposure of those resources.", + "msg": "resource.aws_default_security_group[default].ingress[0].cidr_blocks[0]", + "remediation": { + "terraform": "Remove any invalid `ingress` block from the `aws_security_group` or `aws_default_security_group`." + }, + "type": "terraformconfig", + "subType": "aws_default_security_group", + "issue": "VPC default security group allows unrestricted ingress traffic", + "publicId": "SNYK-CC-00024", + "title": "VPC default security group allows unrestricted ingress traffic", + "references": [ + "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html" + ], + "id": "SNYK-CC-00024", + "isIgnored": false, + "iacDescription": { + "issue": "VPC default security group allows unrestricted ingress traffic", + "impact": "Configuring all VPC default security groups to restrict all inbound traffic encourages least privilege security group development and mindful placement of AWS resources into security groups, which in turn reduces the exposure of those resources.", + "resolve": "Remove any invalid `ingress` block from the `aws_security_group` or `aws_default_security_group`." + }, + "lineNumber": -1, + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00024", + "isGeneratedByCustomRule": false, + "path": [ + "resource", + "aws_default_security_group[default]", + "ingress[0]", + "cidr_blocks[0]" + ], + "compliance": [], + "description": "Configuring all VPC default security groups to restrict all inbound traffic encourages least privilege security group development and mindful placement of AWS resources into security groups, which in turn reduces the exposure of those resources." + }, + { + "severity": "medium", + "resolve": "Reference the `aws_vpc` in an `aws_flog_log` `vpc_id` field.", + "impact": "VPC Flow Logs provide visibility into network traffic that traverses the AWS VPC. Users can use the flow logs to detect anomalous traffic or insight during security workflows.", + "msg": "resource.aws_vpc[mainvpc]", + "remediation": { + "terraform": "Reference the `aws_vpc` in an `aws_flog_log` `vpc_id` field." + }, + "type": "terraformconfig", + "subType": "aws_vpc", + "issue": "VPC flow logging is not enabled", + "publicId": "SNYK-CC-00151", + "title": "VPC flow logging is not enabled", + "references": [ + "https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-cwl.html" + ], + "id": "SNYK-CC-00151", + "isIgnored": false, + "iacDescription": { + "issue": "VPC flow logging is not enabled", + "impact": "VPC Flow Logs provide visibility into network traffic that traverses the AWS VPC. Users can use the flow logs to detect anomalous traffic or insight during security workflows.", + "resolve": "Reference the `aws_vpc` in an `aws_flog_log` `vpc_id` field." + }, + "lineNumber": -1, + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00151", + "isGeneratedByCustomRule": false, + "path": [ + "resource", + "aws_vpc[mainvpc]" + ], + "compliance": [], + "description": "VPC Flow Logs provide visibility into network traffic that traverses the AWS VPC. Users can use the flow logs to detect anomalous traffic or insight during security workflows." + } + ], + "infrastructureAsCodeSuccesses": [ + { + "id": "SNYK-CC-00328", + "severity": "medium", + "type": "terraformconfig", + "subType": "aws_default_security_group", + "path": [ + "resource", + "aws_default_security_group[default]", + "egress" + ], + "msg": "resource.aws_default_security_group[default].egress", + "isIgnored": false + } + ] + }, + { + "meta": { + "isPrivate": true, + "isLicensesEnabled": false, + "org": "org-name", + "policy": "", + "ignoreSettings": { + "adminOnly": false, + "disregardFilesystemIgnores": false, + "reasonRequired": false + } + }, + "filesystemPolicy": false, + "vulnerabilities": [], + "dependencyCount": 0, + "licensesPolicy": null, + "ignoreSettings": { + "adminOnly": false, + "disregardFilesystemIgnores": false, + "reasonRequired": false + }, + "targetFile": "vpc_group.tf", + "projectName": "org-name", + "org": "org-name", + "policy": "", + "isPrivate": true, + "targetFilePath": "/Users/sergiub/snyk/cli/vpc_group.tf", + "packageManager": "terraformconfig", + "path": "/Users/sergiub/snyk/cli", + "projectType": "terraformconfig", + "ok": false, + "infrastructureAsCodeIssues": [ + { + "severity": "medium", + "resolve": "Remove any invalid `ingress` block from the `aws_security_group` or `aws_default_security_group`.", + "impact": "Configuring all VPC default security groups to restrict all inbound traffic encourages least privilege security group development and mindful placement of AWS resources into security groups, which in turn reduces the exposure of those resources.", + "msg": "resource.aws_default_security_group[default].ingress[0].cidr_blocks[0]", + "remediation": { + "terraform": "Remove any invalid `ingress` block from the `aws_security_group` or `aws_default_security_group`." + }, + "type": "terraformconfig", + "subType": "aws_default_security_group", + "issue": "VPC default security group allows unrestricted ingress traffic", + "publicId": "SNYK-CC-00024", + "title": "VPC default security group allows unrestricted ingress traffic", + "references": [ + "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html" + ], + "id": "SNYK-CC-00024", + "isIgnored": false, + "iacDescription": { + "issue": "VPC default security group allows unrestricted ingress traffic", + "impact": "Configuring all VPC default security groups to restrict all inbound traffic encourages least privilege security group development and mindful placement of AWS resources into security groups, which in turn reduces the exposure of those resources.", + "resolve": "Remove any invalid `ingress` block from the `aws_security_group` or `aws_default_security_group`." + }, + "lineNumber": 16, + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00024", + "isGeneratedByCustomRule": false, + "path": [ + "resource", + "aws_default_security_group[default]", + "ingress[0]", + "cidr_blocks[0]" + ], + "compliance": [], + "description": "Configuring all VPC default security groups to restrict all inbound traffic encourages least privilege security group development and mindful placement of AWS resources into security groups, which in turn reduces the exposure of those resources." + }, + { + "severity": "medium", + "resolve": "Reference the `aws_vpc` in an `aws_flog_log` `vpc_id` field.", + "impact": "VPC Flow Logs provide visibility into network traffic that traverses the AWS VPC. Users can use the flow logs to detect anomalous traffic or insight during security workflows.", + "msg": "resource.aws_vpc[mainvpc]", + "remediation": { + "terraform": "Reference the `aws_vpc` in an `aws_flog_log` `vpc_id` field." + }, + "type": "terraformconfig", + "subType": "aws_vpc", + "issue": "VPC flow logging is not enabled", + "publicId": "SNYK-CC-00151", + "title": "VPC flow logging is not enabled", + "references": [ + "https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-cwl.html" + ], + "id": "SNYK-CC-00151", + "isIgnored": false, + "iacDescription": { + "issue": "VPC flow logging is not enabled", + "impact": "VPC Flow Logs provide visibility into network traffic that traverses the AWS VPC. Users can use the flow logs to detect anomalous traffic or insight during security workflows.", + "resolve": "Reference the `aws_vpc` in an `aws_flog_log` `vpc_id` field." + }, + "lineNumber": 5, + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00151", + "isGeneratedByCustomRule": false, + "path": [ + "resource", + "aws_vpc[mainvpc]" + ], + "compliance": [], + "description": "VPC Flow Logs provide visibility into network traffic that traverses the AWS VPC. Users can use the flow logs to detect anomalous traffic or insight during security workflows." + } + ], + "infrastructureAsCodeSuccesses": [ + { + "id": "SNYK-CC-00328", + "severity": "medium", + "type": "terraformconfig", + "subType": "aws_default_security_group", + "path": [ + "resource", + "aws_default_security_group[default]", + "egress" + ], + "msg": "resource.aws_default_security_group[default].egress", + "isIgnored": false + }, + { + "id": "SNYK-CC-00707", + "severity": "high", + "type": "terraformconfig", + "subType": "aws_vpc", + "path": [ + "resource", + "aws_vpc[mainvpc]", + "cluster_security_groups" + ], + "msg": "resource.aws_vpc[mainvpc].cluster_security_groups", + "isIgnored": false + } + ] + }, + { + "meta": { + "isPrivate": true, + "isLicensesEnabled": false, + "org": "org-name", + "policy": "", + "ignoreSettings": { + "adminOnly": false, + "disregardFilesystemIgnores": false, + "reasonRequired": false + } + }, + "filesystemPolicy": false, + "vulnerabilities": [], + "dependencyCount": 0, + "licensesPolicy": null, + "ignoreSettings": { + "adminOnly": false, + "disregardFilesystemIgnores": false, + "reasonRequired": false + }, + "targetFile": "plan_no_vulns.json", + "projectName": "org-name", + "org": "org-name", + "policy": "", + "isPrivate": true, + "targetFilePath": "/Users/sergiub/snyk/cli/plan_no_vulns.json", + "packageManager": "terraformconfig", + "path": "/Users/sergiub/snyk/cli", + "projectType": "terraformconfig", + "ok": true, + "infrastructureAsCodeIssues": [], + "infrastructureAsCodeSuccesses": [ + { + "id": "SNYK-CC-00151", + "severity": "medium", + "type": "terraformconfig", + "subType": "aws_vpc", + "path": [ + "resource", + "aws_vpc[mainvpc]" + ], + "msg": "resource.aws_vpc[mainvpc]", + "isIgnored": false + } + ] + } +] diff --git a/test/jest/unit/iac/process-results/fixtures/snyk-iac-test-results-v2.json b/test/jest/unit/iac/process-results/fixtures/snyk-iac-test-results-v2.json new file mode 100644 index 0000000000..84cabd5a01 --- /dev/null +++ b/test/jest/unit/iac/process-results/fixtures/snyk-iac-test-results-v2.json @@ -0,0 +1,290 @@ +{ + "results": { + "resources": [ + { + "id": "aws_vpc.mainvpc", + "type": "aws_vpc", + "kind": "terraformconfig", + "file": "no_rules_detected.json" + }, + { + "id": "aws_vpc.mainvpc", + "type": "aws_vpc", + "kind": "terraformconfig", + "file": "plan_no_vulns.json" + }, + { + "id": "aws_vpc.mainvpc", + "type": "aws_vpc", + "kind": "terraformconfig", + "file": "plan.json" + }, + { + "id": "aws_default_security_group.default", + "type": "aws_default_security_group", + "kind": "terraformconfig", + "file": "plan.json" + }, + { + "id": "aws_default_security_group.default", + "type": "aws_default_security_group", + "kind": "terraformconfig", + "file": "vpc_group.tf", + "line": 9, + "column": 1 + }, + { + "id": "aws_vpc.mainvpc", + "type": "aws_vpc", + "kind": "terraformconfig", + "file": "vpc_group.tf", + "line": 5, + "column": 1 + } + ], + "vulnerabilities": [ + { + "rule": { + "id": "SNYK-CC-00024", + "title": "VPC default security group allows unrestricted ingress traffic", + "description": "Configuring all VPC default security groups to restrict all inbound traffic encourages least privilege security group development and mindful placement of AWS resources into security groups, which in turn reduces the exposure of those resources.", + "references": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html", + "labels": [ + "best-practices", + "public-access" + ], + "category": "network", + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00024" + }, + "message": "", + "remediation": "Remove any invalid `ingress` block from the `aws_security_group` or `aws_default_security_group`.\n\nEnsure that an [aws_default_security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_security_group) or [aws_security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) `ingress` block does NOT contain the value `0.0.0.0/0` in the `cidr_blocks` (`ipv6_cidr_blocks` for ipv6) field.\n\n# Example Configuration\n\n```hcl\n resource \"aws_security_group\" \"example\" {\n ingress {\n cidr_blocks = [10.0.0.0/16]\n from_port = 5900\n to_port = 5900\n # other required fields here\n }\n}\n```\n", + "severity": "medium", + "ignored": false, + "resource": { + "id": "aws_default_security_group.default", + "type": "aws_default_security_group", + "kind": "terraformconfig", + "path": [ + "ingress", + 0, + "cidr_blocks", + 0 + ], + "formattedPath": "resource.aws_default_security_group[default].ingress[0].cidr_blocks[0]", + "file": "plan.json" + } + }, + { + "rule": { + "id": "SNYK-CC-00151", + "title": "VPC flow logging is not enabled", + "description": "VPC Flow Logs provide visibility into network traffic that traverses the AWS VPC. Users can use the flow logs to detect anomalous traffic or insight during security workflows.", + "references": "https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-cwl.html", + "labels": [ + "logging" + ], + "category": "logging", + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00151" + }, + "message": "", + "remediation": "Reference the `aws_vpc` in an `aws_flog_log` `vpc_id` field.\n\n# Example Configuration\n\n```hcl\nresource \"aws_vpc\" \"valid_vpc\" {\n # other required fields here\n}\n\nresource \"aws_flow_log\" \"test_flow_log\" {\n vpc_id = \"${aws_vpc.valid_vpc.id}\"\n # other required fields here\n}\n```\n", + "severity": "medium", + "ignored": false, + "resource": { + "id": "aws_vpc.mainvpc", + "type": "aws_vpc", + "kind": "terraformconfig", + "formattedPath": "resource.aws_vpc[mainvpc]", + "file": "plan.json" + } + }, + { + "rule": { + "id": "SNYK-CC-00024", + "title": "VPC default security group allows unrestricted ingress traffic", + "description": "Configuring all VPC default security groups to restrict all inbound traffic encourages least privilege security group development and mindful placement of AWS resources into security groups, which in turn reduces the exposure of those resources.", + "references": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html", + "labels": [ + "best-practices", + "public-access" + ], + "category": "network", + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00024" + }, + "message": "", + "remediation": "Remove any invalid `ingress` block from the `aws_security_group` or `aws_default_security_group`.\n\nEnsure that an [aws_default_security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_security_group) or [aws_security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) `ingress` block does NOT contain the value `0.0.0.0/0` in the `cidr_blocks` (`ipv6_cidr_blocks` for ipv6) field.\n\n# Example Configuration\n\n```hcl\n resource \"aws_security_group\" \"example\" {\n ingress {\n cidr_blocks = [10.0.0.0/16]\n from_port = 5900\n to_port = 5900\n # other required fields here\n }\n}\n```\n", + "severity": "medium", + "ignored": false, + "resource": { + "id": "aws_default_security_group.default", + "type": "aws_default_security_group", + "kind": "terraformconfig", + "path": [ + "ingress", + 0, + "cidr_blocks", + 0 + ], + "formattedPath": "resource.aws_default_security_group[default].ingress[0].cidr_blocks[0]", + "file": "vpc_group.tf", + "line": 16, + "column": 5 + } + }, + { + "rule": { + "id": "SNYK-CC-00151", + "title": "VPC flow logging is not enabled", + "description": "VPC Flow Logs provide visibility into network traffic that traverses the AWS VPC. Users can use the flow logs to detect anomalous traffic or insight during security workflows.", + "references": "https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-cwl.html", + "labels": [ + "logging" + ], + "category": "logging", + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00151" + }, + "message": "", + "remediation": "Reference the `aws_vpc` in an `aws_flog_log` `vpc_id` field.\n\n# Example Configuration\n\n```hcl\nresource \"aws_vpc\" \"valid_vpc\" {\n # other required fields here\n}\n\nresource \"aws_flow_log\" \"test_flow_log\" {\n vpc_id = \"${aws_vpc.valid_vpc.id}\"\n # other required fields here\n}\n```\n", + "severity": "medium", + "ignored": false, + "resource": { + "id": "aws_vpc.mainvpc", + "type": "aws_vpc", + "kind": "terraformconfig", + "formattedPath": "resource.aws_vpc[mainvpc]", + "file": "vpc_group.tf", + "line": 5, + "column": 1 + } + } + ], + "passedVulnerabilities": [ + { + "rule": { + "id": "SNYK-CC-00151", + "title": "VPC flow logging is not enabled", + "description": "VPC Flow Logs provide visibility into network traffic that traverses the AWS VPC. Users can use the flow logs to detect anomalous traffic or insight during security workflows.", + "references": "https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs-cwl.html", + "labels": [ + "logging" + ], + "category": "logging", + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00151" + }, + "message": "", + "remediation": "Reference the `aws_vpc` in an `aws_flog_log` `vpc_id` field.\n\n# Example Configuration\n\n```hcl\nresource \"aws_vpc\" \"valid_vpc\" {\n # other required fields here\n}\n\nresource \"aws_flow_log\" \"test_flow_log\" {\n vpc_id = \"${aws_vpc.valid_vpc.id}\"\n # other required fields here\n}\n```\n", + "severity": "medium", + "ignored": false, + "resource": { + "id": "aws_vpc.mainvpc", + "type": "aws_vpc", + "kind": "terraformconfig", + "formattedPath": "resource.aws_vpc[mainvpc]", + "file": "plan_no_vulns.json" + } + }, + { + "rule": { + "id": "SNYK-CC-00328", + "title": "VPC default security group allows unrestricted egress traffic", + "description": "Traffic from a resource could reach any destination. In the event of a breach, this means data could be uploaded externally or additional resources targeted.", + "references": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html", + "labels": [ + "best-practices", + "access-control", + "network" + ], + "category": "network", + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00328" + }, + "message": "", + "remediation": "Update the `cidr_block` attribute with a more restrictive IP range or a specific IP address to ensure traffic can only reach known destinations.\n\n", + "severity": "medium", + "ignored": false, + "resource": { + "id": "aws_default_security_group.default", + "type": "aws_default_security_group", + "kind": "terraformconfig", + "path": [ + "egress" + ], + "formattedPath": "resource.aws_default_security_group[default].egress", + "file": "plan.json" + } + }, + { + "rule": { + "id": "SNYK-CC-00328", + "title": "VPC default security group allows unrestricted egress traffic", + "description": "Traffic from a resource could reach any destination. In the event of a breach, this means data could be uploaded externally or additional resources targeted.", + "references": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html", + "labels": [ + "best-practices", + "access-control", + "network" + ], + "category": "network", + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00328" + }, + "message": "", + "remediation": "Update the `cidr_block` attribute with a more restrictive IP range or a specific IP address to ensure traffic can only reach known destinations.\n\n", + "severity": "medium", + "ignored": false, + "resource": { + "id": "aws_default_security_group.default", + "type": "aws_default_security_group", + "kind": "terraformconfig", + "path": [ + "egress" + ], + "formattedPath": "resource.aws_default_security_group[default].egress", + "file": "vpc_group.tf", + "line": 9, + "column": 1 + } + }, + { + "rule": { + "id": "SNYK-CC-00707", + "title": "Obsolete EC2-classic resource in use", + "description": "AWS has officially retired EC2-classic resources. Retired resources do not receive security updates and are no longer supported by Snyk IaC. Hence EC2-classic infrastructure might be exposed to unknown vulnerabilities.", + "references": "https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/", + "labels": [ + "best-practices" + ], + "category": "network", + "documentation": "https://security.snyk.io/rules/cloud/SNYK-CC-00707" + }, + "message": "", + "remediation": "AWS retired all EC2-classic resources on August 15, 2022, please migrate away.\n\nMultiple resources are affected by this change. It is recommended to follow\nAWS instructions as described in the blog post \"[EC2-Classic Networking is Retiring – Here's How to Prepare](https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/)\".\n", + "severity": "high", + "ignored": false, + "resource": { + "id": "aws_vpc.mainvpc", + "type": "aws_vpc", + "kind": "terraformconfig", + "path": [ + "cluster_security_groups" + ], + "formattedPath": "resource.aws_vpc[mainvpc].cluster_security_groups", + "file": "vpc_group.tf", + "line": 5, + "column": 1 + } + } + ], + "metadata": { + "projectName": "input-files-for-snyk-iac-test-fixtures", + "ignoredCount": 0 + }, + "scanAnalytics": {} + }, + "settings": { + "org": "org-name", + "ignoreSettings": { + "adminOnly": false, + "disregardFilesystemIgnores": false, + "reasonRequired": false + } + } +} diff --git a/test/jest/unit/lib/iac/test/v2/json-old.spec.ts b/test/jest/unit/lib/iac/test/v2/json-old.spec.ts new file mode 100644 index 0000000000..e77b133eae --- /dev/null +++ b/test/jest/unit/lib/iac/test/v2/json-old.spec.ts @@ -0,0 +1,68 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { SnykIacTestError } from '../../../../../../../src/lib/iac/test/v2/errors'; +import { + convertEngineToJsonResults, + Result, +} from '../../../../../../../src/lib/iac/test/v2/json-old'; +import { ScanError } from '../../../../../../../src/lib/iac/test/v2/scan/results'; + +describe('convertEngineToJsonResults', () => { + const snykIacTestFixtureContent = fs.readFileSync( + path.join( + __dirname, + '..', + '..', + '..', + '..', + 'iac', + 'process-results', + 'fixtures', + 'snyk-iac-test-results.json', + ), + 'utf-8', + ); + + const snykIacTestFixture = JSON.parse(snykIacTestFixtureContent); + snykIacTestFixture.errors = snykIacTestFixture.errors?.map((item) => { + const isError = 'code' in item; + return isError ? new SnykIacTestError(item) : item; + }); + + const integratedJsonOutputFixtureContent = fs.readFileSync( + path.join( + __dirname, + '..', + '..', + '..', + '..', + 'iac', + 'process-results', + 'fixtures', + 'integrated-json-output.json', + ), + 'utf-8', + ); + let integratedJsonOutputFixture: Array = JSON.parse( + integratedJsonOutputFixtureContent, + ); + + integratedJsonOutputFixture = integratedJsonOutputFixture.map((item) => + !('error' in item) ? { ...item, path: process.cwd() } : item, + ); + + it('returns expected JSON result', () => { + const result = convertEngineToJsonResults({ + results: snykIacTestFixture, + projectName: 'org-name', + }); + + integratedJsonOutputFixture.forEach((item) => { + if ('targetFilePath' in item) { + item.targetFilePath = path.resolve(item.targetFile); + } + }); + + expect(result).toEqual(integratedJsonOutputFixture); + }); +}); diff --git a/test/jest/unit/lib/iac/test/v2/json.spec.ts b/test/jest/unit/lib/iac/test/v2/json.spec.ts index e406ad6d79..b585758dda 100644 --- a/test/jest/unit/lib/iac/test/v2/json.spec.ts +++ b/test/jest/unit/lib/iac/test/v2/json.spec.ts @@ -18,7 +18,7 @@ describe('convertEngineToJsonResults', () => { 'iac', 'process-results', 'fixtures', - 'snyk-iac-test-results.json', + 'snyk-iac-test-results-v2.json', ), 'utf-8', ); @@ -39,7 +39,7 @@ describe('convertEngineToJsonResults', () => { 'iac', 'process-results', 'fixtures', - 'integrated-json-output.json', + 'integrated-json-output-v2.json', ), 'utf-8', );