Skip to content

Upgrade crass to 1.0.7#442

Merged
rosa merged 1 commit into
mainfrom
upgrade-crass-1.0.7
Jun 29, 2026
Merged

Upgrade crass to 1.0.7#442
rosa merged 1 commit into
mainfrom
upgrade-crass-1.0.7

Conversation

@rosa

@rosa rosa commented Jun 29, 2026

Copy link
Copy Markdown
Member

Summary

Upgrade crass 1.0.6 → 1.0.7 (transitive, via loofah). Closes four DoS-hardening fixes in crass's CSS tokenizer/parser (GHSA-6wmf-3r64-vcwv, GHSA-wwpr-jff3-395c, GHSA-8vfg-2r28-hvhj, GHSA-6jxj-px6v-747w).

No application code changes — pure lockfile bump. bc3 and fizzy already run 1.0.7 in production. Full analysis posted as a comment below.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 29, 2026 10:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot wasn't able to review any files in this pull request.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@rosa

rosa commented Jun 29, 2026

Copy link
Copy Markdown
Member Author

🤖 Upgrade Plan: crass 1.0.6..1.0.7

Upgrade Plan: crass 1.0.6..1.0.7 for writebook

  • Date: 2026-06-29
  • Gem: crass
  • Range: v1.0.6..v1.0.7 (26 commits)
  • Target: writebook (currently on crass 1.0.6, transitive via loofah 2.25.0)

Summary

  • Total commits: 26
  • No impact: 22 (CI matrix, dev-dependency bumps, README/LICENSE/doc edits, version/release stamps, test-only fixes, merge commits)
  • Requires mitigation: 4 — all four are the 1.0.7 security/DoS hardening commits

Bottom line: Upgrade crass 1.0.6 → 1.0.7. It is a single transitive bump through loofah
(crass (~> 1.0.2) already allows 1.0.7), requires no application code changes, and closes
four denial-of-service vectors on the inline-CSS sanitization path. bc3 and fizzy already run
1.0.7 in production, confirming compatibility across our stack.

How writebook reaches crass

writebook does not call crass directly. It reaches it through the Rails HTML-sanitization chain:

HTML/ActionText sanitization (sanitize helpers / ActionText::ContentHelper.sanitizer)
  → rails-html-sanitizer
    → loofah 2.25.0  — lib/loofah/html5/scrub.rb
      → Crass.parse_properties / Crass::Parser  (inline-style CSS parsing & sanitization)

Any path that sanitizes untrusted HTML containing inline style attributes or <style> content
routes that CSS through crass. The four fixes below all harden that tokenizer/parser against
denial-of-service input.

Commits Requiring Mitigation

All four are backwards-compatible DoS fixes inside crass's tokenizer/parser. None changes output
for legitimate CSS. All reach writebook through the loofah inline-style path.

ea6726b7 — Prevent resource-exhaustion DoS via excessively large exponents

unlikely impact · GHSA-6wmf-3r64-vcwv. 1e5000000-style input forced 10**exponent (an
arbitrary-precision integer) before clamping to Float::MAX. Fix bounds the exponent first.
Especially relevant on Ruby ≥ 3.4, where unpatched crass can raise ArgumentError.

25d78cc6 — Prevent a long run of adjacent comments from exhausting the stack

unlikely impact · GHSA-wwpr-jff3-395c. ~12,000+ adjacent CSS comments could recurse the
tokenizer into SystemStackError. Fix rewrites comment-skipping to iterate.

da296646 — Fix inefficient handling of non-ASCII characters during tokenization

unlikely impact · GHSA-8vfg-2r28-hvhj. Many non-ASCII characters caused O(n) multi-byte
slicing CPU blowup. Fix uses internal byte-offset slicing in Crass::Scanner; public :pos
contract preserved.

cf682873 — Limit recursion depth to prevent stack overflow / memory exhaustion

likely impact (low behavioral risk) · GHSA-6jxj-px6v-747w. Adds default :maximum_depth of 25;
over-nested CSS now yields an :error node instead of :simple_block/:function — the only
parse-tree-shape change in the range. Through loofah's sanitizer the over-nested construct is
simply dropped (the safe outcome); no snapshots to update unless the app asserts on crass
parse-tree structure (none expected).

Mitigation (all four): bundle update --conservative crass → 1.0.7. No app code changes.

Execution

  1. bundle update --conservative crass — resolves 1.0.6 → 1.0.7 (satisfies loofah's ~> 1.0.2).
    No Gemfile change; crass is not a direct dependency.
  2. Confirm the lockfile diff touches only crass.
  3. Validation: rely on the repo's CI (pure transitive lockfile bump, zero app code changes,
    already proven by bc3 + fizzy in production).

No Impact (Skipped)

22 of 26 commits assessed as "no impact" during recon — CI matrix churn, dev-dependency bumps,
README/LICENSE/doc edits, version/release stamps, test-only fixes, and merge commits. Full
per-commit catalog: ~/Work/basecamp/37signals-hq/upgrade-recon/crass/<short-sha>.md.


Shared recon for this range applies to every loofah-using app. Deepest app-level exposure
write-up (with concrete rescue SystemStackError production evidence) is in the haystack plan.

@rosa rosa merged commit 1fdfa68 into main Jun 29, 2026
7 checks passed
@rosa rosa deleted the upgrade-crass-1.0.7 branch June 29, 2026 10:12
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