From a090d02260e65b615e13c88ff4dc40e230c92add Mon Sep 17 00:00:00 2001 From: clem-field Date: Sun, 23 Aug 2026 02:05:24 -0500 Subject: [PATCH] feat(hdf-converters): add KICS JSON to HDF mapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maps the native `kics scan --report-formats json` format, plus its fingerprint, frontend intake wiring, sample data and spec. KICS also emits SARIF, and unusually its SARIF is well formed — in a real scan 72 of 72 rules carried a proper CWE taxonomy relationship. What SARIF drops is everything about remediation and identity: - `expected_value`, on 100% of findings. The SARIF message keeps only `actual_value`, so a control can state what the configuration is but never what it should be. - `issue_type` — MissingAttribute 767 / IncorrectValue 68 / RedundantAttribute 14 in the scan measured. Add-a-block versus fix-a-value. - `similarity_id`, KICS's own stable finding fingerprint. - `resource_name`, `search_value`, `description_id`. - One level of severity granularity: CRITICAL and HIGH both become `error`. One control per query, each occurrence a result. KICS already groups its output as `queries[].files[]`, so it maps directly. No severity maps to impact 0. Impact 0.0 reports Not Applicable in HDF and would drop the finding from the compliance score rather than rating it low; INFO and TRACE map to 0.1. Scan coverage ------------- KICS reports violations only. Its output carries no record of the queries that ran without finding anything — `queries` holds only those that fired — so no passing control can be derived from it and a converted profile is failures-only by construction. On a real scan that means 72 of 2,034 executed queries appear, and the profile renders as 100% failed. A `kics-scan-coverage` control carries the denominator: queries executed, queries with findings, files scanned and parsed, with a description stating plainly that the compliance ratio should not be read as a pass rate. Impact 0 reports Not Applicable, so the record cannot skew the ratio it exists to explain. Control resolution ------------------ Follows the pattern the Checkov and AWS Config mappers already use: a reviewed per-rule table is the authoritative source, shipped as data, rather than a mapping computed at conversion time. That is what lets those tables carry control enhancements — CheckovToCciAndNistMappingData resolves CKV2_ADO_1 to CM-3(2) and CM-5(1), a precision the CWE table structurally cannot express. Resolution order, with the tier that answered recorded in a `nistMapping` tag: KicsToCciAndNistMappingData[query_id] -> mapped CWE lookup -> cwe-derived static-analysis defaults -> static-fallback KICS ships its full query catalog as data — 1,811 metadata.json files in the distributed image — so every query is known ahead of a scan and the table can be built completely rather than discovered from whatever a scan happens to trip. The table ships empty. Candidate mappings are ranked against the 800-53 Rev 5 catalog and await adjudication; shipping unreviewed rows would defeat the point of a reviewed table. The resolver takes the table as a parameter so all three tiers are tested without shipping unreviewed data. See mitre/hdf-libs#239. Until the table fills, KICS resolves through its CWE, which reaches only 52% of queries by volume — hence the marker, so the remainder is visibly a fallback rather than a mapping. The source CWE is retained in tags even when it does not resolve. The fingerprint keys on `kics_version` and `severity_counters` alongside `queries`; verified against all 134 sample files in sample_jsons with no collision in either direction. Sample data is generated from a small synthetic Terraform tree, so it is reproducible and carries no proprietary input. Signed-off-by: clem-field --- apps/frontend/src/store/report_intake.ts | 3 + libs/hdf-converters/README.md | 33 +- libs/hdf-converters/index.ts | 1 + .../kics_mapper/kics_findings-hdf.json | 824 ++++++++++ .../kics_findings-withraw-hdf.json | 1357 +++++++++++++++++ .../kics_mapper/kics_zero_findings-hdf.json | 65 + .../sample_input_report/kics_findings.json | 533 +++++++ .../kics_zero_findings.json | 23 + libs/hdf-converters/src/kics-mapper.ts | 376 +++++ .../mappings/KicsToCciAndNistMappingData.ts | 24 + .../src/utils/fingerprinting.ts | 7 + .../test/mappers/forward/kics_mapper.spec.ts | 239 +++ 12 files changed, 3469 insertions(+), 16 deletions(-) create mode 100644 libs/hdf-converters/sample_jsons/kics_mapper/kics_findings-hdf.json create mode 100644 libs/hdf-converters/sample_jsons/kics_mapper/kics_findings-withraw-hdf.json create mode 100644 libs/hdf-converters/sample_jsons/kics_mapper/kics_zero_findings-hdf.json create mode 100644 libs/hdf-converters/sample_jsons/kics_mapper/sample_input_report/kics_findings.json create mode 100644 libs/hdf-converters/sample_jsons/kics_mapper/sample_input_report/kics_zero_findings.json create mode 100644 libs/hdf-converters/src/kics-mapper.ts create mode 100644 libs/hdf-converters/src/mappings/KicsToCciAndNistMappingData.ts create mode 100644 libs/hdf-converters/test/mappers/forward/kics_mapper.spec.ts diff --git a/apps/frontend/src/store/report_intake.ts b/apps/frontend/src/store/report_intake.ts index 7e7f271064..f3f34d53a0 100644 --- a/apps/frontend/src/store/report_intake.ts +++ b/apps/frontend/src/store/report_intake.ts @@ -22,6 +22,7 @@ import { INPUT_TYPES, IonChannelMapper, JfrogXrayMapper, + KicsMapper, MsftSecureScoreResults, NessusResults, NetsparkerResults, @@ -230,6 +231,8 @@ export class InspecIntake extends VuexModule { switch (typeGuess) { case INPUT_TYPES.JFROG: return new JfrogXrayMapper(convertOptions.data).toHdf(); + case INPUT_TYPES.KICS: + return new KicsMapper(convertOptions.data).toHdf(); case INPUT_TYPES.MSFT_SEC_SCORE: return new MsftSecureScoreResults(convertOptions.data).toHdf(); case INPUT_TYPES.ASFF: diff --git a/libs/hdf-converters/README.md b/libs/hdf-converters/README.md index 7e6df3fdd8..2c184a468c 100644 --- a/libs/hdf-converters/README.md +++ b/libs/hdf-converters/README.md @@ -21,22 +21,23 @@ OHDF Converters supplies several methods to convert various types of security to 13. [**gosec-mapper**] - gosec results JSON file 14. [**ionchannel-mapper**] - SBOM data from Ion Channel 15. [**jfrog-xray-mapper**] - JFrog Xray results JSON file -16. [**msft-secure-mapper**] - Microsoft Secure Score results file -17. [**nessus-mapper**] - Nessus XML results file -18. [**netsparker-mapper**] - Netsparker XML results file -19. [**neuvector-mapper**] - NeuVector JSON results file -20. [**nikto-mapper**] - Nikto results JSON file -21. [**prisma-mapper**] - Prisma Cloud Scan Report CSV file -22. [**sarif-mapper**] - SARIF JSON file -23. [**scoutsuite-mapper**] - ScoutSuite results from a Javascript object -24. [**snyk-mapper**] - Snyk results JSON file -25. [**sonarqube-mapper**] - SonarQube vulnerabilities for the specified project name and optional branch or pull/merge request ID name from an API -26. [**splunk-mapper**] - Splunk instance -27. [**trufflehog-mapper**] - Trufflehog results json file -28. [**twistlock-mapper**] - Twistlock CLI output file -29. [**veracode-mapper**] - Veracode Scan Results XML file -30. [**xccdf-results-mapper**] - SCAP client XCCDF-Results XML report -31. [**zap-mapper**] - OWASP ZAP results JSON +16. [**kics-mapper**] - KICS infrastructure-as-code scan results JSON file +17. [**msft-secure-mapper**] - Microsoft Secure Score results file +18. [**nessus-mapper**] - Nessus XML results file +19. [**netsparker-mapper**] - Netsparker XML results file +20. [**neuvector-mapper**] - NeuVector JSON results file +21. [**nikto-mapper**] - Nikto results JSON file +22. [**prisma-mapper**] - Prisma Cloud Scan Report CSV file +23. [**sarif-mapper**] - SARIF JSON file +24. [**scoutsuite-mapper**] - ScoutSuite results from a Javascript object +25. [**snyk-mapper**] - Snyk results JSON file +26. [**sonarqube-mapper**] - SonarQube vulnerabilities for the specified project name and optional branch or pull/merge request ID name from an API +27. [**splunk-mapper**] - Splunk instance +28. [**trufflehog-mapper**] - Trufflehog results json file +29. [**twistlock-mapper**] - Twistlock CLI output file +30. [**veracode-mapper**] - Veracode Scan Results XML file +31. [**xccdf-results-mapper**] - SCAP client XCCDF-Results XML report +32. [**zap-mapper**] - OWASP ZAP results JSON ### NOTICE diff --git a/libs/hdf-converters/index.ts b/libs/hdf-converters/index.ts index 6e1d33de26..206e758553 100644 --- a/libs/hdf-converters/index.ts +++ b/libs/hdf-converters/index.ts @@ -29,6 +29,7 @@ export * from './src/fortify-mapper'; export * from './src/gosec-mapper'; export * from './src/ionchannel-mapper'; export * from './src/jfrog-xray-mapper'; +export * from './src/kics-mapper'; export * from './src/msft-secure-score-mapper'; export * from './src/nessus-mapper'; export * from './src/netsparker-mapper'; diff --git a/libs/hdf-converters/sample_jsons/kics_mapper/kics_findings-hdf.json b/libs/hdf-converters/sample_jsons/kics_mapper/kics_findings-hdf.json new file mode 100644 index 0000000000..1c647b6861 --- /dev/null +++ b/libs/hdf-converters/sample_jsons/kics_mapper/kics_findings-hdf.json @@ -0,0 +1,824 @@ +{ + "platform": { + "name": "Heimdall Tools", + "release": "2.13.0" + }, + "version": "2.13.0", + "statistics": {}, + "profiles": [ + { + "name": "KICS", + "title": "KICS Infrastructure-as-Code Scan", + "version": "v2.1.20", + "supports": [], + "attributes": [], + "groups": [], + "status": "loaded", + "controls": [ + { + "id": "08bd0760-8752-44e1-9779-7bb369b2b4e4", + "title": "DB Instance Storage Not Encrypted", + "desc": "AWS DB Instance should have its storage encrypted by setting the parameter to 'true'. The storage_encrypted default value is 'false'.", + "impact": 0.7, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#storage_encrypted" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SC-8" + ], + "cci": [ + "CCI-002418" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-311" + ], + "severity": "HIGH", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Encryption", + "risk_score": "7.1", + "description_id": "88ca11bc", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[records]", + "message": "Expected: 'storage_encrypted' should be set to true\nActual: 'storage_encrypted' is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "381c3f2a-ef6f-4eff-99f7-b169cda3422c", + "title": "Sensitive Port Is Exposed To Entire Network", + "desc": "A sensitive port, such as port 23 or port 110, is open for the whole network in either TCP or UDP protocol", + "impact": 0.7, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SC-8" + ], + "cci": [ + "CCI-002418" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-200" + ], + "severity": "HIGH", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "risk_score": "7.7", + "description_id": "5be78364", + "issue_type": [ + "IncorrectValue" + ], + "resource_type": [ + "aws_security_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 17\nResource type: aws_security_group\nResource: example-open\nKey: aws_security_group[ingress_all].ingress", + "message": "Expected: SSH (TCP:22) should not be allowed\nActual: SSH (TCP:22) is allowed\nIssue type: IncorrectValue\nSearch value: TCP,22", + "start_time": "" + } + ] + }, + { + "id": "4728cd65-a20c-49da-8b31-9c08b423e4db", + "title": "Unrestricted Security Group Ingress", + "desc": "Security groups allow ingress from 0.0.0.0:0 and/or ::/0", + "impact": 0.7, + "refs": [ + { + "url": "https://www.terraform.io/docs/providers/aws/r/security_group.html" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-668" + ], + "severity": "HIGH", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "risk_score": "7.2", + "description_id": "ce3ee5e0", + "issue_type": [ + "IncorrectValue" + ], + "resource_type": [ + "aws_security_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 21\nResource type: aws_security_group\nResource: example-open\nKey: aws_security_group[ingress_all].ingress.cidr_blocks", + "message": "Expected: aws_security_group[ingress_all].ingress.cidr_blocks should not contain '0.0.0.0/0'\nActual: aws_security_group[ingress_all].ingress.cidr_blocks contains '0.0.0.0/0'\nIssue type: IncorrectValue", + "start_time": "" + } + ] + }, + { + "id": "0afbcfe9-d341-4b92-a64c-7e6de0543879", + "title": "CloudWatch Log Group Without KMS", + "desc": "AWS CloudWatch Log groups should be encrypted using KMS", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SC-8" + ], + "cci": [ + "CCI-002418" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-311" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Encryption", + "risk_score": "5.5", + "description_id": "4258abe6", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_cloudwatch_log_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 9\nResource type: aws_cloudwatch_log_group\nResource: example-audit-log\nKey: aws_cloudwatch_log_group[audit]", + "message": "Expected: Attribute 'kms_key_id' should be set\nActual: Attribute 'kms_key_id' is undefined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "88fd05e0-ac0e-43d2-ba6d-fc0ba60ae1a6", + "title": "IAM Database Auth Not Enabled", + "desc": "IAM Database Auth Enabled should be configured to true when using compatible engine and version", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#iam_database_authentication_enabled" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "AC-3" + ], + "cci": [ + "CCI-000213" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-285" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Encryption", + "risk_score": "5.9", + "description_id": "12b1c8aa", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[records]", + "message": "Expected: 'iam_database_authentication_enabled' should be set to true\nActual: 'iam_database_authentication_enabled' is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "1dc73fb4-5b51-430c-8c5f-25dcf9090b02", + "title": "RDS With Backup Disabled", + "desc": "Make sure the AWS RDS configuration has automatic backup configured. If the retention period is equal to 0 there is no backup", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-754" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Backup", + "risk_score": "5.4", + "description_id": "73fdfe55", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[records]", + "message": "Expected: 'backup_retention_period' should be defined, and bigger than '0'\nActual: 'backup_retention_period' is not defined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "8d7f7b8c-6c7c-40f8-baa6-62006c6c7b56", + "title": "RDS Without Logging", + "desc": "RDS does not have any kind of logger", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#enabled_cloudwatch_logs_exports" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-778" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Observability", + "risk_score": "5.1", + "description_id": "c499a58c", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[records]", + "message": "Expected: 'enabled_cloudwatch_logs_exports' should be defined\nActual: 'enabled_cloudwatch_logs_exports' is undefined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "f861041c-8c9f-4156-acfc-5e6e524f5884", + "title": "S3 Bucket Logging Disabled", + "desc": "Server Access Logging should be enabled on S3 Buckets so that all changes are logged and trackable", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-778" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Observability", + "risk_score": "5.1", + "description_id": "fa5c7c72", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_s3_bucket" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 5\nResource type: aws_s3_bucket\nResource: example-compliance-reports\nKey: aws_s3_bucket[reports]", + "message": "Expected: 'logging' should be defined and not null\nActual: 'logging' is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "568a4d22-3517-44a6-a7ad-6a7eed88722c", + "title": "S3 Bucket Without Versioning", + "desc": "S3 bucket should have versioning enabled", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket#versioning" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-710" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Backup", + "risk_score": "5.7", + "description_id": "7614ce3b", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_s3_bucket" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 5\nResource type: aws_s3_bucket\nResource: example-compliance-reports\nKey: aws_s3_bucket[reports]", + "message": "Expected: 'versioning' should be true\nActual: 'versioning' is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "65905cec-d691-4320-b320-2000436cb696", + "title": "Security Group With Unrestricted Access To SSH", + "desc": "'SSH' (TCP:22) should not be public in AWS Security Group", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "AC-3" + ], + "cci": [ + "CCI-000213" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-732" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "risk_score": "5.2", + "description_id": "51e59188", + "issue_type": [ + "IncorrectValue" + ], + "resource_type": [ + "aws_security_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 17\nResource type: aws_security_group\nResource: example-open\nKey: aws_security_group[ingress_all].ingress", + "message": "Expected: aws_security_group[ingress_all].ingress 'SSH' (Port:22) should not be open\nActual: aws_security_group[ingress_all].ingress 'SSH' (Port:22) is open\nIssue type: IncorrectValue", + "start_time": "" + } + ] + }, + { + "id": "e592a0c5-5bdb-414c-9066-5dba7cdea370", + "title": "IAM Access Analyzer Not Enabled", + "desc": "IAM Access Analyzer should be enabled and configured to continuously monitor resource permissions", + "impact": 0.3, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/accessanalyzer_analyzer" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-710" + ], + "severity": "LOW", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Best Practices", + "risk_score": "3.5", + "description_id": "d03e85ae", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "n/a" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 5\nResource type: n/a\nResource: n/a\nKey: resource", + "message": "Expected: 'aws_accessanalyzer_analyzer' should be set\nActual: 'aws_accessanalyzer_analyzer' is undefined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "6d3dead4-c6b2-4db7-81bd-3a83eae8f255", + "title": "Tags Not Copied to RDS Cluster Snapshot", + "desc": "Tags of the RDS Cluster should be copied to the respective snapshots to ensure that snapshots retain important metadata for identification, cost allocation, and resource management", + "impact": 0.3, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster#copy_tags_to_snapshot" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "AC-3" + ], + "cci": [ + "CCI-000213" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-284" + ], + "severity": "LOW", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Backup", + "risk_score": "1.0", + "description_id": "6d3dead4", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[records]", + "message": "Expected: 'aws_db_instance[records].copy_tags_to_snapshot' should be defined to true\nActual: 'aws_db_instance[records].copy_tags_to_snapshot' is not defined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "ef0b316a-211e-42f1-888e-64efe172b755", + "title": "CloudWatch Without Retention Period Specified", + "desc": "AWS CloudWatch Log groups should have retention days specified", + "impact": 0.1, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-778" + ], + "severity": "INFO", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Observability", + "risk_score": "0.0", + "description_id": "64f08509", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_cloudwatch_log_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 9\nResource type: aws_cloudwatch_log_group\nResource: example-audit-log\nKey: aws_cloudwatch_log_group[audit]", + "message": "Expected: Attribute 'retention_in_days' should be set and valid\nActual: Attribute 'retention_in_days' is undefined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "e38a8e0a-b88b-4902-b3fe-b0fcb17d5c10", + "title": "Resource Not Using Tags", + "desc": "AWS services resource tags are an essential part of managing components. As a best practice, the field 'tags' should have additional tags defined other than 'Name'", + "impact": 0.1, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/resource-tagging" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-665" + ], + "severity": "INFO", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Best Practices", + "risk_score": "0.0", + "description_id": "09db2d52", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance", + "aws_s3_bucket", + "aws_security_group", + "aws_cloudwatch_log_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[{{records}}]", + "message": "Expected: aws_db_instance[{{records}}].tags should be defined and not null\nActual: aws_db_instance[{{records}}].tags is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + }, + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 5\nResource type: aws_s3_bucket\nResource: reports\nKey: aws_s3_bucket[{{reports}}]", + "message": "Expected: aws_s3_bucket[{{reports}}].tags should be defined and not null\nActual: aws_s3_bucket[{{reports}}].tags is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + }, + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 13\nResource type: aws_security_group\nResource: example-open\nKey: aws_security_group[{{ingress_all}}]", + "message": "Expected: aws_security_group[{{ingress_all}}].tags should be defined and not null\nActual: aws_security_group[{{ingress_all}}].tags is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + }, + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 9\nResource type: aws_cloudwatch_log_group\nResource: example-audit-log\nKey: aws_cloudwatch_log_group[{{audit}}]", + "message": "Expected: aws_cloudwatch_log_group[{{audit}}].tags should be defined and not null\nActual: aws_cloudwatch_log_group[{{audit}}].tags is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "4849211b-ac39-479e-ae78-5694d506cb24", + "title": "Security Group Not Used", + "desc": "Security group must be used or not declared", + "impact": 0.1, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "AC-3" + ], + "cci": [ + "CCI-000213" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-284" + ], + "severity": "INFO", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Access Control", + "risk_score": "0.0", + "description_id": "eff2383a", + "issue_type": [ + "IncorrectValue" + ], + "resource_type": [ + "aws_security_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 13\nResource type: aws_security_group\nResource: example-open\nKey: aws_security_group[ingress_all]", + "message": "Expected: 'aws_security_group[ingress_all]' should be used\nActual: 'aws_security_group[ingress_all]' is not used\nIssue type: IncorrectValue", + "start_time": "" + } + ] + }, + { + "id": "68eb4bf3-f9bf-463d-b5cf-e029bb446d2e", + "title": "Security Group Rule Without Description", + "desc": "It's considered a best practice for all rules in AWS Security Group to have a description", + "impact": 0.1, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group#description" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-710" + ], + "severity": "INFO", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Best Practices", + "risk_score": "0.0", + "description_id": "cee242dd", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_security_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 17\nResource type: aws_security_group\nResource: ingress_all\nKey: aws_security_group[ingress_all].ingress", + "message": "Expected: aws_security_group[ingress_all].ingress.description should be defined and not null\nActual: aws_security_group[ingress_all].ingress.description is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "kics-scan-coverage", + "title": "KICS scan coverage", + "impact": 0, + "desc": "KICS executed 1100 queries against 1 file(s); 16 produced findings. KICS reports violations only and does not enumerate the queries that ran without finding anything, so no passing controls can be derived from its output and the compliance ratio should not be read as a pass rate.", + "refs": [], + "source_location": {}, + "code": "", + "tags": { + "queries_executed": 1100, + "queries_with_findings": 16, + "files_scanned": 1, + "files_parsed": 1 + }, + "results": [ + { + "status": "passed", + "code_desc": "KICS executed 1100 queries against 1 file(s); 16 produced findings. KICS reports violations only and does not enumerate the queries that ran without finding anything, so no passing controls can be derived from its output and the compliance ratio should not be read as a pass rate.", + "start_time": "" + } + ] + } + ], + "sha256": "fb8346267095a6ed0714798d4f4e2b2cb8465a26693ae3bd70d4e08a48a90b49" + } + ], + "passthrough": { + "auxiliary_data": [ + { + "name": "KICS", + "data": { + "kics_version": "v2.1.20", + "severity_counters": { + "CRITICAL": 0, + "HIGH": 3, + "INFO": 7, + "LOW": 2, + "MEDIUM": 7, + "TRACE": 0 + }, + "total_counter": 19, + "files_scanned": 1, + "queries_total": 1100 + } + } + ] + } +} \ No newline at end of file diff --git a/libs/hdf-converters/sample_jsons/kics_mapper/kics_findings-withraw-hdf.json b/libs/hdf-converters/sample_jsons/kics_mapper/kics_findings-withraw-hdf.json new file mode 100644 index 0000000000..144142c7f1 --- /dev/null +++ b/libs/hdf-converters/sample_jsons/kics_mapper/kics_findings-withraw-hdf.json @@ -0,0 +1,1357 @@ +{ + "platform": { + "name": "Heimdall Tools", + "release": "2.13.0" + }, + "version": "2.13.0", + "statistics": {}, + "profiles": [ + { + "name": "KICS", + "title": "KICS Infrastructure-as-Code Scan", + "version": "v2.1.20", + "supports": [], + "attributes": [], + "groups": [], + "status": "loaded", + "controls": [ + { + "id": "08bd0760-8752-44e1-9779-7bb369b2b4e4", + "title": "DB Instance Storage Not Encrypted", + "desc": "AWS DB Instance should have its storage encrypted by setting the parameter to 'true'. The storage_encrypted default value is 'false'.", + "impact": 0.7, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#storage_encrypted" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SC-8" + ], + "cci": [ + "CCI-002418" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-311" + ], + "severity": "HIGH", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Encryption", + "risk_score": "7.1", + "description_id": "88ca11bc", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[records]", + "message": "Expected: 'storage_encrypted' should be set to true\nActual: 'storage_encrypted' is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "381c3f2a-ef6f-4eff-99f7-b169cda3422c", + "title": "Sensitive Port Is Exposed To Entire Network", + "desc": "A sensitive port, such as port 23 or port 110, is open for the whole network in either TCP or UDP protocol", + "impact": 0.7, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SC-8" + ], + "cci": [ + "CCI-002418" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-200" + ], + "severity": "HIGH", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "risk_score": "7.7", + "description_id": "5be78364", + "issue_type": [ + "IncorrectValue" + ], + "resource_type": [ + "aws_security_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 17\nResource type: aws_security_group\nResource: example-open\nKey: aws_security_group[ingress_all].ingress", + "message": "Expected: SSH (TCP:22) should not be allowed\nActual: SSH (TCP:22) is allowed\nIssue type: IncorrectValue\nSearch value: TCP,22", + "start_time": "" + } + ] + }, + { + "id": "4728cd65-a20c-49da-8b31-9c08b423e4db", + "title": "Unrestricted Security Group Ingress", + "desc": "Security groups allow ingress from 0.0.0.0:0 and/or ::/0", + "impact": 0.7, + "refs": [ + { + "url": "https://www.terraform.io/docs/providers/aws/r/security_group.html" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-668" + ], + "severity": "HIGH", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "risk_score": "7.2", + "description_id": "ce3ee5e0", + "issue_type": [ + "IncorrectValue" + ], + "resource_type": [ + "aws_security_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 21\nResource type: aws_security_group\nResource: example-open\nKey: aws_security_group[ingress_all].ingress.cidr_blocks", + "message": "Expected: aws_security_group[ingress_all].ingress.cidr_blocks should not contain '0.0.0.0/0'\nActual: aws_security_group[ingress_all].ingress.cidr_blocks contains '0.0.0.0/0'\nIssue type: IncorrectValue", + "start_time": "" + } + ] + }, + { + "id": "0afbcfe9-d341-4b92-a64c-7e6de0543879", + "title": "CloudWatch Log Group Without KMS", + "desc": "AWS CloudWatch Log groups should be encrypted using KMS", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SC-8" + ], + "cci": [ + "CCI-002418" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-311" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Encryption", + "risk_score": "5.5", + "description_id": "4258abe6", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_cloudwatch_log_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 9\nResource type: aws_cloudwatch_log_group\nResource: example-audit-log\nKey: aws_cloudwatch_log_group[audit]", + "message": "Expected: Attribute 'kms_key_id' should be set\nActual: Attribute 'kms_key_id' is undefined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "88fd05e0-ac0e-43d2-ba6d-fc0ba60ae1a6", + "title": "IAM Database Auth Not Enabled", + "desc": "IAM Database Auth Enabled should be configured to true when using compatible engine and version", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#iam_database_authentication_enabled" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "AC-3" + ], + "cci": [ + "CCI-000213" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-285" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Encryption", + "risk_score": "5.9", + "description_id": "12b1c8aa", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[records]", + "message": "Expected: 'iam_database_authentication_enabled' should be set to true\nActual: 'iam_database_authentication_enabled' is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "1dc73fb4-5b51-430c-8c5f-25dcf9090b02", + "title": "RDS With Backup Disabled", + "desc": "Make sure the AWS RDS configuration has automatic backup configured. If the retention period is equal to 0 there is no backup", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-754" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Backup", + "risk_score": "5.4", + "description_id": "73fdfe55", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[records]", + "message": "Expected: 'backup_retention_period' should be defined, and bigger than '0'\nActual: 'backup_retention_period' is not defined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "8d7f7b8c-6c7c-40f8-baa6-62006c6c7b56", + "title": "RDS Without Logging", + "desc": "RDS does not have any kind of logger", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#enabled_cloudwatch_logs_exports" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-778" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Observability", + "risk_score": "5.1", + "description_id": "c499a58c", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[records]", + "message": "Expected: 'enabled_cloudwatch_logs_exports' should be defined\nActual: 'enabled_cloudwatch_logs_exports' is undefined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "f861041c-8c9f-4156-acfc-5e6e524f5884", + "title": "S3 Bucket Logging Disabled", + "desc": "Server Access Logging should be enabled on S3 Buckets so that all changes are logged and trackable", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-778" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Observability", + "risk_score": "5.1", + "description_id": "fa5c7c72", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_s3_bucket" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 5\nResource type: aws_s3_bucket\nResource: example-compliance-reports\nKey: aws_s3_bucket[reports]", + "message": "Expected: 'logging' should be defined and not null\nActual: 'logging' is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "568a4d22-3517-44a6-a7ad-6a7eed88722c", + "title": "S3 Bucket Without Versioning", + "desc": "S3 bucket should have versioning enabled", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket#versioning" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-710" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Backup", + "risk_score": "5.7", + "description_id": "7614ce3b", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_s3_bucket" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 5\nResource type: aws_s3_bucket\nResource: example-compliance-reports\nKey: aws_s3_bucket[reports]", + "message": "Expected: 'versioning' should be true\nActual: 'versioning' is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "65905cec-d691-4320-b320-2000436cb696", + "title": "Security Group With Unrestricted Access To SSH", + "desc": "'SSH' (TCP:22) should not be public in AWS Security Group", + "impact": 0.5, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "AC-3" + ], + "cci": [ + "CCI-000213" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-732" + ], + "severity": "MEDIUM", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "risk_score": "5.2", + "description_id": "51e59188", + "issue_type": [ + "IncorrectValue" + ], + "resource_type": [ + "aws_security_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 17\nResource type: aws_security_group\nResource: example-open\nKey: aws_security_group[ingress_all].ingress", + "message": "Expected: aws_security_group[ingress_all].ingress 'SSH' (Port:22) should not be open\nActual: aws_security_group[ingress_all].ingress 'SSH' (Port:22) is open\nIssue type: IncorrectValue", + "start_time": "" + } + ] + }, + { + "id": "e592a0c5-5bdb-414c-9066-5dba7cdea370", + "title": "IAM Access Analyzer Not Enabled", + "desc": "IAM Access Analyzer should be enabled and configured to continuously monitor resource permissions", + "impact": 0.3, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/accessanalyzer_analyzer" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-710" + ], + "severity": "LOW", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Best Practices", + "risk_score": "3.5", + "description_id": "d03e85ae", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "n/a" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 5\nResource type: n/a\nResource: n/a\nKey: resource", + "message": "Expected: 'aws_accessanalyzer_analyzer' should be set\nActual: 'aws_accessanalyzer_analyzer' is undefined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "6d3dead4-c6b2-4db7-81bd-3a83eae8f255", + "title": "Tags Not Copied to RDS Cluster Snapshot", + "desc": "Tags of the RDS Cluster should be copied to the respective snapshots to ensure that snapshots retain important metadata for identification, cost allocation, and resource management", + "impact": 0.3, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster#copy_tags_to_snapshot" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "AC-3" + ], + "cci": [ + "CCI-000213" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-284" + ], + "severity": "LOW", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Backup", + "risk_score": "1.0", + "description_id": "6d3dead4", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[records]", + "message": "Expected: 'aws_db_instance[records].copy_tags_to_snapshot' should be defined to true\nActual: 'aws_db_instance[records].copy_tags_to_snapshot' is not defined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "ef0b316a-211e-42f1-888e-64efe172b755", + "title": "CloudWatch Without Retention Period Specified", + "desc": "AWS CloudWatch Log groups should have retention days specified", + "impact": 0.1, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-778" + ], + "severity": "INFO", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Observability", + "risk_score": "0.0", + "description_id": "64f08509", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_cloudwatch_log_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 9\nResource type: aws_cloudwatch_log_group\nResource: example-audit-log\nKey: aws_cloudwatch_log_group[audit]", + "message": "Expected: Attribute 'retention_in_days' should be set and valid\nActual: Attribute 'retention_in_days' is undefined\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "e38a8e0a-b88b-4902-b3fe-b0fcb17d5c10", + "title": "Resource Not Using Tags", + "desc": "AWS services resource tags are an essential part of managing components. As a best practice, the field 'tags' should have additional tags defined other than 'Name'", + "impact": 0.1, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/resource-tagging" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-665" + ], + "severity": "INFO", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Best Practices", + "risk_score": "0.0", + "description_id": "09db2d52", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_db_instance", + "aws_s3_bucket", + "aws_security_group", + "aws_cloudwatch_log_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 25\nResource type: aws_db_instance\nResource: records\nKey: aws_db_instance[{{records}}]", + "message": "Expected: aws_db_instance[{{records}}].tags should be defined and not null\nActual: aws_db_instance[{{records}}].tags is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + }, + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 5\nResource type: aws_s3_bucket\nResource: reports\nKey: aws_s3_bucket[{{reports}}]", + "message": "Expected: aws_s3_bucket[{{reports}}].tags should be defined and not null\nActual: aws_s3_bucket[{{reports}}].tags is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + }, + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 13\nResource type: aws_security_group\nResource: example-open\nKey: aws_security_group[{{ingress_all}}]", + "message": "Expected: aws_security_group[{{ingress_all}}].tags should be defined and not null\nActual: aws_security_group[{{ingress_all}}].tags is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + }, + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 9\nResource type: aws_cloudwatch_log_group\nResource: example-audit-log\nKey: aws_cloudwatch_log_group[{{audit}}]", + "message": "Expected: aws_cloudwatch_log_group[{{audit}}].tags should be defined and not null\nActual: aws_cloudwatch_log_group[{{audit}}].tags is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "4849211b-ac39-479e-ae78-5694d506cb24", + "title": "Security Group Not Used", + "desc": "Security group must be used or not declared", + "impact": 0.1, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "AC-3" + ], + "cci": [ + "CCI-000213" + ], + "nistMapping": "cwe-derived", + "cwe": [ + "CWE-284" + ], + "severity": "INFO", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Access Control", + "risk_score": "0.0", + "description_id": "eff2383a", + "issue_type": [ + "IncorrectValue" + ], + "resource_type": [ + "aws_security_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 13\nResource type: aws_security_group\nResource: example-open\nKey: aws_security_group[ingress_all]", + "message": "Expected: 'aws_security_group[ingress_all]' should be used\nActual: 'aws_security_group[ingress_all]' is not used\nIssue type: IncorrectValue", + "start_time": "" + } + ] + }, + { + "id": "68eb4bf3-f9bf-463d-b5cf-e029bb446d2e", + "title": "Security Group Rule Without Description", + "desc": "It's considered a best practice for all rules in AWS Security Group to have a description", + "impact": 0.1, + "refs": [ + { + "url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group#description" + } + ], + "source_location": {}, + "code": "", + "tags": { + "nist": [ + "SA-11", + "RA-5" + ], + "cci": [ + "CCI-003173", + "CCI-001643" + ], + "nistMapping": "static-fallback", + "cwe": [ + "CWE-710" + ], + "severity": "INFO", + "platform": "Terraform", + "cloud_provider": "AWS", + "category": "Best Practices", + "risk_score": "0.0", + "description_id": "cee242dd", + "issue_type": [ + "MissingAttribute" + ], + "resource_type": [ + "aws_security_group" + ] + }, + "results": [ + { + "status": "failed", + "code_desc": "File: ../../path/main.tf\nLine: 17\nResource type: aws_security_group\nResource: ingress_all\nKey: aws_security_group[ingress_all].ingress", + "message": "Expected: aws_security_group[ingress_all].ingress.description should be defined and not null\nActual: aws_security_group[ingress_all].ingress.description is undefined or null\nIssue type: MissingAttribute", + "start_time": "" + } + ] + }, + { + "id": "kics-scan-coverage", + "title": "KICS scan coverage", + "impact": 0, + "desc": "KICS executed 1100 queries against 1 file(s); 16 produced findings. KICS reports violations only and does not enumerate the queries that ran without finding anything, so no passing controls can be derived from its output and the compliance ratio should not be read as a pass rate.", + "refs": [], + "source_location": {}, + "code": "", + "tags": { + "queries_executed": 1100, + "queries_with_findings": 16, + "files_scanned": 1, + "files_parsed": 1 + }, + "results": [ + { + "status": "passed", + "code_desc": "KICS executed 1100 queries against 1 file(s); 16 produced findings. KICS reports violations only and does not enumerate the queries that ran without finding anything, so no passing controls can be derived from its output and the compliance ratio should not be read as a pass rate.", + "start_time": "" + } + ] + } + ], + "sha256": "fb8346267095a6ed0714798d4f4e2b2cb8465a26693ae3bd70d4e08a48a90b49" + } + ], + "passthrough": { + "auxiliary_data": [ + { + "name": "KICS", + "data": { + "kics_version": "v2.1.20", + "severity_counters": { + "CRITICAL": 0, + "HIGH": 3, + "INFO": 7, + "LOW": 2, + "MEDIUM": 7, + "TRACE": 0 + }, + "total_counter": 19, + "files_scanned": 1, + "queries_total": 1100 + } + } + ], + "raw": { + "kics_version": "v2.1.20", + "files_scanned": 1, + "lines_scanned": 32, + "files_parsed": 1, + "lines_parsed": 32, + "lines_ignored": 0, + "files_failed_to_scan": 0, + "queries_total": 1100, + "queries_failed_to_execute": 0, + "queries_failed_to_compute_similarity_id": 0, + "severity_counters": { + "CRITICAL": 0, + "HIGH": 3, + "INFO": 7, + "LOW": 2, + "MEDIUM": 7, + "TRACE": 0 + }, + "total_counter": 19, + "total_bom_resources": 0, + "queries": [ + { + "query_name": "DB Instance Storage Not Encrypted", + "query_id": "08bd0760-8752-44e1-9779-7bb369b2b4e4", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#storage_encrypted", + "severity": "HIGH", + "platform": "Terraform", + "cwe": "311", + "risk_score": "7.1", + "cloud_provider": "AWS", + "category": "Encryption", + "experimental": false, + "description": "AWS DB Instance should have its storage encrypted by setting the parameter to 'true'. The storage_encrypted default value is 'false'.", + "description_id": "88ca11bc", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "4efca9c9c9328e651ec53a8d38e4c3b25f5940cd2e3eb9ca1ef6a5625cf2dab0", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[records]", + "search_line": 25, + "search_value": "", + "expected_value": "'storage_encrypted' should be set to true", + "actual_value": "'storage_encrypted' is undefined or null" + } + ] + }, + { + "query_name": "Sensitive Port Is Exposed To Entire Network", + "query_id": "381c3f2a-ef6f-4eff-99f7-b169cda3422c", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group", + "severity": "HIGH", + "platform": "Terraform", + "cwe": "200", + "risk_score": "7.7", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "experimental": false, + "description": "A sensitive port, such as port 23 or port 110, is open for the whole network in either TCP or UDP protocol", + "description_id": "5be78364", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "9dbff16a6c0c14f73d415aaff331196059d11c552fff0288b84acb2462dbecb6", + "line": 17, + "resource_type": "aws_security_group", + "resource_name": "example-open", + "issue_type": "IncorrectValue", + "search_key": "aws_security_group[ingress_all].ingress", + "search_line": 17, + "search_value": "TCP,22", + "expected_value": "SSH (TCP:22) should not be allowed", + "actual_value": "SSH (TCP:22) is allowed" + } + ] + }, + { + "query_name": "Unrestricted Security Group Ingress", + "query_id": "4728cd65-a20c-49da-8b31-9c08b423e4db", + "query_url": "https://www.terraform.io/docs/providers/aws/r/security_group.html", + "severity": "HIGH", + "platform": "Terraform", + "cwe": "668", + "risk_score": "7.2", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "experimental": false, + "description": "Security groups allow ingress from 0.0.0.0:0 and/or ::/0", + "description_id": "ce3ee5e0", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "c69e33189fa2e8f28646c2ca82654121f4b2372b03838ed353522b4d11223e0c", + "line": 21, + "resource_type": "aws_security_group", + "resource_name": "example-open", + "issue_type": "IncorrectValue", + "search_key": "aws_security_group[ingress_all].ingress.cidr_blocks", + "search_line": 21, + "search_value": "", + "expected_value": "aws_security_group[ingress_all].ingress.cidr_blocks should not contain '0.0.0.0/0'", + "actual_value": "aws_security_group[ingress_all].ingress.cidr_blocks contains '0.0.0.0/0'" + } + ] + }, + { + "query_name": "CloudWatch Log Group Without KMS", + "query_id": "0afbcfe9-d341-4b92-a64c-7e6de0543879", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "311", + "risk_score": "5.5", + "cloud_provider": "AWS", + "category": "Encryption", + "experimental": false, + "description": "AWS CloudWatch Log groups should be encrypted using KMS", + "description_id": "4258abe6", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "2737ae3898f04339f701da0891accf68690355971adb9ff9d4351b0db104d7b0", + "line": 9, + "resource_type": "aws_cloudwatch_log_group", + "resource_name": "example-audit-log", + "issue_type": "MissingAttribute", + "search_key": "aws_cloudwatch_log_group[audit]", + "search_line": -1, + "search_value": "", + "expected_value": "Attribute 'kms_key_id' should be set", + "actual_value": "Attribute 'kms_key_id' is undefined" + } + ] + }, + { + "query_name": "IAM Database Auth Not Enabled", + "query_id": "88fd05e0-ac0e-43d2-ba6d-fc0ba60ae1a6", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#iam_database_authentication_enabled", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "285", + "risk_score": "5.9", + "cloud_provider": "AWS", + "category": "Encryption", + "experimental": false, + "description": "IAM Database Auth Enabled should be configured to true when using compatible engine and version", + "description_id": "12b1c8aa", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "c0ea4d7eea7783d1d62230e39bca8f7038d3b66b4620c12429412ea4a99a24b2", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[records]", + "search_line": 25, + "search_value": "", + "expected_value": "'iam_database_authentication_enabled' should be set to true", + "actual_value": "'iam_database_authentication_enabled' is undefined or null", + "remediation": "iam_database_authentication_enabled = true", + "remediation_type": "addition" + } + ] + }, + { + "query_name": "RDS With Backup Disabled", + "query_id": "1dc73fb4-5b51-430c-8c5f-25dcf9090b02", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "754", + "risk_score": "5.4", + "cloud_provider": "AWS", + "category": "Backup", + "experimental": false, + "description": "Make sure the AWS RDS configuration has automatic backup configured. If the retention period is equal to 0 there is no backup", + "description_id": "73fdfe55", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "aa9b25b02db038eaf33bc851204ff93185b9ca45e4cf5ca0631bf741fe96d307", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[records]", + "search_line": 25, + "search_value": "", + "expected_value": "'backup_retention_period' should be defined, and bigger than '0'", + "actual_value": "'backup_retention_period' is not defined", + "remediation": "backup_retention_period = 12", + "remediation_type": "addition" + } + ] + }, + { + "query_name": "RDS Without Logging", + "query_id": "8d7f7b8c-6c7c-40f8-baa6-62006c6c7b56", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#enabled_cloudwatch_logs_exports", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "778", + "risk_score": "5.1", + "cloud_provider": "AWS", + "category": "Observability", + "experimental": false, + "description": "RDS does not have any kind of logger", + "description_id": "c499a58c", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "8bce8f99bcf15e7fe7a561230081f82d5a3abab3c9d0f18f09df798771aab847", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[records]", + "search_line": 25, + "search_value": "", + "expected_value": "'enabled_cloudwatch_logs_exports' should be defined", + "actual_value": "'enabled_cloudwatch_logs_exports' is undefined" + } + ] + }, + { + "query_name": "S3 Bucket Logging Disabled", + "query_id": "f861041c-8c9f-4156-acfc-5e6e524f5884", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "778", + "risk_score": "5.1", + "cloud_provider": "AWS", + "category": "Observability", + "experimental": false, + "description": "Server Access Logging should be enabled on S3 Buckets so that all changes are logged and trackable", + "description_id": "fa5c7c72", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "dd089a52c29b900af06e5b47519c09c5eb39a385b4f6c461bfb0a9c5f778bb7b", + "line": 5, + "resource_type": "aws_s3_bucket", + "resource_name": "example-compliance-reports", + "issue_type": "MissingAttribute", + "search_key": "aws_s3_bucket[reports]", + "search_line": 5, + "search_value": "", + "expected_value": "'logging' should be defined and not null", + "actual_value": "'logging' is undefined or null" + } + ] + }, + { + "query_name": "S3 Bucket Without Versioning", + "query_id": "568a4d22-3517-44a6-a7ad-6a7eed88722c", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket#versioning", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "710", + "risk_score": "5.7", + "cloud_provider": "AWS", + "category": "Backup", + "experimental": false, + "description": "S3 bucket should have versioning enabled", + "description_id": "7614ce3b", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "ab0463edcbd0e2152adcf6337f249a8f1737944f824e5a6d58844f087abd7de4", + "line": 5, + "resource_type": "aws_s3_bucket", + "resource_name": "example-compliance-reports", + "issue_type": "MissingAttribute", + "search_key": "aws_s3_bucket[reports]", + "search_line": 5, + "search_value": "", + "expected_value": "'versioning' should be true", + "actual_value": "'versioning' is undefined or null" + } + ] + }, + { + "query_name": "Security Group With Unrestricted Access To SSH", + "query_id": "65905cec-d691-4320-b320-2000436cb696", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "732", + "risk_score": "5.2", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "experimental": false, + "description": "'SSH' (TCP:22) should not be public in AWS Security Group", + "description_id": "51e59188", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "bfe9fa20120795e5de7b809e51ebcfc9bf0d39a3a9ae523258eabb7c83cf3d27", + "line": 17, + "resource_type": "aws_security_group", + "resource_name": "example-open", + "issue_type": "IncorrectValue", + "search_key": "aws_security_group[ingress_all].ingress", + "search_line": 17, + "search_value": "", + "expected_value": "aws_security_group[ingress_all].ingress 'SSH' (Port:22) should not be open", + "actual_value": "aws_security_group[ingress_all].ingress 'SSH' (Port:22) is open" + } + ] + }, + { + "query_name": "IAM Access Analyzer Not Enabled", + "query_id": "e592a0c5-5bdb-414c-9066-5dba7cdea370", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/accessanalyzer_analyzer", + "severity": "LOW", + "platform": "Terraform", + "cwe": "710", + "risk_score": "3.5", + "cloud_provider": "AWS", + "category": "Best Practices", + "experimental": false, + "description": "IAM Access Analyzer should be enabled and configured to continuously monitor resource permissions", + "description_id": "d03e85ae", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "0a7d0464de505a54720d6002d14a22a32e6b20bb8189d444ab621d8ab838304f", + "line": 5, + "resource_type": "n/a", + "resource_name": "n/a", + "issue_type": "MissingAttribute", + "search_key": "resource", + "search_line": -1, + "search_value": "", + "expected_value": "'aws_accessanalyzer_analyzer' should be set", + "actual_value": "'aws_accessanalyzer_analyzer' is undefined" + } + ] + }, + { + "query_name": "Tags Not Copied to RDS Cluster Snapshot", + "query_id": "6d3dead4-c6b2-4db7-81bd-3a83eae8f255", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster#copy_tags_to_snapshot", + "severity": "LOW", + "platform": "Terraform", + "cwe": "284", + "risk_score": "1.0", + "cloud_provider": "AWS", + "category": "Backup", + "experimental": false, + "description": "Tags of the RDS Cluster should be copied to the respective snapshots to ensure that snapshots retain important metadata for identification, cost allocation, and resource management", + "description_id": "6d3dead4", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "a1b2703a2862822f18cb98f8d043de10c8bd2b8cf58d0d89fc6677ec686268c4", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[records]", + "search_line": 25, + "search_value": "", + "expected_value": "'aws_db_instance[records].copy_tags_to_snapshot' should be defined to true", + "actual_value": "'aws_db_instance[records].copy_tags_to_snapshot' is not defined" + } + ] + }, + { + "query_name": "CloudWatch Without Retention Period Specified", + "query_id": "ef0b316a-211e-42f1-888e-64efe172b755", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group", + "severity": "INFO", + "platform": "Terraform", + "cwe": "778", + "risk_score": "0.0", + "cloud_provider": "AWS", + "category": "Observability", + "experimental": false, + "description": "AWS CloudWatch Log groups should have retention days specified", + "description_id": "64f08509", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "827beaf01470560c7f380d69f421b6af5460cf87cca1768b693b3d8bbb20b4e3", + "line": 9, + "resource_type": "aws_cloudwatch_log_group", + "resource_name": "example-audit-log", + "issue_type": "MissingAttribute", + "search_key": "aws_cloudwatch_log_group[audit]", + "search_line": 9, + "search_value": "", + "expected_value": "Attribute 'retention_in_days' should be set and valid", + "actual_value": "Attribute 'retention_in_days' is undefined", + "remediation": "retention_in_days = 7", + "remediation_type": "addition" + } + ] + }, + { + "query_name": "Resource Not Using Tags", + "query_id": "e38a8e0a-b88b-4902-b3fe-b0fcb17d5c10", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/resource-tagging", + "severity": "INFO", + "platform": "Terraform", + "cwe": "665", + "risk_score": "0.0", + "cloud_provider": "AWS", + "category": "Best Practices", + "experimental": false, + "description": "AWS services resource tags are an essential part of managing components. As a best practice, the field 'tags' should have additional tags defined other than 'Name'", + "description_id": "09db2d52", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "dabf4bc35ee0177cb62ac970c459db6e210ea48cf48f3a24e3a23a1d074029f5", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[{{records}}]", + "search_line": -1, + "search_value": "", + "expected_value": "aws_db_instance[{{records}}].tags should be defined and not null", + "actual_value": "aws_db_instance[{{records}}].tags is undefined or null" + }, + { + "file_name": "../../path/main.tf", + "similarity_id": "26c1c65539921002d1aea7581c6d8acc41ea9c1f7b552b4a444d0e47aa548c93", + "line": 5, + "resource_type": "aws_s3_bucket", + "resource_name": "reports", + "issue_type": "MissingAttribute", + "search_key": "aws_s3_bucket[{{reports}}]", + "search_line": -1, + "search_value": "", + "expected_value": "aws_s3_bucket[{{reports}}].tags should be defined and not null", + "actual_value": "aws_s3_bucket[{{reports}}].tags is undefined or null" + }, + { + "file_name": "../../path/main.tf", + "similarity_id": "4405b311b47b3a3b2326ddafb88c9d4f4e513a675fcec5ff87f635b4d3fe6ff5", + "line": 13, + "resource_type": "aws_security_group", + "resource_name": "example-open", + "issue_type": "MissingAttribute", + "search_key": "aws_security_group[{{ingress_all}}]", + "search_line": -1, + "search_value": "", + "expected_value": "aws_security_group[{{ingress_all}}].tags should be defined and not null", + "actual_value": "aws_security_group[{{ingress_all}}].tags is undefined or null" + }, + { + "file_name": "../../path/main.tf", + "similarity_id": "1b05b071bac6ac0198c9b8425698cfbcd37aa31610f34e3dcff587994c4f34d0", + "line": 9, + "resource_type": "aws_cloudwatch_log_group", + "resource_name": "example-audit-log", + "issue_type": "MissingAttribute", + "search_key": "aws_cloudwatch_log_group[{{audit}}]", + "search_line": -1, + "search_value": "", + "expected_value": "aws_cloudwatch_log_group[{{audit}}].tags should be defined and not null", + "actual_value": "aws_cloudwatch_log_group[{{audit}}].tags is undefined or null" + } + ] + }, + { + "query_name": "Security Group Not Used", + "query_id": "4849211b-ac39-479e-ae78-5694d506cb24", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group", + "severity": "INFO", + "platform": "Terraform", + "cwe": "284", + "risk_score": "0.0", + "cloud_provider": "AWS", + "category": "Access Control", + "experimental": false, + "description": "Security group must be used or not declared", + "description_id": "eff2383a", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "eaeb0a34f2e1fec1d3728544ba681e17e6b431a6b9fa284ffbe5b0aa00ccf9e9", + "line": 13, + "resource_type": "aws_security_group", + "resource_name": "example-open", + "issue_type": "IncorrectValue", + "search_key": "aws_security_group[ingress_all]", + "search_line": -1, + "search_value": "", + "expected_value": "'aws_security_group[ingress_all]' should be used", + "actual_value": "'aws_security_group[ingress_all]' is not used" + } + ] + }, + { + "query_name": "Security Group Rule Without Description", + "query_id": "68eb4bf3-f9bf-463d-b5cf-e029bb446d2e", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group#description", + "severity": "INFO", + "platform": "Terraform", + "cwe": "710", + "risk_score": "0.0", + "cloud_provider": "AWS", + "category": "Best Practices", + "experimental": false, + "description": "It's considered a best practice for all rules in AWS Security Group to have a description", + "description_id": "cee242dd", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "d9ddb43d6e48365b19f5517f58aa5656a7a5c068b499c46d7f5bc8425ba0ec59", + "line": 17, + "resource_type": "aws_security_group", + "resource_name": "ingress_all", + "issue_type": "MissingAttribute", + "search_key": "aws_security_group[ingress_all].ingress", + "search_line": 17, + "search_value": "", + "expected_value": "aws_security_group[ingress_all].ingress.description should be defined and not null", + "actual_value": "aws_security_group[ingress_all].ingress.description is undefined or null" + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/libs/hdf-converters/sample_jsons/kics_mapper/kics_zero_findings-hdf.json b/libs/hdf-converters/sample_jsons/kics_mapper/kics_zero_findings-hdf.json new file mode 100644 index 0000000000..b12e5025c4 --- /dev/null +++ b/libs/hdf-converters/sample_jsons/kics_mapper/kics_zero_findings-hdf.json @@ -0,0 +1,65 @@ +{ + "platform": { + "name": "Heimdall Tools", + "release": "2.13.0" + }, + "version": "2.13.0", + "statistics": {}, + "profiles": [ + { + "name": "KICS", + "title": "KICS Infrastructure-as-Code Scan", + "version": "v2.1.20", + "supports": [], + "attributes": [], + "groups": [], + "status": "loaded", + "controls": [ + { + "id": "kics-scan-coverage", + "title": "KICS scan coverage", + "impact": 0, + "desc": "KICS executed 1100 queries against 1 file(s); 0 produced findings. KICS reports violations only and does not enumerate the queries that ran without finding anything, so no passing controls can be derived from its output and the compliance ratio should not be read as a pass rate.", + "refs": [], + "source_location": {}, + "code": "", + "tags": { + "queries_executed": 1100, + "queries_with_findings": 0, + "files_scanned": 1, + "files_parsed": 1 + }, + "results": [ + { + "status": "passed", + "code_desc": "KICS executed 1100 queries against 1 file(s); 0 produced findings. KICS reports violations only and does not enumerate the queries that ran without finding anything, so no passing controls can be derived from its output and the compliance ratio should not be read as a pass rate.", + "start_time": "" + } + ] + } + ], + "sha256": "68019f2a0c1342b23ead8360c43b5e2669df996b30638c2825321417525001d3" + } + ], + "passthrough": { + "auxiliary_data": [ + { + "name": "KICS", + "data": { + "kics_version": "v2.1.20", + "severity_counters": { + "CRITICAL": 0, + "HIGH": 0, + "INFO": 0, + "LOW": 0, + "MEDIUM": 0, + "TRACE": 0 + }, + "total_counter": 0, + "files_scanned": 1, + "queries_total": 1100 + } + } + ] + } +} \ No newline at end of file diff --git a/libs/hdf-converters/sample_jsons/kics_mapper/sample_input_report/kics_findings.json b/libs/hdf-converters/sample_jsons/kics_mapper/sample_input_report/kics_findings.json new file mode 100644 index 0000000000..8d8b7ccd87 --- /dev/null +++ b/libs/hdf-converters/sample_jsons/kics_mapper/sample_input_report/kics_findings.json @@ -0,0 +1,533 @@ +{ + "kics_version": "v2.1.20", + "files_scanned": 1, + "lines_scanned": 32, + "files_parsed": 1, + "lines_parsed": 32, + "lines_ignored": 0, + "files_failed_to_scan": 0, + "queries_total": 1100, + "queries_failed_to_execute": 0, + "queries_failed_to_compute_similarity_id": 0, + "severity_counters": { + "CRITICAL": 0, + "HIGH": 3, + "INFO": 7, + "LOW": 2, + "MEDIUM": 7, + "TRACE": 0 + }, + "total_counter": 19, + "total_bom_resources": 0, + "queries": [ + { + "query_name": "DB Instance Storage Not Encrypted", + "query_id": "08bd0760-8752-44e1-9779-7bb369b2b4e4", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#storage_encrypted", + "severity": "HIGH", + "platform": "Terraform", + "cwe": "311", + "risk_score": "7.1", + "cloud_provider": "AWS", + "category": "Encryption", + "experimental": false, + "description": "AWS DB Instance should have its storage encrypted by setting the parameter to 'true'. The storage_encrypted default value is 'false'.", + "description_id": "88ca11bc", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "4efca9c9c9328e651ec53a8d38e4c3b25f5940cd2e3eb9ca1ef6a5625cf2dab0", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[records]", + "search_line": 25, + "search_value": "", + "expected_value": "'storage_encrypted' should be set to true", + "actual_value": "'storage_encrypted' is undefined or null" + } + ] + }, + { + "query_name": "Sensitive Port Is Exposed To Entire Network", + "query_id": "381c3f2a-ef6f-4eff-99f7-b169cda3422c", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group", + "severity": "HIGH", + "platform": "Terraform", + "cwe": "200", + "risk_score": "7.7", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "experimental": false, + "description": "A sensitive port, such as port 23 or port 110, is open for the whole network in either TCP or UDP protocol", + "description_id": "5be78364", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "9dbff16a6c0c14f73d415aaff331196059d11c552fff0288b84acb2462dbecb6", + "line": 17, + "resource_type": "aws_security_group", + "resource_name": "example-open", + "issue_type": "IncorrectValue", + "search_key": "aws_security_group[ingress_all].ingress", + "search_line": 17, + "search_value": "TCP,22", + "expected_value": "SSH (TCP:22) should not be allowed", + "actual_value": "SSH (TCP:22) is allowed" + } + ] + }, + { + "query_name": "Unrestricted Security Group Ingress", + "query_id": "4728cd65-a20c-49da-8b31-9c08b423e4db", + "query_url": "https://www.terraform.io/docs/providers/aws/r/security_group.html", + "severity": "HIGH", + "platform": "Terraform", + "cwe": "668", + "risk_score": "7.2", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "experimental": false, + "description": "Security groups allow ingress from 0.0.0.0:0 and/or ::/0", + "description_id": "ce3ee5e0", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "c69e33189fa2e8f28646c2ca82654121f4b2372b03838ed353522b4d11223e0c", + "line": 21, + "resource_type": "aws_security_group", + "resource_name": "example-open", + "issue_type": "IncorrectValue", + "search_key": "aws_security_group[ingress_all].ingress.cidr_blocks", + "search_line": 21, + "search_value": "", + "expected_value": "aws_security_group[ingress_all].ingress.cidr_blocks should not contain '0.0.0.0/0'", + "actual_value": "aws_security_group[ingress_all].ingress.cidr_blocks contains '0.0.0.0/0'" + } + ] + }, + { + "query_name": "CloudWatch Log Group Without KMS", + "query_id": "0afbcfe9-d341-4b92-a64c-7e6de0543879", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "311", + "risk_score": "5.5", + "cloud_provider": "AWS", + "category": "Encryption", + "experimental": false, + "description": "AWS CloudWatch Log groups should be encrypted using KMS", + "description_id": "4258abe6", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "2737ae3898f04339f701da0891accf68690355971adb9ff9d4351b0db104d7b0", + "line": 9, + "resource_type": "aws_cloudwatch_log_group", + "resource_name": "example-audit-log", + "issue_type": "MissingAttribute", + "search_key": "aws_cloudwatch_log_group[audit]", + "search_line": -1, + "search_value": "", + "expected_value": "Attribute 'kms_key_id' should be set", + "actual_value": "Attribute 'kms_key_id' is undefined" + } + ] + }, + { + "query_name": "IAM Database Auth Not Enabled", + "query_id": "88fd05e0-ac0e-43d2-ba6d-fc0ba60ae1a6", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#iam_database_authentication_enabled", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "285", + "risk_score": "5.9", + "cloud_provider": "AWS", + "category": "Encryption", + "experimental": false, + "description": "IAM Database Auth Enabled should be configured to true when using compatible engine and version", + "description_id": "12b1c8aa", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "c0ea4d7eea7783d1d62230e39bca8f7038d3b66b4620c12429412ea4a99a24b2", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[records]", + "search_line": 25, + "search_value": "", + "expected_value": "'iam_database_authentication_enabled' should be set to true", + "actual_value": "'iam_database_authentication_enabled' is undefined or null", + "remediation": "iam_database_authentication_enabled = true", + "remediation_type": "addition" + } + ] + }, + { + "query_name": "RDS With Backup Disabled", + "query_id": "1dc73fb4-5b51-430c-8c5f-25dcf9090b02", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "754", + "risk_score": "5.4", + "cloud_provider": "AWS", + "category": "Backup", + "experimental": false, + "description": "Make sure the AWS RDS configuration has automatic backup configured. If the retention period is equal to 0 there is no backup", + "description_id": "73fdfe55", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "aa9b25b02db038eaf33bc851204ff93185b9ca45e4cf5ca0631bf741fe96d307", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[records]", + "search_line": 25, + "search_value": "", + "expected_value": "'backup_retention_period' should be defined, and bigger than '0'", + "actual_value": "'backup_retention_period' is not defined", + "remediation": "backup_retention_period = 12", + "remediation_type": "addition" + } + ] + }, + { + "query_name": "RDS Without Logging", + "query_id": "8d7f7b8c-6c7c-40f8-baa6-62006c6c7b56", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance#enabled_cloudwatch_logs_exports", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "778", + "risk_score": "5.1", + "cloud_provider": "AWS", + "category": "Observability", + "experimental": false, + "description": "RDS does not have any kind of logger", + "description_id": "c499a58c", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "8bce8f99bcf15e7fe7a561230081f82d5a3abab3c9d0f18f09df798771aab847", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[records]", + "search_line": 25, + "search_value": "", + "expected_value": "'enabled_cloudwatch_logs_exports' should be defined", + "actual_value": "'enabled_cloudwatch_logs_exports' is undefined" + } + ] + }, + { + "query_name": "S3 Bucket Logging Disabled", + "query_id": "f861041c-8c9f-4156-acfc-5e6e524f5884", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "778", + "risk_score": "5.1", + "cloud_provider": "AWS", + "category": "Observability", + "experimental": false, + "description": "Server Access Logging should be enabled on S3 Buckets so that all changes are logged and trackable", + "description_id": "fa5c7c72", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "dd089a52c29b900af06e5b47519c09c5eb39a385b4f6c461bfb0a9c5f778bb7b", + "line": 5, + "resource_type": "aws_s3_bucket", + "resource_name": "example-compliance-reports", + "issue_type": "MissingAttribute", + "search_key": "aws_s3_bucket[reports]", + "search_line": 5, + "search_value": "", + "expected_value": "'logging' should be defined and not null", + "actual_value": "'logging' is undefined or null" + } + ] + }, + { + "query_name": "S3 Bucket Without Versioning", + "query_id": "568a4d22-3517-44a6-a7ad-6a7eed88722c", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket#versioning", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "710", + "risk_score": "5.7", + "cloud_provider": "AWS", + "category": "Backup", + "experimental": false, + "description": "S3 bucket should have versioning enabled", + "description_id": "7614ce3b", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "ab0463edcbd0e2152adcf6337f249a8f1737944f824e5a6d58844f087abd7de4", + "line": 5, + "resource_type": "aws_s3_bucket", + "resource_name": "example-compliance-reports", + "issue_type": "MissingAttribute", + "search_key": "aws_s3_bucket[reports]", + "search_line": 5, + "search_value": "", + "expected_value": "'versioning' should be true", + "actual_value": "'versioning' is undefined or null" + } + ] + }, + { + "query_name": "Security Group With Unrestricted Access To SSH", + "query_id": "65905cec-d691-4320-b320-2000436cb696", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group", + "severity": "MEDIUM", + "platform": "Terraform", + "cwe": "732", + "risk_score": "5.2", + "cloud_provider": "AWS", + "category": "Networking and Firewall", + "experimental": false, + "description": "'SSH' (TCP:22) should not be public in AWS Security Group", + "description_id": "51e59188", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "bfe9fa20120795e5de7b809e51ebcfc9bf0d39a3a9ae523258eabb7c83cf3d27", + "line": 17, + "resource_type": "aws_security_group", + "resource_name": "example-open", + "issue_type": "IncorrectValue", + "search_key": "aws_security_group[ingress_all].ingress", + "search_line": 17, + "search_value": "", + "expected_value": "aws_security_group[ingress_all].ingress 'SSH' (Port:22) should not be open", + "actual_value": "aws_security_group[ingress_all].ingress 'SSH' (Port:22) is open" + } + ] + }, + { + "query_name": "IAM Access Analyzer Not Enabled", + "query_id": "e592a0c5-5bdb-414c-9066-5dba7cdea370", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/accessanalyzer_analyzer", + "severity": "LOW", + "platform": "Terraform", + "cwe": "710", + "risk_score": "3.5", + "cloud_provider": "AWS", + "category": "Best Practices", + "experimental": false, + "description": "IAM Access Analyzer should be enabled and configured to continuously monitor resource permissions", + "description_id": "d03e85ae", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "0a7d0464de505a54720d6002d14a22a32e6b20bb8189d444ab621d8ab838304f", + "line": 5, + "resource_type": "n/a", + "resource_name": "n/a", + "issue_type": "MissingAttribute", + "search_key": "resource", + "search_line": -1, + "search_value": "", + "expected_value": "'aws_accessanalyzer_analyzer' should be set", + "actual_value": "'aws_accessanalyzer_analyzer' is undefined" + } + ] + }, + { + "query_name": "Tags Not Copied to RDS Cluster Snapshot", + "query_id": "6d3dead4-c6b2-4db7-81bd-3a83eae8f255", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/rds_cluster#copy_tags_to_snapshot", + "severity": "LOW", + "platform": "Terraform", + "cwe": "284", + "risk_score": "1.0", + "cloud_provider": "AWS", + "category": "Backup", + "experimental": false, + "description": "Tags of the RDS Cluster should be copied to the respective snapshots to ensure that snapshots retain important metadata for identification, cost allocation, and resource management", + "description_id": "6d3dead4", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "a1b2703a2862822f18cb98f8d043de10c8bd2b8cf58d0d89fc6677ec686268c4", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[records]", + "search_line": 25, + "search_value": "", + "expected_value": "'aws_db_instance[records].copy_tags_to_snapshot' should be defined to true", + "actual_value": "'aws_db_instance[records].copy_tags_to_snapshot' is not defined" + } + ] + }, + { + "query_name": "CloudWatch Without Retention Period Specified", + "query_id": "ef0b316a-211e-42f1-888e-64efe172b755", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group", + "severity": "INFO", + "platform": "Terraform", + "cwe": "778", + "risk_score": "0.0", + "cloud_provider": "AWS", + "category": "Observability", + "experimental": false, + "description": "AWS CloudWatch Log groups should have retention days specified", + "description_id": "64f08509", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "827beaf01470560c7f380d69f421b6af5460cf87cca1768b693b3d8bbb20b4e3", + "line": 9, + "resource_type": "aws_cloudwatch_log_group", + "resource_name": "example-audit-log", + "issue_type": "MissingAttribute", + "search_key": "aws_cloudwatch_log_group[audit]", + "search_line": 9, + "search_value": "", + "expected_value": "Attribute 'retention_in_days' should be set and valid", + "actual_value": "Attribute 'retention_in_days' is undefined", + "remediation": "retention_in_days = 7", + "remediation_type": "addition" + } + ] + }, + { + "query_name": "Resource Not Using Tags", + "query_id": "e38a8e0a-b88b-4902-b3fe-b0fcb17d5c10", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/resource-tagging", + "severity": "INFO", + "platform": "Terraform", + "cwe": "665", + "risk_score": "0.0", + "cloud_provider": "AWS", + "category": "Best Practices", + "experimental": false, + "description": "AWS services resource tags are an essential part of managing components. As a best practice, the field 'tags' should have additional tags defined other than 'Name'", + "description_id": "09db2d52", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "dabf4bc35ee0177cb62ac970c459db6e210ea48cf48f3a24e3a23a1d074029f5", + "line": 25, + "resource_type": "aws_db_instance", + "resource_name": "records", + "issue_type": "MissingAttribute", + "search_key": "aws_db_instance[{{records}}]", + "search_line": -1, + "search_value": "", + "expected_value": "aws_db_instance[{{records}}].tags should be defined and not null", + "actual_value": "aws_db_instance[{{records}}].tags is undefined or null" + }, + { + "file_name": "../../path/main.tf", + "similarity_id": "26c1c65539921002d1aea7581c6d8acc41ea9c1f7b552b4a444d0e47aa548c93", + "line": 5, + "resource_type": "aws_s3_bucket", + "resource_name": "reports", + "issue_type": "MissingAttribute", + "search_key": "aws_s3_bucket[{{reports}}]", + "search_line": -1, + "search_value": "", + "expected_value": "aws_s3_bucket[{{reports}}].tags should be defined and not null", + "actual_value": "aws_s3_bucket[{{reports}}].tags is undefined or null" + }, + { + "file_name": "../../path/main.tf", + "similarity_id": "4405b311b47b3a3b2326ddafb88c9d4f4e513a675fcec5ff87f635b4d3fe6ff5", + "line": 13, + "resource_type": "aws_security_group", + "resource_name": "example-open", + "issue_type": "MissingAttribute", + "search_key": "aws_security_group[{{ingress_all}}]", + "search_line": -1, + "search_value": "", + "expected_value": "aws_security_group[{{ingress_all}}].tags should be defined and not null", + "actual_value": "aws_security_group[{{ingress_all}}].tags is undefined or null" + }, + { + "file_name": "../../path/main.tf", + "similarity_id": "1b05b071bac6ac0198c9b8425698cfbcd37aa31610f34e3dcff587994c4f34d0", + "line": 9, + "resource_type": "aws_cloudwatch_log_group", + "resource_name": "example-audit-log", + "issue_type": "MissingAttribute", + "search_key": "aws_cloudwatch_log_group[{{audit}}]", + "search_line": -1, + "search_value": "", + "expected_value": "aws_cloudwatch_log_group[{{audit}}].tags should be defined and not null", + "actual_value": "aws_cloudwatch_log_group[{{audit}}].tags is undefined or null" + } + ] + }, + { + "query_name": "Security Group Not Used", + "query_id": "4849211b-ac39-479e-ae78-5694d506cb24", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group", + "severity": "INFO", + "platform": "Terraform", + "cwe": "284", + "risk_score": "0.0", + "cloud_provider": "AWS", + "category": "Access Control", + "experimental": false, + "description": "Security group must be used or not declared", + "description_id": "eff2383a", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "eaeb0a34f2e1fec1d3728544ba681e17e6b431a6b9fa284ffbe5b0aa00ccf9e9", + "line": 13, + "resource_type": "aws_security_group", + "resource_name": "example-open", + "issue_type": "IncorrectValue", + "search_key": "aws_security_group[ingress_all]", + "search_line": -1, + "search_value": "", + "expected_value": "'aws_security_group[ingress_all]' should be used", + "actual_value": "'aws_security_group[ingress_all]' is not used" + } + ] + }, + { + "query_name": "Security Group Rule Without Description", + "query_id": "68eb4bf3-f9bf-463d-b5cf-e029bb446d2e", + "query_url": "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group#description", + "severity": "INFO", + "platform": "Terraform", + "cwe": "710", + "risk_score": "0.0", + "cloud_provider": "AWS", + "category": "Best Practices", + "experimental": false, + "description": "It's considered a best practice for all rules in AWS Security Group to have a description", + "description_id": "cee242dd", + "files": [ + { + "file_name": "../../path/main.tf", + "similarity_id": "d9ddb43d6e48365b19f5517f58aa5656a7a5c068b499c46d7f5bc8425ba0ec59", + "line": 17, + "resource_type": "aws_security_group", + "resource_name": "ingress_all", + "issue_type": "MissingAttribute", + "search_key": "aws_security_group[ingress_all].ingress", + "search_line": 17, + "search_value": "", + "expected_value": "aws_security_group[ingress_all].ingress.description should be defined and not null", + "actual_value": "aws_security_group[ingress_all].ingress.description is undefined or null" + } + ] + } + ] +} diff --git a/libs/hdf-converters/sample_jsons/kics_mapper/sample_input_report/kics_zero_findings.json b/libs/hdf-converters/sample_jsons/kics_mapper/sample_input_report/kics_zero_findings.json new file mode 100644 index 0000000000..3e1c80a253 --- /dev/null +++ b/libs/hdf-converters/sample_jsons/kics_mapper/sample_input_report/kics_zero_findings.json @@ -0,0 +1,23 @@ +{ + "kics_version": "v2.1.20", + "files_scanned": 1, + "lines_scanned": 4, + "files_parsed": 1, + "lines_parsed": 4, + "lines_ignored": 0, + "files_failed_to_scan": 0, + "queries_total": 1100, + "queries_failed_to_execute": 0, + "queries_failed_to_compute_similarity_id": 0, + "severity_counters": { + "CRITICAL": 0, + "HIGH": 0, + "INFO": 0, + "LOW": 0, + "MEDIUM": 0, + "TRACE": 0 + }, + "total_counter": 0, + "total_bom_resources": 0, + "queries": [] +} diff --git a/libs/hdf-converters/src/kics-mapper.ts b/libs/hdf-converters/src/kics-mapper.ts new file mode 100644 index 0000000000..979d33c031 --- /dev/null +++ b/libs/hdf-converters/src/kics-mapper.ts @@ -0,0 +1,376 @@ +import {ExecJSON} from 'inspecjs'; +import * as _ from 'lodash'; +import {version as HeimdallToolsVersion} from '../package.json'; +import {BaseConverter, ILookupPath, MappedTransform} from './base-converter'; +import {CweNistMapping} from './mappings/CweNistMapping'; +import { + data as KicsMappingData, + KicsMappingEntry +} from './mappings/KicsToCciAndNistMappingData'; +import { + conditionallyProvideAttribute, + DEFAULT_STATIC_CODE_ANALYSIS_NIST_TAGS, + getCCIsForNISTTags +} from './utils/global'; + +// One occurrence of a query, against a specific file and resource. +type KicsFile = { + file_name?: string; + similarity_id?: string; + line?: number; + resource_type?: string; + resource_name?: string; + issue_type?: string; + search_key?: string; + search_line?: number; + search_value?: string; + expected_value?: string; + actual_value?: string; + [property: string]: unknown; +}; + +// One query and every place it fired. KICS already groups its output this way, +// so a query maps onto one control and each occurrence onto one result. +type KicsQuery = { + query_id?: string; + query_name?: string; + query_url?: string; + severity?: string; + platform?: string; + cwe?: string; + risk_score?: string | number; + cloud_provider?: string; + category?: string; + experimental?: boolean; + description?: string; + description_id?: string; + files?: KicsFile[]; + [property: string]: unknown; +}; + +type KicsReport = { + queries: KicsQuery[]; + kics_version?: string; + severity_counters?: Record; + total_counter?: number; + files_scanned?: number; + queries_total?: number; + [property: string]: unknown; +}; + +const CWE_NIST_MAPPING = new CweNistMapping(); + +// KICS publishes five severities. Its SARIF output collapses CRITICAL and HIGH +// into `error`; converting the native format keeps them apart. +// +// No level maps to 0: impact 0.0 reports Not Applicable in HDF, which would +// drop the finding from the compliance score rather than rating it low. +// https://docs.kics.io/latest/results/ +const IMPACT_MAPPING: Map = new Map([ + ['critical', 0.9], + ['high', 0.7], + ['medium', 0.5], + ['low', 0.3], + ['info', 0.1], + ['trace', 0.1] +]); + +const DEFAULT_IMPACT = 0.5; + +// Records which tier answered, so a reviewed mapping stays distinguishable from +// a CWE-derived guess and from a static default. +// +// The per-query table is the authoritative source, matching how the Checkov and +// AWS Config mappers work: the rule-to-control decision is made and reviewed +// before shipping rather than computed at conversion time, which is also what +// lets those tables carry control enhancements. CWE is a fallback because it is +// a lossy proxy — KICS carries a CWE on all 1,811 of its shipped queries, but +// only 30 of the 102 distinct CWEs it uses resolve against CweNistMappingData, +// 52% of queries by volume. +const NIST_MAPPING_TAG = 'nistMapping'; +const NIST_MAPPING_TABLE = 'mapped'; +const NIST_MAPPING_CWE = 'cwe-derived'; +const NIST_MAPPING_FALLBACK = 'static-fallback'; + +// KICS emits the bare number, e.g. "778". +function cweIdentifiers(query: KicsQuery): string[] { + const raw = query.cwe; + if (raw === undefined || raw === null) { + return []; + } + const id = `${raw}`.trim().replace(/^CWE-/i, ''); + return /^\d+$/.test(id) ? [id] : []; +} + +function impactMapping(query: KicsQuery): number { + const severity = query.severity; + if (!_.isString(severity) || severity.length === 0) { + return DEFAULT_IMPACT; + } + return IMPACT_MAPPING.get(severity.toLowerCase()) ?? DEFAULT_IMPACT; +} + +// Resolution order: the reviewed per-query table, then the query's CWE, then the +// static-analysis defaults. `table` is a parameter so the precedence is testable +// without shipping unreviewed rows in the table itself. +export function resolveControls( + query: KicsQuery, + table: Record = KicsMappingData +): {nist: string[]; cci: string[]; source: string} { + const entry = _.isString(query.query_id) ? table[query.query_id] : undefined; + if (entry !== undefined && entry.nist.length > 0) { + return {nist: entry.nist, cci: entry.cci, source: NIST_MAPPING_TABLE}; + } + + const fromCwe = CWE_NIST_MAPPING.nistFilter(cweIdentifiers(query), []); + if (fromCwe.length > 0) { + return { + nist: fromCwe, + cci: getCCIsForNISTTags(fromCwe), + source: NIST_MAPPING_CWE + }; + } + + const fallback = [...DEFAULT_STATIC_CODE_ANALYSIS_NIST_TAGS]; + return { + nist: fallback, + cci: getCCIsForNISTTags(fallback), + source: NIST_MAPPING_FALLBACK + }; +} + +function formatCodeDesc(file: KicsFile): string { + const parts = [`File: ${file.file_name ?? 'unknown'}`]; + if (typeof file.line === 'number' && file.line > 0) { + parts.push(`Line: ${file.line}`); + } + if (file.resource_type) { + parts.push(`Resource type: ${file.resource_type}`); + } + if (file.resource_name && file.resource_name !== 'unknown') { + parts.push(`Resource: ${file.resource_name}`); + } + if (file.search_key) { + parts.push(`Key: ${file.search_key}`); + } + return parts.join('\n'); +} + +// The remediation pair. KICS's SARIF keeps only actual_value inside its +// message, so a SARIF-derived control can say what the configuration is but +// never what it should be. +function formatMessage(file: KicsFile): string { + const parts: string[] = []; + if (file.expected_value) { + parts.push(`Expected: ${file.expected_value}`); + } + if (file.actual_value) { + parts.push(`Actual: ${file.actual_value}`); + } + if (file.issue_type) { + parts.push(`Issue type: ${file.issue_type}`); + } + if (file.search_value) { + parts.push(`Search value: ${file.search_value}`); + } + return parts.join('\n'); +} + +// KICS reports violations only. Its output carries no record of the queries that +// ran without finding anything — `queries` holds only those that fired — so no +// passing control can be derived from it, and a converted profile is +// failures-only by construction. That makes the compliance percentage +// misleading on its own: a scan where 72 of 2,034 queries fired renders as 100% +// failed. This control carries the denominator so the ratio is legible. +function formatCoverage(data: KicsReport): string { + const fired = (data.queries ?? []).filter( + (query) => (query.files ?? []).length > 0 + ).length; + return ( + `KICS executed ${data.queries_total ?? 0} queries against ` + + `${data.files_scanned ?? 0} file(s); ${fired} produced findings. ` + + 'KICS reports violations only and does not enumerate the queries that ran ' + + 'without finding anything, so no passing controls can be derived from its ' + + 'output and the compliance ratio should not be read as a pass rate.' + ); +} + +function distinct(values: unknown[]): string[] { + return [...new Set(values.filter(_.isString))]; +} + +export class KicsMapper extends BaseConverter { + withRaw: boolean; + + mappings: MappedTransform< + ExecJSON.Execution & {passthrough: unknown}, + ILookupPath + > = { + platform: { + name: 'Heimdall Tools', + release: HeimdallToolsVersion + }, + version: HeimdallToolsVersion, + statistics: {}, + profiles: [ + { + name: 'KICS', + title: 'KICS Infrastructure-as-Code Scan', + version: {path: 'kics_version'}, + supports: [], + attributes: [], + groups: [], + status: 'loaded', + controls: [ + { + // One control per query: KICS metadata is query-scoped and + // identical across occurrences, so only the location varies. + path: 'queries', + key: 'id', + id: {path: 'query_id'}, + title: {path: 'query_name'}, + desc: {path: 'description'}, + impact: {transformer: impactMapping}, + refs: [ + { + path: 'query_url', + transformer: (url: unknown): Record => + _.isString(url) && url.length > 0 ? {url} : {} + } + ], + source_location: {}, + code: '', + tags: { + transformer: (query: KicsQuery): Record => { + const {nist, cci, source} = resolveControls(query); + const cwe = cweIdentifiers(query); + const files = query.files ?? []; + return { + nist, + cci, + [NIST_MAPPING_TAG]: source, + // Kept even when it does not resolve: an unmapped CWE that is + // invisible in the output is a gap nobody can see. + ...conditionallyProvideAttribute( + 'cwe', + cwe.map((id) => `CWE-${id}`), + cwe.length > 0 + ), + ...conditionallyProvideAttribute( + 'severity', + query.severity, + _.isString(query.severity) + ), + ...conditionallyProvideAttribute( + 'platform', + query.platform, + _.isString(query.platform) + ), + ...conditionallyProvideAttribute( + 'cloud_provider', + query.cloud_provider, + _.isString(query.cloud_provider) + ), + ...conditionallyProvideAttribute( + 'category', + query.category, + _.isString(query.category) + ), + ...conditionallyProvideAttribute( + 'risk_score', + `${query.risk_score}`, + query.risk_score !== undefined + ), + ...conditionallyProvideAttribute( + 'description_id', + query.description_id, + _.isString(query.description_id) + ), + ...conditionallyProvideAttribute( + 'experimental', + true, + query.experimental === true + ), + ...conditionallyProvideAttribute( + 'issue_type', + distinct(files.map((f) => f.issue_type)), + distinct(files.map((f) => f.issue_type)).length > 0 + ), + ...conditionallyProvideAttribute( + 'resource_type', + distinct(files.map((f) => f.resource_type)), + distinct(files.map((f) => f.resource_type)).length > 0 + ) + }; + } + }, + results: [ + { + path: 'files', + // KICS reports only violations; there is no passing or + // suppressed state in its output to derive anything else from. + status: ExecJSON.ControlResultStatus.Failed, + code_desc: {transformer: formatCodeDesc}, + message: {transformer: formatMessage}, + start_time: '' + } + ] + }, + { + id: 'kics-scan-coverage', + title: 'KICS scan coverage', + // Impact 0 reports Not Applicable, so a record of scan context + // cannot skew the compliance ratio it exists to explain. + impact: 0, + desc: {transformer: formatCoverage}, + refs: [], + source_location: {}, + code: '', + tags: { + transformer: (data: KicsReport): Record => ({ + queries_executed: data.queries_total ?? 0, + queries_with_findings: (data.queries ?? []).filter( + (query) => (query.files ?? []).length > 0 + ).length, + files_scanned: data.files_scanned ?? 0, + files_parsed: data.files_parsed ?? 0 + }) + }, + results: [ + { + status: ExecJSON.ControlResultStatus.Passed, + code_desc: {transformer: formatCoverage}, + start_time: '' + } + ] + } + ], + sha256: '' + } + ], + passthrough: { + transformer: (data: KicsReport): Record => { + return { + auxiliary_data: [ + { + name: 'KICS', + data: { + kics_version: data.kics_version, + severity_counters: data.severity_counters, + total_counter: data.total_counter, + files_scanned: data.files_scanned, + queries_total: data.queries_total + } + } + ], + ...conditionallyProvideAttribute('raw', data, this.withRaw) + }; + } + } + }; + + constructor(kicsJson: string, withRaw = false) { + super(JSON.parse(kicsJson) as KicsReport); + this.withRaw = withRaw; + } +} diff --git a/libs/hdf-converters/src/mappings/KicsToCciAndNistMappingData.ts b/libs/hdf-converters/src/mappings/KicsToCciAndNistMappingData.ts new file mode 100644 index 0000000000..055d284f60 --- /dev/null +++ b/libs/hdf-converters/src/mappings/KicsToCciAndNistMappingData.ts @@ -0,0 +1,24 @@ +// CCI + NIST Mappings for KICS queries, keyed on query_id. +// +// Mapping Methodology: +// KICS ships its full query catalog as data — 1,811 metadata.json files under +// /app/bin/assets/queries/ in the distributed image — so every query is known +// ahead of a scan rather than discovered from one. +// +// Candidate controls are proposed by ranking each query's name, description +// and category against the NIST SP 800-53 Rev 5 catalog at sub-part +// granularity, then adjudicated by a qualified reviewer. Rank order is a +// review queue, not a mapping; nothing enters this table unreviewed. +// +// Total queries in catalog: 1,811 +// Total queries mapped: 0 (adjudication in progress) +// +// Until a query appears here the converter falls back to its CWE, and then to +// the static-analysis defaults, recording which tier answered in the +// `nistMapping` tag so a resolved mapping stays distinguishable from a guess. +// +// See mitre/hdf-libs#239 for the adjudication process and the candidate set. + +export type KicsMappingEntry = {cci: string[]; nist: string[]}; + +export const data: Record = {}; diff --git a/libs/hdf-converters/src/utils/fingerprinting.ts b/libs/hdf-converters/src/utils/fingerprinting.ts index cb5dcf6fb6..5a86e775b8 100644 --- a/libs/hdf-converters/src/utils/fingerprinting.ts +++ b/libs/hdf-converters/src/utils/fingerprinting.ts @@ -12,6 +12,7 @@ export enum INPUT_TYPES { GRYPE = 'grype', IONCHANNEL = 'ionchannel', JFROG = 'jfrog', + KICS = 'kics', MSFT_SEC_SCORE = 'msft_secure_score', NIKTO = 'nikto', SARIF = 'sarif', @@ -57,6 +58,12 @@ const fileTypeFingerprints: Record = { 'trigger_hash' ], [INPUT_TYPES.JFROG]: ['total_count', 'data'], + [INPUT_TYPES.KICS]: [ + 'queries', + 'kics_version', + 'severity_counters', + 'total_counter' + ], [INPUT_TYPES.MSFT_SEC_SCORE]: ['secureScore', 'profiles'], [INPUT_TYPES.NEUVECTOR]: [ 'report.base_os', diff --git a/libs/hdf-converters/test/mappers/forward/kics_mapper.spec.ts b/libs/hdf-converters/test/mappers/forward/kics_mapper.spec.ts new file mode 100644 index 0000000000..845aab9eb8 --- /dev/null +++ b/libs/hdf-converters/test/mappers/forward/kics_mapper.spec.ts @@ -0,0 +1,239 @@ +import fs from 'fs'; +import {ExecJSON} from 'inspecjs'; +import {describe, expect, it} from 'vitest'; +import {KicsMapper, resolveControls} from '../../../src/kics-mapper'; +import {omitHDFTimes, omitVersions} from '../../utils'; + +function readSample(name: string): string { + return fs.readFileSync( + `sample_jsons/kics_mapper/sample_input_report/${name}.json`, + {encoding: 'utf-8'} + ); +} + +/** Controls derived from findings, excluding the synthetic coverage record. */ +function findingControls(mapper: KicsMapper) { + return mapper + .toHdf() + .profiles[0].controls.filter((c) => c.id !== 'kics-scan-coverage'); +} + +function readBaseline(name: string): unknown { + return JSON.parse( + fs.readFileSync(`sample_jsons/kics_mapper/${name}-hdf.json`, { + encoding: 'utf-8' + }) + ); +} + +describe('kics_mapper', () => { + describe('kics_findings', () => { + const mapper = new KicsMapper(readSample('kics_findings')); + + // Uncomment to regenerate the baseline, then re-comment before committing: + // fs.writeFileSync( + // 'sample_jsons/kics_mapper/kics_findings-hdf.json', + // JSON.stringify(mapper.toHdf(), null, 2) + // ); + + it('should produce a valid HDF matching the regression baseline', () => { + expect(omitHDFTimes(omitVersions(mapper.toHdf()))).toEqual( + omitHDFTimes(omitVersions(readBaseline('kics_findings'))) + ); + }); + + it('should emit one control per query, keyed on the query id', () => { + const ids = findingControls(mapper).map((c) => c.id); + expect(new Set(ids).size).toEqual(ids.length); + for (const id of ids) expect(id).toMatch(/^[0-9a-f-]{36}$/); + }); + + it('should never assign impact 0, which would report Not Applicable', () => { + for (const control of findingControls(mapper)) { + expect(control.impact).toBeGreaterThan(0); + } + }); + + it('should record why each NIST tag is what it is', () => { + for (const control of findingControls(mapper)) { + expect(['mapped', 'cwe-derived', 'static-fallback']).toContain( + control.tags.nistMapping + ); + } + }); + + it('should mark fallback tags as fallback rather than as a mapping', () => { + for (const control of findingControls(mapper)) { + const nist = control.tags.nist as string[]; + const isFallback = [...nist].sort().join() === ['RA-5', 'SA-11'].join(); + expect(control.tags.nistMapping).toEqual( + isFallback ? 'static-fallback' : 'cwe-derived' + ); + } + }); + + it('should keep the source CWE visible even when it does not resolve', () => { + const unresolved = findingControls(mapper).filter( + (c) => c.tags.nistMapping === 'static-fallback' + ); + expect(unresolved.length).toBeGreaterThan(0); + for (const control of unresolved) expect(control.tags.cwe).toBeTruthy(); + }); + + it('should carry the remediation pair that KICS SARIF drops', () => { + const messages = findingControls(mapper) + .flatMap((c) => c.results) + .map((r) => r.message ?? ''); + expect(messages.some((m) => m.includes('Expected:'))).toBe(true); + expect(messages.some((m) => m.includes('Actual:'))).toBe(true); + expect(messages.some((m) => m.includes('Issue type:'))).toBe(true); + }); + + it('should report every result as failed', () => { + for (const control of findingControls(mapper)) { + for (const result of control.results) { + expect(result.status).toEqual(ExecJSON.ControlResultStatus.Failed); + } + } + }); + + it('should locate each finding by file', () => { + const control = findingControls(mapper)[0]; + expect(control.results[0].code_desc).toContain('File:'); + }); + }); + + describe('scan coverage control', () => { + const mapper = new KicsMapper(readSample('kics_findings')); + + it('records the denominator KICS reports only in top-level counters', () => { + const cov = mapper + .toHdf() + .profiles[0].controls.find((c) => c.id === 'kics-scan-coverage'); + expect(cov).toBeDefined(); + expect(cov!.tags.queries_executed as number).toBeGreaterThan( + cov!.tags.queries_with_findings as number + ); + }); + + it('stays out of the compliance score', () => { + const cov = mapper + .toHdf() + .profiles[0].controls.find((c) => c.id === 'kics-scan-coverage')!; + // impact 0 reports Not Applicable, so scan context cannot skew the ratio + expect(cov.impact).toEqual(0); + expect(cov.results[0].status).toEqual( + ExecJSON.ControlResultStatus.Passed + ); + }); + + it('says plainly that no passing controls can be derived', () => { + const cov = mapper + .toHdf() + .profiles[0].controls.find((c) => c.id === 'kics-scan-coverage')!; + expect(cov.desc).toContain('violations only'); + expect(cov.desc).toContain('should not be read as a pass rate'); + }); + + it('is present even on a scan with no findings', () => { + const empty = new KicsMapper(readSample('kics_zero_findings')); + expect( + empty + .toHdf() + .profiles[0].controls.some((c) => c.id === 'kics-scan-coverage') + ).toBe(true); + }); + }); + + describe('control resolution precedence', () => { + // The shipped table is intentionally empty until adjudication completes, + // so the table tier is exercised with a stub rather than by shipping + // unreviewed rows. + const stub = { + 'query-in-table': {cci: ['CCI-000366'], nist: ['CM-6 b']} + }; + + it('prefers the reviewed per-query table over the CWE', () => { + const resolved = resolveControls( + {query_id: 'query-in-table', cwe: '311'}, + stub + ); + expect(resolved.nist).toEqual(['CM-6 b']); + expect(resolved.cci).toEqual(['CCI-000366']); + expect(resolved.source).toEqual('mapped'); + }); + + it('falls back to the CWE when the query is not in the table', () => { + const resolved = resolveControls({query_id: 'absent', cwe: '311'}, stub); + expect(resolved.source).toEqual('cwe-derived'); + expect(resolved.nist.length).toBeGreaterThan(0); + expect(resolved.nist).not.toEqual(['SA-11', 'RA-5']); + }); + + it('falls back to the static defaults when the CWE does not resolve', () => { + // CWE-778 is one of the 72 KICS uses that CweNistMappingData lacks + const resolved = resolveControls({query_id: 'absent', cwe: '778'}, stub); + expect(resolved.source).toEqual('static-fallback'); + expect(resolved.nist).toEqual(['SA-11', 'RA-5']); + }); + + it('falls back to the static defaults when the query carries no CWE', () => { + const resolved = resolveControls({query_id: 'absent'}, stub); + expect(resolved.source).toEqual('static-fallback'); + }); + + it('ignores a table entry with no controls', () => { + const resolved = resolveControls( + {query_id: 'empty', cwe: '311'}, + { + empty: {cci: [], nist: []} + } + ); + expect(resolved.source).toEqual('cwe-derived'); + }); + }); + + describe('kics_findings withRaw', () => { + const mapper = new KicsMapper(readSample('kics_findings'), true); + + // Uncomment to regenerate the baseline, then re-comment before committing: + // fs.writeFileSync( + // 'sample_jsons/kics_mapper/kics_findings-withraw-hdf.json', + // JSON.stringify(mapper.toHdf(), null, 2) + // ); + + it('should include raw data in passthrough', () => { + const hdf = mapper.toHdf() as ExecJSON.Execution & { + passthrough: Record; + }; + expect(hdf.passthrough).toHaveProperty('raw'); + expect(hdf.passthrough).toHaveProperty('auxiliary_data'); + }); + + it('should match the regression baseline', () => { + expect(omitHDFTimes(omitVersions(mapper.toHdf()))).toEqual( + omitHDFTimes(omitVersions(readBaseline('kics_findings-withraw'))) + ); + }); + }); + + describe('kics_zero_findings', () => { + const mapper = new KicsMapper(readSample('kics_zero_findings')); + + // Uncomment to regenerate the baseline, then re-comment before committing: + // fs.writeFileSync( + // 'sample_jsons/kics_mapper/kics_zero_findings-hdf.json', + // JSON.stringify(mapper.toHdf(), null, 2) + // ); + + it('should convert a scan with no findings without throwing', () => { + expect(findingControls(mapper)).toEqual([]); + }); + + it('should match the regression baseline', () => { + expect(omitHDFTimes(omitVersions(mapper.toHdf()))).toEqual( + omitHDFTimes(omitVersions(readBaseline('kics_zero_findings'))) + ); + }); + }); +});