GitHub Advisoryadvisory · RubyGems

GHSA-9pmc-p236-855h Ruby CSS Parser: SSRF and Local File Disclosure in `CssParser::Parser#read_remote_file`

HIGH Published 2026-07-09 Modified 2026-07-09 Official page ↗
Aliases
CVE-2026-53727
Ecosystem
RubyGems
Packages
css_parser
Severity
High
CVSS vector
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N
Weaknesses
CWE-918
Reviewed
2026-07-09

Affected packages

• RubyGems css_parser: < 3.0.0 → fixed in 3.0.0

Description

Summary `CssParser::Parser#read_remote_file` (and therefore `load_uri!`, and the `@import`-following branch of `add_block!`) issues HTTP/HTTPS requests against any host, port and URI it is handed, with no scheme allowlist, no host / IP filtering, and no protection against link-local, loopback or RFC‑1918 addresses. `Location:` redirects are followed recursively back into the same function, which also services `file://` URIs, so a single attacker-controlled HTTP redirect upgrades the bug from SSRF to arbitrary local file disclosure. In practice, any consumer of `css_parser` that hands it attacker‑influenced CSS together with a `base_uri:` option — Premailer being the canonical example — is exposed. The attacker only needs the ability to land one `@import url(...)` in the CSS that the host application parses. Vulnerable code `lib/css_parser/parser.rb#L613-L687`: ```ruby def read_remote_file(uri) # :nodoc: ... begin uri = Addressable::URI.parse(uri.to_s) if uri.scheme == 'file' # local file path = uri.path path.gsub!(%r{^/}, '') if Gem.win_platform? src = File.read(path, mode: 'rb') # = 300 and res.code.to_i `: | File contents | Recoverable through `to_s` / `each_selector`? | |---|---| | `root:x:0:0:root:/root:/bin/bash\napi_key=…` (no braces) | nothing | | `{"AccessKeyId":"AKIA…","Secret":"wJalr…"}` (JSON, leading `{`) | nothing via `to_s`; partial leak via `each_rule_set` (empty selector + one parsed declaration) | | `SECRET=eyJhbGc… {}` (empty braces) | nothing (rule not added — raw decls empty) | | `api_key=…\ndatabase{host:db.internal;pw:hunter2;}` | full leak: selector = `api_key=… database`, decls = `host: db.internal; pw: hunter2` | | nginx / HCL-style config: `name { key: value; }` | full leak of both slots | In practice this means high-value targets that aren't CSS-shaped — TLS keys, SSH keys, JWTs, `.env` files (`KEY=VALUE` lines), `/etc/passwd`, binary content — do not leak their bytes through the parser API. Configuration files written in block-style DSLs (nginx, HCL/Terraform, Caddy, etc.) leak heavily. The `File.read` itself always executes, which is enough to (a) act as a file-existence oracle and (b) cause resource exhaustion on large pseudo-files like `/dev/zero`. The stronger recovery channel is the SSRF leg, not file disclosure: when the response comes from an internal HTTP service, Premailer-style consumers serialize the parsed CSS back into rendered HTML/email output, and any CSS-shaped bytes in the response surface there. Is it "blind"? It is *not* a classic blind SSRF. The bug has two recovery channels: 1. Application output. The library is most commonly driven by Premailer, which writes the parsed CSS back into the rendered HTML. Any response bytes that happen to form CSS rules are emitted into the output document and therefore visible to whoever receives the rendered output. Internal endpoints frequently return content that parses as CSS by accid…

Related

Alias · CVE (1)

Weakness · CWE (1)

  • CWE-918 Server-Side Request Forgery weakness

References