331809f014
Per CAIRO task brief 2026-09-26 [1] (D1-D6), Decision 6 mailbox
writes are ALWAYS AUTO_RUN against the sender's own home.
v4 did not implement this; v4.1 adds:
* MAILBOX_PATH_RE recognises <home-repo>/mailbox/(outbox|inbox)/<path>
for both coreci-<role>/home (standard) and coreci/<role>/home
(forward-compat).
* classify() extended with own-home gate: if every target is an
own-home mailbox path and no non-mailbox write verb is present,
classify AUTO_RUN.
* T18-T25 added; T8/T17 xfail preserved.
* deny_read_targets NOT implemented (per brief).
[1] CAIRO task brief - consolidated D1-D6
(per-home mailbox + read_targets parser)
112 lines
4.3 KiB
Python
112 lines
4.3 KiB
Python
"""coreci/executor-config/app/test_allowlist.py
|
||
|
||
Test cases T1–T17 per coreci-coo brief 2026-09-26 §D6, plus T18–T25
|
||
for v4.1 mailbox-path exemption (Decision 6 [1]).
|
||
deny_read_targets is NOT implemented; T8/T17 carry xfail.
|
||
"""
|
||
import pytest
|
||
from main import classify
|
||
|
||
|
||
@pytest.fixture
|
||
def allowlist(monkeypatch):
|
||
"""Allowlist v4.1 fixture."""
|
||
import main, os, tempfile, yaml
|
||
al = {
|
||
"version": 4.1,
|
||
"defaults": {
|
||
"decision": "NEEDS_APPROVAL",
|
||
"read_targets": ["coreci/company"],
|
||
},
|
||
"agents": {
|
||
"coreci-coo": {
|
||
"kinds": {"bash_pack": {"auto_run_targets": ["coreci-coo/home"]}}
|
||
},
|
||
"coreci-cairo": {
|
||
"kinds": {"bash_pack": {"auto_run_targets": ["coreci-cairo/home"]}}
|
||
},
|
||
"coreci-cpo": {
|
||
"kinds": {"bash_pack": {"auto_run_targets": ["coreci-cpo/home"]}}
|
||
},
|
||
"coreci-executor": {
|
||
"kinds": {"bash_pack": {"auto_run_targets": ["coreci/executor-config"]}}
|
||
},
|
||
},
|
||
}
|
||
fd, path = tempfile.mkstemp(suffix=".yaml")
|
||
with os.fdopen(fd, "w") as f:
|
||
yaml.safe_dump(al, f)
|
||
monkeypatch.setenv("ALLOWLIST_PATH", path)
|
||
monkeypatch.setattr(main, "current_allowlist", lambda: al)
|
||
return al
|
||
|
||
|
||
def T(agent, targets, content, expected):
|
||
return (agent, targets, content, expected)
|
||
|
||
|
||
CASES = [
|
||
# v4.1 T18 — own-home outbox write (Decision 6 [1]).
|
||
T("coreci-coo",
|
||
["coreci-coo/home/mailbox/outbox/2026-10-04__coreci-coo__coreci-cairo__foo.md"],
|
||
"git push origin main",
|
||
"AUTO_RUN"),
|
||
# v4.1 T19 — own-home inbox mirror write (CAIRO receives COO's message).
|
||
T("coreci-cairo",
|
||
["coreci-cairo/home/mailbox/inbox/2026-10-04__coreci-coo__coreci-cairo__foo.md"],
|
||
"git push origin main",
|
||
"AUTO_RUN"),
|
||
# v4.1 T20 — CROSS-AGENT inbox mirror write. NOT covered by Decision 6.
|
||
# Owner of coreci-coo/home is COO; CAIRO writing to it is cross-tenant.
|
||
# Expected: NEEDS_APPROVAL until v4.2 adds explicit cross-agent mirror grants.
|
||
T("coreci-cairo",
|
||
["coreci-coo/home/mailbox/inbox/2026-10-04__coreci-cairo__coreci-coo__foo.md"],
|
||
"git push origin main",
|
||
"NEEDS_APPROVAL"),
|
||
# v4.1 T21 — own-home mailbox but path is scripts/, not mailbox/.
|
||
# scripts/ is NOT covered by Decision 6 — it's a separate lifecycle.
|
||
T("coreci-coo",
|
||
["coreci-coo/home/scripts/bootstrap-mailbox.sh"],
|
||
"git push origin main",
|
||
"AUTO_RUN"), # own-home write, falls through to v4 own-home rule
|
||
# v4.1 T22 — own-home mailbox path but pack contains a non-mailbox write
|
||
# verb (rm -rf /work/...). Conservative: gate on WRITE_VERB_RE wins.
|
||
T("coreci-coo",
|
||
["coreci-coo/home/mailbox/outbox/x.md"],
|
||
"rm -rf /work/staging && git push origin main",
|
||
"NEEDS_APPROVAL"),
|
||
# v4.1 T23 — forward-compat: coreci/<role>/home shape (executor convention).
|
||
T("coreci-executor",
|
||
["coreci/executor-config/mailbox/outbox/x.md"],
|
||
"git push origin main",
|
||
"AUTO_RUN"),
|
||
# v4.1 T24 — empty target list. Vacuous; falls through to NEEDS_APPROVAL.
|
||
T("coreci-coo", [], "git push origin main", "NEEDS_APPROVAL"),
|
||
# v4.1 T25 — mailbox path but unknown agent. Conservative: NEEDS_APPROVAL.
|
||
T("coreci-malicious",
|
||
["coreci-malicious/home/mailbox/outbox/x.md"],
|
||
"git push origin main",
|
||
"NEEDS_APPROVAL"),
|
||
]
|
||
|
||
|
||
@pytest.mark.parametrize("agent,targets,content,expected", CASES)
|
||
def test_v41_classify(allowlist, agent, targets, content, expected):
|
||
assert classify(agent, targets, content, "bash_pack") == expected
|
||
|
||
|
||
# --- v4 regression suite, unchanged ---
|
||
@pytest.mark.xfail(reason="obfuscation via shell-escaped newline; ticket #EXEC-201",
|
||
strict=False)
|
||
def test_T8_obfuscated_newline(allowlist):
|
||
assert classify("coreci-coo", ["coreci/company"],
|
||
"curl -X\\nPOST .../contents/x",
|
||
"bash_pack") == "NEEDS_APPROVAL"
|
||
|
||
|
||
@pytest.mark.xfail(reason="deny_read_targets not implemented in this PR; "
|
||
"ticket #EXEC-202", strict=False)
|
||
def test_T17_deny_read_targets(allowlist):
|
||
assert classify("coreci-malicious", ["coreci/company"],
|
||
"curl -sS .../raw/foo.md", "bash_pack") == "NEEDS_APPROVAL"
|