Skip to content

DAG-JSON decode() doesn't validate {" /": ...} namespace strictly #35

Description

@sumanjeet0012

The DAG-JSON spec says any {" /": ...} that isn't a CID link or bytes is an error in strict mode. _restore_from_json() silently passes through unrecognized patterns.

Problem

In dag/codecs/dag_json.py:

def _restore_from_json(node: Any) -> Any:
    if isinstance(node, dict):
        if len(node) == 1 and _LINK_KEY in node:
            link_value = node[_LINK_KEY]

            if isinstance(link_value, str):
                return _parse_cid_string(link_value)

            if isinstance(link_value, dict) and len(link_value) == 1 and "bytes" in link_value:
                return _base64_decode_no_pad(link_value["bytes"])

            # ← Unrecognized {"/": ...} patterns are silently ignored!

        return {k: _restore_from_json(v) for k, v in node.items()}
    # ...

This means:

# These should raise errors in strict mode, but don't:
decode(b'{" /": 123}')                    # Integer value
decode(b'{" /": [1, 2, 3]}')              # Array value
decode(b'{" /": {"unknown": "value"}}')   # Unrecognized dict

Proposed Solution

Add strict validation:

def _restore_from_json(node: Any, strict: bool = True) -> Any:
    if isinstance(node, dict):
        if len(node) == 1 and _LINK_KEY in node:
            link_value = node[_LINK_KEY]

            if isinstance(link_value, str):
                return _parse_cid_string(link_value)

            if isinstance(link_value, dict) and len(link_value) == 1 and "bytes" in link_value:
                return _base64_decode_no_pad(link_value["bytes"])

            if strict:
                raise ValueError(
                    f'Invalid DAG-JSON: {{"/": ...}} must be a CID string or '
                    f'{{"bytes": "..."}}, got {type(link_value).__name__}'
                )

        return {k: _restore_from_json(v, strict) for k, v in node.items()}
    # ...

Add a strict parameter to DagJsonCodec.decode():

def decode(self, data: bytes, strict: bool = True) -> IPLDNode:
    raw = json.loads(data)
    return _restore_from_json(raw, strict=strict)

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions