feat(okf): provenance stamping — origin/channel -> trust/disposition per concept, log.md entries (T6, TDD, +5)
This commit is contained in:
parent
f9a89938b4
commit
eac3c91b89
2 changed files with 125 additions and 0 deletions
|
|
@ -22,11 +22,17 @@ from llm_ingestion_guard.okf import (
|
|||
scan_concept,
|
||||
validate_concept_path,
|
||||
validate_resource_url,
|
||||
stamp_concept,
|
||||
trust_for,
|
||||
format_log_entry,
|
||||
Origin,
|
||||
Channel,
|
||||
OKFFrontmatterError,
|
||||
OKFPathError,
|
||||
OKFResourceError,
|
||||
)
|
||||
from llm_ingestion_guard.report import Report
|
||||
from llm_ingestion_guard.disposition import Trust, Disposition
|
||||
|
||||
|
||||
# --- happy path: split + parse the minimal flat subset -----------------------
|
||||
|
|
@ -256,3 +262,44 @@ def test_validate_resource_url_rejects_embedded_whitespace():
|
|||
# a space-split URL can smuggle a second target past a naive consumer parser
|
||||
with pytest.raises(OKFResourceError):
|
||||
validate_resource_url("https://good.example/x javascript:alert(1)")
|
||||
|
||||
|
||||
# --- T6: provenance stamping (origin + channel -> trust + disposition) --------
|
||||
# brief §5: trust follows the data's ORIGIN, not the insertion channel — a manual
|
||||
# paste of external material is still external. The channel is recorded but never
|
||||
# upgrades trust. T6 composes Trust x Disposition; it adds no new disposition.
|
||||
|
||||
def test_trust_follows_origin_not_channel():
|
||||
# the load-bearing §5 property: "channel grants no discount"
|
||||
assert trust_for(Origin.EXTERNAL, Channel.AUTOMATIC) is Trust.UNTRUSTED
|
||||
assert trust_for(Origin.EXTERNAL, Channel.MANUAL) is Trust.UNTRUSTED
|
||||
assert trust_for(Origin.INTERNAL, Channel.AUTOMATIC) is Trust.TRUSTED
|
||||
assert trust_for(Origin.INTERNAL, Channel.MANUAL) is Trust.TRUSTED
|
||||
|
||||
|
||||
def test_stamp_concept_records_origin_channel_and_untrusted_external():
|
||||
stamp = stamp_concept("tables/users", Report(), Origin.EXTERNAL, Channel.MANUAL)
|
||||
assert stamp.concept_id == "tables/users"
|
||||
assert stamp.origin is Origin.EXTERNAL
|
||||
assert stamp.channel is Channel.MANUAL
|
||||
assert stamp.trust is Trust.UNTRUSTED
|
||||
assert isinstance(stamp.disposition, Disposition)
|
||||
|
||||
|
||||
def test_stamp_concept_injection_escalates_disposition():
|
||||
report = scan_concept("---\ntype: table\n---\n" + _INJECTION + "\n")
|
||||
stamp = stamp_concept("tables/users", report, Origin.EXTERNAL, Channel.AUTOMATIC)
|
||||
assert stamp.disposition in (Disposition.QUARANTINE_REVIEW, Disposition.FAIL_SECURE)
|
||||
|
||||
|
||||
def test_format_log_entry_contains_all_fields():
|
||||
stamp = stamp_concept("tables/users", Report(), Origin.INTERNAL, Channel.AUTOMATIC)
|
||||
line = format_log_entry(stamp)
|
||||
for token in ("tables/users", "internal", "automatic", "trusted", stamp.disposition.value):
|
||||
assert token in line
|
||||
|
||||
|
||||
def test_format_log_entry_prepends_timestamp():
|
||||
stamp = stamp_concept("a/b", Report(), Origin.INTERNAL, Channel.AUTOMATIC)
|
||||
line = format_log_entry(stamp, timestamp="2026-07-06T07:00:00Z")
|
||||
assert line.startswith("2026-07-06T07:00:00Z")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue