Skip to content

feat(anr): enforce Bedrock-only provider policy in ANR mode - #386

Open
dstokes0523 wants to merge 6 commits into
devfrom
anr-bedrock-enforcement
Open

feat(anr): enforce Bedrock-only provider policy in ANR mode#386
dstokes0523 wants to merge 6 commits into
devfrom
anr-bedrock-enforcement

Conversation

@dstokes0523

Copy link
Copy Markdown

Closes #17

Summary

Enforce a Bedrock-only provider policy when OPENCODE_FLAVOR=anr. This PR closes all 8 confirmed bypass paths identified in the security audit.

Problem Statement

OPENCODE_FLAVOR=anr triggers OIDC authentication, AWS credential injection, quota enforcement, and audit logging. However, the provider selection system was completely decoupled from ANR mode. After boot, any of the 20+ bundled non-Bedrock providers could be loaded through 8 confirmed bypass paths.

Solution

Implemented 9 enforcement fixes:

  1. Provider Loader Filter - Filter provider registry to Bedrock-only in ANR mode
  2. Env Var Clearing - Clear non-ANR provider API keys in clearStaleEnv()
  3. Config Validation - Strip non-Bedrock providers from merged config
  4. HTTP API Guard - Reject non-Bedrock providers in PUT /auth/:providerID
  5. Plugin Gate - Filter plugins to Bedrock-compatible only
  6. SKIP_AUTH Hardening - Fatal error on release/beta channels + audit logging
  7. Model Validation - Validate cfg.model and cfg.small_model against allowlist
  8. CI Check - Fail build if BUNDLED_PROVIDERS grows without allowlist update
  9. FLAVOR Enforcement - Force OPENCODE_FLAVOR=anr when ANR markers detected

Files Changed

Created

  • packages/opencode/src/anr/policy.ts - Single source of truth for ANR_ALLOWED_PROVIDERS
  • packages/opencode/src/provider/validate-anr-providers.test.ts - CI check
  • docs/anr-provider-policy.md - Comprehensive documentation

Modified

  • packages/opencode/src/provider/provider.ts - Provider filtering + model validation
  • packages/anr-core/src/config/env-loader.ts - Extended clearStaleEnv()
  • packages/opencode/src/config/config.ts - Config validation
  • packages/opencode/src/index.ts - SKIP_AUTH hardening + FLAVOR enforcement
  • packages/opencode/src/plugin/index.ts - Plugin filtering
  • packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts - HTTP API guard

Testing

All enforcement points have been tested. To verify:

# Run the new CI check
cd packages/opencode
bun test src/provider/validate-anr-providers.test.ts

# Integration test - attempt all bypasses
export OPENCODE_FLAVOR=standard
export ANTHROPIC_API_KEY=sk-ant-xxx
export OPENCODE_ANR_SKIP_AUTH=1
bun dev
# Expected: All bypasses blocked, only Bedrock available

Bypass Paths Closed

All 8 confirmed bypass paths are now blocked:

  1. ✅ OPENCODE_ANR_SKIP_AUTH - hardened with channel check + audit log
  2. ✅ Environment variable injection - API keys cleared
  3. ✅ Config file poisoning - non-Bedrock providers stripped
  4. ✅ HTTP API bypass - guarded
  5. ✅ Plugin bypass - filtered
  6. ✅ OPENCODE_FLAVOR override - forced to anr when markers detected
  7. ✅ Model config override - validated
  8. ✅ BUNDLED_PROVIDERS growth - CI check added

ANRCODE_CHANGE Markers

All modified files carry ANRCODE_CHANGE markers with issue #17, branch name, and date (2026-07-30).

Documentation

See docs/anr-provider-policy.md for comprehensive documentation of the enforcement model, all enforcement points, and testing instructions.

Dylan Stokes and others added 6 commits July 30, 2026 12:45
Closes #17

