Skip to content

Implement --registry flag for scanning all images in a Docker registry#4898

Open
jainlakshya wants to merge 14 commits intotrufflesecurity:mainfrom
jainlakshya:feature/implement-docker-registry-flag
Open

Implement --registry flag for scanning all images in a Docker registry#4898
jainlakshya wants to merge 14 commits intotrufflesecurity:mainfrom
jainlakshya:feature/implement-docker-registry-flag

Conversation

@jainlakshya
Copy link
Copy Markdown

@jainlakshya jainlakshya commented Apr 17, 2026

Summary
Implements #1739 - Adds --registry flag to scan all images in a Docker registry, similar to trufflehog github --org=...

Motivation
Currently, TruffleHog can only scan specific Docker images via --image or images within a namespace via --namespace (limited to DockerHub, GHCR, and Quay). This PR adds support for scanning entire private registries (Harbor, Nexus, Artifactory, etc.) by enumerating all repositories using the OCI Distribution Spec /v2/_catalog endpoint.

Changes

  • CLI: Added --registry flag with proper validation and help text
  • Implementation: New GenericOCIRegistry struct implementing the Registry interface
  • API: Uses OCI Distribution Spec /v2/_catalog endpoint with Link header pagination
  • Authentication: Supports bearer token via --registry-token flag
  • Validation: Mutual exclusion with --image and --namespace flags
  • Bug Fix: Fixed UseDockerKeychain logic to not activate for registry/namespace scans
  • Tests: Added 5 comprehensive unit tests with 100% coverage of new code

Usage Examples

Scan all images in a private registry (unauthenticated):
trufflehog docker --registry registry.example.com

Scan with authentication:
trufflehog docker --registry harbor.corp.io --registry-token

Scan with both registry token and image pull token:
trufflehog docker --registry nexus.internal --registry-token --token

Technical Details

  • Implements OCI Distribution Spec v1.0 /v2/_catalog endpoint
  • Pagination via Link response header (same pattern as GHCR)
  • Rate limiting: 1 request per 1.5s with burst of 2 (existing registryRateLimiter)
  • Metrics: Reuses existing dockerListImagesAPIDuration histogram
  • Compatible with: Harbor, Nexus Repository, Artifactory, JFrog, and any OCI-compliant registry

Testing

  • All new unit tests pass
  • Existing tests remain passing
  • Build successful
  • CLI validation working correctly
  • Manual testing with mock HTTP servers

Breaking Changes
None. This is a purely additive feature.

Checklist

  • Added tests for new functionality
  • Updated CLI help text
  • Validated mutual exclusion logic
  • Verified backward compatibility
  • No breaking changes

Note

Medium Risk
Adds a new Docker scan mode that enumerates repositories via registry HTTP APIs and threads new registry data through protobuf/config, which could impact Docker scanning behavior and request patterns if misconfigured.

Overview
Adds a new trufflehog docker --registry mode to scan all images in a private OCI registry host by enumerating repositories via /v2/_catalog (with Link-header pagination) and then scanning the discovered images.

Updates CLI validation to require exactly one of --image, --namespace, or --registry, rejects --registry for known public registries, sanitizes registry host input, and adjusts UseDockerKeychain behavior for registry scans. Plumbs the new registry field through sources.DockerConfig, protobuf (sources.proto/generated), and engine wiring, and adds unit tests covering generic registry listing, pagination, auth header behavior, and error cases.

Reviewed by Cursor Bugbot for commit d9e37ba. Bugbot is set up for automated code reviews on this repo. Configure here.

Implements GitHub issue trufflesecurity#1739

Added --registry flag to scan all images in an OCI Distribution Spec
compliant registry (Harbor, Nexus, Artifactory, etc.) using the
/v2/_catalog endpoint.

Changes:
- Added --registry CLI flag with validation
- Implemented GenericOCIRegistry for /v2/_catalog enumeration
- Added Link header pagination support
- Added bearer token authentication via --registry-token
- Fixed UseDockerKeychain logic to not activate for registry scans
- Added comprehensive test coverage

Usage:
  trufflehog docker --registry registry.example.com
  trufflehog docker --registry harbor.corp.io --registry-token <token>

Resolves: trufflesecurity#1739
@jainlakshya jainlakshya requested a review from a team April 17, 2026 22:58
@jainlakshya jainlakshya requested review from a team as code owners April 17, 2026 22:58
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Apr 17, 2026

