Files
video_socket-server/client/test/unit/rendering-safety.test.js
2026-05-25 22:58:11 +08:00

89 lines
3.2 KiB
JavaScript

import { createMessageElement } from '../../public/call/chat/renderer-chat.js';
import { createParticipantTile, getParticipantTile } from '../../public/call/participants/renderer-participant-grid.js';
import { createUserEntryElement } from '../../public/call/renderers/renderer-ui.js';
import { renderOnlineUsers } from '../../public/call/signaling/connect-directory.js';
const formatTimestamp = value => value;
const unsafeText = '<img src=x onerror=alert(1)>Alice';
function mediaState(overrides = {}) {
return {
audio: true,
video: true,
isSpeaking: false,
...overrides
};
}
describe('safe dynamic rendering', () => {
afterEach(() => {
document.body.innerHTML = '';
});
test('renders chat text as text, not markup', () => {
const element = createMessageElement({
id: 'msg-1',
type: 'text',
isSelf: false,
senderName: unsafeText,
senderAvatar: '/images/p1.png',
content: unsafeText,
timestamp: 'now'
}, formatTimestamp);
expect(element.querySelector('.message-text').textContent).toBe(unsafeText);
expect(element.querySelector('.message-content img')).toBeNull();
expect(element.querySelector('.message-sender').textContent).toBe(unsafeText);
});
test('renders participant names safely and finds ids without selector injection', () => {
const participantId = 'room"] [data-bad="1';
const tile = createParticipantTile(participantId, unsafeText);
const grid = document.createElement('div');
grid.appendChild(tile);
expect(tile.querySelector('.absolute.bottom-3 span').textContent).toBe(unsafeText);
expect(tile.querySelector('.absolute.bottom-3 img')).toBeNull();
expect(getParticipantTile(grid, participantId)).toBe(tile);
});
test('renders user list entries without interpreting user profile fields as HTML', () => {
const entry = createUserEntryElement({
role: 'participant',
id: 'participant-1',
user: {
name: unsafeText,
avatar: '/images/p2.png',
mediaState: mediaState({ audio: false })
}
});
expect(entry.textContent).toContain(unsafeText);
expect(entry.querySelectorAll('img')).toHaveLength(1);
});
test('renders online users without injecting markup from directory data', () => {
const onlineUsersList = document.createElement('div');
const usersContainer = document.createElement('div');
const onlineUsersSummary = document.createElement('div');
renderOnlineUsers({
users: [{
name: unsafeText,
userId: unsafeText,
avatar: '/images/p1.png',
role: 'participant',
connectionId: 'room-1'
}],
currentUserId: 'other-user',
onlineUsersList,
usersContainer,
onlineUsersSummary
});
expect(usersContainer.textContent).toContain(unsafeText);
expect(usersContainer.querySelector('button')).toBeNull();
expect(usersContainer.querySelectorAll('img')).toHaveLength(1);
});
});