@@ -20,6 +20,7 @@ import (
2020 "github.com/google/go-containerregistry/pkg/registry"
2121 v1 "github.com/google/go-containerregistry/pkg/v1"
2222 "github.com/google/go-containerregistry/pkg/v1/layout"
23+ "github.com/google/go-containerregistry/pkg/v1/mutate"
2324 "github.com/google/go-containerregistry/pkg/v1/remote"
2425 "github.com/sirupsen/logrus"
2526 "github.com/stretchr/testify/require"
@@ -374,6 +375,77 @@ func startCPUProfile(name string) {
374375 }
375376}
376377
378+ // TestCollectImageWithHealthcheckNone reproduces a nil pointer dereference (SIGSEGV) in trivy's
379+ // history-dockerfile analyzer when an image has "HEALTHCHECK" in its history but nil Healthcheck
380+ // in its config. This happens when some build tools (e.g. Buildah, Kaniko) record a HEALTHCHECK
381+ // history entry without populating the config field.
382+ //
383+ // The TypeHistoryDockerfile analyzer is disabled in production to avoid this crash.
384+ // This test verifies the crash by temporarily re-enabling it.
385+ func TestCollectImageWithHealthcheckNone (t * testing.T ) {
386+ ctx := context .Background ()
387+ log := logrus .New ()
388+ log .SetLevel (logrus .DebugLevel )
389+
390+ tr := httptest .NewServer (registry .New ())
391+ defer tr .Close ()
392+ u , err := url .Parse (tr .URL )
393+ require .NoError (t , err )
394+
395+ // Create an image with HEALTHCHECK in history but nil Healthcheck in config.
396+ alpineRef , err := name .ParseReference (fmt .Sprintf ("%s/alpine:3.21.3" , u .Host ))
397+ require .NoError (t , err )
398+ baseImg , err := remote .Image (alpineRef )
399+ if err != nil {
400+ // Pull from real registry and push to test registry first.
401+ idx := mustImageIndexFromPath (t , "testdata/images/alpine-3-21-3" )
402+ mustWriteImageIndex (t , u .Host , "alpine:3.21.3" , idx )
403+ idxManifest , err := idx .IndexManifest ()
404+ require .NoError (t , err )
405+ baseImg , err = idx .Image (idxManifest .Manifests [0 ].Digest )
406+ require .NoError (t , err )
407+ }
408+
409+ // Add a HEALTHCHECK history entry but leave Config.Healthcheck nil.
410+ cfg , err := baseImg .ConfigFile ()
411+ require .NoError (t , err )
412+ cfg .Config .Healthcheck = nil // explicitly nil
413+ cfg .History = append (cfg .History , v1.History {
414+ CreatedBy : "HEALTHCHECK &{[\" NONE\" ] \" 0s\" \" 0s\" \" 0s\" \" 0s\" '\\ x00'}" ,
415+ EmptyLayer : true ,
416+ })
417+ img , err := mutate .ConfigFile (baseImg , cfg )
418+ require .NoError (t , err )
419+
420+ ref , err := name .ParseReference (fmt .Sprintf ("%s/healthcheck-none:latest" , u .Host ))
421+ require .NoError (t , err )
422+ require .NoError (t , remote .Write (ref , img ))
423+
424+ mockCache := mockblobcache.MockClient {}
425+ ingestClient := & mockIngestClient {}
426+
427+ c := New (
428+ log ,
429+ config.Config {
430+ ImageID : "healthcheck-none:latest" ,
431+ ImageName : fmt .Sprintf ("%s/healthcheck-none:latest" , u .Host ),
432+ Timeout : 1 * time .Minute ,
433+ Mode : config .ModeRemote ,
434+ Runtime : config .RuntimeDocker ,
435+ Parallel : 1 ,
436+ // Enable the history-dockerfile analyzer to trigger the crash.
437+ DisabledAnalyzers : []string {"secret" },
438+ },
439+ ingestClient ,
440+ mockCache ,
441+ nil ,
442+ )
443+
444+ // This panics with SIGSEGV in trivy's buildHealthcheckInstruction when
445+ // TypeHistoryDockerfile is not disabled.
446+ require .NoError (t , c .Collect (ctx ))
447+ }
448+
377449func TestFindRegistryAuth (t * testing.T ) {
378450 registryAuth := authn.AuthConfig {
379451 Username : "u" ,
0 commit comments