From 6d4ccfa8f6dc80ba321a6a781f609f02e12344ef Mon Sep 17 00:00:00 2001 From: Harmon Herring <109163085+harmonherring-pro@users.noreply.github.com> Date: Tue, 22 Apr 2025 08:23:57 -0400 Subject: [PATCH] [Feat] Support Dockerhub OATs (#4062) * add support for dockerhub oats * fix pattern * fix pattern length requirements --------- Co-authored-by: Kashif Khan <70996046+kashifkhan0771@users.noreply.github.com> --- pkg/detectors/dockerhub/v2/dockerhub.go | 12 ++++++------ pkg/detectors/dockerhub/v2/dockerhub_test.go | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/pkg/detectors/dockerhub/v2/dockerhub.go b/pkg/detectors/dockerhub/v2/dockerhub.go index a108c358c..68a47dc0b 100644 --- a/pkg/detectors/dockerhub/v2/dockerhub.go +++ b/pkg/detectors/dockerhub/v2/dockerhub.go @@ -32,14 +32,14 @@ var ( usernamePat = regexp.MustCompile(`(?im)(?:user|usr|-u|id)\S{0,40}?[:=\s]{1,3}[ '"=]?([a-zA-Z0-9]{4,40})\b`) emailPat = regexp.MustCompile(common.EmailPattern) - // Can use password or personal access token (PAT) for login, but this scanner will only check for PATs. - accessTokenPat = regexp.MustCompile(`\b(dckr_pat_[a-zA-Z0-9_-]{27})(?:[^a-zA-Z0-9_-]|\z)`) + // Can use password or personal/organization access token (PAT/OAT) for login, but this scanner will only check for PATs and OATs. + accessTokenPat = regexp.MustCompile(`\b(dckr_pat_[a-zA-Z0-9_-]{27}|dckr_oat_[a-zA-Z0-9_-]{32})(?:[^a-zA-Z0-9_-]|\z)`) ) // Keywords are used for efficiently pre-filtering chunks. // Use identifiers in the secret preferably, or the provider name. func (s Scanner) Keywords() []string { - return []string{"dckr_pat_"} + return []string{"dckr_pat_", "dckr_oat_"} } // FromData will find and optionally verify Dockerhub secrets in a given set of bytes. @@ -105,9 +105,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result } func (s Scanner) verifyMatch(ctx context.Context, username string, password string) (bool, map[string]string, error) { - payload := strings.NewReader(fmt.Sprintf(`{"username": "%s", "password": "%s"}`, username, password)) + payload := strings.NewReader(fmt.Sprintf(`{"identifier": "%s", "secret": "%s"}`, username, password)) - req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://hub.docker.com/v2/users/login", payload) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://hub.docker.com/v2/auth/token", payload) if err != nil { return false, nil, err } @@ -162,7 +162,7 @@ func (s Scanner) verifyMatch(ctx context.Context, username string, password stri } type tokenResponse struct { - Token string `json:"token"` + Token string `json:"access_token"` } type userClaims struct { diff --git a/pkg/detectors/dockerhub/v2/dockerhub_test.go b/pkg/detectors/dockerhub/v2/dockerhub_test.go index 3945dc91d..e1d888409 100644 --- a/pkg/detectors/dockerhub/v2/dockerhub_test.go +++ b/pkg/detectors/dockerhub/v2/dockerhub_test.go @@ -30,6 +30,7 @@ var ( user: rRwOdIJpY90QrIzOXO95d3hlSzRk5Z9a docker_email: "docker-test@dockerhub.com" docker_token: "dckr_pat_dlndn9l2JLhWvbdyP3blEZw_j7d" + docker_org_token: "dckr_oat_7bA9zRt5-JqX3vP0l_MnY8sK2wE-dF6h" # Notes: # - Remember to rotate the secret every 90 days. @@ -38,6 +39,8 @@ var ( secrets = []string{ "rRwOdIJpY90QrIzOXO95d3hlSzRk5Z9a:dckr_pat_dlndn9l2JLhWvbdyP3blEZw_j7d", "docker-test@dockerhub.com:dckr_pat_dlndn9l2JLhWvbdyP3blEZw_j7d", + "rRwOdIJpY90QrIzOXO95d3hlSzRk5Z9a:dckr_oat_7bA9zRt5-JqX3vP0l_MnY8sK2wE-dF6h", + "docker-test@dockerhub.com:dckr_oat_7bA9zRt5-JqX3vP0l_MnY8sK2wE-dF6h", } )