From a21fb93014dca60012ca326f3b512ab04c8ac13e Mon Sep 17 00:00:00 2001 From: Patrik Kozak <35232443+PatrikKozak@users.noreply.github.com> Date: Wed, 29 Oct 2025 15:25:28 -0400 Subject: [PATCH 1/2] fix: prevent default values on globals when access control denies read --- .../src/fields/hooks/afterRead/promise.ts | 11 +++++-- test/versions/globals/DraftWithMax.ts | 2 +- test/versions/int.spec.ts | 31 +++++++++++++++++++ test/versions/payload-types.ts | 6 ++-- 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/packages/payload/src/fields/hooks/afterRead/promise.ts b/packages/payload/src/fields/hooks/afterRead/promise.ts index 0406c400e9d..2edcf24ca7f 100644 --- a/packages/payload/src/fields/hooks/afterRead/promise.ts +++ b/packages/payload/src/fields/hooks/afterRead/promise.ts @@ -373,12 +373,19 @@ export const promise = async ({ } // Set defaultValue on the field for globals being returned without being first created - // or collection documents created prior to having a default + // or collection documents created prior to having a default. + + // Skip setting defaults when: global has no ID (never created or filtered by access) + // AND access control is active (not overriding). This prevents default values like + // `_status: 'draft'` from appearing when access control filters out the document. + const shouldSkipDefaults = global && !doc.id && !overrideAccess + if ( !removedFieldValue && allowDefaultValue && typeof siblingDoc[field.name!] === 'undefined' && - typeof field.defaultValue !== 'undefined' + typeof field.defaultValue !== 'undefined' && + !shouldSkipDefaults ) { siblingDoc[field.name!] = await getDefaultValue({ defaultValue: field.defaultValue, diff --git a/test/versions/globals/DraftWithMax.ts b/test/versions/globals/DraftWithMax.ts index dab8ec339fb..2e7bf612ef6 100644 --- a/test/versions/globals/DraftWithMax.ts +++ b/test/versions/globals/DraftWithMax.ts @@ -4,7 +4,7 @@ import { draftWithMaxGlobalSlug } from '../slugs.js' const DraftWithMaxGlobal: GlobalConfig = { slug: draftWithMaxGlobalSlug, - label: 'Draft Global', + label: 'Draft with Max Global', admin: { components: { views: { diff --git a/test/versions/int.spec.ts b/test/versions/int.spec.ts index 34c86b67524..4e2f090e915 100644 --- a/test/versions/int.spec.ts +++ b/test/versions/int.spec.ts @@ -2554,6 +2554,37 @@ describe('Versions', () => { expect(retrieved.title).toStrictEqual('i will be a draft') }) + it('should not return _status field when access control denies read', async () => { + // Create a draft global + const draft = await payload.updateGlobal({ + slug: draftGlobalSlug, + data: { + _status: 'draft', + title: 'draft only', + }, + draft: true, + }) + + expect(draft._status).toStrictEqual('draft') + + // Read with a custom req that has no user (simulating unauthenticated request) + // Access control on draftGlobalSlug requires published status when no user + const result = await payload.findGlobal({ + slug: draftGlobalSlug, + overrideAccess: false, + req: { + ...payload.config, + user: null, + payload, + } as any, + }) + + // Should return empty object, not {_status: 'draft'} + // The _status field should not be populated with its default value + expect(Object.keys(result)).toHaveLength(0) + expect(result._status).toBeUndefined() + }) + describe('server functions', () => { let draftDoc let event diff --git a/test/versions/payload-types.ts b/test/versions/payload-types.ts index f492524df89..28b6b667424 100644 --- a/test/versions/payload-types.ts +++ b/test/versions/payload-types.ts @@ -209,7 +209,7 @@ export interface AutosavePost { root: { type: string; children: { - type: string; + type: any; version: number; [k: string]: unknown; }[]; @@ -514,7 +514,7 @@ export interface Diff { root: { type: string; children: { - type: string; + type: any; version: number; [k: string]: unknown; }[]; @@ -529,7 +529,7 @@ export interface Diff { root: { type: string; children: { - type: string; + type: any; version: number; [k: string]: unknown; }[]; From 2e441788d723a7d420f387dfc0e771a198a315db Mon Sep 17 00:00:00 2001 From: Patrik Kozak <35232443+PatrikKozak@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:19:18 -0400 Subject: [PATCH 2/2] chore: adjust test --- test/versions/int.spec.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/versions/int.spec.ts b/test/versions/int.spec.ts index 4e2f090e915..61858807e01 100644 --- a/test/versions/int.spec.ts +++ b/test/versions/int.spec.ts @@ -2567,16 +2567,15 @@ describe('Versions', () => { expect(draft._status).toStrictEqual('draft') - // Read with a custom req that has no user (simulating unauthenticated request) + // Create a request without a user (simulating unauthenticated request) // Access control on draftGlobalSlug requires published status when no user + const req = await createLocalReq({}, payload) + req.user = null + const result = await payload.findGlobal({ slug: draftGlobalSlug, overrideAccess: false, - req: { - ...payload.config, - user: null, - payload, - } as any, + req, }) // Should return empty object, not {_status: 'draft'}