CVE-2026-55086: ep_etherpad-lite Import/export Uses Math.random() For Temp Vulnerability

MODERATE
Advisory Identifier: GHSA-2jwf-f4xq-f24h
CVSS 4.2
CWE-377 CWE-59
Published
Advisory Summary

CVE-2026-55086 is a MODERATE (CVSS 4.2) import/export uses math.random() for temp vulnerability (CWE-377: Insecure Temporary File) affecting ep_etherpad-lite libraries in the npm ecosystem. The issue involves import/export uses math.random() for temp file paths; predictable paths on shared /tmp enable symlink-based file overwrite. Remediation is available by upgrading affected packages to version 3.1.0.

CVE-2026-55086 at a Glance

CVE Identifier CVE-2026-55086
GHSA Identifier GHSA-2jwf-f4xq-f24h
Severity MODERATE
CVSS v3.1 Base Score 4.2
CWE Classification CWE-377: Insecure Temporary File
Affected Product ep_etherpad-lite
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 4.2

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:L/I:L/A:N).

Attack Vector Local (AV:L)
Attack Complexity High (AC:H)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Changed (S:C)
Confidentiality Low (C:L)
Integrity Low (I:L)
Availability None (A:N)

How to Fix CVE-2026-55086

To remediate this vulnerability, upgrade all affected ep_etherpad-lite dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

ep_etherpad-lite fixed: 3.1.0
npm install ep_etherpad-lite@3.1.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls ep_etherpad-lite

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-55086, inspect your project dependencies using npm:

npm ls ep_etherpad-lite

Analyze the terminal output to locate any version of ep_etherpad-lite in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 3.1.0), your application is affected.

Note: The npm ls ep_etherpad-lite command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-377: Insecure Temporary File, where creating and using insecure temporary files can leave application and system data vulnerable to attack. In ep_etherpad-lite, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Description `src/node/handler/ImportHandler.ts` and `src/node/handler/ExportHandler.ts` both compute their temporary working-file paths as: ```ts const randNum = Math.floor(Math.random() * 0xFFFFFFFF); const srcFile = `${os.tmpdir()}/etherpad_export_${randNum}.html`; const destFile = `${os.tmpdir()}/etherpad_export_${randNum}.${type}`; ``` Two flaws compound: 1. **`Math.random()` is not crypto-secure.** It yields at most ~32 bits of entropy and is **predictable across calls within the same Node process** (V8 shares PRNG state between consecutive `Math.random()` invocations). An attacker on the same host who observes any earlier temp-file name from logs or other side channels can predict subsequent names. 2. **The paths land in `os.tmpdir()`.** On a typical Linux system this is `/tmp` — a shared world-writable directory. An unprivileged local attacker can pre-create a symbolic link at a predicted path pointing at any file the Etherpad process can write: ``` ln -s /etc/etherpad/SESSIONKEY.txt /tmp/etherpad_export_<predicted>.html ``` When `ExportHandler` calls `fs.writeFile(srcFile, html)` (or `ImportHandler` calls `fs.rename(srcFile, destFile)` / `soffice` writes its converted output to the path), the open syscall follows the symlink and either reads from or overwrites the linked target. For deployments where the Etherpad process runs as a privileged user (notably some Docker base images that run as root, snap confinement edge cases, or hand-rolled systemd units), this becomes arbitrary file overwrite. The Import path is more impactful in practice: the file content the attacker can land in the symlink target is partially attacker-controlled (the post-soffice/post-mammoth conversion output of the uploaded document). ## Severity rationale - **AV:L** — requires local access to the host that runs Etherpad. Multi-tenant hosts (shared dev boxes, k8s shared-node setups, single-server CI workers) are the realistic threat surface. - **AC:H** — attacker needs to predict the temp filename, which requires observing prior names or shared PRNG state. - **PR:L** — any unprivileged local account. - **UI:N** — no user interaction. - **S:C** — scope change from local user to whatever the Etherpad process can write. - **C:L / I:L / A:N** — bounded by what the Etherpad process can already touch; confidentiality is via secondary read-paths (the LibreOffice conversion error log can echo bytes from the symlinked file). Single-tenant deployments where only the Etherpad operator has shell access on the host are not exposed. ## Affected versions - All `ep_etherpad-lite` versions through `v3.0.0` (inclusive). The `Math.floor(Math.random() * 0xFFFFFFFF)` pattern is present in `src/node/handler/ImportHandler.ts` and `src/node/handler/ExportHandler.ts` for as far back as the repository history extends — older than the 2024-03-16 snapshot at commit [`107598b`](https://github.com/ether/etherpad/commit/107598b) where it first appears in the current file paths, and predating the v1.x era. ## Patched versions - `ep_etherpad-lite >= 3.1.0` — the fix is on `develop` HEAD as commit `8c6104c`. Update this field with the actual tagged release version when it ships. ## Proof of concept (sketch) ``` # Pre-condition: attacker has shell on the same host as Etherpad, # has read access to /tmp, and Etherpad's process can write to the # chosen target (e.g. runs as root inside a Docker container). # 1. Observe a temp filename from logs (or guess via prior exports). TARGET=/etc/etherpad/SESSIONKEY.txt GUESS=/tmp/etherpad_export_$(./predict-next-random)$EXT # implementation-specific # 2. Place the symlink before Etherpad creates the file. ln -s "$TARGET" "$GUESS" # 3. Trigger an export from the Etherpad UI (or via the API). # Etherpad calls fs.writeFile(srcFile, ...) which open()s the symlink # target with O_WRONLY|O_TRUNC and clobbers $TARGET with HTML content. ``` In practice the exact reproduction depends on the host's PRNG state predictability and the deployment's process-owner privileges. A reliable exploit chain needs `setpriv`-style host access or a sibling tenant. ## Workarounds - Run Etherpad in a container with a private `/tmp` (Docker: `--tmpfs /tmp:rw,noexec,nosuid,nodev,size=64m`, systemd: `PrivateTmp=true`). - Ensure the Etherpad process does NOT run as root and has write access only to its own data directory. - Set `TMPDIR` to an Etherpad-private directory in the environment. ## Fix Patched in [`8c6104c`](https://github.com/ether/etherpad/commit/8c6104c) (PR [#7784](https://github.com/ether/etherpad/pull/7784)): ```diff -const randNum = Math.floor(Math.random() * 0xFFFFFFFF); +const randNum = crypto.randomBytes(16).toString('hex'); ``` 128 bits of CSPRNG entropy. Predicting the next filename is no longer feasible. A bigger follow-up — per-request `mkdtemp` subdirectories with `O_EXCL`/`O_NOFOLLOW` semantics — is deferred to a later release; the immediate window (predictable 32-bit collisions across processes) is what the patch closes. ## Resources - Patched in: https://github.com/ether/etherpad/pull/7784 (squash commit `8c6104c`). - `Math.random()` is not appropriate for security-sensitive contexts. See the MDN guidance for [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) and the Node.js recommendation to use [`crypto.randomBytes`](https://nodejs.org/api/crypto.html#cryptorandombytessize-callback) for unpredictable values. ## Credits Reported during an internal security audit by Claude (via @JohnMcLear).

