goharbor/harbor · error · Exception

cert file {} should include SAN

Error message

cert file {} should include SAN

What it means

The second assertion in MergeNativeSummary (src/pkg/scan/report/summary.go): s1 passed the *vuln.NativeReportSummary check but the SECOND operand s2 did not. Both operands must be exact *vuln.NativeReportSummary pointers for the native summary merge to proceed.

Source

Thrown at make/photon/prepare/models.py:91

            raise Exception('File {} not exist'.format(filename))

        if not path.is_file:
            raise Exception('invalid {}'.format(filename))

        # check key file permission
        if filename.endswith('.key') and not check_permission(path, mode=0o600):
            raise Exception('key file {} permission is not 600'.format(filename))

        # check certificate file
        if filename.endswith('.crt'):
            if not owner_can_read(path.stat().st_mode):
                # check owner can read cert file
                raise Exception('File {} should readable by owner'.format(filename))
            if not san_existed(path):
                # check SAN included
                if filename == 'harbor_internal_ca.crt':
                    return
                raise Exception('cert file {} should include SAN'.format(filename))

    def validate(self):
        if not self.enabled:
            # pass the validation if not enabled
            return

        if not internal_tls_dir.exists():
            raise Exception('Internal dir for tls {} not exist'.format(internal_tls_dir))

        for filename in self.required_filenames:
            self._check(filename)

    def prepare(self):
        """
        Prepare moves certs in tls file to data volume with correct permission.
        """
        if not self.enabled:
            logging.info('internal tls NOT enabled...')

View on GitHub (pinned to 7b2fd08cc5)

Solutions

  1. Validate every summary in the fold before merging; skip or fail fast on any that is not *vuln.NativeReportSummary
  2. Regenerate stale summaries from their reports instead of merging mixed-version data
  3. Log the dynamic type of the failing operand to locate the producer

Example fix

// before
sum, err := report.MergeNativeSummary(ns1, s2) // s2 is `any`

// after
ns2, ok := s2.(*vuln.NativeReportSummary)
if !ok {
    return nil, fmt.Errorf("second summary is %T, want *vuln.NativeReportSummary", s2)
}
sum, err := report.MergeNativeSummary(ns1, ns2)
Defensive patterns

Strategy: type-guard

Validate before calling

if !isNativeSummary(s2) {
    return fmt.Errorf("second summary operand is %T, want *vuln.NativeReportSummary", s2)
}

Type guard

func isNativeSummary(v any) bool {
    _, ok := v.(*vuln.NativeReportSummary)
    return ok
}

Try / catch

sum, err := report.MergeNativeSummary(ns1, s2)
if err != nil && strings.Contains(err.Error(), "native report summary required") {
    return fmt.Errorf("s2 not a native summary (%T); regenerate it from its report", s2)
}

Prevention

When it happens

Trigger: Merging summaries collected from multiple scan requests where the second one was loaded from storage, a different code path, or never initialised (nil any).

Common situations: Incremental summary updates mixing old and new payload shapes; concurrent producers writing summaries with different versions of the model; nil elements in a slice being folded into one summary.

Related errors


AI-assisted analysis of goharbor/harbor@7b2fd08cc5 (2026-08-16). Data as JSON: /api/errors/b0b1a69c5766423f. Report an issue: GitHub.