Fix NoMethodError when security report has no scanner information

Summary

The Security::StoreScanService raises a NoMethodError when processing security reports that lack scanner information, causing scan ingestion to fail.

Sentry Error: https://new-sentry.gitlab.net/organizations/gitlab/issues/3010954

Error Details

NoMethodError: undefined method `external_id' for nil:NilClass (NoMethodError)

      project.vulnerability_scanners.safe_find_or_create_by!(external_id: security_report.scanner.external_id) do |scanner|
                                                                                                 ^^^^^^^^^^^^
  from security/store_scan_service.rb:154:in `vulnerability_scanner'
  from security/store_scan_service.rb:97:in `store_findings'
  from security/store_scan_service.rb:38:in `execute'

Root Cause

In ee/app/services/security/store_scan_service.rb, the vulnerability_scanner method (line 154) attempts to access security_report.scanner.external_id without checking if security_report.scanner is nil.

Security reports can have missing scanner information when:

  • The report JSON lacks both top-level scanner data (scan.scanner) and finding-level scanner data
  • The scanner data is malformed and fails validation during parsing

The parser in lib/gitlab/ci/parsers/security/common.rb only sets report.scanner if valid scanner data exists, otherwise it remains nil.

Proposed Solution

1. Add nil check in vulnerability_scanner method

def vulnerability_scanner
  return unless security_report.scanner

  project.vulnerability_scanners.safe_find_or_create_by!(external_id: security_report.scanner.external_id) do |scanner|
    scanner.assign_attributes(security_report.scanner.to_hash)
  end
end

2. Handle nil scanner in store_findings method

def store_findings
  scanner = vulnerability_scanner
  
  if scanner.nil?
    mark_scan_as_failed!
    Gitlab::AppLogger.warn(
      message: "Security scan missing scanner information",
      scan_id: security_scan.id,
      report_type: security_report.type
    )
    return
  end

  StoreFindingsService.execute(security_scan, scanner, security_report, register_finding_keys).then do |result|
    update_deduplicated_findings if result[:status] == :error && deduplicate_findings?
  end

  security_scan.succeeded!
rescue StandardError => error
  mark_scan_as_failed!
  Gitlab::ErrorTracking.track_exception(error)
end

Testing

Add test coverage in ee/spec/services/security/store_scan_service_spec.rb using the existing :sast_with_missing_scanner fixture (spec/fixtures/security_reports/master/gl-sast-missing-scanner.json).

The test should verify:

  • No NoMethodError is raised
  • Scan is marked as preparation_failed
  • StoreFindingsService is not called
  • Processing error is added to the scan
  • No vulnerability scanner record is created
  • Error is tracked via Gitlab::ErrorTracking

Impact

This bug prevents security scan ingestion for reports with missing scanner information, resulting in:

  • Failed security scans not being recorded properly
  • Missing vulnerability data in the vulnerability report
  • Incomplete security posture visibility
  • ee/app/services/security/store_scan_service.rb
  • ee/spec/services/security/store_scan_service_spec.rb
  • lib/gitlab/ci/parsers/security/common.rb
  • spec/fixtures/security_reports/master/gl-sast-missing-scanner.json
Edited by 🤖 GitLab Bot 🤖