|
|
|
|
@@ -1,5 +1,5 @@
|
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
|
|
|
import { mdTextFormat, CodeClassNameEnum } from '@/components/Markdown/utils';
|
|
|
|
|
import { mdTextFormat, CodeClassNameEnum, filterSafeProps } from '@/components/Markdown/utils';
|
|
|
|
|
|
|
|
|
|
describe('Markdown utils', () => {
|
|
|
|
|
describe('mdTextFormat', () => {
|
|
|
|
|
@@ -56,4 +56,121 @@ describe('Markdown utils', () => {
|
|
|
|
|
expect(CodeClassNameEnum.audio).toBe('audio');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('filterSafeProps', () => {
|
|
|
|
|
const allowedAttrs = new Set(['class', 'style', 'title', 'id']);
|
|
|
|
|
|
|
|
|
|
it('should filter out non-whitelisted attributes', () => {
|
|
|
|
|
const props = {
|
|
|
|
|
class: 'test',
|
|
|
|
|
nonexistent: 'value',
|
|
|
|
|
title: 'title'
|
|
|
|
|
};
|
|
|
|
|
const result = filterSafeProps(props, allowedAttrs);
|
|
|
|
|
expect(result).toEqual({
|
|
|
|
|
class: 'test',
|
|
|
|
|
title: 'title'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should filter out dangerous event handlers', () => {
|
|
|
|
|
const props = {
|
|
|
|
|
class: 'test',
|
|
|
|
|
onClick: () => {},
|
|
|
|
|
onMouseover: () => {}
|
|
|
|
|
};
|
|
|
|
|
const result = filterSafeProps(props, allowedAttrs);
|
|
|
|
|
expect(result).toEqual({
|
|
|
|
|
class: 'test'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should filter out dangerous protocols', () => {
|
|
|
|
|
const props = {
|
|
|
|
|
title: 'javascript:alert(1)',
|
|
|
|
|
id: 'vbscript:alert(1)',
|
|
|
|
|
class: 'safe'
|
|
|
|
|
};
|
|
|
|
|
const result = filterSafeProps(props, allowedAttrs);
|
|
|
|
|
expect(result).toEqual({
|
|
|
|
|
class: 'safe'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle encoded malicious content', () => {
|
|
|
|
|
const props = {
|
|
|
|
|
title: 'javascript:alert(1)',
|
|
|
|
|
id: '%6A%61%76%61%73%63%72%69%70%74%3Aalert(1)',
|
|
|
|
|
class: 'safe'
|
|
|
|
|
};
|
|
|
|
|
const result = filterSafeProps(props, allowedAttrs);
|
|
|
|
|
expect(result).toEqual({
|
|
|
|
|
class: 'safe'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should filter style objects', () => {
|
|
|
|
|
const props = {
|
|
|
|
|
style: {
|
|
|
|
|
color: 'red',
|
|
|
|
|
background: 'javascript:alert(1)'
|
|
|
|
|
},
|
|
|
|
|
class: 'test'
|
|
|
|
|
};
|
|
|
|
|
const result = filterSafeProps(props, allowedAttrs);
|
|
|
|
|
expect(result).toEqual({
|
|
|
|
|
class: 'test'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle empty and null values', () => {
|
|
|
|
|
const props = {
|
|
|
|
|
class: '',
|
|
|
|
|
title: null,
|
|
|
|
|
style: null
|
|
|
|
|
};
|
|
|
|
|
const result = filterSafeProps(props, allowedAttrs);
|
|
|
|
|
expect(result).toEqual({
|
|
|
|
|
class: '',
|
|
|
|
|
title: null,
|
|
|
|
|
style: null
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should filter nested objects except style', () => {
|
|
|
|
|
const props = {
|
|
|
|
|
data: { key: 'value' },
|
|
|
|
|
style: { color: 'red' },
|
|
|
|
|
class: 'test'
|
|
|
|
|
};
|
|
|
|
|
const result = filterSafeProps(props, allowedAttrs);
|
|
|
|
|
expect(result).toEqual({
|
|
|
|
|
style: { color: 'red' },
|
|
|
|
|
class: 'test'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should handle multiple iterations of encoded content', () => {
|
|
|
|
|
const props = {
|
|
|
|
|
title: encodeURIComponent(encodeURIComponent('javascript:alert(1)')),
|
|
|
|
|
class: 'safe'
|
|
|
|
|
};
|
|
|
|
|
const result = filterSafeProps(props, allowedAttrs);
|
|
|
|
|
expect(result).toEqual({
|
|
|
|
|
class: 'safe'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should filter suspicious content patterns', () => {
|
|
|
|
|
const props = {
|
|
|
|
|
title: 'Function("alert(1)")',
|
|
|
|
|
id: 'eval("alert(1)")',
|
|
|
|
|
class: 'test'
|
|
|
|
|
};
|
|
|
|
|
const result = filterSafeProps(props, allowedAttrs);
|
|
|
|
|
expect(result).toEqual({
|
|
|
|
|
class: 'test'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|