Initial server source import

This commit is contained in:
sashatrask
2026-09-30 20:30:56 +03:00
commit 170dd941b9
498 changed files with 261563 additions and 0 deletions
+313
View File
@@ -0,0 +1,313 @@
import json
import os
from pathlib import Path
import subprocess
from types import SimpleNamespace
import pytest
from test_docker_codec_recovery import TARGET, archive_bytes, descriptor, encoded, recovery
import console_runner
import docker_shadow
import scanner
import scanner_db
ROUTINE = [
('info-2', 'trufflehog dev'), ('info-2', 'starting scanner workers'),
('info-2', 'starting detector workers'), ('info-2', 'starting verificationOverlap workers'),
('info-2', 'starting notifier workers'), ('info-0', 'running source'),
('info-2', 'enumerating source'), ('info-2', 'scanning image'),
('info-2', 'scanning image history'), ('info-2', 'scanning image history entry'),
('info-2', 'scanning image layers'), ('info-2', 'scanning layer'),
]
SIZE_SKIP = 'skipping file: size exceeds max allowed'
def line(msg, level='info-2', **extra):
return json.dumps({'level': level, 'logger': 'trufflehog', 'msg': msg, **extra}, ensure_ascii=False)
FINISHED = line('finished scanning', level='info-0')
def parse(lines, source='docker', rc=0, finished=True):
return scanner.apply_trufflehog_diagnostics(
{'findings': [{'DetectorName': 'SyntheticRetained'}], 'errors': []},
'\n'.join([*lines, *([FINISHED] if finished else [])]), rc, source, require_completion=True,
)
@pytest.mark.parametrize('source', ['docker', 'filesystem'])
@pytest.mark.parametrize('rc', [0, 1, -1])
def test_exact_size_skip_is_incomplete_and_nonretryable(source, rc):
result = parse([line(SIZE_SKIP, filename='fixture.txt', size=8240, limit=1024)], source, rc)
assert len(result['findings']) == 1 and result['retryable'] is False
if rc == 0:
assert not result['errors']
assert result['degraded'] and result['warning_classes'] == ['archive_member_size']
else:
assert result['errors'] and result['error_class'] == 'archive_member_size'
assert scanner_db.target_status(result) != 'clean'
def test_size_warning_does_not_replace_missing_completion_failure():
result = parse([line(SIZE_SKIP)], finished=False)
assert result['error_class'] == 'command_incomplete' and result['retryable']
assert result['warning_classes'] == ['archive_member_size']
@pytest.mark.parametrize('message', [SIZE_SKIP, 'scanning layer', 'finished scanning'])
@pytest.mark.parametrize('detail', [{'error': 'unclassified fatal cause'}, {'errors': ['unclassified fatal cause']},
{'error': 'unknown flag'}, {'errors': ['unknown flag', 'connection reset']},
{'message': 'unknown flag'}])
def test_known_messages_cannot_hide_singular_plural_or_detail_errors(message, detail):
result = parse([line(message, **detail)])
assert result['errors'] and not result.get('warnings')
assert len(result['findings']) == 1
def test_known_message_with_error_level_is_fatal():
assert parse([line('scanning layer', level='error')])['errors']
def test_routine_progress_does_not_consume_unclassified_budget():
messages = [line(message, level=level) for level, message in ROUTINE] * 50
result = parse(messages)
assert not result['errors']
assert result['scan_meta']['diagnostic_lines_processed'] == len(messages) + 1
assert 'stderr_unclassified' not in result['scan_meta']
@pytest.mark.parametrize('message', [line('future unknown progress'), line('scanning layer', logger='different'),
line('scanning layer', errors=[]), line('scanning layer', error=''),
'scanning layer', line('scanning layer', level='info-1')])
def test_unknown_or_error_shaped_info_still_has_retention_bound(message):
result = parse([message] * 21)
assert result['error_class'] == 'source_resource'
assert 'unclassified limit' in result['scan_meta']['diagnostic_output_limit_reason']
def test_size_message_matching_is_exact():
result = parse([line(SIZE_SKIP + ' (new variant)')])
assert not result.get('warnings')
assert result['scan_meta']['stderr_unclassified']
def test_routine_info_remains_unclassified_for_unmodified_git_path():
result = parse([line('scanning layer')] * 21, source='git')
assert result['error_class'] == 'source_resource'
def test_routine_info_cannot_hide_nonzero_exit():
result = parse([line(message, level=level) for level, message in ROUTINE], rc=3)
assert result['errors'] and result['error_class'] == 'wrapper_exit'
def test_routine_info_still_counts_towards_total_line_cap():
result = parse([line('scanning layer')] * 2001)
assert result['error_class'] == 'source_resource'
assert 'total line limit' in result['scan_meta']['diagnostic_output_limit_reason']
@pytest.mark.parametrize('text,reason', [('x' * 8193, 'line character'), ('\u00e9' * 5000, 'line byte')])
def test_routine_info_cannot_bypass_per_line_caps(text, reason):
result = parse([line('scanning layer', layer=text)])
assert result['error_class'] == 'source_resource'
assert reason in result['scan_meta']['diagnostic_output_limit_reason']
def test_coverage_warning_volume_still_fails_closed():
result = parse([line(SIZE_SKIP)] * 201)
assert result['error_class'] == 'source_resource'
assert 'warning limit' in result['scan_meta']['diagnostic_output_limit_reason']
assert len(result['findings']) == 1
def test_file_backed_output_limit_after_routine_info_is_not_ignored():
def output():
yield line('scanning layer')
raise scanner.CommandOutputLimitError('TruffleHog stderr exceeded its byte bound')
result = scanner.apply_trufflehog_diagnostics({'findings': [{}], 'errors': []}, output(), 0, 'docker')
assert result['error_class'] == 'source_resource'
assert result['scan_meta']['diagnostic_output_limited'] and result['findings']
def source_args(**overrides):
return SimpleNamespace(**{
**dict(platform='docker', workers=1, timeout=30, save_dir='synthetic-output', detectors=None,
exclude_detectors=None, no_verification=True, trufflehog_config='', drop_detectors=[],
trufflehog_concurrency=3, docker_layer_config_max_bytes=2048,
docker_layer_max_bytes=2 << 20, docker_layer_image_max_bytes=3 << 20,
docker_layer_max_layers=3, docker_layer_archive_max_size_bytes=4096,
docker_layer_archive_max_depth=2, docker_layer_archive_timeout_sec=4,
docker_layer_blob_timeout_sec=17, docker_layer_filesystem_concurrency=1,
docker_layer_blob_max_attempts=2, docker_layer_min_free_bytes=987654), **overrides})
def test_source_limits_reach_full_scan_but_not_durable_layer_dispatch(monkeypatch):
args = source_args()
monkeypatch.setattr(console_runner, 'get_platform_token', lambda args: None)
monkeypatch.setattr(scanner.scan_config, 'drop_detectors', [])
monkeypatch.setattr(scanner.docker_token_manager, 'get_next_config', lambda: None)
options = console_runner.prepare_scan_options(args, 1)
assert options['docker_recovery_limits'] == console_runner.docker_layer_limits(args)
assert options['docker_recovery_min_free_bytes'] == 987654
calls = []
def full(target, **kwargs):
calls.append(kwargs)
return {'findings': [], 'errors': []}
def layer(target, work, *, timeout_sec, detectors, exclude_detectors, no_verification, trufflehog_config):
calls.append({'work': work})
return {'findings': [], 'errors': []}
monkeypatch.setattr(scanner, 'scan_docker_image', full)
monkeypatch.setattr(scanner, 'scan_docker_layer_plan', layer)
scanner.scan_target_result(TARGET, 'docker', 'full-fixture', options)
assert calls[-1]['docker_recovery_limits']['archive_max_size_bytes'] == 4096
assert calls[-1]['docker_recovery_min_free_bytes'] == 987654
work = {'synthetic': 'bound work'}
scanner.scan_target_result(TARGET, 'docker', 'layer-fixture', dict(options, docker_layer_work=work))
assert calls[-1] == {'work': work}
assert 'docker_recovery_limits' in options # Dispatcher copies rather than mutating the caller.
def test_non_docker_options_do_not_receive_recovery_settings(monkeypatch):
monkeypatch.setattr(console_runner, 'get_platform_token', lambda args: None)
monkeypatch.setattr(scanner.scan_config, 'drop_detectors', [])
options = console_runner.prepare_scan_options(source_args(platform='github'), 1)
assert not any(key.startswith('docker_recovery') for key in options)
def test_full_policy_fingerprint_includes_source_recovery_limits(monkeypatch):
monkeypatch.setattr(console_runner, 'hash_file', lambda path: 'a' * 64)
args = source_args()
options = {'docker_recovery_limits': console_runner.docker_layer_limits(args)}
before = console_runner.docker_layer_scan_policy_sha256(args, options)
options['docker_recovery_limits'] = dict(options['docker_recovery_limits'], archive_max_depth=3)
assert console_runner.docker_layer_scan_policy_sha256(args, options) != before
def test_selection_and_scheduling_caps_do_not_rekey_execution_coverage(monkeypatch):
monkeypatch.setattr(console_runner, 'hash_file', lambda path: 'a' * 64)
args = source_args()
options = {'docker_recovery_limits': console_runner.docker_layer_limits(args)}
before = console_runner.docker_layer_scan_policy_sha256(args, options)
options['docker_recovery_limits'] = dict(options['docker_recovery_limits'], max_layers=8,
image_max_bytes=8 << 20, blob_timeout_sec=30, blob_max_attempts=3)
assert console_runner.docker_layer_scan_policy_sha256(args, options) == before
def test_full_shadow_forwards_source_recovery_settings(monkeypatch):
class DB:
def docker_adaptive_shadow_control_identities(self, scan_id):
return set(), set()
calls = []
monkeypatch.setattr(scanner.docker_token_manager, 'get_next_config', lambda: None)
monkeypatch.setattr(scanner, 'scan_docker_image', lambda *a, **k: calls.append(k) or {'findings': [], 'errors': []})
limits = console_runner.docker_layer_limits(source_args())
metrics = docker_shadow.private_full_side_metrics(DB(), {'target_scan_id': 1, 'normalized_target': TARGET},
{'timeout_sec': 30, 'docker_recovery_limits': limits, 'docker_recovery_min_free_bytes': 987654})
assert metrics['failure_count'] == 0
assert calls[0]['docker_recovery_limits'] == limits and calls[0]['docker_recovery_min_free_bytes'] == 987654
@pytest.fixture
def native_runner(tmp_path, monkeypatch):
executable = Path(r'C:\Tools\trufflehog.exe')
if not executable.is_file():
pytest.skip('installed TruffleHog unavailable')
monkeypatch.setattr(scanner, 'get_trufflehog_cmd', lambda: str(executable))
env = {key: os.environ[key] for key in ('SYSTEMROOT', 'WINDIR', 'PATH') if key in os.environ}
env.update(TEMP=str(tmp_path), TMP=str(tmp_path), HOME=str(tmp_path), USERPROFILE=str(tmp_path),
DOCKER_CONFIG=str(tmp_path), HTTP_PROXY='http://127.0.0.1:1', HTTPS_PROXY='http://127.0.0.1:1')
def command(args, timeout, unused_env, **kwargs):
completed = subprocess.run(args, env=env, cwd=tmp_path, capture_output=True, timeout=min(timeout, 30))
return scanner.streamed_output_from_text(stdout=completed.stdout.decode(), stderr=completed.stderr.decode(), returncode=completed.returncode)
monkeypatch.setattr(scanner, 'run_command_streamed', command)
return command
@pytest.mark.parametrize('sibling', [False, True])
def test_actual_nested_size_skip_through_shared_helper_is_not_clean(tmp_path, native_runner, sibling):
import time
marker = b'OFFLINENESTED_ABCDEFGHIJKLMNOPQRSTUVWXYZ012345'
nested = archive_bytes([('large.txt', b'x' * 8192 + b'\n' + marker + b'\n')])
entries = [('inner.tar.gz', encoded(nested, 'gzip'))]
if sibling:
entries.insert(0, ('outside.txt', marker + b'\n'))
payload = archive_bytes(entries)
path = tmp_path / 'blob'
path.write_bytes(payload)
policy = tmp_path / 'policy.yaml'
policy.write_text('detectors:\n - name: OfflineNested\n keywords: [OFFLINENESTED]\n regex:\n marker: "OFFLINENESTED_[A-Z0-9]{32}"\n', encoding='ascii')
limits = dict(console_runner.docker_layer_limits(source_args()), archive_max_size_bytes=1024, archive_max_depth=4)
result = scanner._scan_docker_content_file(str(path), descriptor(payload), limits, time.monotonic() + 30,
no_verification=True, trufflehog_config=str(policy))
assert not result['errors'] and result['degraded'] and not result['retryable']
assert result['warning_classes'] == ['archive_member_size']
assert bool(result['findings']) == sibling
assert scanner_db.target_status(result) != 'clean'
@pytest.mark.parametrize('sibling', [False, True])
def test_actual_nested_skip_prevents_full_recovery_success(recovery, tmp_path, monkeypatch, sibling):
executable = Path(r'C:\Tools\trufflehog.exe')
if not executable.is_file():
pytest.skip('installed TruffleHog unavailable')
monkeypatch.setattr(scanner, 'get_trufflehog_cmd', lambda: str(executable))
marker = b'OFFLINENESTED_ABCDEFGHIJKLMNOPQRSTUVWXYZ012345'
nested = archive_bytes([('large.txt', b'x' * 8192 + b'\n' + marker + b'\n')])
entries = [('inner.tar.gz', encoded(nested, 'gzip'))]
if sibling:
entries.insert(0, ('outside.txt', marker + b'\n'))
payload = archive_bytes(entries)
item = descriptor(payload)
recovery.resolved['layers'] = [item]
recovery.payloads[item['digest']] = payload
policy = tmp_path / 'policy.yaml'
policy.write_text('detectors:\n - name: OfflineNested\n keywords: [OFFLINENESTED]\n regex:\n marker: "OFFLINENESTED_[A-Z0-9]{32}"\n', encoding='ascii')
limits = dict(console_runner.docker_layer_limits(source_args()), archive_max_size_bytes=1024, archive_max_depth=4)
recovery.real_cli = True
result = recovery.run(docker_recovery_limits=limits, no_verification=True, trufflehog_config=str(policy))
assert result['errors'] and not result['retryable']
assert result['error_class'] == 'recovery_scan_incomplete'
assert not result['scan_meta']['docker_full_recovery']['coverage_complete']
assert len(result['findings']) >= 1 + int(sibling)
assert all(command[command.index('--log-level') + 1] == '2' for command, _, _ in recovery.commands)
def test_actual_normal_full_docker_with_many_routine_lines_succeeds(tmp_path, native_runner, monkeypatch):
layers = []
diffs = []
for index in range(32):
raw = archive_bytes([(f'file-{index}.txt', b'ordinary synthetic content\n')])
layers.append((f'layer-{index}.tar', encoded(raw, 'gzip')))
diffs.append(descriptor(raw)['digest'])
config = json.dumps({'os': 'linux', 'architecture': 'amd64', 'config': {},
'rootfs': {'type': 'layers', 'diff_ids': diffs},
'history': [{'created_by': 'offline fixture'} for _ in layers]}).encode()
manifest = json.dumps([{'Config': 'config.json', 'RepoTags': ['offline-fixture:latest'],
'Layers': [name for name, _ in layers]}]).encode()
image = tmp_path / 'image.tar'
image.write_bytes(archive_bytes([('manifest.json', manifest), ('config.json', config), *layers]))
policy = tmp_path / 'policy.yaml'
policy.write_text('detectors:\n - name: OfflineNested\n keywords: [OFFLINENESTED]\n regex:\n marker: "OFFLINENESTED_[A-Z0-9]{32}"\n', encoding='ascii')
def local_image_only(args, *rest, **kwargs):
assert args[1] == 'docker' and args[args.index('--image') + 1] == TARGET
args = list(args)
args[args.index('--image') + 1] = 'file://' + str(image)
return native_runner(args, *rest, **kwargs)
monkeypatch.setattr(scanner, 'run_command_streamed', local_image_only)
result = scanner.scan_docker_image(TARGET, config_dir=str(tmp_path), trufflehog_config=str(policy),
no_verification=True, trufflehog_concurrency=1, log_target=False)
assert not result['errors'] and not result.get('warnings')
assert result['scan_meta']['diagnostic_lines_processed'] >= 70
assert 'stderr_unclassified' not in result['scan_meta']
assert scanner_db.target_status(result) == 'clean'