fix(mcp): enforce exact replay sandbox contract

This commit is contained in:
ogt
2026-07-15 11:07:00 +08:00
parent 387f84132d
commit e2cd70501d
5 changed files with 140 additions and 24 deletions

View File

@@ -52,6 +52,27 @@ EXPECTED_PACKAGE_KEYS = {
"playwright@1.62.0-alpha-1783623505000",
"playwright-core@1.62.0-alpha-1783623505000",
}
EXPECTED_WORK_ITEM_ID = "MCP-REPLAY-PLAYWRIGHT-001"
EXPECTED_CANDIDATE_ID = "external.playwright-verifier"
EXPECTED_ADAPTER_ID = "playwright_public_accessibility_verifier_v1"
EXPECTED_OWNER_LANE = "awoooi_mcp_supply_chain"
EXPECTED_ARTIFACT_REPOSITORY = (
"registry.wooo.work/awoooi/mcp-artifacts/playwright-mcp@"
)
EXPECTED_ARTIFACT_RUNTIME_REPOSITORY = (
"192.168.0.110:5000/awoooi/mcp-artifacts/playwright-mcp@"
)
EXPECTED_RUNTIME_REPOSITORY = "registry.wooo.work/awoooi/ci-runner@"
EXPECTED_RUNTIME_ARGUMENTS = (
"--headless",
"--isolated",
"--allowed-origins",
"https://awoooi.wooo.work",
"--output-dir",
"/tmp/mcp-output",
"--executable-path",
"/nonexistent/browser",
)
class ReplayError(RuntimeError):
@@ -203,6 +224,13 @@ def load_policy(path: Path) -> ReplayPolicy:
raise ReplayError("replay_scope_invalid")
if payload.get("risk_class") != "high":
raise ReplayError("replay_risk_class_invalid")
if (
payload.get("work_item_id") != EXPECTED_WORK_ITEM_ID
or payload.get("candidate_id") != EXPECTED_CANDIDATE_ID
or payload.get("adapter_id") != EXPECTED_ADAPTER_ID
or payload.get("owner_lane") != EXPECTED_OWNER_LANE
):
raise ReplayError("replay_asset_identity_invalid")
expiry_text = _require_text(payload.get("expires_at"), "replay_expiry_invalid")
try:
@@ -220,6 +248,8 @@ def load_policy(path: Path) -> ReplayPolicy:
_require_text(payload.get(field), f"replay_{field}_invalid")
artifact = _require_dict(payload.get("artifact_source"), "artifact_source_invalid")
if artifact.get("audit_branch") != "mcp-artifact-receipts":
raise ReplayError("audit_branch_invalid")
audit_commit = _require_text(artifact.get("audit_commit"), "audit_commit_invalid")
if not GIT_SHA_PATTERN.fullmatch(audit_commit):
raise ReplayError("audit_commit_invalid")
@@ -235,32 +265,42 @@ def load_policy(path: Path) -> ReplayPolicy:
raise ReplayError("package_integrity_invalid")
normalized_integrities[key] = digest
artifact_ref = _validate_oci_ref(artifact.get("artifact_ref"), "artifact_ref_invalid")
artifact_runtime_ref = _validate_oci_ref(
artifact.get("artifact_runtime_ref"), "artifact_runtime_ref_invalid"
)
if not artifact_ref.startswith(EXPECTED_ARTIFACT_REPOSITORY):
raise ReplayError("artifact_repository_not_allowlisted")
if not artifact_runtime_ref.startswith(EXPECTED_ARTIFACT_RUNTIME_REPOSITORY):
raise ReplayError("artifact_runtime_repository_not_allowlisted")
runtime = _require_dict(payload.get("runtime"), "runtime_invalid")
runtime_image_ref = _validate_oci_ref(
runtime.get("image_ref"), "runtime_image_ref_invalid"
)
if not runtime_image_ref.startswith(EXPECTED_RUNTIME_REPOSITORY):
raise ReplayError("runtime_repository_not_allowlisted")
if (
runtime.get("network_mode") != "none"
or runtime.get("read_only_root") is not True
or runtime.get("no_new_privileges") is not True
or runtime.get("cap_drop") != ["ALL"]
or runtime.get("platform") != "linux/amd64"
or runtime.get("user") != "65534:65534"
or runtime.get("pids_limit") != 64
or runtime.get("memory_limit") != "384m"
or runtime.get("cpu_limit") != "0.5"
or runtime.get("tmpfs") != "/tmp:rw,noexec,nosuid,size=32m"
or runtime.get("startup_timeout_seconds") != 20
or runtime.get("shutdown_timeout_seconds") != 5
):
raise ReplayError("runtime_isolation_invalid")
entrypoint = _require_text_list(runtime.get("entrypoint"), "entrypoint_invalid")
arguments = _require_text_list(runtime.get("arguments"), "arguments_invalid")
if entrypoint != ("node", "node_modules/@playwright/mcp/cli.js"):
raise ReplayError("entrypoint_not_allowlisted")
required_arguments = {
"--headless",
"--isolated",
"--allowed-origins",
"https://awoooi.wooo.work",
"--output-dir",
"/tmp/mcp-output",
"--executable-path",
"/nonexistent/browser",
}
if set(arguments) != required_arguments or len(arguments) != 8:
if arguments != EXPECTED_RUNTIME_ARGUMENTS:
raise ReplayError("arguments_not_allowlisted")
if runtime.get("user") != "65534:65534":
raise ReplayError("runtime_user_invalid")
protocol = _require_dict(
payload.get("protocol_contract"), "protocol_contract_invalid"
@@ -332,10 +372,8 @@ def load_policy(path: Path) -> ReplayPolicy:
artifact.get("verifier_receipt_sha256"),
"verifier_receipt_sha256_invalid",
),
artifact_ref=_validate_oci_ref(artifact.get("artifact_ref"), "artifact_ref_invalid"),
artifact_runtime_ref=_validate_oci_ref(
artifact.get("artifact_runtime_ref"), "artifact_runtime_ref_invalid"
),
artifact_ref=artifact_ref,
artifact_runtime_ref=artifact_runtime_ref,
bundle_manifest_sha256=_validate_sha256(
artifact.get("bundle_manifest_sha256"), "bundle_manifest_sha256_invalid"
),
@@ -343,9 +381,7 @@ def load_policy(path: Path) -> ReplayPolicy:
artifact.get("cyclonedx_sbom_sha256"), "cyclonedx_sbom_sha256_invalid"
),
package_integrities=normalized_integrities,
runtime_image_ref=_validate_oci_ref(
runtime.get("image_ref"), "runtime_image_ref_invalid"
),
runtime_image_ref=runtime_image_ref,
runtime_platform=_require_text(
runtime.get("platform"), "runtime_platform_invalid"
),