Repository navigation
Description
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
- Create a file that is not valid JSON:
printf 'not json at all' > /tmp/bad-claim.json
- Run the verify subcommand against it:
cmcp verify /tmp/bad-claim.json
- Observe an uncaught
JSONDecodeErrortraceback 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.jsonExpected 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-manifestgoes throughload_agent_manifestcli.py:143—--configgoes throughload_configcli.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 eImpact 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