Skip to content

Conversation

@stescobedo92
Copy link

@stescobedo92 stescobedo92 commented Jan 8, 2026

Description

This PR implements the ability to specify a target platform when downloading Docker images. Previously, images were downloaded for ARM64 architecture by default based on the server's architecture. Now users can explicitly request images for specific platforms like linux/amd64, linux/arm64, etc.

Usage

# Download for linux/amd64
curl "https://dockerimagesave.akiel.dev/image?name=alpine:latest&platform=linux/amd64"

# URL-encoded format also supported
curl "https://dockerimagesave.akiel.dev/image?name=alpine:latest&platform=linux%2Famd64"

# Download for ARM64
curl "https://dockerimagesave.akiel.dev/image?name=alpine:latest&platform=linux/arm64"

Changes by File

registry.go

Added new types and functions for platform support:

// New Platform struct
type Platform struct {
    OS           string
    Architecture string
}

// Helper functions
func DefaultPlatform() Platform           // Returns linux/amd64
func ParsePlatform(platform string) Platform
func (p Platform) String() string         // Returns "os/architecture"

Modified ImageReference struct:

type ImageReference struct {
    Registry   string
    Repository string
    Tag        string
    Platform   Platform  // NEW FIELD
}

Modified ParseImageReference():

  • Now initializes Platform field with default value (linux/amd64)

Modified selectManifestDigest():

  • Changed from hardcoded linux/amd64 preference to using ref.Platform
  • Improved error messages showing available platforms when requested platform is not found

image.go

Modified DownloadImage() signature:

// Before
func DownloadImage(imageRef string, outputDir string) (string, error)

// After
func DownloadImage(imageRef string, outputDir string, platform string) (string, error)

Modified createOutputTar():

  • Filename now includes platform: {image}_{tag}_{os}_{arch}.tar.gz
  • Example: library_alpine_latest_linux_amd64.tar.gz

server.go

Modified imageHandler():

  • Added parsing of platform query parameter
  • Added platform validation with validatePlatform()
  • Normalizes empty platform to linux/amd64 to ensure consistent cache keys and proper deduplication of concurrent requests
  • Updated cache key to include platform (prevents cache conflicts between platforms)
  • Improved logging to show platform being downloaded

Added validatePlatform() function:

  • Validates OS: linux, windows, darwin
  • Validates architectures: amd64, arm64, arm, 386, ppc64le, s390x, riscv64
  • Returns descriptive error messages

Added security functions to prevent path traversal (CodeQL fix):

  • sanitizePathComponent() - Removes dangerous characters (/, \, ..) from path components
  • validatePathContainment() - Ensures generated paths stay within the expected directory
  • sanitizePlatform() - Returns sanitized platform reconstructed from validated whitelist values

Modified getCacheFilename():

// Before
func (s *Server) getCacheFilename(imageName string) string

// After  
func (s *Server) getCacheFilename(imageName string, platform string) string

Modified serveImageFile():

  • Added platform parameter to generate correct filename

main.go

