docker/compose · error

git subdirectory path traversal detected: %s

Error message

git subdirectory path traversal detected: %s

What it means

The path-traversal guard of `validateGitSubDir`: after cleaning, the sub-directory equals `..` or starts with `../` (or `..\`), meaning it would escape the checked-out repository root. Compose rejects it to prevent includes from reading files outside the git cache directory.

Source

Thrown at pkg/remote/git.go:148

	return local, err
}

func (g gitRemoteLoader) Dir(path string) string {
	return g.known[path]
}

// validateGitSubDir ensures a subdirectory path is contained within the base directory
// and doesn't escape via path traversal. Unlike validatePathInBase for OCI artifacts,
// this allows nested directories but prevents traversal outside the base.
func validateGitSubDir(base, subDir string) error {
	cleanSubDir := filepath.Clean(subDir)

	if filepath.IsAbs(cleanSubDir) {
		return fmt.Errorf("git subdirectory must be relative, got: %s", subDir)
	}

	if cleanSubDir == ".." || strings.HasPrefix(cleanSubDir, "../") || strings.HasPrefix(cleanSubDir, "..\\") {
		return fmt.Errorf("git subdirectory path traversal detected: %s", subDir)
	}

	if len(cleanSubDir) >= 2 && cleanSubDir[1] == ':' {
		return fmt.Errorf("git subdirectory must be relative, got: %s", subDir)
	}

	targetPath := filepath.Join(base, cleanSubDir)
	cleanBase := filepath.Clean(base)
	cleanTarget := filepath.Clean(targetPath)

	// Ensure the target starts with the base path
	relPath, err := filepath.Rel(cleanBase, cleanTarget)
	if err != nil {
		return fmt.Errorf("invalid git subdirectory path: %w", err)
	}

	if relPath == ".." || strings.HasPrefix(relPath, "../") || strings.HasPrefix(relPath, "..\\") {
		return fmt.Errorf("git subdirectory escapes base directory: %s", subDir)

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Point the include at a path inside the repository: `git://host/repo.git#ref:valid/subdir`
  2. If you need files from another repo, declare a second include for that repo instead of traversing with `..`

Example fix

# before
include:
  - path: git://github.com/org/repo.git#main:../shared

# after
include:
  - path: git://github.com/org/repo.git#main:shared
  - path: git://github.com/org/shared.git#main
Defensive patterns

Strategy: validation

Validate before calling

# reject traversal fragments before invoking compose
python3 - <<'EOF'
import sys, posixpath
for arg in sys.argv[1:]:
    if '#' not in arg: continue
    sub = arg.split('#',1)[1].split(':',1)[1] if ':' in arg.split('#',1)[1] else ''
    c = posixpath.normpath(sub)
    if c == '..' or c.startswith('../') or c.startswith('..\\'):
        sys.exit(f"traversal in include fragment: {arg}")
EOF

Prevention

When it happens

Trigger: An include fragment crafted (or typo'd) as `..`, `../other-project`, `..\other`, or a path like `subdir/../../..` that survives cleaning with a `..` prefix.

Common situations: Attempting to include a sibling checkout via relative `..` paths; malformed include strings from templating; the error is also the expected response to malicious path-traversal payloads.

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/9613a10d904ebaf1. Report an issue: GitHub.