CVE-2026-55086 Affected ep_etherpad-lite Versions

1 component(s)
Fixed Versions:
3.1.0
Product / Package Ecosystem Affected Versions Fixed Version
ep_etherpad-lite
npm
< 3.1.0
3.1.0

References

Frequently Asked Questions

What is CVE-2026-55086?
CVE-2026-55086 is a security vulnerability in ep_etherpad-lite involving Import/export uses Math.random() for temp file paths; predictable paths on shared /tmp enable symlink-based file overwrite under CWE-377: Insecure Temporary File.
Which ep_etherpad-lite versions are affected?
Affected versions include releases prior to 3.1.0 across affected packages: ep_etherpad-lite.
What version fixes CVE-2026-55086?
Upgrading to 3.1.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is ep_etherpad-lite affected?
Yes, ep_etherpad-lite is affected in releases prior to 3.1.0. Upgrade your dependency to 3.1.0 or later.
What is the CVSS score of CVE-2026-55086?
CVE-2026-55086 has a CVSS v3.1 base score of 4.2 (MODERATE) with vector string CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:L/I:L/A:N.
What CWE is associated with CVE-2026-55086?
CVE-2026-55086 is classified under CWE-377: Insecure Temporary File (Creating and using insecure temporary files can leave application and system data vulnerable to attack.).
How can I check npm dependencies for CVE-2026-55086?
Run the dependency tree command: 'npm ls ep_etherpad-lite'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-2jwf-f4xq-f24h",
  "modified" : "2026-08-13T14:11:08Z",
  "published" : "2026-08-13T14:11:07Z",
  "aliases" : [ "CVE-2026-55086" ],
  "summary" : "ep_etherpad-lite: Import/export uses Math.random() for temp file paths; predictable paths on shared /tmp enable symlink-based file overwrite",
  "details" : "## Description\n\n`src/node/handler/ImportHandler.ts` and `src/node/handler/ExportHandler.ts` both compute their temporary working-file paths as:\n\n```ts\nconst randNum = Math.floor(Math.random() * 0xFFFFFFFF);\nconst srcFile = `${os.tmpdir()}/etherpad_export_${randNum}.html`;\nconst destFile = `${os.tmpdir()}/etherpad_export_${randNum}.${type}`;\n```\n\nTwo flaws compound:\n\n1. **`Math.random()` is not crypto-secure.** It yields at most ~32 bits of entropy and is **predictable across calls within the same Node process** (V8 shares PRNG state between consecutive `Math.random()` invocations). An attacker on the same host who observes any earlier temp-file name from logs or other side channels can predict subsequent names.\n\n2. **The paths land in `os.tmpdir()`.** On a typical Linux system this is `/tmp` — a shared world-writable directory. An unprivileged local attacker can pre-create a symbolic link at a predicted path pointing at any file the Etherpad process can write:\n\n   ```\n   ln -s /etc/etherpad/SESSIONKEY.txt /tmp/etherpad_export_<predicted>.html\n   ```\n\n   When `ExportHandler` calls `fs.writeFile(srcFile, html)` (or `ImportHandler` calls `fs.rename(srcFile, destFile)` / `soffice` writes its converted output to the path), the open syscall follows the symlink and either reads from or overwrites the linked target. For deployments where the Etherpad process runs as a privileged user (notably some Docker base images that run as root, snap confinement edge cases, or hand-rolled systemd units), this becomes arbitrary file overwrite.\n\nThe Import path is more impactful in practice: the file content the attacker can land in the symlink target is partially attacker-controlled (the post-soffice/post-mammoth conversion output of the uploaded document).\n\n## Severity rationale\n\n- **AV:L** — requires local access to the host that runs Etherpad. Multi-tenant hosts (shared dev boxes, k8s shared-node setups, single-server CI workers) are the realistic threat surface.\n- **AC:H** — attacker needs to predict the temp filename, which requires observing prior names or shared PRNG state.\n- **PR:L** — any unprivileged local account.\n- **UI:N** — no user interaction.\n- **S:C** — scope change from local user to whatever the Etherpad process can write.\n- **C:L / I:L / A:N** — bounded by what the Etherpad process can already touch; confidentiality is via secondary read-paths (the LibreOffice conversion error log can echo bytes from the symlinked file).\n\nSingle-tenant deployments where only the Etherpad operator has shell access on the host are not exposed.\n\n## Affected versions\n\n- All `ep_etherpad-lite` versions through `v3.0.0` (inclusive). The `Math.floor(Math.random() * 0xFFFFFFFF)` pattern is present in `src/node/handler/ImportHandler.ts` and `src/node/handler/ExportHandler.ts` for as far back as the repository history extends — older than the 2024-03-16 snapshot at commit [`107598b`](https://github.com/ether/etherpad/commit/107598b) where it first appears in the current file paths, and predating the v1.x era.\n\n## Patched versions\n\n- `ep_etherpad-lite >= 3.1.0` — the fix is on `develop` HEAD as commit `8c6104c`. Update this field with the actual tagged release version when it ships.\n\n## Proof of concept (sketch)\n\n```\n# Pre-condition: attacker has shell on the same host as Etherpad,\n# has read access to /tmp, and Etherpad's process can write to the\n# chosen target (e.g. runs as root inside a Docker container).\n\n# 1. Observe a temp filename from logs (or guess via prior exports).\nTARGET=/etc/etherpad/SESSIONKEY.txt\nGUESS=/tmp/etherpad_export_$(./predict-next-random)$EXT  # implementation-specific\n\n# 2. Place the symlink before Etherpad creates the file.\nln -s \"$TARGET\" \"$GUESS\"\n\n# 3. Trigger an export from the Etherpad UI (or via the API).\n# Etherpad calls fs.writeFile(srcFile, ...) which open()s the symlink\n# target with O_WRONLY|O_TRUNC and clobbers $TARGET with HTML content.\n```\n\nIn practice the exact reproduction depends on the host's PRNG state predictability and the deployment's process-owner privileges. A reliable exploit chain needs `setpriv`-style host access or a sibling tenant.\n\n## Workarounds\n\n- Run Etherpad in a container with a private `/tmp` (Docker: `--tmpfs /tmp:rw,noexec,nosuid,nodev,size=64m`, systemd: `PrivateTmp=true`).\n- Ensure the Etherpad process does NOT run as root and has write access only to its own data directory.\n- Set `TMPDIR` to an Etherpad-private directory in the environment.\n\n## Fix\n\nPatched in [`8c6104c`](https://github.com/ether/etherpad/commit/8c6104c) (PR [#7784](https://github.com/ether/etherpad/pull/7784)):\n\n```diff\n-const randNum = Math.floor(Math.random() * 0xFFFFFFFF);\n+const randNum = crypto.randomBytes(16).toString('hex');\n```\n\n128 bits of CSPRNG entropy. Predicting the next filename is no longer feasible.\n\nA bigger follow-up — per-request `mkdtemp` subdirectories with `O_EXCL`/`O_NOFOLLOW` semantics — is deferred to a later release; the immediate window (predictable 32-bit collisions across processes) is what the patch closes.\n\n## Resources\n\n- Patched in: https://github.com/ether/etherpad/pull/7784 (squash commit `8c6104c`).\n- `Math.random()` is not appropriate for security-sensitive contexts. See the MDN guidance for [`Math.random()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) and the Node.js recommendation to use [`crypto.randomBytes`](https://nodejs.org/api/crypto.html#cryptorandombytessize-callback) for unpredictable values.\n\n## Credits\n\nReported during an internal security audit by Claude (via @JohnMcLear).",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:C/C:L/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "ep_etherpad-lite"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "3.1.0"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 3.0.0"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/ether/etherpad/security/advisories/GHSA-2jwf-f4xq-f24h"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/ether/etherpad/pull/7784"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/ether/etherpad/commit/8c6104c5d5daf41f0d454acc04d42dffa0e0d996"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/ether/etherpad"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-377", "CWE-59" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-08-13T14:11:07Z",
    "nvd_published_at" : null
  }
}