mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-27 20:24:21 +00:00
* feat: introduce optional content protection seam * feat: enforce source-aware content filters * feat: complete source-aware content enforcement * test: activate skill file-text fail-close fixtures * fix: harden source-aware content filters * fix: harden model-bound content filtering * fix: preserve legacy filters and generated files * fix: inspect shared scalar metadata * test: align mocks with current dev dependencies * feat: add persisted content filter safeguards * feat: complete source-aware content filter enforcement * fix: move resume content preflight into TypeScript * fix: close content inspection edge cases * fix: harden content protection boundaries * fix: complete content protection safeguards * test: align persisted memory filter coverage * fix: reconcile content protection with current dev * fix: reconcile content protection with latest dev * fix: close content protection review gaps * fix: enforce source-aware provider boundaries * fix: preserve legacy PII preflight semantics * test: stabilize stored branch preflight fixture * fix: defer agent writes until protected model admission * perf: harden source-aware model-bound filtering * fix: canonicalize provider lineage before validation * fix: satisfy model-bound callback type checks * perf: Bound content protection filtering work * fix: Bound submission array traversal * fix: Stabilize bounded content snapshots * fix: Scope model-bound traversal overflows * fix: Preserve scoped content inspection * fix: Accumulate aggregate traversal scopes * fix: centralize content policy boundaries * test: align deferred tool policy context * test: align controller policy mocks * style: normalize content protection imports * fix: close content policy review gaps * fix: narrow active skill policy config * fix: address content protection review boundaries * fix: retain exact provenance overflow sentinel * fix: preserve literal and scoped provenance updates * fix: narrow persisted edit provenance * fix: isolate exact overflow attribution * fix: centralize stored prompt protection * fix: fail closed on incomplete transcript evidence * fix: align canonical transcript routing * refactor: centralize content policy preflights * fix: isolate upload policy error typing * style: sort policy preflight imports * refactor: centralize content policy boundaries
122 lines
3.7 KiB
JavaScript
122 lines
3.7 KiB
JavaScript
const fs = require('fs').promises;
|
|
const os = require('os');
|
|
const path = require('path');
|
|
const { EModelEndpoint } = require('librechat-data-provider');
|
|
const { bulkIncrementTagCounts, bulkSaveConvos, bulkSaveMessages } = require('~/models');
|
|
const { getImporter } = require('./importers');
|
|
const importConversations = require('./importConversations');
|
|
|
|
jest.mock('~/models', () => ({
|
|
bulkIncrementTagCounts: jest.fn(),
|
|
bulkSaveConvos: jest.fn(),
|
|
bulkSaveMessages: jest.fn(),
|
|
}));
|
|
|
|
jest.mock('./importers', () => ({
|
|
getImporter: jest.fn(),
|
|
}));
|
|
|
|
const filters = {
|
|
messages: {
|
|
pii: {
|
|
fields: ['text'],
|
|
starterPatterns: [],
|
|
customPatterns: [
|
|
{
|
|
id: 'import-secret',
|
|
label: 'restricted import value',
|
|
regex: 'IMPORT-SECRET',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|
|
|
|
describe('importConversations content filtering', () => {
|
|
let tempDir;
|
|
let filepath;
|
|
|
|
beforeEach(async () => {
|
|
jest.clearAllMocks();
|
|
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'librechat-import-filter-'));
|
|
filepath = path.join(tempDir, 'conversation.json');
|
|
await fs.writeFile(filepath, JSON.stringify({ normalized: true }), 'utf8');
|
|
bulkIncrementTagCounts.mockResolvedValue();
|
|
bulkSaveConvos.mockResolvedValue();
|
|
bulkSaveMessages.mockResolvedValue();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
});
|
|
|
|
it('threads filters into the normalized builder and removes a blocked upload', async () => {
|
|
getImporter.mockReturnValue(async (_jsonData, requestUserId, builderFactory) => {
|
|
const builder = builderFactory(requestUserId);
|
|
builder.startConversation(EModelEndpoint.openAI);
|
|
builder.addUserMessage('IMPORT-SECRET');
|
|
builder.finishConversation('safe title');
|
|
await builder.saveBatch();
|
|
});
|
|
|
|
await expect(
|
|
importConversations({
|
|
filepath,
|
|
requestUserId: 'user-123',
|
|
interfaceConfig: { retentionMode: 'all' },
|
|
filters,
|
|
}),
|
|
).rejects.toMatchObject({
|
|
code: 'content_filter_block',
|
|
statusCode: 400,
|
|
body: {
|
|
error: 'content_filter_block',
|
|
source: 'message',
|
|
field: 'text',
|
|
},
|
|
});
|
|
|
|
await expect(fs.stat(filepath)).rejects.toMatchObject({ code: 'ENOENT' });
|
|
expect(bulkSaveConvos).not.toHaveBeenCalled();
|
|
expect(bulkSaveMessages).not.toHaveBeenCalled();
|
|
expect(bulkIncrementTagCounts).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('threads strict attribution with a legacy-only detector into imported rows', async () => {
|
|
getImporter.mockReturnValue(async (_jsonData, requestUserId, builderFactory) => {
|
|
const builder = builderFactory(requestUserId);
|
|
builder.startConversation(EModelEndpoint.openAI);
|
|
builder.saveMessage({
|
|
sender: 'Assistant',
|
|
isCreatedByUser: false,
|
|
text: 'Legacy unattributed IMPORT-SECRET',
|
|
});
|
|
builder.finishConversation('safe title');
|
|
await builder.saveBatch();
|
|
});
|
|
|
|
await expect(
|
|
importConversations({
|
|
filepath,
|
|
requestUserId: 'user-123',
|
|
filters: { messages: { unattributedAssistantContent: 'inspect' } },
|
|
legacyPii: {
|
|
starterPatterns: [],
|
|
customPatterns: [
|
|
{
|
|
id: 'import-secret',
|
|
label: 'restricted import value',
|
|
regex: 'IMPORT-SECRET',
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
).rejects.toMatchObject({
|
|
code: 'content_filter_block',
|
|
body: expect.objectContaining({ source: 'message', field: 'text' }),
|
|
});
|
|
|
|
await expect(fs.stat(filepath)).rejects.toMatchObject({ code: 'ENOENT' });
|
|
expect(bulkSaveMessages).not.toHaveBeenCalled();
|
|
});
|
|
});
|