coulomb-social/tests/test_bubble_access_audit.py

111 lines
4.3 KiB
Python
Raw Normal View History

"""Missing access metadata must never turn into empty or approved permissions."""
import hashlib
import importlib.util
import json
from pathlib import Path
import pytest
SPEC = importlib.util.spec_from_file_location(
'bubble_access_audit', Path(__file__).resolve().parents[1] / 'scripts/audit-bubble-access.py')
audit = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(audit)
def inputs():
return {'spaces': {'1x1': {'crew_list_user': ['LOOKUP__User__9x9'],
'members_list_user': [], 'isprivate_boolean': False}},
'articles': {'2x2': {'_owner_user': '9x9', 'Created By': 'unresolved'}},
'retained_spaces': {'1x1': {}, '3x3': {}}}
def test_aliases_collapse_but_no_grants_or_empty_missing_inference():
report = audit.audit(inputs())
assert report['source_principals'] == ['9x9']
fields = report['records']['spaces'][0]['fields']
assert fields['members_list_user']['state'] == 'empty'
assert fields['guests_list_user']['state'] == 'missing'
created_by = report['records']['articles'][0]['fields']['Created By']
assert created_by['state'] == 'unresolved_reference'
assert report['inventory_only_space_ids'] == ['3x3']
assert report['summary']['known_spaces'] == 2
assert report['summary']['target_grants_proposed'] == 0
assert report['summary']['target_identity_mappings_established'] == 0
assert '9x9' not in json.dumps(report['summary'])
@pytest.mark.parametrize('raw,expected', [(None, 'null'), ('9x9', 'invalid_container'),
([], 'empty'), ([{}], 'unresolved_reference'),
(['9x9', None], 'unresolved_reference')])
def test_field_states(raw, expected):
result = audit.field_state({'members_list_user': raw}, 'members_list_user', True)
assert result['state'] == expected
assert result['raw'] == raw
def test_private_html_escapes_source_and_disables_network():
data = inputs()
data['articles']['2x2']['Created By'] = '<script>alert(1)</script>'
result = audit.review_html(audit.audit(data))
assert b'<script>' not in result
assert b'&lt;script&gt;' in result
assert b"default-src 'none'" in result
assert b"form-action 'none'" in result
def source_bundle(tmp_path):
tmp_path.chmod(0o700)
source = tmp_path / 'source'
source.mkdir(mode=0o700)
capture = source / 'source-capture'
capture.mkdir(mode=0o700)
hashes = {}
for kind, values in inputs().items():
raw = json.dumps(values).encode()
path = capture / (kind + '.json')
path.write_bytes(raw)
path.chmod(0o600)
hashes[kind] = hashlib.sha256(raw).hexdigest()
path = source / 'reconciliation.json'
path.write_text(json.dumps({'schema': 'coulomb.bubble-review-bundle.v1',
'status': 'completed_offline_preparation', 'source_sha256': hashes}))
path.chmod(0o600)
return source
def test_source_binding_private_modes_and_no_overwrite(tmp_path):
source = source_bundle(tmp_path)
before = {p: p.read_bytes() for p in source.rglob('*') if p.is_file()}
out = tmp_path / 'review'
result = audit.prepare(source, out)
assert all(p.read_bytes() == raw for p, raw in before.items())
assert not out.stat().st_mode & 0o077
for name, digest in result['output_sha256'].items():
assert hashlib.sha256((out / name).read_bytes()).hexdigest() == digest
assert not (out / name).stat().st_mode & 0o077
with pytest.raises(ValueError):
audit.prepare(source, out)
@pytest.mark.parametrize('defect', ['drift', 'public', 'symlink', 'hardlink', 'nested-output'])
def test_unsafe_custody_or_drift_refused(tmp_path, defect):
source = source_bundle(tmp_path)
path = source / 'source-capture/articles.json'
out = tmp_path / 'review'
if defect == 'drift':
path.write_text('{}')
elif defect == 'public':
path.chmod(0o644)
elif defect == 'symlink':
saved = path.with_suffix('.saved')
path.rename(saved)
path.symlink_to(saved)
elif defect == 'hardlink':
path.with_suffix('.linked').hardlink_to(path)
else:
out = source / 'review'
with pytest.raises(ValueError):
audit.prepare(source, out)
assert not out.exists()