TryGhost
OSS
Ghost
Overview
Sign in / Sign up
Open main menu
Ghost
GitHub
Overview
Runs
Analytics
Loading workspace stats
Loading workspace insights...
Statistics interval
7 days
30 days
Latest CI Pipeline Executions
Status
Fix filter
Filter
Fuzzy
Filter range
Sort by
Sort by
Start time
Sort ascending
Sort descending
Succeeded
main
0f60af63 Improved embedded iframe handling in the ActivityPub reader (#29525) ref https://linear.app/ghost/issue/SC-69
by Sag
S
Succeeded
main
0f60af63 Improved embedded iframe handling in the ActivityPub reader (#29525) ref https://linear.app/ghost/issue/SC-69
by Sag
S
Succeeded
main
0f60af63 Improved embedded iframe handling in the ActivityPub reader (#29525) ref https://linear.app/ghost/issue/SC-69
by Sag
S
Succeeded
29525
86d1661b Improved embedded iframe handling in the ActivityPub reader no ref Apply a fixed sandbox to iframes as a defence-in-depth hardening measure.
by Sag
S
Succeeded
29525
a7998550 Improved embedded iframe handling in the ActivityPub reader no ref Apply a fixed sandbox to iframes as a defence-in-depth hardening measure.
by Sag
S
Succeeded
29525
a55e7c59 Improved embedded iframe handling in the ActivityPub reader no ref Apply a fixed sandbox to iframes as a defence-in-depth hardening measure.
by Sag
S
Succeeded
29525
05be71e6 Improved embedded iframe handling in the ActivityPub reader no ref Apply a fixed sandbox to iframes as a defence-in-depth hardening measure.
by Sag
S
Succeeded
29519
7e42862c Improved embedded iframe handling in the ActivityPub reader no ref Apply a fixed sandbox to iframes as a defence-in-depth hardening measure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
by Sag
S
Succeeded
29519
0acf93b4 Improved embedded iframe handling in the ActivityPub reader no ref Apply a fixed sandbox to iframes as a defence-in-depth hardening measure. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
by Sag
S
Failed
main
c941de1a Added client-side sanitization to the ActivityPub reader (#29483) ref https://linear.app/ghost/issue/SC-69 ## Problem The ActivityPub reader relied on the backend sanitisation layer at ingestion, using `sanitize-html` . Every other remote-content surface in the app (feed items, bios, notifications, profile fields) already had a second DOMPurify layer — the reader was the one place where a single server-side sanitizer was the sole XSS control, which is fragile against sanitizer bypasses or legacy unsanitized rows. ## Solution The new `sanitizeArticleContent` now sanitizes the ActivityPub reader as the last step before articles are injected into the iframe. Rather than recreating the backend `sanitize-html` rules, it uses DOMPurify defaults with targeted overrides mirroring what the backend deliberately allows: - iframes stay allowed so YouTube-style static embeds keep working (dangerous attributes like `srcdoc` and event handlers remain stripped by DOMPurify defaults) - scripts are only kept when loaded via https from `platform.twitter.com` / `platform.x.com` — matching the backend's `allowedScriptHostnames` — so Twitter embeds keep working; inline script code is stripped even on allowed scripts It runs on a dedicated DOMPurify instance so the looser article rules and the script hook can't leak into the default sanitizer used everywhere else, and with `FORCE_BODY` so articles that start with an embed script don't silently lose it to the parser hoisting it into `<head>`. Unit tests cover the embed-preserving and attack-rejecting paths, including lookalike script hostnames and instance isolation.
by Sag
S
Succeeded
main
c941de1a Added client-side sanitization to the ActivityPub reader (#29483) ref https://linear.app/ghost/issue/SC-69 ## Problem The ActivityPub reader relied on the backend sanitisation layer at ingestion, using `sanitize-html` . Every other remote-content surface in the app (feed items, bios, notifications, profile fields) already had a second DOMPurify layer — the reader was the one place where a single server-side sanitizer was the sole XSS control, which is fragile against sanitizer bypasses or legacy unsanitized rows. ## Solution The new `sanitizeArticleContent` now sanitizes the ActivityPub reader as the last step before articles are injected into the iframe. Rather than recreating the backend `sanitize-html` rules, it uses DOMPurify defaults with targeted overrides mirroring what the backend deliberately allows: - iframes stay allowed so YouTube-style static embeds keep working (dangerous attributes like `srcdoc` and event handlers remain stripped by DOMPurify defaults) - scripts are only kept when loaded via https from `platform.twitter.com` / `platform.x.com` — matching the backend's `allowedScriptHostnames` — so Twitter embeds keep working; inline script code is stripped even on allowed scripts It runs on a dedicated DOMPurify instance so the looser article rules and the script hook can't leak into the default sanitizer used everywhere else, and with `FORCE_BODY` so articles that start with an embed script don't silently lose it to the parser hoisting it into `<head>`. Unit tests cover the embed-preserving and attack-rejecting paths, including lookalike script hostnames and instance isolation.
by Sag
S
Succeeded
29483
7122bb08 Improved comments
by Sag
S
Succeeded
29483
7d290e60 Improved comments
by Sag
S
Succeeded
29483
bc08a17c Added client-side sanitization to the ActivityPub reader The reader injected remote ActivityPub article HTML into a script-executing, unsandboxed srcdoc iframe with only link-protocol filtering, so it relied entirely on the backend sanitize-html pass at ingestion. Every other remote-content surface in the app already had a second DOMPurify layer; a single permissive server-side sanitizer as the sole XSS control is fragile against sanitizer bypasses or legacy unsanitized rows. The new sanitizeArticleContent uses DOMPurify defaults with targeted overrides mirroring the backend rules rather than recreating them: iframes stay allowed for YouTube-style embeds, and scripts are only kept when loaded via https from platform.twitter.com / platform.x.com (with inline code stripped) so Twitter embeds keep working. It runs on a dedicated DOMPurify instance so the looser rules can't leak into the default sanitizer used by the feed, and with FORCE_BODY so articles that start with an embed script don't silently lose it to head hoisting.
by Sag
S
Succeeded
29169
80675f53 Fixed missing spaces in copied profile image in ActivityPub fixes https://github.com/TryGhost/ActivityPub/issues/1312 The "Share your profile" card is copied as an image by rendering a hidden clone of the card through html2canvas, which draws each word onto a canvas with fillText. The clone forced `font-family: system-ui`, and Firefox on macOS resolves that generic alias to a different (wider/heavier) face in canvas than it does in DOM layout. Each word was therefore drawn wider than the slot measured for it and bled into the following inter-word gap, so the copied image read as e.g. "Availableon Ghost,Flipboard" while the preview looked fine. Chrome and Safari resolve the alias consistently, which is why only Firefox was affected. Using concrete family names instead of the `system-ui` alias makes DOM layout and canvas text resolve the same font in every browser, keeping the spaces in the copied image.
by Sag
S
Succeeded
29129
2701a79a 🐛 Fixed post card styles and scripts going missing until Ghost restarts ref https://linear.app/ghost/issue/ONC-1865 - cards.min.css/js are generated into content/public at runtime; when the files went missing or the folder was unwritable, every request 404d until reboot because nothing ever retried the build - a missing generated file now triggers one shared rebuild and a single retried read; concurrent requests join the in-flight build instead of 404ing - build policy (single-flight, 10s retry backoff, rebuild-on-theme-change) lives on the assets service so every entry point shares one throttle - the minifier now writes atomically (temp file + rename) so a request can never read a half-written asset, and built files are no longer pinned in the middleware memory cache where a bad read would stick until restart Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
by Sag
S
Succeeded
29128
b5599384 🐛 Fixed post card styles and scripts going missing until Ghost restarts ref https://linear.app/ghost/issue/ONC-1865 - cards.min.css/js were built into content/public and served by reading them back from disk, so a wiped or unwritable content folder meant a 404 on every request until reboot - the assets are rebuilt from theme sources on every boot anyway, so the disk copy is not a real cache; the serving middleware already held the body in memory permanently - card assets are now kept in memory by the assets service and served from there; the disk write is best-effort back-compat only, so write failures and deleted files can no longer take the assets offline - build policy (single-flight, retry backoff, rebuild-on-theme-change) now lives in one place on the service instead of per-route Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
by Sag
S
Succeeded
main
0b98ffe1 Removed markdownToMobiledoc test helper (#29075) ref https://linear.app/ghost/issue/GVA-816 Follow-up to #28906, which removed mobiledoc rendering and HTML-to-mobiledoc conversion. Test fixtures were still seeding post content as mobiledoc via the `markdownToMobiledoc` helper — since the model now converts any mobiledoc input to lexical on save, that content ended up stored as lexical anyway, so the helper was just a roundabout way of producing lexical content. ### Changes - Replaced `DataGenerator.markdownToMobiledoc` with `DataGenerator.markdownToLexical`, which produces the exact markdown-card lexical doc that `mobiledocToLexical()` emitted for these fixtures — stored content and rendered HTML are unchanged, so no snapshot updates are needed. - `DataGenerator.forKnex.createPost` now defaults to lexical content (still accepts an explicit `mobiledoc` override), and `DataGenerator.forModel.posts` picks `lexical` instead of `mobiledoc`. - Tests that deliberately exercise the legacy mobiledoc input path now build their mobiledoc locally, making the legacy intent explicit: - importer v1 tests (v1 exports predate lexical) - the `force_rerender` mobiledoc→lexical conversion test in `posts-legacy.test.js` - The `post-with-all-media-types-mobiledoc` fixture and `restoreLegacyMobiledocPosts` are untouched — they still cover the legacy mobiledoc read/render paths.
by Sag
S
Succeeded
29075
657325e9 Fixed webhook tests reading mobiledoc from lexical fixtures ref https://linear.app/ghost/issue/GVA-816 - the e2e-webhooks tests built API payloads from fixtureManager.get('posts', 1).mobiledoc, which is undefined now that the fixture stores lexical, so posts were created blank and the webhook body snapshots failed in CI - the rendered html in the snapshots is unchanged as the lexical fixture carries the same markdown card the mobiledoc version did Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
by Sag
S
Failed
29075
09e6adee Removed markdownToMobiledoc test helper ref https://linear.app/ghost/issue/GVA-816 - Ghost renders lexical only; content stored as mobiledoc is converted on save, so test fixtures seeded via markdownToMobiledoc ended up stored as lexical anyway - replaced the helper with markdownToLexical, which produces the same markdown-card lexical doc that the mobiledoc converter emitted, keeping stored content and rendered HTML identical - DataGenerator.forKnex.createPost now defaults to lexical content while still accepting explicit mobiledoc for tests covering the legacy input path (importer v1, force re-render conversion), which now build their mobiledoc locally to make the legacy intent explicit Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
by Sag
S
Previous page
Previous
Next
Next page