feat(policy): allowlist enforcement, confirm gates, backup-before-edit
This commit is contained in:
34
src/rook/policy.py
Normal file
34
src/rook/policy.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
APPROVED_DIRS: list[str] = [
|
||||||
|
os.path.abspath(os.path.expanduser('~/dev')),
|
||||||
|
os.path.abspath(os.path.expanduser('~/logs')),
|
||||||
|
os.path.abspath(os.path.expanduser('~/ModernCoSy')),
|
||||||
|
]
|
||||||
|
|
||||||
|
CAPABILITY_ALLOWLISTS: dict[str, list[str]] = {
|
||||||
|
'file': ['read', 'write', 'edit'],
|
||||||
|
'shell': ['run'],
|
||||||
|
'git': ['status', 'log', 'diff', 'add', 'commit'],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def is_approved_dir(path: str) -> bool:
|
||||||
|
resolved = os.path.abspath(path)
|
||||||
|
return any(resolved.startswith(d) for d in APPROVED_DIRS)
|
||||||
|
|
||||||
|
|
||||||
|
def confirm_spawn(action: str, details: str) -> bool:
|
||||||
|
response = input(f"Allow '{action}' with '{details}'? [y/n]: ")
|
||||||
|
return response.strip().lower() == 'y'
|
||||||
|
|
||||||
|
|
||||||
|
def backup_before_edit(file_path: str) -> str:
|
||||||
|
backup_path = file_path + '.bak'
|
||||||
|
shutil.copy2(file_path, backup_path)
|
||||||
|
return backup_path
|
||||||
|
|
||||||
|
|
||||||
|
def check_allowlist(capability: str, operation: str) -> bool:
|
||||||
|
return operation in CAPABILITY_ALLOWLISTS.get(capability, [])
|
||||||
42
tests/test_policy.py
Normal file
42
tests/test_policy.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import os
|
||||||
|
from unittest.mock import patch
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from rook.policy import is_approved_dir, confirm_spawn, backup_before_edit, check_allowlist
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_approved_dir_true():
|
||||||
|
assert is_approved_dir(os.path.expanduser('~/dev/myproject')) == True
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_approved_dir_false():
|
||||||
|
assert is_approved_dir('/tmp/evil') == False
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_approved_dir_subpath():
|
||||||
|
assert is_approved_dir(os.path.expanduser('~/logs/app.log')) == True
|
||||||
|
|
||||||
|
|
||||||
|
def test_confirm_spawn_yes():
|
||||||
|
with patch('builtins.input', return_value='y'):
|
||||||
|
assert confirm_spawn('git push', 'origin main') == True
|
||||||
|
|
||||||
|
|
||||||
|
def test_confirm_spawn_no():
|
||||||
|
with patch('builtins.input', return_value='n'):
|
||||||
|
assert confirm_spawn('rm file', 'myfile.txt') == False
|
||||||
|
|
||||||
|
|
||||||
|
def test_backup_before_edit(tmp_path):
|
||||||
|
tmp_file = tmp_path / 'sample.txt'
|
||||||
|
tmp_file.write_text('hello')
|
||||||
|
backup_path = backup_before_edit(str(tmp_file))
|
||||||
|
assert os.path.exists(backup_path)
|
||||||
|
assert open(backup_path).read() == 'hello'
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_allowlist():
|
||||||
|
assert check_allowlist('file', 'read') == True
|
||||||
|
assert check_allowlist('file', 'delete') == False
|
||||||
|
assert check_allowlist('git', 'push') == False
|
||||||
|
assert check_allowlist('git', 'commit') == True
|
||||||
Reference in New Issue
Block a user