Fixed config file handling:

  • Config file is now optional (no longer crashes if config.yaml doesn't exist)
  • Falls back to defaults (port 8080) when no config is provided
  • Auto-detects config.yaml if present in current directory

New Unit Tests

registry_test.go

Test Description
TestParsePlatform Tests parsing of platform strings (valid, empty, invalid)
TestPlatformString Tests Platform.String() serialization
TestDefaultPlatform Verifies default platform is linux/amd64
TestParseImageReference_IncludesPlatform Verifies ImageReference includes platform

server_test.go

Test Description
TestImageHandler_WithPlatform Integration test with platform parameter
TestImageHandler_WithURLEncodedPlatform Tests URL-encoded platform (linux%2Famd64)
TestImageHandler_InvalidPlatform Tests error handling for invalid platforms
TestValidatePlatform Exhaustive validation tests (16 cases)
TestGetCacheFilename_WithPlatform Tests filename generation with platform
TestImageHandler_PlatformNormalization Verifies empty platform normalizes to linux/amd64
TestSanitizePathComponent Tests path component sanitization (8 cases)
TestValidatePathContainment Tests path containment validation (5 cases)
TestSanitizePlatform Tests platform sanitization whitelist (18 cases)

image_test.go

Test Description
TestDownloadImage_WithPlatform Integration test downloading with platform
TestDownloadImage_UnsupportedPlatform Tests error for unavailable platform
TestCreateOutputTar_IncludesPlatformInFilename Verifies filename format

Testing

# Run all unit tests
go test -short -v ./...

# Run integration tests (requires network)
go test -v ./...

All 59 tests pass ✅


Security Enhancements

This PR includes security hardening to address CodeQL's "Uncontrolled data used in path expression" warning:

Path Traversal Protection

  1. sanitizePathComponent(component string) - Removes dangerous characters:

    • Path separators (/, \)
    • Parent directory sequences (..)
    • Leading dots (.hidden)
  2. sanitizeForPath(s string) - Cleans strings for safe path usage:

    • Replaces : with -
    • Replaces / with _
    • Removes \ and other unsafe characters
  3. validatePathContainment(basePath, targetPath string) - Ensures paths stay within bounds:

    • Resolves both paths to absolute form
    • Verifies target is under base directory
    • Returns error if path escapes containment
  4. sanitizePlatform(platform string) - Reconstructs platform from validated values:

    • Only allows whitelisted OS values: linux, windows, darwin
    • Only allows whitelisted architectures: amd64, arm64, arm, 386, ppc64le, s390x, riscv64
    • Reconstructs string from validated parts (never uses raw user input in paths)

Defense in Depth

Even though platform values are validated against a whitelist, we apply multiple layers of protection:

  1. Validation first: validatePlatform() rejects invalid platforms with 400 error
  2. Sanitization: sanitizePlatform() reconstructs from whitelist values only
  3. Path containment: validatePathContainment() verifies final path stays within expected directory

Checklist

  • Code compiles without errors
  • All existing tests pass
  • New unit tests added for platform functionality
  • Platform validation with helpful error messages
  • URL-encoded platform parameter supported (%2F)
  • Security: Path traversal protection (CodeQL fix)
  • Security: Whitelist-based platform sanitization
  • Security: Path containment validation
  • Cache properly separates images by platform
  • Backwards compatible (default platform is linux/amd64)

Copilot AI review requested due to automatic review settings January 8, 2026 16:42
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds platform-specific Docker image download support, allowing users to explicitly request images for specific OS/architecture combinations (e.g., linux/amd64, linux/arm64) via a new platform query parameter. Previously, images defaulted to the server's architecture.

Key changes:

  • Added Platform struct and related functions (ParsePlatform, DefaultPlatform) to handle platform specification
  • Modified image download flow to accept and use platform parameter throughout the stack
  • Updated cache filename format to include platform information to prevent conflicts
  • Enhanced error messages to show available platforms when requested platform is unavailable

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
registry.go Adds Platform struct with parsing/serialization functions; updates ImageReference to include Platform field; modifies manifest selection to use requested platform instead of hardcoded linux/amd64
server.go Adds platform query parameter parsing and validation; updates cache key generation to include platform; modifies all image serving functions to accept platform parameter
image.go Updates DownloadImage signature to accept platform parameter; modifies output filename format to include platform information
main.go Improves config file handling to make it optional with automatic detection of config.yaml
registry_test.go Adds comprehensive tests for Platform parsing, serialization, and default values
server_test.go Adds tests for platform parameter handling, validation, URL encoding, and cache filename generation
image_test.go Adds integration tests for downloading with different platforms and handling unsupported platforms

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

platform = DefaultPlatform().String()
}

// Create a unique cache key combining image name and platform

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
server.go Outdated
safeImageName := strings.ReplaceAll(ref.Repository, "/", "_")
return fmt.Sprintf("%s_%s.tar.gz", safeImageName, ref.Tag)
safePlatform := strings.ReplaceAll(ref.Platform.String(), "/", "_")
return fmt.Sprintf("%s_%s_%s.tar.gz", safeImageName, ref.Tag, safePlatform)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.
@jadolg
Copy link
Owner

jadolg commented Jan 8, 2026

Please document how to download images in the README (using wget) and (optional) update the website so users can download images without curl/wget

Copy link
Owner

@jadolg jadolg left a comment

Choose a reason for hiding this comment

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

I'm ok with using AI. Most of this new code is AI generated. But please review before requesting changes.

@jadolg jadolg self-assigned this Jan 15, 2026
@jadolg
Copy link
Owner

jadolg commented Jan 30, 2026

Mind fixing the problems found by CodeQL?
https://github.com/jadolg/DockerImageSave/pull/191/checks?check_run_id=60988930014

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.

2 participants