Every CVE Search/ Get API key

CVE-2026-112953

ApostropheCMS: Exposure of Sensitive Information via predictable export filenames

6.0 MediumFix available

ApostropheCMS is an open-source Node.js content management system. Prior to 3.7.1 of the import-export module, predictable export archive filenames allow an unauthenticated requester to download another user's export from publicly readable export storage before the archive expires or is removed. A user with export permission must first create the archive, and sites with exports disabled, private upload storage, or authenticated upload delivery are not exposed through this network-download path. This issue is fixed in version 3.7.1.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
Passive

What an attacker can affect

Confidentiality
High
Integrity
None
Availability
None

Exploit likelihood

0.56%Unchanged since Oct 21, 2026
90%50%10%1%

What each source reports

CVE List Score used 6.0

ApostropheCMS: Exposure of Sensitive Information via predictable export filenames

ApostropheCMS is an open-source Node.js content management system. Prior to 3.7.1 of the import-export module, predictable export archive filenames allow an unauthenticated requester to download another user's export from publicly readable export storage before the archive expires or is removed. A user with export permission must first create the archive, and sites with exports disabled, private upload storage, or authenticated upload delivery are not exposed through this network-download path. This issue is fixed in version 3.7.1.

Record
CVE-2026-112953
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-200
Updated
2026-11-02
EUVDEUVD-2026-105103 6.0

ApostropheCMS is an open-source Node.js content management system. Prior to 3.7.1 of the import-export module, predictable export archive filenames allow an unauthenticated requester to download another user's export from publicly readable export storage before the archive expires or is removed. A user with export permission must first create the archive, and sites with exports disabled, private upload storage, or authenticated upload delivery are not exposed through this network-download path. This issue is fixed in version 3.7.1.

Record
EUVD-2026-105103
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-5gc8-frw3-3vqm 6.0

ApostropheCMS: Exposure of Sensitive Information via predictable export filenames

Summary

When a user exports content through @apostrophecms/import-export, the module copies the generated archive into uploadfs at /exports/ and returns a public URL under /uploads/exports/. The filename is fully deterministic: site shortName, document type, a YYYYMMDDHHmmss timestamp, and the format extension. There is no secret token.

Anyone who can guess that path can download the archive with a normal unauthenticated HTTP GET while the file is still present (10 minutes by default, longer if the site raises importExport.export.expiration). That is a different model from attachment URLs, which rely on hard-to-guess IDs.

Sites that disable export, store uploads on a private backend with no anonymous read, or serve /uploads only behind authentication are not hit by the network download step—but the predictable naming is still a design flaw for the default local/public uploadfs layout.

Import-only workflows and imports themselves are out of scope; this is specific to the export download URL.

Affected configurations

All of the following must apply:

  1. The @apostrophecms/import-export module is enabled and export is allowed for at least one page or piece type, and
  2. Export files are served the usual way: copyIn to /exports/… and delivery via the public uploads URL (default local uploadfs or any deployment that exposes the same path without per-file auth).

If export is turned off per type or globally, or /uploads is not world-readable, an anonymous attacker cannot pull the file over HTTP—but a co-resident attacker with filesystem or bucket listing access would still see guessable names.

Remediation

  • Put a high-entropy component in the export filename (e.g. apos.util.generateId()), or
  • Stop publishing exports on a world-readable URL—serve downloads through an authenticated route and keep objects in a private prefix until then, or
  • Document that importExport.export.expiration must stay short and treat /uploads/exports/ as sensitive (weak on its own).

Details

Technical details

exportFile() in packages/import-export/lib/methods/export.js builds the name and publishes it:

const date = dayjs().format('YYYYMMDDHHmmss');
const filename = `${self.apos.shortName}-${req.body.type.toLowerCase()}-export-${date}${format.extension}`;
const downloadPath = path.join('/exports', filename);
await copyIn(filepath, downloadPath);

The admin UI sends type as the module name (e.g. article), which matches the filename. The download URL is only shown in the exporter’s notification; nothing binds the HTTP request to that user afterward. After expiration (default 1000 * 60 * 10 ms) a timer deletes the object from uploadfs, which limits exposure but leaves a brute-forceable window.

Proof of concept

The script boots a minimal Apostrophe app with import-export, performs an export as the task user, then downloads the same archive without a session—first by path guessing, then with raw fetch. It exits non-zero on failure.

Requirements: Node 18+, pnpm install at the monorepo root (so apostrophe and @apostrophecms/import-export resolve). Set APOSTROPHE_ROOT to the checkout path.

#!/usr/bin/env bash
set -euo pipefail

# --- config ---
APOSTROPHE_ROOT="${APOSTROPHE_ROOT:-}"
if [[ -z "${APOSTROPHE_ROOT}" || ! -f "${APOSTROPHE_ROOT}/packages/import-export/package.json" ]]; then
  echo "Set APOSTROPHE_ROOT to your apostrophe monorepo checkout." >&2
  exit 1
fi

export APOS_TEST_DB_PROTOCOL=sqlite
cd "${APOSTROPHE_ROOT}/packages/import-export"

# --- run embedded PoC (no extra repo files) ---
node <<'NODE'
const assert = require('node:assert/strict');
const path = require('node:path');
const dayjs = require('dayjs');
const t = require('apostrophe/test-lib/util.js');

