Skip to content

feat(hdf-converters): add KICS JSON to HDF mapper - #8573

Open
clem-field wants to merge 1 commit into
mitre:masterfrom
clem-field:feature/kics2hdf
Open

feat(hdf-converters): add KICS JSON to HDF mapper#8573
clem-field wants to merge 1 commit into
mitre:masterfrom
clem-field:feature/kics2hdf

Conversation

@clem-field

Copy link
Copy Markdown
Contributor

Closes #8572.

Maps the native kics scan --report-formats json format, plus its fingerprint, frontend intake wiring, sample data and spec.

Why not sarif2hdf

KICS emits SARIF, and unusually its SARIF is well formed — in a real scan, 72 of 72 rules carried a proper CWE taxonomy relationship. That is better than most scanners manage.

What SARIF drops is everything about remediation and identity:

Native field Fate in SARIF Consequence
expected_value dropped on 100% of findings the message keeps only actual_value, so a control can state what the configuration is but never what it should be
issue_type dropped MissingAttribute 767 / IncorrectValue 68 / RedundantAttribute 14 in the scan measured — add-a-block versus fix-a-value
similarity_id dropped KICS's own stable finding fingerprint
resource_name, search_value, description_id dropped
severity 5 levels to 4 CRITICAL and HIGH both become error

Design notes

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.

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.

NIST mapping provenance — worth a look

Controls carry a nistMapping tag reading cwe-derived or static-fallback, so a resolved mapping stays distinguishable from a static default.

The motivation is measurable. KICS carries a CWE on all 1,811 of its shipped queries across 102 distinct CWEs, but only 30 of those resolve against CweNistMappingData52% of queries by volume. The largest misses are the most-used checks: CWE-778 (211 queries), CWE-665 (113), CWE-710 (111), CWE-668 (78).

Without the marker, roughly half of a KICS run carries SA-11, RA-5 with nothing to indicate it is a fallback rather than a mapping. The source CWE is retained in tags even when it does not resolve, so the gap is visible rather than silent.

Thirteen mappers in this repo resolve tags.nist through the same table, so this is likely a broader condition than KICS. Raising it here rather than expanding scope; happy to drop the tag if maintainers would rather not set the precedent in a converter PR.

The equivalent converter for the v3 line is mitre/hdf-libs#240, and mitre/hdf-libs#239 tracks the mapping work this anticipates.

Fixtures

Generated from a small synthetic Terraform tree, so they are reproducible and carry no proprietary input: findings across four severities, and a genuine zero-findings scan.

Testing

13 tests covering rule grouping, the severity scale, the provenance marker, retention of unresolved CWEs, the remediation pair, and the zero-findings case.

vitest run in libs/hdf-converters: 4 failures, all reproducing identically on an unmodified master checkout (3 sonarqube tests that reach a live API, 1 splunk reverse mapper).

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 <kc8yhe@me.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 New Bugs (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@clem-field

Copy link
Copy Markdown
Contributor Author

Added: a scan coverage record

Following review feedback that a KICS profile reads as 100% failed.

KICS reports violations only. Its output has no record of the queries that ran
without finding anything — queries[] holds only those that fired — so no
passing requirement can be derived from it, and a converted profile is
failures-only by construction. On a real Terraform scan that means 72 of 2,034
executed queries
appear in the document, and the compliance ratio renders as
100% failed when 1,962 checks ran clean.

The converter now emits a kics-scan-coverage requirement carrying the
denominator:

queriesExecuted:     2034
queriesWithFindings:   72
filesScanned:         294
filesParsed:          292

with a description stating plainly that KICS does not enumerate passing queries
and the ratio should not be read as a pass rate. Impact 0 reports Not
Applicable, matching buildNoFindingsRequirement, so the record cannot skew the
ratio it exists to explain.

This is deliberately a record, not synthesised passes. KICS tells us how many
queries ran but not which ones, so naming them would be invention. Reconstructing
the passing set is possible in principle — the shipped catalog enumerates all
1,811 queries, so a converter could diff findings against the queries applicable
to the platforms scanned — but that needs the catalog alongside the converter and
is the same machinery as Not-Applicable seeding. Out of scope here; worth its own
issue if maintainers want it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a KICS mapper (native JSON, not SARIF)

1 participant