Every CVE Search/ Get API key

CVE-2026-59992

Tina: Broken Access Control: arbitrary bucket-key write/delete in `next-tinacms-s3` (and sibling production media adapters)

5.4 MediumFix available

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

0.38%Up 0.1 points since Aug 20, 2026
90%50%10%1%

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 MediumCVSS: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 MediumCVSS: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 presigned PutObjectCommand URL with attacker-chosen Key.
    • packages/next-tinacms-s3/src/handlers.ts:199-223 — DELETE reads [, objectKey] = media and issues DeleteObjectCommand against attacker-chosen Key.
    • packages/next-tinacms-dos/src/handlers.ts:79-152 and :249-278 — same write/delete pattern, plus a server-side upload that builds the key with path.join(mediaRoot, prefix + filename) over attacker-controlled directory and filename.
    • packages/next-tinacms-azure/src/handlers.ts:44-95 — uploadMedia writes path.join(directory, filename) with both fields attacker-controlled (no mediaRoot configured at all); deleteAsset deletes any blob in the container.
    • packages/next-tinacms-cloudinary/src/handlers.ts:193-204 — cloudinary.uploader.destroy(public_id) over attacker-chosen public_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 mediaRoot boundary 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 MediumCVSS: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 MediumCVSS: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 MediumCVSS: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 presigned PutObjectCommand URL with attacker-chosen Key.
    • packages/next-tinacms-s3/src/handlers.ts:199-223 — DELETE reads [, objectKey] = media and issues DeleteObjectCommand against attacker-chosen Key.
    • packages/next-tinacms-dos/src/handlers.ts:79-152 and :249-278 — same write/delete pattern, plus a server-side upload that builds the key with path.join(mediaRoot, prefix + filename) over attacker-controlled directory and filename.
    • packages/next-tinacms-azure/src/handlers.ts:44-95 — uploadMedia writes path.join(directory, filename) with both fields attacker-controlled (no mediaRoot configured at all); deleteAsset deletes any blob in the container.
    • packages/next-tinacms-cloudinary/src/handlers.ts:193-204 — cloudinary.uploader.destroy(public_id) over attacker-chosen public_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 mediaRoot boundary 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 MediumCVSS: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

Affected packages
PackageAffectedFixed
next-tinacms-s3
npm
before 23.0.4
23.0.4
next-tinacms-dos
npm
before 23.0.4
23.0.4
next-tinacms-azure
npm
before 14.0.4
14.0.4
next-tinacms-cloudinary
npm
before 26.0.4
26.0.4

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-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

This page doesn't list everything

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

MoveEnter Open