@@ -1663,6 +1663,88 @@ describe('Vulnerabilities', () => {
16631663 } ) ;
16641664 } ) ;
16651665
1666+ describe ( '(GHSA-vr5f-2r24-w5hc) Stored XSS via Content-Type and file extension mismatch' , ( ) => {
1667+ const headers = {
1668+ 'X-Parse-Application-Id' : 'test' ,
1669+ 'X-Parse-REST-API-Key' : 'rest' ,
1670+ } ;
1671+
1672+ it ( 'overrides mismatched Content-Type with extension-derived MIME type on buffered upload' , async ( ) => {
1673+ const adapter = Config . get ( 'test' ) . filesController . adapter ;
1674+ const spy = spyOn ( adapter , 'createFile' ) . and . callThrough ( ) ;
1675+ const content = Buffer . from ( '<script>alert(1)</script>' ) . toString ( 'base64' ) ;
1676+ await request ( {
1677+ method : 'POST' ,
1678+ url : 'http://localhost:8378/1/files/evil.txt' ,
1679+ body : JSON . stringify ( {
1680+ _ApplicationId : 'test' ,
1681+ _JavaScriptKey : 'test' ,
1682+ _ContentType : 'text/html' ,
1683+ base64 : content ,
1684+ } ) ,
1685+ headers,
1686+ } ) ;
1687+ expect ( spy ) . toHaveBeenCalled ( ) ;
1688+ const contentTypeArg = spy . calls . mostRecent ( ) . args [ 2 ] ;
1689+ expect ( contentTypeArg ) . toBe ( 'text/plain' ) ;
1690+ } ) ;
1691+
1692+ it ( 'overrides mismatched Content-Type with extension-derived MIME type on stream upload' , async ( ) => {
1693+ const adapter = Config . get ( 'test' ) . filesController . adapter ;
1694+ const spy = spyOn ( adapter , 'createFile' ) . and . callThrough ( ) ;
1695+ const body = '<script>alert(1)</script>' ;
1696+ await request ( {
1697+ method : 'POST' ,
1698+ url : 'http://localhost:8378/1/files/evil.txt' ,
1699+ headers : {
1700+ ...headers ,
1701+ 'Content-Type' : 'text/html' ,
1702+ 'X-Parse-Upload-Mode' : 'stream' ,
1703+ } ,
1704+ body,
1705+ } ) ;
1706+ expect ( spy ) . toHaveBeenCalled ( ) ;
1707+ const contentTypeArg = spy . calls . mostRecent ( ) . args [ 2 ] ;
1708+ expect ( contentTypeArg ) . toBe ( 'text/plain' ) ;
1709+ } ) ;
1710+
1711+ it ( 'preserves Content-Type when no file extension is present' , async ( ) => {
1712+ const adapter = Config . get ( 'test' ) . filesController . adapter ;
1713+ const spy = spyOn ( adapter , 'createFile' ) . and . callThrough ( ) ;
1714+ await request ( {
1715+ method : 'POST' ,
1716+ url : 'http://localhost:8378/1/files/noextension' ,
1717+ headers : {
1718+ ...headers ,
1719+ 'Content-Type' : 'image/png' ,
1720+ } ,
1721+ body : Buffer . from ( 'fake png content' ) ,
1722+ } ) ;
1723+ expect ( spy ) . toHaveBeenCalled ( ) ;
1724+ const contentTypeArg = spy . calls . mostRecent ( ) . args [ 2 ] ;
1725+ expect ( contentTypeArg ) . toBe ( 'image/png' ) ;
1726+ } ) ;
1727+
1728+ it ( 'infers Content-Type from extension when none is provided' , async ( ) => {
1729+ const adapter = Config . get ( 'test' ) . filesController . adapter ;
1730+ const spy = spyOn ( adapter , 'createFile' ) . and . callThrough ( ) ;
1731+ const content = Buffer . from ( 'test content' ) . toString ( 'base64' ) ;
1732+ await request ( {
1733+ method : 'POST' ,
1734+ url : 'http://localhost:8378/1/files/data.txt' ,
1735+ body : JSON . stringify ( {
1736+ _ApplicationId : 'test' ,
1737+ _JavaScriptKey : 'test' ,
1738+ base64 : content ,
1739+ } ) ,
1740+ headers,
1741+ } ) ;
1742+ expect ( spy ) . toHaveBeenCalled ( ) ;
1743+ const contentTypeArg = spy . calls . mostRecent ( ) . args [ 2 ] ;
1744+ expect ( contentTypeArg ) . toBe ( 'text/plain' ) ;
1745+ } ) ;
1746+ } ) ;
1747+
16661748 describe ( '(GHSA-q3vj-96h2-gwvg) SQL Injection via Increment amount on nested Object field' , ( ) => {
16671749 const headers = {
16681750 'Content-Type' : 'application/json' ,
0 commit comments