{"record":{"id":"d9d1cc9db9c13bd0","repo":"goharbor/harbor","slug":"malformed-scan-report-object","errorCode":null,"errorMessage":"malformed scan report object","messagePattern":"malformed scan report object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/pkg/scan/report/manager.go","lineNumber":133,"sourceCode":"}\n\n// NewManager news basic manager.\nfunc NewManager() Manager {\n\treturn &basicManager{\n\t\tdao:     scan.New(),\n\t\tvulnDao: scan.NewVulnerabilityRecordDao(),\n\t}\n}\n\n// Create ...\nfunc (bm *basicManager) Create(ctx context.Context, r *scan.Report) (string, error) {\n\t// Validate report object\n\tif r == nil {\n\t\treturn \"\", errors.New(\"nil scan report object\")\n\t}\n\n\tif len(r.Digest) == 0 || len(r.RegistrationUUID) == 0 || len(r.MimeType) == 0 {\n\t\treturn \"\", errors.New(\"malformed scan report object\")\n\t}\n\n\tr.UUID = uuid.New().String()\n\n\t// Insert\n\tif _, err := bm.dao.Create(ctx, r); err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn r.UUID, nil\n}\n\nfunc (bm *basicManager) Delete(ctx context.Context, uuid string) error {\n\t_, err := bm.vulnDao.DeleteForReport(ctx, uuid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tquery := q.Query{Keywords: q.KeyWords{\"uuid\": uuid}}","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/goharbor/harbor/blob/7b2fd08cc568955cca339afeefab27372840d936/src/pkg/scan/report/manager.go#L115-L151","documentation":"Returned by basicManager.Create when the report is non-nil but one of Digest, RegistrationUUID, or MimeType is empty. These three fields are the minimum identity of a scan report (what artifact, scanned by which scanner, in which format); without them the row cannot be inserted meaningfully.","triggerScenarios":"Creating a report from a partially populated struct, e.g. digest set but registration UUID missing because the scanner lookup failed silently; requests arriving without mime type after mime negotiation; code that fills fields conditionally.","commonSituations":"On-demand scan API handlers losing the registration context; adapters that do not report a mime type; refactors that rename fields and leave the old ones empty.","solutions":["Populate all three required fields before Create: Digest (sha256:...), RegistrationUUID (from the scanner registration), MimeType (e.g. application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0)","Log the offending struct right before Create to see which field is blank","Validate at the API boundary and return BAD_REQUEST with field details"],"exampleFix":"// before\nr := &scan.Report{Digest: dgst}\nuuid, err := bm.Create(ctx, r)\n\n// after\nr := &scan.Report{\n    Digest:           dgst,\n    RegistrationUUID: reg.UUID,\n    MimeType:         v1.MimeTypeNativeReport,\n}\nuuid, err := bm.Create(ctx, r)","handlingStrategy":"validation","validationCode":"if r == nil || len(r.Digest) == 0 || len(r.RegistrationUUID) == 0 || len(r.MimeType) == 0 {\n    return errors.New(\"report needs digest, registration UUID and mime type\")\n}\nuuid, err := bm.Create(ctx, r)","typeGuard":"func isReportComplete(r *scan.Report) bool {\n    return r != nil &&\n        len(r.Digest) > 0 &&\n        len(r.RegistrationUUID) > 0 &&\n        len(r.MimeType) > 0\n}","tryCatchPattern":null,"preventionTips":["Build reports from a constructor that requires the three fields","Reject at the API boundary with per-field 400 details","Log the struct contents when validation fails to spot the blank field fast"],"tags":["scan","report","required-fields","validation"],"backgroundTag":null,"analyzedSha":"7b2fd08cc568955cca339afeefab27372840d936","analyzedAt":"2026-08-16T00:00:10.961Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}