Contributing
This page describes how to set up a development environment for Loofah, run the test suite and linters, and how the project's CI is organized. It is based on the repository's Gemfile, Rakefile, and .github/workflows/ci.yml.
Getting the Source
git clone https://github.com/flavorjones/loofah.git
cd loofah
bundle install
The Gemfile uses the gemspec for runtime dependencies (nokogiri, crass) and adds development tools: minitest, rake, rdoc, json, and hoe-markdown. On Ruby 3.0 and newer it also installs pinned versions of RuboCop and the rubocop-minitest, rubocop-packaging, rubocop-performance, rubocop-rake, and rubocop-shopify plugins. On JRuby it pins jar-dependencies.
Running the Tests
bundle exec rake test
The test task (Rake's TestTask) runs every file under test/:
| Directory | Contents |
|---|---|
test/unit/ |
API surface, scrubber traversal, helpers, encodings |
test/integration/ |
End-to-end scrubbing of HTML and XML, ad-hoc regression cases, helpers |
test/html5/ |
Sanitizer behavior, CSS and URI scrubbing, safelist properties |
test/assets/ |
Fixtures such as msword.html and html5lib sanitizer test data |
test/helper.rb prints the Nokogiri version info at startup and defines LOOFAH_HTML_VERSIONS, LOOFAH_HTML_DOCUMENT_CLASSES, and LOOFAH_HTML_DOCUMENT_FRAGMENT_CLASSES. These include the HTML5 variants only when Loofah.html5_support? is true, so most tests automatically run once per parser. On JRuby, the HTML5 cases are skipped and a separate test asserts that the html5_* methods raise NotImplementedError.
Tests use Minitest's spec syntax with context as an alias for describe.
When writing tests that depend on the parser, follow the existing pattern of branching on html5?, Nokogiri.jruby?, or Nokogiri.uses_libxml?("<2.9.11") rather than assuming one output.
Linting
bundle exec rake rubocop
RuboCop checks Gemfile, Rakefile, lib, loofah.gemspec, and test. To regenerate the todo file:
bundle exec rake rubocop:todo
If RuboCop cannot be loaded (for example on Ruby older than 3.0), the Rakefile prints NOTE: rubocop not available and skips the RuboCop tasks.
Rake Tasks
| Task | Description |
|---|---|
rake test |
Run the test suite (the default task) |
rake rubocop |
Run RuboCop (also part of the default task when RuboCop is available) |
rake rubocop:todo |
Regenerate .rubocop_todo.yml |
rake generate_safelists |
Load tasks/generate-safelists, a maintainer script for generating safelists from specifications |
rake debug_manifest |
Print the files packaged into the gem |
hoe-markdown also defines Markdown housekeeping tasks for the README and CHANGELOG.
Note: Much of
tasks/generate-safelistsis commented out, and the companiontasks/dompurify-to-jsonscript expects a local DOMPurify checkout. Treat these as maintainer tooling rather than part of the normal workflow.
Benchmarks
The benchmark/ directory contains scripts that compare Loofah with other sanitizers (Rails' sanitizer, sanitize, html5lib, HTMLFilter) on sample pages. They require several old third-party gems that are not in the Gemfile, so they are not expected to run in a default setup.
Continuous Integration
The ci workflow runs on pushes to main and v*.*.x branches, on version tags, on pull requests, weekly on a schedule, and on manual dispatch. Jobs:
| Job | What it does |
|---|---|
rubocop |
bundle exec rake rubocop on Ruby 3.4 |
| GitHub Actions audit | actionlint and zizmor on the workflow files |
test |
bundle exec rake test on Ruby 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, head, TruffleRuby head, JRuby 9.4, JRuby 10.0, and JRuby head |
downstream |
Clones rails-html-sanitizer, points its bundle at the Loofah checkout, and runs its test suite |
The downstream job is a good reminder: a change in Loofah's behavior can break rails-html-sanitizer and, through it, Rails applications. Run its tests locally if you change sanitizer behavior.
Project Files
| File | Purpose |
|---|---|
CHANGELOG.md |
Release notes, with a "next / unreleased" section at the top |
SECURITY.md |
Vulnerability reporting process |
docs/2022-10-decision-on-cdata-nodes.md |
Design decision on escaping CDATA content |
MIT-LICENSE.txt |
License |
Reporting Security Issues
Do not open public issues for vulnerabilities. SECURITY.md asks reporters to use GitHub's private reporting. Reports are acknowledged within 24 hours, with a more detailed response within 72 hours. If there is no reply within 48 hours, you can contact the security coordinator, Mike Dalessio, or post on the loofah-talk mailing list without describing the issue.