Skip to content

fix(policy,catalog): enforce canonical base64url signatures, strict decimal bundle versions, and duplicate-key rejection #802

Open

Description

@drk-ram

Summary

Three verification boundary gaps exist in src/cmcp_runtime/policy/bundle.py and src/cmcp_runtime/catalog/approval.py:

  1. Non-canonical base64url signatures accepted in bundle._b64url_decode and approval._decode (src/cmcp_runtime/policy/bundle.py lines 68-70, src/cmcp_runtime/catalog/approval.py lines 108-115):

    • Neither decoder checks ^[A-Za-z0-9_-]+$, len(value) % 4 != 1, and exact round-trip base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii") == value.
    • As a result, 64-byte Ed25519 signatures with non-zero unused low bits in the 86th base64url character (or standard base64 +///= characters) are accepted in PolicySigningKeys.apply, verify_bundle_signature, and verify_catalog_change.
  2. Permissive integer parsing in parse_bundle_version (src/cmcp_runtime/policy/bundle.py lines 252-265):

    • parse_bundle_version calls tuple(int(p) for p in version.split(".")) without checking isinstance(version, str) and version or p.isascii() and p.isdecimal(), accepting negative integers ("1.-2.0"), +/_ signs ("1.+2.0", "1.2_0.0"), whitespace (" 1.0.0 "), and non-ASCII Unicode digits.
  3. Duplicate JSON keys and NaN/Infinity accepted in load_policy_bundle, PolicySigningKeys.apply_file, and _approval_schema:

    • manifest.json and signing-key-revocations.json in bundle.py are parsed with bare json.loads(...) without object_pairs_hook or parse_constant.

Proposed Fix

  • Enforce canonical unpadded base64url (^[A-Za-z0-9_-]+$, len % 4 != 1, round-trip match) in bundle._b64url_decode and approval._decode.
  • Require each dotted segment in parse_bundle_version to satisfy p.isascii() and p.isdecimal().
  • Reject duplicate JSON keys and NaN/Infinity in load_policy_bundle and PolicySigningKeys.apply_file.

Activity

  1. github-actions commented on Oct 23, 2026

    @github-actions
    Contributor

    🟡 Contributor Check: MEDIUM

    Check Result
    Profile MEDIUM
    Credential LOW
    Overall MEDIUM

    Automated check by AgenTrust Contributor Check.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions