Skip to content

Commit 39c71cc

Browse files
Validate a hash is legit when it links to its own page (#2183)
Co-authored-by: Alexis Aguilar <[email protected]>
1 parent dc4af18 commit 39c71cc

File tree

17 files changed

+89
-22
lines changed

17 files changed

+89
-22
lines changed

docs/account-portal/disable-account-portal.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ To disable the Account Portal:
1010

1111
1. In the Clerk Dashboard, navigate to the [**Account Portal**](https://dashboard.clerk.com/last-active?path=account-portal) page.
1212
1. Select the **Danger** tab.
13-
1. Select **Disable Account Portal**. You will not be able to select this button until you have [set up an authentication flow for your users](#customizing-your-sign-upsign-in-flow), as applying this setting will immediately result in a 404 for all Account Portal pages.
13+
1. Select **Disable Account Portal**. You will not be able to select this button until you have [set up an authentication flow for your users](#customize-your-sign-up-sign-in-flow), as applying this setting will immediately result in a 404 for all Account Portal pages.
1414

1515
## Customize your sign-up/sign-in flow
1616

docs/authentication/social-connections/linkedin-oidc.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ To make the setup process easier, it's recommended to keep two browser tabs open
4949
### Create a LinkedIn application
5050

5151
> [!TIP]
52-
> If you already have a LinkedIn app you'd like to connect to Clerk, select your app from the [**My apps**](https://www.linkedin.com/developers/apps?appStatus=active) page in the LinkedIn Developer Portal and proceed to the [next step](#get-your-client-id-and-client-secret) in this tutorial.
52+
> If you already have a LinkedIn app you'd like to connect to Clerk, select your app from the [**My apps**](https://www.linkedin.com/developers/apps?appStatus=active) page in the LinkedIn Developer Portal and proceed to the [next step](#set-the-client-id-and-primary-client-secret-in-the-clerk-dashboard) in this tutorial.
5353
5454
1. On the homepage of the [LinkedIn Developer Portal](https://developer.linkedin.com/), select **Create app**. You'll be redirected to the **Create an app** form.
5555
1. Fill out the necessary information. Then, select **Create app**. You'll be redirected to the app's **Products** page.

docs/backend-requests/resources/session-tokens.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ You can also create custom tokens using a [JWT template](/docs/backend-requests/
4747

4848
The Clerk session token is stored in a cookie. All modern browsers [limit the maximum size of a cookie to 4kb](https://datatracker.ietf.org/doc/html/rfc2109#section-6.3). Exceeding this limit can have adverse effects, including a possible infinite redirect loop for users who exceed this size in Next.js applications.
4949

50-
A session token with the [default session claims](#session-claims) won't run into this issue, as this configuration produces a cookie significantly smaller than 4kb. However, this limitation becomes relevant when implementing a [custom session token](/docs/backend-requests/custom-session-token). In this case, it's recommended to move particularly large claims out of the token and fetch these using a separate API call from your backend.
50+
A session token with the [default session claims](#default-claims) won't run into this issue, as this configuration produces a cookie significantly smaller than 4kb. However, this limitation becomes relevant when implementing a [custom session token](/docs/backend-requests/custom-session-token). In this case, it's recommended to move particularly large claims out of the token and fetch these using a separate API call from your backend.
5151

5252
Claims to monitor for size limits:
5353

docs/components/authentication/sign-up.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ The following example includes basic implementation of the `<SignUp />` componen
221221
---
222222

223223
- `props?`
224-
- [`SignUpProps`](#sign-up-props)
224+
- [`SignUpProps`](#properties)
225225

226226
The properties to pass to the `<SignUp />` component.
227227
</Properties>
@@ -299,7 +299,7 @@ The following example includes basic implementation of the `<SignUp />` componen
299299

300300
<Properties>
301301
- `props?`
302-
- [`SignUpProps`](#sign-up-props)
302+
- [`SignUpProps`](#properties)
303303

304304
The properties to pass to the `<SignUp />` component
305305
</Properties>

docs/custom-flows/email-links.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ This guide demonstrates how to use Clerk's API to build a custom flow for handli
2424

2525
- [Sign up](#sign-up-flow)
2626
- [Sign in](#sign-in-flow)
27-
- [Verify a new email address](#email-address-verification-flow)
27+
- [Verify a new email address](#add-new-email-flow)
2828

2929
<Steps>
3030
## Enable email link authentication

docs/deployments/changing-domains.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Learn how to change your Clerk production instance's domain or subdomain.
1616
1. Once you make the change to your domain, you will need to update the following:
1717
- Update DNS records
1818
- Generate new SSL certificates
19-
- [Update your Publishable Key](#updating-your-publishable-key)
19+
- [Update your Publishable Key](#update-your-publishable-key)
2020
- If using social connections, update the settings with your social connections so that the redirect URL they are using is correct.
2121
- If using JWT templates, update JWT issuer and JWKS Endpoint in external JWT SSO services.
2222

docs/references/backend/types/auth-object.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ The `Auth` object contains important information like the current user's session
8383
---
8484

8585
- [`getToken()`](#get-token)
86-
- [`ServerGetToken`](#server-get-token)
86+
- `ServerGetToken`
8787

8888
A function that gets the current user's [session token](/docs/backend-requests/resources/session-tokens) or a [custom JWT template](/docs/backend-requests/jwt-templates).
8989

docs/references/chrome-extension/configure-consistent-crx-id.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ To configure a consistent CRX ID for a new extension, follow these steps:
5858
1. Ensure that the development build is updated by running `pnpm dev`.
5959
1. Open Chrome or a Chromium-based browser and navigate to `chrome://extensions`.
6060
1. Remove and re-install the extension. To re-install, in the top-left, select **Load unpacked**. Navigate to where your project is located and select the `build/chrome-mv3-dev` folder. Then select **Select**. Your extension will now be loaded and shown in the list of extensions.
61-
1. Confirm that the ID shown in your extension matches the CRX ID you saved [earlier](#configure-a-crx-id-for-a-new-extension).
61+
1. Confirm that the ID shown in your extension matches the CRX ID you saved [earlier](#generate-your-keypairs).
6262
</Steps>
6363

6464
## For an extension uploaded to the Chrome Developer Dashboard

docs/references/javascript/sign-up.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The `SignUp` object holds the state of the current sign-up and provides helper m
88
The following steps outline the sign-up process:
99

1010
1. Initiate the sign-up process by collecting the user's authentication information and passing the appropriate parameters to the [`create()`](#create) method.
11-
1. Prepare the [verification](#verification).
11+
1. Prepare the verification.
1212
1. Attempt to complete the verification.
1313
1. If the verification is successful, set the newly created session as the active session by passing the `SignIn.createdSessionId` to the [`setActive()`](/docs/references/javascript/clerk#set-active) method on the `Clerk` object.
1414

docs/references/javascript/types/role.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ An interface that represents a role in an organization.
3535
---
3636

3737
- `permissions`
38-
- <code>[PermissionResource](#permission-resource)\[]</code>
38+
- <code>[PermissionResource](/docs/references/javascript/types/permission)\[]</code>
3939

4040
The permissions of the role.
4141

docs/references/nextjs/basic-rbac.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ This guide assumes that you're using Next.js App Router, but the concepts can be
6666
Create a helper function to simplify checking roles.
6767

6868
1. In your application's root directory, create a `utils/` folder.
69-
1. Inside this directory, create a `roles.ts` file with the following code. The `checkRole()` helper uses the [`auth()`](/docs/references/nextjs/auth) helper to access the user's session claims. From the session claims, it accesses the `metadata` object to check the user's role. The `checkRole()` helper accepts a role of type `Roles`, which you created in the [Create a global TypeScript definition](#create-a-global-typescript-definition) step. It returns `true` if the user has that role or `false` if they do not.
69+
1. Inside this directory, create a `roles.ts` file with the following code. The `checkRole()` helper uses the [`auth()`](/docs/references/nextjs/auth) helper to access the user's session claims. From the session claims, it accesses the `metadata` object to check the user's role. The `checkRole()` helper accepts a role of type `Roles`, which you created in the [Create a global TypeScript definition](#create-a-global-type-script-definition) step. It returns `true` if the user has that role or `false` if they do not.
7070

7171
```ts {{ filename: 'utils/roles.ts' }}
7272
import { Roles } from '@/types/globals'

docs/references/nextjs/waitlist.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ In [**Waitlist** mode](/docs/authentication/configuration/restrictions#waitlist)
107107

108108
To allow users to sign in once they've been approved from the waitlist, you must:
109109

110-
- [Add `clerkMiddleware()` to your app.](#add-clerkmiddleware-to-your-app)
110+
- [Add `clerkMiddleware()` to your app.](#add-clerk-middleware-to-your-app)
111111
- [Add a sign-in page.](#add-a-sign-in-page)
112112

113113
### Add `clerkMiddleware()` to your app

docs/testing/playwright/test-authenticated-flows.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ This guide demonstrates how to save the auth state globally and load it in your
6868

6969
## Load the stored auth state in your tests
7070

71-
You can either load the stored auth state [in the config](#-in-the-config) or directly [in a test file](#-in-a-test-file). Loading in the config is useful if you want to authenticate once and reuse the same auth state for all tests or groups of tests. Loading in a test file is useful if you want to authenticate for a specific test case.
71+
You can either load the stored auth state [in the config](#in-the-config) or directly [in a test file](#in-a-test-file). Loading in the config is useful if you want to authenticate once and reuse the same auth state for all tests or groups of tests. Loading in a test file is useful if you want to authenticate for a specific test case.
7272

7373
### In the config
7474

docs/testing/playwright/test-helpers.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ Before calling `clerk.signIn()`, it is required to call `page.goto()` and naviga
3535
---
3636

3737
- `setupClerkTestingTokenOptions?`
38-
- [`SetupClerkTestingTokenOptions`](#set-up-clerk-testing-token-options)
38+
- [`SetupClerkTestingTokenOptions`](#setup-clerk-testing-token-options)
3939

40-
Options to pass to `setupClerkTestingToken()`. See [`SetupClerkTestingTokenOptions`](#set-up-clerk-testing-token-options).
40+
Options to pass to `setupClerkTestingToken()`. See [`SetupClerkTestingTokenOptions`](#setup-clerk-testing-token-options).
4141
</Properties>
4242

4343
#### `ClerkSignInParams`

docs/users/user-impersonation.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ curl -X POST https://api.clerk.com/v1/actor_tokens -d '{ \
4040
}'
4141
```
4242

43-
When creating actor tokens, the object that you pass as the `actor` parameter will end up in the authentication token's `act` claim. You can read more details in the [JWT claims](#jwt-claims) section of this document.
43+
When creating actor tokens, the object that you pass as the `actor` parameter will end up in the authentication token's `act` claim. You can read more details in the [guide on session tokens](/docs/backend-requests/resources/session-tokens#default-claims).
4444

4545
### Revoke an actor token
4646

scripts/build-docs.test.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,68 @@ test`,
632632
expect(output).toContain(`warning Hash "my-heading" not found in /docs/page-2`)
633633
expect(output).toContain(`warning Doc /docs/page-3 not found`)
634634
})
635+
636+
test('Links with only a hash to the same page are valid', async () => {
637+
const { tempDir } = await createTempFiles([
638+
{
639+
path: './docs/manifest.json',
640+
content: JSON.stringify({
641+
navigation: [[{ title: 'Page 1', href: '/docs/page-1' }]],
642+
}),
643+
},
644+
{
645+
path: './docs/page-1.mdx',
646+
content: `---
647+
title: Page 1
648+
description: This is a test page
649+
---
650+
651+
# Heading
652+
653+
[Valid Link to self](#heading)`,
654+
},
655+
])
656+
657+
const output = await build(
658+
createConfig({
659+
...baseConfig,
660+
basePath: tempDir,
661+
validSdks: ['react'],
662+
}),
663+
)
664+
665+
expect(output).toBe('')
666+
})
667+
668+
test('Invalid Links with only a hash to the same page should be reported', async () => {
669+
const { tempDir } = await createTempFiles([
670+
{
671+
path: './docs/manifest.json',
672+
content: JSON.stringify({
673+
navigation: [[{ title: 'Page 1', href: '/docs/page-1' }]],
674+
}),
675+
},
676+
{
677+
path: './docs/page-1.mdx',
678+
content: `---
679+
title: Page 1
680+
description: This is a test page
681+
---
682+
683+
[Invalid Link to self](#invalid-heading)`,
684+
},
685+
])
686+
687+
const output = await build(
688+
createConfig({
689+
...baseConfig,
690+
basePath: tempDir,
691+
validSdks: ['react'],
692+
}),
693+
)
694+
695+
expect(output).toContain(`warning Hash "invalid-heading" not found in /docs/page-1`)
696+
})
635697
})
636698

637699
describe('Path and File Handling', () => {

scripts/build-docs.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -970,23 +970,28 @@ export const build = async (config: BuildConfig) => {
970970
if (node.type !== 'link') return
971971
if (!('url' in node)) return
972972
if (typeof node.url !== 'string') return
973-
if (!node.url.startsWith('/docs/')) return
973+
if (!node.url.startsWith('/docs/') && !node.url.startsWith('#')) return
974974
if (!('children' in node)) return
975975

976-
const [url, hash] = removeMdxSuffix(node.url).split('#')
976+
let [url, hash] = removeMdxSuffix(node.url).split('#')
977+
978+
if (url === '') {
979+
// If the link is just a hash, then we need to link to the same doc
980+
url = doc.href
981+
}
977982

978983
const ignore = config.ignorePaths.some((ignoreItem) => url.startsWith(ignoreItem))
979984
if (ignore === true) return
980985

981-
const doc = docsMap.get(url)
986+
const linkedDoc = docsMap.get(url)
982987

983-
if (doc === undefined) {
988+
if (linkedDoc === undefined) {
984989
safeMessage(config, vfile, filePath, 'link-doc-not-found', [url], node.position)
985990
return
986991
}
987992

988993
if (hash !== undefined) {
989-
const hasHash = doc.headingsHashs.includes(hash)
994+
const hasHash = linkedDoc.headingsHashs.includes(hash)
990995

991996
if (hasHash === false) {
992997
safeMessage(config, vfile, filePath, 'link-hash-not-found', [hash, url], node.position)

0 commit comments

Comments
 (0)