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
```ruby
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
```ruby
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
## Related Files
- `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`
issue