import fs from 'fs'; import path from 'path'; import parseReadme from '../src'; function readReadme(project: string, fileName = 'readme.md'): Promise { return new Promise((resolve, reject): void => { fs.readFile(path.join(__dirname, 'partials', project, fileName), 'utf8', (err, data) => { if (err) { return reject(err); } return resolve(data.toString()); }); }); } function clean(text: string): string { return text.replace(/\n|\r/g, '').trim(); } describe('readme', () => { test('should handle empty readme', () => { expect(parseReadme('')).toBeUndefined(); }); test('should handle single string readme', () => { expect(parseReadme('this is a readme')).toEqual('

this is a readme

'); }); test('should handle wrong text', () => { // @ts-expect-error expect(parseReadme(undefined)).toBeUndefined(); }); describe('basic parsing', () => { test('should parse basic', () => { expect(parseReadme('# hi')).toEqual(`

hi

`); }); test('should parse basic / js alert', () => { expect(parseReadme("[Basic](javascript:alert('Basic'))")).toEqual('

Basic

'); }); test('should parse basic / local storage', () => { expect( parseReadme('[Local Storage](javascript:alert(JSON.stringify(localStorage)))') ).toEqual('

Local Storage

'); }); test('should parse basic / case insensitive', () => { expect(parseReadme("[CaseInsensitive](JaVaScRiPt:alert('CaseInsensitive'))")).toEqual( '

CaseInsensitive

' ); }); test('should parse basic / url', () => { expect(parseReadme("[URL](javascript://www.google.com%0Aalert('URL'))")).toEqual( '

URL

' ); }); test('should parse basic / in quotes', () => { expect(parseReadme('[In Quotes](\'javascript:alert("InQuotes")\')')).toEqual( '

In Quotes

' ); }); }); describe('should parse images', () => { test('in quotes', () => { expect( parseReadme( '![Escape SRC - onload](https://www.example.com/image.png"onload="alert(\'ImageOnLoad\'))' ) ).toEqual( '

Escape SRC - onload

" ); }); test('in image error', () => { expect(parseReadme('![Escape SRC - onerror]("onerror="alert(\'ImageOnError\'))')).toEqual( '

Escape SRC - onerror

' ); }); }); describe('should test fuzzing', () => { test('xss / document cookie', () => { expect(parseReadme('[XSS](javascript:prompt(document.cookie))')).toEqual('

XSS

'); }); test('xss / white space cookie', () => { expect( parseReadme('[XSS](j a v a s c r i p t:prompt(document.cookie))') ).toEqual('

[XSS](j a v a s c r i p t:prompt(document.cookie))

'); }); test('xss / data test/html', () => { expect( parseReadme('[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)') ).toEqual('

XSS

'); }); test('xss / data test/html encoded', () => { expect( parseReadme( '[XSS](javascript:ale' + '2t('XSS'))' ) ).toEqual( '

XSS

' ); }); test('xss / js prompt', () => { expect(parseReadme('[XSS]: (javascript:prompt(document.cookie))')).toEqual(''); }); test('xss / js window error alert', () => { expect(parseReadme('[XSS](javascript:window.onerror=alert;throw%20document.cookie)')).toEqual( '

XSS

' ); }); test('xss / js window encoded prompt', () => { expect(parseReadme('[XSS](javascript://%0d%0aprompt(1))')).toEqual('

XSS

'); }); test('xss / js window encoded prompt multiple statement', () => { expect(parseReadme('[XSS](javascript://%0d%0aprompt(1);com)')).toEqual('

XSS

'); }); test('xss / js window encoded window error alert multiple statement', () => { expect(parseReadme('[XSS](javascript:window.onerror=alert;throw%20document.cookie)')).toEqual( '

XSS

' ); }); test('xss / js window encoded window error alert throw error', () => { expect( parseReadme('[XSS](javascript://%0d%0awindow.onerror=alert;throw%20document.cookie)') ).toEqual('

XSS

'); }); test('xss / js window encoded data text/html base 64', () => { expect( parseReadme('[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)') ).toEqual('

XSS

'); }); test('xss / js vbscript alert', () => { expect(parseReadme('[XSS](vbscript:alert(document.domain))')).toEqual('

XSS

'); }); describe('xss / js alert this', () => { test('xss / js case #1', () => { expect(parseReadme('[XSS](javascript:this;alert(1))')).toEqual('

XSS

'); }); test('xss / js case #2', () => { expect(parseReadme('[XSS](javascript:this;alert(1))')).toEqual('

XSS

'); }); test('xss / js case #3', () => { expect(parseReadme('[XSS](javascript:this;alert(1))')).toEqual('

XSS

'); }); test('xss / js case #4', () => { expect(parseReadme('[XSS](Javascript:alert(1))')).toEqual('

XSS

'); }); test('xss / js case #5', () => { expect(parseReadme('[XSS](Javas%26%2399;ript:alert(1))')).toEqual( '

XSS

' ); }); test('xss / js case #6', () => { expect(parseReadme('[XSS](javascript:alert￾(1))')).toEqual('

XSS

'); }); }); test('xss / js confirm', () => { expect(parseReadme('[XSS](javascript:confirm(1)')).toEqual('

XSS

'); }); describe('xss / js url', () => { test('xss / case #1', () => { expect(parseReadme('[XSS](javascript://www.google.com%0Aprompt(1))')).toEqual( '

XSS

' ); }); test('xss / case #2', () => { expect(parseReadme('[XSS](javascript://%0d%0aconfirm(1);com)')).toEqual( '

XSS

' ); }); test('xss / case #3', () => { expect(parseReadme('[XSS](javascript:window.onerror=confirm;throw%201)')).toEqual( '

XSS

' ); }); test('xss / case #4', () => { expect(parseReadme('[XSS](�javascript:alert(document.domain))')).toEqual( '

XSS

' ); }); test('xss / case #5', () => { expect(parseReadme('![XSS](javascript:prompt(document.cookie))\\')).toEqual( '

XSS\\

' ); }); test('xss / case #6', () => { expect( parseReadme('![XSS](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)\\') ).toEqual( '

XSS\\

' ); }); // FIXME: requires proper parsing test.skip('xss / case #7', () => { expect(parseReadme(`![XSS'"\`onerror=prompt(document.cookie)](x)\\`)).toEqual( '

![XSS\'\\"`onerror=prompt(document.cookie)](x)\\\\

' ); }); }); }); describe('mix readmes / markdown', () => { test('should parse marked', async () => { const readme: string = await readReadme('mixed-html-mk'); expect(clean(parseReadme(readme) as string)).toMatchInlineSnapshot( '"

mix html and XSS markdown

Basic<' + '/a>

"' ); }); }); });