From 4d0f19a96838f52f71ccf1048003826302d95b9c Mon Sep 17 00:00:00 2001 From: Gio Della-Libera Date: Fri, 26 Jun 2026 18:28:35 -0700 Subject: [PATCH] test(policy): add config coverage report (#87081) Merged via squash. Prepared head SHA: 689734541b7ac6f893f7ff9a0aa044dcef0a16e9 Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com> Co-authored-by: giodl73-repo <235387111+giodl73-repo@users.noreply.github.com> Reviewed-by: @giodl73-repo --- package.json | 1 + scripts/check-policy-config-coverage.ts | 232 +++++++ scripts/lib/policy-config-coverage.jsonc | 761 +++++++++++++++++++++++ 3 files changed, 994 insertions(+) create mode 100644 scripts/check-policy-config-coverage.ts create mode 100644 scripts/lib/policy-config-coverage.jsonc diff --git a/package.json b/package.json index 9d77a9bae87f..41c456f14c77 100644 --- a/package.json +++ b/package.json @@ -1695,6 +1695,7 @@ "plugin-sdk:surface:check": "node --max-old-space-size=8192 scripts/plugin-sdk-surface-report.mjs --check", "plugin-sdk:sync-exports": "node scripts/sync-plugin-sdk-exports.mjs", "plugin-sdk:usage": "node --max-old-space-size=8192 --import tsx scripts/analyze-plugin-sdk-usage.ts", + "policy:config-coverage": "node --import tsx scripts/check-policy-config-coverage.ts", "plugins:boundary-report": "node --import tsx scripts/plugin-boundary-report.ts", "plugins:boundary-report:ci": "node --import tsx scripts/plugin-boundary-report.ts --summary --fail-on-cross-owner --fail-on-unclassified-unused-reserved --fail-on-eligible-compat", "plugins:boundary-report:json": "node --import tsx scripts/plugin-boundary-report.ts --json", diff --git a/scripts/check-policy-config-coverage.ts b/scripts/check-policy-config-coverage.ts new file mode 100644 index 000000000000..f48a566d0a81 --- /dev/null +++ b/scripts/check-policy-config-coverage.ts @@ -0,0 +1,232 @@ +#!/usr/bin/env node +import fs from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import JSON5 from "json5"; +import { + renderConfigDocBaselineArtifacts, + type ConfigDocBaselineEntry, +} from "../src/config/doc-baseline.js"; + +type ClassificationStatus = "observed" | "ignored" | "out-of-scope" | "deferred"; + +type CoverageClassification = { + readonly pattern: string; + readonly status: ClassificationStatus; + readonly area: string; + readonly policy?: string; + readonly reason: string; + readonly allowNoSchemaPath?: boolean; +}; + +type CoverageConfig = { + readonly monitored: readonly string[]; + readonly classifications: readonly CoverageClassification[]; +}; + +type ConfigDocBaseline = { + readonly coreEntries: readonly ConfigDocBaselineEntry[]; + readonly channelEntries: readonly ConfigDocBaselineEntry[]; + readonly pluginEntries: readonly ConfigDocBaselineEntry[]; +}; + +function flattenConfigDocBaselineEntries( + baseline: ConfigDocBaseline, +): readonly ConfigDocBaselineEntry[] { + return [...baseline.coreEntries, ...baseline.channelEntries, ...baseline.pluginEntries]; +} + +type ClassifiedEntry = { + readonly path: string; + readonly kind: ConfigDocBaselineEntry["kind"]; + readonly classification?: CoverageClassification; +}; + +type UnmatchedMonitoredPattern = { + readonly pattern: string; +}; + +const args = new Set(process.argv.slice(2)); +const json = args.has("--json"); +const check = args.has("--check"); +const showCovered = args.has("--show-covered"); + +if (args.has("--help")) { + console.log(`Usage: pnpm policy:config-coverage [--check] [--json] [--show-covered] + +Internal maintainer report for Policy config coverage. + +Default mode is report-only and exits 0 even when paths are unclassified. +Use --check when a policy maintainer intentionally wants unclassified or stale +coverage entries to fail locally.`); + process.exit(0); +} + +const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const configPath = path.join(repoRoot, "scripts/lib/policy-config-coverage.jsonc"); + +const config = JSON5.parse(await fs.readFile(configPath, "utf8")) as CoverageConfig; +const { baseline } = await renderConfigDocBaselineArtifacts(); +const monitoredEntries = flattenConfigDocBaselineEntries(baseline) + .filter((entry) => !entry.hasChildren) + .filter((entry) => matchesAny(config.monitored, entry.path)) + .toSorted((left, right) => left.path.localeCompare(right.path)); +const leafEntries = flattenConfigDocBaselineEntries(baseline).filter((entry) => !entry.hasChildren); +const unmatchedMonitored = config.monitored + .filter( + (pattern) => + !leafEntries.some((entry) => pathMatchesPattern(pattern, entry.path)) && + !config.classifications.some( + (item) => item.allowNoSchemaPath === true && pathMatchesPattern(item.pattern, pattern), + ), + ) + .map((pattern) => ({ pattern })) + .toSorted((left, right) => left.pattern.localeCompare(right.pattern)); + +const classified: ClassifiedEntry[] = monitoredEntries.map((entry) => ({ + path: entry.path, + kind: entry.kind, + classification: config.classifications.find((item) => + pathMatchesPattern(item.pattern, entry.path), + ), +})); +const unclassified = classified.filter((entry) => entry.classification === undefined); +const stale = config.classifications.filter( + (item) => + item.allowNoSchemaPath !== true && + !monitoredEntries.some((entry) => pathMatchesPattern(item.pattern, entry.path)), +); +const summaryCounts = summarize(classified); + +if (json) { + console.log( + JSON.stringify( + { + ok: unclassified.length === 0 && stale.length === 0 && unmatchedMonitored.length === 0, + monitoredPaths: monitoredEntries.length, + counts: summaryCounts, + unclassified, + unmatchedMonitored, + stale, + }, + null, + 2, + ), + ); +} else { + printTextReport({ + monitoredPaths: monitoredEntries.length, + counts: summaryCounts, + unclassified, + unmatchedMonitored, + stale, + classified, + }); +} + +if (check && (unclassified.length > 0 || stale.length > 0 || unmatchedMonitored.length > 0)) { + process.exit(1); +} + +function printTextReport(input: { + readonly monitoredPaths: number; + readonly counts: Record; + readonly unclassified: readonly ClassifiedEntry[]; + readonly unmatchedMonitored: readonly UnmatchedMonitoredPattern[]; + readonly stale: readonly CoverageClassification[]; + readonly classified: readonly ClassifiedEntry[]; +}): void { + console.log(`Policy config coverage: ${input.monitoredPaths} monitored config leaf paths`); + for (const [key, count] of Object.entries(input.counts).toSorted(([a], [b]) => + a.localeCompare(b), + )) { + console.log(` ${key}: ${count}`); + } + + if (input.unclassified.length > 0) { + console.log("\nUnclassified config paths:"); + for (const entry of input.unclassified) { + console.log(` - ${entry.path} (${entry.kind})`); + } + console.log( + "\nClassify each as observed, ignored, out-of-scope, or deferred in scripts/lib/policy-config-coverage.jsonc.", + ); + } else { + console.log("\nNo unclassified monitored config paths."); + } + + if (input.unmatchedMonitored.length > 0) { + console.log("\nMonitored patterns with no matching config paths:"); + for (const entry of input.unmatchedMonitored) { + console.log(` - ${entry.pattern}`); + } + } else { + console.log("\nNo monitored patterns without matching config paths."); + } + + if (input.stale.length > 0) { + console.log("\nStale coverage classifications:"); + for (const entry of input.stale) { + console.log(` - ${entry.pattern} (${entry.area}, ${entry.status})`); + } + } + + if (showCovered) { + console.log("\nCovered paths:"); + for (const entry of input.classified) { + const classification = entry.classification; + console.log( + ` - ${entry.path}: ${classification?.area ?? "unclassified"} / ${ + classification?.status ?? "unclassified" + }`, + ); + } + } +} + +function summarize(entries: readonly ClassifiedEntry[]): Record { + const counts: Record = {}; + for (const entry of entries) { + const key = + entry.classification === undefined + ? "unclassified" + : `${entry.classification.area}.${entry.classification.status}`; + counts[key] = (counts[key] ?? 0) + 1; + } + return counts; +} + +function matchesAny(patterns: readonly string[], value: string): boolean { + return patterns.some((pattern) => pathMatchesPattern(pattern, value)); +} + +function pathMatchesPattern(pattern: string, value: string): boolean { + const patternParts = pattern.split("."); + const valueParts = value.split("."); + return matchesParts(patternParts, valueParts); +} + +function matchesParts(patternParts: readonly string[], valueParts: readonly string[]): boolean { + if (patternParts.length === 0) { + return valueParts.length === 0; + } + const [head, ...tail] = patternParts; + if (head === "**") { + if (tail.length === 0) { + return true; + } + for (let index = 0; index <= valueParts.length; index += 1) { + if (matchesParts(tail, valueParts.slice(index))) { + return true; + } + } + return false; + } + if (valueParts.length === 0) { + return false; + } + if (head !== "*" && head !== valueParts[0]) { + return false; + } + return matchesParts(tail, valueParts.slice(1)); +} diff --git a/scripts/lib/policy-config-coverage.jsonc b/scripts/lib/policy-config-coverage.jsonc new file mode 100644 index 000000000000..b9a96605f993 --- /dev/null +++ b/scripts/lib/policy-config-coverage.jsonc @@ -0,0 +1,761 @@ +{ + // Internal maintainer inventory for `pnpm policy:config-coverage`. + // Keep this report-only by default: it helps policy maintainers notice config + // drift without making every config PR author update Policy. + "monitored": [ + "auth.profiles.*.mode", + "auth.profiles.*.provider", + "browser.ssrfPolicy.allowPrivateNetwork", + "browser.ssrfPolicy.dangerouslyAllowPrivateNetwork", + "channels.*.accounts.*.dmPolicy", + "channels.*.accounts.*.groupPolicy", + "channels.*.accounts.*.groups.*.requireMention", + "channels.*.dmPolicy", + "channels.*.enabled", + "channels.*.groupPolicy", + "channels.*.groups.*.requireMention", + "diagnostics.otel.captureContent", + "gateway.auth.mode", + "gateway.auth.rateLimit.*", + "gateway.bind", + "gateway.controlUi.allowInsecureAuth", + "gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback", + "gateway.controlUi.dangerouslyDisableDeviceAuth", + "gateway.customBindHost", + "gateway.http.endpoints.*.*.allowUrl", + "gateway.http.endpoints.*.*.urlAllowlist.*", + "gateway.http.endpoints.*.enabled", + "gateway.mode", + "gateway.remote.enabled", + "gateway.tailscale.mode", + "gateway.tailscale.preserveFunnel", + "logging.redactSensitive", + "memory.qmd.sessions.enabled", + "mcp.servers.*.command", + "mcp.servers.*.transport", + "mcp.servers.*.url", + "models.providers.*.type", + "models.selected", + "models.selectedByAgent.*", + "models.selectedByChannel.*", + "session.dmScope", + "session.maintenance.mode", + "secrets.defaults.provider", + "secrets.providers.*.allowInsecureTransport", + "secrets.providers.*.source", + "tools.allow.*", + "tools.alsoAllow.*", + "tools.deny.*", + "tools.elevated.allowFrom.*.*", + "tools.elevated.enabled", + "tools.exec.ask", + "tools.exec.host", + "tools.exec.security", + "tools.fs.workspaceOnly", + "tools.profile", + "tools.sandbox.tools.allow.*", + "tools.sandbox.tools.alsoAllow.*", + "tools.sandbox.tools.deny.*", + "tools.web.fetch.ssrfPolicy.allowIpv6UniqueLocalRange", + "tools.web.fetch.ssrfPolicy.allowPrivateNetwork", + "tools.web.fetch.ssrfPolicy.allowRfc2544BenchmarkRange", + "tools.web.fetch.ssrfPolicy.dangerouslyAllowPrivateNetwork", + "agents.defaults.memorySearch.enabled", + "agents.defaults.memorySearch.experimental.sessionMemory", + "agents.defaults.memorySearch.sources.*", + "agents.defaults.model.fallbacks.*", + "agents.defaults.model.primary", + "agents.defaults.models.*.alias", + "agents.defaults.sandbox.backend", + "agents.defaults.sandbox.browser.binds.*", + "agents.defaults.sandbox.browser.cdpSourceRange", + "agents.defaults.sandbox.docker.apparmorProfile", + "agents.defaults.sandbox.docker.binds.*", + "agents.defaults.sandbox.docker.dangerouslyAllowContainerNamespaceJoin", + "agents.defaults.sandbox.docker.network", + "agents.defaults.sandbox.docker.readOnlyRoot", + "agents.defaults.sandbox.docker.seccompProfile", + "agents.defaults.sandbox.mode", + "agents.defaults.sandbox.workspaceAccess", + "agents.defaults.tools.allow.*", + "agents.defaults.tools.alsoAllow.*", + "agents.defaults.tools.deny.*", + "agents.defaults.tools.elevated.allowFrom.*.*", + "agents.defaults.tools.elevated.enabled", + "agents.defaults.tools.exec.ask", + "agents.defaults.tools.exec.host", + "agents.defaults.tools.exec.security", + "agents.defaults.tools.fs.workspaceOnly", + "agents.defaults.tools.profile", + "agents.defaults.tools.sandbox.tools.allow.*", + "agents.defaults.tools.sandbox.tools.alsoAllow.*", + "agents.defaults.tools.sandbox.tools.deny.*", + "agents.list.*.memorySearch.enabled", + "agents.list.*.memorySearch.experimental.sessionMemory", + "agents.list.*.memorySearch.sources.*", + "agents.list.*.model.fallbacks.*", + "agents.list.*.model.primary", + "agents.list.*.models.*.alias", + "agents.list.*.sandbox.backend", + "agents.list.*.sandbox.browser.binds.*", + "agents.list.*.sandbox.browser.cdpSourceRange", + "agents.list.*.sandbox.docker.apparmorProfile", + "agents.list.*.sandbox.docker.binds.*", + "agents.list.*.sandbox.docker.dangerouslyAllowContainerNamespaceJoin", + "agents.list.*.sandbox.docker.network", + "agents.list.*.sandbox.docker.readOnlyRoot", + "agents.list.*.sandbox.docker.seccompProfile", + "agents.list.*.sandbox.mode", + "agents.list.*.sandbox.workspaceAccess", + "agents.list.*.tools.allow.*", + "agents.list.*.tools.alsoAllow.*", + "agents.list.*.tools.deny.*", + "agents.list.*.tools.elevated.allowFrom.*.*", + "agents.list.*.tools.elevated.enabled", + "agents.list.*.tools.exec.ask", + "agents.list.*.tools.exec.host", + "agents.list.*.tools.exec.security", + "agents.list.*.tools.fs.workspaceOnly", + "agents.list.*.tools.profile", + "agents.list.*.tools.sandbox.tools.allow.*", + "agents.list.*.tools.sandbox.tools.alsoAllow.*", + "agents.list.*.tools.sandbox.tools.deny.*", + ], + "classifications": [ + { + "pattern": "browser.ssrfPolicy.dangerouslyAllowPrivateNetwork", + "status": "observed", + "area": "network", + "policy": "network.privateNetwork.allow", + "reason": "Policy observes private-network browser SSRF posture.", + }, + { + "pattern": "browser.ssrfPolicy.allowPrivateNetwork", + "status": "observed", + "area": "network", + "policy": "network.privateNetwork.allow", + "reason": "Policy observes the legacy browser private-network toggle.", + "allowNoSchemaPath": true, + }, + { + "pattern": "tools.web.fetch.ssrfPolicy.dangerouslyAllowPrivateNetwork", + "status": "observed", + "area": "network", + "policy": "network.privateNetwork.allow", + "reason": "Policy observes private-network web-fetch SSRF posture.", + "allowNoSchemaPath": true, + }, + { + "pattern": "tools.web.fetch.ssrfPolicy.allowPrivateNetwork", + "status": "observed", + "area": "network", + "policy": "network.privateNetwork.allow", + "reason": "Policy observes the legacy web-fetch private-network toggle.", + "allowNoSchemaPath": true, + }, + { + "pattern": "tools.web.fetch.ssrfPolicy.allowRfc2544BenchmarkRange", + "status": "observed", + "area": "network", + "policy": "network.privateNetwork.allow", + "reason": "Policy treats RFC 2544 benchmark ranges as private-network posture.", + }, + { + "pattern": "tools.web.fetch.ssrfPolicy.allowIpv6UniqueLocalRange", + "status": "observed", + "area": "network", + "policy": "network.privateNetwork.allow", + "reason": "Policy treats IPv6 unique-local ranges as private-network posture.", + }, + { + "pattern": "session.dmScope", + "status": "observed", + "area": "ingress", + "policy": "ingress.session.requireDmScope", + "reason": "Policy observes direct-message session isolation scope.", + }, + { + "pattern": "logging.redactSensitive", + "status": "observed", + "area": "dataHandling", + "policy": "dataHandling.sensitiveLogging.requireRedaction", + "reason": "Policy observes sensitive log redaction posture.", + "allowNoSchemaPath": true, + }, + { + "pattern": "diagnostics.otel.captureContent", + "status": "observed", + "area": "dataHandling", + "policy": "dataHandling.telemetry.denyContentCapture", + "reason": "Policy observes telemetry content-capture posture.", + "allowNoSchemaPath": true, + }, + { + "pattern": "session.maintenance.mode", + "status": "observed", + "area": "dataHandling", + "policy": "dataHandling.retention.requireSessionMaintenance", + "reason": "Policy observes session maintenance enforcement posture.", + }, + { + "pattern": "memory.qmd.sessions.enabled", + "status": "observed", + "area": "dataHandling", + "policy": "dataHandling.memory.denySessionTranscriptIndexing", + "reason": "Policy observes QMD session-transcript indexing.", + }, + { + "pattern": "agents.defaults.memorySearch.enabled", + "status": "observed", + "area": "dataHandling", + "policy": "dataHandling.memory.denySessionTranscriptIndexing", + "reason": "Policy observes default memory-search session indexing enablement.", + }, + { + "pattern": "agents.defaults.memorySearch.experimental.sessionMemory", + "status": "observed", + "area": "dataHandling", + "policy": "dataHandling.memory.denySessionTranscriptIndexing", + "reason": "Policy observes default memory-search session-memory toggle.", + }, + { + "pattern": "agents.defaults.memorySearch.sources.*", + "status": "observed", + "area": "dataHandling", + "policy": "dataHandling.memory.denySessionTranscriptIndexing", + "reason": "Policy observes whether default memory-search sources include sessions.", + }, + { + "pattern": "agents.list.*.memorySearch.enabled", + "status": "observed", + "area": "dataHandling", + "policy": "dataHandling.memory.denySessionTranscriptIndexing", + "reason": "Policy observes per-agent memory-search session indexing enablement.", + }, + { + "pattern": "agents.list.*.memorySearch.experimental.sessionMemory", + "status": "observed", + "area": "dataHandling", + "policy": "dataHandling.memory.denySessionTranscriptIndexing", + "reason": "Policy observes per-agent memory-search session-memory toggle.", + }, + { + "pattern": "agents.list.*.memorySearch.sources.*", + "status": "observed", + "area": "dataHandling", + "policy": "dataHandling.memory.denySessionTranscriptIndexing", + "reason": "Policy observes whether per-agent memory-search sources include sessions.", + }, + { + "pattern": "auth.profiles.*.mode", + "status": "observed", + "area": "auth", + "policy": "auth.profiles.allowModes", + "reason": "Policy observes configured auth profile mode metadata.", + }, + { + "pattern": "auth.profiles.*.provider", + "status": "observed", + "area": "auth", + "policy": "auth.profiles.requireMetadata", + "reason": "Policy observes configured auth profile provider metadata.", + }, + { + "pattern": "channels.*.enabled", + "status": "observed", + "area": "channels", + "policy": "channels.denyRules", + "reason": "Provider deny rules only apply to enabled configured channels.", + }, + { + "pattern": "channels.*.accounts.*.dmPolicy", + "status": "observed", + "area": "ingress", + "policy": "ingress.channels.allowDmPolicies", + "reason": "Policy observes account-level direct-message access posture.", + }, + { + "pattern": "channels.*.dmPolicy", + "status": "observed", + "area": "ingress", + "policy": "ingress.channels.allowDmPolicies", + "reason": "Policy observes channel-level direct-message access posture.", + }, + { + "pattern": "channels.*.accounts.*.groupPolicy", + "status": "observed", + "area": "ingress", + "policy": "ingress.channels.denyOpenGroups", + "reason": "Policy observes account-level group access posture.", + }, + { + "pattern": "channels.*.groupPolicy", + "status": "observed", + "area": "ingress", + "policy": "ingress.channels.denyOpenGroups", + "reason": "Policy observes channel-level group access posture.", + }, + { + "pattern": "channels.*.accounts.*.groups.*.requireMention", + "status": "observed", + "area": "ingress", + "policy": "ingress.channels.requireMentionInGroups", + "reason": "Policy observes account group mention gates.", + }, + { + "pattern": "channels.*.groups.*.requireMention", + "status": "observed", + "area": "ingress", + "policy": "ingress.channels.requireMentionInGroups", + "reason": "Policy observes channel group mention gates.", + }, + { + "pattern": "gateway.bind", + "status": "observed", + "area": "gateway", + "policy": "gateway.exposure.allowNonLoopbackBind", + "reason": "Policy observes Gateway bind exposure posture.", + }, + { + "pattern": "gateway.customBindHost", + "status": "observed", + "area": "gateway", + "policy": "gateway.exposure.allowNonLoopbackBind", + "reason": "Policy observes custom bind host exposure posture.", + }, + { + "pattern": "gateway.tailscale.mode", + "status": "observed", + "area": "gateway", + "policy": "gateway.exposure.allowTailscaleFunnel", + "reason": "Policy observes Tailscale serve/funnel mode when deriving Gateway exposure posture.", + }, + { + "pattern": "gateway.tailscale.preserveFunnel", + "status": "observed", + "area": "gateway", + "policy": "gateway.exposure.allowTailscaleFunnel", + "reason": "Policy observes preserveFunnel because serve mode can preserve Funnel exposure.", + }, + { + "pattern": "gateway.auth.mode", + "status": "observed", + "area": "gateway", + "policy": "gateway.auth.requireAuth", + "reason": "Policy observes Gateway auth mode posture.", + }, + { + "pattern": "gateway.auth.rateLimit.*", + "status": "observed", + "area": "gateway", + "policy": "gateway.auth.requireExplicitRateLimit", + "reason": "Policy observes whether Gateway auth rate limiting is explicitly configured.", + }, + { + "pattern": "gateway.controlUi.allowInsecureAuth", + "status": "observed", + "area": "gateway", + "policy": "gateway.controlUi.allowInsecure", + "reason": "Policy observes the Control UI insecure auth toggle.", + }, + { + "pattern": "gateway.controlUi.dangerouslyDisableDeviceAuth", + "status": "observed", + "area": "gateway", + "policy": "gateway.controlUi.allowInsecure", + "reason": "Policy observes the Control UI device-auth disable toggle.", + }, + { + "pattern": "gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback", + "status": "observed", + "area": "gateway", + "policy": "gateway.controlUi.allowInsecure", + "reason": "Policy observes the Control UI Host-header origin fallback toggle.", + }, + { + "pattern": "gateway.mode", + "status": "observed", + "area": "gateway", + "policy": "gateway.remote.allow", + "reason": "Policy observes whether Gateway remote mode is enabled.", + }, + { + "pattern": "gateway.remote.enabled", + "status": "observed", + "area": "gateway", + "policy": "gateway.remote.allow", + "reason": "Policy observes explicit remote Gateway enablement.", + }, + { + "pattern": "gateway.http.endpoints.*.enabled", + "status": "observed", + "area": "gateway", + "policy": "gateway.http.denyEndpoints", + "reason": "Policy observes Gateway HTTP endpoint enablement.", + }, + { + "pattern": "gateway.http.endpoints.*.*.allowUrl", + "status": "observed", + "area": "gateway", + "policy": "gateway.http.requireUrlAllowlists", + "reason": "Policy observes URL-fetch enablement on Gateway HTTP inputs.", + }, + { + "pattern": "gateway.http.endpoints.*.*.urlAllowlist.*", + "status": "observed", + "area": "gateway", + "policy": "gateway.http.requireUrlAllowlists", + "reason": "Policy observes URL-fetch allowlists on Gateway HTTP inputs.", + }, + { + "pattern": "mcp.servers.*.command", + "status": "observed", + "area": "mcp", + "policy": "mcp.servers.allow / mcp.servers.deny", + "reason": "Policy observes configured MCP server ids and command posture context.", + }, + { + "pattern": "mcp.servers.*.transport", + "status": "observed", + "area": "mcp", + "policy": "mcp.servers.allow / mcp.servers.deny", + "reason": "Policy observes configured MCP server transport posture context.", + }, + { + "pattern": "mcp.servers.*.url", + "status": "observed", + "area": "mcp", + "policy": "mcp.servers.allow / mcp.servers.deny", + "reason": "Policy observes configured MCP server URL posture context.", + }, + { + "pattern": "models.providers.*.type", + "status": "observed", + "area": "models", + "policy": "models.providers.allow / models.providers.deny", + "reason": "Policy observes configured provider ids.", + "allowNoSchemaPath": true, + }, + { + "pattern": "models.selected", + "status": "observed", + "area": "models", + "policy": "models.providers.allow / models.providers.deny", + "reason": "Policy observes selected model refs.", + "allowNoSchemaPath": true, + }, + { + "pattern": "models.selectedByAgent.*", + "status": "observed", + "area": "models", + "policy": "models.providers.allow / models.providers.deny", + "reason": "Policy observes agent-specific selected model refs.", + "allowNoSchemaPath": true, + }, + { + "pattern": "models.selectedByChannel.*", + "status": "observed", + "area": "models", + "policy": "models.providers.allow / models.providers.deny", + "reason": "Policy observes channel-specific selected model refs.", + "allowNoSchemaPath": true, + }, + { + "pattern": "agents.defaults.model.**", + "status": "observed", + "area": "models", + "policy": "models.providers.allow / models.providers.deny", + "reason": "Policy observes default agent model refs.", + }, + { + "pattern": "agents.defaults.models.*.alias", + "status": "observed", + "area": "models", + "policy": "models.providers.allow / models.providers.deny", + "reason": "Policy observes default agent model aliases.", + }, + { + "pattern": "agents.list.*.model.**", + "status": "observed", + "area": "models", + "policy": "models.providers.allow / models.providers.deny", + "reason": "Policy observes per-agent model refs.", + }, + { + "pattern": "agents.list.*.models.*.alias", + "status": "observed", + "area": "models", + "policy": "models.providers.allow / models.providers.deny", + "reason": "Policy observes per-agent model aliases.", + }, + { + "pattern": "secrets.defaults.provider", + "status": "observed", + "area": "secrets", + "policy": "secrets.requireManagedProviders", + "reason": "Policy observes default SecretRef provider provenance.", + "allowNoSchemaPath": true, + }, + { + "pattern": "secrets.providers.*.source", + "status": "observed", + "area": "secrets", + "policy": "secrets.denySources", + "reason": "Policy observes configured secret provider source type.", + }, + { + "pattern": "secrets.providers.*.allowInsecureTransport", + "status": "observed", + "area": "secrets", + "policy": "secrets.allowInsecureProviders", + "reason": "Policy observes insecure secret-provider transport posture.", + "allowNoSchemaPath": true, + }, + { + "pattern": "tools.profile", + "status": "observed", + "area": "tools", + "policy": "tools.profiles.allow", + "reason": "Policy observes global tool profile posture.", + }, + { + "pattern": "tools.fs.workspaceOnly", + "status": "observed", + "area": "tools", + "policy": "tools.fs.requireWorkspaceOnly", + "reason": "Policy observes global filesystem workspace-only posture.", + }, + { + "pattern": "tools.exec.security", + "status": "observed", + "area": "tools", + "policy": "tools.exec.allowSecurity", + "reason": "Policy observes global exec security posture.", + }, + { + "pattern": "tools.exec.ask", + "status": "observed", + "area": "tools", + "policy": "tools.exec.requireAsk", + "reason": "Policy observes global exec approval posture.", + }, + { + "pattern": "tools.exec.host", + "status": "observed", + "area": "tools", + "policy": "tools.exec.allowHosts", + "reason": "Policy observes global exec host routing posture.", + }, + { + "pattern": "tools.elevated.enabled", + "status": "observed", + "area": "tools", + "policy": "tools.elevated.allow", + "reason": "Policy observes global elevated tool posture.", + }, + { + "pattern": "tools.elevated.allowFrom.*.*", + "status": "observed", + "area": "tools", + "policy": "tools.elevated.allow", + "reason": "Policy observes global elevated provider allowlists.", + }, + { + "pattern": "tools.allow.*", + "status": "observed", + "area": "tools", + "policy": "tool posture evidence", + "reason": "Policy includes global tool allow posture in evidence for attestation drift.", + }, + { + "pattern": "tools.alsoAllow.*", + "status": "observed", + "area": "tools", + "policy": "tools.alsoAllow.expected", + "reason": "Policy observes global tools.alsoAllow posture.", + }, + { + "pattern": "tools.deny.*", + "status": "observed", + "area": "tools", + "policy": "tools.denyTools", + "reason": "Policy observes global tool deny posture.", + }, + { + "pattern": "tools.sandbox.tools.*.*", + "status": "observed", + "area": "tools", + "policy": "tools.denyTools", + "reason": "Policy observes global sandbox tool posture.", + }, + { + "pattern": "agents.*.tools.**", + "status": "observed", + "area": "tools", + "policy": "tools.* scoped by agentIds", + "reason": "Policy observes default and per-agent tool posture overrides.", + "allowNoSchemaPath": true, + }, + { + "pattern": "agents.list.*.tools.**", + "status": "observed", + "area": "tools", + "policy": "tools.* scoped by agentIds", + "reason": "Policy observes per-agent tool posture overrides.", + }, + { + "pattern": "agents.*.sandbox.mode", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.requireMode", + "reason": "Policy observes sandbox mode posture.", + }, + { + "pattern": "agents.list.*.sandbox.mode", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.requireMode", + "reason": "Policy observes per-agent sandbox mode posture.", + }, + { + "pattern": "agents.*.sandbox.backend", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.allowBackends", + "reason": "Policy observes sandbox backend posture.", + }, + { + "pattern": "agents.list.*.sandbox.backend", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.allowBackends", + "reason": "Policy observes per-agent sandbox backend posture.", + }, + { + "pattern": "agents.*.sandbox.workspaceAccess", + "status": "observed", + "area": "agents", + "policy": "agents.workspace.allowedAccess", + "reason": "Policy observes sandbox workspace access posture.", + }, + { + "pattern": "agents.list.*.sandbox.workspaceAccess", + "status": "observed", + "area": "agents", + "policy": "agents.workspace.allowedAccess", + "reason": "Policy observes per-agent sandbox workspace access posture.", + }, + { + "pattern": "agents.*.sandbox.docker.network", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.denyHostNetwork and sandbox.containers.denyContainerNamespaceJoin", + "reason": "Policy observes Docker container network posture.", + }, + { + "pattern": "agents.list.*.sandbox.docker.network", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.denyHostNetwork and sandbox.containers.denyContainerNamespaceJoin", + "reason": "Policy observes per-agent Docker container network posture.", + }, + { + "pattern": "agents.*.sandbox.docker.binds.*", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.requireReadOnlyMounts and sandbox.containers.denyContainerRuntimeSocketMounts", + "reason": "Policy observes Docker bind mount posture.", + }, + { + "pattern": "agents.list.*.sandbox.docker.binds.*", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.requireReadOnlyMounts and sandbox.containers.denyContainerRuntimeSocketMounts", + "reason": "Policy observes per-agent Docker bind mount posture.", + }, + { + "pattern": "agents.*.sandbox.browser.binds.*", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.requireReadOnlyMounts", + "reason": "Policy observes sandbox browser bind mount posture.", + }, + { + "pattern": "agents.list.*.sandbox.browser.binds.*", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.requireReadOnlyMounts", + "reason": "Policy observes per-agent sandbox browser bind mount posture.", + }, + { + "pattern": "agents.*.sandbox.docker.apparmorProfile", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.denyUnconfinedProfiles", + "reason": "Policy observes Docker AppArmor profile posture.", + }, + { + "pattern": "agents.list.*.sandbox.docker.apparmorProfile", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.denyUnconfinedProfiles", + "reason": "Policy observes per-agent Docker AppArmor profile posture.", + }, + { + "pattern": "agents.*.sandbox.docker.seccompProfile", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.denyUnconfinedProfiles", + "reason": "Policy observes Docker seccomp profile posture.", + }, + { + "pattern": "agents.list.*.sandbox.docker.seccompProfile", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.denyUnconfinedProfiles", + "reason": "Policy observes per-agent Docker seccomp profile posture.", + }, + { + "pattern": "agents.*.sandbox.docker.dangerouslyAllowContainerNamespaceJoin", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.denyContainerNamespaceJoin", + "reason": "Policy observes explicit Docker namespace-join escape posture.", + }, + { + "pattern": "agents.list.*.sandbox.docker.dangerouslyAllowContainerNamespaceJoin", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.denyContainerNamespaceJoin", + "reason": "Policy observes explicit per-agent Docker namespace-join escape posture.", + }, + { + "pattern": "agents.*.sandbox.docker.readOnlyRoot", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.requireReadOnlyMounts", + "reason": "Policy observes Docker read-only root posture.", + }, + { + "pattern": "agents.list.*.sandbox.docker.readOnlyRoot", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.containers.requireReadOnlyMounts", + "reason": "Policy observes per-agent Docker read-only root posture.", + }, + { + "pattern": "agents.*.sandbox.browser.cdpSourceRange", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.browser.requireCdpSourceRange", + "reason": "Policy observes sandbox browser CDP source range posture.", + }, + { + "pattern": "agents.list.*.sandbox.browser.cdpSourceRange", + "status": "observed", + "area": "sandbox", + "policy": "sandbox.browser.requireCdpSourceRange", + "reason": "Policy observes per-agent sandbox browser CDP source range posture.", + }, + ], +}