Skip to content

cmcp verify raises uncaught JSONDecodeError on malformed claim file or audit bundle #844

Open

Description

@saintmalik

cmcp-gateway version

Source build at git SHA 30d21b4.

Note the package in this checkout identifies as cmcp-runtime 0.5.0 with entry point
cmcp = "cmcp_runtime.cli:main" (pyproject.toml:71); there is no cmcp-gateway binary here, so I'm reporting the SHA plus the installed distribution version.

Python version

Python 3.11.14

TEE provider

none, this is the verify CLI input path, not a hardware-attestation path.

Reproduction steps

  1. Create a file that is not valid JSON:
    printf 'not json at all' > /tmp/bad-claim.json
  2. Run the verify subcommand against it:
    cmcp verify /tmp/bad-claim.json
  3. Observe an uncaught JSONDecodeError traceback instead of a diagnostic message.

The same applies to the audit-bundle input:

printf 'not json at all' > /tmp/bad-bundle.json
cmcp verify <valid-claim.json> --audit-bundle /tmp/bad-bundle.json

Expected behavior

A structured error naming the file and the parse position, and a non-zero exit — the same
shape as the other CLI input paths in this same command:

  • cli.py:119 — --agent-manifest goes through load_agent_manifest
  • cli.py:143 — --config goes through load_config
  • cli.py:110 — bundle loading opens in binary with its own handling

All three report cleanly. CLAIM_FILE and --audit-bundle do not.

Actual behavior

Full traceback, and JSONDecodeError escapes the Click command:

Traceback (most recent call last):
  ...
  File ".../click/core.py", line 910, in invoke
    return callback(*args, **kwargs)
  File ".../src/cmcp_runtime/cli.py", line 240, in verify
    claim = _json.load(f)
    ^^^^^^^^^^^^^
  ...
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Exit code is 1, but it comes from the unhandled exception rather than a handled error path.

Relevant logs or TRACE Claim output

Both affected sites read caller-supplied JSON with a raw open() + json.load() and no error handling:

src/cmcp_runtime/cli.py

Line Input Declaration Read by
239-240 claim_file (positional) cli.py:192 click.Path(exists=True) raw open() + _json.load()
293-294 --audit-bundle cli.py:201 click.Path(exists=True) raw open() + _json.load()

click.Path(exists=True) checks that the file exists but says nothing about its contents, so malformed input reaches json.load unguarded.

Suggested fix, route both through one loader that converts JSONDecodeError and OSError into click.ClickException, including the path and parse offset:

def _load_json_file(path: str, label: str) -> dict:
    try:
        with open(path) as f:
            return _json.load(f)
    except OSError as e:
        raise click.ClickException(f"{label} {path!r} could not be read: {e}") from e
    except _json.JSONDecodeError as e:
        raise click.ClickException(
            f"{label} {path!r} is not valid JSON: {e.msg} "
            f"at line {e.lineno} column {e.colno}"
        ) from e

Impact is caller-supplied input, so this is a robustness and usability defect rather than a security boundary, the caller already controls both the file and the process. The harm is a traceback where automation expects a structured error.

Conformance test ID (if applicable)

Not applicable, this is CLI input handling, not an ATTEST/POLICY/AUDIT/TRACE conformance case. The nearest existing coverage is whatever exercises verify happy-path inputs.

Note on timing

There is an active hardening wave (#798–#814 / PRs #800–#819) covering adjacent verify
surfaces (sev_snp, tpm, tcg_event_log, nv_certify, provenance, NRAS). This CLI boundary does not appear to be in that set, if it is already queued, please close this as a duplicate rather than carrying duplicate work.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions