68 lines
3.0 KiB
Python
68 lines
3.0 KiB
Python
from pathlib import Path
|
|
import hashlib,json,os,tarfile,sys,tempfile
|
|
import pytest
|
|
|
|
ROOT=Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0,str(ROOT/"active-runtime/app"))
|
|
import aios_isolated_executor as mod
|
|
|
|
def make_archive(tmp_path):
|
|
root=tmp_path/"src"; root.mkdir()
|
|
(root/"hello.py").write_text("VALUE = 1\n")
|
|
archive=tmp_path/"source.tgz"
|
|
with tarfile.open(archive,"w:gz") as tf: tf.add(root/"hello.py",arcname="hello.py")
|
|
return archive
|
|
|
|
def test_isolated_executor_applies_hash_bound_change(tmp_path,monkeypatch):
|
|
archive=make_archive(tmp_path)
|
|
monkeypatch.delenv("CREDENTIALS_DIRECTORY",raising=False)
|
|
monkeypatch.setattr(mod,"assert_uncredentialed",lambda:None)
|
|
request={
|
|
"schema":"aios.isolated-execution.request.v1",
|
|
"request_id":"task-req-0000000000000000",
|
|
"source_archive":str(archive),
|
|
"source_archive_sha256":mod.sha256(archive.read_bytes()),
|
|
"change_set":[{
|
|
"path":"hello.py","operation":"replace_text","old":"VALUE = 1","new":"VALUE = 2","count":1,
|
|
"expected_sha256":mod.sha256(b"VALUE = 1\n")
|
|
}],
|
|
"verification":[{"label":"compile","argv":["/usr/bin/python3","-m","py_compile","hello.py"],"cwd":".","timeout_seconds":30}]
|
|
}
|
|
req=tmp_path/"req.json"; res=tmp_path/"res.json"
|
|
req.write_text(json.dumps(request))
|
|
mod.execute_request(req,res)
|
|
out=json.loads(res.read_text())
|
|
assert out["request_id"]=="task-req-0000000000000000"
|
|
assert out["credential_isolation"]["credential_env_absent"] is True
|
|
changed=out["changed_files"][0]
|
|
assert changed["path"]=="hello.py" and changed["deleted"] is False
|
|
|
|
def test_isolated_executor_source_drift_denied(tmp_path,monkeypatch):
|
|
archive=make_archive(tmp_path); monkeypatch.setattr(mod,"assert_uncredentialed",lambda:None)
|
|
request={
|
|
"schema":"aios.isolated-execution.request.v1","request_id":"task-req-0000000000000001",
|
|
"source_archive":str(archive),
|
|
"source_archive_sha256":mod.sha256(archive.read_bytes()),
|
|
"change_set":[{"path":"hello.py","operation":"write_file","content":"x\n","expected_sha256":"0"*64}],
|
|
"verification":[]
|
|
}
|
|
req=tmp_path/"req.json"; res=tmp_path/"res.json"; req.write_text(json.dumps(request))
|
|
with pytest.raises(mod.IsolatedExecutorError,match="SOURCE_DRIFT"):
|
|
mod.execute_request(req,res)
|
|
|
|
def test_credential_boundary_eacces_is_accepted(monkeypatch):
|
|
class DeniedDirectory:
|
|
def exists(self): return True
|
|
def is_symlink(self): return False
|
|
def is_dir(self): return True
|
|
def iterdir(self): raise PermissionError(13,"permission denied")
|
|
monkeypatch.setattr(mod.os,"access",lambda *_args,**_kwargs:False)
|
|
assert mod._credential_exposure(DeniedDirectory()) is None
|
|
|
|
def test_systemd_unit_has_no_credentials_and_private_network():
|
|
unit=(ROOT/"deploy/systemd/aios-online-agent-isolated-executor.service").read_text()
|
|
assert "PrivateNetwork=true" in unit
|
|
assert "InaccessiblePaths=/run/credentials" in unit
|
|
assert "LoadCredential" not in unit
|
|
assert "SupplementaryGroups=gitops" not in unit
|