@@ -315,33 +315,6 @@ component extends="app.Controllers.Controller" {
315315 }
316316 }
317317
318- // Add API functions from all versions dynamically
319- var apiVersions = getAvailableVersions ();
320- for (var version in apiVersions ) {
321- var versionClean = replace (version , " v" , " " , " one" );
322- var docData = getDocJSON (version );
323- if (! structIsEmpty (docData ) && structKeyExists (docData , " functions" )) {
324- for (var func in docData .functions ) {
325- if (structKeyExists (func , " slug" )) {
326- // Get the function details using getFunctionFromDocs to verify it exists
327- var verifiedFunc = getFunctionFromDocs (docData , func .slug );
328- if (! structIsEmpty (verifiedFunc )) {
329- // Add all format routes for this function
330- var formats = [" html" , " json" , " xml" , " pdf" ];
331- for (var fmt in formats ) {
332- arrayAppend (urls , {
333- loc : getBaseUrl () & " /api/v" & versionClean & " /" & func .slug & " ." & fmt ,
334- lastmod : dateFormat (now (), " yyyy-mm-dd" ),
335- changefreq : " weekly" ,
336- priority : " 0.6"
337- });
338- }
339- }
340- }
341- }
342- }
343- }
344-
345318 // Add other public pages
346319 var publicPages = [
347320 " /blog" ,
@@ -376,21 +349,24 @@ component extends="app.Controllers.Controller" {
376349 }
377350
378351 // Generate XML
379- var xml = ' <?xml version="1.0" encoding="UTF-8"?>
380- <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' ;
381-
352+ xml = ' <?xml version="1.0" encoding="UTF-8"?>' & chr ( 10 );
353+ xml & = ' <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' ;
354+ xml = replace ( xml , " \\n " , chr ( 10 ), " all " );
382355 for (var link in urls ) {
383- xml & = '
384- <url>
385- <loc>#link .loc #</loc>
386- <lastmod>#link .lastmod #</lastmod>
387- <changefreq>#link .changefreq #</changefreq>
388- <priority>#link .priority #</priority>
389- </url>' ;
356+ // Skip if loc is missing or empty
357+ if (! structKeyExists (link , " loc" ) || ! len (trim (link .loc ?: " " ))) continue ;
358+ var loc = xmlEscape (link .loc ?: " " );
359+ var lastmod = xmlEscape (link .lastmod ?: " " );
360+ var changefreq = xmlEscape (link .changefreq ?: " " );
361+ var priority = xmlEscape (link .priority ?: " " );
362+ xml & = ' <url>' &
363+ ' <loc>' & loc & ' </loc>' &
364+ ' <lastmod>' & lastmod & ' </lastmod>' &
365+ ' <changefreq>' & changefreq & ' </changefreq>' &
366+ ' <priority>' & priority & ' </priority>' &
367+ ' </url>' ;
390368 }
391-
392- xml & = '
393- </urlset>' ;
369+ xml & = chr (10 ) & ' </urlset>' ;
394370
395371 var sitemapPath = expandPath (" /sitemap/sitemap.xml" );
396372 model (" Log" ).log (
@@ -437,6 +413,18 @@ component extends="app.Controllers.Controller" {
437413 renderText (text = " Failed to generate sitemap. Please try again later." );
438414 }
439415 }
416+
417+ // Escape XML special characters for safe XML output
418+ private string function xmlEscape (required string value ) {
419+ if (isNull (arguments .value ) || ! len (arguments .value & " " )) return " " ;
420+ var s = arguments .value & " " ; // force string
421+ s = replace (s , " &" , " &" , " all" );
422+ s = replace (s , " <" , " <" , " all" );
423+ s = replace (s , " >" , " >" , " all" );
424+ s = replace (s , ' "' , " "" , " all" );
425+ s = replace (s , " '" , " '" , " all" );
426+ return s ;
427+ }
440428
441429 function downloads () {
442430 // serves download page
0 commit comments