mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
fix: preserve Langfuse settings when clearing secrets
This commit is contained in:
parent
bc636c9a2e
commit
ddb7090d64
3 changed files with 97 additions and 4 deletions
|
|
@ -919,6 +919,45 @@ describe('Agent Controllers - Mass Assignment Protection', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('should preserve existing Langfuse settings when update only clears the secret', async () => {
|
||||
const encryptedOriginal = await encryptStoredSecret('sk-original');
|
||||
await Agent.updateOne(
|
||||
{ id: existingAgentId },
|
||||
{
|
||||
langfuse: {
|
||||
enabled: true,
|
||||
publicKey: 'pk-original',
|
||||
secretKey: encryptedOriginal,
|
||||
baseUrl: 'https://cloud.langfuse.com',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
mockReq.params.id = existingAgentId;
|
||||
mockReq.body = {
|
||||
langfuse: {
|
||||
secretKey: LANGFUSE_SECRET_CLEAR_VALUE,
|
||||
},
|
||||
};
|
||||
|
||||
await updateAgentHandler(mockReq, mockRes);
|
||||
|
||||
expect(mockRes.json).toHaveBeenCalled();
|
||||
const updatedAgent = mockRes.json.mock.calls[0][0];
|
||||
expect(updatedAgent.langfuse).toEqual({
|
||||
enabled: true,
|
||||
publicKey: 'pk-original',
|
||||
baseUrl: 'https://cloud.langfuse.com',
|
||||
});
|
||||
|
||||
const agentInDb = await Agent.findOne({ id: existingAgentId }).lean();
|
||||
expect(agentInDb.langfuse).toEqual({
|
||||
enabled: true,
|
||||
publicKey: 'pk-original',
|
||||
baseUrl: 'https://cloud.langfuse.com',
|
||||
});
|
||||
});
|
||||
|
||||
test('uploadAgentAvatarHandler should redact Langfuse secret in response', async () => {
|
||||
await Agent.updateOne(
|
||||
{ id: existingAgentId },
|
||||
|
|
|
|||
|
|
@ -38,6 +38,46 @@ describe('normalizeLangfuseConfig', () => {
|
|||
publicKey: 'pk-agent',
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves existing non-secret fields when clearing only the secret', async () => {
|
||||
const result = await normalizeLangfuseConfig(
|
||||
{
|
||||
secretKey: LANGFUSE_SECRET_CLEAR_VALUE,
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
publicKey: 'pk-agent',
|
||||
secretKey: '0123456789abcdef0123456789abcdef:736b2d6167656e74',
|
||||
baseUrl: 'https://cloud.langfuse.com',
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
enabled: true,
|
||||
publicKey: 'pk-agent',
|
||||
baseUrl: 'https://cloud.langfuse.com',
|
||||
});
|
||||
});
|
||||
|
||||
it('clears explicit blank non-secret fields while preserving absent fields', async () => {
|
||||
const result = await normalizeLangfuseConfig(
|
||||
{
|
||||
publicKey: '',
|
||||
secretKey: LANGFUSE_SECRET_CLEAR_VALUE,
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
publicKey: 'pk-agent',
|
||||
secretKey: '0123456789abcdef0123456789abcdef:736b2d6167656e74',
|
||||
baseUrl: 'https://cloud.langfuse.com',
|
||||
},
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
enabled: true,
|
||||
baseUrl: 'https://cloud.langfuse.com',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('redactLangfuseSecret', () => {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|||
return value != null && typeof value === 'object' && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function hasOwn(value: Record<string, unknown>, key: string): boolean {
|
||||
return Object.prototype.hasOwnProperty.call(value, key);
|
||||
}
|
||||
|
||||
function toPlainObject(value: unknown): unknown {
|
||||
if (!isRecord(value) || typeof value.toObject !== 'function') {
|
||||
return value;
|
||||
|
|
@ -57,14 +61,24 @@ export async function normalizeLangfuseConfig(
|
|||
const existingLangfuse = isRecord(existingConfig) ? existingConfig : {};
|
||||
const normalized: LangfuseConfig = {};
|
||||
|
||||
if (typeof incoming.enabled === 'boolean') {
|
||||
if (hasOwn(incoming, 'enabled') && typeof incoming.enabled === 'boolean') {
|
||||
normalized.enabled = incoming.enabled;
|
||||
} else if (typeof existingLangfuse.enabled === 'boolean') {
|
||||
normalized.enabled = existingLangfuse.enabled;
|
||||
}
|
||||
|
||||
for (const key of ['publicKey', 'baseUrl'] as const) {
|
||||
const value = incoming[key];
|
||||
if (isNonEmptyString(value)) {
|
||||
normalized[key] = value.trim();
|
||||
if (hasOwn(incoming, key)) {
|
||||
const value = incoming[key];
|
||||
if (isNonEmptyString(value)) {
|
||||
normalized[key] = value.trim();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const existingValue = existingLangfuse[key];
|
||||
if (isNonEmptyString(existingValue)) {
|
||||
normalized[key] = existingValue.trim();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue