Skip to content

Commit 964721a

Browse files
Sanitize loc and href in sitemap template (#445)
Co-authored-by: Duncan McClean <duncan@duncanmcclean.com>
1 parent 0180e43 commit 964721a

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

src/Sitemap/Page.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public function path()
2020

2121
public function loc()
2222
{
23-
return $this->data->get('canonical_url');
23+
return htmlspecialchars($this->data->get('canonical_url'), ENT_QUOTES | ENT_XML1, 'UTF-8');
2424
}
2525

2626
public function lastmod()

src/Sitemap/Sitemap.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,11 @@ private function hrefLangsForEntry(Entry $entry): array
258258
->filter(fn (Site $site) => $entry->in($site->handle())->published())
259259
->reject(fn (Site $site) => collect(config('statamic.seo-pro.alternate_locales.excluded_sites'))->contains($site->handle()))
260260
->map(fn (Site $site) => [
261-
'href' => $entry->in($site->handle())->absoluteUrl(),
261+
'href' => $this->sanitizeUrl($entry->in($site->handle())->absoluteUrl()),
262262
'hreflang' => strtolower(str_replace('_', '-', $site->locale())),
263263
])
264264
->push([
265-
'href' => $entry->root()->absoluteUrl(),
265+
'href' => $this->sanitizeUrl($entry->root()->absoluteUrl()),
266266
'hreflang' => 'x-default',
267267
])
268268
->all();
@@ -274,13 +274,18 @@ private function hrefLangsForTerm(Term $term): array
274274
->values()
275275
->reject(fn (Site $site) => collect(config('statamic.seo-pro.alternate_locales.excluded_sites'))->contains($site->handle()))
276276
->map(fn (Site $site) => [
277-
'href' => $term->in($site->handle())->absoluteUrl(),
277+
'href' => $this->sanitizeUrl($term->in($site->handle())->absoluteUrl()),
278278
'hreflang' => strtolower(str_replace('_', '-', $site->locale())),
279279
])
280280
->push([
281-
'href' => $term->inDefaultLocale()->absoluteUrl(),
281+
'href' => $this->sanitizeUrl($term->inDefaultLocale()->absoluteUrl()),
282282
'hreflang' => 'x-default',
283283
])
284284
->all();
285285
}
286+
287+
private function sanitizeUrl(string $url): string
288+
{
289+
return htmlspecialchars($url, ENT_QUOTES | ENT_XML1, 'UTF-8');
290+
}
286291
}

tests/SitemapTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,35 @@ public function it_outputs_additional_items()
489489

490490
$this->assertCount(8, $this->getPagesFromSitemapXml($content));
491491
}
492+
493+
#[Test]
494+
public function it_sanitizes_special_characters()
495+
{
496+
Sitemap::hook('additional', function ($payload, $next) {
497+
$payload->items->push((new Page)->with([
498+
'canonical_url' => url('\'"<>&'),
499+
'last_modified' => Carbon::parse('2025-01-01'),
500+
'change_frequency' => 'monthly',
501+
'priority' => 0.5,
502+
]));
503+
504+
return $next($payload);
505+
});
506+
507+
$this
508+
->get('/sitemap.xml')
509+
->assertOk()
510+
->assertHeader('Content-Type', 'text/xml; charset=UTF-8')
511+
->assertSeeInOrder([
512+
'<url>',
513+
'<loc>http://cool-runnings.com/&apos;&quot;&lt;&gt;&amp;</loc>',
514+
'<lastmod>2025-01-01</lastmod>',
515+
'<changefreq>monthly</changefreq>',
516+
'<priority>0.5</priority>',
517+
'</url>',
518+
'</urlset>',
519+
], escape: false);
520+
}
492521
}
493522

494523
class CustomSitemap extends Sitemap

0 commit comments

Comments
 (0)