diff --git a/.changeset/fair-actors-remain.md b/.changeset/fair-actors-remain.md
new file mode 100644
index 000000000..e77aac777
--- /dev/null
+++ b/.changeset/fair-actors-remain.md
@@ -0,0 +1,5 @@
+---
+"gitbook": patch
+---
+
+Fix security issue with injection of "javacript:` url in the back button of PDFs
diff --git a/.changeset/lazy-oranges-play.md b/.changeset/lazy-oranges-play.md
new file mode 100644
index 000000000..a965a9300
--- /dev/null
+++ b/.changeset/lazy-oranges-play.md
@@ -0,0 +1,5 @@
+---
+'gitbook': patch
+---
+
+Fix OpenAPISchemas description padding
diff --git a/packages/gitbook/src/components/DocumentView/OpenAPI/style.css b/packages/gitbook/src/components/DocumentView/OpenAPI/style.css
index e0b5488e6..ad32f7e45 100644
--- a/packages/gitbook/src/components/DocumentView/OpenAPI/style.css
+++ b/packages/gitbook/src/components/DocumentView/OpenAPI/style.css
@@ -156,6 +156,10 @@
@apply prose-sm text-balance mt-1.5 !text-[0.813rem] text-tint overflow-hidden !font-normal select-text prose-strong:font-semibold prose-strong:text-inherit;
}
+.openapi-section-schemas > .openapi-section-body > .openapi-schema-root-description {
+ @apply px-2.5 pt-1 !text-sm;
+}
+
.openapi-schema-properties {
@apply flex flex-col;
}
diff --git a/packages/gitbook/src/components/PDF/PDFPage.tsx b/packages/gitbook/src/components/PDF/PDFPage.tsx
index 081fadc02..96cb784cc 100644
--- a/packages/gitbook/src/components/PDF/PDFPage.tsx
+++ b/packages/gitbook/src/components/PDF/PDFPage.tsx
@@ -20,7 +20,6 @@ import { TrademarkLink } from '@/components/TableOfContents/Trademark';
import type { PolymorphicComponentProp } from '@/components/utils/types';
import { getSpaceLanguage } from '@/intl/server';
import { tString } from '@/intl/translate';
-import { getPagePDFContainerId } from '@/lib/links';
import { resolvePageId } from '@/lib/pages';
import { tcls } from '@/lib/tailwind';
import { defaultCustomization } from '@/lib/utils';
@@ -29,6 +28,7 @@ import { type PDFSearchParams, getPDFSearchParams } from './urls';
import { PageControlButtons } from './PageControlButtons';
import { PrintButton } from './PrintButton';
import './pdf.css';
+import { sanitizeGitBookAppURL } from '@/lib/app';
const DEFAULT_LIMIT = 100;
@@ -92,7 +92,10 @@ export async function PDFPage(props: {
{
- assertIsNotV2();
- const headersList = await headers();
- const path = headersList.get('x-gitbook-basepath') ?? '/';
-
- return withTrailingSlash(withLeadingSlash(path));
-}
-
-/**
- * Return the site base path for the current request.
- * The value will start and finish with /
- */
-export async function getSiteBasePath(): Promise {
- assertIsNotV2();
- const headersList = await headers();
- const path = headersList.get('x-gitbook-site-basepath') ?? '/';
-
- return withTrailingSlash(withLeadingSlash(path));
-}
-
-/**
- * Return the current host for the current request.
- */
-export async function getHost(): Promise {
- assertIsNotV2();
- const headersList = await headers();
- const mode = headersList.get('x-gitbook-mode');
- if (mode === 'proxy') {
- return headersList.get('x-forwarded-host') ?? '';
- }
-
- return headersList.get('x-gitbook-host') ?? headersList.get('host') ?? '';
-}
-
-/**
- * Return the root URL for the GitBook Open instance (not the content).
- * Use `baseUrl` to get the base URL for the current content.
- *
- * The URL will end with "/".
- */
-export async function getRootUrl(): Promise {
- assertIsNotV2();
- const [headersList, host] = await Promise.all([headers(), getHost()]);
- const protocol = headersList.get('x-forwarded-proto') ?? 'https';
- let path = headersList.get('x-gitbook-origin-basepath') ?? '/';
-
- if (!path.startsWith('/')) {
- path = `/${path}`;
- }
-
- if (!path.endsWith('/')) {
- path = `${path}/`;
- }
-
- return `${protocol}://${host}${path}`;
-}
-
-/**
- * Return the base URL for the current content.
- * The URL will end with "/".
- */
-export async function getBaseUrl(): Promise {
- assertIsNotV2();
- const [headersList, host, basePath] = await Promise.all([headers(), getHost(), getBasePath()]);
- const protocol = headersList.get('x-forwarded-proto') ?? 'https';
- return `${protocol}://${host}${basePath}`;
-}
-
-/**
- * Create an absolute href in the current content.
- */
-export async function getAbsoluteHref(href: string, withHost = false): Promise {
- assertIsNotV2();
- const base = withHost ? await getBaseUrl() : await getBasePath();
- return `${base}${href.startsWith('/') ? href.slice(1) : href}`;
-}
-
-/**
- * Create an absolute href in the GitBook application.
- */
-export function getGitbookAppHref(pathname: string): string {
- const appUrl = new URL(GITBOOK_APP_URL);
- appUrl.pathname = pathname;
-
- return appUrl.toString();
-}
-
-/**
- * Create a link to a page path in the current space.
- */
-export async function getPageHref(
- rootPages: RevisionPage[],
- page: RevisionPageDocument | RevisionPageGroup,
- context: PageHrefContext = {},
- /** Anchor to link to in the page. */
- anchor?: string
-): Promise {
- assertIsNotV2();
- const { pdf } = context;
-
- if (pdf) {
- if (pdf.includes(page.id)) {
- return `#${getPagePDFContainerId(page, anchor)}`;
- }
- if (page.type === RevisionPageType.Group) {
- return '#';
- }
-
- // Use an absolute URL to the page
- return page.urls.app;
- }
-
- const href =
- (await getAbsoluteHref(getPagePath(rootPages, page))) + (anchor ? `#${anchor}` : '');
- return href;
-}
-
-/**
- * Create the HTML ID for the container of a page during a PDF rendering.
- */
-export function getPagePDFContainerId(
- page: RevisionPageDocument | RevisionPageGroup,
- anchor?: string
-): string {
- return `pdf-page-${page.id}${anchor ? `-${anchor}` : ''}`;
-}
diff --git a/packages/gitbook/src/lib/references.tsx b/packages/gitbook/src/lib/references.tsx
index 9b1e469b6..ab941e43c 100644
--- a/packages/gitbook/src/lib/references.tsx
+++ b/packages/gitbook/src/lib/references.tsx
@@ -15,8 +15,8 @@ import type React from 'react';
import { PageIcon } from '@/components/PageIcon';
+import { getGitBookAppHref } from './app';
import { getBlockById, getBlockTitle } from './document';
-import { getGitbookAppHref } from './links';
import { resolvePageId } from './pages';
import { findSiteSpaceById } from './sites';
import type { ClassValue } from './tailwind';
@@ -194,7 +194,7 @@ export async function resolveContentRef(
if (!targetSpace) {
return {
- href: getGitbookAppHref(`/s/${contentRef.space}`),
+ href: getGitBookAppHref(`/s/${contentRef.space}`),
text: 'space',
active: false,
};
@@ -224,7 +224,7 @@ export async function resolveContentRef(
case 'collection': {
return {
- href: getGitbookAppHref('/home'),
+ href: getGitBookAppHref('/home'),
text: 'collection',
active: false,
};
@@ -242,7 +242,7 @@ export async function resolveContentRef(
return null;
}
return {
- href: getGitbookAppHref(`/s/${space.id}`),
+ href: getGitBookAppHref(`/s/${space.id}`),
text: reusableContent.title,
active: false,
reusableContent,
diff --git a/packages/gitbook/src/lib/v1.ts b/packages/gitbook/src/lib/v1.ts
index 625eef0a4..ea5219816 100644
--- a/packages/gitbook/src/lib/v1.ts
+++ b/packages/gitbook/src/lib/v1.ts
@@ -9,6 +9,7 @@ import { createImageResizer } from '@v2/lib/images';
import { createLinker } from '@v2/lib/links';
import { DataFetcherError, wrapDataFetcherError } from '@v2/lib/data';
+import { headers } from 'next/headers';
import {
type SiteContentPointer,
type SpaceContentPointer,
@@ -31,7 +32,8 @@ import {
searchSiteContent,
} from './api';
import { getDynamicCustomizationSettings } from './customization';
-import { getBasePath, getHost, getSiteBasePath } from './links';
+import { withLeadingSlash, withTrailingSlash } from './paths';
+import { assertIsNotV2 } from './v2';
/*
* Code that will be used until the migration to v2 is complete.
@@ -329,3 +331,41 @@ export function getSitePointerFromContext(context: GitBookSiteContext): SiteCont
siteShareKey: context.shareKey,
};
}
+
+/**
+ * Return the base path for the current request.
+ * The value will start and finish with /
+ */
+async function getBasePath(): Promise {
+ assertIsNotV2();
+ const headersList = await headers();
+ const path = headersList.get('x-gitbook-basepath') ?? '/';
+
+ return withTrailingSlash(withLeadingSlash(path));
+}
+
+/**
+ * Return the site base path for the current request.
+ * The value will start and finish with /
+ */
+async function getSiteBasePath(): Promise {
+ assertIsNotV2();
+ const headersList = await headers();
+ const path = headersList.get('x-gitbook-site-basepath') ?? '/';
+
+ return withTrailingSlash(withLeadingSlash(path));
+}
+
+/**
+ * Return the current host for the current request.
+ */
+async function getHost(): Promise {
+ assertIsNotV2();
+ const headersList = await headers();
+ const mode = headersList.get('x-gitbook-mode');
+ if (mode === 'proxy') {
+ return headersList.get('x-forwarded-host') ?? '';
+ }
+
+ return headersList.get('x-gitbook-host') ?? headersList.get('host') ?? '';
+}