@@ -56,6 +56,36 @@ function escapeForJsString(input) {
5656 . replaceAll ( '\r' , '' ) ;
5757}
5858
59+ function decodeHtmlAttributeValue ( input ) {
60+ return String ( input )
61+ . replace ( / & (?: a m p | q u o t | l t | g t | # 3 9 ) ; / g, ( entity ) => {
62+ switch ( entity ) {
63+ case '&' :
64+ return '&' ;
65+ case '"' :
66+ return '"' ;
67+ case '<' :
68+ return '<' ;
69+ case '>' :
70+ return '>' ;
71+ case ''' :
72+ return "'" ;
73+ default :
74+ return entity ;
75+ }
76+ } ) ;
77+ }
78+
79+ function toPublicErrorMessage ( err , fallback = 'Request failed' ) {
80+ const message = err instanceof Error ? err . message : String ( err || '' ) ;
81+ const clean = message . replace ( / \s + / g, ' ' ) . trim ( ) ;
82+ if ( ! clean ) return fallback ;
83+ if ( clean . includes ( '\n' ) || / \b a t \s + \S + / . test ( clean ) || clean . startsWith ( 'Error:' ) ) {
84+ return fallback ;
85+ }
86+ return clean ;
87+ }
88+
5989function sendJson ( res , status , data ) {
6090 const body = JSON . stringify ( data ) ;
6191 res . writeHead ( status , {
@@ -234,7 +264,7 @@ function parseAttributes(tag) {
234264 let m ;
235265 while ( ( m = re . exec ( attrSlice ) ) ) {
236266 const key = m [ 1 ] . toLowerCase ( ) ;
237- const value = m [ 2 ] ?? m [ 3 ] ?? m [ 4 ] ?? '' ;
267+ const value = decodeHtmlAttributeValue ( m [ 2 ] ?? m [ 3 ] ?? m [ 4 ] ?? '' ) ;
238268 attrs [ key ] = value ;
239269 }
240270 return attrs ;
@@ -287,12 +317,7 @@ function resolveUrl(raw, base) {
287317 const clean = raw
288318 . trim ( )
289319 . replace ( / ^ u r l \( ( .* ) \) $ / i, '$1' )
290- . replace ( / ^ [ ' " ] | [ ' " ] $ / g, '' )
291- . replaceAll ( '&' , '&' )
292- . replaceAll ( '"' , '"' )
293- . replaceAll ( ''' , "'" )
294- . replaceAll ( '<' , '<' )
295- . replaceAll ( '>' , '>' ) ;
320+ . replace ( / ^ [ ' " ] | [ ' " ] $ / g, '' ) ;
296321 try {
297322 return new URL ( clean , base ) . toString ( ) ;
298323 } catch {
@@ -582,7 +607,7 @@ async function handleExtract(req, res, reqUrl) {
582607 groups,
583608 } ) ;
584609 } catch ( err ) {
585- return sendJson ( res , 400 , { error : String ( err . message || err ) } ) ;
610+ return sendJson ( res , 400 , { error : toPublicErrorMessage ( err , 'Unable to extract images from that URL' ) } ) ;
586611 }
587612}
588613
@@ -615,7 +640,7 @@ async function handleAsset(req, res, reqUrl) {
615640 } ) ;
616641 res . end ( buf ) ;
617642 } catch ( err ) {
618- return sendJson ( res , 400 , { error : String ( err . message || err ) } ) ;
643+ return sendJson ( res , 400 , { error : toPublicErrorMessage ( err , 'Unable to fetch that asset' ) } ) ;
619644 }
620645}
621646
@@ -657,7 +682,7 @@ const server = http.createServer(async (req, res) => {
657682 } ) ;
658683 res . end ( rendered ) ;
659684 } catch ( err ) {
660- sendText ( res , 500 , String ( err . message || err ) ) ;
685+ sendText ( res , 500 , 'Internal Server Error' ) ;
661686 }
662687 return ;
663688 }
0 commit comments