Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ describe('createServeRequestHandler', () => {
/café 200 café.html
//file 301 /file → 200 file.html
/%E0%A4%A 400 Bad Request
/%00 500 Internal Server Error
/%00 400 Bad Request
/..%2Fdocusaurus.config.js 400 Bad Request"
`);
});
Expand Down Expand Up @@ -226,7 +226,7 @@ describe('createServeRequestHandler', () => {
/café 301 /caf%C3%A9/ → 200 café.html
//file 301 /file → 301 /file/ → 200 file.html
/%E0%A4%A 301 /%E0%A4%A/ → 400 Bad Request
/%00 301 /%00/ → 500 Internal Server Error
/%00 400 Bad Request
/..%2Fdocusaurus.config.js 400 Bad Request"
`);
});
Expand Down Expand Up @@ -287,7 +287,7 @@ describe('createServeRequestHandler', () => {
/café 200 café.html
//file 301 /file → 200 file.html
/%E0%A4%A 400 Bad Request
/%00 500 Internal Server Error
/%00 400 Bad Request
/..%2Fdocusaurus.config.js 400 Bad Request"
`);
});
Expand Down Expand Up @@ -350,7 +350,7 @@ describe('createServeRequestHandler', () => {
/base/café 200 café.html
/base//file 301 /base/file → 200 file.html
/base/%E0%A4%A 400 Bad Request
/base/%00 500 Internal Server Error
/base/%00 400 Bad Request
/base/..%2Fdocusaurus.config.js 400 Bad Request"
`);
});
Expand Down Expand Up @@ -411,7 +411,7 @@ describe('createServeRequestHandler', () => {
/base/café 301 /base/caf%C3%A9/ → 200 café.html
/base//file 301 /base/file → 301 /base/file/ → 200 file.html
/base/%E0%A4%A 301 /base/%E0%A4%A/ → 400 Bad Request
/base/%00 301 /base/%00/ → 500 Internal Server Error
/base/%00 400 Bad Request
/base/..%2Fdocusaurus.config.js 400 Bad Request"
`);
});
Expand Down Expand Up @@ -472,7 +472,7 @@ describe('createServeRequestHandler', () => {
/base/café 200 café.html
/base//file 301 /base/file → 200 file.html
/base/%E0%A4%A 400 Bad Request
/base/%00 500 Internal Server Error
/base/%00 400 Bad Request
/base/..%2Fdocusaurus.config.js 400 Bad Request"
`);
});
Expand Down Expand Up @@ -803,14 +803,23 @@ describe('createServeRequestHandler', () => {
);
});

// TODO fix: answer 400, like the malformed percent-encodings
it('answers 500 to null bytes', async () => {
// Like the malformed percent-encodings
it('answers 400 to null bytes', async () => {
await expect(
serveRequest(
{baseUrl: '/base/', trailingSlash: undefined},
'/base/%00',
),
).resolves.toBe('500 Internal Server Error');
).resolves.toBe('400 Bad Request');
});

it('keeps null bytes in the query string', async () => {
await expect(
serveRequest(
{baseUrl: '/base/', trailingSlash: undefined},
'/base/file?query=%00',
),
).resolves.toBe('200 file.html');
});

// Like other ".." segments going above the root
Expand Down
14 changes: 14 additions & 0 deletions packages/docusaurus/src/commands/serve/requestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ export function createServeRequestHandler({
return;
}

// serve-handler answers 500 to null bytes, its file system calls throw
// Answer 400 instead, like serve-handler does to malformed encodings
if (hasNullByte(req.url)) {
res.writeHead(400);
res.end('Bad Request');
return;
}

const redirectLocation = await getRedirectLocation(req.url, {
outDir,
baseUrl,
Expand Down Expand Up @@ -180,6 +188,12 @@ function parseFakeRootURLPath(
return {pathname: pathname.slice(fakeRoot.length), search};
}

// Like serve-handler, only the pathname is decoded, not the query string
function hasNullByte(url: string): boolean {
const pathname = url.split(/[?#]/)[0]!;
return pathname.includes('%00');
}

// Pages with a dot in their name look like assets, like "docs/2.x"
async function isPage(outDir: string, urlPath: string): Promise<boolean> {
const filePath = getFilePath(outDir, urlPath);
Expand Down
Loading