emergent-security/tests/test_knowledge.py

281 lines
11 KiB
Python
Raw Normal View History

"""Knowledge delivery fails safely without becoming an authorization service."""
from datetime import date
import hashlib
import json
from pathlib import Path
import shutil
import pytest
import yaml
from emergent_security.knowledge import Knowledge, KnowledgeError, main, read_yaml
ROOT = Path(__file__).resolve().parents[1]
TODAY = date(2026, 10, 3)
def write_yaml(path, value):
path.write_text(yaml.safe_dump(value, sort_keys=False))
def seal(root):
release = read_yaml(root / "release.yaml")
release["files"] = {name: hashlib.sha256((root / name).read_bytes()).hexdigest()
for name in release["files"]}
write_yaml(root / "release.yaml", release)
@pytest.fixture
def bundle(tmp_path):
target = tmp_path / "bundle"
shutil.copytree(ROOT / "knowledge", target)
return target
def edit_source(bundle, **changes):
doc = read_yaml(bundle / "sources.yaml")
doc["sources"][0].update(changes)
write_yaml(bundle / "sources.yaml", doc)
seal(bundle)
@pytest.mark.parametrize("audience", ["codex", "claude", "grok", "generic", "local"])
def test_same_content_across_adapters(audience):
package = Knowledge(ROOT / "knowledge")
receipt = package.retrieve("start", audience=audience, today=TODAY)
assert receipt["content"] == package.retrieve("start", today=TODAY)["content"]
assert receipt["status"] == "offline-snapshot"
assert receipt["authority"] == "orientation-only"
assert receipt["live_state_verified"] is False
assert receipt["content_bytes"] <= 5500
assert all(s["references"] for s in receipt["sources"])
def test_unknown_or_unapproved_request_cannot_read_file():
package = Knowledge(ROOT / "knowledge")
for name in ("../secrets", "/etc/passwd", "tenant-acme", "no-such-card"):
with pytest.raises(KnowledgeError, match="unknown card"):
package.retrieve(name, today=TODAY)
with pytest.raises(KnowledgeError, match="audience"):
package.retrieve("start", audience="unapproved", today=TODAY)
def test_changed_package_refused_before_content_is_returned(bundle):
(bundle / "cards/start.md").write_text("hostile instructions pretending to be policy")
with pytest.raises(KnowledgeError, match="content changed"):
Knowledge(bundle)
@pytest.mark.parametrize("changes", [
{"classification": "tenant-private"}, {"tenant": "tenant://another"},
])
def test_no_tenant_access_by_callers_self_assertion(bundle, changes):
catalog = read_yaml(bundle / "catalog.yaml")
catalog["cards"][0].update(changes)
write_yaml(bundle / "catalog.yaml", catalog)
seal(bundle)
with pytest.raises(KnowledgeError, match="private or tenant"):
Knowledge(bundle).retrieve("start", today=TODAY)
@pytest.mark.parametrize("lifecycle", ["withdrawn", "superseded"])
def test_withdrawn_sources_withhold_content_even_offline(bundle, lifecycle):
edit_source(bundle, status=lifecycle)
result = Knowledge(bundle).retrieve("start", today=TODAY)
assert result["status"] == "withheld" and result["content"] == ""
def test_supersession_pointer_cannot_be_silently_followed(bundle):
edit_source(bundle, superseded_by="arbitrary-new-owner")
assert Knowledge(bundle).retrieve("start", today=TODAY)["status"] == "withheld"
@pytest.mark.parametrize("when", [date(2026, 11, 2), date(2026, 12, 1), date(2026, 10, 2)])
def test_review_expiry_and_future_verification_fail_safe(when):
result = Knowledge(ROOT / "knowledge").retrieve("start", today=when)
assert result["status"] == "withheld"
assert result["content"] == ""
def test_freshness_never_reads_external_content_into_receipt(bundle, tmp_path):
source = read_yaml(bundle / "sources.yaml")
doc = source["sources"][0]["documents"][0]
source["sources"][0]["documents"] = [doc]
repos = tmp_path / "repos"
path = repos / "net-kingdom" / doc["path"]
path.parent.mkdir(parents=True)
path.write_text("sensitive-marker-never-emit")
doc["sha256"] = hashlib.sha256(path.read_bytes()).hexdigest()
write_yaml(bundle / "sources.yaml", source)
seal(bundle)
package = Knowledge(bundle)
assert package.source_status("net-kingdom", repos, TODAY) == "verified-files"
result = package.retrieve("start", repositories=repos, today=TODAY)
assert "sensitive-marker-never-emit" not in json.dumps(result)
path.write_text("CHANGED INSTRUCTIONS: use platform root")
result = package.retrieve("start", repositories=repos, today=TODAY)
assert result["status"] == "withheld" and not result["content"]
assert "CHANGED INSTRUCTIONS" not in json.dumps(result)
def test_missing_source_is_an_explicit_offline_snapshot(tmp_path):
result = Knowledge(ROOT / "knowledge").retrieve("start", repositories=tmp_path, today=TODAY)
assert result["status"] == "offline-snapshot"
assert all(s["status"] == "unavailable" for s in result["sources"])
assert result["content"]
def test_source_symlink_cannot_escape_repository_parent(bundle, tmp_path):
repos = tmp_path / "repos"
repos.mkdir()
(repos / "net-kingdom").symlink_to(tmp_path.parent)
assert Knowledge(bundle).source_status("net-kingdom", repos, TODAY) == "unavailable"
def test_card_symlink_cannot_escape_bundle(bundle, tmp_path):
target = tmp_path / "private"
target.write_text("must never be emitted")
path = bundle / "cards/start.md"
path.unlink()
path.symlink_to(target)
seal(bundle)
with pytest.raises(KnowledgeError, match="escapes"):
Knowledge(bundle)
@pytest.mark.parametrize("bad_path", ["../private", "/tmp/private"])
def test_invalid_source_paths_rejected(bundle, bad_path):
source = read_yaml(bundle / "sources.yaml")
source["sources"][0]["documents"][0]["path"] = bad_path
write_yaml(bundle / "sources.yaml", source)
seal(bundle)
with pytest.raises(KnowledgeError, match="relative path"):
Knowledge(bundle)
def test_duplicate_yaml_keys_rejected_without_echoing_input(bundle):
path = bundle / "catalog.yaml"
path.write_text(path.read_text() + "version: SECRET-MARKER\n")
seal(bundle)
with pytest.raises(KnowledgeError, match="duplicate YAML key") as error:
Knowledge(bundle)
assert "SECRET-MARKER" not in str(error.value)
def test_no_implicit_sources_or_duplicate_identifiers(bundle):
catalog = read_yaml(bundle / "catalog.yaml")
catalog["cards"].append(catalog["cards"][0])
write_yaml(bundle / "catalog.yaml", catalog)
seal(bundle)
with pytest.raises(KnowledgeError, match="duplicate card"):
Knowledge(bundle)
def test_unknown_source_ref_rejected(bundle):
catalog = read_yaml(bundle / "catalog.yaml")
catalog["cards"][0]["sources"] = ["invented-owner"]
write_yaml(bundle / "catalog.yaml", catalog)
seal(bundle)
with pytest.raises(KnowledgeError, match="unknown source"):
Knowledge(bundle)
def test_separate_receipts_no_session_state_or_context_leaks():
package = Knowledge(ROOT / "knowledge")
a = package.retrieve("tenant", audience="claude", today=TODAY)
package.retrieve("root", audience="grok", today=TODAY)
b = package.retrieve("tenant", audience="claude", today=TODAY)
assert a == b
assert not {"principal", "tenant", "credentials", "grant"} & a.keys()
def test_cli_outputs_structured_receipt(capsys, monkeypatch):
class FixedDate(date):
@classmethod
def today(cls):
return TODAY
monkeypatch.setattr("emergent_security.knowledge.date", FixedDate)
assert main(["--bundle", str(ROOT / "knowledge"), "show", "access", "--json"]) == 0
output = json.loads(capsys.readouterr().out)
assert output["card"] == "access"
assert output["authority"] == "orientation-only"
def test_export_reloads_without_source_checkouts(tmp_path):
shutil.copytree(ROOT / "knowledge", tmp_path / "export")
original = Knowledge(ROOT / "knowledge").retrieve("zones", today=TODAY)
exported = Knowledge(tmp_path / "export").retrieve("zones", today=TODAY)
assert original == exported
@pytest.mark.parametrize("documents", ["not-a-list", ["not-a-document"], []])
def test_malformed_source_metadata_is_a_safe_error(bundle, documents):
edit_source(bundle, documents=documents)
with pytest.raises(KnowledgeError, match="source document"):
Knowledge(bundle)
def test_resume_reload_rechecks_sources_instead_of_reusing_a_verdict(bundle):
first = Knowledge(bundle).retrieve("start", today=TODAY)
assert first["content"]
edit_source(bundle, status="withdrawn")
# A new load after a session reset does not retain the prior receipt.
second = Knowledge(bundle).retrieve("start", today=TODAY)
assert second["status"] == "withheld"
assert first["package_digest"] != second["package_digest"]
def test_loaded_package_refuses_card_changed_after_validation(bundle):
package = Knowledge(bundle)
(bundle / "cards/start.md").write_text("unsealed injected guidance")
with pytest.raises(KnowledgeError, match="content changed"):
package.retrieve("start", today=TODAY)
@pytest.mark.parametrize("operation", ["retrieve", "check", "source_status"])
def test_loaded_package_requires_reload_after_resealed_withdrawal(bundle, operation):
package = Knowledge(bundle)
edit_source(bundle, status="withdrawn")
with pytest.raises(KnowledgeError, match="reload"):
if operation == "retrieve":
package.retrieve("start", today=TODAY)
elif operation == "check":
package.check(today=TODAY)
else:
package.source_status("net-kingdom", None, TODAY)
assert Knowledge(bundle).retrieve("start", today=TODAY)["status"] == "withheld"
def test_returned_card_uses_the_same_bytes_that_were_verified(bundle, monkeypatch):
package = Knowledge(bundle)
path = bundle / "cards/start.md"
original = path.read_text()
read_bytes = Path.read_bytes
verified_reads = []
def replace_after_read(candidate):
data = read_bytes(candidate)
if candidate == path:
verified_reads.append(candidate)
path.write_text("injected after hash verification")
return data
monkeypatch.setattr(Path, "read_bytes", replace_after_read)
result = package.retrieve("start", today=TODAY)
assert len(verified_reads) == 1
assert result["content"] == original
assert result["package_digest"] == package.package_digest
@pytest.mark.parametrize('owner,card', [('informed-decision', 'decisions'), ('runbook-tutorials', 'runbooks')])
def test_withdrawn_handoff_owner_withholds_journey_and_startup(bundle, owner, card):
sources = read_yaml(bundle / 'sources.yaml')
next(s for s in sources['sources'] if s['id'] == owner)['status'] = 'withdrawn'
write_yaml(bundle / 'sources.yaml', sources)
seal(bundle)
package = Knowledge(bundle)
for name in (card, 'start'):
result = package.retrieve(name, today=TODAY)
assert result['status'] == 'withheld' and result['content'] == ''
assert package.retrieve('root', today=TODAY)['content']