diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/SensitiveKeysTab.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/SensitiveKeysTab.tsx new file mode 100644 index 00000000..7faa7d03 --- /dev/null +++ b/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/SensitiveKeysTab.tsx @@ -0,0 +1,123 @@ +import { useState } from 'react'; +import { Badge, Button, Input, Tag } from '@cameleer/design-system'; +import { Shield, Info } from 'lucide-react'; +import { useSensitiveKeys } from '../../../../api/queries/admin/sensitive-keys'; +import type { SensitiveKeysFormState } from '../hooks/useDeploymentPageState'; +import skStyles from '../../../Admin/SensitiveKeysPage.module.css'; + +const AGENT_DEFAULTS = [ + 'Authorization', + 'Cookie', + 'Set-Cookie', + 'X-API-Key', + 'X-Auth-Token', + 'Proxy-Authorization', +]; + +interface Props { + value: SensitiveKeysFormState; + onChange: (next: SensitiveKeysFormState) => void; + disabled?: boolean; +} + +export function SensitiveKeysTab({ value, onChange, disabled }: Props) { + const [newKey, setNewKey] = useState(''); + const { data: globalKeysConfig } = useSensitiveKeys(); + const globalKeys = globalKeysConfig?.keys ?? []; + + function addKey() { + const v = newKey.trim(); + if (v && !value.sensitiveKeys.some((k) => k.toLowerCase() === v.toLowerCase())) { + onChange({ sensitiveKeys: [...value.sensitiveKeys, v] }); + setNewKey(''); + } + } + + function removeKey(index: number) { + onChange({ sensitiveKeys: value.sensitiveKeys.filter((_, i) => i !== index) }); + } + + return ( +
+
+ + Agent built-in defaults +
+
+ {AGENT_DEFAULTS.map((key) => ( + + ))} +
+ + {globalKeys.length > 0 && ( + <> +
+
+ Global keys (enforced) + {globalKeys.length} +
+
+ {globalKeys.map((key) => ( + + ))} +
+ + )} + +
+
+ Application-specific keys + {value.sensitiveKeys.length > 0 && ( + {value.sensitiveKeys.length} + )} +
+ +
+ {value.sensitiveKeys.map((k, i) => ( + !disabled && removeKey(i)} + /> + ))} + {value.sensitiveKeys.length === 0 && ( + + No app-specific keys — agents use built-in defaults + {globalKeys.length > 0 ? ' and global keys' : ''} + + )} +
+ +
+ setNewKey(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + addKey(); + } + }} + placeholder="Add key or glob pattern (e.g. *password*)" + disabled={disabled} + /> + +
+ +
+ + + The final masking configuration is: agent defaults + global keys + app-specific keys. + Supports exact header names and glob patterns. + +
+
+ ); +}