Skip to content

feat: allowedModels / disallowedModels policy enforcement in api-proxy#5095

Merged
lpcox merged 4 commits into
mainfrom
copilot/allowed-and-disallowed-models-again
Jun 16, 2026
Merged

feat: allowedModels / disallowedModels policy enforcement in api-proxy#5095
lpcox merged 4 commits into
mainfrom
copilot/allowed-and-disallowed-models-again

Conversation

Copilot AI commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Adds enterprise-grade model access control to the api-proxy: an allowlist (allowedModels) and denylist (disallowedModels) accepting arrays of glob patterns. Disallowed always takes precedence over allowed.

Enforcement happens at two points:

  • Alias resolution — policy-violating candidates are filtered before selection; if all candidates are blocked, resolution returns null (no silent fallback to an unauthorized model)
  • Inference guardmodel-policy-guard runs in enforceGuards on every request, rejecting blocked models with 403 before the request is forwarded

Config

# awf.yml
apiProxy:
  allowedModels: ["*sonnet*", "*haiku*"]   # allowlist — omit for no restriction
  disallowedModels: ["*opus*", "gpt-5*"]   # denylist  — omit for no restriction

Flows through TypeScript config → AWF_ALLOWED_MODELS / AWF_DISALLOWED_MODELS env vars (JSON arrays) into the api-proxy container.

Changes

  • containers/api-proxy/guards/model-policy-guard.js (new) — core policy logic; exports parseModelPatterns, isModelPermittedByPolicy, getModelPolicyBlockState, buildModelPolicyError
  • model-resolver.jsresolveModel / _resolveAliasPatterns accept modelPolicyConfig and filter candidates inline (avoids circular dep — no import of guard module)
  • model-body-rewriter.js / model-config.js — thread MODEL_POLICY_CONFIG from env through to resolver
  • proxy-request.js / websocket-proxy.js / common-guard-checks.js — wire guard into both HTTP and WebSocket enforce-guards paths
  • src/types/api-proxy-model-options.ts — add allowedModels?: string[], disallowedModels?: string[]
  • src/config-file.ts — parse fields from awf.yml and map through CLI options
  • src/services/api-proxy-service-config.ts — forward as env vars (only when non-empty)
  • src/awf-config-schema.json / docs/awf-config.schema.json — schema definitions for both fields

Implements issue #4389: allowed and disallowed model glob pattern lists
enforced at two points:
1. Alias resolution: policy-violating candidates filtered in _resolveAliasPatterns
2. Inference guard: model-policy-guard.js checked in enforceGuards pipeline

Config flows from awf.yml → TypeScript config → env vars AWF_ALLOWED_MODELS /
AWF_DISALLOWED_MODELS (JSON arrays) → api-proxy container.

Disallowed takes priority over allowed when a model matches both lists.
Copilot AI changed the title [WIP] Add support for allowed and disallowed models in firewall config feat: allowedModels / disallowedModels policy enforcement in api-proxy Jun 16, 2026
Copilot finished work on behalf of lpcox June 16, 2026 04:35
Copilot AI requested a review from lpcox June 16, 2026 04:35
@lpcox lpcox marked this pull request as ready for review June 16, 2026 04:36
Copilot AI review requested due to automatic review settings June 16, 2026 04:36
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Documentation Preview

Documentation build failed for this PR. View logs.

Built from commit 7af45fc

Comment thread containers/api-proxy/model-config.js Fixed
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 96.89% 96.93% 📈 +0.04%
Statements 96.75% 96.79% 📈 +0.04%
Functions 98.81% 98.81% ➡️ +0.00%
Branches 91.23% 91.28% 📈 +0.05%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/workdir-setup.ts 92.6% → 94.4% (+1.85%) 92.6% → 94.4% (+1.85%)

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds model access control to the API proxy by introducing allowlist/denylist configuration (allowedModels / disallowedModels) expressed as glob patterns, then enforcing the policy during model alias resolution and request guarding.

Changes:

  • Introduces a new model-policy-guard and wires it into the shared guard-check pipeline.
  • Threads model-policy config from awf.yml → CLI options/types/schema → docker-compose env vars → api-proxy resolution/guards.
  • Extends model alias resolution to filter candidates against policy and adds tests for both proxy env forwarding and resolver behavior.
