Skip to content

Keep Lexxy's engine from changing applications that don't use it - #1262

Open
jorgemanrubia wants to merge 2 commits into
mainfrom
safe-to-load-everywhere
Open

jorgemanrubia wants to merge 2 commits into
mainfrom
safe-to-load-everywhere

Conversation

@jorgemanrubia

@jorgemanrubia jorgemanrubia commented Sep 11, 2026 •

Copy link
Copy Markdown
Member

Make Lexxy the default Action Text editor for new applications makes Action Text load Lexxy in every application. That includes applications that use Trix, and ones that don't use rich text at all. Two parts of Lexxy's engine change those applications:

  • Sanitizer lists. When Action Text content loads, Lexxy replaces ActionText::ContentHelper.allowed_tags and allowed_attributes with its own widened lists. An application that restricts those lists gets Lexxy's tables, media and style attributes added back.
  • Blob JSON. Lexxy's Blob#as_json adds previewable and a preview url to every previewable blob. Without ActiveStorage::Current.url_options it raises, and it ignores only, except and root.

What changes

  • Sanitizer lists, on a Rails version that includes Lexxy. Lexxy is already registered as an editor there, so the engine doesn't touch the lists. Action Text's own defaults already allow Lexxy's markup, and the application's lists stay as it sets them.
  • Sanitizer lists, elsewhere. Lexxy keeps adding its markup to the lists, including lists an application sets itself. Applications rely on that today: this repository's dummy app sets its lists in to_prepare, and making Lexxy stop appending broke 8 of its tests.
  • Blob JSON. Blob#as_json now:
    • skips the preview fields without URL options;
    • follows Active Model for only and except: a supplied only, even an empty one, is an allowlist and takes precedence over except;
    • puts the fields inside the root when one is requested.
  • Docs. A new "Sanitizing rendered content" section documents who owns the sanitizer lists.

Tests

The new tests fail on main and pass here. They cover the sanitizer lists, using throwaway apps in subprocesses with lazy and eager loading, and Blob#as_json. Locally:

Setup Runs Result
Rails main 98 0 failures, 0 errors (3 skips)
Rails 8.1, USE_RAILS_WITHOUT_ACTION_TEXT_ADAPTER=true 98 0 failures (2 skips)

The Rails 8.1 run needed json < 3 pinned locally, because json 3.0 breaks Rails 8.1.3.1.

CI

  • Rails 8.1 job: it fails because json 3.0 breaks Rails 8.1.3.1's ActiveSupport::JSON.decode. It fails the same way on main and on other open PRs.

  • Rails main job: It passes. The first run hit one error and the rerun passed: 98 runs, 0 failures, 0 errors. The error was PageRefreshesTest#test_prompts_work_after_page_refresh, which times out waiting for the mention prompt after a Turbo page refresh. That test is flaky without this change. Run on its own:

    Branch Failed
    Only the gemspec change 2 of 4 runs
    Respect config.action_text.editor on Rails that include Lexxy #1261 2 of 6 runs
    This branch 6 of 8 runs

    It passed in both full local suite runs on this branch, and nothing here touches prompts or page refreshes.

This is independent of Depend on railties instead of rails and Respect config.action_text.editor on Rails that include Lexxy. The Rails PR needs all three.

Rails versions that include Lexxy load it in every Action Text
application. Two parts of Lexxy's engine then changed applications that
use Trix, or no editor at all:

- The sanitizer initializer replaced the tags and attributes an
  application allows. On a Rails version that includes Lexxy, Action Text
  already allows Lexxy's markup, so leave the lists to the application
  there. Elsewhere, keep adding Lexxy's markup to the lists, as before.
- Blob#as_json raised without URL options and ignored only, except, and
  root. Skip the preview fields without URL options, and respect those
  serialization options.
Copilot AI balanced review requested due to automatic review settings September 11, 2026 10:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Explicit empty only allowlists must exclude preview fields.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR prevents Lexxy from overriding sanitizer settings and makes preview blob JSON serialization safer.

Changes:

  • Conditionally applies sanitizer customization.
  • Respects URL, only, except, and root serialization options.
  • Adds sanitizer and blob regression tests.

Review finding: Moderate — blob.as_json(only: []) incorrectly includes preview fields.

File summaries
File Description
test/actiontext/sanitizer_lists_test.rb Tests sanitizer behavior across configurations.
test/actiontext/blob_test.rb Tests preview JSON serialization options.
lib/lexxy/engine.rb Gates sanitizer customization.
lib/active_storage/blob_with_preview_url.rb Updates preview JSON handling.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +25 to +28
only = Array(options[:only]).map(&:to_s)
except = Array(options[:except]).map(&:to_s)

(only.empty? || only.include?(field)) && except.exclude?(field)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Fixed in 1f017ae. The preview fields now follow Active Model: a supplied only, including an empty array, is an allowlist and takes precedence over except. as_json(only: []) returns no preview fields, and as_json(only: [ :id, :url ], except: :url) returns id and url. Both cases are in the regression tests.

Review found that Blob#as_json added preview fields for only: [] and
applied except alongside only, unlike Active Model. The sanitizer tests
also depended on which Rails was bundled, and asked for load_defaults
that Rails 8.0 doesn't have.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants