dgtlmoon/changedetection.io · error · ValueError

Zip Slip path traversal detected in backup archive: {member.

Error message

Zip Slip path traversal detected in backup archive: {member.filename!r}

What it means

After stripping the 'json:' prefix, the expression is parsed with jsonpath_ng.ext.parse. If it raises JsonPathParserError or JsonPathLexerError the validator rejects the input with this message including the parser error text. It means the string is not a valid JSONPath expression per the jsonpath-ng extended syntax.

Source

Thrown at changedetectionio/blueprint/backups/restore.py:74

    skipped_watches = 0

    current_tags = datastore.data['settings']['application'].get('tags', {})
    current_watches = datastore.data['watching']

    with tempfile.TemporaryDirectory() as tmpdir:
        logger.debug(f"Restore: extracting zip to {tmpdir}")
        with zipfile.ZipFile(zip_stream, 'r') as zf:
            total_uncompressed = sum(m.file_size for m in zf.infolist())
            if total_uncompressed > _MAX_DECOMPRESSED_BYTES:
                raise ValueError(
                    f"Backup archive decompressed size ({total_uncompressed // (1024 * 1024)} MB) "
                    f"exceeds the {_MAX_DECOMPRESSED_BYTES // (1024 * 1024)} MB limit"
                )
            resolved_dest = os.path.realpath(tmpdir)
            for member in zf.infolist():
                member_dest = os.path.realpath(os.path.join(resolved_dest, member.filename))
                if not member_dest.startswith(resolved_dest + os.sep) and member_dest != resolved_dest:
                    raise ValueError(f"Zip Slip path traversal detected in backup archive: {member.filename!r}")
                zf.extract(member, tmpdir)
        logger.debug("Restore: zip extracted, scanning UUID directories")

        for entry in os.scandir(tmpdir):
            if not entry.is_dir():
                continue

            uuid = entry.name
            if not _UUID_RE.match(uuid):
                logger.warning(f"Restore: skipping non-UUID directory {uuid!r}")
                continue
            tag_json_path = os.path.join(entry.path, 'tag.json')
            watch_json_path = os.path.join(entry.path, 'watch.json')

            # --- Tags (groups) ---
            if include_groups and os.path.exists(tag_json_path):
                if uuid in current_tags and not include_groups_replace:
                    logger.debug(f"Restore: skipping existing group {uuid} (replace not requested)")

View on GitHub (pinned to 5d9c7c6da7)

Solutions

  1. Validate in a REPL: from jsonpath_ng.ext import parse; parse('$.foo[*].bar')
  2. Ensure the path starts with $ or a bracket expression and brackets/quotes are balanced
  3. Use JSONPath testers and remember the extended syntax jsonpath-ng supports
  4. Don't mix jq pipe syntax into json: lines — use jq: prefix instead

Example fix

# before
json:.store.book[0].title
# after
json:$.store.book[0].title
Defensive patterns

Strategy: validation

Validate before calling

from jsonpath_ng.ext import parse
from jsonpath_ng.exceptions import JsonPathParserError, JsonPathLexerError

def jsonpath_ok(expr: str) -> bool:
    try:
        parse(expr)
        return True
    except (JsonPathParserError, JsonPathLexerError):
        return False

Type guard

def is_valid_jsonpath(expr: str) -> bool:
    try:
        parse(expr)
        return True
    except (JsonPathParserError, JsonPathLexerError):
        return False

Try / catch

try:
    parse(expr)
except (JsonPathParserError, JsonPathLexerError) as e:
    # e.message / str(e) pinpoints the offending token
    ...

Prevention

When it happens

Trigger: Submitting 'json:' lines with malformed JSONPath such as 'json:$[', 'json:.foo..[' (bad recursion/filter syntax), unknown extensions, or unbalanced brackets/quotes that fail jsonpath_ng.ext.parse().

Common situations: Confusing JSONPath with jq syntax (e.g. 'json:.foo | .bar'); missing the leading $ ; bad filter expressions like $[?(@.a ==)]; typos from hand-writing paths instead of copying from a JSONPath tester.

Related errors


AI-assisted analysis of dgtlmoon/changedetection.io@5d9c7c6da7 (2026-08-27). Data as JSON: /api/errors/a0a86a4942051b15. Report an issue: GitHub.