Revert "Revert "Added support for scanning images from the docker daemon (#4276)" (#4291)"

This reverts commit 562dd7242b.
This commit is contained in:
Stephen Aghaulor
2025-07-11 15:10:42 -07:00
parent 55cfe6b18a
commit 053d6d8b70
4 changed files with 118 additions and 11 deletions
+7
View File
@@ -280,7 +280,14 @@ trufflehog gcs --project-id=<project-ID> --cloud-environment --results=verified,
Use the `--image` flag multiple times to scan multiple images.
```bash
# to scan from a remote registry
trufflehog docker --image trufflesecurity/secrets --results=verified,unknown
# to scan from the local docker daemon
trufflehog docker --image docker://new_image:tag --results=verified,unknown
# to scan from an image saved as a tarball
trufflehog docker --image file://path_to_image.tar --results=verified,unknown
```
## 12: Scan in CI
+1 -1
View File
@@ -184,7 +184,7 @@ var (
circleCiScanToken = circleCiScan.Flag("token", "CircleCI token. Can also be provided with environment variable").Envar("CIRCLECI_TOKEN").Required().String()
dockerScan = cli.Command("docker", "Scan Docker Image")
dockerScanImages = dockerScan.Flag("image", "Docker image to scan. Use the file:// prefix to point to a local tarball, otherwise a image registry is assumed.").Required().Strings()
dockerScanImages = dockerScan.Flag("image", "Docker image to scan. Use the file:// prefix to point to a local tarball, the docker:// prefix to point to the docker daemon, otherwise an image registry is assumed.").Required().Strings()
dockerScanToken = dockerScan.Flag("token", "Docker bearer token. Can also be provided with environment variable").Envar("DOCKER_TOKEN").String()
dockerExcludePaths = dockerScan.Flag("exclude-paths", "Comma separated list of paths to exclude from scan").String()
+37 -9
View File
@@ -13,6 +13,7 @@ import (
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/daemon"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/tarball"
gzip "github.com/klauspost/pgzip"
@@ -179,8 +180,8 @@ func (s *Source) Chunks(ctx context.Context, chunksChan chan *sources.Chunk, _ .
func (s *Source) processImage(ctx context.Context, image string) (imageInfo, error) {
var (
imgInfo imageInfo
hasDigest bool
imageName name.Reference
err error
)
remoteOpts, err := s.remoteOpts()
@@ -189,6 +190,7 @@ func (s *Source) processImage(ctx context.Context, image string) (imageInfo, err
}
const filePrefix = "file://"
const dockerPrefix = "docker://"
if strings.HasPrefix(image, filePrefix) {
image = strings.TrimPrefix(image, filePrefix)
imgInfo.base = image
@@ -196,18 +198,21 @@ func (s *Source) processImage(ctx context.Context, image string) (imageInfo, err
if err != nil {
return imgInfo, err
}
} else if strings.HasPrefix(image, dockerPrefix) {
image = strings.TrimPrefix(image, dockerPrefix)
imgInfo, imageName, err = s.extractImageNameTagDigest(image)
if err != nil {
return imgInfo, err
}
imgInfo.image, err = daemon.Image(imageName)
if err != nil {
return imgInfo, err
}
} else {
imgInfo.base, imgInfo.tag, hasDigest = baseAndTagFromImage(image)
if hasDigest {
imageName, err = name.NewDigest(image)
} else {
imageName, err = name.NewTag(image)
}
imgInfo, imageName, err = s.extractImageNameTagDigest(image)
if err != nil {
return imgInfo, err
}
imgInfo.image, err = remote.Image(imageName, remoteOpts...)
if err != nil {
return imgInfo, err
@@ -219,6 +224,29 @@ func (s *Source) processImage(ctx context.Context, image string) (imageInfo, err
return imgInfo, nil
}
// extractImageNameTagDigest parses the provided Docker image string and returns a name.Reference
// representing either the image's tag or digest, and any error encountered during parsing.
func (*Source) extractImageNameTagDigest(image string) (imageInfo, name.Reference, error) {
var (
hasDigest bool
imgInfo imageInfo
imgName name.Reference
err error
)
imgInfo.base, imgInfo.tag, hasDigest = baseAndTagFromImage(image)
if hasDigest {
imgName, err = name.NewDigest(image)
} else {
imgName, err = name.NewTag(image)
}
if err != nil {
return imgInfo, imgName, err
}
return imgInfo, imgName, nil
}
// getHistoryEntries collates an image's configuration history together with the
// corresponding layer digests for any non-empty layers.
func getHistoryEntries(ctx context.Context, imgInfo imageInfo, layers []v1.Layer) ([]historyEntryInfo, error) {
+72
View File
@@ -134,6 +134,78 @@ func TestDockerImageScanWithDigest(t *testing.T) {
assert.Equal(t, 1, historyCounter)
}
func TestDockerImageScanFromLocalDaemon(t *testing.T) {
dockerDaemonTestCases := []struct {
name string
image string
}{
{
name: "TestDockerImageScanFromLocalDaemon",
image: "docker://trufflesecurity/secrets",
},
{
name: "TestDockerImageScanFromLocalDaemonWithDigest",
image: "docker://trufflesecurity/secrets@sha256:864f6d41209462d8e37fc302ba1532656e265f7c361f11e29fed6ca1f4208e11",
},
{
name: "TestDockerImageScanFromLocalDaemonWithTag",
image: "docker://trufflesecurity/secrets:latest",
},
}
for _, tt := range dockerDaemonTestCases {
t.Run(tt.name, func(t *testing.T) {
// This test assumes the local Docker daemon is running and the image exists locally.
// You may need to pull or build the image "trufflesecurity/secrets" locally before running this test.
dockerConn := &sourcespb.Docker{
Credential: &sourcespb.Docker_Unauthenticated{
Unauthenticated: &credentialspb.Unauthenticated{},
},
Images: []string{tt.image},
}
conn := &anypb.Any{}
err := conn.MarshalFrom(dockerConn)
assert.NoError(t, err)
s := &Source{}
err = s.Init(context.TODO(), "test source", 0, 0, false, conn, 1)
assert.NoError(t, err)
var wg sync.WaitGroup
chunksChan := make(chan *sources.Chunk, 1)
chunkCounter := 0
layerCounter := 0
historyCounter := 0
wg.Add(1)
go func() {
defer wg.Done()
for chunk := range chunksChan {
assert.NotEmpty(t, chunk)
chunkCounter++
if isHistoryChunk(t, chunk) {
historyCounter++
} else {
layerCounter++
}
}
}()
err = s.Chunks(context.TODO(), chunksChan)
assert.NoError(t, err)
close(chunksChan)
wg.Wait()
assert.Equal(t, 2, chunkCounter)
assert.Equal(t, 1, layerCounter)
assert.Equal(t, 1, historyCounter)
})
}
}
func TestBaseAndTagFromImage(t *testing.T) {
tests := []struct {
image string