mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-06 15:02:20 +02:00
- P2-1: reject non-process PID values (None, 0, negative integers) in pid_alive without invoking underlying process probe. - P2-2: truthfully represent production external bridge executions as uncontained, external, non-authoritative grants carrying sanitized endpoint metadata. - P2-3: reject writable_extra overlay bindings over protected system roots and their descendants while preserving legitimate scratch destinations.
545 lines
22 KiB
Python
545 lines
22 KiB
Python
"""The containment API's own invariants.
|
|
|
|
These pin the contract rather than any one mechanism, so they run identically on
|
|
a host with bubblewrap and one without: every test substitutes
|
|
``containment.MECHANISMS`` with fake mechanisms whose availability and provided
|
|
dimensions are stated in the test. No real sandbox, no real process.
|
|
|
|
The one thing these tests must prove above all others: a request that cannot be
|
|
contained does not execute. That is asserted by recording every spawn attempt
|
|
and showing the list is empty.
|
|
"""
|
|
|
|
import asyncio
|
|
|
|
import pytest
|
|
|
|
from src import containment
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _isolated_store(tmp_path, monkeypatch):
|
|
"""Keep grant records out of ./data for every test in this module."""
|
|
store = tmp_path / "containment_grants.json"
|
|
monkeypatch.setattr(containment, "_store_path", lambda: store)
|
|
return store
|
|
|
|
|
|
@pytest.fixture
|
|
def workspace(tmp_path):
|
|
path = tmp_path / "ws"
|
|
path.mkdir()
|
|
return str(path)
|
|
|
|
|
|
@pytest.fixture
|
|
def no_spawn(monkeypatch):
|
|
"""Record spawn attempts and refuse them, so "did not execute" is provable."""
|
|
attempts = []
|
|
|
|
async def _refuse(*args, **kwargs):
|
|
attempts.append(args)
|
|
raise AssertionError("containment spawned a process it should not have")
|
|
|
|
monkeypatch.setattr(asyncio, "create_subprocess_exec", _refuse)
|
|
return attempts
|
|
|
|
|
|
def mechanism(name, rank, provides, *, available=True):
|
|
return containment.Mechanism(
|
|
name=name,
|
|
rank=rank,
|
|
available=lambda: available,
|
|
provides=lambda spec, _provides=frozenset(provides): _provides,
|
|
)
|
|
|
|
|
|
def install(monkeypatch, *mechanisms):
|
|
monkeypatch.setattr(containment, "MECHANISMS", tuple(mechanisms))
|
|
|
|
|
|
def enforcing(monkeypatch):
|
|
monkeypatch.setattr(containment, "CONTAINMENT_MODE", containment.MODE_ENFORCING)
|
|
|
|
|
|
def report_only(monkeypatch):
|
|
monkeypatch.setattr(containment, "CONTAINMENT_MODE", containment.MODE_REPORT_ONLY)
|
|
|
|
|
|
def spec_for(workspace, **kwargs):
|
|
kwargs.setdefault("env", {"PATH": "/usr/bin"})
|
|
kwargs.setdefault("wall_clock_s", 5)
|
|
return containment.ContainmentSpec(workspace=workspace, **kwargs)
|
|
|
|
|
|
ALL = tuple(sorted(containment.DIMENSIONS))
|
|
|
|
|
|
# ── The postcondition ───────────────────────────────────────────────────────
|
|
@pytest.mark.parametrize("provided", [
|
|
frozenset(containment.DEFAULT_REQUIRED),
|
|
containment.DIMENSIONS,
|
|
])
|
|
def test_required_is_always_a_subset_of_enforced(monkeypatch, workspace, provided):
|
|
"""spec.required <= grant.enforced, for any mechanism that can satisfy it."""
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("fake", 10, provided))
|
|
grant = containment.acquire(spec_for(workspace), owner="session-1")
|
|
assert grant.spec.required <= grant.enforced
|
|
assert grant.contained is True
|
|
assert grant.unenforced_required == ()
|
|
|
|
|
|
def test_enforced_never_exceeds_what_the_spec_requested(monkeypatch, workspace):
|
|
"""A grant is never a superset of its spec: unrequested dimensions are not claimed."""
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("generous", 10, containment.DIMENSIONS))
|
|
grant = containment.acquire(spec_for(workspace), owner="session-1")
|
|
# network/memory/process_count were not asked for, so they are not enforced
|
|
# even though the mechanism offers them.
|
|
assert grant.enforced == frozenset(containment.DEFAULT_REQUIRED)
|
|
assert containment.NETWORK not in grant.enforced
|
|
assert grant.degraded == ()
|
|
|
|
|
|
def test_enforced_is_always_within_the_known_dimension_set(monkeypatch, workspace):
|
|
"""A mechanism cannot invent a dimension the API does not define."""
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism(
|
|
"liar", 10, frozenset(containment.DEFAULT_REQUIRED) | {"telepathy"},
|
|
))
|
|
grant = containment.acquire(spec_for(workspace), owner="session-1")
|
|
assert grant.enforced <= containment.DIMENSIONS
|
|
|
|
|
|
# ── Deterministic failure: the request does not execute ─────────────────────
|
|
async def test_uncontainable_request_does_not_execute(monkeypatch, workspace, no_spawn):
|
|
"""The headline contract: no mechanism for a required dimension → no process.
|
|
|
|
Written as a call site would use the API — acquire, then run — so the proof
|
|
covers the whole path and not just the raising function.
|
|
"""
|
|
enforcing(monkeypatch)
|
|
# The only mechanism available cannot do filesystem, which is required.
|
|
install(monkeypatch, mechanism(
|
|
"group_only", 10, {containment.PROCESS_TREE, containment.WALL_CLOCK},
|
|
))
|
|
|
|
spec = containment.agent_spec(workspace, {"PATH": "/usr/bin"}, 5)
|
|
try:
|
|
grant = containment.acquire(spec, owner="session-1")
|
|
except containment.ContainmentUnavailable as exc:
|
|
result = containment.unavailable_tool_result(exc, tool="bash")
|
|
else: # pragma: no cover - the point of the test is that this is unreachable
|
|
result = await containment.run(grant, "echo hello")
|
|
|
|
assert no_spawn == [], "an uncontainable request reached a spawn"
|
|
assert result["exit_code"] == 1
|
|
assert "command not executed" in result["error"]
|
|
assert "filesystem" in result["error"]
|
|
assert result["containment"]["contained"] is False
|
|
assert result["containment"]["executed"] is False
|
|
assert result["containment"]["unenforced_required"] == ["filesystem"]
|
|
# A run that could not be contained is distinguishable from a contained run
|
|
# that failed: there is no output key at all, and `executed` is explicit.
|
|
assert "output" not in result
|
|
|
|
|
|
def test_unavailable_names_every_missing_dimension_and_the_mechanism_tried(
|
|
monkeypatch, workspace,
|
|
):
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("group_only", 10, {containment.WALL_CLOCK}))
|
|
spec = containment.agent_spec(workspace, {}, 5)
|
|
with pytest.raises(containment.ContainmentUnavailable) as caught:
|
|
containment.acquire(spec, owner="session-1")
|
|
assert caught.value.missing == frozenset({
|
|
containment.FILESYSTEM, containment.PROCESS_TREE,
|
|
})
|
|
assert caught.value.mechanism_tried == "group_only"
|
|
assert "containment unavailable" in str(caught.value)
|
|
|
|
|
|
def test_no_mechanism_at_all_still_refuses_rather_than_running(
|
|
monkeypatch, workspace, no_spawn,
|
|
):
|
|
"""With nothing available there is no weaker thing to fall back to."""
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("absent", 10, containment.DIMENSIONS, available=False))
|
|
with pytest.raises(containment.ContainmentUnavailable) as caught:
|
|
containment.acquire(containment.agent_spec(workspace, {}, 5), owner="s")
|
|
assert caught.value.mechanism_tried == "none"
|
|
assert no_spawn == []
|
|
|
|
|
|
async def test_a_forged_grant_cannot_buy_a_spawn(monkeypatch, workspace, no_spawn):
|
|
"""run() re-checks the postcondition at the point of effect.
|
|
|
|
A grant is a plain record, so a caller could construct one claiming
|
|
dimensions it does not have. run() refuses it rather than trusting the
|
|
record it was handed.
|
|
"""
|
|
enforcing(monkeypatch)
|
|
spec = containment.agent_spec(workspace, {}, 5)
|
|
forged = containment.ContainmentGrant(
|
|
id="forged",
|
|
mechanism="bubblewrap",
|
|
workspace=workspace,
|
|
enforced=frozenset({containment.WALL_CLOCK}),
|
|
degraded=(),
|
|
unenforced_required=(), # the lie: claims nothing is missing
|
|
owner="session-1",
|
|
mode=containment.MODE_ENFORCING,
|
|
spec=spec,
|
|
)
|
|
with pytest.raises(containment.ContainmentUnavailable):
|
|
await containment.run(forged, "echo hello")
|
|
assert no_spawn == []
|
|
|
|
|
|
# ── Best-effort dimensions degrade, they do not refuse ──────────────────────
|
|
def test_unavailable_best_effort_dimension_is_reported_not_refused(monkeypatch, workspace):
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("fake", 10, containment.DEFAULT_REQUIRED))
|
|
spec = containment.agent_spec(
|
|
workspace, {}, 5, network=containment.NETWORK_NONE, max_memory_bytes=1 << 30,
|
|
)
|
|
grant = containment.acquire(spec, owner="session-1")
|
|
assert grant.contained is True
|
|
assert grant.degraded == (containment.MEMORY, containment.NETWORK)
|
|
# And it is reported as fact in the model-visible block, never as permission.
|
|
block = grant.to_dict()
|
|
assert block["degraded"] == ["memory", "network"]
|
|
assert block["contained"] is True
|
|
assert "env" not in block
|
|
|
|
|
|
def test_a_degraded_dimension_is_never_also_enforced(monkeypatch, workspace):
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("fake", 10, containment.DEFAULT_REQUIRED))
|
|
spec = containment.agent_spec(workspace, {}, 5, max_processes=16)
|
|
grant = containment.acquire(spec, owner="session-1")
|
|
assert set(grant.degraded).isdisjoint(grant.enforced)
|
|
|
|
|
|
# ── Mechanism selection: strongest first, command-independent ───────────────
|
|
def test_selection_is_strongest_first(monkeypatch, workspace):
|
|
enforcing(monkeypatch)
|
|
install(
|
|
monkeypatch,
|
|
mechanism("weak", 10, containment.DEFAULT_REQUIRED),
|
|
mechanism("strong", 30, containment.DIMENSIONS),
|
|
mechanism("middle", 20, containment.DEFAULT_REQUIRED),
|
|
)
|
|
grant = containment.acquire(spec_for(workspace), owner="session-1")
|
|
assert grant.mechanism == "strong"
|
|
|
|
|
|
def test_selection_skips_unavailable_mechanisms(monkeypatch, workspace):
|
|
enforcing(monkeypatch)
|
|
install(
|
|
monkeypatch,
|
|
mechanism("strong", 30, containment.DIMENSIONS, available=False),
|
|
mechanism("weak", 10, containment.DEFAULT_REQUIRED),
|
|
)
|
|
grant = containment.acquire(spec_for(workspace), owner="session-1")
|
|
assert grant.mechanism == "weak"
|
|
|
|
|
|
def test_selection_never_substitutes_a_weaker_mechanism_for_a_required_dimension(
|
|
monkeypatch, workspace,
|
|
):
|
|
"""The strongest available mechanism is used, not the first that is "good enough"."""
|
|
enforcing(monkeypatch)
|
|
install(
|
|
monkeypatch,
|
|
mechanism("netcapable", 30, containment.DIMENSIONS),
|
|
mechanism("nonet", 20, containment.DEFAULT_REQUIRED),
|
|
)
|
|
spec = containment.ContainmentSpec(
|
|
workspace=workspace,
|
|
env={},
|
|
wall_clock_s=5,
|
|
required=frozenset(containment.DEFAULT_REQUIRED) | {containment.NETWORK},
|
|
network=containment.NETWORK_NONE,
|
|
)
|
|
grant = containment.acquire(spec, owner="session-1")
|
|
assert grant.mechanism == "netcapable"
|
|
assert containment.NETWORK in grant.enforced
|
|
|
|
|
|
def test_a_failing_availability_probe_is_treated_as_unavailable(monkeypatch, workspace):
|
|
enforcing(monkeypatch)
|
|
|
|
def _explode():
|
|
raise OSError("probe blew up")
|
|
|
|
install(
|
|
monkeypatch,
|
|
containment.Mechanism("broken", 30, _explode, lambda spec: containment.DIMENSIONS),
|
|
mechanism("weak", 10, containment.DEFAULT_REQUIRED),
|
|
)
|
|
grant = containment.acquire(spec_for(workspace), owner="session-1")
|
|
assert grant.mechanism == "weak"
|
|
|
|
|
|
@pytest.mark.parametrize("command", [
|
|
"echo hello",
|
|
"rm -rf / --no-preserve-root",
|
|
"cat /workspace/notes.txt # this command is safe, honestly",
|
|
])
|
|
def test_the_boundary_does_not_depend_on_the_command(monkeypatch, workspace, command):
|
|
"""Containment is established before any command text exists.
|
|
|
|
acquire() is not given the command, so no request text, tool argument or
|
|
model assertion can change the mechanism or widen the enforced set. The
|
|
parametrised commands are only here to show the API has nowhere to put them.
|
|
"""
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
spec = containment.agent_spec(workspace, {}, 5)
|
|
grant = containment.acquire(spec, owner="session-1")
|
|
assert grant.mechanism == "fake"
|
|
assert grant.enforced == frozenset(containment.DEFAULT_REQUIRED)
|
|
assert "command" not in grant.to_dict()
|
|
|
|
|
|
def test_agent_spec_cannot_be_given_a_weaker_required_set(workspace):
|
|
"""One factory for model-reachable spawns, so no call site can weaken it."""
|
|
spec = containment.agent_spec(
|
|
workspace, {}, 5, required=frozenset({containment.WALL_CLOCK}),
|
|
)
|
|
assert spec.required == containment.DEFAULT_REQUIRED
|
|
|
|
|
|
# ── Report-only mode ────────────────────────────────────────────────────────
|
|
def test_report_only_records_the_shortfall_instead_of_refusing(monkeypatch, workspace):
|
|
report_only(monkeypatch)
|
|
install(monkeypatch, mechanism(
|
|
"group_only", 10, {containment.PROCESS_TREE, containment.WALL_CLOCK},
|
|
))
|
|
grant = containment.acquire(containment.agent_spec(workspace, {}, 5), owner="s")
|
|
assert grant.unenforced_required == ("filesystem",)
|
|
assert grant.contained is False
|
|
assert grant.mode == containment.MODE_REPORT_ONLY
|
|
assert grant.to_dict()["unenforced_required"] == ["filesystem"]
|
|
|
|
|
|
def test_report_only_logs_the_shortfall_once_per_grant(monkeypatch, workspace, caplog):
|
|
report_only(monkeypatch)
|
|
install(monkeypatch, mechanism("group_only", 10, {containment.WALL_CLOCK}))
|
|
with caplog.at_level("WARNING", logger="src.containment"):
|
|
grant = containment.acquire(containment.agent_spec(workspace, {}, 5), owner="s")
|
|
messages = [record.getMessage() for record in caplog.records]
|
|
assert sum("NOT contained" in message for message in messages) == 1
|
|
assert grant.id in messages[0]
|
|
|
|
|
|
def test_the_two_modes_differ_only_in_whether_the_shortfall_refuses(monkeypatch, workspace):
|
|
install(monkeypatch, mechanism("group_only", 10, {containment.WALL_CLOCK}))
|
|
spec = containment.agent_spec(workspace, {}, 5)
|
|
|
|
report_only(monkeypatch)
|
|
reported = containment.acquire(spec, owner="s")
|
|
|
|
enforcing(monkeypatch)
|
|
with pytest.raises(containment.ContainmentUnavailable) as caught:
|
|
containment.acquire(spec, owner="s")
|
|
|
|
assert frozenset(reported.unenforced_required) == caught.value.missing
|
|
|
|
|
|
def test_enforcement_is_the_shipped_default():
|
|
assert containment.CONTAINMENT_MODE == containment.MODE_ENFORCING
|
|
|
|
|
|
# ── Spec validation: caller bugs raise in both modes ────────────────────────
|
|
@pytest.mark.parametrize("mode", [containment.MODE_ENFORCING, containment.MODE_REPORT_ONLY])
|
|
@pytest.mark.parametrize("overrides, fragment", [
|
|
({"required": frozenset({"telepathy"})}, "unknown required dimension"),
|
|
({"required": frozenset({containment.NETWORK})}, "does not request it"),
|
|
({"required": frozenset({containment.MEMORY})}, "does not request it"),
|
|
({"wall_clock_s": 0}, "must be positive"),
|
|
({"wall_clock_s": -1}, "must be positive"),
|
|
({"max_output_bytes": 0}, "max_output_bytes must be positive"),
|
|
({"max_memory_bytes": 0}, "max_memory_bytes must be a positive int"),
|
|
({"max_processes": -4}, "max_processes must be a positive int"),
|
|
({"network": "maybe"}, "network must be"),
|
|
({"env": {"A": 1}}, "env keys and values must be str"),
|
|
({"env": {"A": "x\x00y"}}, "must not contain NUL"),
|
|
({"writable_extra": ("relative/path",)}, "must be absolute"),
|
|
({"writable_extra": ("/",)}, "reserved path"),
|
|
({"readonly_extra": ("/workspace",)}, "reserved path"),
|
|
])
|
|
def test_a_malformed_spec_raises_in_both_modes(
|
|
monkeypatch, workspace, mode, overrides, fragment,
|
|
):
|
|
monkeypatch.setattr(containment, "CONTAINMENT_MODE", mode)
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
spec = spec_for(workspace, **overrides)
|
|
with pytest.raises(ValueError, match=fragment):
|
|
containment.acquire(spec, owner="session-1")
|
|
|
|
|
|
@pytest.mark.parametrize("bad_workspace, fragment", [
|
|
("", "non-empty path"),
|
|
("relative/ws", "must be absolute"),
|
|
])
|
|
def test_a_malformed_workspace_raises(monkeypatch, bad_workspace, fragment):
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
spec = containment.ContainmentSpec(
|
|
workspace=bad_workspace, env={}, wall_clock_s=5,
|
|
)
|
|
with pytest.raises(ValueError, match=fragment):
|
|
containment.acquire(spec, owner="session-1")
|
|
|
|
|
|
def test_a_workspace_that_is_not_a_directory_raises(monkeypatch, tmp_path):
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
missing = tmp_path / "nope"
|
|
spec = containment.ContainmentSpec(workspace=str(missing), env={}, wall_clock_s=5)
|
|
with pytest.raises(ValueError, match="not a directory"):
|
|
containment.acquire(spec, owner="session-1")
|
|
|
|
|
|
def test_a_grant_without_an_owner_raises(monkeypatch, workspace):
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
with pytest.raises(ValueError, match="needs an owner"):
|
|
containment.acquire(spec_for(workspace), owner=" ")
|
|
|
|
|
|
def test_the_child_environment_cannot_be_edited_after_acquire(monkeypatch, workspace):
|
|
"""env is part of the boundary, so the caller's dict is copied and frozen."""
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
caller_env = {"PATH": "/usr/bin"}
|
|
grant = containment.acquire(
|
|
spec_for(workspace, env=caller_env), owner="session-1",
|
|
)
|
|
caller_env["LD_PRELOAD"] = "/tmp/evil.so"
|
|
assert dict(grant.spec.env) == {"PATH": "/usr/bin"}
|
|
with pytest.raises(TypeError):
|
|
grant.spec.env["LD_PRELOAD"] = "/tmp/evil.so"
|
|
|
|
|
|
# ── Durable records: one owner, one record ─────────────────────────────────
|
|
def test_a_grant_is_recorded_with_its_owner_and_declared_limits(
|
|
monkeypatch, workspace, _isolated_store,
|
|
):
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
spec = containment.agent_spec(workspace, {}, 7, max_processes=8)
|
|
grant = containment.acquire(spec, owner="session-42")
|
|
|
|
active = containment.active_grants()
|
|
assert [record["id"] for record in active] == [grant.id]
|
|
record = active[0]
|
|
assert record["owner"] == "session-42"
|
|
assert record["wall_clock_s"] == 7
|
|
assert record["max_processes"] == 8
|
|
assert record["required"] == sorted(containment.DEFAULT_REQUIRED)
|
|
assert record["pid"] is None
|
|
|
|
|
|
def test_releasing_a_grant_with_no_process_clears_it_from_active(monkeypatch, workspace):
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
grant = containment.acquire(containment.agent_spec(workspace, {}, 5), owner="s")
|
|
outcome = containment.release(grant)
|
|
assert outcome.dead is True
|
|
assert outcome.escalated is False
|
|
assert outcome.survivors == ()
|
|
assert containment.active_grants() == []
|
|
|
|
|
|
def test_forget_drops_a_record(monkeypatch, workspace):
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
grant = containment.acquire(containment.agent_spec(workspace, {}, 5), owner="s")
|
|
containment.forget(grant.id)
|
|
assert containment.active_grants() == []
|
|
|
|
|
|
def test_an_unwritable_store_does_not_take_out_execution(monkeypatch, workspace, caplog):
|
|
"""The record is observability, not a containment dimension.
|
|
|
|
Refusing an authorized command because a journal file could not be written
|
|
would be a worse failure than running it, so this degrades loudly and the
|
|
grant still describes the boundary accurately.
|
|
"""
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
|
|
def _explode(*args, **kwargs):
|
|
raise OSError("read-only filesystem")
|
|
|
|
monkeypatch.setattr(containment, "atomic_write_json", _explode)
|
|
with caplog.at_level("WARNING", logger="src.containment"):
|
|
grant = containment.acquire(containment.agent_spec(workspace, {}, 5), owner="s")
|
|
assert grant.contained is True
|
|
assert any("could not persist" in record.getMessage() for record in caplog.records)
|
|
|
|
|
|
def test_a_corrupt_store_does_not_take_out_execution(monkeypatch, workspace, _isolated_store):
|
|
enforcing(monkeypatch)
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
_isolated_store.write_text("{ this is not json", encoding="utf-8")
|
|
grant = containment.acquire(containment.agent_spec(workspace, {}, 5), owner="s")
|
|
assert [record["id"] for record in containment.active_grants()] == [grant.id]
|
|
|
|
|
|
# ── Execution that leaves the box is declared, not pretended ───────────────
|
|
async def test_an_external_bridge_grant_claims_nothing_and_cannot_be_run_locally(
|
|
monkeypatch, workspace, no_spawn,
|
|
):
|
|
enforcing(monkeypatch)
|
|
spec = containment.agent_spec(workspace, {}, 5)
|
|
grant = containment.declare_external_bridge(
|
|
spec, owner="session-1", endpoint="http://127.0.0.1:8777/exec",
|
|
)
|
|
assert grant.mechanism == "external_bridge"
|
|
assert grant.enforced == frozenset()
|
|
assert grant.external is True
|
|
assert grant.contained is False
|
|
assert grant.to_dict()["external"] is True
|
|
with pytest.raises(ValueError, match="does not own"):
|
|
await containment.run(grant, "echo hello")
|
|
assert no_spawn == []
|
|
|
|
|
|
@pytest.mark.parametrize("protected_dest", [
|
|
"/etc",
|
|
"/etc/ssl",
|
|
"/usr",
|
|
"/usr/local",
|
|
"/bin",
|
|
"/bin/sh",
|
|
"/sbin",
|
|
"/lib",
|
|
"/lib64",
|
|
"/proc",
|
|
"/proc/sys",
|
|
"/dev",
|
|
"/dev/shm",
|
|
"/sys",
|
|
"/root",
|
|
"/root/.ssh",
|
|
"/home",
|
|
"/workspace",
|
|
"/workspace/sub",
|
|
])
|
|
def test_writable_extra_rejects_protected_system_roots_and_descendants(monkeypatch, workspace, protected_dest):
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
spec = spec_for(workspace, writable_extra=(protected_dest,))
|
|
with pytest.raises(ValueError, match="reserved path"):
|
|
containment.acquire(spec, owner="session-1")
|
|
|
|
|
|
def test_writable_extra_accepts_legitimate_scratch_destinations(monkeypatch, workspace):
|
|
install(monkeypatch, mechanism("fake", 10, containment.DIMENSIONS))
|
|
spec = spec_for(workspace, writable_extra=("/var/scratch", "/tmp/custom_scratch", "/home/testuser/scratch"))
|
|
grant = containment.acquire(spec, owner="session-1")
|
|
assert "/var/scratch" in grant.spec.writable_extra
|
|
assert "/tmp/custom_scratch" in grant.spec.writable_extra
|
|
assert "/home/testuser/scratch" in grant.spec.writable_extra
|