Initial server source import
This commit is contained in:
@@ -0,0 +1,316 @@
|
||||
import copy
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
APP_DIR = Path(__file__).resolve().parents[1] / 'app'
|
||||
sys.path.insert(0, str(APP_DIR))
|
||||
|
||||
import child_bootstrap
|
||||
import lifecycle_authority as authority
|
||||
import scanner
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def manifested_git(tmp_path, monkeypatch):
|
||||
app_dir = tmp_path / 'app'
|
||||
app_dir.mkdir()
|
||||
for name in (*authority.CODE_AUTHORITY_FILES, *authority.EXTERNAL_CODE_AUTHORITY_FILES):
|
||||
path = app_dir / name
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text('# fixture\n', encoding='ascii')
|
||||
trufflehog = tmp_path / 'trufflehog.exe'
|
||||
trufflehog.write_bytes(b'trufflehog fixture')
|
||||
git = tmp_path / 'git.exe'
|
||||
git.write_bytes(b'git fixture')
|
||||
manifest = authority.build_code_manifest(
|
||||
app_dir=app_dir, trufflehog_path=trufflehog, git_path=git,
|
||||
)
|
||||
monkeypatch.delenv(authority.CHILD_KIND_ENV, raising=False)
|
||||
return manifest, git, app_dir, trufflehog
|
||||
|
||||
|
||||
def clone_command(manifest, tmp_path):
|
||||
return [
|
||||
manifest['executables']['git']['path'],
|
||||
'clone', '--no-checkout', '--no-recurse-submodules', '--',
|
||||
'https://example.invalid/owner/repository.git', str(tmp_path / 'clone'),
|
||||
]
|
||||
|
||||
|
||||
def test_manifest_requires_both_content_identities(manifested_git):
|
||||
manifest, git, _, _ = manifested_git
|
||||
assert set(manifest['executables']) == {'trufflehog', 'git'}
|
||||
assert manifest['executables']['git'] == {
|
||||
'path': authority.canonical_path(git), 'sha256': authority.sha256_file(git),
|
||||
}
|
||||
assert authority.verify_code_manifest(manifest) == manifest
|
||||
digest = authority.code_manifest_sha256(manifest)
|
||||
assert child_bootstrap._verify_manifest(
|
||||
{'code_manifest': manifest, 'code_manifest_sha256': digest},
|
||||
{'code_manifest_sha256': digest},
|
||||
) == manifest['root']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('change', ['missing', 'extra', 'entry', 'relative', 'empty', 'digest'])
|
||||
def test_manifest_rejects_invalid_git_identity(manifested_git, change):
|
||||
manifest = copy.deepcopy(manifested_git[0])
|
||||
if change == 'missing':
|
||||
del manifest['executables']['git']
|
||||
elif change == 'extra':
|
||||
manifest['executables']['shell'] = manifest['executables']['git'].copy()
|
||||
elif change == 'entry':
|
||||
manifest['executables']['git'] = None
|
||||
else:
|
||||
field, value = {
|
||||
'relative': ('path', 'git.exe'),
|
||||
'empty': ('path', ''),
|
||||
'digest': ('sha256', 'z' * 64),
|
||||
}[change]
|
||||
manifest['executables']['git'][field] = value
|
||||
with pytest.raises(authority.LifecycleAuthorityError):
|
||||
authority.normalize_code_manifest(manifest)
|
||||
|
||||
|
||||
def test_git_drift_rejected_even_with_preserved_size_and_mtime(manifested_git):
|
||||
manifest, git, _, _ = manifested_git
|
||||
before = git.stat()
|
||||
git.write_bytes(b'GIT fixture')
|
||||
os.utime(git, ns=(before.st_atime_ns, before.st_mtime_ns))
|
||||
assert git.stat().st_size == before.st_size
|
||||
assert git.stat().st_mtime_ns == before.st_mtime_ns
|
||||
with pytest.raises(authority.LifecycleAuthorityError, match='executable:git'):
|
||||
authority.verify_code_manifest(manifest)
|
||||
|
||||
|
||||
def test_git_requires_exact_private_acl(manifested_git, monkeypatch):
|
||||
manifest, git, _, _ = manifested_git
|
||||
monkeypatch.setattr(authority, 'private_file_ready', lambda path: path != authority.canonical_path(git))
|
||||
with pytest.raises(authority.LifecycleAuthorityError, match='executable:git'):
|
||||
authority.verify_code_manifest(manifest, require_private_acl=True)
|
||||
|
||||
|
||||
def test_git_included_in_existing_only_preflight(manifested_git):
|
||||
_, git, app_dir, trufflehog = manifested_git
|
||||
paths = authority.manifest_authority_paths(
|
||||
app_dir, trufflehog, existing_only=True, git_path=git,
|
||||
)
|
||||
assert authority.canonical_path(git) in paths
|
||||
|
||||
|
||||
@pytest.mark.parametrize('existing_only', [False, True])
|
||||
def test_missing_git_fails_closed_in_preflight(manifested_git, existing_only):
|
||||
_, git, app_dir, trufflehog = manifested_git
|
||||
git.unlink()
|
||||
with pytest.raises(authority.LifecycleAuthorityError, match='Git executable'):
|
||||
authority.manifest_authority_paths(
|
||||
app_dir, trufflehog, existing_only=existing_only, git_path=git,
|
||||
)
|
||||
with pytest.raises(authority.LifecycleAuthorityError, match='Git executable'):
|
||||
authority.build_code_manifest(app_dir, trufflehog, git_path=git)
|
||||
|
||||
|
||||
def test_project_private_git_preferred_for_capture_and_preflight(manifested_git):
|
||||
_, _, app_dir, trufflehog = manifested_git
|
||||
private_git = app_dir.parent / 'runtime' / 'git' / 'cmd' / 'git.exe'
|
||||
private_git.parent.mkdir(parents=True)
|
||||
private_git.write_bytes(b'private Git fixture')
|
||||
with mock.patch.object(authority.shutil, 'which', side_effect=AssertionError('unexpected PATH lookup')):
|
||||
manifest = authority.build_code_manifest(app_dir, trufflehog)
|
||||
paths = authority.manifest_authority_paths(app_dir, trufflehog, existing_only=True)
|
||||
resolved = authority.resolve_manifest_executable(None, name='git', app_dir=app_dir)
|
||||
assert resolved == authority.canonical_path(private_git)
|
||||
assert manifest['executables']['git']['path'] == resolved
|
||||
assert manifest['executables']['git']['sha256'] == authority.sha256_file(private_git)
|
||||
assert resolved in paths
|
||||
|
||||
|
||||
def test_explicit_git_override_precedes_private_copy(manifested_git):
|
||||
_, git, app_dir, trufflehog = manifested_git
|
||||
private_git = app_dir.parent / 'runtime' / 'git' / 'cmd' / 'git.exe'
|
||||
private_git.parent.mkdir(parents=True)
|
||||
private_git.write_bytes(b'private Git fixture')
|
||||
with mock.patch.object(authority.shutil, 'which', side_effect=AssertionError('unexpected PATH lookup')):
|
||||
manifest = authority.build_code_manifest(app_dir, trufflehog, git_path=git)
|
||||
paths = authority.manifest_authority_paths(app_dir, trufflehog, git_path=git, existing_only=True)
|
||||
assert manifest['executables']['git']['path'] == authority.canonical_path(git)
|
||||
assert authority.canonical_path(git) in paths
|
||||
assert authority.canonical_path(private_git) not in paths
|
||||
with pytest.raises(authority.LifecycleAuthorityError, match='Git executable'):
|
||||
authority.build_code_manifest(app_dir, trufflehog, git_path=app_dir.parent / 'missing.exe')
|
||||
|
||||
|
||||
def test_invalid_private_git_does_not_fall_back_to_path(manifested_git):
|
||||
_, _, app_dir, trufflehog = manifested_git
|
||||
private_git = app_dir.parent / 'runtime' / 'git' / 'cmd' / 'git.exe'
|
||||
private_git.mkdir(parents=True)
|
||||
with mock.patch.object(authority.shutil, 'which', side_effect=AssertionError('unexpected PATH lookup')):
|
||||
with pytest.raises(authority.LifecycleAuthorityError, match='Git executable is not a regular file'):
|
||||
authority.build_code_manifest(app_dir, trufflehog)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('private_parent_exists', [False, True])
|
||||
def test_default_git_is_resolved_only_at_manifest_capture(manifested_git, monkeypatch, private_parent_exists):
|
||||
_, git, app_dir, trufflehog = manifested_git
|
||||
if private_parent_exists:
|
||||
(app_dir.parent / 'runtime' / 'git' / 'cmd').mkdir(parents=True)
|
||||
with mock.patch.object(authority.shutil, 'which', return_value=str(git)) as which:
|
||||
manifest = authority.build_code_manifest(app_dir, trufflehog)
|
||||
paths = authority.manifest_authority_paths(app_dir, trufflehog, existing_only=True)
|
||||
assert which.call_args_list == [mock.call('git'), mock.call('git')]
|
||||
assert authority.canonical_path(git) in paths
|
||||
monkeypatch.setattr(scanner, '_runtime_initialized', True)
|
||||
metadata = {'code_manifest': manifest}
|
||||
with mock.patch.object(scanner, 'require_active_supervisor_child', return_value=metadata) as authenticate, \
|
||||
mock.patch.object(scanner.shutil, 'which', side_effect=AssertionError('launch PATH lookup')):
|
||||
assert scanner.get_git_cmd() == authority.canonical_path(git)
|
||||
authenticate.assert_called_once_with(child_kind='scanner', require_dsn=True)
|
||||
|
||||
|
||||
def test_uninitialized_getter_is_not_launch_authority(monkeypatch, manifested_git, tmp_path):
|
||||
monkeypatch.setattr(scanner, '_runtime_initialized', False)
|
||||
with mock.patch.object(scanner.shutil, 'which', return_value=None):
|
||||
assert scanner.get_git_cmd() == 'git'
|
||||
with mock.patch.dict(os.environ, {}, clear=True):
|
||||
with pytest.raises(authority.LifecycleAuthorityError, match='direct mutation is retired'):
|
||||
scanner.require_git_clone_launch_authority(clone_command(manifested_git[0], tmp_path))
|
||||
|
||||
|
||||
def test_remote_scanner_child_prefers_manifested_git(manifested_git):
|
||||
manifest, git, _, _ = manifested_git
|
||||
token = scanner._client_scan_manifest.set(manifest)
|
||||
try:
|
||||
env = scanner.prepend_client_git_environment({'PATH': 'foreign-path'})
|
||||
finally:
|
||||
scanner._client_scan_manifest.reset(token)
|
||||
entries = env['PATH'].split(os.pathsep)
|
||||
assert os.path.normcase(entries[0]) == os.path.normcase(str(git.parent))
|
||||
assert entries[1:] == ['foreign-path']
|
||||
|
||||
|
||||
def test_exact_clone_authenticates_and_returns_metadata(manifested_git, tmp_path):
|
||||
metadata = {'code_manifest': manifested_git[0]}
|
||||
with mock.patch.object(scanner, 'require_active_supervisor_child', return_value=metadata) as authenticate:
|
||||
assert scanner.require_git_clone_launch_authority(clone_command(manifested_git[0], tmp_path)) is metadata
|
||||
authenticate.assert_called_once_with(child_kind='scanner', require_dsn=True)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('kind', ['docker-shadow', 'keycheck-provider', 'janitor'])
|
||||
def test_git_requires_scanner_child_kind(manifested_git, tmp_path, monkeypatch, kind):
|
||||
monkeypatch.setenv(authority.CHILD_KIND_ENV, kind)
|
||||
monkeypatch.setattr(scanner, '_runtime_initialized', True)
|
||||
with mock.patch.object(scanner, 'require_active_supervisor_child') as authenticate:
|
||||
with pytest.raises(RuntimeError, match='requires scanner authority'):
|
||||
scanner.require_git_clone_launch_authority(clone_command(manifested_git[0], tmp_path))
|
||||
with pytest.raises(RuntimeError, match='requires scanner authority'):
|
||||
scanner.get_git_cmd()
|
||||
authenticate.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('operation', ['fetch', 'checkout', 'config', 'submodule', 'init', '--version'])
|
||||
def test_other_operations_rejected_before_authentication(manifested_git, tmp_path, operation):
|
||||
cmd = clone_command(manifested_git[0], tmp_path)
|
||||
cmd[1] = operation
|
||||
with mock.patch.object(scanner, 'require_active_supervisor_child') as authenticate:
|
||||
with pytest.raises(RuntimeError, match='argv contract'):
|
||||
scanner.require_git_clone_launch_authority(cmd)
|
||||
authenticate.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('change', ['extra', 'missing', 'reordered', 'separator', 'config', 'depth', 'string', 'none', 'nonstring', 'nul'])
|
||||
def test_only_exact_clone_argv_is_allowed(manifested_git, tmp_path, change):
|
||||
cmd = clone_command(manifested_git[0], tmp_path)
|
||||
if change == 'extra':
|
||||
cmd.append('--verbose')
|
||||
elif change == 'missing':
|
||||
del cmd[3]
|
||||
elif change == 'reordered':
|
||||
cmd[2], cmd[3] = cmd[3], cmd[2]
|
||||
elif change == 'separator':
|
||||
cmd[4] = '--bare'
|
||||
elif change in {'config', 'depth'}:
|
||||
cmd[2] = '-c' if change == 'config' else '--depth=1'
|
||||
elif change == 'string':
|
||||
cmd = ' '.join(cmd)
|
||||
elif change == 'none':
|
||||
cmd = None
|
||||
elif change == 'nonstring':
|
||||
cmd[6] = Path(cmd[6])
|
||||
elif change == 'nul':
|
||||
cmd[6] += '\x00'
|
||||
with mock.patch.object(scanner, 'require_active_supervisor_child') as authenticate:
|
||||
with pytest.raises(RuntimeError, match='argv contract'):
|
||||
scanner.require_git_clone_launch_authority(cmd)
|
||||
authenticate.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('source', [
|
||||
'http://example.invalid/repo', 'ssh://example.invalid/repo',
|
||||
'file:///fixture/repo', '/fixture/repo', 'C:\\fixture\\repo',
|
||||
'git@example.invalid:repo', 'ext::command', '--upload-pack=command',
|
||||
'https:///repo', 'https://example.invalid',
|
||||
'https://user@example.invalid/repo', 'https://user:password@example.invalid/repo',
|
||||
'https://example.invalid/repo?token=sentinel', 'https://example.invalid/repo#sentinel',
|
||||
'https://example.invalid\\@other.invalid/repo', 'https://example.invalid/%20 repo',
|
||||
'https://example.invalid%2fother/repo', 'https://[broken/repo',
|
||||
'https://example.invalid:bad/repo', 'https://example.invalid:99999/repo',
|
||||
])
|
||||
def test_unsafe_sources_rejected_even_when_uninitialized(manifested_git, tmp_path, monkeypatch, source):
|
||||
monkeypatch.setattr(scanner, '_runtime_initialized', False)
|
||||
cmd = clone_command(manifested_git[0], tmp_path)
|
||||
cmd[5] = source
|
||||
with mock.patch.object(scanner, 'require_active_supervisor_child') as authenticate:
|
||||
with pytest.raises(RuntimeError, match='credential-free absolute HTTPS'):
|
||||
scanner.require_git_clone_launch_authority(cmd)
|
||||
authenticate.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('destination', ['relative', '-option', ''])
|
||||
def test_invalid_destination_rejected(manifested_git, tmp_path, destination):
|
||||
cmd = clone_command(manifested_git[0], tmp_path)
|
||||
cmd[6] = destination
|
||||
with pytest.raises(RuntimeError):
|
||||
scanner.require_git_clone_launch_authority(cmd)
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.name != 'nt', reason='Windows drive-relative paths')
|
||||
@pytest.mark.parametrize('destination', ['\\clone', '/clone', 'C:clone'])
|
||||
def test_drive_relative_destination_rejected(manifested_git, tmp_path, destination):
|
||||
cmd = clone_command(manifested_git[0], tmp_path)
|
||||
cmd[6] = destination
|
||||
with pytest.raises(RuntimeError, match='absolute path'):
|
||||
scanner.require_git_clone_launch_authority(cmd)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('replacement', ['path', 'bare', 'trufflehog'])
|
||||
def test_unmanifested_executable_is_rejected(manifested_git, tmp_path, replacement):
|
||||
manifest = manifested_git[0]
|
||||
cmd = clone_command(manifest, tmp_path)
|
||||
cmd[0] = {
|
||||
'path': str(tmp_path / 'replacement.exe'), 'bare': 'git',
|
||||
'trufflehog': manifest['executables']['trufflehog']['path'],
|
||||
}[replacement]
|
||||
with mock.patch.object(scanner, 'require_active_supervisor_child', return_value={'code_manifest': manifest}):
|
||||
with pytest.raises(RuntimeError, match='immutable supervisor authority'):
|
||||
scanner.require_git_clone_launch_authority(cmd)
|
||||
|
||||
|
||||
def test_getter_and_guard_propagate_content_verification_failure(manifested_git, tmp_path, monkeypatch):
|
||||
manifest, git, _, _ = manifested_git
|
||||
git.write_bytes(b'changed Git executable')
|
||||
monkeypatch.setattr(scanner, '_runtime_initialized', True)
|
||||
|
||||
def authenticate(**kwargs):
|
||||
assert kwargs == {'child_kind': 'scanner', 'require_dsn': True}
|
||||
authority.verify_code_manifest(manifest)
|
||||
return {'code_manifest': manifest}
|
||||
|
||||
monkeypatch.setattr(scanner, 'require_active_supervisor_child', authenticate)
|
||||
with pytest.raises(authority.LifecycleAuthorityError, match='executable:git'):
|
||||
scanner.get_git_cmd()
|
||||
with pytest.raises(authority.LifecycleAuthorityError, match='executable:git'):
|
||||
scanner.require_git_clone_launch_authority(clone_command(manifest, tmp_path))
|
||||
Reference in New Issue
Block a user