Files
trufflehog/pkg/sources/docker/registries_test.go
3cb2166ddd Pagination and Rate-Limit Handling In Docker Registry Namespace API Calls (#4557)
* Added pagination and retry logic on rate-limit errors in docker registry list images calls

* added rate limit error failure test

* added exponential backoff

* updated defered response body discards, added more comments, removed some unnecessary logic in Quay.ListImages()

* readded quay list images query that the requests private images

* added golang.org/x/time/rate to limit registry API calling rate

* switched to using retryable http client in docker registry calls

* modified rate limiter. created constant for max page size in api request

---------

Co-authored-by: Amaan Ullah <aman.ullah.jalal@trufflesec.com>
Co-authored-by: Kashif Khan <70996046+kashifkhan0771@users.noreply.github.com>
2025-12-15 13:09:11 +05:00

103 lines
3.4 KiB
Go

package docker
import (
"fmt"
"net/http"
"slices"
"testing"
"github.com/stretchr/testify/assert"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
)
func TestDockerhubListImages(t *testing.T) {
// Dockerhub registry
dockerhub := MakeRegistryFromNamespace("trufflesecurity") // no authentication
dockerImages, err := dockerhub.ListImages(context.Background(), "trufflesecurity") // namespace without any prefix defaults to dockerhub registry
assert.NoError(t, err)
assert.Equal(t, len(dockerImages), 7)
dockerExpectedImages := []string{
"trufflesecurity/trufflehog", "trufflesecurity/lint-robot", "trufflesecurity/protos",
"trufflesecurity/driftwood", "trufflesecurity/secrets", "trufflesecurity/of-cors", "trufflesecurity/email-graffiti",
}
slices.Sort(dockerImages)
slices.Sort(dockerExpectedImages)
assert.Equal(t, dockerImages, dockerExpectedImages)
}
func TestQuayListImages(t *testing.T) {
// Quay.io registry
quay := MakeRegistryFromNamespace("quay.io/truffledockerman") // no authentication
quayImages, err := quay.ListImages(context.Background(), "quay.io/truffledockerman")
assert.NoError(t, err)
assert.Equal(t, len(quayImages), 2)
expectedQuayImages := []string{"quay.io/truffledockerman/test", "quay.io/truffledockerman/test2"}
slices.Sort(quayImages)
slices.Sort(expectedQuayImages)
assert.Equal(t, quayImages, expectedQuayImages)
}
func TestGHCRListImages(t *testing.T) {
secret, err := common.GetTestSecret(context.Background())
if err != nil {
t.Fatal(fmt.Errorf("failed to access secret: %v", err))
}
// For the personal access token test
githubToken := secret.MustGetField("GITHUB_PACKAGES_TEST")
ghcr := MakeRegistryFromNamespace("ghcr.io/mongodb")
ghcr.WithRegistryToken(githubToken) // authentication is required for GHCR
ghcrImages, err := ghcr.ListImages(context.Background(), "ghcr.io/mongodb")
assert.NoError(t, err)
assert.Equal(t, len(ghcrImages), 1)
assert.Equal(t, ghcrImages, []string{"ghcr.io/mongodb/kingfisher"})
}
func TestDockerHubListImages_RateLimitError(t *testing.T) {
t.Parallel()
// Dockerhub registry
dockerhub := MakeRegistryFromNamespace("trufflesecurity") // no authentication
// Cast dockerhub to *DockerHub registry to override the HTTP client
dockerhub.WithClient(common.ConstantResponseHttpClient(http.StatusTooManyRequests, "{}"))
dockerImages, err := dockerhub.ListImages(context.Background(), "trufflesecurity") // namespace without any prefix defaults to dockerhub registry
assert.Error(t, err)
assert.Nil(t, dockerImages)
}
func TestQuayListImages_RateLimitError(t *testing.T) {
t.Parallel()
// Quay.io registry
quay := MakeRegistryFromNamespace("quay.io/truffledockerman") // no authentication
// Cast quay to *Quay registry to override the HTTP client
quay.WithClient(common.ConstantResponseHttpClient(http.StatusTooManyRequests, "{}"))
quayImages, err := quay.ListImages(context.Background(), "quay.io/truffledockerman")
assert.Error(t, err)
assert.Nil(t, quayImages)
}
func TestGHCRListImages_RateLimitError(t *testing.T) {
t.Parallel()
// GHCR registry
ghcr := MakeRegistryFromNamespace("ghcr.io/mongodb") // no authentication
// Cast ghcr to *GHCR registry to override the HTTP client
ghcr.WithClient(common.ConstantResponseHttpClient(http.StatusTooManyRequests, "{}"))
ghcrImages, err := ghcr.ListImages(context.Background(), "ghcr.io/mongodb")
assert.Error(t, err)
assert.Nil(t, ghcrImages)
}