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.

Merge request reports

Loading
Loading