File tree Expand file tree Collapse file tree 2 files changed +24
-1
lines changed
Expand file tree Collapse file tree 2 files changed +24
-1
lines changed Original file line number Diff line number Diff line change @@ -16,7 +16,7 @@ export function formatEventStreamMessage(message: EventStreamMessage): string {
1616 result += `retry: ${ message . retry } \n` ;
1717 }
1818 const data = typeof message . data === "string" ? message . data : "" ;
19- for ( const line of data . split ( "\n" ) ) {
19+ for ( const line of data . split ( / \r \n | \r | \n / ) ) {
2020 result += `data: ${ line } \n` ;
2121 }
2222 result += "\n" ;
Original file line number Diff line number Diff line change @@ -159,3 +159,26 @@ it("prevents data field injection of new events", () => {
159159 `data: hi\ndata: \ndata: event: system\ndata: data: INJECTED\n\n` ,
160160 ) ;
161161} ) ;
162+
163+ it ( "sanitizes carriage returns in data to prevent SSE injection" , ( ) => {
164+ const result = formatEventStreamMessage ( {
165+ data : "legit\revent: evil" ,
166+ } ) ;
167+ // \r should be treated as a line break, not passed through
168+ expect ( result ) . toBe ( `data: legit\ndata: event: evil\n\n` ) ;
169+ } ) ;
170+
171+ it ( "sanitizes \\r\\n in data field" , ( ) => {
172+ const result = formatEventStreamMessage ( {
173+ data : "line1\r\nline2\rline3\nline4" ,
174+ } ) ;
175+ expect ( result ) . toBe ( `data: line1\ndata: line2\ndata: line3\ndata: line4\n\n` ) ;
176+ } ) ;
177+
178+ it ( "prevents event splitting via \\r\\r in data" , ( ) => {
179+ const result = formatEventStreamMessage ( {
180+ data : "first\r\rdata: injected" ,
181+ } ) ;
182+ // Double \r should produce an empty line, not a message boundary
183+ expect ( result ) . toBe ( `data: first\ndata: \ndata: data: injected\n\n` ) ;
184+ } ) ;
You can’t perform that action at this time.
0 commit comments