Security fixes: SARIF fail gate, path traversal, action classification, HTML injection
Five security and correctness issues identified in the Issue 097 security audit are resolved in this release. All five had already been tracked as open after Issue 098 addressed the sensitive-value masking category; this release closes the remaining findings.
🐛 Bug fixes
-
SARIF fail gate now catches parse failures (
--fail-on-static-code-analysis-errors): previously, a corrupted or truncated SARIF file would silently reduce the in-memory finding count to zero and the gate would pass. The gate now treats any SARIF load warning as an immediate failure — the failed file paths and error messages are written to stderr and the process exits with code 10. A clean SARIF batch continues to behave as before. -
Path traversal blocked in custom template directory: when
--template-diris used, template names containing../segments could previously resolve to files outside the configured directory. The loader now canonicalizes both the template directory and every candidate path and rejects any path that escapes the root, throwing a clearInvalidOperationExceptionwith the offending path. -
[Details]links in code-analysis tables use angle-bracket URLs:help_urivalues from SARIF were previously rendered as[Details](url), which breaks for any URL containing). Links now use the CommonMark angle-bracket form[Details](<url>), and</>characters in the URI are percent-encoded, so the link is robust to any URL content. -
HTML injection via
model.Typefixed: the resource type field in the summary HTML block was rendered unencoded, allowing a crafted plan with a<script>tag in the resource type to inject HTML. The field is now encoded withHtmlEncoder.Default.Encode. -
Terraform
forgetaction correctly classified: theforgetaction (Terraform 1.7+, removes a resource from state without destroying infrastructure) was previously treated as a no-op. It is now recognized as a distinct action (reported as ❌forget) and counted in the Destroy total in the plan summary, matching Terraform's own semantic. Non-empty unrecognized action sets are classified asunknown(⚠️) rather than no-op.