Skip to content

feat(api-proxy): add allow/deny model policy to apiProxy config#4406

Draft
Copilot wants to merge 4 commits into
mainfrom
copilot/allowed-and-disallowed-models
Draft

feat(api-proxy): add allow/deny model policy to apiProxy config#4406
Copilot wants to merge 4 commits into
mainfrom
copilot/allowed-and-disallowed-models

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 5, 2026

Adds apiProxy.allowedModels and apiProxy.disallowedModels to the AWF config — provider-scoped glob lists that gate which models the api-proxy will resolve and serve. Patterns follow the gh-aw model-alias spec (provider/modelglob, */glob, or bare glob). Denylist wins; non-empty allowlist requires a match; both empty = allow all.

{
  "apiProxy": {
    "allowedModels": ["copilot/gpt-*", "copilot/claude-sonnet-*"],
    "disallowedModels": ["*/claude-opus-*", "copilot/o1-*"]
  }
}

Enforcement

  • Alias resolutionresolveModel, selectMiddlePowerFallback, and filterResolvableAliases filter candidates through the policy, so blocked models can't sneak in via aliases or middle-power fallback, and /reflect hides aliases that resolve only to blocked models.

  • Inference requestsrewriteModelInBody re-checks the resolved model and returns a blocked sentinel; composeBodyTransforms short-circuits the chain and proxy-request.js responds 403 model_blocked_by_policy:

    {
      "error": {
        "type": "model_blocked_by_policy",
        "message": "Model 'claude-opus-4' is blocked by AWF policy",
        "provider": "copilot",
        "model": "claude-opus-4",
        "reason": "disallowed"
      }
    }

Plumbing

  • Schema (docs/awf-config.schema.json, regenerated src/awf-config-schema.json), AwfFileConfig mapping, ApiProxyOptions, LogAndLimitsResult validation, and WrapperConfig threading.
  • api-proxy-service-config.ts forwards AWF_ALLOWED_MODELS / AWF_DISALLOWED_MODELS (JSON-encoded string[]) to the sidecar; model-config.js parses them into a frozen MODEL_POLICY and activates the body transform whenever aliases or policy are set.

Docs

  • New §12.7 "Model Allow/Deny Policy" in docs/awf-config-spec.md plus CLI-mapping entries.
  • Both fields are config-file-only; no CLI flags added.

Tests

  • New coverage in model-resolver.test.js for parseModelGlobList, matchProviderModelPattern, checkModelPolicy, policy-aware resolveModel, and blocked-request paths through rewriteModelInBody.
  • Validator coverage in validate-options.test.ts for the validateModelGlobList branch of src/commands/validators/log-and-limits.ts (invalid type, non-string entries, empty/whitespace trimming, happy path).

Smoke workflow

  • New .github/workflows/smoke-model-policy.md (Copilot engine, AWF sandbox) exercises the policy end-to-end: the agent curls $COPILOT_API_URL/chat/completions with a model matching the injected disallow pattern and asserts HTTP 403 + "type":"model_blocked_by_policy". A post-step also greps the api-proxy JSONL logs for blocked_model entries as a secondary check.
  • scripts/ci/postprocess-smoke-workflows.ts now injects apiProxy.allowedModels: ["*"] and apiProxy.disallowedModels: ["*/awf-smoke-blocked-test-model*"] into the inline awf-config.json JSON emitted by the gh-aw compiler — scoped to smoke-model-policy.lock.yml only and idempotent via a negative-lookahead regex. The disallow pattern is intentionally namespaced so it never collides with a real model and the agent's own model is unaffected.
  • Added 5 regex unit tests in scripts/ci/postprocess-smoke-workflows.test.ts covering match, replacement, both idempotency branches, and a no-match guard.

Copilot AI linked an issue Jun 5, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Add allowed and disallowed models for firewall config Add allow/deny model policy to apiProxy config Jun 5, 2026
Copilot AI requested a review from zarenner June 5, 2026 19:03
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Documentation Preview

Documentation build failed for this PR. View logs.

