mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-01 07:03:02 +00:00
fix(ui): guard i18n label lookups against prototype-named values
A server value such as "toString" used as a key into a label map resolved to an Object.prototype member and was passed to t(), which throws. Check own properties before translating so those values fall back to the raw string as before
This commit is contained in:
parent
bfe84f4dee
commit
daf620353d
12 changed files with 48 additions and 23 deletions
|
|
@ -23,7 +23,9 @@ const RedisTypeSelector: React.FC<RedisTypeSelectorProps> = ({ redisType, redisT
|
|||
<label className="text-sm font-medium">{t("caching.settings.redisType.label")}</label>
|
||||
<Select value={redisType} onValueChange={(value) => value !== null && onTypeChange(value)}>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue>{REDIS_TYPE_LABELS[redisType] ? t(REDIS_TYPE_LABELS[redisType]) : redisType}</SelectValue>
|
||||
<SelectValue>
|
||||
{Object.hasOwn(REDIS_TYPE_LABELS, redisType) ? t(REDIS_TYPE_LABELS[redisType]) : redisType}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{Object.entries(REDIS_TYPE_LABELS).map(([value, label]) => (
|
||||
|
|
@ -34,7 +36,10 @@ const RedisTypeSelector: React.FC<RedisTypeSelectorProps> = ({ redisType, redisT
|
|||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t(redisTypeDescriptions[redisType] || "caching.settings.redisType.fallbackDescription")}
|
||||
{t(
|
||||
(Object.hasOwn(redisTypeDescriptions, redisType) && redisTypeDescriptions[redisType]) ||
|
||||
"caching.settings.redisType.fallbackDescription",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@ const CoordinationRedisTypeSelector: React.FC<CoordinationRedisTypeSelectorProps
|
|||
</label>
|
||||
<Select value={redisType} onValueChange={(value) => value !== null && onTypeChange(value)}>
|
||||
<SelectTrigger id="coordination-redis-type" className="w-full">
|
||||
<SelectValue>{t(COORDINATION_REDIS_TYPE_LABELS[redisType])}</SelectValue>
|
||||
<SelectValue>
|
||||
{Object.hasOwn(COORDINATION_REDIS_TYPE_LABELS, redisType)
|
||||
? t(COORDINATION_REDIS_TYPE_LABELS[redisType])
|
||||
: ""}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{COORDINATION_REDIS_TYPES.map((type) => (
|
||||
|
|
|
|||
|
|
@ -156,6 +156,6 @@ export const sourceBadge = (
|
|||
t: TFunction = i18n.t.bind(i18n),
|
||||
): SourceBadgeDescriptor => {
|
||||
const known: Readonly<Record<string, SourceBadgeKeys>> = SOURCE_BADGES;
|
||||
const badge = (source && known[source]) || NOT_CONFIGURED_BADGE;
|
||||
const badge = source && Object.hasOwn(known, source) ? known[source] : NOT_CONFIGURED_BADGE;
|
||||
return { tone: badge.tone, label: t(badge.label), tooltip: t(badge.tooltip) };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export default function CyberArk() {
|
|||
onSuccess: () => {
|
||||
toast.success(
|
||||
t("adminPanel.cyberArk.toasts.fieldCleared", {
|
||||
field: (FIELD_LABELS[clearingField] && t(FIELD_LABELS[clearingField])) ?? clearingField,
|
||||
field: (Object.hasOwn(FIELD_LABELS, clearingField) && t(FIELD_LABELS[clearingField])) || clearingField,
|
||||
}),
|
||||
);
|
||||
setClearingField(null);
|
||||
|
|
@ -103,7 +103,7 @@ export default function CyberArk() {
|
|||
variant="ghost"
|
||||
size="icon-sm"
|
||||
aria-label={t("adminPanel.cyberArk.actions.clearField", {
|
||||
field: (FIELD_LABELS[key] && t(FIELD_LABELS[key])) ?? key,
|
||||
field: (Object.hasOwn(FIELD_LABELS, key) && t(FIELD_LABELS[key])) || key,
|
||||
})}
|
||||
onClick={() => setClearingField(key)}
|
||||
>
|
||||
|
|
@ -191,7 +191,7 @@ export default function CyberArk() {
|
|||
<dl className="divide-y divide-border overflow-hidden rounded-md border border-border">
|
||||
<DetailRow label={t("adminPanel.cyberArk.authMethodLabel")}>{detectAuthMethod(rawValues, t)}</DetailRow>
|
||||
{fieldsToShow.map(([key]) => (
|
||||
<DetailRow key={key} label={(FIELD_LABELS[key] && t(FIELD_LABELS[key])) ?? key}>
|
||||
<DetailRow key={key} label={(Object.hasOwn(FIELD_LABELS, key) && t(FIELD_LABELS[key])) || key}>
|
||||
{renderValue(key)}
|
||||
</DetailRow>
|
||||
))}
|
||||
|
|
@ -229,7 +229,9 @@ export default function CyberArk() {
|
|||
<DeleteResourceModal
|
||||
isOpen={clearingField !== null}
|
||||
title={t("adminPanel.cyberArk.clearModal.title", {
|
||||
field: clearingField ? (FIELD_LABELS[clearingField] && t(FIELD_LABELS[clearingField])) ?? clearingField : "",
|
||||
field: clearingField
|
||||
? (Object.hasOwn(FIELD_LABELS, clearingField) && t(FIELD_LABELS[clearingField])) || clearingField
|
||||
: "",
|
||||
})}
|
||||
message={t("adminPanel.cyberArk.clearModal.message")}
|
||||
resourceInformationTitle={t("adminPanel.cyberArk.clearModal.resourceTitle")}
|
||||
|
|
@ -237,7 +239,7 @@ export default function CyberArk() {
|
|||
{
|
||||
label: t("adminPanel.cyberArk.clearModal.fieldLabel"),
|
||||
value: clearingField
|
||||
? (FIELD_LABELS[clearingField] && t(FIELD_LABELS[clearingField])) ?? clearingField
|
||||
? (Object.hasOwn(FIELD_LABELS, clearingField) && t(FIELD_LABELS[clearingField])) || clearingField
|
||||
: "",
|
||||
},
|
||||
]}
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ const EditCyberArkModal: React.FC<EditCyberArkModalProps> = ({ isVisible, onCanc
|
|||
key={fieldName}
|
||||
control={form.control}
|
||||
name={fieldName}
|
||||
label={(FIELD_LABELS[fieldName] && t(FIELD_LABELS[fieldName])) ?? fieldName}
|
||||
label={(Object.hasOwn(FIELD_LABELS, fieldName) && t(FIELD_LABELS[fieldName])) || fieldName}
|
||||
>
|
||||
{({ ref, ...field }) =>
|
||||
isSensitive ? (
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ const EditHashicorpVaultModal: React.FC<EditHashicorpVaultModalProps> = ({ isVis
|
|||
key={fieldName}
|
||||
control={form.control}
|
||||
name={fieldName}
|
||||
label={(FIELD_LABELS[fieldName] && t(FIELD_LABELS[fieldName])) ?? fieldName}
|
||||
label={(Object.hasOwn(FIELD_LABELS, fieldName) && t(FIELD_LABELS[fieldName])) || fieldName}
|
||||
>
|
||||
{({ ref, ...field }) =>
|
||||
isSensitive ? (
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export default function HashicorpVault() {
|
|||
onSuccess: () => {
|
||||
toast.success(
|
||||
t("adminPanel.hashicorpVault.toasts.fieldCleared", {
|
||||
field: (FIELD_LABELS[clearingField] && t(FIELD_LABELS[clearingField])) ?? clearingField,
|
||||
field: (Object.hasOwn(FIELD_LABELS, clearingField) && t(FIELD_LABELS[clearingField])) || clearingField,
|
||||
}),
|
||||
);
|
||||
setClearingField(null);
|
||||
|
|
@ -105,7 +105,7 @@ export default function HashicorpVault() {
|
|||
variant="ghost"
|
||||
size="icon-sm"
|
||||
aria-label={t("adminPanel.hashicorpVault.actions.clearField", {
|
||||
field: (FIELD_LABELS[key] && t(FIELD_LABELS[key])) ?? key,
|
||||
field: (Object.hasOwn(FIELD_LABELS, key) && t(FIELD_LABELS[key])) || key,
|
||||
})}
|
||||
onClick={() => setClearingField(key)}
|
||||
>
|
||||
|
|
@ -193,7 +193,7 @@ export default function HashicorpVault() {
|
|||
{detectAuthMethod(rawValues, t)}
|
||||
</DetailRow>
|
||||
{fieldsToShow.map(([key]) => (
|
||||
<DetailRow key={key} label={(FIELD_LABELS[key] && t(FIELD_LABELS[key])) ?? key}>
|
||||
<DetailRow key={key} label={(Object.hasOwn(FIELD_LABELS, key) && t(FIELD_LABELS[key])) || key}>
|
||||
{renderValue(key)}
|
||||
</DetailRow>
|
||||
))}
|
||||
|
|
@ -226,7 +226,9 @@ export default function HashicorpVault() {
|
|||
<DeleteResourceModal
|
||||
isOpen={clearingField !== null}
|
||||
title={t("adminPanel.hashicorpVault.clearModal.title", {
|
||||
field: clearingField ? (FIELD_LABELS[clearingField] && t(FIELD_LABELS[clearingField])) ?? clearingField : "",
|
||||
field: clearingField
|
||||
? (Object.hasOwn(FIELD_LABELS, clearingField) && t(FIELD_LABELS[clearingField])) || clearingField
|
||||
: "",
|
||||
})}
|
||||
message={t("adminPanel.hashicorpVault.clearModal.message")}
|
||||
resourceInformationTitle={t("adminPanel.hashicorpVault.clearModal.resourceTitle")}
|
||||
|
|
@ -234,7 +236,7 @@ export default function HashicorpVault() {
|
|||
{
|
||||
label: t("adminPanel.hashicorpVault.clearModal.fieldLabel"),
|
||||
value: clearingField
|
||||
? (FIELD_LABELS[clearingField] && t(FIELD_LABELS[clearingField])) ?? clearingField
|
||||
? (Object.hasOwn(FIELD_LABELS, clearingField) && t(FIELD_LABELS[clearingField])) || clearingField
|
||||
: "",
|
||||
},
|
||||
]}
|
||||
|
|
|
|||
|
|
@ -387,7 +387,9 @@ export const SSOProviderSelectField = () => {
|
|||
<Logo
|
||||
src={logo}
|
||||
label={
|
||||
ssoProviderDisplayNames[optionValue] ? t(ssoProviderDisplayNames[optionValue]) : optionValue
|
||||
Object.hasOwn(ssoProviderDisplayNames, optionValue)
|
||||
? t(ssoProviderDisplayNames[optionValue])
|
||||
: optionValue
|
||||
}
|
||||
className="h-6 w-6 mr-3 object-contain"
|
||||
/>
|
||||
|
|
@ -477,7 +479,7 @@ const DEFAULT_ROLE_OPTIONS: ReadonlyArray<{ value: string; label: ParseKeys }> =
|
|||
];
|
||||
|
||||
const providerOptionLabel = (value: string, t: TFunction) =>
|
||||
ssoProviderDisplayNames[value]
|
||||
Object.hasOwn(ssoProviderDisplayNames, value)
|
||||
? t(ssoProviderDisplayNames[value])
|
||||
: t("adminPanel.ssoSettings.providers.fallback", { name: value.charAt(0).toUpperCase() + value.slice(1) });
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,13 @@ export default function RoleMappings({ roleMappings }: { roleMappings: RoleMappi
|
|||
id: "role",
|
||||
accessorKey: "role",
|
||||
header: t("adminPanel.ssoSettings.roleMappings.columns.role"),
|
||||
cell: ({ row }) => <strong className="font-semibold">{t(defaultRoleDisplayNames[row.original.role])}</strong>,
|
||||
cell: ({ row }) => (
|
||||
<strong className="font-semibold">
|
||||
{Object.hasOwn(defaultRoleDisplayNames, row.original.role)
|
||||
? t(defaultRoleDisplayNames[row.original.role])
|
||||
: ""}
|
||||
</strong>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "groups",
|
||||
|
|
@ -69,7 +75,11 @@ export default function RoleMappings({ roleMappings }: { roleMappings: RoleMappi
|
|||
{t("adminPanel.ssoSettings.roleMappings.defaultRole")}
|
||||
</h5>
|
||||
<div>
|
||||
<strong className="font-semibold">{t(defaultRoleDisplayNames[roleMappings.default_role])}</strong>
|
||||
<strong className="font-semibold">
|
||||
{Object.hasOwn(defaultRoleDisplayNames, roleMappings.default_role)
|
||||
? t(defaultRoleDisplayNames[roleMappings.default_role])
|
||||
: ""}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ export default function SSOSettings() {
|
|||
<Logo
|
||||
src={ssoProviderLogoMap[selectedProvider]}
|
||||
label={
|
||||
ssoProviderDisplayNames[selectedProvider]
|
||||
Object.hasOwn(ssoProviderDisplayNames, selectedProvider)
|
||||
? t(ssoProviderDisplayNames[selectedProvider])
|
||||
: selectedProvider
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ export const getLoggingCallbacksTableColumns = (
|
|||
return (
|
||||
<StatusBadge
|
||||
tone={callbackModeTone(mode)}
|
||||
label={CALLBACK_MODE_LABELS[mode] ? t(CALLBACK_MODE_LABELS[mode]) : mode}
|
||||
label={Object.hasOwn(CALLBACK_MODE_LABELS, mode) ? t(CALLBACK_MODE_LABELS[mode]) : mode}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ const STRATEGY_LABELS: Readonly<Record<string, ParseKeys>> = {
|
|||
};
|
||||
|
||||
export const formatStrategyLabel = (strategy: string, t: TFunction): string =>
|
||||
STRATEGY_LABELS[strategy] ? t(STRATEGY_LABELS[strategy]) : strategy;
|
||||
Object.hasOwn(STRATEGY_LABELS, strategy) ? t(STRATEGY_LABELS[strategy]) : strategy;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue