proofbundle 3.1.1__tar.gz → 3.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. {proofbundle-3.1.1/src/proofbundle.egg-info → proofbundle-3.1.2}/PKG-INFO +1 -1
  2. {proofbundle-3.1.1 → proofbundle-3.1.2}/pyproject.toml +1 -1
  3. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/__init__.py +1 -1
  4. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/decision.py +21 -10
  5. {proofbundle-3.1.1 → proofbundle-3.1.2/src/proofbundle.egg-info}/PKG-INFO +1 -1
  6. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_decision_hardening.py +17 -0
  7. {proofbundle-3.1.1 → proofbundle-3.1.2}/LICENSE +0 -0
  8. {proofbundle-3.1.1 → proofbundle-3.1.2}/README.md +0 -0
  9. {proofbundle-3.1.1 → proofbundle-3.1.2}/setup.cfg +0 -0
  10. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/_inspect_registry.py +0 -0
  11. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/_integration.py +0 -0
  12. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/_strict_json.py +0 -0
  13. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/adapters/__init__.py +0 -0
  14. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/adapters/_provenance.py +0 -0
  15. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/adapters/eee.py +0 -0
  16. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/adapters/inspect_ai.py +0 -0
  17. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/adapters/lm_eval.py +0 -0
  18. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/adapters/promptfoo.py +0 -0
  19. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/adapters/samples.py +0 -0
  20. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/anchors.py +0 -0
  21. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/anchors_chia.py +0 -0
  22. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/anchors_chia_add.py +0 -0
  23. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/anchors_markovian.py +0 -0
  24. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/anchors_ots.py +0 -0
  25. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/anchors_rfc3161.py +0 -0
  26. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/beacon.py +0 -0
  27. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/bundle.py +0 -0
  28. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/canonical.py +0 -0
  29. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/checkpoint.py +0 -0
  30. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/cli.py +0 -0
  31. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/demo.py +0 -0
  32. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/dsse.py +0 -0
  33. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/eee_eval_schema.json +0 -0
  34. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/emit.py +0 -0
  35. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/errors.py +0 -0
  36. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/evalclaim.py +0 -0
  37. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/experimental/__init__.py +0 -0
  38. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/experimental/enclave.py +0 -0
  39. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/hf_evals.py +0 -0
  40. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/inspect_hook.py +0 -0
  41. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/intoto.py +0 -0
  42. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/kbjwt.py +0 -0
  43. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/merkle.py +0 -0
  44. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/persample.py +0 -0
  45. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/policies/decision-receipt-template-v1.json +0 -0
  46. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/policies/research-preview-v1.json +0 -0
  47. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/policies/strict-eval-authenticated-root-template-v1.json +0 -0
  48. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/policies/strict-eval-template-v1.json +0 -0
  49. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/policies/strict-prereg-template-v1.json +0 -0
  50. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/policy.py +0 -0
  51. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/policy_profiles.py +0 -0
  52. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/prereg.py +0 -0
  53. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/py.typed +0 -0
  54. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/pytest_plugin.py +0 -0
  55. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/sdjwt.py +0 -0
  56. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/sdjwt_issue.py +0 -0
  57. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/signature.py +0 -0
  58. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/statuslist.py +0 -0
  59. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle/tlogproof.py +0 -0
  60. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle.egg-info/SOURCES.txt +0 -0
  61. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle.egg-info/dependency_links.txt +0 -0
  62. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle.egg-info/entry_points.txt +0 -0
  63. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle.egg-info/requires.txt +0 -0
  64. {proofbundle-3.1.1 → proofbundle-3.1.2}/src/proofbundle.egg-info/top_level.txt +0 -0
  65. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_adapters.py +0 -0
  66. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_adversarial.py +0 -0
  67. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_anchor_target_trustedtime.py +0 -0
  68. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_anchors_chia.py +0 -0
  69. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_anchors_chia_add.py +0 -0
  70. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_anchors_chia_claims.py +0 -0
  71. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_anchors_generic.py +0 -0
  72. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_anchors_markovian.py +0 -0
  73. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_anchors_ots.py +0 -0
  74. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_anchors_rfc3161.py +0 -0
  75. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_beacon.py +0 -0
  76. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_branch_base_check.py +0 -0
  77. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_bundle.py +0 -0
  78. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_bundle_robustness.py +0 -0
  79. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_canonical.py +0 -0
  80. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_checkpoint.py +0 -0
  81. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_claims_hygiene.py +0 -0
  82. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_cli.py +0 -0
  83. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_cli_eval.py +0 -0
  84. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_cli_require_anchor.py +0 -0
  85. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_conformance.py +0 -0
  86. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_cosignature.py +0 -0
  87. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_cosignature_mldsa.py +0 -0
  88. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_decision_anchors.py +0 -0
  89. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_decision_cli.py +0 -0
  90. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_decision_evidence.py +0 -0
  91. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_decision_fuzz.py +0 -0
  92. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_decision_policy.py +0 -0
  93. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_decision_schema.py +0 -0
  94. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_decision_validator_api.py +0 -0
  95. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_decision_verify.py +0 -0
  96. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_demo.py +0 -0
  97. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_docs_truth.py +0 -0
  98. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_dup_key_reject.py +0 -0
  99. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_ed25519_semantics.py +0 -0
  100. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_eee.py +0 -0
  101. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_emit.py +0 -0
  102. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_eval_claim_schema.py +0 -0
  103. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_eval_evidence_class.py +0 -0
  104. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_evalclaim.py +0 -0
  105. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_examples.py +0 -0
  106. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_experimental_enclave.py +0 -0
  107. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_fork_pr_secret_isolation.py +0 -0
  108. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_fuzz_parsers.py +0 -0
  109. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_hf_entry_verify_eee_digest.py +0 -0
  110. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_hf_evals.py +0 -0
  111. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_inspect_hook.py +0 -0
  112. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_intoto.py +0 -0
  113. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_intoto_claims_hygiene.py +0 -0
  114. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_intoto_content_root_migration.py +0 -0
  115. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_intoto_dsse.py +0 -0
  116. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_intoto_eval_result.py +0 -0
  117. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_intoto_examples.py +0 -0
  118. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_intoto_spec_diff.py +0 -0
  119. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_intoto_svr.py +0 -0
  120. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_kbjwt.py +0 -0
  121. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_merkle.py +0 -0
  122. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_merkle_property.py +0 -0
  123. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_ok_semantics.py +0 -0
  124. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_oversized_int_guard.py +0 -0
  125. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_persample.py +0 -0
  126. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_pinned_key_validation.py +0 -0
  127. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_policy_explain_lint.py +0 -0
  128. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_policy_profiles.py +0 -0
  129. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_policy_templates.py +0 -0
  130. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_predicate_type_enforcement.py +0 -0
  131. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_prereg.py +0 -0
  132. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_promptfoo.py +0 -0
  133. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_provenance.py +0 -0
  134. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_pytest_plugin.py +0 -0
  135. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_rekor_interop.py +0 -0
  136. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_release_integrity_gate.py +0 -0
  137. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_rfc6962_external_vectors.py +0 -0
  138. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_root_authenticity.py +0 -0
  139. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_schema.py +0 -0
  140. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_sdjwt_duplicate_cnf.py +0 -0
  141. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_sdjwt_issue.py +0 -0
  142. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_sdjwt_reference.py +0 -0
  143. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_sdjwt_verify_binding.py +0 -0
  144. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_signature.py +0 -0
  145. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_statuslist.py +0 -0
  146. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_tlogproof.py +0 -0
  147. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_trust_policy.py +0 -0
  148. {proofbundle-3.1.1 → proofbundle-3.1.2}/tests/test_verify_matrix.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proofbundle
