Add shadow-mode Duo triage for custom SAST findings
What does this MR do and why?
Add shadow-mode Duo triage for custom SAST findings
Implements Phase 1 of automated SAST triage per work item https://gitlab.com/gitlab-com/gl-security/product-security/appsec/appsec-team/-/work_items/1448.
Today the ping-appsec-for-sast-findings job posts every custom-rule finding to the MR with an appsec-sast-ping::unresolved label and asks AppSec to triage. AppSec calibrates the bot using appsec-sast::helpful / appsec-sast::unhelpful labels.
This change adds a SastTriageClassifier that calls the existing Duo Chat aiAction GraphQL mutation for each finding, polls aiMessages for the response, parses a strict-JSON verdict, and writes per-finding verdicts to a gl-sast-triage-verdicts.json CI artifact retained for 90 days. AppSec can backtest precision against the helpful/unhelpful labels using that artifact.
The classifier runs only when the SAST_TRIAGE_ENABLED CI variable is set to "1", so this MR lands code without changing pipeline behavior. All failure modes (mutation errors, polling timeout, unparseable response, network failure) fall through to an uncertain verdict and log to job stdout; the existing inline-comment posting path is not affected by any classifier error.
The prompt segregates trusted rule metadata from untrusted code excerpts and explicitly instructs the model not to follow instructions embedded in the excerpt; specs cover prompt-injection-shaped input.
Phase 2 (active-mode dismissal gated by precision threshold) will be tracked in a sibling issue after shadow-mode data is collected.
References
https://gitlab.com/gitlab-com/gl-security/product-security/appsec/appsec-team/-/work_items/1448
Screenshots or screen recordings
| Before | After |
|---|---|
How to set up and validate locally
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.