---
id: "CVE-2026-44266"
kind: "vulnerability"
aliases: ["CVE-2026-44266", "GHSA-7w38-ggh5-v542"]
title: "Elo Ruby backend has Remote Code Execution via unescaped `#{...}` interpolation in emitted string literals and subtype-constraint labels"
severity: "critical"
cvss: 9.8
epss: null
exploited: false
status: "open"
category: "package"
products: [{"name": "@enspirit/elo", "fixed": ["1.0.1"], "affected": "<= 1.0.0", "ecosystem": "npm"}]
ecosystems: ["npm"]
tags: ["cwe-116", "cwe-1336", "cwe-94", "elo", "eloc", "npm", "package", "ruby"]
not_affected_if: ["javascript-or-python-or-sql-target-only", "trusted-elo-source-only"]
published_at: "2026-11-03T17:51:30+00:00"
first_seen_at: "2026-11-03T17:52:03+00:00"
updated_at: "2026-11-03T17:53:08+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-7w38-ggh5-v542", "first_seen_at": "2026-11-03T17:52:03+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44266", "first_seen_at": "2026-11-03T18:22:10+00:00"}]
---

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

The Elo compiler's Ruby target writes string literals into Ruby double-quoted strings without escaping the # character, so a #{...} sequence in Elo source becomes live Ruby code. An attacker who can supply Elo source compiled to Ruby can run arbitrary shell commands through backticks.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:@enspirit/elo affected <= 1.0.0 fixed 1.0.1

## How to check

- Check the installed version of `@enspirit/elo` in your project lockfile or with `npm ls @enspirit/elo`.
- Find any build, CI or server step that runs the Ruby target (`eloc -t ruby` or the Ruby emission API) on Elo source you do not fully control.

## Fix

- `@enspirit/elo` fixed in `1.0.1`; affected versions are `<= 1.0.0`.

## Timeline

- 2026-11-03T17:52:03+00:00 ghsa https://github.com/advisories/GHSA-7w38-ggh5-v542
- 2026-11-03T18:22:10+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-44266

## Documents

- [advisory] Maintainer's advisory: api.github.com https://api.github.com/repos/enspirit/elo/security-advisories/GHSA-7w38-ggh5-v542
- [advisory] Maintainer's advisory: github.com https://github.com/enspirit/elo/security/advisories/GHSA-7w38-ggh5-v542
- [fix] Patch: github.com https://github.com/enspirit/elo/commit/51b869963fbe445c18328714a7ccfbd76b21ba38
- [fix] Patch: github.com https://github.com/enspirit/elo/commit/d2a89a8db76a244eb740687524a7e8529b7554b5

## From the sources

### 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.

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
