From a98c5cdfa9d43945166ab2e7ef65b0c8fb717d47 Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 12 May 2026 18:16:04 -0500 Subject: [PATCH] fix(plugin): warn on missing kubernetes adapter env --- .../sandbox-providers/kubernetes/README.md | 2 +- .../kubernetes/src/manifest.ts | 2 +- .../kubernetes/src/plugin.ts | 17 ++++++++-- .../kubernetes/test/unit/manifest.test.ts | 6 +++- .../kubernetes/test/unit/plugin.test.ts | 31 ++++++++++++++++++- 5 files changed, 52 insertions(+), 6 deletions(-) diff --git a/packages/plugins/sandbox-providers/kubernetes/README.md b/packages/plugins/sandbox-providers/kubernetes/README.md index f724dad2..1faeeaef 100644 --- a/packages/plugins/sandbox-providers/kubernetes/README.md +++ b/packages/plugins/sandbox-providers/kubernetes/README.md @@ -68,7 +68,7 @@ Common optional fields: | `imageAllowList` | `[]` | Glob patterns of allowed `target.imageOverride` values. Empty = no override permitted. | | `imagePullSecrets` | `[]` | Names of pre-created Docker image pull secrets in the tenant namespace. | | `egressAllowFqdns` | `[]` | Additional FQDNs (beyond adapter defaults like `api.anthropic.com`). | -| `egressAllowCidrs` | `[]` | Additional CIDRs to allow egress to. | +| `egressAllowCidrs` | `[]` | Additional CIDRs to allow HTTPS egress to. CIDR egress is restricted to TCP port 443. | | `egressMode` | `"standard"` | `standard` (NetworkPolicy + CIDRs, plus public HTTPS fallback when adapter FQDNs are configured) or `cilium` (CiliumNetworkPolicy + exact FQDN allow-list). | | `runtimeClassName` | (none) | e.g. `kata-fc` for Firecracker-backed microVMs. Cluster must have the RuntimeClass installed. | | `serviceAccountAnnotations` | `{}` | Annotations applied to per-tenant ServiceAccount (e.g. IRSA `eks.amazonaws.com/role-arn`). | diff --git a/packages/plugins/sandbox-providers/kubernetes/src/manifest.ts b/packages/plugins/sandbox-providers/kubernetes/src/manifest.ts index 250f2bae..d8c20179 100644 --- a/packages/plugins/sandbox-providers/kubernetes/src/manifest.ts +++ b/packages/plugins/sandbox-providers/kubernetes/src/manifest.ts @@ -72,7 +72,7 @@ const manifest: PaperclipPluginManifestV1 = { egressAllowCidrs: { type: "array", items: { type: "string" }, - description: "Additional CIDRs to allow egress to from agent pods.", + description: "Additional CIDRs to allow HTTPS egress to from agent pods. CIDR egress is restricted to TCP port 443.", }, egressMode: { type: "string", diff --git a/packages/plugins/sandbox-providers/kubernetes/src/plugin.ts b/packages/plugins/sandbox-providers/kubernetes/src/plugin.ts index e71f2047..e14c2eb9 100644 --- a/packages/plugins/sandbox-providers/kubernetes/src/plugin.ts +++ b/packages/plugins/sandbox-providers/kubernetes/src/plugin.ts @@ -72,11 +72,24 @@ function deriveTenantNamespace(config: KubernetesProviderConfig, companyId: stri * TODO: future milestones may thread per-run secrets differently (e.g. via * a secret store reference on the environment config). */ -function extractAdapterEnvFromProcess(envKeys: string[]): Record { +export function extractAdapterEnvFromProcess( + envKeys: string[], + warn: (message: string) => void = console.warn, +): Record { const out: Record = {}; + const missing: string[] = []; for (const k of envKeys) { const v = process.env[k]; - if (v) out[k] = v; + if (v) { + out[k] = v; + } else { + missing.push(k); + } + } + if (missing.length > 0) { + warn( + `[plugin-kubernetes] adapter environment variable(s) missing from plugin worker process: ${missing.join(", ")}. Agent pods may fail provider authentication unless these keys are optional for the selected adapter.`, + ); } return out; } diff --git a/packages/plugins/sandbox-providers/kubernetes/test/unit/manifest.test.ts b/packages/plugins/sandbox-providers/kubernetes/test/unit/manifest.test.ts index 080ceee2..85571a99 100644 --- a/packages/plugins/sandbox-providers/kubernetes/test/unit/manifest.test.ts +++ b/packages/plugins/sandbox-providers/kubernetes/test/unit/manifest.test.ts @@ -3,7 +3,7 @@ import manifest from "../../src/manifest.js"; describe("manifest", () => { const configSchema = manifest.environmentDrivers[0]?.configSchema as { - properties: Record; + properties: Record; anyOf: Array<{ properties?: Record; required?: string[]; @@ -23,4 +23,8 @@ describe("manifest", () => { }); expect(configSchema.anyOf).toContainEqual({ required: ["kubeconfig"] }); }); + + it("documents that CIDR egress is HTTPS-only", () => { + expect(configSchema.properties.egressAllowCidrs.description).toContain("TCP port 443"); + }); }); diff --git a/packages/plugins/sandbox-providers/kubernetes/test/unit/plugin.test.ts b/packages/plugins/sandbox-providers/kubernetes/test/unit/plugin.test.ts index b005b748..daa30ea9 100644 --- a/packages/plugins/sandbox-providers/kubernetes/test/unit/plugin.test.ts +++ b/packages/plugins/sandbox-providers/kubernetes/test/unit/plugin.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import plugin from "../../src/plugin.js"; +import plugin, { extractAdapterEnvFromProcess } from "../../src/plugin.js"; describe("plugin", () => { it("exports the kubernetes driver", () => { @@ -91,4 +91,33 @@ describe("plugin", () => { expect(result.ok).toBe(true); expect(result.warnings).toBeUndefined(); }); + + it("warns when adapter env keys are missing from the worker process", () => { + const warnMessages: string[] = []; + const originalPresent = process.env.PAPERCLIP_TEST_PRESENT_KEY; + const originalMissing = process.env.PAPERCLIP_TEST_MISSING_KEY; + process.env.PAPERCLIP_TEST_PRESENT_KEY = "secret-value"; + delete process.env.PAPERCLIP_TEST_MISSING_KEY; + try { + const result = extractAdapterEnvFromProcess( + ["PAPERCLIP_TEST_PRESENT_KEY", "PAPERCLIP_TEST_MISSING_KEY"], + (message) => warnMessages.push(message), + ); + expect(result).toEqual({ PAPERCLIP_TEST_PRESENT_KEY: "secret-value" }); + expect(warnMessages).toHaveLength(1); + expect(warnMessages[0]).toContain("PAPERCLIP_TEST_MISSING_KEY"); + expect(warnMessages[0]).not.toContain("secret-value"); + } finally { + if (originalPresent === undefined) { + delete process.env.PAPERCLIP_TEST_PRESENT_KEY; + } else { + process.env.PAPERCLIP_TEST_PRESENT_KEY = originalPresent; + } + if (originalMissing === undefined) { + delete process.env.PAPERCLIP_TEST_MISSING_KEY; + } else { + process.env.PAPERCLIP_TEST_MISSING_KEY = originalMissing; + } + } + }); });