Repository navigation
tools: export accepted findings reports as SARIF 2.1.0 - #232
Stefano Demiliani (demiliani) wants to merge 3 commits into
Conversation
Add tools/ConvertTo-Sarif.ps1, which converts an accepted findings-report into a SARIF 2.1.0 log so findings appear as code scanning annotations in GitHub, Azure DevOps, and other SARIF viewers. - converts only top-level findings, so super-skill copies of leaf findings are not duplicated - knowledge-backed rules link to their article; agent findings get a rule without a help link - blocker/major -> error, minor -> warning, info -> note - partial or failed outcomes (including failed sub-results) set executionSuccessful = false with notifications - -SourcePathPrefix maps app-relative paths to repository paths Cover the mapping with offline assertions in Test-ReviewContract.ps1 (run by the review-fixtures workflow) and document the export in the standalone runner guide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Hi Jesper Schulz-Wedde (@JesperSchulz), could you review this one, or route it to whoever owns the tooling? It only touches In short: it adds It's presentation only: no change to Entry, READ, DO, the report schema, or any skill. Two choices worth a look:
The output validates against the official SARIF 2.1.0 schema. Offline tests sit in Thanks! |
Jesper Schulz-Wedde (JesperSchulz)
left a comment
There was a problem hiding this comment.
Reviewed 3e2f553047e1f3a51148dce867df15b2023463f6. The top-level-only export, severity mapping and incomplete-invocation handling are sound, but source locations are not losslessly converted to URIs.
tools/ConvertTo-Sarif.ps1:133-146 copies a filesystem path directly into artifactLocation.uri. A real source path such as src/Price #1.al passes Validate-FindingsReport with its authoritative SourceRoot and scope, but the converter emits that literal string. URI resolution then treats #1.al as a fragment and targets /src/Price rather than the actual file. Literal percent sequences and question marks have analogous problems on platforms where allowed. This silently breaks the core annotation/file binding even for a fully accepted report; JSON/SARIF structural validation does not catch it.
Encode each repository-relative path segment as URI data after combining SourcePathPrefix, preserving / separators and not turning percent/hash characters into escapes/fragments. Add regressions for spaces, #, literal %, non-ASCII paths and prefixed app folders, asserting decoded URI paths resolve to the original filenames with no fragment/query. Do not change the report's raw source path. Existing exact-head contract and integration tests otherwise pass.
Preserve source file bindings for reserved and non-ASCII path characters, including app folder prefixes. Add accepted-report regression coverage and document raw path inputs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed Jesper Schulz-Wedde (@JesperSchulz)'s review in commit 8fa00b0.
Validation: the new regression failed before the fix; |
There was a problem hiding this comment.
Revised — this approval is withdrawn; see my changes-requested review on the same head.
Reviewed 8fa00b0b75a6532d0abfef53b2908bfbb5e43983. The URI-binding blocker is resolved: the converter encodes each combined source/prefix path segment while preserving separators, leaves raw report paths untouched, and has end-to-end regressions for spaces, #, literal %, non-ASCII names and prefixes. Top-level-only conversion, severity mapping, and incomplete-outcome handling are sound.
However, findings without a location (allowed by the findings-report schema) produce SARIF results that GitHub code scanning rejects (details in the follow-up review).
Jesper Schulz-Wedde (JesperSchulz)
left a comment
There was a problem hiding this comment.
Requesting changes on 8fa00b0b75a6532d0abfef53b2908bfbb5e43983. This supersedes my earlier approval.
Merge-critical: one location-less finding makes the GitHub code scanning upload fail
findings[].location is optional in schemas/findings-report.schema.json, and DO describes it as optional too. For a finding without a location, ConvertTo-Sarif.ps1 emits a result that has no locations. That is valid SARIF 2.1.0, so the official schema check passes. GitHub code scanning, however, requires at least one location per result and rejects the whole upload (locationFromSarifResult: expected at least one location; see microsoft/sarif-sdk#2770 and GitHub's "SARIF support for code scanning" docs). A single repository-level or advisory finding therefore breaks the documented GitHub path, which is the main purpose of this PR.
Please handle location-less findings deterministically, without dropping them silently. Either:
- Leave them out of
resultsand surface them elsewhere: for example, atoolExecutionNotificationsentry naming each finding ID and its message, plus a count inrun.properties. - Or anchor them to a fallback artifact the caller provides, for example
-FallbackLocation app.json. Never invent one.
Then document the behavior in docs/standalone-runner.md. Add a regression with a location-less finding that asserts:
- every emitted result has at least one location with a non-empty
artifactLocation.uri, and - the finding is still visible in the SARIF log.
Everything else is sound: the per-segment URI encoding fix and its regressions, top-level-only conversion, the severity mapping, and the executionSuccessful handling. The executionSuccessful handling relies on DO deriving a partial/failed super-skill outcome from failed leaves.
Keep locationless findings visible with their rule, message, severity and metadata while emitting only located results for GitHub code scanning. Document the count and add regression coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed Jesper Schulz-Wedde (@JesperSchulz)'s latest review in commit 10169cf using the notification approach.
Regression coverage includes accepted reports with mixed located/location-less findings and location-less-only findings, all four severities, non-empty artifact URIs on every emitted result, notification visibility and metadata, and coexistence with partial-outcome notifications. Validation: the new regression failed before the fix; |
Why
A BCQuality findings report is a project-specific JSON document. To show findings next to the code in a pull request, every CI integration currently has to write its own converter. SARIF 2.1.0 is the standard interchange format for static-analysis results. GitHub code scanning, Azure DevOps (SARIF SAST Scans Tab), and VS Code SARIF viewers consume it directly. With a supported converter, BCQuality can be used as a PR annotation tool with no custom code.
What
tools/ConvertTo-Sarif.ps1 -ReportPath <accepted report> [-OutputPath] [-SourcePathPrefix] [-KnowledgeBaseUrl]findings[].idruleId. Knowledge-backed rules getname= article slug,shortDescription= article title, andhelpUri= article link.agent:findings get a rule without a help link.severityblocker/major→error,minor→warning,info→notelocation(+range)physicalLocationrelative toSRCROOT, with an optional-SourcePathPrefixfor apps in a sub-folderconfidence,domain,references,from-sub-skill,suggested-code*result.propertiesoutcomepartial/failed(top level or any sub-result)invocations[0].executionSuccessful = false+toolExecutionNotificationsskill,outcome,suppressedrun.propertiesDesign choices:
findings[], so convertingsub-resultsas well would duplicate them, and failed leaves' findings must not be published.partial/failedreport yieldsexecutionSuccessful: falsewith the outcome reasons.Validate-FindingsReport.ps1; the docs say to convert only accepted reports.Tests and docs
tools/Test-ReviewContract.ps1gains offline assertions: rule ID, level mapping, help link, path prefix, range, preserved confidence, and the failed-outcome notification. Thereview-fixturesworkflow already runs it throughTest-ReviewFixtures.ps1, so no workflow change is needed.docs/standalone-runner.md: new section "Publish findings as SARIF"; linked fromdocs/README.md.Validation
tools/Test-ReviewContract.ps1,tools/Test-ReviewFixtures.ps1,.github/scripts/Test-KnowledgeIndex.ps1: PASSED.