fix(sitemap): reject HTML error pages instead of publishing them as the sitemap - #347
Merged
Merged
Conversation
The staging source moved from staging.deriv.com to staging-webflow.deriv.com. The old host now answers /sitemap.xml with a Next.js "Page not found" page, and it does so with a 200 status. Because the curl calls had no --fail flag, that HTML body was saved as temp/staging_sitemap.xml and curl exited 0. xml2js parsed it happily, but the root element was <html> rather than <urlset>, so every transform in modify_sitemap_new.js returned early through its `if (!sitemap.urlset)` guard and the closing-tag "repair" appended a </urlset> to the HTML document. The result was a 14 KB error page published as the live sitemap by a run that reported success. Add two guards so a bad upstream response can no longer be published: - curl now uses --fail, so an HTTP error can never be stored as content, plus --retry/--retry-all-errors to absorb the transient origin errors that failed later runs, and a verification step that greps for <urlset> and dumps the response head on failure. - modify_sitemap_new.js rejects input whose root element is not <urlset>, and the </urlset> append is replaced by a thrown error so it can no longer mask a malformed document. Output for a valid source sitemap is byte-identical to before. Co-authored-by: Cursor <cursoragent@cursor.com>
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.OpenSSF Scorecard
Scanned Manifest Files |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
We recently moved the staging source from
staging.deriv.comtostaging-webflow.deriv.com. The live sitemap athttps://urls.deriv.com/sitemap.xmlbroke around that change. This PR fixes the mechanism that allowed the breakage to be published, and makes the same class of failure loud instead of silent.What is actually live right now
https://urls.deriv.com/sitemap.xmlis not a sitemap. It is a Next.js "Page not found" HTML page with an XML declaration on top and a</urlset>tag glued to the bottom — 14,464 bytes, where a real sitemap is ~15 MB. It ends like this:Root cause
The old host
staging.deriv.comno longer serves a sitemap after the migration. It returns a Next.js 404 page — with an HTTP 200 status andcontent-type: text/html.Four things then lined up:
curl -o ... || exit 1cannot catch that, becausecurlhad no--fail. On an HTTP 200 it exits 0 regardless of the body.xml2jsparsed the HTML successfully — Next.js output is well-formed enough.html, noturlset, so every transform bailed out silently through its guard, e.g.if (!sitemap.urlset || !sitemap.urlset.url) return sitemap;.</urlset>to a document that never contained one.Run #11 did exactly this and reported success:
Verified by feeding the current
staging.deriv.comresponse throughmodify_sitemap_new.jslocally: the output is byte-identical to whaturls.deriv.comserves today, and 14,464 bytes matches the "14.13 KB" in that log.Why the later runs failed instead
Runs #12 and #13 already had the corrected hostname, but both failed:
sax reports lines 0-indexed and
Columnas the 1-based position of the>, so this is a 7-character closing tag at zero indentation on line 238 —</head>,</body>or</html>. The real sitemap uses 4-space indentation and has no such tag, so this was again an HTML document, this time one that did not parse. Same missing--fail; the only difference is that the bad HTML happened to be unparseable, so nothing was uploaded and run #11's corrupt file stayed live.The failing run was the safer outcome. That asymmetry is what this PR removes.
Changes
.github/workflows/generate_new_sitemap.ymlcurlnow uses--fail, so an HTTP error can never be saved as content.--retry 5 --retry-delay 10 --retry-all-errors --max-time, which absorbs the transient origin errors that killed runs ako/ add EOF #12 and ako/ add publish config #13.<urlset>and dumps the first 500 bytes on failure, so the log says what actually arrived..github/modify_sitemap_new.jsassertIsSitemaphard-fails when the parsed root element is not<urlset>.xml += "\n</urlset>"append is replaced by a thrown error. That single line is what turned an error page into a "valid" sitemap.MIN_EXPECTED_URLSfloor (1,000) catches a silently gutted sitemap.Verification
xml2jsurlset, 5,813 URLshttps://deriv.com, zerostagingreferencesgenerate_new_sitemap.ymlrunblocks passbash -nScope
This PR does not change the sitemap's content, filtering rules, or the
lastmodremoval from #346. For a healthy source the output is byte-identical. It only changes what happens when the source responds with something that is not a sitemap.It also does not fix the origin. If
staging-webflow.deriv.comserves an error page again, the workflow will now fail cleanly rather than publish it. The origin is currently healthy (200, 22,266,855 bytes,application/rss+xml, regenerated at07:43:49 GMT), so a run on this branch should restore the live sitemap.Follow-up, deliberately not included
publish-website.ymllines 99-100 still have the identical unguardedcurlagainst the retiredstaging.deriv.com:That workflow will corrupt the sitemap in exactly the way run #11 did the next time it runs. Kept out of this PR to hold the blast radius down — happy to do it here or separately.