Add Seal Security CSV parser - #15592
Conversation
Adds a parser for the CSV export of the Seal Security CLI, produced by `seal scan --csv <file>`. The export has one row per vulnerable package, with a pipe-separated list of vulnerability identifiers. Each identifier becomes its own Finding so that it can be triaged independently. Identifiers are not always CVEs: Seal reports the most specific identifier it has, falling back to a GitHub advisory or Snyk identifier when no CVE is assigned. Two properties of the export are worth noting: - A scan without findings leaves the file empty rather than writing a header, so an empty file is a valid report and yields no findings. - A vulnerability reaching the project through an embedded (shaded) package is reported as `CVE-2021-1234(via shaded lib1&lib2)`. The identifier is used for the Finding and the embedding packages are named in the description. The export has no severity column, so findings default to Medium. The parser reads an optional Score column when present and maps it onto the standard severity bands. Severity is deliberately left out of the deduplication hashcode so that findings imported before and after a report gains that column are not treated as distinct. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
valentijnscholten
left a comment
There was a problem hiding this comment.
Clean, well-structured parser with good coverage (empty/header-only, one/many vulns, shaded, score column). Excluding severity from the dedupe hashcode is the right call and nicely justified. One minor, non-blocking consistency nit:
dojo/tools/seal/parser.py — fix_available can disagree with the mitigation text
fix_available=can_seal is derived only from the Can Seal column, but the sealed-version mitigation is used only when can_seal and sealed_version:
can_seal = (row.get("Can Seal") or "").strip().upper() == "TRUE"
...
if can_seal and sealed_version:
mitigation = SEALED_MITIGATION_TEMPLATE.format(...)
else:
mitigation = NO_FIX_MITIGATION
...
finding = Finding(..., fix_available=can_seal, ...)So a row with Can Seal=TRUE but an empty Sealed Version would yield a Finding marked fix_available=True whose mitigation reads "Seal has no sealed version for this package version yet." The sample data never hits that combination (every Can Seal=TRUE row carries a Sealed Version), so it isn't exercised by the tests — but the mitigation branch already anticipates that state, so it'd be good to keep the two consistent, e.g.:
fix_available=can_seal and bool(sealed_version),Low severity — only matters if real Seal exports ever emit Can Seal=TRUE with a blank Sealed Version. Otherwise this looks close to merge-ready.
Description
Adds a parser for the CSV export of the Seal Security CLI, produced by
seal scan --csv <file>. Seal backports security fixes into "sealed" versions of open-source packages, so a vulnerable dependency can be remediated without a major-version upgrade.The export has one row per vulnerable package, with a pipe-separated list of vulnerability identifiers:
Each identifier becomes its own Finding, so that each one can be triaged and risk-accepted independently. Identifiers are not always CVEs: Seal reports the most specific identifier it has, falling back to a GitHub advisory or Snyk identifier when no CVE is assigned.
Two properties of the export are worth calling out, both covered by test files:
CVE-2021-1234(via shaded lib1&lib2). The identifier is used for the Finding, and the embedding packages are named in the description. The Finding's component stays the package that is actually present in the project.When a sealed version is available,
fix_availableis set and the mitigation names the version to update to.Severity
Not every Seal report carries severity, so the parser supports both shapes of the export.
When a
Scorecolumn is present its value is mapped onto the standard severity bands;when it is absent there is nothing in the report to derive a severity from, and findings
are imported as Medium. Either export imports cleanly, with no configuration and no
scan-type variant.
For that reason
severityis deliberately left out of the deduplication hashcode. Aproject can import a report without a score today and one with a score tomorrow, and
including severity would fork every existing finding into a duplicate at that point.
Test results
Ruff clean against the pinned
ruff==0.16.1.Documentation
docs/content/supported_tools/parsers/file/seal.mdadded.Checklist
dev.dev.🤖 Generated with Claude Code