fix(guardrails): enforce monitor mode and fail output closed by default - #640
Conversation
Two guardrail behaviors contradicted their configuration (AISIX-Cloud#788
P1-3).
enforcement_mode: monitor was stored but never enforced — the DP logged a
warning and then hard-blocked anyway, so operators who expected an audit
mode were actually rejecting traffic. build_one now wraps a monitor-mode
row in a MonitorGuardrail decorator that runs the rule exactly as configured
but downgrades a Block to Allow, emitting an audit log line ("would have
blocked") with the guardrail name and hook side. It also reports
EndOfStreamCheck for streamed output so a rule that can never block does not
force the response to hold back; it can never weaken a blocking peer because
the chain keeps the strictest member's policy. An unrecognized mode falls
back to block (fail-safe). chain.rs and index.rs are untouched.
Remote guardrail output fail-open was inconsistent: Bedrock and Azure Prompt
Shield used a single fail_open (default true) for both hooks, so a provider
outage on the output side released unscanned model output. They now carry an
independent output_fail_open (default false / fail-closed), matching the
Azure/Aliyun text-moderation guardrails. The input hook still follows the
outer fail_open; only the output hook changes its default.
Behavior changes:
- monitor-mode guardrails observe and log instead of blocking.
- Bedrock / azure_content_safety output hook fails closed by default on a
provider outage. Operators who want the old behavior set
output_fail_open: true.
Part of api7/AISIX-Cloud#788
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds independent per-hook fail-open policies to Bedrock and Azure Content Safety guardrails: the input hook follows the existing outer ChangesPer-hook output fail-open policy (Bedrock + Azure Content Safety)
Monitor enforcement mode
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes 🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/aisix-core/src/models/guardrail.rs`:
- Around line 131-134: The doc comment for the fail-open policy field in the
guardrail model at lines 131-134 contains implementation details and internal
references that are not appropriate for public API documentation. Rewrite this
comment to focus on clear, operator-facing semantics by removing references to
"outer `fail_open`" and internal comparisons like "Mirrors the independent
output policy the Azure/Aliyun text-moderation guardrails already use". Describe
what the feature does from the user's perspective in straightforward language.
Apply the same user-facing approach to the additional comments at lines 356-359,
then regenerate the JSON schema to reflect the updated model documentation.
In `@crates/aisix-guardrails/src/bedrock.rs`:
- Line 519: The test in the BedrockGuardrail::new call is setting both the outer
fail_open parameter and output_fail_open to true, which means the test cannot
distinguish whether output is correctly using its own policy versus accidentally
using the input policy. Change the fourth parameter (the outer fail_open) from
true to false in the BedrockGuardrail::new call so that input and output
policies are properly distinguished during testing.
In `@crates/aisix-guardrails/src/prompt_shield.rs`:
- Around line 563-566: The output-policy tests are not properly verifying the
HTTP path and wiring because they can pass for the wrong reason (404/ConfigError
can also block). Replace the direct call to `handle_failure` in the opt-in test
with a call to `check_output` function and pass `fail_open=false` as a
parameter. This will prove that the output hook properly uses the
`output_fail_open` setting and actually exercises the intended HTTP path with
the 500 response from the mock at `/contentsafety/text:shieldPrompt`, rather
than just relying on unmatched mocks returning errors.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c3f122c8-bf4c-42a2-b702-701dbceb1418
📒 Files selected for processing (6)
crates/aisix-core/src/models/guardrail.rscrates/aisix-guardrails/src/bedrock.rscrates/aisix-guardrails/src/build.rscrates/aisix-guardrails/src/prompt_shield.rsschemas/resources/guardrail.schema.jsontests/e2e/src/cases/guardrail-monitor-mode-e2e.test.ts
Address CodeRabbit on #640: - Reword output_fail_open schema descriptions to user-facing semantics (drop the internal 'outer fail_open' / 'mirrors Azure/Aliyun' phrasing); regenerate guardrail.schema.json. - Bedrock opt-in test sets input fail_open=false so a passing output bypass proves the output hook uses output_fail_open, not the input policy; also assert input still blocks. - Prompt Shield output tests drive the real HTTP path (path + api-version matcher, expect(1)) so the verdict can't come from an unmatched-route 404; the opt-in test runs check_output with input fail_open=false.
Problem
Two guardrail behaviors contradicted their own configuration (architecture review item AISIX-Cloud#788 P1-3):
enforcement_mode: monitorwas never enforced. The DP stored the field, logged a warning at chain-build time, then hard-blocked anyway. Operators who set monitor expecting an audit/observe mode were actually rejecting traffic.azure_content_safety) used a singlefail_open(defaulttrue) for both the input and output hooks, so a provider outage on the output side released unscanned model output. The Azure/Aliyun text-moderation guardrails already split this with an independentoutput_fail_opendefaulting to fail-closed.Implementation
Monitor mode —
build_onenow appliesenforcement_modeafter building a row's runtime guardrail:block(default): returned as-is.monitor: wrapped in aMonitorGuardraildecorator that runs the rule exactly as configured but downgrades aBlockverdict toAllow, emitting an audit log line (guardrail name +input/outputhook + reason). It reportsEndOfStreamCheckfor streamed output so a rule that can never block does not force hold-back; it can never weaken a blocking peer because the chain folds to the strictest member.block(fail-safe) with a warning.chain.rsandindex.rsare untouched — the decorator is fully contained inbuild.rs.Output fail-closed —
BedrockConfigandAzureContentSafetyConfiggain an independentoutput_fail_open(#[serde(default)]→false). The bedrock/prompt-shield dispatchers now pick the input-sidefail_openforcheck_inputandoutput_fail_openforcheck_output, mirroring the existing Azure/Aliyun text-moderation pattern. The runtime/published JSON Schema is regenerated viadump-schema.Behavior changes
bedrock/azure_content_safetyoutput hook now fails closed by default on a provider outage. Operators who prefer availability over correctness setoutput_fail_open: true. The input hook is unchanged.Tests
tests/e2e/src/cases/guardrail-monitor-mode-e2e.test.ts): creates a keyword rule inblockmode, confirms it 422s, flips the same rule tomonitor, and confirms the forbidden request now reaches the upstream — the block→monitor flip makes the assertion non-racy.Docs: paired
api7/docsPR documentsenforcement_modeand the input/output fail-open split.Part of api7/AISIX-Cloud#788
Summary by CodeRabbit
New Features
Enhancements
output_fail_openpolicy for the output hook, defaulting to fail-closed while the input hook continues using the existing top-levelfail_open.Testing / Validation
Fixes api7/AISIX-Cloud#788