3
- Version: 3.1.1
3
+ Version: 3.1.2
4
4
  Summary: Emit and verify portable cryptographic evidence bundles, offline: Ed25519 + RFC 6962 Merkle + optional SD-JWT.
5
5
  Author: Konrad Gruszka
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "proofbundle"
7
- version = "3.1.1"
7
+ version = "3.1.2"
8
8
  description = "Emit and verify portable cryptographic evidence bundles, offline: Ed25519 + RFC 6962 Merkle + optional SD-JWT."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -13,7 +13,7 @@ from __future__ import annotations
13
13
 
14
14
  from typing import TYPE_CHECKING
15
15
 
16
- __version__ = "3.1.1"
16
+ __version__ = "3.1.2"
17
17
 
18
18
  # The `proofbundle/v0.1` normative spec revision this build implements — kept in sync with the
19
19
  # `Revision:` line at the top of SPEC.md by tests/test_docs_truth.py (WP-B1, closes #28). Bump
@@ -429,16 +429,27 @@ def verify_decision_receipt(envelope: dict, public_key: bytes, *, strict: bool =
429
429
  # confirmation is a separate step (resolve_evidence_ref), never implied by evidence_bound.
430
430
  if isinstance(ev, list) and ev:
431
431
  r["evidence_bound"] = all(isinstance(x, dict) and _is_digest(x.get("digest")) for x in ev)
432
- val = predicate.get("validity")
433
- if isinstance(val, dict):
434
- if expected_audience is not None:
435
- r["audience_ok"] = expected_audience in (val.get("audience") or [])
436
- if not r["audience_ok"]:
437
- r["errors"].append("audience mismatch (cross-audience replay?)")
438
- if expected_nonce is not None:
439
- r["nonce_ok"] = val.get("nonce") == expected_nonce
440
- if not r["nonce_ok"]:
441
- r["errors"].append("nonce mismatch (replay?)")
432
+ # 3.1.2 fail-closed fix (audit 2026-07-13, sibling of the eval-path F4 hardening and the decision
433
+ # template/expiry gates): a relying party who supplies expected_audience/expected_nonce is ASKING for
434
+ # RFC-9901-§7.3-style replay/audience binding. If the receipt carries NO validity object (or a
435
+ # non-dict one), the previous `if isinstance(val, dict):` guard SKIPPED the checks entirely →
436
+ # audience_ok/nonce_ok stayed None → the CLI exit gate (None is not False) let it pass exit 0: a
437
+ # requested binding silently unenforced (fail-OPEN downgrade). Evaluate against {} instead, so an
438
+ # absent validity/audience/nonce is a FAIL (fail-closed), never a silent pass.
439
+ _val = predicate.get("validity")
440
+ _validity = _val if isinstance(_val, dict) else {}
441
+ if expected_audience is not None:
442
+ r["audience_ok"] = expected_audience in (_validity.get("audience") or [])
443
+ if not r["audience_ok"]:
444
+ r["errors"].append(
445
+ "audience mismatch or absent validity.audience — requested audience binding cannot be "
446
+ "enforced (cross-audience replay?, fail-closed)")
447
+ if expected_nonce is not None:
448
+ r["nonce_ok"] = _validity.get("nonce") == expected_nonce
449
+ if not r["nonce_ok"]:
450
+ r["errors"].append(
451
+ "nonce mismatch or absent validity.nonce — requested replay binding cannot be enforced "
452
+ "(replay?, fail-closed)")
442
453
 
443
454
  # Detached anchors (Fix 2): verify anchor evidence for the statement's OWN content root — sha256 over the
444
455
  # EXACT signed payload bytes (never re-canonicalized), computed only once the bytes are authentic+canonical.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proofbundle
3
- Version: 3.1.1
3
+ Version: 3.1.2
4
4
  Summary: Emit and verify portable cryptographic evidence bundles, offline: Ed25519 + RFC 6962 Merkle + optional SD-JWT.
5
5
  Author: Konrad Gruszka
6
6
  License: MIT
@@ -96,6 +96,23 @@ class TestDecisionHardening(unittest.TestCase):
96
96
  "--nonce", "deadbeefdeadbeef"])
