Skip to content

Commit 895ed62

Browse files
authored
fix(utils): prevent sitemap metadata leak across url without loc (#3795)
`SitemapXmlParser` in `packages/utils/src/internals/sitemap.ts` could leak per-url metadata from a malformed `<url>` block into the next one, and emitted `Invalid Date` for unparseable `<lastmod>` values. This PR fixes both.
1 parent b660506 commit 895ed62

2 files changed

Lines changed: 41 additions & 3 deletions

File tree

packages/utils/src/internals/sitemap.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,10 @@ class SitemapXmlParser extends Transform {
136136
this.currentTag = undefined;
137137
}
138138

139-
if (name === 'url' && this.url.loc !== undefined) {
140-
this.push({ type: 'url', ...this.url, loc: this.url.loc } satisfies SitemapItem);
139+
if (name === 'url') {
140+
if (this.url.loc !== undefined) {
141+
this.push({ type: 'url', ...this.url, loc: this.url.loc } satisfies SitemapItem);
142+
}
141143
this.url = {};
142144
}
143145
}
@@ -157,7 +159,10 @@ class SitemapXmlParser extends Transform {
157159
text = text.trim();
158160

159161
if (this.currentTag === 'lastmod') {
160-
this.url.lastmod = new Date(text);
162+
const lastmod = new Date(text);
163+
if (!Number.isNaN(lastmod.getTime())) {
164+
this.url.lastmod = lastmod;
165+
}
161166
}
162167

163168
if (this.currentTag === 'priority') {

packages/utils/test/sitemap.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,39 @@ describe('Sitemap', () => {
423423
);
424424
});
425425

426+
it('does not leak metadata from a url without loc and drops invalid lastmod', async () => {
427+
const items: SitemapUrl[] = [];
428+
429+
for await (const item of parseSitemap([
430+
{
431+
type: 'raw',
432+
content: [
433+
'<?xml version="1.0" encoding="UTF-8"?>',
434+
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
435+
'<url>',
436+
'<loc>http://not-exists.com/a</loc>',
437+
'<lastmod>not-a-date</lastmod>',
438+
'</url>',
439+
'<url>',
440+
'<lastmod>2020-01-01</lastmod>',
441+
'<priority>0.5</priority>',
442+
'</url>',
443+
'<url>',
444+
'<loc>http://not-exists.com/c</loc>',
445+
'</url>',
446+
'</urlset>',
447+
].join('\n'),
448+
},
449+
])) {
450+
items.push(item);
451+
}
452+
453+
expect(items.map((item) => item.loc)).toEqual(['http://not-exists.com/a', 'http://not-exists.com/c']);
454+
expect(items[0].lastmod).toBeUndefined();
455+
expect(items[1].lastmod).toBeUndefined();
456+
expect(items[1].priority).toBeUndefined();
457+
});
458+
426459
it('loads sitemaps that reference other sitemaps from string', async () => {
427460
const sitemap = await Sitemap.fromXmlString(
428461
[

0 commit comments

Comments
 (0)