fix(next): exclude permissions from page response when unauthenticated (#13796)

Similar spirit as #13714.

Permissions are embedded into the page response, exposing some field
names to unauthenticated users.

For example, when setting `read: () => false` on a field, that field's
name is now included in the response due to its presence in the
permissions object.

We now search the HTML source directly in the test, similar to "view
source" in the browser, which will be much effective at preventing
regression going forward.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211347942663256
This commit is contained in:
Jacob Fletcher
2025-09-12 16:57:03 -04:00
committed by GitHub
parent e2632c86d0
commit 8113d3bdef
3 changed files with 20 additions and 4 deletions

View File

@@ -122,7 +122,7 @@ export const RootLayout = async ({
languageCode={languageCode}
languageOptions={languageOptions}
locale={req.locale}
permissions={permissions}
permissions={req.user ? permissions : null}
serverFunction={serverFunction}
switchLanguageServerAction={switchLanguageServerAction}
theme={theme}

View File

@@ -190,8 +190,14 @@ export default buildConfigWithDefaults({
label: 'Auth Debug',
},
{
// This is a uniquely identifiable field that we use to ensure it doesn't appear in the page source when unauthenticated
// E.g. if the user is authenticated, it will appear in the both the client config
name: 'shouldNotShowInClientConfigUnlessAuthenticated',
type: 'text',
access: {
// Setting this forces the field to show up in the permissions object
read: () => true,
},
},
],
},

View File

@@ -182,13 +182,17 @@ describe('Auth', () => {
await saveDocAndAssert(page, '#action-save')
})
test('should protect the client config behind authentication', async () => {
test('should protect field schemas behind authentication', async () => {
await logout(page, serverURL)
// This element is absolutely positioned and `opacity: 0`
// Inspect the page source (before authentication)
const loginPageRes = await page.goto(`${serverURL}/admin/login`)
const loginPageSource = await loginPageRes?.text()
expect(loginPageSource).not.toContain('shouldNotShowInClientConfigUnlessAuthenticated')
// Inspect the client config (before authentication)
await expect(page.locator('#unauthenticated-client-config')).toBeAttached()
// Search for our uniquely identifiable field name
await expect(
page.locator('#unauthenticated-client-config', {
hasText: 'shouldNotShowInClientConfigUnlessAuthenticated',
@@ -199,6 +203,7 @@ describe('Auth', () => {
await page.goto(serverURL + '/admin')
// Inspect the client config (after authentication)
await expect(page.locator('#authenticated-client-config')).toBeAttached()
await expect(
@@ -206,6 +211,11 @@ describe('Auth', () => {
hasText: 'shouldNotShowInClientConfigUnlessAuthenticated',
}),
).toHaveCount(1)
// Inspect the page source (after authentication)
const dashboardPageRes = await page.goto(`${serverURL}/admin`)
const dashboardPageSource = await dashboardPageRes?.text()
expect(dashboardPageSource).toContain('shouldNotShowInClientConfigUnlessAuthenticated')
})
test('should allow change password', async () => {