G7 증명과 G8 clean-head 승격 준비
This commit is contained in:
parent
94c681d450
commit
5221f79e3f
52 changed files with 6876 additions and 506 deletions
|
|
@ -2,9 +2,9 @@
|
|||
"""Fail-closed validator for the complete G7 external evidence gate.
|
||||
|
||||
All four artifacts are required: authenticated public physical-microphone soak,
|
||||
process-local voice runtime sampling, pinned Linux topology sampling, and an
|
||||
independent human-held-out voice-gain pack. No artifact may contain raw audio or
|
||||
transcripts, and synthetic evidence cannot satisfy this gate.
|
||||
process-local voice runtime sampling, pinned deployment topology sampling, and
|
||||
an independent human-held-out voice-gain pack. No artifact may contain raw audio
|
||||
or transcripts, and synthetic evidence cannot satisfy this gate.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -13,6 +13,7 @@ import argparse
|
|||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import ntpath
|
||||
import sys
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
|
@ -29,6 +30,7 @@ from app.services.g7_voice_gain_evidence import evaluate_human_voice_gain # noq
|
|||
|
||||
|
||||
MINIMUM_SOAK_SECONDS = 3_000.0
|
||||
PINNED_PSUTIL_VERSION = "6.1.1"
|
||||
|
||||
# 이 게이트는 특정 벤더가 아니라 **운영하기로 결정한 provider** 를 강제한다.
|
||||
# 2026-08-08 소유자 결정: STT 는 노트북 상주 faster-whisper(`local_whisper`),
|
||||
|
|
@ -82,6 +84,15 @@ def _iso(value: object) -> datetime | None:
|
|||
return parsed.astimezone(UTC)
|
||||
|
||||
|
||||
def _lower_hex(value: object, lengths: set[int]) -> bool:
|
||||
return (
|
||||
isinstance(value, str)
|
||||
and len(value) in lengths
|
||||
and value == value.lower()
|
||||
and all(character in "0123456789abcdef" for character in value)
|
||||
)
|
||||
|
||||
|
||||
def validate_public_soak(payload: dict[str, Any], errors: list[str]) -> None:
|
||||
prefix = "voice_soak"
|
||||
_require(
|
||||
|
|
@ -327,57 +338,11 @@ def validate_runtime(payload: dict[str, Any], errors: list[str]) -> None:
|
|||
_require(errors, counters.get(field) == 0, f"{prefix}:{field}")
|
||||
|
||||
|
||||
def validate_topology(payload: dict[str, Any], errors: list[str]) -> None:
|
||||
def _validate_linux_compose_topology(
|
||||
payload: dict[str, Any],
|
||||
errors: list[str],
|
||||
) -> None:
|
||||
prefix = "topology"
|
||||
_require(
|
||||
errors,
|
||||
payload.get("schema_version") == "vignette.g7-topology-evidence.v1",
|
||||
f"{prefix}:schema",
|
||||
)
|
||||
_require(errors, payload.get("status") == "passed", f"{prefix}:status")
|
||||
scope = payload.get("scope")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(scope, dict) and scope.get("metadata_only") is True,
|
||||
f"{prefix}:privacy",
|
||||
)
|
||||
if isinstance(scope, dict):
|
||||
for field in (
|
||||
"raw_command_output_retained",
|
||||
"socket_endpoints_retained",
|
||||
"request_payloads_retained",
|
||||
"audio_retained",
|
||||
"transcripts_retained",
|
||||
):
|
||||
_require(errors, scope.get(field) is False, f"{prefix}:{field}")
|
||||
edge = scope.get("cloudflare_edge")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(edge, dict)
|
||||
and edge.get("internal_queue_measured") is False
|
||||
and edge.get("evidence_boundary") == "separate_external_artifact_required",
|
||||
f"{prefix}:edge_boundary",
|
||||
)
|
||||
requested = payload.get("requested")
|
||||
completed = payload.get("samples_completed")
|
||||
_require(errors, isinstance(requested, dict), f"{prefix}:requested")
|
||||
if isinstance(requested, dict):
|
||||
sample_count = requested.get("samples")
|
||||
interval = _number(requested.get("interval_seconds"))
|
||||
_require(
|
||||
errors,
|
||||
isinstance(sample_count, int)
|
||||
and sample_count == completed
|
||||
and sample_count > 1,
|
||||
f"{prefix}:sample_count",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
interval is not None
|
||||
and isinstance(completed, int)
|
||||
and (completed - 1) * interval >= MINIMUM_SOAK_SECONDS,
|
||||
f"{prefix}:coverage",
|
||||
)
|
||||
targets = payload.get("targets")
|
||||
_require(
|
||||
errors,
|
||||
|
|
@ -442,6 +407,499 @@ def validate_topology(payload: dict[str, Any], errors: list[str]) -> None:
|
|||
and (_number(host_tcp.get("connections_max")) or 0) > 0,
|
||||
f"{prefix}:host_tcp",
|
||||
)
|
||||
|
||||
|
||||
def _validate_windows_topology(
|
||||
payload: dict[str, Any],
|
||||
errors: list[str],
|
||||
) -> None:
|
||||
prefix = "topology"
|
||||
requested = payload.get("requested")
|
||||
_require(errors, isinstance(requested, dict), f"{prefix}:windows_requested")
|
||||
if not isinstance(requested, dict):
|
||||
return
|
||||
expected_git_sha = requested.get("git_sha")
|
||||
_require(
|
||||
errors,
|
||||
_lower_hex(expected_git_sha, {40, 64}),
|
||||
f"{prefix}:windows_git_sha",
|
||||
)
|
||||
source_pin = requested.get("source_pin")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(source_pin, dict),
|
||||
f"{prefix}:windows_source_pin",
|
||||
)
|
||||
expected_tree_sha = (
|
||||
source_pin.get("git_tree_sha") if isinstance(source_pin, dict) else None
|
||||
)
|
||||
expected_scripts = (
|
||||
source_pin.get("script_sha256") if isinstance(source_pin, dict) else None
|
||||
)
|
||||
expected_dependencies = (
|
||||
source_pin.get("runtime_dependencies") if isinstance(source_pin, dict) else None
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
_lower_hex(expected_tree_sha, {40, 64}),
|
||||
f"{prefix}:windows_git_tree_sha_pin",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(expected_scripts, dict)
|
||||
and set(expected_scripts) == {"runner", "collector", "checker"}
|
||||
and all(_lower_hex(value, {64}) for value in expected_scripts.values()),
|
||||
f"{prefix}:windows_script_sha256_pin",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(expected_dependencies, dict)
|
||||
and expected_dependencies == {"psutil": PINNED_PSUTIL_VERSION},
|
||||
f"{prefix}:windows_psutil_version_pin",
|
||||
)
|
||||
|
||||
source_provenance = payload.get("source_provenance")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(source_provenance, dict),
|
||||
f"{prefix}:windows_source_provenance",
|
||||
)
|
||||
if isinstance(source_provenance, dict):
|
||||
observed_scripts = source_provenance.get("script_sha256")
|
||||
observed_dependencies = source_provenance.get("runtime_dependencies")
|
||||
_require(
|
||||
errors,
|
||||
source_provenance.get("detached_head") is True,
|
||||
f"{prefix}:windows_detached_head",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
source_provenance.get("tracked_clean") is True,
|
||||
f"{prefix}:windows_tracked_clean",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
source_provenance.get("git_sha") == expected_git_sha,
|
||||
f"{prefix}:windows_source_git_sha",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
source_provenance.get("git_tree_sha") == expected_tree_sha,
|
||||
f"{prefix}:windows_source_git_tree_sha",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(observed_scripts, dict)
|
||||
and isinstance(expected_scripts, dict)
|
||||
and observed_scripts == expected_scripts,
|
||||
f"{prefix}:windows_source_script_sha256",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(observed_dependencies, dict)
|
||||
and isinstance(expected_dependencies, dict)
|
||||
and observed_dependencies == expected_dependencies,
|
||||
f"{prefix}:windows_source_psutil_version",
|
||||
)
|
||||
repo_root = requested.get("repo_root")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(repo_root, str) and ntpath.isabs(repo_root),
|
||||
f"{prefix}:windows_repo_root",
|
||||
)
|
||||
api_listen_port = requested.get("api_listen_port")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(api_listen_port, int)
|
||||
and not isinstance(api_listen_port, bool)
|
||||
and 1 <= api_listen_port <= 65_535,
|
||||
f"{prefix}:windows_api_listen_port",
|
||||
)
|
||||
requested_roles = requested.get("roles")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(requested_roles, dict)
|
||||
and set(requested_roles) == {"api", "cloudflared"},
|
||||
f"{prefix}:windows_requested_roles",
|
||||
)
|
||||
targets = payload.get("targets")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(targets, dict) and set(targets) == {"api", "cloudflared"},
|
||||
f"{prefix}:targets",
|
||||
)
|
||||
capture_started = _iso(payload.get("started_at_utc"))
|
||||
target_pids: list[int] = []
|
||||
if isinstance(targets, dict) and isinstance(requested_roles, dict):
|
||||
for role in ("api", "cloudflared"):
|
||||
target = targets.get(role)
|
||||
expectation = requested_roles.get(role)
|
||||
_require(errors, isinstance(target, dict), f"{prefix}:{role}_target")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(expectation, dict),
|
||||
f"{prefix}:{role}_expectation",
|
||||
)
|
||||
if not isinstance(target, dict) or not isinstance(expectation, dict):
|
||||
continue
|
||||
_require(
|
||||
errors,
|
||||
target.get("role") == role,
|
||||
f"{prefix}:{role}_role",
|
||||
)
|
||||
pid = target.get("pid")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(pid, int) and not isinstance(pid, bool) and pid > 0,
|
||||
f"{prefix}:{role}_pid",
|
||||
)
|
||||
if isinstance(pid, int) and not isinstance(pid, bool):
|
||||
target_pids.append(pid)
|
||||
_require(
|
||||
errors,
|
||||
pid == expectation.get("pid"),
|
||||
f"{prefix}:{role}_pid_pin",
|
||||
)
|
||||
executable_sha256 = target.get("executable_sha256")
|
||||
_require(
|
||||
errors,
|
||||
_lower_hex(executable_sha256, {64})
|
||||
and executable_sha256 == expectation.get("expected_executable_sha256"),
|
||||
f"{prefix}:{role}_executable_sha256",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
_lower_hex(target.get("command_line_sha256"), {64}),
|
||||
f"{prefix}:{role}_command_line_sha256",
|
||||
)
|
||||
executable_name = target.get("executable_name")
|
||||
expected_name = expectation.get("expected_executable_name")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(executable_name, str)
|
||||
and isinstance(expected_name, str)
|
||||
and executable_name.casefold() == expected_name.casefold(),
|
||||
f"{prefix}:{role}_executable_name",
|
||||
)
|
||||
cwd = target.get("cwd")
|
||||
expected_cwd = expectation.get("expected_cwd")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(cwd, str)
|
||||
and isinstance(expected_cwd, str)
|
||||
and ntpath.normcase(ntpath.normpath(cwd))
|
||||
== ntpath.normcase(ntpath.normpath(expected_cwd)),
|
||||
f"{prefix}:{role}_cwd",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
target.get("git_sha") == expected_git_sha,
|
||||
f"{prefix}:{role}_git_sha",
|
||||
)
|
||||
target_started = _iso(target.get("started_at"))
|
||||
_require(
|
||||
errors,
|
||||
capture_started is not None
|
||||
and target_started is not None
|
||||
and target_started <= capture_started,
|
||||
f"{prefix}:{role}_process_start",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
len(target_pids) == 2 and len(set(target_pids)) == 2,
|
||||
f"{prefix}:windows_distinct_pids",
|
||||
)
|
||||
|
||||
completed = payload.get("samples_completed")
|
||||
samples = payload.get("samples")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(samples, list)
|
||||
and isinstance(completed, int)
|
||||
and len(samples) == completed,
|
||||
f"{prefix}:windows_samples",
|
||||
)
|
||||
capture_ended = _iso(payload.get("ended_at_utc"))
|
||||
metric_values: dict[str, dict[str, list[float]]] = {
|
||||
role: {
|
||||
field: []
|
||||
for field in (
|
||||
"rss_bytes",
|
||||
"peak_rss_bytes",
|
||||
"cpu_time_seconds",
|
||||
"cpu_percent",
|
||||
"handles",
|
||||
"threads",
|
||||
)
|
||||
}
|
||||
for role in ("api", "cloudflared")
|
||||
}
|
||||
tcp_values: dict[str, dict[str, list[int]]] = {
|
||||
role: {field: [] for field in ("connections", "established", "listeners")}
|
||||
for role in ("api", "cloudflared")
|
||||
}
|
||||
listener_owned: list[int] = []
|
||||
listener_conflicts: list[int] = []
|
||||
host_connections: list[int] = []
|
||||
if isinstance(samples, list):
|
||||
previous_cpu: dict[str, float] = {}
|
||||
for index, sample in enumerate(samples, start=1):
|
||||
if not isinstance(sample, dict):
|
||||
errors.append(f"{prefix}:windows_sample_shape")
|
||||
continue
|
||||
_require(
|
||||
errors,
|
||||
sample.get("sequence") == index,
|
||||
f"{prefix}:windows_sample_sequence",
|
||||
)
|
||||
observed_at = _iso(sample.get("observed_at_utc"))
|
||||
_require(
|
||||
errors,
|
||||
capture_started is not None
|
||||
and capture_ended is not None
|
||||
and observed_at is not None
|
||||
and capture_started <= observed_at <= capture_ended,
|
||||
f"{prefix}:windows_sample_time",
|
||||
)
|
||||
processes = sample.get("processes")
|
||||
process_tcp = sample.get("process_tcp")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(processes, dict)
|
||||
and set(processes) == {"api", "cloudflared"},
|
||||
f"{prefix}:windows_sample_processes",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(process_tcp, dict)
|
||||
and set(process_tcp) == {"api", "cloudflared"},
|
||||
f"{prefix}:windows_sample_process_tcp",
|
||||
)
|
||||
for role in ("api", "cloudflared"):
|
||||
metrics = processes.get(role) if isinstance(processes, dict) else None
|
||||
tcp = process_tcp.get(role) if isinstance(process_tcp, dict) else None
|
||||
_require(
|
||||
errors,
|
||||
isinstance(metrics, dict),
|
||||
f"{prefix}:{role}_sample_metrics",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(tcp, dict),
|
||||
f"{prefix}:{role}_sample_tcp",
|
||||
)
|
||||
if isinstance(metrics, dict):
|
||||
for field in metric_values[role]:
|
||||
numeric = _number(metrics.get(field))
|
||||
positive = field != "cpu_percent"
|
||||
_require(
|
||||
errors,
|
||||
numeric is not None
|
||||
and numeric >= 0
|
||||
and (not positive or numeric > 0),
|
||||
f"{prefix}:{role}_{field}_sample",
|
||||
)
|
||||
if numeric is not None:
|
||||
metric_values[role][field].append(numeric)
|
||||
cpu_time = _number(metrics.get("cpu_time_seconds"))
|
||||
if cpu_time is not None:
|
||||
_require(
|
||||
errors,
|
||||
cpu_time >= previous_cpu.get(role, 0.0),
|
||||
f"{prefix}:{role}_cpu_time_monotonic",
|
||||
)
|
||||
previous_cpu[role] = cpu_time
|
||||
if isinstance(tcp, dict):
|
||||
for field in tcp_values[role]:
|
||||
value = tcp.get(field)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(value, int)
|
||||
and not isinstance(value, bool)
|
||||
and value >= 0,
|
||||
f"{prefix}:{role}_tcp_{field}_sample",
|
||||
)
|
||||
if isinstance(value, int) and not isinstance(value, bool):
|
||||
tcp_values[role][field].append(value)
|
||||
if role == "api":
|
||||
_require(
|
||||
errors,
|
||||
(_number(tcp.get("listeners")) or 0) > 0,
|
||||
f"{prefix}:api_listener_sample",
|
||||
)
|
||||
else:
|
||||
_require(
|
||||
errors,
|
||||
(_number(tcp.get("established")) or 0) > 0,
|
||||
f"{prefix}:cloudflared_tunnel_sample",
|
||||
)
|
||||
listener = sample.get("api_listener")
|
||||
host_tcp = sample.get("host_tcp")
|
||||
_require(errors, isinstance(listener, dict), f"{prefix}:listener_sample")
|
||||
_require(errors, isinstance(host_tcp, dict), f"{prefix}:host_tcp_sample")
|
||||
if isinstance(listener, dict):
|
||||
owned = listener.get("owned_listener_count")
|
||||
conflicts = listener.get("conflicting_listener_count")
|
||||
_require(
|
||||
errors,
|
||||
listener.get("port") == api_listen_port,
|
||||
f"{prefix}:listener_port_sample",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(owned, int) and not isinstance(owned, bool) and owned > 0,
|
||||
f"{prefix}:listener_owner_sample",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(conflicts, int)
|
||||
and not isinstance(conflicts, bool)
|
||||
and conflicts == 0,
|
||||
f"{prefix}:listener_conflict_sample",
|
||||
)
|
||||
if isinstance(owned, int) and not isinstance(owned, bool):
|
||||
listener_owned.append(owned)
|
||||
if isinstance(conflicts, int) and not isinstance(conflicts, bool):
|
||||
listener_conflicts.append(conflicts)
|
||||
if isinstance(host_tcp, dict):
|
||||
connections = host_tcp.get("connections")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(connections, int)
|
||||
and not isinstance(connections, bool)
|
||||
and connections > 0,
|
||||
f"{prefix}:host_tcp_connections_sample",
|
||||
)
|
||||
if isinstance(connections, int) and not isinstance(connections, bool):
|
||||
host_connections.append(connections)
|
||||
|
||||
summary = payload.get("summary")
|
||||
_require(errors, isinstance(summary, dict), f"{prefix}:summary")
|
||||
if isinstance(summary, dict):
|
||||
process_summary = summary.get("processes")
|
||||
listener_summary = summary.get("api_listener")
|
||||
host_summary = summary.get("host_tcp")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(process_summary, dict)
|
||||
and set(process_summary) == {"api", "cloudflared"},
|
||||
f"{prefix}:windows_process_summary",
|
||||
)
|
||||
if isinstance(process_summary, dict):
|
||||
for role in ("api", "cloudflared"):
|
||||
values = process_summary.get(role)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(values, dict),
|
||||
f"{prefix}:{role}_summary",
|
||||
)
|
||||
if not isinstance(values, dict):
|
||||
continue
|
||||
for field, observations in metric_values[role].items():
|
||||
_require(
|
||||
errors,
|
||||
bool(observations)
|
||||
and _number(values.get(f"{field}_max")) == max(observations),
|
||||
f"{prefix}:{role}_{field}_max",
|
||||
)
|
||||
for field, observations in tcp_values[role].items():
|
||||
_require(
|
||||
errors,
|
||||
bool(observations)
|
||||
and _number(values.get(f"tcp_{field}_max"))
|
||||
== max(observations),
|
||||
f"{prefix}:{role}_tcp_{field}_max",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(listener_summary, dict)
|
||||
and listener_summary.get("port") == api_listen_port
|
||||
and bool(listener_owned)
|
||||
and listener_summary.get("owned_listener_count_min")
|
||||
== min(listener_owned)
|
||||
and bool(listener_conflicts)
|
||||
and listener_summary.get("conflicting_listener_count_max")
|
||||
== max(listener_conflicts),
|
||||
f"{prefix}:windows_listener_summary",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
isinstance(host_summary, dict)
|
||||
and bool(host_connections)
|
||||
and host_summary.get("connections_max") == max(host_connections),
|
||||
f"{prefix}:host_tcp",
|
||||
)
|
||||
|
||||
|
||||
def validate_topology(payload: dict[str, Any], errors: list[str]) -> None:
|
||||
prefix = "topology"
|
||||
_require(
|
||||
errors,
|
||||
payload.get("schema_version") == "vignette.g7-topology-evidence.v1",
|
||||
f"{prefix}:schema",
|
||||
)
|
||||
_require(errors, payload.get("status") == "passed", f"{prefix}:status")
|
||||
scope = payload.get("scope")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(scope, dict) and scope.get("metadata_only") is True,
|
||||
f"{prefix}:privacy",
|
||||
)
|
||||
if isinstance(scope, dict):
|
||||
for field in (
|
||||
"raw_command_output_retained",
|
||||
"socket_endpoints_retained",
|
||||
"request_payloads_retained",
|
||||
"audio_retained",
|
||||
"transcripts_retained",
|
||||
):
|
||||
_require(errors, scope.get(field) is False, f"{prefix}:{field}")
|
||||
edge = scope.get("cloudflare_edge")
|
||||
_require(
|
||||
errors,
|
||||
isinstance(edge, dict)
|
||||
and edge.get("internal_queue_measured") is False
|
||||
and edge.get("evidence_boundary") == "separate_external_artifact_required",
|
||||
f"{prefix}:edge_boundary",
|
||||
)
|
||||
requested = payload.get("requested")
|
||||
completed = payload.get("samples_completed")
|
||||
_require(errors, isinstance(requested, dict), f"{prefix}:requested")
|
||||
if isinstance(requested, dict):
|
||||
sample_count = requested.get("samples")
|
||||
interval = _number(requested.get("interval_seconds"))
|
||||
_require(
|
||||
errors,
|
||||
isinstance(sample_count, int)
|
||||
and sample_count == completed
|
||||
and sample_count > 1,
|
||||
f"{prefix}:sample_count",
|
||||
)
|
||||
_require(
|
||||
errors,
|
||||
interval is not None
|
||||
and isinstance(completed, int)
|
||||
and (completed - 1) * interval >= MINIMUM_SOAK_SECONDS,
|
||||
f"{prefix}:coverage",
|
||||
)
|
||||
topology_mode = payload.get("topology_mode")
|
||||
_require(
|
||||
errors,
|
||||
topology_mode in (None, "linux_compose", "windows_host"),
|
||||
f"{prefix}:mode",
|
||||
)
|
||||
if topology_mode == "windows_host":
|
||||
_require(
|
||||
errors,
|
||||
isinstance(scope, dict)
|
||||
and scope.get("topology_boundary")
|
||||
== "windows_host_api_and_cloudflared_processes"
|
||||
and scope.get("configured_listener_port_retained") is True,
|
||||
f"{prefix}:windows_scope",
|
||||
)
|
||||
_validate_windows_topology(payload, errors)
|
||||
elif topology_mode in (None, "linux_compose"):
|
||||
_validate_linux_compose_topology(payload, errors)
|
||||
_require(errors, payload.get("failure_type") is None, f"{prefix}:failure")
|
||||
|
||||
|
||||
|
|
@ -489,11 +947,20 @@ def validate_binding(
|
|||
else:
|
||||
windows.append((start, end))
|
||||
if len(windows) == 3:
|
||||
overlap_started = max(start for start, _ in windows)
|
||||
overlap_ended = min(end for _, end in windows)
|
||||
_require(
|
||||
errors,
|
||||
max(start for start, _ in windows) <= min(end for _, end in windows),
|
||||
overlap_started <= overlap_ended,
|
||||
"binding:no_concurrent_overlap",
|
||||
)
|
||||
if overlap_started <= overlap_ended:
|
||||
_require(
|
||||
errors,
|
||||
(overlap_ended - overlap_started).total_seconds()
|
||||
>= MINIMUM_SOAK_SECONDS,
|
||||
"binding:concurrent_overlap_below_3000_seconds",
|
||||
)
|
||||
|
||||
|
||||
def artifact_sha256(path: Path) -> str:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue