@@ -6,34 +6,36 @@ import xml from 'xml2js'
66type XmlParserOptions = { disableContentParser ?: boolean ; parseAsArray ?: string [ ] }
77type RequestError = Error & { statusCode ?: number }
88
9- export function decodeXmlNumericEntities ( value : string ) : string {
10- const isValidXmlCodePoint = ( codePoint : number ) => {
11- if ( ! Number . isInteger ( codePoint ) || codePoint < 0 || codePoint > 0x10ffff ) {
12- return false
13- }
14-
15- return (
16- codePoint === 0x9 ||
17- codePoint === 0xa ||
18- codePoint === 0xd ||
19- ( codePoint >= 0x20 && codePoint <= 0xd7ff ) ||
20- ( codePoint >= 0xe000 && codePoint <= 0xfffd ) ||
21- ( codePoint >= 0x10000 && codePoint <= 0x10ffff )
22- )
9+ function isValidXmlCodePoint ( codePoint : number ) : boolean {
10+ if ( ! Number . isInteger ( codePoint ) || codePoint < 0 || codePoint > 0x10ffff ) {
11+ return false
2312 }
2413
25- return value . replace (
26- / & # ( [ x X ] [ 0 - 9 a - f A - F ] { 1 , 6 } | [ 0 - 9 ] { 1 , 7 } ) ; / g,
27- ( match : string , rawValue : string ) => {
28- const isHex = rawValue [ 0 ] . toLowerCase ( ) === 'x'
29- const codePoint = Number . parseInt ( isHex ? rawValue . slice ( 1 ) : rawValue , isHex ? 16 : 10 )
30- if ( ! isValidXmlCodePoint ( codePoint ) ) {
31- return match
32- }
14+ return (
15+ codePoint === 0x9 ||
16+ codePoint === 0xa ||
17+ codePoint === 0xd ||
18+ ( codePoint >= 0x20 && codePoint <= 0xd7ff ) ||
19+ ( codePoint >= 0xe000 && codePoint <= 0xfffd ) ||
20+ ( codePoint >= 0x10000 && codePoint <= 0x10ffff )
21+ )
22+ }
23+
24+ function getInvalidXmlNumericEntity ( value : string ) : string | undefined {
25+ const numericEntityPattern = / & # ( [ x X ] [ 0 - 9 a - f A - F ] { 1 , 6 } | [ 0 - 9 ] { 1 , 7 } ) ; / g
26+
27+ let match = numericEntityPattern . exec ( value )
28+ while ( match ) {
29+ const rawValue = match [ 1 ]
30+ const isHex = rawValue [ 0 ] . toLowerCase ( ) === 'x'
31+ const codePoint = Number . parseInt ( isHex ? rawValue . slice ( 1 ) : rawValue , isHex ? 16 : 10 )
3332
34- return String . fromCodePoint ( codePoint )
33+ if ( ! isValidXmlCodePoint ( codePoint ) ) {
34+ return match [ 0 ]
3535 }
36- )
36+
37+ match = numericEntityPattern . exec ( value )
38+ }
3739}
3840
3941function forcePathAsArray ( node : unknown , pathSegments : string [ ] ) : void {
@@ -82,16 +84,23 @@ export const xmlParser = fastifyPlugin(
8284 return
8385 }
8486
87+ const xmlBody = typeof body === 'string' ? body : body . toString ( 'utf8' )
88+ const invalidNumericEntity = getInvalidXmlNumericEntity ( xmlBody )
89+ if ( invalidNumericEntity ) {
90+ const parseError : RequestError = new Error (
91+ `Invalid XML payload: invalid numeric entity ${ invalidNumericEntity } `
92+ )
93+ parseError . statusCode = 400
94+ done ( parseError )
95+ return
96+ }
97+
8598 xml . parseString (
86- body ,
99+ xmlBody ,
87100 {
88101 explicitArray : false ,
89102 trim : true ,
90- valueProcessors : [
91- decodeXmlNumericEntities ,
92- xml . processors . parseNumbers ,
93- xml . processors . parseBooleans ,
94- ] ,
103+ valueProcessors : [ xml . processors . parseNumbers , xml . processors . parseBooleans ] ,
95104 } ,
96105 ( err : Error | null , parsed : unknown ) => {
97106 if ( err ) {
0 commit comments