CVE-2026-113652
ApostropheCMS uploadfs Azure backend leaks account-level SAS token into every public attachment URL (sas: true)
ApostropheCMS is an open-source Node.js content management system. From 1.25.0 until 1.28.0, uploadfs Azure backend leaks account-level SAS token into every public attachment URL. When uploadfs is configured with the Azure backend and sas: true, createContainer() builds the BlobServiceClient from a URL that embeds the full SAS query string, and Azure SDK v12 derived clients inherit that query string. This issue is fixed in version 1.28.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- Low
- Integrity
- Low
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
5.6
ApostropheCMS uploadfs Azure backend leaks account-level SAS token into every public attachment URL (sas: true)
ApostropheCMS is an open-source Node.js content management system. From 1.25.0 until 1.28.0, uploadfs Azure backend leaks account-level SAS token into every public attachment URL. When uploadfs is configured with the Azure backend and sas: true, createContainer() builds the BlobServiceClient from a URL that embeds the full SAS query string, and Azure SDK v12 derived clients inherit that query string. This issue is fixed in version 1.28.0.
- Record
CVE-2026-113652- CVSS 3.1
- 5.6 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L - Weakness
- CWE-200, CWE-522
- Updated
- 2026-11-02
EUVDEUVD-2026-105180
5.6
ApostropheCMS is an open-source Node.js content management system. From 1.25.0 until 1.28.0, uploadfs Azure backend leaks account-level SAS token into every public attachment URL. When uploadfs is configured with the Azure backend and sas: true, createContainer() builds the BlobServiceClient from a URL that embeds the full SAS query string, and Azure SDK v12 derived clients inherit that query string. This issue is fixed in version 1.28.0.
- Record
EUVD-2026-105180- CVSS 3.1
- 5.6 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-hhph-8536-gfq5
5.6
ApostropheCMS uploadfs Azure backend leaks account-level SAS token into every public attachment URL (sas: true)
Summary
When uploadfs is configured with the Azure backend and sas: true (a supported configuration introduced in uploadfs 1.25.0), createContainer() builds the BlobServiceClient from a URL that embeds the full SAS query string, and Azure SDK v12 derived clients inherit that query string. As a result, getUrl() returns a base URL carrying the complete account/container SAS signature (?sv=...&sig=...), and Apostrophe's attachment.url() prepends it to every public attachment link — broadcasting a container-level SAS credential (commonly sp=rwdlac, full read/write/delete/list) to every site visitor, including anonymous ones. Anyone who can see an attachment URL can read, overwrite, delete, and list every blob in the container for the SAS lifetime. A companion functional bug (blob path appended after the query string, producing broken URLs) shows this code path was never exercised end to end. Shared-key mode (default) and non-Azure backends are not affected.
Verified at commit 5a3746aaed49761e171c2cbfe793267c959829fd (uploadfs 1.27.0 in the apostrophe monorepo).
Details
Root cause: a credential-bearing client URL is reused as the public base URL. The chain:
- Credential-embedded client: azure.js#L123-L125 —
new BlobServiceClient("https://<account>.blob.core.windows.net?<SAS query string>")wherecluster.keyis the full SAS string includingsig - Leak point: azure.js#L513-L520 —
getUrl()takesgetContainerClient(...).getBlobClient('').urlasbaseUrl; SDK v12 derived clients inherit the SAS query string, sobaseUrlalready contains?sv=...&sig=... - Public exposure: attachment/index.js#L694 —
attachment.url()=uploadfs.getUrl() + pathfor every rendered attachment link; attachment/index.js#L1503 —getBrowserData()also shipsuploadsUrlto every logged-in editor session - Companion bug: the blob path is appended after the query string (
/container/?sv=...&sig=XXX/attachments/id-name), so these URLs are also broken — evidence the 1.25.0 code path was never tested
Attack surface: every public page rendering attachment URLs (images, download links), plus the uploadsUrl delivered to editor sessions. The leak is passive and unconditional — no action required from the victim beyond serving a page. The credential further propagates through HTML source, logs, Referer headers, and CDN/proxy access logs.
Suggested remediation:
- Strip the query string before building public URLs:
const u = new URL(blob.svc.getContainerClient(...).getBlobClient('').url); u.search = '';(or composeaccountUrl + '/' + containermanually), and append the blob path to the pathname, not after the query. - Document that SAS mode must use least privilege (read-only, ideally
sr=bper-blob), and prefer generating short-lived per-blob read-only signed URLs viagenerateBlobSASQueryParametersinstead of embedding a service-level SAS in permanent public URLs.
PoC
Full PoC posted as the first comment below (poc.js + README; offline, no Azure account needed). It replicates the library's exact two steps with the declared dependency @azure/storage-blob@12:
npm install @azure/storage-blob@12 && node poc.js- The script constructs
new BlobServiceClient("https://myaccount.blob.core.windows.net?sv=...&sig=FAKESIGNATURE")exactly likecreateContainer(), then evaluates the…
- Record
GHSA-hhph-8536-gfq5- CVSS 3.1
- 5.6 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L - Weakness
- CWE-200, CWE-522
- Updated
- 2026-11-02
NVD
5.6
ApostropheCMS is an open-source Node.js content management system. From 1.25.0 until 1.28.0, uploadfs Azure backend leaks account-level SAS token into every public attachment URL. When uploadfs is configured with the Azure backend and sas: true, createContainer() builds the BlobServiceClient from a URL that embeds the full SAS query string, and Azure SDK v12 derived clients inherit that query string. This issue is fixed in version 1.28.0.
- Record
CVE-2026-113652- CVSS 3.1
- 5.6 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L - Weakness
- CWE-200, CWE-522
- Updated
- 2026-11-02
OSV
5.6
ApostropheCMS uploadfs Azure backend leaks account-level SAS token into every public attachment URL (sas: true)
ApostropheCMS is an open-source Node.js content management system. From 1.25.0 until 1.28.0, uploadfs Azure backend leaks account-level SAS token into every public attachment URL. When uploadfs is configured with the Azure backend and sas: true, createContainer() builds the BlobServiceClient from a URL that embeds the full SAS query string, and Azure SDK v12 derived clients inherit that query string. This issue is fixed in version 1.28.0.
- Record
CVE-2026-113652- CVSS 3.1
- 5.6 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L - Weakness
- CWE-200, CWE-522
- Updated
- 2026-11-02
OSVGHSA-hhph-8536-gfq5
5.6
ApostropheCMS uploadfs Azure backend leaks account-level SAS token into every public attachment URL (sas: true)
Summary
When uploadfs is configured with the Azure backend and sas: true (a supported configuration introduced in uploadfs 1.25.0), createContainer() builds the BlobServiceClient from a URL that embeds the full SAS query string, and Azure SDK v12 derived clients inherit that query string. As a result, getUrl() returns a base URL carrying the complete account/container SAS signature (?sv=...&sig=...), and Apostrophe's attachment.url() prepends it to every public attachment link — broadcasting a container-level SAS credential (commonly sp=rwdlac, full read/write/delete/list) to every site visitor, including anonymous ones. Anyone who can see an attachment URL can read, overwrite, delete, and list every blob in the container for the SAS lifetime. A companion functional bug (blob path appended after the query string, producing broken URLs) shows this code path was never exercised end to end. Shared-key mode (default) and non-Azure backends are not affected.
Verified at commit 5a3746aaed49761e171c2cbfe793267c959829fd (uploadfs 1.27.0 in the apostrophe monorepo).
Details
Root cause: a credential-bearing client URL is reused as the public base URL. The chain:
- Credential-embedded client: azure.js#L123-L125 —
new BlobServiceClient("https://<account>.blob.core.windows.net?<SAS query string>")wherecluster.keyis the full SAS string includingsig - Leak point: azure.js#L513-L520 —
getUrl()takesgetContainerClient(...).getBlobClient('').urlasbaseUrl; SDK v12 derived clients inherit the SAS query string, sobaseUrlalready contains?sv=...&sig=... - Public exposure: attachment/index.js#L694 —
attachment.url()=uploadfs.getUrl() + pathfor every rendered attachment link; attachment/index.js#L1503 —getBrowserData()also shipsuploadsUrlto every logged-in editor session - Companion bug: the blob path is appended after the query string (
/container/?sv=...&sig=XXX/attachments/id-name), so these URLs are also broken — evidence the 1.25.0 code path was never tested
Attack surface: every public page rendering attachment URLs (images, download links), plus the uploadsUrl delivered to editor sessions. The leak is passive and unconditional — no action required from the victim beyond serving a page. The credential further propagates through HTML source, logs, Referer headers, and CDN/proxy access logs.
Suggested remediation:
- Strip the query string before building public URLs:
const u = new URL(blob.svc.getContainerClient(...).getBlobClient('').url); u.search = '';(or composeaccountUrl + '/' + containermanually), and append the blob path to the pathname, not after the query. - Document that SAS mode must use least privilege (read-only, ideally
sr=bper-blob), and prefer generating short-lived per-blob read-only signed URLs viagenerateBlobSASQueryParametersinstead of embedding a service-level SAS in permanent public URLs.
PoC
Full PoC posted as the first comment below (poc.js + README; offline, no Azure account needed). It replicates the library's exact two steps with the declared dependency @azure/storage-blob@12:
npm install @azure/storage-blob@12 && node poc.js- The script constructs
new BlobServiceClient("https://myaccount.blob.core.windows.net?sv=...&sig=FAKESIGNATURE")exactly likecreateContainer(), then evaluates the…
- Record
GHSA-hhph-8536-gfq5- CVSS 3.1
- 5.6 Medium
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L - Weakness
- CWE-200, CWE-522
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
uploadfsnpm |
1.25.0 up to 1.28.0 | 1.28.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-113652 is a medium vulnerability (CVSS 5.6) in Apostrophecms Apostrophe, published October 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.28%, the 20th percentile. Fixed in uploadfs 1.28.0.
References
github.com/apostrophecms/apostrophe/security/advisories/GHSA-hhph-8536-gfq5
github.com/apostrophecms/apostrophe/commit/9500b8b0e1d630d5e07fbb5fd2c25ed0943845ce
github.com/apostrophecms/apostrophe/commit/f84fd41cbf6c22eb8d0008fc9c25df1f7130b350
github.com/apostrophecms/apostrophe/releases/tag/[email protected]
github.com/advisories/GHSA-hhph-8536-gfq5
github.com/CVEProject/cvelistV5/tree/main/cves/2026/113xxx/CVE-2026-113652.json
nvd.nist.gov/vuln/detail/CVE-2026-113652
github.com/apostrophecms/apostrophe
Left out here: the rest of 2 source descriptions. The JSON record has them.