Skip to content

fix(sarif): read CWEs from the rule, and derive cci from the resolved nist - #8570

Open
clem-field wants to merge 1 commit into
mitre:masterfrom
clem-field:fix/sarif-cwe-extraction
Open

fix(sarif): read CWEs from the rule, and derive cci from the resolved nist#8570
clem-field wants to merge 1 commit into
mitre:masterfrom
clem-field:fix/sarif-cwe-extraction

Conversation

@clem-field

Copy link
Copy Markdown
Contributor

Two defects in the SARIF mapper's tag resolution, with one root cause: each of the three tags was computed independently from a different source.

CWEs were scraped from the message text

extractCwe split the finding message on the last ( and trimmed two characters. That works only for producers whose message happens to end in a parenthesised CWE list. For any other producer it returned a fragment of the prose as a "CWE", which then failed to map and fell through to the default tags.

Converting a real Semgrep SARIF against master today:

"cwe": [
  "Detected a dynamic value being used with urllib. urllib supports 'file://' schemes",
  "so a dynamic value controlled by a malicious actor may allow them to read arbitrary files. Audit uses of urllib calls to ensure user data cannot control the URLs",
  "or consider using the 'requests' library instea"
]

SARIF publishes CWEs on the rule, not the result — in properties.tags (Semgrep, CodeQL) and in relationships[].target.id against a CWE taxonomy. Rules are now indexed once per document and a result resolves its own; the message is kept as a fallback for producers that publish nothing on the rule. After: "cwe": ["CWE-939"].

Notably, flawfinder — the producer the old scrape appeared to handle — publishes its CWEs in relationships[].target.id all along. The mapper never looked there.

cci did not correspond to nist

The cci tag read vulnerabilityClassifications, a path SARIF does not define. It always resolved to nothing, so the value fell through to the CCIs of the default tags. On the flawfinder sample that means 21 of 21 controls were tagged nist: ["SI-10"] alongside cci: ["CCI-003173", "CCI-001643"] — the CCIs for SA-11 and RA-5. cci is now derived from the nist tags actually resolved for that control, giving ["CCI-001310"].

Sample data changes

cci becomes CCI-001310 on all 21 flawfinder controls, and four controls have their cwe list reordered because it now comes from the rule's taxonomy relationships rather than the message text. The CWE sets themselves are unchanged, and every NIST control reachable from a CWE has an entry in the NIST-to-CCI table, so no control loses tags it previously had.

Adds a real Semgrep SARIF as a second producer fixture: its rules carry CWEs in properties.tags and its messages carry none, which the previous implementation could not read at all.

Prior art

sarif-to-hdf in mitre/hdf-libs already resolves CWEs this way — relationships first, then properties.tags, message text last, with cci derived from the resolved nist. This brings the v1 mapper in line with that design.

Testing

Extraction is now strict: only CWE-<digits> tokens are accepted, so a message with no identifier yields no cwe tag rather than a fragment of prose. Five new tests cover taxonomy relationships, properties.tags, the message fallback, the no-identifier case, and cci/nist agreement.

vitest run in libs/hdf-converters: 159 passing. The 4 failures (3 sonarqube, 1 splunk reverse) reproduce identically on an unmodified master checkout.

… nist

Two defects in the SARIF mapper's tag resolution, with one root cause: each of
the three tags was computed independently from a different source.

CWEs were scraped out of the result message by splitting on the last '(' and
trimming two characters. That works only for producers whose message happens
to end in a parenthesised CWE list. For any other producer the scrape returned
a fragment of the prose as a "CWE", which then failed to map and silently fell
through to the default tags. SARIF publishes CWEs on the rule object, not the
result -- in properties.tags (Semgrep, CodeQL) and in
relationships[].target.id against a CWE taxonomy (the standards-blessed form,
which flawfinder uses). Rules are now indexed once per document and a result
resolves its own; the message is kept as a fallback for producers that
publish nothing on the rule.

The cci tag read `vulnerabilityClassifications`, a path SARIF does not define.
It therefore always resolved to nothing, and the value fell through to the
CCIs of the default tags -- so every control carried CCI-003173 and CCI-001643
(SA-11, RA-5) no matter what its own nist tag said. On the flawfinder sample
that meant 21 of 21 controls tagged nist SI-10 alongside CCIs belonging to two
unrelated controls. cci is now derived from the nist tags actually resolved
for that control.

Extraction is also strict: only CWE-<digits> tokens are accepted, so a message
with no identifier now yields no cwe tag rather than a fragment of prose.

Sample data changes on the existing flawfinder fixture: cci becomes
CCI-001310 (SI-10's actual CCI) on all 21 controls, and four controls have
their cwe list reordered because it now comes from the rule's relationships
rather than the message text. The CWE sets themselves are unchanged -- every
NIST control reachable from a CWE has an entry in the NIST-to-CCI table, so
no control loses tags it previously had.

Adds a real Semgrep SARIF sample as a second producer: its rules carry CWEs
in properties.tags and its messages carry none, which the previous
implementation could not read at all.

Signed-off-by: clem-field <kc8yhe@me.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 New Code Smells (required ≤ 0)

See analysis details on SonarQube Cloud

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

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.

1 participant