GitHub Advisoryadvisory · npm

GHSA-7w38-ggh5-v542 Elo Ruby backend has Remote Code Execution via unescaped `#{...}` interpolation in emitted string literals and subtype-constraint labels

CRITICAL Published 2026-11-03 Modified 2026-11-03 Official page ↗
Aliases
CVE-2026-44266
Ecosystem
npm
Packages
@enspirit/elo
Severity
Critical
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weaknesses
CWE-116, CWE-1336, CWE-94
Reviewed
2026-11-03

Affected packages

• npm @enspirit/elo: < 1.0.1 → fixed in 1.0.1

Description

Summary Elo's Ruby backend emits attacker-controlled text into Ruby double-quoted strings without escaping the `#` character. Because `#` begins Ruby's interpolation sequences — `#{expr}`, `#@ivar`, `#@@cvar`, `#$gvar` — and any expression wrapped in `#{...}` may use backticks for shell execution, an Elo program that contains a crafted string literal or a labelled subtype constraint compiles to Ruby that evaluates attacker-chosen Ruby, and through backticks runs attacker-chosen shell commands, at runtime. The same Elo source compiled to JavaScript, Python, or SQL is benign, so the bug also breaks the cross-target portability guarantee the rest of the compiler honours. Vulnerable sinks Two sinks exist in `src/compilers/ruby.ts` at commit `749af7b`: 1. String-literal emitter — lines 180–184. The emitter escapes backslash, double quote, and control characters, but not `#`. Any `#{...}` sequence in an Elo string literal flows straight into the emitted Ruby source. 2. Labelled subtype-constraint branch — lines 405–412. The error message passed to `p_fail(p, "...")` is wrapped in a Ruby double-quoted string with no escape pass at all. Because the parser at `src/parser.ts:1339–1348` accepts single-quoted Elo strings as constraint labels, a label such as `'#{` `` `id` `` `}'` flows unchanged into the emitted Ruby. Attack surfaces All three surfaces share the same root cause (the missing `#` escape in the Ruby double-quoted-string emission path) and are addressed by the same patch: | # | Entry point | Threat model | |---|---|---| | 1 | `/try` playground on elo-lang.org (Ruby target) | 0-click server-side RCE against elo-lang.org infrastructure from a shared PoC URL | | 2 | `bin/eloc -t ruby -x` on a CI or DevOps host | RCE on any host that passes Elo strings through the CLI | | 3 | Library API — `defaultFormats.elo.parse()` with a Ruby emission step | Ruby RCE for any downstream integrator using Elo as a rule engine, configuration DSL, or form builder DSL (the primary documented use cases) | Proof of Concept String-literal sink: ``` poc.elo 'RES=#{`id 2>&1`}' ``` ``` $ eloc -t ruby -x poc.elo emitted Ruby contains: "RES=#{`id 2>&1`}" running it returns the output of id(1). ``` Replacing `` `id 2>&1` `` with `` `touch /tmp/pwned` `` produces a trivially observable filesystem side effect. Subtype-constraint label sink: ``` poc-subtype.elo let X = Int(i | '#{`touch /tmp/pwned2`}': i > 0) in X(-1) ``` The predicate fails for `-1`, causing `p_fail(p, "#{...}")` to interpolate and execute the backtick command at the Ruby layer. Both flows are automated in the reproduction script submitted with the original report and were verified against Ruby 3.x. Root cause The Ruby-emitter's double-quoted-string escape routine omitted `#` from its character set, and one emission path (the subtype-constraint label branch) performed no escape pass at all.

Related

Alias · CVE (1)

  • CVE-2026-44266 Elo is a purely functional expression language for no-code tools. vulnerability

Weakness · CWE (3)

  • CWE-116 Improper Encoding or Escaping of Output weakness
  • CWE-1336 Improper Neutralization of Special Elements Used in a Template Engine weakness
  • CWE-94 Improper Control of Generation of Code weakness

References