Skip to content

feat: report semgrep findings as a CWE/OWASP table; honour nosemgrep - #8

Merged
arootroatch merged 1 commit into
masterfrom
semgrep-report-table
Jul 28, 2026
Merged

feat: report semgrep findings as a CWE/OWASP table; honour nosemgrep#8
arootroatch merged 1 commit into
masterfrom
semgrep-report-table

Conversation

@arootroatch

Copy link
Copy Markdown
Collaborator

Requested: map findings to their CWE / OWASP Top 10 classes instead of just
printing a location. semgrep already carries all of it in
tool.driver.rules[].properties.tags.

#  SEVERITY  FILE:LINE                                      RULE               CWE               OWASP     CONFIDENCE
1  warning   src/clj/cleancoders/db_browser/engine.clj:58   cc-generic-catch   CWE-396, CWE-636  A10:2025  LOW
2  warning   src/clj/cleancoders/db_browser/engine.clj:165  cc-generic-catch   CWE-396, CWE-636  A10:2025  LOW
3  warning   src/clj/cleancoders/mfa.clj:218                cc-generic-catch   CWE-396, CWE-636  A10:2025  LOW
4  warning   src/clj/cleancoders/web.clj:31                 cc-generic-catch   CWE-396, CWE-636  A10:2025  LOW
5  error     src/cljs/cleancoders/contact.cljs:224          cc-cljs-innerhtml  CWE-79            A05:2025  HIGH

CWE:
  CWE-396: Declaration of Catch for Generic Exception
  CWE-636: Not Failing Securely ('Failing Open')
  CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
OWASP:
  A05:2025 - Injection
  A10:2025 - Mishandling of Exceptional Conditions

semgrep: 5 finding(s), 1 blocking (error-level)
         2 additional finding(s) suppressed in source (nosemgrep)

Identifiers stay in the table and the legend expands each once — inlining the
full CWE titles pushed rows past 200 columns.

It found a real bug: nosemgrep was being ignored

Building the fixture from cleancoders.com's actual scan turned up that both
secret findings carry suppressions: [{"kind": "inSource"}] — the source has
nosemgrep annotations and semgrep honoured them, but still emits the
result. The reporting counted them anyway.

So those two already-triaged findings were being reported and blocking the
build, which makes nosemgrep look broken. Suppressed results are now excluded
from the table, the legend and the exit code, and reported as a count so the
suppression stays visible rather than silent.

cleancoders.com's real number is 1 blocking, not 3. The only genuinely
actionable finding is cc-cljs-innerhtml at contact.cljs:224.

Structure

Rendering and the exit decision move into bin/report-sarif.sh, called via the
checkout the semgrep job already does. 37 lines of inline jq in YAML become 7.
bin/test-report-sarif.sh covers it with 20 cases against a real captured SARIF,
pinning: suppressed excluded, warnings never block, error-level blocks, clean
scan passes, missing SARIF fails loudly.

The fixture has region.snippet stripped. semgrep embeds the matched source
line, which for a secret-detection rule is the secret itself — and this repo is
public.

🤖 Generated with Claude Code

The one-line-per-finding format said WHERE but not WHAT CLASS, so acting on a
finding meant looking the rule up by hand. semgrep already carries CWE, OWASP
category and confidence in tool.driver.rules[].properties.tags, so the report
now renders:

  #  SEVERITY  FILE:LINE                              RULE               CWE      OWASP     CONFIDENCE
  1  warning   src/clj/cleancoders/mfa.clj:218         cc-generic-catch   396,636  A10:2025  LOW
  2  error     src/cljs/cleancoders/contact.cljs:224   cc-cljs-innerhtml  CWE-79   A05:2025  HIGH

Identifiers stay in the table and a legend expands each CWE and OWASP category
once, so rows remain scannable instead of ~200 columns wide.

Fixes a real bug found while building the fixture: semgrep still emits a result
for source annotated with a nosemgrep comment, marking it
suppressions:[{kind:"inSource"}]. The reporting counted those, so
cleancoders.com's two already-triaged secret findings were being reported AND
blocking the build -- which makes nosemgrep look broken. Suppressed results are
now excluded from the table, the legend and the exit code, and reported as a
count so the suppression stays visible rather than silent. That repo's real
number is 1 blocking, not 3.

Rendering and the exit decision move into bin/report-sarif.sh with 20 tests in
bin/test-report-sarif.sh, run against a SARIF captured from an actual consumer
scan. The fixture has region.snippet stripped: semgrep embeds matched source,
which for a secret-detection rule means the secret itself, and this repo is
public.
@arootroatch
arootroatch merged commit 7d9d7ed into master Jul 28, 2026
10 checks passed
@arootroatch
arootroatch deleted the semgrep-report-table branch July 28, 2026 18:06
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