🧼 fix: Sanitize User Response Fields (#13421)

This commit is contained in:
Danny Avila 2026-05-30 19:35:52 -04:00 committed by GitHub
parent 479e9d59b7
commit 75baa5b848
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 137 additions and 9 deletions

View file

@ -108,10 +108,114 @@ afterEach(async () => {
}
});
const { deleteUserController } = require('./UserController');
const { deleteUserController, getUserController } = require('./UserController');
const { Group } = require('~/db/models');
const { deleteConvos } = require('~/models');
describe('getUserController', () => {
const mockRes = {
status: jest.fn().mockReturnThis(),
send: jest.fn().mockReturnThis(),
};
beforeEach(() => {
jest.clearAllMocks();
});
it('should only expose public user response fields from the request user', async () => {
const createdAt = new Date('2026-01-01T00:00:00.000Z');
const updatedAt = new Date('2026-01-02T00:00:00.000Z');
const req = {
config: {},
user: {
id: 'user-id',
_id: 'user-id',
name: 'OpenID User',
username: 'openid-user',
email: 'openid@test.com',
emailVerified: true,
avatar: '/avatars/user-id.png',
provider: 'openid',
role: 'USER',
plugins: ['web_search'],
twoFactorEnabled: true,
termsAccepted: true,
personalization: { memories: false },
favorites: [{ model: 'gpt-5', endpoint: 'openAI' }],
skillStates: { skill_one: true },
createdAt,
updatedAt,
tenantId: 'tenant-id',
password: 'hashed-password',
__v: 1,
totpSecret: 'totp-secret',
backupCodes: [{ codeHash: 'backup-code' }],
pendingTotpSecret: 'pending-totp-secret',
pendingBackupCodes: [{ codeHash: 'pending-backup-code' }],
refreshToken: [{ refreshToken: 'legacy-refresh-token' }],
googleId: 'google-id',
openidId: 'openid-id',
openidIssuer: 'openid-issuer',
idOnTheSource: 'external-source-id',
federatedTokens: {
access_token: 'access-token',
id_token: 'id-token',
refresh_token: 'refresh-token',
},
openidTokens: {
access_token: 'openid-access-token',
refresh_token: 'openid-refresh-token',
},
tokenset: {
access_token: 'tokenset-access-token',
refresh_token: 'tokenset-refresh-token',
},
safeLookingRuntimeField: 'internal-value',
},
};
await getUserController(req, mockRes);
expect(mockRes.status).toHaveBeenCalledWith(200);
const sentUser = mockRes.send.mock.calls[0][0];
expect(sentUser).toMatchObject({
id: 'user-id',
_id: 'user-id',
name: 'OpenID User',
username: 'openid-user',
email: 'openid@test.com',
emailVerified: true,
avatar: '/avatars/user-id.png',
provider: 'openid',
role: 'USER',
plugins: ['web_search'],
twoFactorEnabled: true,
termsAccepted: true,
personalization: { memories: false },
favorites: [{ model: 'gpt-5', endpoint: 'openAI' }],
skillStates: { skill_one: true },
createdAt,
updatedAt,
tenantId: 'tenant-id',
});
expect(sentUser).not.toHaveProperty('password');
expect(sentUser).not.toHaveProperty('__v');
expect(sentUser).not.toHaveProperty('totpSecret');
expect(sentUser).not.toHaveProperty('backupCodes');
expect(sentUser).not.toHaveProperty('pendingTotpSecret');
expect(sentUser).not.toHaveProperty('pendingBackupCodes');
expect(sentUser).not.toHaveProperty('refreshToken');
expect(sentUser).not.toHaveProperty('googleId');
expect(sentUser).not.toHaveProperty('openidId');
expect(sentUser).not.toHaveProperty('openidIssuer');
expect(sentUser).not.toHaveProperty('idOnTheSource');
expect(sentUser).not.toHaveProperty('federatedTokens');
expect(sentUser).not.toHaveProperty('openidTokens');
expect(sentUser).not.toHaveProperty('tokenset');
expect(sentUser).not.toHaveProperty('safeLookingRuntimeField');
});
});
describe('deleteUserController', () => {
const mockRes = {
status: jest.fn().mockReturnThis(),