Harden stamp/verify/hash_report so unknown and content-shaped args fail instead of being ignored; publish additionalProperties=false and cover with schema/runtime tests. Also keep streamable-http host allowlist.
77 lines
2.6 KiB
Python
77 lines
2.6 KiB
Python
"""Memory ledger: hash-only stamp, verify, report. Same hash is idempotent."""
|
|
|
|
import pytest
|
|
|
|
from verae_mcp_timestamping_server.ledger import MemoryLedger, SHA256_EMPTY
|
|
|
|
pytestmark = pytest.mark.anyio
|
|
|
|
|
|
async def test_stamp_returns_json_array_with_one_receipt():
|
|
ledger = MemoryLedger()
|
|
receipts = await ledger.stamp(SHA256_EMPTY, scope="local")
|
|
assert isinstance(receipts, list)
|
|
assert len(receipts) == 1
|
|
rec = receipts[0]
|
|
assert rec.hash == SHA256_EMPTY
|
|
assert rec.scope == "local"
|
|
assert rec.receipt_id
|
|
assert rec.created_at
|
|
assert rec.merkle
|
|
|
|
|
|
async def test_same_hash_returns_same_receipt():
|
|
ledger = MemoryLedger()
|
|
first = await ledger.stamp(SHA256_EMPTY, scope="local")
|
|
second = await ledger.stamp(SHA256_EMPTY, scope="local")
|
|
assert first[0].receipt_id == second[0].receipt_id
|
|
assert first[0].created_at == second[0].created_at
|
|
|
|
|
|
async def test_rejects_non_sha256():
|
|
ledger = MemoryLedger()
|
|
with pytest.raises(ValueError, match="sha256"):
|
|
await ledger.stamp("not-a-hash", scope="local")
|
|
|
|
|
|
async def test_verify_receipt_and_scope_rank():
|
|
ledger = MemoryLedger()
|
|
[rec] = await ledger.stamp(SHA256_EMPTY, scope="local")
|
|
ok = await ledger.verify(rec.receipt_id, want_scope="local")
|
|
assert ok.valid is True
|
|
assert ok.receipt is not None
|
|
assert ok.receipt.receipt_id == rec.receipt_id
|
|
|
|
weak = await ledger.verify(rec.receipt_id, want_scope="global")
|
|
assert weak.valid is False
|
|
|
|
|
|
async def test_hash_report_is_json_array():
|
|
ledger = MemoryLedger()
|
|
await ledger.stamp(SHA256_EMPTY, scope="local")
|
|
report = await ledger.hash_report(SHA256_EMPTY)
|
|
assert isinstance(report, list)
|
|
assert report[0].hash == SHA256_EMPTY
|
|
|
|
|
|
def test_validate_sha256_rejects_content_shaped_strings():
|
|
from verae_mcp_timestamping_server.ledger import validate_sha256
|
|
import base64
|
|
import pytest
|
|
|
|
with pytest.raises(ValueError, match="sha256"):
|
|
validate_sha256("")
|
|
with pytest.raises(ValueError, match="sha256"):
|
|
validate_sha256(base64.b64encode(b"hello world file bytes").decode())
|
|
with pytest.raises(ValueError, match="sha256"):
|
|
validate_sha256("line1\nline2")
|
|
|
|
|
|
def test_reject_content_keys_lists_forbidden():
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
from verae_mcp_timestamping_server.guard import reject_content_keys
|
|
|
|
with pytest.raises(ValidationError, match="content/bytes paths are rejected"):
|
|
reject_content_keys({"sha256": "a" * 64, "content": "nope"})
|
|
reject_content_keys({"sha256": "a" * 64, "scope": "local"}) # ok
|