(async () => {
  let apos;
  const shortName = 'poc-export-site';
  const exportsPath = (root) => path.join(root, 'public/uploads/exports');

  try {
    apos…
Record
GHSA-5gc8-frw3-3vqm
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-200
Updated
2026-11-02
NVD 6.0

ApostropheCMS is an open-source Node.js content management system. Prior to 3.7.1 of the import-export module, predictable export archive filenames allow an unauthenticated requester to download another user's export from publicly readable export storage before the archive expires or is removed. A user with export permission must first create the archive, and sites with exports disabled, private upload storage, or authenticated upload delivery are not exposed through this network-download path. This issue is fixed in version 3.7.1.

Record
CVE-2026-112953
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Weakness
CWE-200
Updated
2026-11-02
OSV 6.0

ApostropheCMS: Exposure of Sensitive Information via predictable export filenames

ApostropheCMS is an open-source Node.js content management system. Prior to 3.7.1 of the import-export module, predictable export archive filenames allow an unauthenticated requester to download another user's export from publicly readable export storage before the archive expires or is removed. A user with export permission must first create the archive, and sites with exports disabled, private upload storage, or authenticated upload delivery are not exposed through this network-download path. This issue is fixed in version 3.7.1.

Record
CVE-2026-112953
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-200
Updated
2026-11-02
OSVGHSA-5gc8-frw3-3vqm 6.0

ApostropheCMS: Exposure of Sensitive Information via predictable export filenames

Summary

When a user exports content through @apostrophecms/import-export, the module copies the generated archive into uploadfs at /exports/ and returns a public URL under /uploads/exports/. The filename is fully deterministic: site shortName, document type, a YYYYMMDDHHmmss timestamp, and the format extension. There is no secret token.

Anyone who can guess that path can download the archive with a normal unauthenticated HTTP GET while the file is still present (10 minutes by default, longer if the site raises importExport.export.expiration). That is a different model from attachment URLs, which rely on hard-to-guess IDs.

Sites that disable export, store uploads on a private backend with no anonymous read, or serve /uploads only behind authentication are not hit by the network download step—but the predictable naming is still a design flaw for the default local/public uploadfs layout.

Import-only workflows and imports themselves are out of scope; this is specific to the export download URL.

Affected configurations

All of the following must apply:

  1. The @apostrophecms/import-export module is enabled and export is allowed for at least one page or piece type, and
  2. Export files are served the usual way: copyIn to /exports/… and delivery via the public uploads URL (default local uploadfs or any deployment that exposes the same path without per-file auth).

If export is turned off per type or globally, or /uploads is not world-readable, an anonymous attacker cannot pull the file over HTTP—but a co-resident attacker with filesystem or bucket listing access would still see guessable names.

Remediation

  • Put a high-entropy component in the export filename (e.g. apos.util.generateId()), or
  • Stop publishing exports on a world-readable URL—serve downloads through an authenticated route and keep objects in a private prefix until then, or
  • Document that importExport.export.expiration must stay short and treat /uploads/exports/ as sensitive (weak on its own).

Details

Technical details

exportFile() in packages/import-export/lib/methods/export.js builds the name and publishes it:

const date = dayjs().format('YYYYMMDDHHmmss');
const filename = `${self.apos.shortName}-${req.body.type.toLowerCase()}-export-${date}${format.extension}`;
const downloadPath = path.join('/exports', filename);
await copyIn(filepath, downloadPath);

The admin UI sends type as the module name (e.g. article), which matches the filename. The download URL is only shown in the exporter’s notification; nothing binds the HTTP request to that user afterward. After expiration (default 1000 * 60 * 10 ms) a timer deletes the object from uploadfs, which limits exposure but leaves a brute-forceable window.

Proof of concept

The script boots a minimal Apostrophe app with import-export, performs an export as the task user, then downloads the same archive without a session—first by path guessing, then with raw fetch. It exits non-zero on failure.

Requirements: Node 18+, pnpm install at the monorepo root (so apostrophe and @apostrophecms/import-export resolve). Set APOSTROPHE_ROOT to the checkout path.

#!/usr/bin/env bash
set -euo pipefail

# --- config ---
APOSTROPHE_ROOT="${APOSTROPHE_ROOT:-}"
if [[ -z "${APOSTROPHE_ROOT}" || ! -f "${APOSTROPHE_ROOT}/packages/import-export/package.json" ]]; then
  echo "Set APOSTROPHE_ROOT to your apostrophe monorepo checkout." >&2
  exit 1
fi

export APOS_TEST_DB_PROTOCOL=sqlite
cd "${APOSTROPHE_ROOT}/packages/import-export"

# --- run embedded PoC (no extra repo files) ---
node <<'NODE'
const assert = require('node:assert/strict');
const path = require('node:path');
const dayjs = require('dayjs');
const t = require('apostrophe/test-lib/util.js');

(async () => {
  let apos;
  const shortName = 'poc-export-site';
  const exportsPath = (root) => path.join(root, 'public/uploads/exports');

  try {
    apos…
Record
GHSA-5gc8-frw3-3vqm
CVSS 4.0
6.0 MediumCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Weakness
CWE-200
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
@apostrophecms/import-export
npm
before 3.7.1
3.7.1

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-112953 is a medium vulnerability (CVSS 6.0) in @apostrophecms Import-export, published October 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.56%, the 46th percentile. Fixed in @apostrophecms/import-export 3.7.1.

References

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open