Summary
ApostropheCMS uploadfs Azure backend leaks account-level SAS token into every public attachment URL (sas: true)
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 thegetUrl()logic (getContainerClient('uploads-container').getBlobClient('').url) and asserts the resulting base URL still contains thesigparameter. - Observed output (exit code 0):
baseUrl: https://myaccount.blob.core.windows.net/uploads-container/?sv=2021-06-08&ss=b&srt=co&sp=rwdlac&se=2099-01-01&sig=FAKESIGNATURE
attachment URL as served to visitors: https://myaccount.blob.core.windows.net/uploads-container/?sv=2021-06-08&ss=b&srt=co&sp=rwdlac&se=2099-01-01&sig=FAKESIGNATURE/attachments/abc123-photo.jpg
The produced URL both leaks the token (sv…sig) and demonstrates the broken path placement. Cross-checking attachment/index.js confirms this URL reaches public pages unmodified.
Impact
- What it is: insufficiently protected credentials (CWE-522) leading to information exposure (CWE-200). A container-level SAS token, commonly granted
sp=rwdlac(read/write/delete/list/ACL), is disclosed to every site visitor including anonymous users, valid for the SAS lifetime (often years). Holders can read, overwrite, delete, and enumerate every attachment in the container; the library applies no narrowing. - Who is impacted: Apostrophe sites using uploadfs with the Azure backend and
sas: true(introduced in 1.25.0; present through 1.27.0). Default shared-key mode and other backends are unaffected. When the SAS is provisioned read-only, the impact reduces to unauthenticated read/download of all attachments.
Reported by Kimi Security Team ([email protected]).
CVE-2026-113652 has a CVSS score of 5.6 (Medium). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (1.28.0); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-113652? CVE-2026-113652 is a medium-severity security vulnerability in uploadfs (npm), affecting versions >= 1.25.0, < 1.28.0. It is fixed in 1.28.0.
- How severe is CVE-2026-113652? CVE-2026-113652 has a CVSS score of 5.6 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of uploadfs are affected by CVE-2026-113652? uploadfs (npm) versions >= 1.25.0, < 1.28.0 is affected.
- Is there a fix for CVE-2026-113652? Yes. CVE-2026-113652 is fixed in 1.28.0. Upgrade to this version or later.
- Is CVE-2026-113652 exploitable, and should I be worried? Whether CVE-2026-113652 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-113652 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-113652? Upgrade
uploadfsto 1.28.0 or later.