crowdsecurity/crowdsec · error

%q: must be a relative path

Error message

%q: must be a relative path

What it means

SafePath joins a user-supplied relative path onto a base directory and refuses absolute paths. Because Windows treats "/x" as drive-rooted, the check also rejects any path starting with a path separator outright, ensuring the result stays inside the base directory namespace.

Source

Thrown at pkg/cwhub/safepath.go:23

	"path/filepath"
	"os"
	"strings"
)

// SafePath returns a joined path and ensures that it does not escape the base directory.
// We can't use the traversal-resistant methods in "os.Root" because install link targets are outside their base directories
// (installdir -> hubdir), which would not be allowed if hubdir is not inside installdir.
func SafePath(baseDir, relPath string) (string, error) {
	absBase, err := filepath.Abs(filepath.Clean(baseDir))
	if err != nil {
		return "", err
	}

	if filepath.IsAbs(relPath) ||
		// on windows, IsAbs fails for paths beginning with "/", since it's the root of the drive
		strings.HasPrefix(relPath, string(os.PathSeparator)) ||
		strings.HasPrefix(relPath, "/") {
		return "", fmt.Errorf("%q: must be a relative path", relPath)
	}

	absFilePath, err := filepath.Abs(filepath.Join(absBase, relPath))
	if err != nil {
		return "", err
	}

	rel, err := filepath.Rel(absBase, absFilePath)
	if err != nil {
		return "", err
	}

	if strings.HasPrefix(rel, "..") {
		return "", fmt.Errorf("%q: path escapes base directory %q", relPath, baseDir)
	}

	return absFilePath, nil
}

View on GitHub (pinned to 909b515798)

Solutions

  1. Pass a relative path (e.g. "parsers/s01-parse/x.yaml") instead of an absolute one
  2. Strip the base-directory prefix from stored absolute paths before calling SafePath
  3. Use filepath.ToSlash and filepath.Rel(base, p) to normalize input
  4. Treat the error as a security signal — investigate where the absolute path came from

Example fix

// before
SafePath(baseDir, "/etc/crowdsec/parsers/x.yaml")
// after
SafePath(baseDir, "parsers/s01-parse/x.yaml")
Defensive patterns

Strategy: validation

Validate before calling

func isRelative(p string) bool {
    return p != "" && !filepath.IsAbs(p) && !strings.HasPrefix(p, "/") && !strings.Contains(p, "\\\\")
}

Prevention

When it happens

Trigger: Calling SafePath (via PathForInstall, PathForDownload, downloadDataSet, or anonymous download handlers) with a path like "/etc/passwd", "C:\\x", or one beginning with "/".

Common situations: Untrusted index/index entry paths; a malicious or corrupt hub index containing absolute remote/local paths; user-supplied install path flags with absolute values.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/441311f3950f9795. Report an issue: GitHub.