97
97
  self.assertEqual(rc, 2)
98
98
 
99
+ def test_missing_validity_with_expected_aud_nonce_fails_closed(self):
100
+ # 3.1.2 fail-closed fix (audit 2026-07-13): a receipt with NO validity object must NOT silently pass
101
+ # when the relying party requested --aud/--nonce binding. The previous `if isinstance(val, dict):`
102
+ # guard skipped the checks → audience_ok/nonce_ok stayed None → exit 0 (a requested binding silently
103
+ # unenforced, fail-OPEN). Now an absent validity/audience/nonce is a FAIL (exit 2).
104
+ from proofbundle.decision import emit_decision_receipt # noqa: PLC0415
105
+ pred = _pred("deny")
106
+ pred.pop("validity", None)
107
+ env = emit_decision_receipt(pred, self.signer, strict=False)
108
+ r = verify_decision_receipt(env, self.pub, expected_audience="https://relying.example",
109
+ expected_nonce="n-123")
110
+ self.assertIs(r["audience_ok"], False, "absent validity must fail the requested audience binding")
111
+ self.assertIs(r["nonce_ok"], False, "absent validity must fail the requested nonce binding")
112
+ rc, _ = _run(["decision", "verify", self._write_env(env), "--pub", self.pub_b64,
113
+ "--aud", "https://relying.example", "--nonce", "n-123"])
114
+ self.assertEqual(rc, 2, "a requested binding that cannot be enforced must not exit 0")
115
+
99
116
  def test_crypto_fail_has_no_positive_trailer(self):
100
117
  env = copy.deepcopy(self.env)
101
118
  body = json.loads(base64.b64decode(env["payload"]))
File without changes
File without changes
File without changes