G7 공개 캡처 경로 보정

This commit is contained in:
Yun Chan 2026-08-09 22:36:25 +09:00
parent c743e9ccb9
commit b34623f3db
2 changed files with 45 additions and 0 deletions

View file

@ -27,6 +27,10 @@ SNAPSHOT_SCHEMA_VERSION = "vignette.voice-runtime.v1"
MAX_SAMPLES = 7_200 MAX_SAMPLES = 7_200
MAX_INTERVAL_SECONDS = 60.0 MAX_INTERVAL_SECONDS = 60.0
MAX_CAPTURE_WINDOW_SECONDS = 7_200.0 MAX_CAPTURE_WINDOW_SECONDS = 7_200.0
_BROWSER_UA = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/140.0 Safari/537.36"
)
class EvidenceFailure(RuntimeError): class EvidenceFailure(RuntimeError):
@ -158,6 +162,9 @@ def fetch_snapshot(
headers={ headers={
"Accept": "application/json", "Accept": "application/json",
"Cookie": f"{cookie_name}={cookie_value}", "Cookie": f"{cookie_name}={cookie_value}",
# Cloudflare는 Python urllib 기본 User-Agent를 공개 API 도달 전에
# 거부한다. 공개 배포 probe와 같은 브라우저 호환 전송 경계를 쓴다.
"User-Agent": _BROWSER_UA,
}, },
method="GET", method="GET",
) )

View file

@ -4,9 +4,11 @@ from __future__ import annotations
import copy import copy
import importlib.util import importlib.util
import json
import sys import sys
import unittest import unittest
from pathlib import Path from pathlib import Path
from unittest import mock
RUNNER_PATH = Path(__file__).with_name("capture-g7-runtime-evidence.py") RUNNER_PATH = Path(__file__).with_name("capture-g7-runtime-evidence.py")
@ -103,6 +105,42 @@ class G7RuntimeEvidenceTests(unittest.TestCase):
self.assertNotIn("secret", serialized) self.assertNotIn("secret", serialized)
self.assertFalse(evidence["cookie_value_logged"]) self.assertFalse(evidence["cookie_value_logged"])
def test_public_fetch_uses_browser_compatible_user_agent(self) -> None:
captured_request = None
class Response:
status = 200
def __enter__(self):
return self
def __exit__(self, *_args):
return False
def read(self, _limit):
return json.dumps(snapshot()).encode("utf-8")
def urlopen(request, **_kwargs):
nonlocal captured_request
captured_request = request
return Response()
with mock.patch.object(self.runner.urllib.request, "urlopen", urlopen):
result = self.runner.fetch_snapshot(
url="https://api.example.test/admin/voice-runtime",
cookie_name="sid",
cookie_value="secret",
timeout_seconds=5.0,
)
self.assertEqual("vignette.voice-runtime.v1", result["schema_version"])
self.assertIsNotNone(captured_request)
headers = {
name.lower(): value for name, value in captured_request.header_items()
}
self.assertEqual(self.runner._BROWSER_UA, headers["user-agent"])
self.assertEqual("application/json", headers["accept"])
def test_worker_drift_fails_closed(self) -> None: def test_worker_drift_fails_closed(self) -> None:
first = snapshot() first = snapshot()
second = copy.deepcopy(first) second = copy.deepcopy(first)