Skip to content

provenance verifier never enforces required tool_catalog.tool_count #256

Closed

Description

@altrudev

Current behavior

spec/server-provenance-v1.md marks tool_catalog.tool_count as required and defines it as the number of tools described by the catalog. The field is intentionally redundant with the hash so a consumer can distinguish a plausible-count/hash mismatch from a completely different catalog.

At current main (25013c59d607860a4a4d2608476280286f688243):

  • build_record() emits tool_count = len(tools);
  • verify_record() validates tool_catalog.hash but never validates tool_count;
  • check_tool_catalog() reads tool_count only after the hash has already mismatched, to include it in the diagnostic.

A correctly signed record can therefore carry the correct catalog hash for two tools and tool_count: 999; verify_record() succeeds and check_tool_catalog() also succeeds because the hash matches.

The same is true for missing, negative, boolean, string, or otherwise malformed tool_count values: the required field is not load-bearing on the consumer path.

Why this is distinct from #142

Issue #142 noted in passing that tool_count was never checked, but its merged fix #146 addressed the four structural defects that allowed tee-attested records without evidence and malformed identity/timestamp state. tool_count remains unchanged on current main.

This issue isolates that remaining field rather than reopening #142.

Smallest fix

There are two complementary checks:

  1. verify_record() should require tool_count to be a non-negative JSON integer (excluding bool), because the spec marks the member required.
  2. check_tool_catalog() should compare the declared count with len(tools) after the hash matches, so a signed internally inconsistent record cannot pass the live-server check.

For example:

declared_count = catalog.get("tool_count")
if (
    not isinstance(declared_count, int)
    or isinstance(declared_count, bool)
    or declared_count < 0
):
    raise ProvenanceError("tool_catalog.tool_count must be a non-negative integer")

If the live hash mismatches, the existing ToolCatalogMismatch remains the right result: the paper may be fine and the server differs. If the live hash matches but len(tools) != declared_count, the record contradicts its own signed catalog metadata, so that should remain an ordinary ProvenanceError, not ToolCatalogMismatch.

Regression evidence to add

  • correct hash + correct count -> unchanged success;
  • correct hash + wrong positive count -> verify_record() can establish only structural validity, but check_tool_catalog() refuses the internally inconsistent record;
  • missing count -> ProvenanceError;
  • negative count -> ProvenanceError;
  • boolean count -> ProvenanceError;
  • string count -> ProvenanceError;
  • ordinary catalog hash mismatch -> existing ToolCatalogMismatch behavior preserved.

No wire-format or normative specification change is needed; this makes the reference consumer enforce an already required signed field while preserving the existing bad-document versus wrong-server error distinction.

AI-assistance disclosure: ChatGPT assisted with source triage, adversarial-case design, and drafting. altrudev reviewed the bounded claim and remains responsible for the contribution.

Activity

  1. github-actions commented on Aug 30, 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