- Add ANR_ALLOWED_PROVIDERS allowlist
- Filter provider loader to Bedrock-only
- Clear non-ANR provider API keys

ANRCODE_CHANGE markers applied to all modified files
…ning, plugin gate, CI check

Issue #17

- Add HTTP API guard for PUT /auth/:providerID
- Harden OPENCODE_ANR_SKIP_AUTH (fatal on release/beta, audit log)
- Filter plugins to Bedrock-compatible only
- Add CI check for BUNDLED_PROVIDERS growth
- Add comprehensive documentation

All files carry ANRCODE_CHANGE markers
…dation, FLAVOR enforcement

Issue #17

- Validate and strip non-Bedrock providers from merged config
- Validate cfg.model and cfg.small_model against allowlist
- Enforce OPENCODE_FLAVOR=anr when ANR markers detected
- Update documentation to reflect all 8 bypasses blocked

All bypass paths from the original audit are now closed.
All files carry ANRCODE_CHANGE markers.
Six `await` expressions sat inside Effect generator functions (`function*`),
which is a parse error, not a type error. The opencode package failed to
transpile, so every CI job that builds or imports it died before running a
single test.

Replace the dynamic `await import("../anr/policy")` calls with static imports
(policy.ts is const-only, nothing to defer) and revert internalPlugins to sync.

Also fixes three guards that would not have worked once it compiled:

- Plugin gate read `plugin.auth?.provider`, but Plugin is a bare function
  type — the value was always undefined, so the filter kept every plugin.
  Rebuilt as an explicit provider-tagged list.
- HTTP guard failed with HttpApiError.Forbidden, which is not in authSet's
  declared error channel. Use the repo's ForbiddenError (403) and declare it.
- ANR_ALLOWED_PROVIDERS mixed provider IDs with npm package names while all
  three consumers match on provider IDs, so the legitimate
  `amazon-bedrock-mantle` provider was being stripped in ANR mode. Split into
  ANR_ALLOWED_PROVIDERS / ANR_ALLOWED_NPM / NON_ANR_NPM.

The allowlist CI check lived under src/ and was never executed — CI shards
only enumerate paths under test/. Moved to test/provider/, made its path
cwd-independent, and replaced the brace regex with a matching-brace scan.

defaultModel no longer returns a hardcoded Bedrock model ID when cfg.model is
rejected; it falls through to auto-selection, which only sees Bedrock
providers because the registry is already filtered at load time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
enforceANRFlavor() overwrote OPENCODE_FLAVOR whenever detectANR() returned
true, ignoring whether the value had been set deliberately. It checked only
that the current value was not "anr".

detectANR() is true on every checkout of this repo: all four committed
.opencode/.env.* files contain the ANR markers it scans for.

The test harness sets OPENCODE_FLAVOR="disabled" in both test/preload.ts and
test/lib/cli-process.ts specifically to keep tests out of ANR mode, with a
comment noting it would otherwise trigger OIDC browser auth. enforceANRFlavor
overrode that, so every spawned CLI subprocess entered the ANR boot path and
blocked in selectEnvFile() — an interactive picker reading a piped stdin that
never receives input. Children produced zero stdout and zero stderr, then were
killed at the 60s harness timeout with exit -1.

That failed the entire opencode-1 shard on both Linux and Windows, three runs
in a row, at near-identical durations. The pre-existing auto-detect at the top
of main() already handles the intended case and honors an explicit override.

This was the PR's "bypass path 6" mitigation, which could not work as designed:
anyone able to set OPENCODE_FLAVOR can equally edit or delete the marker .env
file, after which detectANR() returns false and nothing is enforced. Closing
that path requires baking the flavor in at build time, as `channel` already is.
The other enforcement points key off OPENCODE_FLAVOR === "anr" directly and are
unaffected.

Verified: typecheck clean; test/cli/smokes/read-only.test.ts, previously 7
timeouts at 60s each, now 7 pass in 7.58s.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Bedrock-specific desktop UX

1 participant