Show a summary per file
File Description
src/types/api-proxy-model-options.ts Documents new allowedModels / disallowedModels options (including behavior and env vars).
src/services/api-proxy-service-misc-forwarding.test.ts Adds tests asserting allowed/disallowed model lists are forwarded (or omitted when empty).
src/services/api-proxy-service-config.ts Forwards allowed/disallowed lists into AWF_ALLOWED_MODELS / AWF_DISALLOWED_MODELS env vars.
src/config-file.ts Parses apiProxy.allowedModels / apiProxy.disallowedModels from awf.yml into CLI options.
src/awf-config-schema.json Adds schema definitions for allowedModels / disallowedModels.
docs/awf-config.schema.json Mirrors schema updates for documentation tooling.
containers/api-proxy/websocket-proxy.js Threads model-policy guard dependencies into the WebSocket guard dependency bundle.
containers/api-proxy/proxy-request.js Wires model-policy guard into the HTTP enforceGuards path.
containers/api-proxy/model-resolver.test.js Adds coverage for policy filtering behavior during alias resolution.
containers/api-proxy/model-resolver.js Threads modelPolicyConfig through alias resolution and filters candidate models accordingly.
containers/api-proxy/model-config.js Constructs MODEL_POLICY_CONFIG from env-derived policy lists and passes it into body rewrite resolution.
containers/api-proxy/model-body-rewriter.js Passes modelPolicyConfig through to resolveModel(...).
containers/api-proxy/guards/model-policy-guard.test.js Adds unit tests for parsing, permit/deny evaluation, block-state, and error-body building.
containers/api-proxy/guards/model-policy-guard.js Implements model policy parsing, evaluation, block state, and error-body builder.
containers/api-proxy/guards/common-guard-checks.js Adds model_policy_violation as a shared guard descriptor.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 15/15 changed files
  • Comments generated: 7

Comment on lines +132 to +146
isBlocked: block => !!block,
statusCode: 400,
eventName: 'model_policy_violation',
buildError: buildModelPolicyError,
Comment thread src/types/api-proxy-model-options.ts
Comment thread src/types/api-proxy-model-options.ts Outdated
Comment on lines +32 to +34
if (!Array.isArray(parsed)) return null;
const strings = parsed.filter(p => typeof p === 'string' && p.trim());
return strings.length > 0 ? strings : null;
Comment on lines +106 to +111
/**
* Builds the structured 400 error response body for a model-policy rejection.
*
* @param {{ model: string, reason: string }} state
* @returns {{ error: object }}
*/
Comment thread containers/api-proxy/model-config.js Outdated
Comment on lines 366 to +369

const [aliasKey, aliasRaw] = aliasEntry;
const aliasDefinition = resolveAliasDefinition(aliasRaw);
return _resolveAliasPatterns(aliasKey, aliasDefinition, requestedModel, aliases, availableModels, currentProvider, newChain, fallbackConfig, log);
return _resolveAliasPatterns(aliasKey, aliasDefinition, requestedModel, aliases, availableModels, currentProvider, newChain, fallbackConfig, log, modelPolicyConfig);
…ort, function or class'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Copilot review passed.

@copilot Please add the ready-for-aw label to this PR to trigger agentic CI smoke tests. Use: gh pr edit 5095 --add-label ready-for-aw

@lpcox

lpcox commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

@copilot address review feedback and add the ready-for-aw label to this PR to trigger agentic CI smoke tests. Use: gh pr edit 5095 --add-label ready-for-aw

- common-guard-checks.js: move model_policy_violation check before
  unknown_model_ai_credits so policy takes precedence; use 403 status
- model-policy-guard.js: trim patterns in parseModelPatterns; update
  buildModelPolicyError JSDoc to reflect 403 status code
- model-resolver.js: apply modelPolicyConfig in _resolveDirectMatch
  path (direct match and family-version fallback)
- src/types/api-proxy-model-options.ts: update docstrings to say 403

Copilot AI commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

@copilot address review feedback and add the ready-for-aw label to this PR to trigger agentic CI smoke tests. Use: gh pr edit 5095 --add-label ready-for-aw

Done in commit 2b1025a. Here's a summary of what was addressed:

  • Guard ordering + 403: model_policy_violation now runs before unknown_model_ai_credits in buildCommonGuardChecks and uses HTTP 403 — policy violations are no longer masked by the pricing guard.
  • Pattern trimming: parseModelPatterns now trims each pattern, so " *sonnet* " matches correctly.
  • Direct-match policy enforcement: _resolveDirectMatch now accepts and applies modelPolicyConfig for both the direct-match and family-version-fallback paths, closing the bypass in alias-less resolution.
  • Docstrings updated: buildModelPolicyError JSDoc and the allowedModels/disallowedModels docstrings in api-proxy-model-options.ts now consistently say 403.

Also added the ready-for-aw label to trigger agentic CI smoke tests.

Copilot finished work on behalf of lpcox June 16, 2026 04:48
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

📡 Smoke OTel Tracing completed. All tracing scenarios validated. ✅

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

🔑 Smoke Copilot PAT PAT auth validated. All systems operational. ✅

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Smoke Copilot BYOK AOAI (Entra) completed. Copilot AOAI BYOK (Entra) mode operational. 🔓

Commented on PR and added smoke-copilot-byok-aoai-entra label

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Smoke Gemini completed. All facets verified. 💎

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Chroot tests passed! Smoke Chroot - All security and functionality tests succeeded.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

@github-actions

Copy link
Copy Markdown
Contributor

@lpcox

  • MCP connectivity: ❌
  • GitHub.com connectivity: ❌
  • File read/write: ❌
  • Direct BYOK mode: ✅

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 status: FAIL

🪪 BYOK (AOAI Entra) report filed by Smoke Copilot BYOK AOAI (Entra)

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: PAT Auth — FAIL

Test Result
GitHub MCP connectivity
GitHub.com HTTP ❌ (pre-step template vars unresolved)
File write/read ❌ (pre-step template vars unresolved)

