Skip to content

Commit 1baeeee

Browse files
waveywavesclaude
andcommitted
fix(materials): best-effort SPDX main component extraction
Address review feedback: - Return name and version even when PrimaryPackagePurpose is absent (kind will be empty string instead of erroring out) - When OCI image reference parsing fails (e.g. missing registry credentials), log at debug level and continue with the original name instead of returning an error Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Vibhav Bobade <vibhav.bobde@gmail.com>
1 parent 9e0f4dc commit 1baeeee

2 files changed

Lines changed: 14 additions & 13 deletions

File tree

pkg/attestation/crafter/materials/spdxjson.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,21 +122,20 @@ func (i *SPDXJSONCrafter) extractMainComponent(m *api.Attestation_Material, doc
122122
name := describedPkg.PackageName
123123
version := describedPkg.PackageVersion
124124

125-
// PrimaryPackagePurpose is optional in SPDX 2.3. If absent, skip main component extraction
126-
// since we cannot determine the component kind.
125+
// PrimaryPackagePurpose is optional in SPDX 2.3. Best effort: return name
126+
// and version even if kind is unknown.
127127
kind := strings.ToLower(describedPkg.PrimaryPackagePurpose)
128-
if kind == "" {
129-
return fmt.Errorf("described package %q has no PrimaryPackagePurpose set", describedPkg.PackageName)
130-
}
131128

132129
// For container packages, standardize the name via go-containerregistry
133-
// to get the full repository name and strip any tag (matching CycloneDX behavior)
130+
// to get the full repository name and strip any tag (matching CycloneDX behavior).
131+
// If parsing fails (e.g. missing registry credentials), continue with the original name.
134132
if kind == containerComponentKind {
135133
ref, err := remotename.ParseReference(name)
136134
if err != nil {
137-
return fmt.Errorf("couldn't parse OCI image repository name: %w", err)
135+
i.logger.Debug().Err(err).Str("name", name).Msg("couldn't parse OCI image reference, using original name")
136+
} else {
137+
name = ref.Context().String()
138138
}
139-
name = ref.Context().String()
140139
}
141140

142141
m.M.(*api.Attestation_Material_SbomArtifact).SbomArtifact.MainComponent = &api.Attestation_Material_SBOMArtifact_MainComponent{

pkg/attestation/crafter/materials/spdxjson_test.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,13 @@ func TestSPDXJSONCraft(t *testing.T) {
165165
},
166166
},
167167
{
168-
name: "described package without PrimaryPackagePurpose (optional in SPDX 2.3)",
169-
filePath: "./testdata/sbom-spdx-no-purpose.json",
170-
wantDigest: "sha256:140b55bcbdd447fee1c86d50d8459b05159bebcd80a8a8da4ea6475eeab2f487",
171-
wantFilename: "sbom-spdx-no-purpose.json",
172-
wantNoMainComponent: true,
168+
name: "described package without PrimaryPackagePurpose returns best-effort name and version",
169+
filePath: "./testdata/sbom-spdx-no-purpose.json",
170+
wantDigest: "sha256:140b55bcbdd447fee1c86d50d8459b05159bebcd80a8a8da4ea6475eeab2f487",
171+
wantFilename: "sbom-spdx-no-purpose.json",
172+
wantMainComponent: "my-lib",
173+
wantMainComponentKind: "",
174+
wantMainComponentVersion: "2.0.0",
173175
annotations: map[string]string{
174176
"chainloop.material.tool.name": "syft",
175177
"chainloop.material.tool.version": "0.100.0",

0 commit comments

Comments
 (0)