Skip to content

Commit e4935bf

Browse files
fix(guardrails): coerce stored redaction language in the resolver
The persist-path resolver accepted any stored language string, so a stale/invalid code (e.g. a dropped locale) would reach Presidio and scrub the log even though the admin UI shows English. Coerce against the supported set via a shared coercePiiLanguage helper (now reused by the data-retention route too), falling back to en for unknown values.
1 parent 67b303f commit e4935bf

4 files changed

Lines changed: 24 additions & 10 deletions

File tree

apps/sim/app/api/organizations/[id]/data-retention/route.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,7 @@ import { isOrganizationOnEnterprisePlan } from '@/lib/billing/core/subscription'
1616
import { isBillingEnabled } from '@/lib/core/config/env-flags'
1717
import { isFeatureEnabled } from '@/lib/core/config/feature-flags'
1818
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
19-
import { PII_LANGUAGE_CODES, type PIILanguage } from '@/lib/guardrails/pii-entities'
20-
21-
/** Narrow a stored (loosely-typed) language to the supported set; unknown ⇒ undefined (defaults to en). */
22-
function coercePiiLanguage(value: string | undefined): PIILanguage | undefined {
23-
return value && (PII_LANGUAGE_CODES as readonly string[]).includes(value)
24-
? (value as PIILanguage)
25-
: undefined
26-
}
19+
import { coercePiiLanguage } from '@/lib/guardrails/pii-entities'
2720

2821
const logger = createLogger('DataRetentionAPI')
2922

apps/sim/lib/billing/retention.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ describe('resolveEffectivePiiRedaction', () => {
4646
expect(result).toEqual({ enabled: true, entityTypes: ['ES_NIF'], language: 'es' })
4747
})
4848

49+
it('falls back to en when a stored language is unsupported/stale', () => {
50+
const result = resolveEffectivePiiRedaction({
51+
orgSettings: settings([
52+
{ id: 'r-de', entityTypes: ['EMAIL_ADDRESS'], workspaceId: 'ws-1', language: 'de' },
53+
]),
54+
workspaceId: 'ws-1',
55+
})
56+
expect(result).toEqual({ enabled: true, entityTypes: ['EMAIL_ADDRESS'], language: 'en' })
57+
})
58+
4959
it('exempts a workspace when its specific rule has no entity types', () => {
5060
const result = resolveEffectivePiiRedaction({
5161
orgSettings: settings([allRule, { id: 'r-1', entityTypes: [], workspaceId: 'ws-1' }]),

apps/sim/lib/billing/retention.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { DataRetentionSettings } from '@sim/db/schema'
2-
import { DEFAULT_PII_LANGUAGE } from '@/lib/guardrails/pii-entities'
2+
import { coercePiiLanguage, DEFAULT_PII_LANGUAGE } from '@/lib/guardrails/pii-entities'
33

44
export interface EffectivePiiRedaction {
55
enabled: boolean
@@ -38,6 +38,6 @@ export function resolveEffectivePiiRedaction(params: {
3838
? rule.entityTypes.filter((t): t is string => typeof t === 'string')
3939
: []
4040
if (types.length === 0) return DEFAULT_PII_REDACTION
41-
const language = typeof rule?.language === 'string' ? rule.language : DEFAULT_PII_LANGUAGE
41+
const language = coercePiiLanguage(rule?.language) ?? DEFAULT_PII_LANGUAGE
4242
return { enabled: true, entityTypes: types, language }
4343
}

apps/sim/lib/guardrails/pii-entities.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,14 @@ export const PII_LANGUAGE_CODES = PII_LANGUAGES.map((l) => l.value) as [
145145

146146
/** Default redaction language when a rule doesn't set one. */
147147
export const DEFAULT_PII_LANGUAGE: PIILanguage = 'en'
148+
149+
/**
150+
* Narrow a loosely-typed (stored/legacy) language to a supported code. Unknown or
151+
* stale values (e.g. a dropped locale) return `undefined` so callers fall back to
152+
* the default rather than forwarding an unsupported language to Presidio.
153+
*/
154+
export function coercePiiLanguage(value: string | undefined): PIILanguage | undefined {
155+
return value && (PII_LANGUAGE_CODES as readonly string[]).includes(value)
156+
? (value as PIILanguage)
157+
: undefined
158+
}

0 commit comments

Comments
 (0)