Auth mode: PAT (COPILOT_GITHUB_TOKEN)
CC @lpcox @Copilot

Pre-step outputs (SMOKE_HTTP_CODE, SMOKE_FILE_PATH, SMOKE_FILE_CONTENT) were not substituted — workflow step ordering issue.

🔑 PAT report filed by Smoke Copilot PAT

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smoke Test: API Proxy OpenTelemetry Tracing

Scenario Result Details
Module Loading otel.js loads cleanly; exports 14 functions (startRequestSpan, setTokenAttributes, setBudgetAttributes, endSpan, endSpanError, shutdown, isEnabled, ...)
Test Suite 59 tests passed, 0 failed across otel.test.js + otel-fanout.test.js
Env Var Forwarding api-proxy-service-config.ts forwards GH_AW_OTLP_ENDPOINTS, OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_HEADERS, GITHUB_AW_OTEL_TRACE_ID, GITHUB_AW_OTEL_PARENT_SPAN_ID, OTEL_SERVICE_NAME
Token Tracker Integration onUsage callback present in token-tracker-http.js (lines 283, 324) as OTEL hook point
OTEL Diagnostics 1 span exported to /tmp/gh-aw/otel.jsonl during this run (service.name: gh-aw.smoke-otel-tracing, service.version: 1.0.60)

All scenarios pass. OTEL tracing integration is functioning correctly.

📡 OTel tracing validated by Smoke OTel Tracing

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: Copilot BYOK (Direct) Mode

Status: PASS

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

  • ✅ GitHub MCP connectivity
  • ✅ github.com reachability (HTTP 200)
  • ✅ BYOK inference path (processing request)

@lpcox @Copilot

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions

Copy link
Copy Markdown
Contributor

@Copilot @lpcox

Smoke Test Results

  • GitHub MCP API: ✅
  • GitHub.com HTTP access: ✅
  • File I/O in sandbox: ✅
  • BYOK inference path: ✅

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)

@github-actions

Copy link
Copy Markdown
Contributor

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: ❌ Tests did not pass — Python and Node.js versions differ between host and chroot environments.

Tested by Smoke Chroot

@github-actions

Copy link
Copy Markdown
Contributor

🔬 Smoke Test Results

PR: feat: allowedModels / disallowedModels policy enforcement in api-proxy
Author: @Copilot | Assignees: @lpcox, @Copilot

Test Result
GitHub MCP connectivity
GitHub.com HTTP connectivity ❌ (template vars unexpanded)
File write/read ❌ (template vars unexpanded)

Overall: FAIL — pre-step outputs were not substituted into the workflow prompt.

📰 BREAKING: Report filed by Smoke Copilot

@github-actions

Copy link
Copy Markdown
Contributor

Merged PRs:

  • fix: broaden GHES detection for Copilot auth prefix
  • refactor: decompose finalizeHttpTracking into focused helper functions

Results:

  • GitHub reads: ✅
  • Playwright GitHub title: ✅
  • File write/read: ✅
  • Discussion lookup/comment: ✅
  • Build: ❌ (node missing)
    Overall: FAIL

🔮 The oracle has spoken through Smoke Codex

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ 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 #5095 ·

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test Results — FAIL ❌

Check Result
Redis PING (host.docker.internal:6379) ❌ Connection timeout
PostgreSQL pg_isready (host.docker.internal:5432) ❌ No response
PostgreSQL SELECT 1 ❌ Unreachable

host.docker.internal resolves to 172.17.0.1 but both ports 6379 and 5432 are timing out — service containers appear to not be running or are unreachable from this runner.

🔌 Service connectivity validated by Smoke Services

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test Results

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

@lpcox lpcox merged commit 8de2222 into main Jun 16, 2026
83 of 86 checks passed
@lpcox lpcox deleted the copilot/allowed-and-disallowed-models-again branch June 16, 2026 14:00
@github-actions github-actions Bot mentioned this pull request Jun 16, 2026
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
- Fix disallowedModels description in awf-config.schema.json: HTTP 400 → HTTP 403
  (commit 8de2222 changed the rejection status code to 403)
- Add apiProxy.allowedModels and apiProxy.disallowedModels to the CLI mapping
  list in awf-config-spec.md (maps to AWF_ALLOWED_MODELS / AWF_DISALLOWED_MODELS)

Triggered by: 8de2222
feat: allowedModels / disallowedModels policy enforcement in api-proxy (#5095)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lpcox pushed a commit that referenced this pull request Jun 16, 2026
* docs: sync schemas and specs with allowedModels/disallowedModels changes

- Fix disallowedModels description in awf-config.schema.json: HTTP 400 → HTTP 403
  (commit 8de2222 changed the rejection status code to 403)
- Add apiProxy.allowedModels and apiProxy.disallowedModels to the CLI mapping
  list in awf-config-spec.md (maps to AWF_ALLOWED_MODELS / AWF_DISALLOWED_MODELS)

Triggered by: 8de2222
feat: allowedModels / disallowedModels policy enforcement in api-proxy (#5095)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: sync src schema disallowedModels status wording

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
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.

4 participants