feat: support secret-backed OpenAI-compatible targets - #7576
Conversation
apiProxy.targets.openai.baseUrlEnv
There was a problem hiding this comment.
Pull request overview
Adds runtime resolution for secret-backed OpenAI-compatible endpoints while aiming to isolate endpoint details from agents and artifacts.
Changes:
- Adds URL resolution, validation, routing, and network-policy integration.
- Adds environment exclusion and artifact-redaction utilities.
- Extends configuration schemas, CLI documentation, and tests.
Show a summary per file
| File | Description |
|---|---|
src/types/api-proxy-routing-options.ts |
Defines the routing option. |
src/services/api-proxy-env-config.ts |
Configures sidecar routing. |
src/services/api-proxy-env-config.test.ts |
Tests sidecar configuration. |
src/services/agent-environment/excluded-vars.ts |
Excludes the named secret variable. |
src/services/agent-environment/excluded-vars.test.ts |
Tests environment exclusion. |
src/redact-secrets.ts |
Adds endpoint redaction helpers. |
src/redact-secrets.test.ts |
Tests redaction helpers. |
src/openai-base-url-env.ts |
Resolves and validates endpoint URLs. |
src/openai-base-url-env.test.ts |
Tests URL validation. |
src/coverage-branch-gaps-3.test.ts |
Covers compose redaction. |
src/config-writer.ts |
Redacts audit artifacts. |
src/config-writer-new-branches.test.ts |
Tests Squid configuration redaction. |
src/config-mapper.ts |
Maps file configuration. |
src/config-file.ts |
Extends configuration types. |
src/config-file-mapping.test.ts |
Tests configuration mapping. |
src/compose-generator.ts |
Redacts literal sensitive values. |
src/commands/resolve-credentials.ts |
Resolves the new option. |
src/commands/preflight.ts |
Derives sensitive network policy. |
src/commands/preflight.test.ts |
Tests preflight behavior. |
src/cli-options.ts |
Adds the CLI flag. |
src/awf-config-schema.json |
Updates the generated schema. |
docs/awf-config.schema.json |
Updates the canonical schema. |
docs/awf-config-spec.md |
Documents normative behavior. |
docs/api-proxy-sidecar.md |
Documents sidecar configuration. |
docs-site/src/content/docs/reference/cli-reference.md |
Adds CLI reference documentation. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Suppressed comments (1)
src/commands/preflight.ts:194
- Passing the secret URL as
openaiEndpointOverridedoes not implement the documented precedence.resolveApiTargetsToAllowedDomains()checksoptions.openaiApiTargetand thenOPENAI_API_TARGETbefore consulting this parameter, while sidecar routing givesbaseUrlEnvprecedence. If either older target is present, Squid allows that target instead of the secret-backed host and the sidecar's requests fail. The resolver needs a distinct sensitive target input that takes precedence while still writing only tosensitiveAllowedDomains.
resolveSecretOpenAiBaseUrl(options)
?? additionalEnv?.['OPENAI_ENDPOINT_OVERRIDE']
- Files reviewed: 25/25 changed files
- Comments generated: 5
- Review effort level: Balanced
| * @returns The normalized base URL, or undefined when the feature is not configured. | ||
| */ | ||
| function resolveSecretOpenAiBaseUrl(options: Record<string, unknown>): string | undefined { | ||
| const envVarName = options.openaiBaseUrlEnv as string | undefined; |
| if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') { | ||
| throw new Error( | ||
| `Environment variable "${name}" uses an unsupported URL scheme. ` + | ||
| 'Only http:// and https:// endpoints are supported.' |
| const secretOpenAiBaseUrl = resolveOpenAiBaseUrlFromEnv(config.openaiBaseUrlEnv); | ||
| const resolvedOpenAiTarget = secretOpenAiBaseUrl?.host ?? config.openaiApiTarget ?? openAiEndpointOverride; |
| const sensitiveEndpointForms = deriveSensitiveEndpointForms(config.sensitiveAllowedDomains); | ||
|
|
||
| // Save squid.conf for audit (domain ACLs and proxy config, sensitive hosts redacted) | ||
| fs.writeFileSync( | ||
| path.join(auditDir, 'squid.conf'), | ||
| redactSensitiveValues(squidConfig, sensitiveEndpointForms), |
|
@copilot address review feedback |
|
✅ Copilot review passed with no inline comments. @copilot Add the |
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (4 files)
✨ New Files (1 files)
Coverage comparison generated by |
…ile' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…xpression for hostnames' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
|
@copilot make sure pr addresses all review feedback |
apiProxy.targets.openai.baseUrlEnvCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7d7ef30e-3c24-432a-8bd1-ef165d6535f0
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤
|
|
✅ Smoke Copilot BYOK completed. Copilot BYOK mode operational. 🔓
|
|
EGRESS_RESULT allow=pass deny=pass ✅ Allowed domain (github.com) reachable — Overall status: PASS Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
To allow these domains, add them to the network:
allowed:
- defaults
- "example.com"See Network Configuration for more information.
|
|
Smoke Test: Copilot BYOK (Direct) — PASS ✅
Running in direct BYOK mode (
|
|
Running in direct BYOK mode (COPILOT_PROVIDER_API_KEY + COPILOT_PROVIDER_BASE_URL) via api-proxy → Azure OpenAI (Foundry, o4-mini-aw)
|
|
Smoke Test: Services Connectivity — FAIL
Overall: FAIL —
|
Chroot Version Comparison
Overall: ❌ FAILED — Node.js version mismatch between host and chroot environments.
|
Smoke Test
Warning Firewall blocked 3 domainsThe following domains were blocked by the firewall during workflow execution:
To allow these domains, add them to the network:
allowed:
- defaults
- "msfeed2.pkgs.visualstudio.com"
- "msfeed25.pkgs.visualstudio.com"
- "registry.npmjs.org"See Network Configuration for more information.
|
|
Docker Sbx Smoke Test
✅ GitHub MCP connectivity Overall: PASS cc
|
📡 OTel Tracing Smoke Test Results
Overall: all 5 scenarios pass, no regressions detected.
|
|
MCP tool test: ❌ Running in direct BYOK mode (AWF_AUTH_TYPE=github-oidc + AWF_AUTH_AZURE_* + COPILOT_PROVIDER_BASE_URL) via api-proxy → Azure OpenAI (Foundry, o4-mini-aw) authenticated via Microsoft Entra Overall: FAIL cc
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS All clones, builds, installs, and tests completed successfully across all 8 ecosystems (18 total projects). Network egress through the AWF firewall (including the Maven proxy override needed for Note: for Java,
|
Codex workflows routed through a private OpenAI-compatible load balancer had no way to configure the endpoint from a runtime secret: the OpenAI target is a concrete startup value, forcing the sensitive host into generated lockfiles or brittle post-compile patching. This adds an environment-backed target that AWF resolves and validates on the runner, derives network policy from, and keeps out of the agent environment, logs, and artifacts.
{ "apiProxy": { "targets": { "openai": { "baseUrlEnv": "CODEX_LB_BASE_URL" } } } }gh-awcan then bind${{ secrets.CODEX_LB_BASE_URL }}to the named runner variable without writing its value anywhere.Resolution and validation (
src/openai-base-url-env.ts)http(s)URL; rejects embeddeduser:pass@credentials, query strings/fragments, malformed hosts, unsupported schemes, and non-default ports (the sidecar only reaches the scheme's default port, so a custom port would be silently dropped).url,host,hostPort, andbasePath. Every error message is value-free, so a misconfigured endpoint can't leak through a failure path.Wiring
commands/preflight.ts): the resolved URL feeds the existing sensitive-endpoint path, landing insensitiveAllowedDomains— merged intosquid.confbut never intoallowedDomains(which is logged and serialized to the audit artifact). Validation failures exit 1 before agent startup.services/api-proxy-env-config.ts): derived host/path becomeOPENAI_API_TARGET/OPENAI_API_BASE_PATH, taking precedence overtargets.openai.host/basePath.services/agent-environment/excluded-vars.ts): the named variable is excluded from the agent environment unconditionally (not gated onenableApiProxy), so--env-allcan't reintroduce it.docs/awf-config.schema.json+ regeneratedsrc/awf-config-schema.json, newopenaiTarget$defsobaseUrlEnvdoesn't leak onto other providers),config-file.ts,config-mapper.ts,resolve-credentials.ts, routing option types, and a--openai-base-url-env <name>flag.Artifact redaction
deriveSensitiveEndpointForms()/redactSensitiveValues()inredact-secrets.tsexpand each sensitive allowlist entry into its URL, host, andhost:portforms.writeAuditArtifacts()now redacts those forms from the auditedsquid.conf, andredactDockerComposeSecrets()accepts them so values in non-secret-named env vars (e.g.OPENAI_API_TARGET) are scrubbed. This also closes the same gap for the pre-existingOPENAI_ENDPOINT_OVERRIDEpath, which previously wrote the secret host into both artifacts.Docs
Normative §9.7 in
docs/awf-config-spec.md, plus CLI-mapping, CLI reference, and api-proxy sidecar entries.Tests
New suites for URL validation and non-leaking errors, plus additions covering policy derivation, sidecar target/base-path configuration, fail-fast on invalid values, agent-env exclusion, config mapping, and compose/
squid.confredaction.