{"id":"CVE-2026-44266","aliases":["GHSA-7w38-ggh5-v542"],"title":"Elo Ruby backend has Remote Code Execution via unescaped `#{...}` interpolation in emitted string literals and subtype-constraint labels","summary":"Elo Ruby backend has Remote Code Execution via unescaped `#{...}` interpolation in emitted string literals and subtype-constraint labels","severity":"critical","cvss":9.8,"cwe":["CWE-94","CWE-116","CWE-1336"],"vendor":"enspirit","product":"@enspirit/elo","ecosystem":"npm","affected":["@enspirit/elo <= 1.0.0"],"patched":["@enspirit/elo 1.0.1"],"published":"2026-11-03","updated":"2026-11-03","sourceUpdated":"2026-11-03T17:51:30Z","source":"GHSA","sourceUrl":"https://github.com/advisories/GHSA-7w38-ggh5-v542","references":[{"url":"https://github.com/enspirit/elo/security/advisories/GHSA-7w38-ggh5-v542"},{"url":"https://github.com/enspirit/elo/commit/51b869963fbe445c18328714a7ccfbd76b21ba38"},{"url":"https://github.com/enspirit/elo/commit/d2a89a8db76a244eb740687524a7e8529b7554b5"},{"url":"https://github.com/advisories/GHSA-7w38-ggh5-v542"}],"tags":["ghsa","npm"],"ingestedAt":"2026-11-03T18:52:03.886Z","slug":"CVE-2026-44266","body":"## Overview\n\n### Summary\n\nElo'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.\n\nThe 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.\n\n### Vulnerable sinks\n\nTwo sinks exist in `src/compilers/ruby.ts` at commit `749af7b`:\n\n1. **String-literal emitter — lines 180–184.**\n   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.\n\n2. **Labelled subtype-constraint branch — lines 405–412.**\n   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.\n\n### Attack surfaces\n\nAll 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:\n\n| # | Entry point | Threat model |\n|---|---|---|\n| 1 | `/try` playground on elo-lang.org (Ruby target) | 0-click server-side RCE against elo-lang.org infrastructure from a shared PoC URL |\n| 2 | `bin/eloc -t ruby -x` on a CI or DevOps host | RCE on any host that passes Elo strings through the CLI |\n| 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) |\n\n### Proof of Concept\n\n**String-literal sink:**\n\n```\n# poc.elo\n'RES=#{`id 2>&1`}'\n```\n\n```\n$ eloc -t ruby -x poc.elo\n# emitted Ruby contains:  \"RES=#{`id 2>&1`}\"\n# running it returns the output of id(1).\n```\n\nReplacing `` `id 2>&1` `` with `` `touch /tmp/pwned` `` produces a trivially observable filesystem side effect.\n\n**Subtype-constraint label sink:**\n\n```\n# poc-subtype.elo\nlet X = Int(i | '#{`touch /tmp/pwned2`}': i > 0) in X(-1)\n```\n\nThe predicate fails for `-1`, causing `p_fail(p, \"#{...}\")` to interpolate and execute the backtick command at the Ruby layer.\n\nBoth flows are automated in the reproduction script submitted with the original report and were verified against Ruby 3.x.\n\n### Root cause\n\nThe 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.\n\n## Affected packages\n\n- `@enspirit/elo <= 1.0.0`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `@enspirit/elo 1.0.1`","depth":"midnight","depthScore":54,"depthScoreParts":{"impact":53.9,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}