{"record":{"id":"d80bfdaf2dd6819b","repo":"HelloZeroNet/ZeroNet","slug":"invalid-path","errorCode":null,"errorMessage":"Invalid path","messagePattern":"Invalid path","errorType":"validation","errorClass":"SecurityError","httpStatus":403,"severity":"warning","filePath":"src/Ui/UiRequest.py","lineNumber":612,"sourceCode":"        origin_pattern = \"http[s]{0,1}://(.*?/.*?/).*\"\n        is_origin_full = re.match(origin_pattern, url_a)\n        if not is_origin_full:  # Origin looks trimmed to host, require only same host\n            origin_pattern = \"http[s]{0,1}://(.*?/).*\"\n\n        origin_a = re.sub(origin_pattern, \"\\\\1\", url_a)\n        origin_b = re.sub(origin_pattern, \"\\\\1\", url_b)\n\n        return origin_a == origin_b\n\n    # Return {address: 1Site.., inner_path: /data/users.json} from url path\n    def parsePath(self, path):\n        path = path.replace(\"\\\\\", \"/\")\n        path = path.replace(\"/index.html/\", \"/\")  # Base Backward compatibility fix\n        if path.endswith(\"/\"):\n            path = path + \"index.html\"\n\n        if \"../\" in path or \"./\" in path:\n            raise SecurityError(\"Invalid path\")\n\n        match = re.match(r\"/media/(?P<address>[A-Za-z0-9]+[A-Za-z0-9\\._-]+)(?P<inner_path>/.*|$)\", path)\n        if match:\n            path_parts = match.groupdict()\n            if self.isDomain(path_parts[\"address\"]):\n                path_parts[\"address\"] = self.resolveDomain(path_parts[\"address\"])\n            path_parts[\"request_address\"] = path_parts[\"address\"]  # Original request address (for Merger sites)\n            path_parts[\"inner_path\"] = path_parts[\"inner_path\"].lstrip(\"/\")\n            if not path_parts[\"inner_path\"]:\n                path_parts[\"inner_path\"] = \"index.html\"\n            return path_parts\n        else:\n            return None\n\n    # Serve a media for site\n    def actionSiteMedia(self, path, header_length=True, header_noscript=False):\n        try:\n            path_parts = self.parsePath(path)","sourceCodeStart":594,"sourceCodeEnd":630,"githubUrl":"https://github.com/HelloZeroNet/ZeroNet/blob/454c0b2e7e000fda7000cba49027541fbf327b96/src/Ui/UiRequest.py#L594-L630","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove any '../' or './' segments from the path before requesting; use absolute inner paths starting from the site root","Normalize/resolve the URL on the client side (e.g. posixpath.normpath) and verify it has no relative segments","If building inner_paths programmatically, sanitize with a whitelist regex like [A-Za-z0-9._-] and reject '..' components","If you control the site, fix the offending links/templates that emit relative URLs"],"exampleFix":"// before\nimg_src = '../data/logo.png'\n// after\nimport posixpath\npath = posixpath.normpath('/' + img_src).lstrip('/')\nassert '../' not in path and './' not in path","handlingStrategy":"validation","validationCode":"import posixpath\n\ndef is_safe_path(path):\n    p = path.replace('\\\\', '/')\n    normalized = posixpath.normpath(p)\n    return '../' not in p and './' not in p and normalized == p or '../' not in normalized","typeGuard":"def has_no_traversal(path: str) -> bool:\n    return '../' not in path and './' not in path","tryCatchPattern":null,"preventionTips":["Always normalize request paths with posixpath.normpath before sending","Build inner paths from whitelisted characters only ([A-Za-z0-9._-])","Never concatenate user input directly into site/media URLs","Use absolute paths from the site root instead of relative segments"],"tags":["security","path-traversal","url"],"backgroundTag":"path-traversal-blocked","analyzedSha":"454c0b2e7e000fda7000cba49027541fbf327b96","analyzedAt":"2026-09-02T19:46:57.278Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}