Skip to content

Commit 61bb270

Browse files
committed
fix(bom): return invalidStructuredImageDescriptorError for malformed digest
appendContainerSHA returns ErrCodeInvalidRequest which IsInvalidStructured- ImageDescriptor does not recognize, so mirror-discovery and BOM-generation callers took their warn-and-continue branch instead of failing closed. Inline the sha256 format check in imageReferenceFromMapping and wrap the failure in invalidStructuredImageDescriptorError (field "digest", line, column) so callers that gate on IsInvalidStructuredImageDescriptor fail closed. Update regression tests to assert IsInvalidStructuredImageDescriptor rather than checking only the error string. Signed-off-by: Terry Howe <thowe@nvidia.com>
1 parent c89f888 commit 61bb270

2 files changed

Lines changed: 28 additions & 7 deletions

File tree

pkg/bom/extract.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ func isStructuredImageKey(key string) bool {
229229
func imageReferenceFromMapping(n *yaml.Node) (string, error) {
230230
var name, repository, tag, digest string
231231
var namePresent bool
232+
var digestNode *yaml.Node
232233
for i := 0; i+1 < len(n.Content); i += 2 {
233234
key, value := n.Content[i], n.Content[i+1]
234235
switch key.Value {
@@ -278,6 +279,7 @@ func imageReferenceFromMapping(n *yaml.Node) (string, error) {
278279
tag = scalar
279280
case imageDigestKey:
280281
digest = scalar
282+
digestNode = value
281283
}
282284
}
283285
if !namePresent {
@@ -291,7 +293,22 @@ func imageReferenceFromMapping(n *yaml.Node) (string, error) {
291293
return "", nil
292294
}
293295
ref := combineCRDTriplet(name, repository, tag)
294-
return appendContainerSHA(ref, digest)
296+
if digest == "" || strings.Contains(ref, "@") {
297+
return ref, nil
298+
}
299+
if !containerSHARE.MatchString(digest) {
300+
return "", errors.Wrap(
301+
errors.ErrCodeInvalidRequest,
302+
"invalid image descriptor member",
303+
&invalidStructuredImageDescriptorError{
304+
field: imageDigestKey,
305+
line: digestNode.Line,
306+
column: digestNode.Column,
307+
reason: fmt.Sprintf("must match sha256:<64 lowercase hex chars>, got %q", digest),
308+
},
309+
)
310+
}
311+
return ref + "@" + digest, nil
295312
}
296313

297314
func nonNullImageMappingScalar(n *yaml.Node) (string, bool) {

pkg/bom/extract_test.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -602,9 +602,10 @@ func TestExtractImagesFromYAML_InvalidContainerSHA(t *testing.T) {
602602

603603
// TestExtractImagesFromYAML_InvalidDigestInMapping exercises the fail-loud
604604
// guard for the structured image descriptor's `digest` field: any value that
605-
// does not match `^sha256:[a-f0-9]{64}$` must surface as an extraction error
606-
// so a typo, truncation, or bogus override cannot silently ship a malformed
607-
// ref into the BOM/PURL output.
605+
// does not match `^sha256:[a-f0-9]{64}$` must surface as an
606+
// invalidStructuredImageDescriptorError so mirror-discovery and BOM-generation
607+
// callers that gate on IsInvalidStructuredImageDescriptor fail closed rather
608+
// than warning-and-continuing with the image omitted.
608609
func TestExtractImagesFromYAML_InvalidDigestInMapping(t *testing.T) {
609610
tests := []struct {
610611
name string
@@ -620,7 +621,7 @@ func TestExtractImagesFromYAML_InvalidDigestInMapping(t *testing.T) {
620621
tag: "17.4"
621622
digest: notasha256
622623
`,
623-
wantSub: `invalid containerSHA "notasha256"`,
624+
wantSub: `field "digest"`,
624625
},
625626
{
626627
name: "sha256 prefix but truncated hex in digest field",
@@ -631,7 +632,7 @@ func TestExtractImagesFromYAML_InvalidDigestInMapping(t *testing.T) {
631632
tag: "17.4"
632633
digest: sha256:abc123
633634
`,
634-
wantSub: `invalid containerSHA "sha256:abc123"`,
635+
wantSub: `field "digest"`,
635636
},
636637
{
637638
name: "uppercase hex in digest field",
@@ -642,7 +643,7 @@ func TestExtractImagesFromYAML_InvalidDigestInMapping(t *testing.T) {
642643
tag: "17.4"
643644
digest: sha256:304AB813518754228F9F792F79D6DA36359B82D8ECF418096C636725F8C930AD
644645
`,
645-
wantSub: `invalid containerSHA`,
646+
wantSub: `field "digest"`,
646647
},
647648
}
648649
for _, tt := range tests {
@@ -651,6 +652,9 @@ func TestExtractImagesFromYAML_InvalidDigestInMapping(t *testing.T) {
651652
if err == nil {
652653
t.Fatal("expected error for malformed digest field")
653654
}
655+
if !IsInvalidStructuredImageDescriptor(err) {
656+
t.Errorf("IsInvalidStructuredImageDescriptor(%v) = false, want true", err)
657+
}
654658
if !strings.Contains(err.Error(), tt.wantSub) {
655659
t.Errorf("error %q does not contain %q", err.Error(), tt.wantSub)
656660
}

0 commit comments

Comments
 (0)