Initial server source import
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
import contextlib
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
from unittest import mock
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
APP_DIR = ROOT / 'app'
|
||||
sys.path.insert(0, str(APP_DIR))
|
||||
|
||||
import migrate_layout
|
||||
import audit_github_tokens
|
||||
import sync_alive_github_tokens
|
||||
from runtime_security import PrivateFileLock, ensure_private_directory, harden_private_file, private_file_ready
|
||||
|
||||
|
||||
class LegacyLayoutMigrationTests(unittest.TestCase):
|
||||
def test_default_mode_holds_authority_and_proves_offline_without_mutating(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
runtime = os.path.join(temp_dir, 'runtime')
|
||||
layout = {
|
||||
'root_dir': temp_dir,
|
||||
'project_dir': os.path.join(temp_dir, 'app'),
|
||||
'runtime_dir': runtime,
|
||||
'results_dir': os.path.join(runtime, 'results'),
|
||||
'queue_dir': os.path.join(runtime, 'queues'),
|
||||
'log_dir': os.path.join(runtime, 'logs'),
|
||||
'state_dir': os.path.join(runtime, 'state'),
|
||||
'keycheck_dir': os.path.join(runtime, 'keychecks'),
|
||||
'work_dir': os.path.join(temp_dir, 'work'),
|
||||
'proxy_file': os.path.join(runtime, 'proxy.txt'),
|
||||
}
|
||||
args = SimpleNamespace(
|
||||
config='config.yaml', source_app=str(APP_DIR), target_app=None,
|
||||
in_place=True, old_root=os.path.join(temp_dir, 'legacy'),
|
||||
desktop_hf=os.path.join(temp_dir, 'desktop'), overwrite=False,
|
||||
dry_run=False, apply=False, no_app_copy=True, no_desktop_import=True,
|
||||
)
|
||||
with mock.patch.object(migrate_layout, 'parse_args', return_value=args), \
|
||||
mock.patch.object(migrate_layout, 'load_config', return_value={'global': layout}), \
|
||||
mock.patch.object(migrate_layout, 'ClusterAuthorityLock', return_value=contextlib.nullcontext()) as authority, \
|
||||
mock.patch.object(migrate_layout, 'require_runtime_hardening_stopped') as stopped:
|
||||
self.assertEqual(migrate_layout.main(), 0)
|
||||
authority.assert_called_once()
|
||||
stopped.assert_called_once()
|
||||
self.assertFalse(os.path.exists(runtime))
|
||||
|
||||
def test_overwrite_false_never_merges_or_replaces_existing_directory(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
source = os.path.join(temp_dir, 'source')
|
||||
destination = os.path.join(temp_dir, 'destination')
|
||||
os.makedirs(source)
|
||||
os.makedirs(destination)
|
||||
Path(source, 'new.txt').write_text('new', encoding='ascii')
|
||||
Path(destination, 'sentinel.txt').write_text('sentinel', encoding='ascii')
|
||||
self.assertFalse(migrate_layout.copy_dir(source, destination, overwrite=False, dry_run=False))
|
||||
self.assertEqual(Path(destination, 'sentinel.txt').read_text(encoding='ascii'), 'sentinel')
|
||||
self.assertFalse(Path(destination, 'new.txt').exists())
|
||||
|
||||
def test_directory_replacement_is_retired_even_with_apply_marker(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
source = os.path.join(temp_dir, 'source')
|
||||
destination = os.path.join(temp_dir, 'destination')
|
||||
os.makedirs(source)
|
||||
os.makedirs(destination)
|
||||
Path(destination, 'sentinel.txt').write_text('sentinel', encoding='ascii')
|
||||
with self.assertRaisesRegex(RuntimeError, 'replacement is retired'):
|
||||
migrate_layout.copy_dir(source, destination, overwrite=True, verified_apply=True)
|
||||
self.assertTrue(Path(destination, 'sentinel.txt').exists())
|
||||
|
||||
def test_production_apply_is_retired_before_authority_or_filesystem_work(self):
|
||||
args = SimpleNamespace(apply=True, dry_run=False)
|
||||
with mock.patch.object(migrate_layout, 'parse_args', return_value=args), \
|
||||
mock.patch.object(migrate_layout, 'load_config') as load_config, \
|
||||
mock.patch.object(migrate_layout, 'ClusterAuthorityLock') as authority:
|
||||
with self.assertRaisesRegex(SystemExit, 'already migrated'):
|
||||
migrate_layout.main()
|
||||
load_config.assert_not_called()
|
||||
authority.assert_not_called()
|
||||
|
||||
|
||||
class AliveTokenSyncTests(unittest.TestCase):
|
||||
@staticmethod
|
||||
def authority_kwargs(secrets_path):
|
||||
return {
|
||||
'canonical_secrets_path': secrets_path,
|
||||
'authority_lock': SimpleNamespace(acquired=True),
|
||||
'stopped_verified': True,
|
||||
}
|
||||
|
||||
def fixture(self, temp_dir, alive_status='VALID'):
|
||||
ensure_private_directory(temp_dir, reject_reparse=True)
|
||||
secrets_path = os.path.join(temp_dir, 'secrets.yaml')
|
||||
alive_path = os.path.join(temp_dir, 'githubAlive.txt')
|
||||
Path(secrets_path).write_text(
|
||||
'auth_pools:\n github_main:\n - name: gh_1\n token: ghp_existing_fixture\n',
|
||||
encoding='ascii',
|
||||
)
|
||||
Path(alive_path).write_text(
|
||||
f'ghp_new_fixture\t{alive_status}\taccepted\tfixture\n',
|
||||
encoding='ascii',
|
||||
)
|
||||
harden_private_file(secrets_path)
|
||||
harden_private_file(alive_path)
|
||||
return secrets_path, alive_path
|
||||
|
||||
def docker_fixture(self, temp_dir, existing_username='existing-user'):
|
||||
ensure_private_directory(temp_dir, reject_reparse=True)
|
||||
secrets_path = os.path.join(temp_dir, 'secrets.yaml')
|
||||
alive_path = os.path.join(temp_dir, 'dockerhubAlive.txt')
|
||||
existing_token = 'dckr_pat_' + ('e' * 27)
|
||||
new_token = 'dckr_pat_' + ('n' * 27)
|
||||
Path(secrets_path).write_text(
|
||||
'auth_pools:\n dockerhub_main:\n'
|
||||
f' - name: dockerhub_1\n username: {existing_username}\n token: {existing_token}\n',
|
||||
encoding='ascii',
|
||||
)
|
||||
Path(alive_path).write_text(
|
||||
f'new-user:{new_token}\tVALID\taccepted\tfixture\n',
|
||||
encoding='ascii',
|
||||
)
|
||||
harden_private_file(secrets_path)
|
||||
harden_private_file(alive_path)
|
||||
return secrets_path, alive_path, existing_token, new_token
|
||||
|
||||
def test_main_defaults_dry_and_requires_cluster_authority_and_stopped_proof(self):
|
||||
args = SimpleNamespace(
|
||||
config='config.yaml', secrets='secrets.yaml', alive_file='alive.txt',
|
||||
pool='github_main', name_prefix='gh', dry_run=False, apply=False,
|
||||
)
|
||||
result = {
|
||||
'alive_unique': 0, 'existing_before': 0, 'added': 0,
|
||||
'normalized_existing': 0, 'pool_after': 0,
|
||||
}
|
||||
with mock.patch.object(sync_alive_github_tokens, 'parse_args', return_value=args), \
|
||||
mock.patch.object(sync_alive_github_tokens, 'canonical_path', side_effect=lambda value: os.path.abspath(value)), \
|
||||
mock.patch.object(sync_alive_github_tokens, 'require_private_file'), \
|
||||
mock.patch.object(sync_alive_github_tokens, 'load_config', return_value={'global': {'secrets_file': os.path.abspath('secrets.yaml')}}), \
|
||||
mock.patch.object(sync_alive_github_tokens, 'ClusterAuthorityLock', return_value=contextlib.nullcontext()) as authority, \
|
||||
mock.patch.object(sync_alive_github_tokens, 'require_runtime_hardening_stopped') as stopped, \
|
||||
mock.patch.object(sync_alive_github_tokens, 'sync_tokens', return_value=result) as sync:
|
||||
self.assertEqual(sync_alive_github_tokens.main(), 0)
|
||||
authority.assert_called_once()
|
||||
stopped.assert_called_once()
|
||||
self.assertFalse(sync.call_args.kwargs['apply'])
|
||||
|
||||
def test_foreign_secrets_path_is_rejected_before_authority_or_token_read(self):
|
||||
args = SimpleNamespace(
|
||||
config='config.yaml', secrets='foreign.yaml', alive_file='alive.txt',
|
||||
pool='github_main', name_prefix='gh', dry_run=True, apply=False,
|
||||
)
|
||||
with mock.patch.object(sync_alive_github_tokens, 'parse_args', return_value=args), \
|
||||
mock.patch.object(sync_alive_github_tokens, 'canonical_path', side_effect=lambda value: os.path.abspath(value)), \
|
||||
mock.patch.object(sync_alive_github_tokens, 'require_private_file'), \
|
||||
mock.patch.object(sync_alive_github_tokens, 'load_config', return_value={'global': {'secrets_file': os.path.abspath('canonical.yaml')}}), \
|
||||
mock.patch.object(sync_alive_github_tokens, 'ClusterAuthorityLock') as authority, \
|
||||
mock.patch.object(sync_alive_github_tokens, 'sync_tokens') as sync:
|
||||
with self.assertRaisesRegex(SystemExit, 'exactly match'):
|
||||
sync_alive_github_tokens.main()
|
||||
authority.assert_not_called()
|
||||
sync.assert_not_called()
|
||||
|
||||
def test_unaccepted_alive_status_is_rejected_without_changing_secrets(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
secrets_path, alive_path = self.fixture(temp_dir, alive_status='DEAD')
|
||||
before = Path(secrets_path).read_bytes()
|
||||
with self.assertRaisesRegex(ValueError, 'unaccepted'):
|
||||
sync_alive_github_tokens.sync_tokens(
|
||||
secrets_path, alive_path, 'github_main', 'gh', apply=True,
|
||||
**self.authority_kwargs(secrets_path),
|
||||
)
|
||||
self.assertEqual(Path(secrets_path).read_bytes(), before)
|
||||
|
||||
def test_atomic_publication_failure_never_truncates_active_secrets(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
secrets_path, alive_path = self.fixture(temp_dir)
|
||||
before = Path(secrets_path).read_bytes()
|
||||
with mock.patch.object(
|
||||
sync_alive_github_tokens,
|
||||
'_atomic_write_private_yaml',
|
||||
side_effect=OSError('simulated publication failure'),
|
||||
):
|
||||
with self.assertRaisesRegex(OSError, 'publication failure'):
|
||||
sync_alive_github_tokens.sync_tokens(
|
||||
secrets_path, alive_path, 'github_main', 'gh', apply=True,
|
||||
**self.authority_kwargs(secrets_path),
|
||||
)
|
||||
self.assertEqual(Path(secrets_path).read_bytes(), before)
|
||||
|
||||
def test_sync_lock_rejects_concurrent_writer_without_changing_secrets(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
secrets_path, alive_path = self.fixture(temp_dir)
|
||||
before = Path(secrets_path).read_bytes()
|
||||
held = PrivateFileLock(secrets_path + '.sync.lock').acquire()
|
||||
try:
|
||||
with self.assertRaises(BlockingIOError):
|
||||
sync_alive_github_tokens.sync_tokens(
|
||||
secrets_path, alive_path, 'github_main', 'gh', apply=True,
|
||||
**self.authority_kwargs(secrets_path),
|
||||
)
|
||||
finally:
|
||||
held.release()
|
||||
self.assertEqual(Path(secrets_path).read_bytes(), before)
|
||||
|
||||
def test_apply_uses_private_atomic_replacement(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
secrets_path, alive_path = self.fixture(temp_dir)
|
||||
result = sync_alive_github_tokens.sync_tokens(
|
||||
secrets_path, alive_path, 'github_main', 'gh', apply=True,
|
||||
**self.authority_kwargs(secrets_path),
|
||||
)
|
||||
value = yaml.safe_load(Path(secrets_path).read_text(encoding='utf-8'))
|
||||
tokens = [entry['token'] for entry in value['auth_pools']['github_main']]
|
||||
self.assertEqual(result['added'], 1)
|
||||
self.assertIn('ghp_new_fixture', tokens)
|
||||
self.assertTrue(private_file_ready(secrets_path))
|
||||
|
||||
def test_dockerhub_sync_adds_only_complete_username_token_pairs(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
secrets_path, alive_path, _, new_token = self.docker_fixture(temp_dir)
|
||||
result = sync_alive_github_tokens.sync_tokens(
|
||||
secrets_path, alive_path, 'dockerhub_main', 'dockerhub', apply=True,
|
||||
provider='dockerhub', **self.authority_kwargs(secrets_path),
|
||||
)
|
||||
value = yaml.safe_load(Path(secrets_path).read_text(encoding='utf-8'))
|
||||
added = [entry for entry in value['auth_pools']['dockerhub_main'] if entry['token'] == new_token]
|
||||
self.assertEqual(result['added'], 1)
|
||||
self.assertEqual(added[0]['username'], 'new-user')
|
||||
self.assertTrue(private_file_ready(secrets_path))
|
||||
|
||||
def test_dockerhub_sync_skips_alive_token_without_username(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
secrets_path, alive_path, _, new_token = self.docker_fixture(temp_dir)
|
||||
Path(alive_path).write_text(
|
||||
f'{new_token}\tVALID\taccepted\tfixture\n', encoding='ascii',
|
||||
)
|
||||
harden_private_file(alive_path)
|
||||
result = sync_alive_github_tokens.sync_tokens(
|
||||
secrets_path, alive_path, 'dockerhub_main', 'dockerhub', apply=True,
|
||||
provider='dockerhub', **self.authority_kwargs(secrets_path),
|
||||
)
|
||||
self.assertEqual(result['added'], 0)
|
||||
self.assertEqual(result['skipped_missing_username'], 1)
|
||||
|
||||
def test_dockerhub_sync_fills_missing_existing_username(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
secrets_path, alive_path, existing_token, _ = self.docker_fixture(temp_dir, existing_username='')
|
||||
Path(alive_path).write_text(
|
||||
f'recovered-user:{existing_token}\tVALID\taccepted\tfixture\n', encoding='ascii',
|
||||
)
|
||||
harden_private_file(alive_path)
|
||||
result = sync_alive_github_tokens.sync_tokens(
|
||||
secrets_path, alive_path, 'dockerhub_main', 'dockerhub', apply=True,
|
||||
provider='dockerhub', **self.authority_kwargs(secrets_path),
|
||||
)
|
||||
value = yaml.safe_load(Path(secrets_path).read_text(encoding='utf-8'))
|
||||
self.assertEqual(result['username_filled'], 1)
|
||||
self.assertEqual(value['auth_pools']['dockerhub_main'][0]['username'], 'recovered-user')
|
||||
|
||||
def test_dockerhub_sync_preserves_conflicting_existing_username(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
secrets_path, alive_path, existing_token, _ = self.docker_fixture(temp_dir)
|
||||
Path(alive_path).write_text(
|
||||
f'other-user:{existing_token}\tVALID\taccepted\tfixture\n', encoding='ascii',
|
||||
)
|
||||
harden_private_file(alive_path)
|
||||
before = Path(secrets_path).read_bytes()
|
||||
result = sync_alive_github_tokens.sync_tokens(
|
||||
secrets_path, alive_path, 'dockerhub_main', 'dockerhub', apply=True,
|
||||
provider='dockerhub', **self.authority_kwargs(secrets_path),
|
||||
)
|
||||
self.assertEqual(Path(secrets_path).read_bytes(), before)
|
||||
self.assertEqual(result['username_conflicts'], 1)
|
||||
self.assertEqual(result['added'], 0)
|
||||
|
||||
def test_dockerhub_sync_explicitly_replaces_conflicting_username(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
secrets_path, alive_path, existing_token, _ = self.docker_fixture(temp_dir)
|
||||
Path(alive_path).write_text(
|
||||
f'validated-user:{existing_token}\tVALID\taccepted\tfixture\n', encoding='ascii',
|
||||
)
|
||||
harden_private_file(alive_path)
|
||||
result = sync_alive_github_tokens.sync_tokens(
|
||||
secrets_path, alive_path, 'dockerhub_main', 'dockerhub', apply=True,
|
||||
provider='dockerhub', replace_conflicting_usernames=True,
|
||||
**self.authority_kwargs(secrets_path),
|
||||
)
|
||||
value = yaml.safe_load(Path(secrets_path).read_text(encoding='utf-8'))
|
||||
self.assertEqual(result['username_replaced'], 1)
|
||||
self.assertEqual(result['username_conflicts'], 0)
|
||||
self.assertEqual(value['auth_pools']['dockerhub_main'][0]['username'], 'validated-user')
|
||||
|
||||
def test_sync_function_refuses_before_files_without_acquired_authority(self):
|
||||
with mock.patch.object(sync_alive_github_tokens.os.path, 'exists') as exists, \
|
||||
mock.patch('builtins.open') as open_file:
|
||||
with self.assertRaisesRegex(RuntimeError, 'authority lock'):
|
||||
sync_alive_github_tokens.sync_tokens(
|
||||
'secrets.yaml', 'alive.txt', 'github_main', 'gh', apply=False,
|
||||
canonical_secrets_path='secrets.yaml',
|
||||
authority_lock=SimpleNamespace(acquired=False),
|
||||
stopped_verified=True,
|
||||
)
|
||||
exists.assert_not_called()
|
||||
open_file.assert_not_called()
|
||||
|
||||
|
||||
class RetiredCredentialToolTests(unittest.TestCase):
|
||||
def test_github_audit_is_retired_before_args_files_or_network(self):
|
||||
with self.assertRaisesRegex(SystemExit, 'retired'):
|
||||
audit_github_tokens.main()
|
||||
source = (APP_DIR / 'audit_github_tokens.py').read_text(encoding='utf-8')
|
||||
self.assertNotIn('requests', source)
|
||||
self.assertNotIn('open(', source)
|
||||
|
||||
def test_openrouter_powershell_checker_has_no_credential_or_network_execution(self):
|
||||
source = (ROOT / 'runtime' / 'check-openrouter-keys.ps1').read_text(encoding='utf-8')
|
||||
self.assertIn('supervisor-managed OpenRouter keychecks', source)
|
||||
for forbidden in ('Get-Content', 'HttpClient', 'SendAsync', 'orkey.txt', 'proxy.txt'):
|
||||
self.assertNotIn(forbidden, source)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user