Enhance SARIF support in usage metrics

What does this MR do and why?

This change refers to this comment in a previous MR.

This MR fixes the existing tracking mechanisms and adds support for SARIF artifacts and fan-out report: the secure::scan event already fires for SARIF but with empty data, and SARIF-derived scans silently inflate the per-type Service Ping metrics.

Changes:

  • Fix secure::scan for SARIF - Ci::Build.unmerged_security_reports now captures the SARIF parser's fan-out instead of discarding it, so TrackScanService emits one accurate event per typed scan (correct scan_type, findings_count, scanner) instead of a single empty property: 'sarif' event.
  • Keep same-type runs distinct - the event idempotency key now uses scanner_external_id, so multiple SARIF runs inferring the same scan type are tracked as separate events per scanner instead of collapsing into one.
  • Count SARIF-derived scans - Added a new sarif_derived scope to Security::Scan, used in a new CountSarifSecurityScansMetric based on the existing CountSecurityScansMetric instrumentation.
  • Existing per-type metrics unchanged - Rather than alter existing metrics' calculation, the new sarif_derived_security_scans metric separates SARIF-derived from native scans at analysis time.

Changelog: changed
EE: true

Add sarif ingestion metrics (#604719 - closed) • Gal Katz • 19.5

Query plans

CountSarifSecurityScansMetric

Raw SQL

all:

SELECT
    COUNT("security_scans"."build_id")
FROM
    "security_scans"
WHERE
    build_id BETWEEN 100 AND 999999
    AND "security_scans"."scanner_external_id" IS NOT NULL

28 days:

SELECT
    COUNT("security_scans"."build_id")
FROM
    "security_scans"
WHERE
    "security_scans"."scanner_external_id" IS NOT NULL
    AND build_id BETWEEN 100 AND 999999
    AND "security_scans"."created_at" BETWEEN '2026-07-08 15:20:20' AND '2026-08-05 15:21:13'
Plans

all: See this.

 Aggregate  (cost=2.90..2.91 rows=1 width=8) (actual time=0.059..0.059 rows=1 loops=1)
   Buffers: shared hit=25
   I/O Timings: read=0.000 write=0.000
   ->  Index Only Scan using idx_security_scans_on_build_scan_type_and_scanner on public.security_scans  (cost=0.57..2.90 rows=1 width=8) (actual time=0.054..0.055 rows=0 loops=1)
         Index Cond: ((security_scans.build_id >= 100) AND (security_scans.build_id <= 999999) AND (security_scans.scanner_external_id IS NOT NULL))
         Heap Fetches: 0
         Index Searches: 1
         Buffers: shared hit=25
         I/O Timings: read=0.000 write=0.000
Settings: effective_cache_size = '338688MB', jit = 'off', work_mem = '100MB', random_page_cost = '1.5', seq_page_cost = '4'
Query ID: -2471465146305712945

28 days: See this.

 Aggregate  (cost=2.91..2.92 rows=1 width=8) (actual time=0.043..0.043 rows=1 loops=1)
   Buffers: shared hit=25
   I/O Timings: read=0.000 write=0.000
   ->  Index Scan using idx_security_scans_on_build_scan_type_and_scanner on public.security_scans  (cost=0.57..2.90 rows=1 width=8) (actual time=0.040..0.040 rows=0 loops=1)
         Index Cond: ((security_scans.build_id >= 100) AND (security_scans.build_id <= 999999) AND (security_scans.scanner_external_id IS NOT NULL))
         Index Searches: 1
         Filter: ((security_scans.created_at >= '2026-07-08 15:20:20+00'::timestamp with time zone) AND (security_scans.created_at <= '2026-08-05 15:21:13+00'::timestamp with time zone))
         Buffers: shared hit=25
         I/O Timings: read=0.000 write=0.000
Settings: seq_page_cost = '4', effective_cache_size = '338688MB', jit = 'off', work_mem = '100MB', random_page_cost = '1.5'
Query ID: -3677981160874255784

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.

Edited by Gal Katz

Merge request reports

Loading
Loading