Initial server source import
This commit is contained in:
@@ -0,0 +1,708 @@
|
||||
import os
|
||||
import json
|
||||
import base64
|
||||
import hashlib
|
||||
import io
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from datetime import datetime, timezone
|
||||
from unittest import mock
|
||||
from types import SimpleNamespace
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
APP_DIR = ROOT / 'app'
|
||||
sys.path.insert(0, str(APP_DIR))
|
||||
|
||||
from result_bundle import (
|
||||
BundleReservation,
|
||||
ResultBundleConflictError,
|
||||
ResultBundleError,
|
||||
ResultBundleReader,
|
||||
ResultBundleWriter,
|
||||
)
|
||||
from runtime_security import ensure_private_directory, harden_private_file
|
||||
import runtime_security
|
||||
import result_bundle
|
||||
import console_runner
|
||||
import scanner
|
||||
import worker_contracts
|
||||
from worker_contracts import (
|
||||
AssignmentOutcome,
|
||||
DiagnosticCategory,
|
||||
DiagnosticKind,
|
||||
ScanOutcome,
|
||||
WorkerPhase,
|
||||
build_diagnostic_envelope,
|
||||
decode_diagnostic_envelope,
|
||||
diagnostic_material_bytes,
|
||||
encode_diagnostic_envelope,
|
||||
)
|
||||
from result_ingester import ResultIngester
|
||||
|
||||
|
||||
def reservation():
|
||||
bundle_id = 'a' * 32
|
||||
return BundleReservation(
|
||||
reservation_id=7,
|
||||
reservation_token='reservation-token',
|
||||
bundle_id=bundle_id,
|
||||
scan_event_id='b' * 32,
|
||||
queue_id=11,
|
||||
claim_lease_token='lease-token',
|
||||
declared_bytes=1024 * 1024,
|
||||
ready_path=f'ready/{bundle_id[:2]}/{bundle_id}.trb',
|
||||
source='github',
|
||||
platform='github',
|
||||
target='https://example.invalid/repo',
|
||||
normalized_target='https://example.invalid/repo',
|
||||
)
|
||||
|
||||
|
||||
def diagnostic(
|
||||
*, occurrence_id='bundle-diagnostic', reservation_id=7,
|
||||
assignment_outcome=AssignmentOutcome.ACCEPTED,
|
||||
scan_outcome=ScanOutcome.ERROR,
|
||||
):
|
||||
timestamp = '2026-09-24T00:00:00Z'
|
||||
return build_diagnostic_envelope(
|
||||
occurrence_id=occurrence_id,
|
||||
reservation_id=reservation_id,
|
||||
scan_event_id='b' * 32,
|
||||
slot_id=0,
|
||||
source='github',
|
||||
phase=WorkerPhase.SCANNING,
|
||||
kind=DiagnosticKind.ASSIGNMENT,
|
||||
category=DiagnosticCategory.SCANNER,
|
||||
code='scan.fixture_error',
|
||||
summary='fixture diagnostic',
|
||||
retryable=False,
|
||||
attempt=1,
|
||||
assignment_outcome=assignment_outcome,
|
||||
scan_outcome=scan_outcome,
|
||||
occurred_at=timestamp,
|
||||
captured_at=timestamp,
|
||||
)
|
||||
|
||||
|
||||
class ResultBundleV2Tests(unittest.TestCase):
|
||||
def private_root(self, temp_dir):
|
||||
root = os.path.join(temp_dir, 'bundles')
|
||||
ensure_private_directory(root, reject_reparse=True)
|
||||
return root
|
||||
|
||||
def test_round_trip_streams_frames_and_exact_footer(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
with ResultBundleWriter.open(root, reservation()) as writer:
|
||||
writer.write_finding({'DetectorName': 'OpenAI', 'Raw': 'secret'})
|
||||
writer.write_error('fixture error')
|
||||
writer.write_candidate({'service': 'openai', 'secret_text': 'secret'})
|
||||
commit = writer.finish({'status': 'found', 'findings_count': 1})
|
||||
|
||||
reader = ResultBundleReader(os.path.join(root, *commit.relative_path.split('/')))
|
||||
metadata = reader.validate()
|
||||
self.assertEqual(metadata.scan_event_hash, commit.scan_event_hash)
|
||||
self.assertEqual(metadata.actual_bytes, commit.actual_bytes)
|
||||
self.assertEqual(list(reader.iter_findings())[0]['DetectorName'], 'OpenAI')
|
||||
self.assertEqual(list(reader.iter_errors()), ['fixture error'])
|
||||
self.assertEqual(list(reader.iter_candidates())[0]['service'], 'openai')
|
||||
self.assertEqual(reader.metadata()['status'], 'found')
|
||||
|
||||
def test_diagnostic_frames_are_canonical_counted_and_identity_checked(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
expected = diagnostic()
|
||||
with ResultBundleWriter.open(root, reservation()) as writer:
|
||||
writer.write_error('fixture error remains an E frame')
|
||||
writer.write_diagnostic(expected)
|
||||
commit = writer.finish({'status': 'error'})
|
||||
|
||||
reader = ResultBundleReader(os.path.join(root, *commit.relative_path.split('/')))
|
||||
metadata = reader.validate()
|
||||
values = list(reader.iter_diagnostics())
|
||||
self.assertEqual(metadata.diagnostic_count, 1)
|
||||
self.assertEqual(
|
||||
metadata.diagnostic_bytes,
|
||||
result_bundle.FRAME_HEADER.size + len(encode_diagnostic_envelope(expected)),
|
||||
)
|
||||
self.assertEqual(
|
||||
values,
|
||||
[json.loads(encode_diagnostic_envelope(expected).decode('ascii'))],
|
||||
)
|
||||
self.assertEqual(list(reader.iter_errors()), ['fixture error remains an E frame'])
|
||||
|
||||
with self.assertRaisesRegex(ResultBundleError, 'identity conflicts'):
|
||||
with ResultBundleWriter.open(root, BundleReservation(
|
||||
**{**reservation().__dict__, 'bundle_id': 'c' * 32,
|
||||
'ready_path': 'ready/cc/' + ('c' * 32) + '.trb'}
|
||||
)) as writer:
|
||||
writer.write_diagnostic(diagnostic(reservation_id=8))
|
||||
|
||||
def test_diagnostic_duplicate_aggregate_order_and_legacy_footer_are_strict(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
with ResultBundleWriter.open(root, reservation()) as writer:
|
||||
commit = writer.finish({'status': 'clean'})
|
||||
legacy_reader = ResultBundleReader(
|
||||
os.path.join(root, *commit.relative_path.split('/'))
|
||||
)
|
||||
legacy_reader.validate()
|
||||
footer = list(legacy_reader._iter_frames())[-1][1]
|
||||
self.assertNotIn('diagnostic_count', footer)
|
||||
self.assertNotIn('diagnostic_bytes', footer)
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
writer = ResultBundleWriter.open(root, reservation())
|
||||
writer.write_diagnostic(diagnostic())
|
||||
with self.assertRaisesRegex(ResultBundleError, 'duplicated'):
|
||||
writer.write_diagnostic(diagnostic())
|
||||
writer.abort()
|
||||
|
||||
def test_dless_error_without_timestamps_uses_fixed_replay_timestamp(self):
|
||||
projected = []
|
||||
for _index in range(2):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
with ResultBundleWriter.open(root, reservation()) as writer:
|
||||
writer.write_error('legacy timestamp-free error')
|
||||
commit = writer.finish({'status': 'error'})
|
||||
reader = ResultBundleReader(
|
||||
os.path.join(root, *commit.relative_path.split('/'))
|
||||
)
|
||||
projected.append(reader.effective_diagnostics())
|
||||
self.assertEqual(projected[0], projected[1])
|
||||
self.assertEqual(
|
||||
projected[0][0]['occurred_at'],
|
||||
worker_contracts.LEGACY_DIAGNOSTIC_FALLBACK_TIMESTAMP,
|
||||
)
|
||||
|
||||
for outcome, scan_outcome, metadata_status in (
|
||||
(AssignmentOutcome.PREBUNDLE_FAILED, ScanOutcome.UNAVAILABLE, 'error'),
|
||||
(AssignmentOutcome.ACCEPTED, ScanOutcome.CLEAN, 'error'),
|
||||
):
|
||||
with self.subTest(
|
||||
outcome=outcome, scan_outcome=scan_outcome,
|
||||
), tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
with ResultBundleWriter.open(root, reservation()) as writer:
|
||||
writer.write_diagnostic(diagnostic(
|
||||
assignment_outcome=outcome,
|
||||
scan_outcome=scan_outcome,
|
||||
))
|
||||
commit = writer.finish({'status': metadata_status})
|
||||
with self.assertRaisesRegex(ResultBundleError, 'outcome'):
|
||||
ResultBundleReader(
|
||||
os.path.join(root, *commit.relative_path.split('/'))
|
||||
).validate()
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
writer = ResultBundleWriter.open(root, reservation())
|
||||
invalid = json.loads(encode_diagnostic_envelope(diagnostic()))
|
||||
invalid['source'] = ' '
|
||||
with self.assertRaisesRegex(ResultBundleError, 'diagnostic frame'):
|
||||
writer.write_diagnostic(invalid)
|
||||
writer.abort()
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
writer = ResultBundleWriter.open(root, reservation())
|
||||
writer.write_candidate({'service': 'openai'})
|
||||
with self.assertRaisesRegex(ResultBundleError, 'order'):
|
||||
writer.write_diagnostic(diagnostic())
|
||||
writer.abort()
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
writer = ResultBundleWriter.open(root, reservation())
|
||||
with self.assertRaisesRegex(ResultBundleError, 'aggregate'):
|
||||
for index in range(32):
|
||||
value = build_diagnostic_envelope(
|
||||
occurrence_id=f'aggregate-{index}', reservation_id=7,
|
||||
scan_event_id='b' * 32, slot_id=0, source='github',
|
||||
phase=WorkerPhase.SCANNING, kind=DiagnosticKind.ASSIGNMENT,
|
||||
category=DiagnosticCategory.SCANNER, code='scan.aggregate',
|
||||
summary='x' * 9000, retryable=False, attempt=1,
|
||||
assignment_outcome=AssignmentOutcome.ACCEPTED,
|
||||
scan_outcome=ScanOutcome.ERROR,
|
||||
occurred_at='2026-09-24T00:00:00Z',
|
||||
captured_at='2026-09-24T00:00:00Z',
|
||||
)
|
||||
writer.write_diagnostic(value)
|
||||
writer.abort()
|
||||
|
||||
def test_scan_diagnostic_preserves_preparse_raw_process_material(self):
|
||||
secret = 'raw-provider-token-evidence'
|
||||
raw_stderr = json.dumps({
|
||||
'level': 'error', 'msg': f'fatal {secret}',
|
||||
}) + '\n'
|
||||
result = {'findings': [], 'errors': []}
|
||||
with scanner.streamed_output_from_text('', raw_stderr, 1) as output:
|
||||
scanner.apply_trufflehog_diagnostics(
|
||||
result, output, output.returncode, 'git',
|
||||
redactions=(secret,),
|
||||
)
|
||||
result.update({
|
||||
'target': reservation().target,
|
||||
'scan_type': reservation().platform,
|
||||
'scan_event_id': reservation().scan_event_id,
|
||||
'scan_started_at': '2026-09-24T00:00:00Z',
|
||||
'timestamp': '2026-09-24T00:00:01Z',
|
||||
'duration_sec': 1.0,
|
||||
})
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
staged = scanner.stage_result_bundle(
|
||||
result, reservation(), root, {}, {'queue_status': 'failed'},
|
||||
)
|
||||
reader = ResultBundleReader(
|
||||
os.path.join(root, *staged.relative_path.split('/'))
|
||||
)
|
||||
diagnostic_value = list(reader.iter_diagnostics())[0]
|
||||
envelope = decode_diagnostic_envelope(json.dumps(
|
||||
diagnostic_value, ensure_ascii=True, sort_keys=True,
|
||||
separators=(',', ':'),
|
||||
).encode('ascii'))
|
||||
self.assertEqual(
|
||||
diagnostic_material_bytes(envelope.process.stderr),
|
||||
raw_stderr.encode('utf-8'),
|
||||
)
|
||||
self.assertIn(secret, diagnostic_material_bytes(
|
||||
envelope.process.stderr,
|
||||
).decode('utf-8'))
|
||||
self.assertNotIn(secret, '\n'.join(reader.iter_errors()))
|
||||
self.assertIn('pre-existing configured parser redactions',
|
||||
envelope.exception.message)
|
||||
|
||||
def test_scan_diagnostic_preserves_raw_http_response_body(self):
|
||||
import requests
|
||||
|
||||
response = requests.Response()
|
||||
response.status_code = 503
|
||||
response._content = b'\xffraw-provider-body\x00'
|
||||
response.headers['Content-Type'] = 'application/octet-stream'
|
||||
failure = requests.HTTPError('provider request failed', response=response)
|
||||
with mock.patch.object(scanner, 'scan_git_repo', side_effect=failure):
|
||||
result = scanner.scan_target_result(
|
||||
reservation().target, 'github', reservation().scan_event_id, {},
|
||||
)
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
staged = scanner.stage_result_bundle(
|
||||
result, reservation(), root, {}, {'queue_status': 'failed'},
|
||||
)
|
||||
reader = ResultBundleReader(
|
||||
os.path.join(root, *staged.relative_path.split('/'))
|
||||
)
|
||||
envelope = decode_diagnostic_envelope(json.dumps(
|
||||
list(reader.iter_diagnostics())[0], ensure_ascii=True,
|
||||
sort_keys=True, separators=(',', ':'),
|
||||
).encode('ascii'))
|
||||
self.assertEqual(envelope.http.status_code, 503)
|
||||
self.assertEqual(
|
||||
diagnostic_material_bytes(envelope.http.body), response.content,
|
||||
)
|
||||
|
||||
def test_naive_legacy_scan_timestamp_is_not_relabelled_as_utc(self):
|
||||
result = {
|
||||
'target': reservation().target,
|
||||
'scan_type': reservation().platform,
|
||||
'scan_event_id': reservation().scan_event_id,
|
||||
'scan_started_at': '2020-01-01T12:00:00',
|
||||
'timestamp': '2020-01-01T12:00:01',
|
||||
'duration_sec': 1.0,
|
||||
'findings': [],
|
||||
'errors': ['legacy scanner failure'],
|
||||
}
|
||||
before = datetime.now(timezone.utc)
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
staged = scanner.stage_result_bundle(
|
||||
result, reservation(), root, {}, {'queue_status': 'failed'},
|
||||
)
|
||||
diagnostic_value = list(ResultBundleReader(os.path.join(
|
||||
root, *staged.relative_path.split('/'),
|
||||
)).iter_diagnostics())[0]
|
||||
after = datetime.now(timezone.utc)
|
||||
envelope = decode_diagnostic_envelope(json.dumps(
|
||||
diagnostic_value, ensure_ascii=True, sort_keys=True,
|
||||
separators=(',', ':'),
|
||||
).encode('ascii'))
|
||||
occurred = datetime.fromisoformat(
|
||||
envelope.occurred_at.replace('Z', '+00:00'),
|
||||
)
|
||||
self.assertGreaterEqual(occurred, before)
|
||||
self.assertLessEqual(occurred, after)
|
||||
|
||||
def test_build_api_error_and_git_adapter_preserve_raw_http_evidence(self):
|
||||
import requests
|
||||
|
||||
response = requests.Response()
|
||||
response.status_code = 429
|
||||
response._content = b'{"message":"raw rate body"}\x00'
|
||||
response._content_consumed = True
|
||||
response.headers.update({
|
||||
'Content-Type': 'application/json',
|
||||
'X-GitHub-Request-Id': 'request-fixture',
|
||||
'Retry-After': '60',
|
||||
})
|
||||
error = scanner.github_api_error(response)
|
||||
self.assertEqual(error.diagnostic_http['status_code'], 429)
|
||||
self.assertEqual(
|
||||
base64.b64decode(error.diagnostic_http['body_b64']),
|
||||
response.content,
|
||||
)
|
||||
claim = {
|
||||
**reservation().__dict__,
|
||||
'reservation_id': reservation().reservation_id,
|
||||
'scan_event_id': reservation().scan_event_id,
|
||||
'target': reservation().target,
|
||||
}
|
||||
failure = console_runner._GitResolutionFailure(error)
|
||||
result = console_runner.git_resolution_failure_result(
|
||||
SimpleNamespace(platform='github'), claim, {'token': ''}, failure,
|
||||
)
|
||||
self.assertEqual(result['_diagnostic_http'], error.diagnostic_http)
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
staged = scanner.stage_result_bundle(
|
||||
result, reservation(), root, {}, {'queue_status': 'deferred'},
|
||||
)
|
||||
reader = ResultBundleReader(
|
||||
os.path.join(root, *staged.relative_path.split('/'))
|
||||
)
|
||||
envelope = decode_diagnostic_envelope(json.dumps(
|
||||
list(reader.iter_diagnostics())[0], ensure_ascii=True,
|
||||
sort_keys=True, separators=(',', ':'),
|
||||
).encode('ascii'))
|
||||
self.assertEqual(envelope.http.status_code, 429)
|
||||
self.assertEqual(envelope.http.request_id, 'request-fixture')
|
||||
self.assertEqual(
|
||||
diagnostic_material_bytes(envelope.http.body), response.content,
|
||||
)
|
||||
captured_headers = json.loads(
|
||||
diagnostic_material_bytes(envelope.http.headers).decode('ascii')
|
||||
)
|
||||
self.assertEqual(
|
||||
captured_headers['X-GitHub-Request-Id'], 'request-fixture',
|
||||
)
|
||||
self.assertIn('parsed response headers', envelope.exception.message)
|
||||
|
||||
def test_streamed_api_error_body_is_captured_once_with_explicit_bound(self):
|
||||
import requests
|
||||
|
||||
for payload, truncated in (
|
||||
(b'{"message":"small streamed body"}', False),
|
||||
(b'x' * (worker_contracts.MAX_DIAGNOSTIC_BODY_BYTES + 4096), True),
|
||||
):
|
||||
with self.subTest(truncated=truncated):
|
||||
response = requests.Response()
|
||||
response.status_code = 429
|
||||
response.raw = io.BytesIO(payload)
|
||||
response._content = False
|
||||
response._content_consumed = False
|
||||
response.headers['Content-Type'] = 'application/octet-stream'
|
||||
message = scanner.response_message(response)
|
||||
error = scanner.build_api_error(
|
||||
'github', 'rate_limit', message, response,
|
||||
)
|
||||
captured = base64.b64decode(
|
||||
error.diagnostic_http['body_b64']
|
||||
)
|
||||
self.assertEqual(
|
||||
captured,
|
||||
payload[:worker_contracts.MAX_DIAGNOSTIC_BODY_BYTES],
|
||||
)
|
||||
self.assertEqual(
|
||||
error.diagnostic_http['body_capture_truncated'], truncated,
|
||||
)
|
||||
self.assertEqual(
|
||||
error.diagnostic_http['body_original_size'], len(payload),
|
||||
)
|
||||
self.assertEqual(
|
||||
error.diagnostic_http['body_stored_size'], len(captured),
|
||||
)
|
||||
self.assertEqual(
|
||||
error.diagnostic_http['body_sha256'],
|
||||
hashlib.sha256(payload).hexdigest(),
|
||||
)
|
||||
|
||||
claim = {
|
||||
**reservation().__dict__,
|
||||
'reservation_id': reservation().reservation_id,
|
||||
'scan_event_id': reservation().scan_event_id,
|
||||
'target': reservation().target,
|
||||
}
|
||||
result = console_runner.git_resolution_failure_result(
|
||||
SimpleNamespace(platform='github'), claim, {'token': ''},
|
||||
console_runner._GitResolutionFailure(error),
|
||||
)
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
staged = scanner.stage_result_bundle(
|
||||
result, reservation(), root, {}, {'queue_status': 'deferred'},
|
||||
)
|
||||
reader = ResultBundleReader(
|
||||
os.path.join(root, *staged.relative_path.split('/'))
|
||||
)
|
||||
envelope = decode_diagnostic_envelope(json.dumps(
|
||||
list(reader.iter_diagnostics())[0], ensure_ascii=True,
|
||||
sort_keys=True, separators=(',', ':'),
|
||||
).encode('ascii'))
|
||||
self.assertEqual(
|
||||
diagnostic_material_bytes(envelope.http.body), captured,
|
||||
)
|
||||
self.assertEqual(envelope.http.body.original_size, len(payload))
|
||||
self.assertEqual(envelope.http.body.stored_size, len(captured))
|
||||
self.assertEqual(
|
||||
envelope.http.body.sha256, hashlib.sha256(payload).hexdigest(),
|
||||
)
|
||||
self.assertIs(envelope.http.body.truncated, truncated)
|
||||
|
||||
def test_staging_rejects_cross_reservation_result_identity_before_publish(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
base = {
|
||||
'reservation_id': 7,
|
||||
'queue_id': 11,
|
||||
'scan_event_id': 'b' * 32,
|
||||
'source': 'github',
|
||||
'normalized_target': 'https://example.invalid/repo',
|
||||
'scan_type': 'github',
|
||||
'target': 'https://example.invalid/repo',
|
||||
}
|
||||
cases = {
|
||||
'reservation': {'reservation_id': 8},
|
||||
'queue': {'queue_id': 12},
|
||||
'event': {'scan_event_id': 'c' * 32},
|
||||
'source': {'source': 'gitlab'},
|
||||
'normalized_target': {
|
||||
'normalized_target': 'https://secret.invalid/cross-reservation',
|
||||
},
|
||||
'target': {'target': 'https://secret.invalid/cross-reservation'},
|
||||
}
|
||||
for name, mismatch in cases.items():
|
||||
with self.subTest(name=name):
|
||||
result = {
|
||||
**base, **mismatch,
|
||||
'findings': [{'Raw': 'SECRET_SENTINEL'}],
|
||||
'errors': [],
|
||||
}
|
||||
with self.assertRaisesRegex(ValueError, 'identity') as raised:
|
||||
scanner.stage_result_bundle(
|
||||
result, reservation(), root, {},
|
||||
{'queue_status': 'done'},
|
||||
)
|
||||
self.assertNotIn('secret.invalid', str(raised.exception))
|
||||
self.assertNotIn('SECRET_SENTINEL', str(raised.exception))
|
||||
self.assertFalse(os.path.exists(os.path.join(root, 'ready')))
|
||||
|
||||
def test_truncation_and_noncanonical_payload_are_rejected(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
with ResultBundleWriter.open(root, reservation()) as writer:
|
||||
commit = writer.finish({'status': 'clean'})
|
||||
path = os.path.join(root, *commit.relative_path.split('/'))
|
||||
payload = Path(path).read_bytes()
|
||||
Path(path).write_bytes(payload[:-1])
|
||||
harden_private_file(path)
|
||||
with self.assertRaises(ResultBundleError):
|
||||
ResultBundleReader(path).validate()
|
||||
|
||||
def test_reader_rejects_ready_file_replacement_after_validation(self):
|
||||
with tempfile.TemporaryDirectory() as first_dir, tempfile.TemporaryDirectory() as second_dir:
|
||||
first_root = self.private_root(first_dir)
|
||||
second_root = self.private_root(second_dir)
|
||||
with ResultBundleWriter.open(first_root, reservation()) as writer:
|
||||
writer.write_finding({'DetectorName': 'OpenAI'})
|
||||
first = writer.finish({'status': 'found'})
|
||||
with ResultBundleWriter.open(second_root, reservation()) as writer:
|
||||
writer.write_finding({'DetectorName': 'GitLab'})
|
||||
second = writer.finish({'status': 'found'})
|
||||
first_path = os.path.join(first_root, *first.relative_path.split('/'))
|
||||
second_path = os.path.join(second_root, *second.relative_path.split('/'))
|
||||
reader = ResultBundleReader(first_path)
|
||||
reader.validate()
|
||||
os.replace(second_path, first_path)
|
||||
|
||||
with self.assertRaisesRegex(ResultBundleError, 'changed after validation'):
|
||||
list(reader.iter_findings())
|
||||
|
||||
def test_reader_rejects_non_object_typed_frames(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
with ResultBundleWriter.open(root, reservation()) as writer:
|
||||
writer.write_finding('not-an-object')
|
||||
commit = writer.finish({'status': 'found'})
|
||||
path = os.path.join(root, *commit.relative_path.split('/'))
|
||||
|
||||
with self.assertRaisesRegex(ResultBundleError, 'JSON object'):
|
||||
ResultBundleReader(path).validate()
|
||||
|
||||
def test_temporarily_unavailable_private_file_is_not_a_validation_failure(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
with ResultBundleWriter.open(root, reservation()) as writer:
|
||||
commit = writer.finish({'status': 'clean'})
|
||||
path = os.path.join(root, *commit.relative_path.split('/'))
|
||||
with mock.patch.object(result_bundle, 'private_file_ready', return_value=False):
|
||||
with self.assertRaises(result_bundle.ResultBundleUnavailableError):
|
||||
ResultBundleReader(path).validate()
|
||||
|
||||
def test_post_rename_fault_never_allows_abort_to_remove_ready_bundle(self):
|
||||
def fault(stage, _writer):
|
||||
if stage == 'after_rename':
|
||||
raise RuntimeError('injected post-rename crash')
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
writer = ResultBundleWriter.open(root, reservation(), fault=fault)
|
||||
with self.assertRaisesRegex(RuntimeError, 'post-rename'):
|
||||
writer.finish({'status': 'clean'})
|
||||
writer.abort()
|
||||
ready = os.path.join(root, *reservation().ready_path.replace('/', os.sep).split(os.sep))
|
||||
self.assertTrue(os.path.isfile(ready))
|
||||
self.assertEqual(ResultBundleReader(ready).validate().scan_event_id, 'b' * 32)
|
||||
|
||||
def test_post_publish_consumption_is_not_reported_as_acl_failure(self):
|
||||
def consume(stage, writer):
|
||||
if stage == 'after_rename':
|
||||
os.remove(writer.ready_path)
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
writer = ResultBundleWriter.open(root, reservation(), fault=consume)
|
||||
commit = writer.finish({'status': 'clean'})
|
||||
|
||||
self.assertTrue(writer.finished)
|
||||
self.assertEqual(commit.reservation_id, reservation().reservation_id)
|
||||
self.assertFalse(os.path.exists(writer.ready_path))
|
||||
|
||||
def test_pre_handoff_fault_windows_never_publish_ready_name(self):
|
||||
for stage in ('before_footer', 'before_fsync', 'before_rename'):
|
||||
with self.subTest(stage=stage), tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
|
||||
def fault(current, _writer):
|
||||
if current == stage:
|
||||
raise OSError(f'injected {stage}')
|
||||
|
||||
writer = ResultBundleWriter.open(root, reservation(), fault=fault)
|
||||
with self.assertRaisesRegex(OSError, stage):
|
||||
writer.finish({'status': 'clean'})
|
||||
writer.abort()
|
||||
ready = os.path.join(
|
||||
root, *reservation().ready_path.replace('/', os.sep).split(os.sep),
|
||||
)
|
||||
self.assertFalse(os.path.exists(ready))
|
||||
|
||||
def test_existing_deterministic_ready_name_is_never_replaced(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
with ResultBundleWriter.open(root, reservation()) as writer:
|
||||
writer.finish({'status': 'clean'})
|
||||
with self.assertRaises(ResultBundleConflictError):
|
||||
ResultBundleWriter.open(root, reservation())
|
||||
|
||||
def test_frame_bound_fails_before_ready_publication(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
writer = ResultBundleWriter.open(root, reservation())
|
||||
with self.assertRaises(ResultBundleError):
|
||||
writer.write_finding({'value': 'x' * (1024 * 1024)})
|
||||
writer.abort()
|
||||
ready = os.path.join(root, *reservation().ready_path.replace('/', os.sep).split(os.sep))
|
||||
self.assertFalse(os.path.exists(ready))
|
||||
|
||||
def test_private_path_inspection_never_maps_storage_failure_to_absence(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
ensure_private_directory(os.path.join(root, 'ready', 'aa'), reject_reparse=True)
|
||||
absent = runtime_security.inspect_private_relative_path(
|
||||
root, 'ready/aa/' + ('a' * 32) + '.trb',
|
||||
)
|
||||
self.assertEqual(absent.state, runtime_security.PrivatePathState.ABSENT)
|
||||
with mock.patch.object(runtime_security.os, 'scandir', side_effect=OSError('storage offline')):
|
||||
unknown = runtime_security.inspect_private_relative_path(
|
||||
root, 'ready/aa/' + ('a' * 32) + '.trb',
|
||||
)
|
||||
self.assertEqual(unknown.state, runtime_security.PrivatePathState.UNKNOWN)
|
||||
self.assertIn('storage offline', unknown.detail)
|
||||
|
||||
def test_refund_retains_claim_when_partial_unlink_is_unconfirmed(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
value = reservation()
|
||||
from result_bundle import bundle_partial_path, ensure_bundle_reservation_paths
|
||||
|
||||
ensure_bundle_reservation_paths(root, value)
|
||||
partial = bundle_partial_path(root, value.bundle_id, value.reservation_token)
|
||||
Path(partial).write_bytes(b'partial')
|
||||
harden_private_file(partial)
|
||||
|
||||
class DB:
|
||||
called = False
|
||||
|
||||
def refund_uncommitted_reservation(self, *args, **kwargs):
|
||||
self.called = True
|
||||
return True
|
||||
|
||||
db = DB()
|
||||
claim = {
|
||||
'reservation_id': value.reservation_id,
|
||||
'reservation_token': value.reservation_token,
|
||||
'bundle_id': value.bundle_id,
|
||||
'ready_relative_path': value.ready_path,
|
||||
}
|
||||
with mock.patch.object(
|
||||
console_runner, 'durable_unlink', side_effect=OSError('unlink unavailable'),
|
||||
):
|
||||
refunded = console_runner.refund_v2_claim_after_no_handoff(
|
||||
db, root, claim, {}, 'fixture',
|
||||
)
|
||||
self.assertFalse(refunded)
|
||||
self.assertFalse(db.called)
|
||||
self.assertTrue(os.path.isfile(partial))
|
||||
|
||||
def test_terminal_reconciliation_revisits_unknown_artifact_state(self):
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
root = self.private_root(temp_dir)
|
||||
|
||||
class DB:
|
||||
deleted = []
|
||||
deferred = []
|
||||
|
||||
@staticmethod
|
||||
def bundle_terminal_temp_artifacts(_limit):
|
||||
return [{'id': 9, 'relative_path': 'tmp/aa/fixture.partial'}]
|
||||
|
||||
def mark_pipeline_artifact_deleted(self, artifact_id):
|
||||
self.deleted.append(artifact_id)
|
||||
return True
|
||||
|
||||
def defer_pipeline_artifact_cleanup(self, artifact_id, error):
|
||||
self.deferred.append((artifact_id, error))
|
||||
return True
|
||||
|
||||
db = DB()
|
||||
worker = ResultIngester(db, root, 'fixture')
|
||||
unknown = runtime_security.PrivatePathInspection(
|
||||
runtime_security.PrivatePathState.UNKNOWN, 'fixture', 'storage unavailable',
|
||||
)
|
||||
absent = runtime_security.PrivatePathInspection(
|
||||
runtime_security.PrivatePathState.ABSENT, 'fixture',
|
||||
)
|
||||
with mock.patch.object(worker, '_inspect', side_effect=[unknown, absent]):
|
||||
worker.reconcile_terminal_artifacts(max_pages=1)
|
||||
self.assertEqual(db.deleted, [])
|
||||
self.assertEqual(db.deferred[0][0], 9)
|
||||
worker.reconcile_terminal_artifacts(max_pages=1)
|
||||
self.assertEqual(db.deleted, [9])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user