HelloZeroNet/ZeroNet · warning · SecurityError

Invalid path

Error message

Invalid path

What it means

parsePath rejects request paths that contain traversal sequences ('../' or './') as a security measure against path traversal attacks on zite media/file routes. It throws a SecurityError instead of a plain Exception to signal a malicious or malformed path rather than a normal routing miss. The regex for /media/ routes then only processes safe, whitelisted character sets.

Source

Thrown at src/Ui/UiRequest.py:612

        origin_pattern = "http[s]{0,1}://(.*?/.*?/).*"
        is_origin_full = re.match(origin_pattern, url_a)
        if not is_origin_full:  # Origin looks trimmed to host, require only same host
            origin_pattern = "http[s]{0,1}://(.*?/).*"

        origin_a = re.sub(origin_pattern, "\\1", url_a)
        origin_b = re.sub(origin_pattern, "\\1", url_b)

        return origin_a == origin_b

    # Return {address: 1Site.., inner_path: /data/users.json} from url path
    def parsePath(self, path):
        path = path.replace("\\", "/")
        path = path.replace("/index.html/", "/")  # Base Backward compatibility fix
        if path.endswith("/"):
            path = path + "index.html"

        if "../" in path or "./" in path:
            raise SecurityError("Invalid path")

        match = re.match(r"/media/(?P<address>[A-Za-z0-9]+[A-Za-z0-9\._-]+)(?P<inner_path>/.*|$)", path)
        if match:
            path_parts = match.groupdict()
            if self.isDomain(path_parts["address"]):
                path_parts["address"] = self.resolveDomain(path_parts["address"])
            path_parts["request_address"] = path_parts["address"]  # Original request address (for Merger sites)
            path_parts["inner_path"] = path_parts["inner_path"].lstrip("/")
            if not path_parts["inner_path"]:
                path_parts["inner_path"] = "index.html"
            return path_parts
        else:
            return None

    # Serve a media for site
    def actionSiteMedia(self, path, header_length=True, header_noscript=False):
        try:
            path_parts = self.parsePath(path)

View on GitHub (pinned to 454c0b2e7e)

Solutions

  1. Remove any '../' or './' segments from the path before requesting; use absolute inner paths starting from the site root
  2. Normalize/resolve the URL on the client side (e.g. posixpath.normpath) and verify it has no relative segments
  3. If building inner_paths programmatically, sanitize with a whitelist regex like [A-Za-z0-9._-] and reject '..' components
  4. If you control the site, fix the offending links/templates that emit relative URLs

Example fix

// before
img_src = '../data/logo.png'
// after
import posixpath
path = posixpath.normpath('/' + img_src).lstrip('/')
assert '../' not in path and './' not in path
Defensive patterns

Strategy: validation

Validate before calling

import posixpath

def is_safe_path(path):
    p = path.replace('\\', '/')
    normalized = posixpath.normpath(p)
    return '../' not in p and './' not in p and normalized == p or '../' not in normalized

Type guard

def has_no_traversal(path: str) -> bool:
    return '../' not in path and './' not in path

Prevention

When it happens

Trigger: Requesting a media or site URL whose path includes '../' or './' segments, e.g. /media/SiteAddress/../../etc/passwd or a link with relative segments like ./file.html; also paths using backslashes that are normalized to slashes but still contain traversal after normalization.

Common situations: Crafted/attacker-supplied links embedded in a zite, relative links generated incorrectly by site templates, proxies or clients rewriting URLs, or media inner_paths built by string concatenation without normalization.

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 HelloZeroNet/ZeroNet@454c0b2e7e (2026-09-02). Data as JSON: /api/errors/d80bfdaf2dd6819b. Report an issue: GitHub.