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](https://github.com/apostrophecms/apostrophe/blob/5a3746aaed49761e171c2cbfe793267c959829fd/packages/uploadfs/lib/storage/azure.js#L123-L125) — `new BlobServiceClient("https://.blob.core.windows.net?")` where `cluster.key` is the full SAS string including `s
- Products
- uploadfs
- Affected versions
- Not specified
- Fixed versions
- 1.28.0
- CVEs
- CVE-2026-113652
- Confirmed fixed CVEs
- Not specified
- Updated in this feed
- 31 Oct 2026, 13:14 UTC · Revision 1
Source evidence
ApostropheCMS uploadfs Azure backend leaks account-level SAS token into every public attachment URL (sas: true)
core-osv · Original sourcePublished 20 Oct 2026, 18:20 UTC
## 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](https://github.com/apostrophecms/apostrophe/blob/5a3746aaed49761e171c2cbfe793267c959829fd/packages/uploadfs/lib/storage/azure.js#L123-L125) — `new BlobServiceClient("https://.blob.core.windows.net?")` where `cluster.key` is the full SAS string including `s
osv:GHSA-hhph-8536-gfq5
uploadfs
Fixed versions: 1.28.0
Complete source record
{
"id": "2dec09e9fc6ea001d652cebca6c80ee13355e08d06c7372d004e7ebdea01ab74",
"url": "https://osv.dev/vulnerability/GHSA-hhph-8536-gfq5",
"method": "structured",
"payload": {
"kind": "advisory",
"title": "ApostropheCMS uploadfs Azure backend leaks account-level SAS token into every public attachment URL (sas: true)",
"summary": "## 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](https://github.com/apostrophecms/apostrophe/blob/5a3746aaed49761e171c2cbfe793267c959829fd/packages/uploadfs/lib/storage/azure.js#L123-L125) — `new BlobServiceClient(\"https://.blob.core.windows.net?\")` where `cluster.key` is the full SAS string including `s",
"evidence": "osv:GHSA-hhph-8536-gfq5",
"products": [
{
"name": {
"value": "uploadfs",
"evidence": "/affected/0/package/name"
},
"prerequisites": [],
"fixed_versions": [
{
"value": "1.28.0",
"evidence": "/affected/0/ranges"
}
],
"affected_versions": []
}
],
"severity": "medium",
"structured": {
"id": "GHSA-hhph-8536-gfq5",
"aliases": [
"CVE-2026-113652",
"GHSA-hhph-8536-gfq5"
],
"metrics": [
{
"type": "CVSS_V3",
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L"
}
],
"affected": [
{
"ranges": [
{
"type": "SEMVER",
"events": [
{
"introduced": "1.25.0"
},
{
"fixed": "1.28.0"
}
]
}
],
"package": {
"name": "uploadfs",
"purl": "pkg:npm/uploadfs",
"ecosystem": "npm"
},
"database_specific": {
"source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-hhph-8536-gfq5/GHSA-hhph-8536-gfq5.json"
}
}
],
"modified": "2026-10-20T18:30:03.590258002Z",
"provider": "osv",
"references": [
"https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-hhph-8536-gfq5",
"https://github.com/apostrophecms/apostrophe/commit/9500b8b0e1d630d5e07fbb5fd2c25ed0943845ce",
"https://github.com/apostrophecms/apostrophe/commit/f84fd41cbf6c22eb8d0008fc9c25df1f7130b350",
"https://github.com/apostrophecms/apostrophe",
"https://github.com/apostrophecms/apostrophe/releases/tag/uploadfs@1.28.0"
],
"provider_metadata": {
"cwe_ids": [
"CWE-200",
"CWE-522"
],
"severity": "MODERATE",
"github_reviewed": true,
"nvd_published_at": null,
"github_reviewed_at": "2026-10-20T18:20:35Z"
}
},
"related_cves": [
{
"value": "CVE-2026-113652",
"evidence": "/aliases"
}
],
"security_relevant": true,
"severity_evidence": "/severity/0/score"
},
"source_id": "core-osv",
"document_id": 390146,
"published_at": "2026-10-20T18:20:35+00:00"
}