CLA assistant check
All committers have signed the CLA.

Comment thread pkg/sources/docker/registries.go Outdated
Fixed pagination bug where relative URLs from OCI Distribution Spec
registries (Docker Distribution, Harbor, Nexus) would fail with
'unsupported protocol scheme' error.

The OCI spec allows registries to return relative URLs in Link headers
like </v2/_catalog?n=100&last=repo100>; rel="next". These need to be
resolved against the base URL before making the next request.

Changes:
- Added resolveNextURL() to resolve relative URLs against base URL
- Modified ListImages() to use URL resolution for pagination
- Updated test to use relative URL (matching real OCI behavior)
- Added test for absolute URL pagination (GHCR-style)

Both relative and absolute URLs now work correctly.
Comment thread main.go Outdated
Only disable Docker keychain for registry scans (which use registry API token).
Namespace and image scans should still use Docker keychain when no bearer token is provided.
Comment thread pkg/sources/docker/registries.go
Previously resolveNextURL silently returned empty string on parse errors,
causing pagination to stop without warning and potentially skipping repositories.
Now returns explicit error so caller knows scanning is incomplete.
Copy link
Copy Markdown
Contributor

@kashifkhan0771 kashifkhan0771 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if someone pass --registry hub.docker.com? We should somehow restrict this flag to only accept private registries because public registries are already covered by flag --namespace

jainlakshya and others added 3 commits April 21, 2026 07:16
Add validation to prevent --registry from accepting public registry hosts
(hub.docker.com, quay.io, ghcr.io) since they are already properly handled
by --namespace with dedicated implementations using custom APIs.

Public registries use different endpoints:
- DockerHub: /v2/namespaces/<ns>/repositories
- Quay: /api/v1/repository?namespace=<ns>
- GHCR: api.github.com/users/<ns>/packages

The --registry flag is designed for private OCI registries (Harbor, Nexus,
Artifactory) that implement the standard /v2/_catalog endpoint.
@jainlakshya
Copy link
Copy Markdown
Author

@kashifkhan0771 You're absolutely right.

I've added validation to prevent --registry from accepting public registries (11d4342). Now if someone tries --registry hub.docker.com, they'll get a helpful error pointing them to use --namespace instead.

The issue was that public registries use completely different APIs (DockerHub uses /v2/namespaces//repositories, not the standard /v2/_catalog), so using --registry with them would either fail or give weird results.

Comment thread main.go
Add sanitizeRegistryHost function to strip protocol prefixes and paths
from --registry values before passing to DockerConfig. This prevents
malformed URLs like https://https://harbor.corp.io/v2/_catalog.

Users can now provide registry hosts in any format:
- --registry https://harbor.corp.io
- --registry http://localhost:5000
- --registry harbor.corp.io

All are sanitized to clean hostnames for proper URL construction.
Comment thread main.go Outdated
Use case-insensitive prefix detection to prevent hostname corruption when
users provide mixed-case protocols like HTTPS:// or Http://. Previously,
these would fail to match TrimPrefix and the path-stripping logic would
truncate at the first / in ://, producing garbage like HTTPS: or Http:.

Now correctly handles:
- HTTPS://harbor.corp.io -> harbor.corp.io
- Http://localhost:5000 -> localhost:5000
- HtTpS://registry.io -> registry.io
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 3f5dc7c. Configure here.

Comment thread main.go
Comment thread main.go
jainlakshya and others added 2 commits April 21, 2026 14:35
…ization

1. Extract normalizeRegistryHost helper to eliminate duplicated URL sanitization
   logic between isPublicRegistry and sanitizeRegistryHost. This reduces
   maintenance burden and ensures consistent behavior.

2. Add validation after sanitization to catch empty registry values that pass
   initial validation but become empty after normalization (e.g., 'https://',
   '  ', 'http://'). This prevents silent no-op scans with confusing behavior.

Both functions now use the same normalization logic, and invalid inputs are
caught with clear error messages.
@kashifkhan0771
Copy link
Copy Markdown
Contributor

You need to run make man command for the linter check to pass.

jainlakshya and others added 2 commits April 22, 2026 20:18
@jainlakshya
Copy link
Copy Markdown
Author

@kashifkhan0771 Done! Ran make man and pushed the updated documentation in 3312d83. The man page now includes the --registry flag documentation. Linter check should pass now. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants