CVE-2026-59992
Tina: Broken Access Control: arbitrary bucket-key write/delete in `next-tinacms-s3` (and sibling production media adapters)
Tina is a headless content management system. Prior to next-tinacms-s3 23.0.4, next-tinacms-dos 23.0.4, next-tinacms-azure 14.0.4, and next-tinacms-cloudinary 26.0.4, the first-party production media adapters pass attacker-controlled object keys to storage SDK upload and delete operations without enforcing the operator's configured mediaRoot. In packages/next-tinacms-s3/src/handlers.ts, createMediaHandler accepts req.query.key for a signed PutObject URL and the DELETE path uses req.query.media as the DeleteObjectCommand key. The same missing key-boundary check exists in packages/next-tinacms-dos/src/handlers.ts, packages/next-tinacms-azure/src/handlers.ts, and packages/next-tinacms-cloudinary/src/handlers.ts. An authenticated CMS editor can therefore create or delete objects anywhere the deployment's storage credential can reach, including other tenants' or non-media objects. These issues are fixed in next-tinacms-s3 23.0.4, next-tinacms-dos 23.0.4, next-tinacms-azure 14.0.4, and next-tinacms-cloudinary 26.0.4.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- Low
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
5.4
Tina: Broken Access Control: arbitrary bucket-key write/delete in `next-tinacms-s3` (and sibling production media adapters)
Tina is a headless content management system. Prior to next-tinacms-s3 23.0.4, next-tinacms-dos 23.0.4, next-tinacms-azure 14.0.4, and next-tinacms-cloudinary 26.0.4, the first-party production media adapters pass attacker-controlled object keys to storage SDK upload and delete operations without enforcing the operator's configured mediaRoot. In packages/next-tinacms-s3/src/handlers.ts, createMediaHandler accepts req.query.key for a signed PutObject URL and the DELETE path uses req.query.media as the DeleteObjectCommand key. The same missing key-boundary check exists in packages/next-tinacms-dos/src/handlers.ts, packages/next-tinacms-azure/src/handlers.ts, and packages/next-tinacms-cloudinary/src/handlers.ts. An authenticated CMS editor can therefore create or delete objects anywhere the deployment's storage credential can reach, including other tenants' or non-media objects. These issues are fixed in next-tinacms-s3 23.0.4, next-tinacms-dos 23.0.4, next-tinacms-azure 14.0.4, and next-tinacms-cloudinary 26.0.4.
- Record
CVE-2026-59992- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Weakness
- CWE-639, CWE-862
- Updated
- 2026-11-02
EUVDEUVD-2026-63102
5.4
Tina is a headless content management system. Prior to next-tinacms-s3 23.0.4, next-tinacms-dos 23.0.4, next-tinacms-azure 14.0.4, and next-tinacms-cloudinary 26.0.4, the first-party production media adapters pass attacker-controlled object keys to storage SDK upload and delete operations without enforcing the operator's configured mediaRoot. In packages/next-tinacms-s3/src/handlers.ts, createMediaHandler accepts req.query.key for a signed PutObject URL and the DELETE path uses req.query.media as the DeleteObjectCommand key. The same missing key-boundary check exists in packages/next-tinacms-dos/src/handlers.ts, packages/next-tinacms-azure/src/handlers.ts, and packages/next-tinacms-cloudinary/src/handlers.ts. An authenticated CMS editor can therefore create or delete objects anywhere the deployment's storage credential can reach, including other tenants' or non-media objects. These issues are fixed in next-tinacms-s3 23.0.4, next-tinacms-dos 23.0.4, next-tinacms-azure 14.0.4, and next-tinacms-cloudinary 26.0.4.
- Record
EUVD-2026-63102- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-8mq9-5fw2-5rm4
5.4
Tina: Broken Access Control: arbitrary bucket-key write/delete in `next-tinacms-s3` (and sibling production media adapters)
Summary
The production media handler shipped by next-tinacms-s3 (createMediaHandler in packages/next-tinacms-s3/src/handlers.ts) accepts an attacker-chosen ?key= query parameter and returns an AWS-signed PutObject URL whose Key is that value, with no check that the key falls under the operator's configured mediaRoot. The same handler's DELETE branch reads objectKey = (req.query.media as string[])[1] and dispatches a DeleteObjectCommand for that exact key, again unbounded by mediaRoot. Any caller that passes the operator-supplied authorized() predicate — i.e. any logged-in CMS editor in a typical TinaCloud / self-hosted deployment — therefore has write and delete authority over the entire S3 bucket the IAM key can reach, even though the package documents mediaRoot as the place where editors are scoped. The same shape is present in next-tinacms-dos, next-tinacms-azure, and next-tinacms-cloudinary, so a single design mistake spans every first-party production media backend.
- Project: TinaCMS — first-party production media adapters (consumed by self-hosted Next.js sites and TinaCloud-backed deployments).
- Source reviewed:
tinacms/tinacms@main(b56dad4). - Deployed artefact validated:
[email protected]handler logic, exercised against@aws-sdk/[email protected]via[email protected](the AWS SDK signs the URL identically whether the bucket is real or mocked). - Affected file(s):
packages/next-tinacms-s3/src/handlers.ts:67-90—GET ?key=returns presignedPutObjectCommandURL with attacker-chosenKey.packages/next-tinacms-s3/src/handlers.ts:199-223—DELETEreads[, objectKey] = mediaand issuesDeleteObjectCommandagainst attacker-chosenKey.packages/next-tinacms-dos/src/handlers.ts:79-152and:249-278— same write/delete pattern, plus a server-side upload that builds the key withpath.join(mediaRoot, prefix + filename)over attacker-controlleddirectoryandfilename.packages/next-tinacms-azure/src/handlers.ts:44-95—uploadMediawritespath.join(directory, filename)with both fields attacker-controlled (nomediaRootconfigured at all);deleteAssetdeletes any blob in the container.packages/next-tinacms-cloudinary/src/handlers.ts:193-204—cloudinary.uploader.destroy(public_id)over attacker-chosenpublic_id.
- CWE: CWE-639 — Authorization Bypass Through User-Controlled Key. Adjacent: CWE-284 (Improper Access Control), CWE-862 (Missing Authorization on the per-key authority check).
- OWASP 2021: A01:2021 — Broken Access Control (the operator's intended
mediaRootboundary is enforced only on listing, not on writes or deletes). Secondary: A04:2021 — Insecure Design (every adapter independently re-implements the same broken pattern).
Vulnerable code
packages/next-tinacms-s3/src/handlers.ts:39-98:
export const createMediaHandler = (config: S3Config, options?: S3Options) => {
const client = new S3Client(config.config);
const bucket = config.bucket;
let mediaRoot = config.mediaRoot || ''; // (1)
if (mediaRoot) { /* normalise to "media/" form */ }
return async (req: NextApiRequest, res: NextApiResponse) => {
const isAuthorized = await config.authorized(req, res);
if (!isAuthorized) {
res.status(401).json({ message: 'sorry this user is unauthorized' });
return;
}
switch (req.method) {
case 'GET':
if (req.query.key) {
const expiresIn: number =
(req.query.expiresIn && Number(req.query.expiresIn)) || 3600; // (2)
const s3_key = req.query.key
? Array.isArray(req.query.key) ? req.query.key[0] : req.query.key
: null;
if (!s3_key) return res.status(400).json({ message: 'key is required' });
if (await keyExists(client, bucket, s3_key)) {
return res.status(400).json({ message: 'key already exists' }); // (3)
…
- Record
GHSA-8mq9-5fw2-5rm4- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Weakness
- CWE-639, CWE-862
- Updated
- 2026-11-02
NVD
5.4
Tina is a headless content management system. Prior to next-tinacms-s3 23.0.4, next-tinacms-dos 23.0.4, next-tinacms-azure 14.0.4, and next-tinacms-cloudinary 26.0.4, the first-party production media adapters pass attacker-controlled object keys to storage SDK upload and delete operations without enforcing the operator's configured mediaRoot. In packages/next-tinacms-s3/src/handlers.ts, createMediaHandler accepts req.query.key for a signed PutObject URL and the DELETE path uses req.query.media as the DeleteObjectCommand key. The same missing key-boundary check exists in packages/next-tinacms-dos/src/handlers.ts, packages/next-tinacms-azure/src/handlers.ts, and packages/next-tinacms-cloudinary/src/handlers.ts. An authenticated CMS editor can therefore create or delete objects anywhere the deployment's storage credential can reach, including other tenants' or non-media objects. These issues are fixed in next-tinacms-s3 23.0.4, next-tinacms-dos 23.0.4, next-tinacms-azure 14.0.4, and next-tinacms-cloudinary 26.0.4.
- Record
CVE-2026-59992- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Weakness
- CWE-639, CWE-862
- Updated
- 2026-11-02
OSV
5.4
Tina: Broken Access Control: arbitrary bucket-key write/delete in `next-tinacms-s3` (and sibling production media adapters)
Tina is a headless content management system. Prior to next-tinacms-s3 23.0.4, next-tinacms-dos 23.0.4, next-tinacms-azure 14.0.4, and next-tinacms-cloudinary 26.0.4, the first-party production media adapters pass attacker-controlled object keys to storage SDK upload and delete operations without enforcing the operator's configured mediaRoot. In packages/next-tinacms-s3/src/handlers.ts, createMediaHandler accepts req.query.key for a signed PutObject URL and the DELETE path uses req.query.media as the DeleteObjectCommand key. The same missing key-boundary check exists in packages/next-tinacms-dos/src/handlers.ts, packages/next-tinacms-azure/src/handlers.ts, and packages/next-tinacms-cloudinary/src/handlers.ts. An authenticated CMS editor can therefore create or delete objects anywhere the deployment's storage credential can reach, including other tenants' or non-media objects. These issues are fixed in next-tinacms-s3 23.0.4, next-tinacms-dos 23.0.4, next-tinacms-azure 14.0.4, and next-tinacms-cloudinary 26.0.4.
- Record
CVE-2026-59992- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Weakness
- CWE-639, CWE-862
- Updated
- 2026-11-02
OSVGHSA-8mq9-5fw2-5rm4
5.4
Tina: Broken Access Control: arbitrary bucket-key write/delete in `next-tinacms-s3` (and sibling production media adapters)
Summary
The production media handler shipped by next-tinacms-s3 (createMediaHandler in packages/next-tinacms-s3/src/handlers.ts) accepts an attacker-chosen ?key= query parameter and returns an AWS-signed PutObject URL whose Key is that value, with no check that the key falls under the operator's configured mediaRoot. The same handler's DELETE branch reads objectKey = (req.query.media as string[])[1] and dispatches a DeleteObjectCommand for that exact key, again unbounded by mediaRoot. Any caller that passes the operator-supplied authorized() predicate — i.e. any logged-in CMS editor in a typical TinaCloud / self-hosted deployment — therefore has write and delete authority over the entire S3 bucket the IAM key can reach, even though the package documents mediaRoot as the place where editors are scoped. The same shape is present in next-tinacms-dos, next-tinacms-azure, and next-tinacms-cloudinary, so a single design mistake spans every first-party production media backend.
- Project: TinaCMS — first-party production media adapters (consumed by self-hosted Next.js sites and TinaCloud-backed deployments).
- Source reviewed:
tinacms/tinacms@main(b56dad4). - Deployed artefact validated:
[email protected]handler logic, exercised against@aws-sdk/[email protected]via[email protected](the AWS SDK signs the URL identically whether the bucket is real or mocked). - Affected file(s):
packages/next-tinacms-s3/src/handlers.ts:67-90—GET ?key=returns presignedPutObjectCommandURL with attacker-chosenKey.packages/next-tinacms-s3/src/handlers.ts:199-223—DELETEreads[, objectKey] = mediaand issuesDeleteObjectCommandagainst attacker-chosenKey.packages/next-tinacms-dos/src/handlers.ts:79-152and:249-278— same write/delete pattern, plus a server-side upload that builds the key withpath.join(mediaRoot, prefix + filename)over attacker-controlleddirectoryandfilename.packages/next-tinacms-azure/src/handlers.ts:44-95—uploadMediawritespath.join(directory, filename)with both fields attacker-controlled (nomediaRootconfigured at all);deleteAssetdeletes any blob in the container.packages/next-tinacms-cloudinary/src/handlers.ts:193-204—cloudinary.uploader.destroy(public_id)over attacker-chosenpublic_id.
- CWE: CWE-639 — Authorization Bypass Through User-Controlled Key. Adjacent: CWE-284 (Improper Access Control), CWE-862 (Missing Authorization on the per-key authority check).
- OWASP 2021: A01:2021 — Broken Access Control (the operator's intended
mediaRootboundary is enforced only on listing, not on writes or deletes). Secondary: A04:2021 — Insecure Design (every adapter independently re-implements the same broken pattern).
Vulnerable code
packages/next-tinacms-s3/src/handlers.ts:39-98:
export const createMediaHandler = (config: S3Config, options?: S3Options) => {
const client = new S3Client(config.config);
const bucket = config.bucket;
let mediaRoot = config.mediaRoot || ''; // (1)
if (mediaRoot) { /* normalise to "media/" form */ }
return async (req: NextApiRequest, res: NextApiResponse) => {
const isAuthorized = await config.authorized(req, res);
if (!isAuthorized) {
res.status(401).json({ message: 'sorry this user is unauthorized' });
return;
}
switch (req.method) {
case 'GET':
if (req.query.key) {
const expiresIn: number =
(req.query.expiresIn && Number(req.query.expiresIn)) || 3600; // (2)
const s3_key = req.query.key
? Array.isArray(req.query.key) ? req.query.key[0] : req.query.key
: null;
if (!s3_key) return res.status(400).json({ message: 'key is required' });
if (await keyExists(client, bucket, s3_key)) {
return res.status(400).json({ message: 'key already exists' }); // (3)
…
- Record
GHSA-8mq9-5fw2-5rm4- CVSS 3.1
- 5.4 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L - Weakness
- CWE-639, CWE-862
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
next-tinacms-s3npm |
before 23.0.4 | 23.0.4 |
next-tinacms-dosnpm |
before 23.0.4 | 23.0.4 |
next-tinacms-azurenpm |
before 14.0.4 | 14.0.4 |
next-tinacms-cloudinarynpm |
before 26.0.4 | 26.0.4 |
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-59992 is a medium vulnerability (CVSS 5.4) in Tinacms Next-tinacms-azure, published August 19, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.38%, the 32nd percentile. Fixed in next-tinacms-s3 23.0.4.
References
github.com/tinacms/tinacms/security/advisories/GHSA-8mq9-5fw2-5rm4
github.com/tinacms/tinacms/pull/7088
github.com/tinacms/tinacms/commit/d44558e9b4502d4f4fc2c970d22985339fe2b6ce
github.com/tinacms/tinacms/releases/tag/[email protected]
nvd.nist.gov/vuln/detail/CVE-2026-59992
github.com/advisories/GHSA-8mq9-5fw2-5rm4
github.com/CVEProject/cvelistV5/tree/main/cves/2026/59xxx/CVE-2026-59992.json
github.com/tinacms/tinacms
Left out here: the rest of 2 source descriptions. The JSON record has them.