Built from commit f3d5f89

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 96.61% 96.51% 📉 -0.10%
Statements 96.51% 96.40% 📉 -0.11%
Functions 98.70% 98.16% 📉 -0.54%
Branches 91.03% 90.69% 📉 -0.34%
📁 Per-file Coverage Changes (2 files)
File Lines (Before → After) Statements (Before → After)
src/commands/validators/log-and-limits.ts 90.3% → 82.7% (-7.66%) 90.3% → 81.8% (-8.51%)
src/config-writer.ts 89.3% → 90.9% (+1.65%) 89.3% → 90.9% (+1.65%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@zarenner
Copy link
Copy Markdown
Collaborator

zarenner commented Jun 5, 2026

@copilot fix failing checks, also add test agentic workflow(s)

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jun 5, 2026

@copilot fix failing checks, also add test agentic workflow(s)

Looked at the failing checks:

  • Test Coverage: Regression was on src/commands/validators/log-and-limits.ts (the new validateModelGlobList branch was uncovered). Added 6 unit tests in validate-options.test.ts covering invalid type, non-string entries, empty/whitespace trimming, and the happy path. Local run is now 92.0% (lines) for that file (baseline was 90.3%).
  • PR Title Check: The PR title Add allow/deny model policy to apiProxy config doesn't carry a Conventional Commits prefix (e.g. feat: / feat(api-proxy):). I can't edit PR titles from here — could you rename it? E.g. feat(api-proxy): add allow/deny model policy to apiProxy config.
  • Smoke Copilot BYOK AOAI and Security Guard: Both failures are inside the agentic step (Agent did not call add_comment on a pull_request trigger / Claude CLI exited non-zero) and aren't caused by anything in this PR. Re-running once the title is fixed should clear them.

For the test agentic workflow(s) ask — could you clarify what you'd like to see? apiProxy.allowedModels / disallowedModels are config-file-only and the awf-config.json consumed under sandbox: agent: id: awf is generated by gh-aw from its built-in defaults (no .md frontmatter path to inject policy today). Two options I can pursue:

  1. A new smoke .md workflow that exercises the policy by mutating the gh-aw-generated awf-config.json via scripts/ci/postprocess-smoke-workflows.ts (injects allowedModels/disallowedModels post-compile, asserts the sidecar returns model_blocked_by_policy).
  2. A bash-only integration job (like test-examples.yml) that runs awf --config <fixture> against an apiProxy.allowedModels / disallowedModels fixture and asserts the sidecar behavior end-to-end without an agent.

Which would you prefer (or both)?

@zarenner zarenner changed the title Add allow/deny model policy to apiProxy config feat(api-proxy): add allow/deny model policy to apiProxy config Jun 5, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jun 5, 2026

@copilot implement this

A new smoke .md workflow that exercises the policy by mutating the gh-aw-generated awf-config.json via scripts/ci/postprocess-smoke-workflows.ts (injects allowedModels/disal...

Done in 5a0f7f9.

  • New smoke-model-policy.md (Copilot engine, AWF sandbox) runs a curl against $COPILOT_API_URL/chat/completions with model: "awf-smoke-blocked-test-model-001" and asserts HTTP 403 + "type":"model_blocked_by_policy". A post-step also greps api-proxy/*.jsonl for blocked_model log entries.
  • postprocess-smoke-workflows.ts now injects apiProxy.allowedModels: ["*"] and apiProxy.disallowedModels: ["*/awf-smoke-blocked-test-model*"] into the inline awf-config.json JSON string emitted by gh-aw — scoped to smoke-model-policy.lock.yml only, idempotent via a negative-lookahead regex ("apiProxy":\{(?!"allowedModels"|"disallowedModels")). The blocked-pattern is intentionally namespaced (awf-smoke-…) so it never collides with a real model and the agent's own model is unaffected.
  • Added 5 regex unit tests in postprocess-smoke-workflows.test.ts covering match, replacement, both idempotency branches, and a no-match guard.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 96.61% 96.56% 📉 -0.05%
Statements 96.51% 96.47% 📉 -0.04%
Functions 98.70% 98.73% 📈 +0.03%
Branches 91.03% 90.64% 📉 -0.39%
📁 Per-file Coverage Changes (4 files)
File Lines (Before → After) Statements (Before → After)
src/commands/build-config.ts 100.0% → 92.8% (-7.15%) 100.0% → 92.8% (-7.15%)
src/commands/main-action.ts 95.5% → 95.6% (+0.13%) 95.5% → 95.6% (+0.13%)
src/config-writer.ts 89.3% → 90.9% (+1.65%) 89.3% → 90.9% (+1.65%)
src/commands/validators/log-and-limits.ts 90.3% → 92.0% (+1.68%) 90.3% → 92.2% (+1.88%)
✨ New Files (1 files)
  • src/dind-bootstrap.ts: 88.5% lines

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Claude Engine Smoke Test

  • ✅ GitHub API: 2 PR entries found
  • ✅ GitHub check: playwright_check=PASS
  • ✅ File verify: smoke-test-claude-27045115208.txt exists

Total: PASS

💥 [THE END] — Illustrated by Smoke Claude

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Smoke Test: API Proxy Model Allow/Deny Policy ✅ PASS

Check Result
HTTP status 403
model_blocked_by_policy in body ✅ Yes
Blocked model name in body awf-smoke-blocked-test-model-001
Overall ✅ PASS

🛡️ Model policy enforced by Smoke Model Policy

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Smoke Test: Copilot BYOK (Direct) — PASS ✅

Running in direct BYOK mode (COPILOT_PROVIDER_API_KEY) via api-proxy → api.githubcopilot.com

Overall: PASS. cc author @Copilot · assignees @zarenner @Copilot

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

🔬 Smoke Test Results — PASS

Test Result
GitHub MCP connectivity ✅ PR #4410: "fix(test): update placeholder test to find token in copilot-byok.js"
GitHub.com HTTP ✅ HTTP 200
File write/read /tmp/gh-aw/agent/smoke-test-copilot-27045115175.txt

Overall: PASS | PR by @Copilot · Assignees: @zarenner, @Copilot

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

feat(api-proxy): add allow/deny model policy to apiProxy config
fix(test): update placeholder test to find token in copilot-byok.js
fix(api-proxy): add Azure/AWS/GCP OIDC support to Copilot adapter
✅ GitHub PR list
✅ GitHub title check
✅ smoke-test file
✅ build
Overall status: PASS

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • registry.npmjs.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

🔭 Smoke Test: API Proxy OpenTelemetry Tracing

Scenario Status Notes
1. Module Loading otel.js loads successfully; isEnabled: true; exports: startRequestSpan, setTokenAttributes, endSpan, endSpanError, shutdown, isEnabled + testing internals
2. Test Suite 33/33 tests passed — spans, attributes, events, parent context, exporters, serialization, shutdown
3. Env Var Forwarding api-proxy-service-config.ts forwards OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, GITHUB_AW_OTEL_TRACE_ID, GITHUB_AW_OTEL_PARENT_SPAN_ID, OTEL_SERVICE_NAME to the sidecar
4. Token Tracker Integration token-tracker-http.js exposes both onUsage callback (OTEL hook) and onSpanEnd callback at lines 62–63
5. OTEL Diagnostics No OTEL_EXPORTER_OTLP_ENDPOINT set → graceful degradation to FileSpanExporter (/var/log/api-proxy/otel.jsonl); no unexpected errors

All 5 scenarios pass. ✅

📡 OTel tracing validated by Smoke OTel Tracing

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Chroot Version Comparison Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3
Node.js v24.16.0 v22.22.3
Go go1.22.12 go1.22.12

Overall: ❌ Not all tests passed.

Go versions match, but Python and Node.js differ between host and chroot environments.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color 1/1 passed ✅ PASS
Go env 1/1 passed ✅ PASS
Go uuid 1/1 passed ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx All passed ✅ PASS
Node.js execa All passed ✅ PASS
Node.js p-limit All passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS

Generated by Build Test Suite for issue #4406 · sonnet46 5.2M ·

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Smoke Test Results

  • GitHub MCP: ❌ (mcpscripts not found)
  • GitHub.com Connectivity: ❌ (SSL error 35)
  • File Writing: ✅
  • Bash Tool: ✅

Overall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • localhost

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "localhost"

See Network Configuration for more information.

💎 Faceted by Smoke Gemini

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

Smoke Test: GitHub Actions Services Connectivity

Check Result
Redis PING ❌ Timeout (no response on host.docker.internal:6379)
PostgreSQL pg_isready ❌ No response on host.docker.internal:5432
PostgreSQL SELECT 1 ❌ Not reached (pg_isready failed)

Overall: FAIL — Neither Redis nor PostgreSQL are reachable via host.docker.internal. Service containers may not be running or host.docker.internal DNS may not resolve in this environment.

🔌 Service connectivity validated by Smoke Services

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 5, 2026

@Copilot @zarenner

  • MCP connectivity: ✅
  • github.com connectivity (HTTP 200): ✅
  • file write/read test: ✅
  • BYOK inference (direct AZ Foundry): ✅

Running in direct BYOK mode (COPILOT_PROVIDER_API_KEY + COPILOT_PROVIDER_BASE_URL) via api-proxy → Azure OpenAI (Foundry, o4-mini-aw)
Overall: PASS

🔑 BYOK (AOAI api-key) report filed by Smoke Copilot BYOK AOAI (api-key)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allowed and disallowed models.

2 participants