diff --git a/scripts/run-wp7-04-validation.mjs b/scripts/run-wp7-04-validation.mjs index c06d80d..4f2d93d 100644 --- a/scripts/run-wp7-04-validation.mjs +++ b/scripts/run-wp7-04-validation.mjs @@ -36,6 +36,7 @@ function readConsoleReview() { auto_scaling: "not_verified", paid_fallback: "not_verified", qps: "not_verified", + qps_limit_per_second: null, reviewed_at: null, security_restriction: "not_verified", service_binding: "not_verified", @@ -45,15 +46,18 @@ function readConsoleReview() { if (!raw) return fallback; try { const parsed = JSON.parse(raw); - const requiredKeys = ["allowlist", "auto_scaling", "paid_fallback", "qps", "reviewed_at", "security_restriction", "service_binding", "source"]; + const requiredKeys = ["allowlist", "auto_scaling", "paid_fallback", "qps", "qps_limit_per_second", "reviewed_at", "security_restriction", "service_binding", "source"]; if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || Object.keys(parsed).sort().join("|") !== requiredKeys.sort().join("|")) { return { ...fallback, source: "invalid_input", valid: false }; } const statusFields = ["allowlist", "qps", "security_restriction", "service_binding"]; const statusValid = statusFields.every((field) => ["failed", "not_verified", "passed"].includes(parsed[field])); const disabledFieldsValid = ["auto_scaling", "paid_fallback"].every((field) => ["disabled", "not_verified"].includes(parsed[field])); + const qpsLimitValid = parsed.qps === "passed" + ? Number.isSafeInteger(parsed.qps_limit_per_second) && parsed.qps_limit_per_second >= 1 && parsed.qps_limit_per_second <= 1_000 + : parsed.qps_limit_per_second === null; const reviewedAtValid = typeof parsed.reviewed_at === "string" && Number.isFinite(Date.parse(parsed.reviewed_at)); - if (!statusValid || !disabledFieldsValid || !reviewedAtValid || parsed.source !== "amap_console_manual_review") { + if (!statusValid || !disabledFieldsValid || !qpsLimitValid || !reviewedAtValid || parsed.source !== "amap_console_manual_review") { return { ...fallback, source: "invalid_input", valid: false }; } return { ...parsed, valid: true }; @@ -90,17 +94,23 @@ const serviceRegression = build.exit_code === 0 const localHardStop = build.exit_code === 0 ? run("pnpm", ["exec", "vitest", "run", "tests/api/wp7-04-amap-release-gate.test.ts"]) : skipped("pnpm exec vitest run tests/api/wp7-04-amap-release-gate.test.ts"); +const productionAdapter = build.exit_code === 0 + ? run("pnpm", ["exec", "vitest", "run", "tests/api/wp7-04-amap-production-adapter.test.ts"]) + : skipped("pnpm exec vitest run tests/api/wp7-04-amap-production-adapter.test.ts"); const consentFlow = build.exit_code === 0 ? run("pnpm", ["exec", "vitest", "run", "tests/unit/wp4-04-palette-dynamic.test.ts"]) : skipped("pnpm exec vitest run tests/unit/wp4-04-palette-dynamic.test.ts"); const supervisorBuild = run("dotnet", ["build", "supervisor/Dada.Supervisor/Dada.Supervisor.csproj", "--configuration", "Release"]); +const supervisorSecurity = supervisorBuild.exit_code === 0 + ? run("dotnet", ["run", "--project", "supervisor/Dada.Supervisor.Tests/Dada.Supervisor.Tests.csproj", "--configuration", "Release"]) + : skipped("dotnet run --project supervisor/Dada.Supervisor.Tests/Dada.Supervisor.Tests.csproj --configuration Release"); const supervisorExe = resolve("supervisor", "Dada.Supervisor", "bin", "Release", "net8.0-windows", "Dada.Supervisor.exe"); const external = supervisorBuild.exit_code === 0 && existsSync(supervisorExe) ? run(supervisorExe, ["secrets", "probe", "api-amap"], "Dada.Supervisor.exe secrets probe api-amap") : skipped("Dada.Supervisor.exe secrets probe api-amap"); const trace = run("pnpm", ["validate:tdd-trace"]); const security = run("pnpm", ["test:security"]); -const commands = [build, locationRegression, serviceRegression, localHardStop, consentFlow, supervisorBuild, external, trace, security] +const commands = [build, locationRegression, serviceRegression, localHardStop, productionAdapter, consentFlow, supervisorBuild, supervisorSecurity, external, trace, security] .map(({ command, exit_code, finished_at, started_at }) => ({ command, exit_code, finished_at, started_at })); const parsedExternal = (() => { try { return JSON.parse(external.stdout.trim().split(/\r?\n/).at(-1) ?? ""); } catch { return {}; } @@ -111,9 +121,11 @@ const realCalls = Number.isSafeInteger(parsedExternal.real_calls) && parsedExter : 0; const probePassed = external.exit_code === 0 && safeProbeCode === "amap_probe_passed" && realCalls === 2; const hardStopPassed = localHardStop.exit_code === 0; +const productionAdapterPassed = productionAdapter.exit_code === 0; const consentPassed = consentFlow.exit_code === 0; +const supervisorSecurityPassed = supervisorSecurity.exit_code === 0; const regressionPassed = locationRegression.exit_code === 0 && serviceRegression.exit_code === 0; -const automatedPassed = build.exit_code === 0 && hardStopPassed && consentPassed && regressionPassed && trace.exit_code === 0 && security.exit_code === 0; +const automatedPassed = build.exit_code === 0 && hardStopPassed && productionAdapterPassed && consentPassed && supervisorSecurityPassed && regressionPassed && trace.exit_code === 0 && security.exit_code === 0; const manualReviewPassed = consoleReview.valid && consoleReview.service_binding === "passed" && consoleReview.qps === "passed" @@ -131,7 +143,9 @@ if (consoleReview.security_restriction !== "passed") requiredBeforeRelease.push( if (consoleReview.paid_fallback !== "disabled") requiredBeforeRelease.push("paid_fallback_disabled"); if (consoleReview.auto_scaling !== "disabled") requiredBeforeRelease.push("auto_scaling_disabled"); if (!hardStopPassed) requiredBeforeRelease.push("monthly_1000_hard_stop"); +if (!productionAdapterPassed) requiredBeforeRelease.push("production_amap_adapter"); if (!consentPassed) requiredBeforeRelease.push("dyn004_confirmation"); +if (!supervisorSecurityPassed) requiredBeforeRelease.push("controlled_probe_security"); const blocker = !automatedPassed ? "automated_regression_failed" @@ -153,11 +167,15 @@ const contractEvidence = { checks: { allowlist: consoleReview.allowlist === "passed" ? "manual_console_passed" : consoleReview.allowlist, auto_scaling: consoleReview.auto_scaling, + client_security_controls: productionAdapterPassed && supervisorSecurityPassed ? "automated_passed" : "failed", + controlled_probe_security: supervisorSecurityPassed ? "automated_passed" : "failed", dyn004_hard_stop: consentPassed ? "confirmation_contract_passed" : "failed", location_and_reverse_geocode: probePassed ? "real_probe_passed" : "not_verified", monthly_hard_limit_1000: hardStopPassed ? "local_pre_egress_passed" : "failed", paid_fallback: consoleReview.paid_fallback, + production_adapter: productionAdapterPassed ? "credential_channel_real_adapter_passed" : "failed", provider_qps: consoleReview.qps === "passed" ? "manual_console_passed" : consoleReview.qps, + provider_qps_limit_per_second: consoleReview.qps_limit_per_second, security_binding: consoleReview.security_restriction === "passed" ? "manual_console_passed" : consoleReview.security_restriction, service_binding: consoleReview.service_binding === "passed" ? "manual_console_passed" : consoleReview.service_binding, }, @@ -183,6 +201,7 @@ const manualReviewEvidence = { auto_scaling: consoleReview.auto_scaling, paid_fallback: consoleReview.paid_fallback, qps: consoleReview.qps, + qps_limit_per_second: consoleReview.qps_limit_per_second, security_restriction: consoleReview.security_restriction, service_binding: consoleReview.service_binding, }, @@ -221,7 +240,7 @@ const redactionEvidence = { forbidden_fields_present: !evidenceRedactionPassed, source_security_scan: security.exit_code === 0 ? "passed" : "failed", status: evidenceRedactionPassed && security.exit_code === 0 ? "passed" : "failed", - stored_fields: ["service", "status", "logical_limit", "manual_review_status"], + stored_fields: ["service", "status", "logical_limit", "manual_review_status", "qps_limit_per_second"], schema_version: "1.1", }; if (redactionEvidence.status !== "passed") {