Repository navigation
feat(guardrails): PII detection and redaction (mask/block) for request and response bodies #694
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2d7abd3
a03fb31
acb8f0c
ee17abf
1d4d846
905e6e7
6cb660b
d672128
2f76e09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,7 +24,8 @@ use async_trait::async_trait; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::index::{GuardrailIndex, RequestContext, ScopeKind}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::keyword::{KeywordBlocklist, KeywordRule}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::{Guardrail, GuardrailChain, GuardrailVerdict, StreamOutputPolicy}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::pii::{builtin_rule, PiiAction, PiiGuardrail, PiiRule}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::{Guardrail, GuardrailChain, GuardrailVerdict, Redaction, StreamOutputPolicy}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// A snapshot table's guardrail entries in deterministic chain order: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// `created_at` ascending (RFC3339 strings in a fixed offset compare | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -211,6 +212,58 @@ fn build_one_inner( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(Some(Arc::new(blocklist))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GuardrailKind::Pii(cfg) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if cfg.detectors.is_empty() && cfg.custom_patterns.is_empty() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Ok(None); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let default_action = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PiiAction::parse(&cfg.default_action).ok_or_else(|| BuildError::InvalidValue { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| field: "default_action", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: cfg.default_action.clone(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut rules: Vec<PiiRule> = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Vec::with_capacity(cfg.detectors.len() + cfg.custom_patterns.len()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for d in &cfg.detectors { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let action = match d.action.as_deref() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None => default_action, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Some(s) => PiiAction::parse(s).ok_or_else(|| BuildError::InvalidValue { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| field: "detectors[].action", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: s.to_owned(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let rule = builtin_rule(&d.detector_type, action).ok_or_else(|| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BuildError::InvalidValue { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| field: "detectors[].type", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: d.detector_type.clone(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rules.push(rule); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for p in &cfg.custom_patterns { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let action = match p.action.as_deref() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None => default_action, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Some(s) => PiiAction::parse(s).ok_or_else(|| BuildError::InvalidValue { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| field: "custom_patterns[].action", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: s.to_owned(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let rule = PiiRule::new(p.name.clone(), &p.regex, action, None).map_err(|e| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BuildError::InvalidRegex { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern: p.regex.clone(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source: e, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })?; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rules.push(rule); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let on_exceeded_fail_open = cfg.on_buffer_exceeded == "fail_open"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Validate Unknown values currently become fail-closed via Proposed fix- let on_exceeded_fail_open = cfg.on_buffer_exceeded == "fail_open";
+ let on_exceeded_fail_open = match cfg.on_buffer_exceeded.as_str() {
+ "fail_open" => true,
+ "fail_closed" => false,
+ other => {
+ return Err(BuildError::InvalidValue {
+ field: "on_buffer_exceeded",
+ value: other.to_owned(),
+ });
+ }
+ };📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let g = PiiGuardrail::new( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rules, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| row.hook_point, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| usize::try_from(cfg.max_buffer_bytes).unwrap_or(usize::MAX), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| on_exceeded_fail_open, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(Some(Arc::new(g))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[cfg(feature = "bedrock")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GuardrailKind::Bedrock(cfg) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Phase 2: build the AWS-SDK-backed dispatcher. cp-api | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -299,6 +352,11 @@ enum BuildError { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pattern: String, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source: regex::Error, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// An enum-ish config field carrying a value the DP doesn't know | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// (unknown built-in detector id, unrecognised action). The row is | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// skipped + warned rather than silently running a weaker policy. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[error("invalid {field} value {value:?}")] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| InvalidValue { field: &'static str, value: String }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// A guardrail kind whose runtime dispatch was compiled out via | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// feature flags (e.g. a pruned build that excluded `--features bedrock` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// or `--features azure-content-safety`). The chain treats the row as | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -331,6 +389,19 @@ struct MonitorGuardrail { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl MonitorGuardrail { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Log the mask counts a monitor-mode redacting guardrail WOULD have | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// applied. Counts carry detector names only, never matched values. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn observe_redaction(&self, hook: &'static str, r: Option<Redaction>) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(r) = r { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tracing::info!( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| guardrail_name = %self.row_name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hook, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| counts = ?r.counts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "guardrail in monitor mode observed maskable spans; not redacting (enforcement_mode=monitor)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn observe(&self, hook: &'static str, verdict: GuardrailVerdict) -> GuardrailVerdict { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| match verdict { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GuardrailVerdict::Block { reason, .. } => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -372,6 +443,28 @@ impl Guardrail for MonitorGuardrail { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn runs_on_output(&self) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.inner.runs_on_output() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Monitor mode observes without modifying: redaction is suppressed the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // same way Block verdicts are downgraded. The would-be mask counts are | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // logged so operators can stage a redaction rule and audit its impact | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // before enforcing it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redacts_input(&self) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redacts_output(&self) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redact_input_text(&self, text: &str) -> Option<Redaction> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.observe_redaction("input", self.inner.redact_input_text(text)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redact_output_text(&self, text: &str) -> Option<Redaction> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.observe_redaction("output", self.inner.redact_output_text(text)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+451
to
+467
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Keep monitor redaction discoverable so observation runs. Returning Proposed fix fn redacts_input(&self) -> bool {
- false
+ self.inner.redacts_input()
}
fn redacts_output(&self) -> bool {
- false
+ self.inner.redacts_output()
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// `mandatory: true` decorator. A remote guardrail that can't reach its | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -440,6 +533,24 @@ impl Guardrail for MandatoryGuardrail { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn runs_on_output(&self) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.inner.runs_on_output() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Mandatory only rewrites the failure (Bypass) verdict; redaction | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // passes straight through to the inner guardrail. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redacts_input(&self) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.inner.redacts_input() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redacts_output(&self) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.inner.redacts_output() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redact_input_text(&self, text: &str) -> Option<Redaction> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.inner.redact_input_text(text) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redact_output_text(&self, text: &str) -> Option<Redaction> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.inner.redact_output_text(text) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Adapter that wraps a snapshot handle and rebuilds the runtime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -538,6 +649,22 @@ impl Guardrail for LiveGuardrailChain { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn runs_on_output(&self) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.current().runs_on_output() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redacts_input(&self) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.current().redacts_input() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redacts_output(&self) -> bool { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.current().redacts_output() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redact_input_text(&self, text: &str) -> Option<Redaction> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.current().redact_input_text(text) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn redact_output_text(&self, text: &str) -> Option<Redaction> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.current().redact_output_text(text) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // --------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: api7/aisix
Length of output: 1349
🏁 Script executed:
Repository: api7/aisix
Length of output: 16164
🏁 Script executed:
Repository: api7/aisix
Length of output: 35620
Add enums for the PII policy fields
actionanddefault_actionalready fail fast in the build layer, but the schema still leaves them as free-form strings.PiiConfig.on_buffer_exceededis the bigger gap: any value other thanfail_openis treated asfail_closed. Constrain these wire values so admin-side validation matches the runtime contract.🤖 Prompt for AI Agents