Skip to content

Commit

Permalink
fix(node): Fix malformed URLs crashing the server in certain cases (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Princesseuh authored Apr 4, 2023
1 parent 1ec1df1 commit 4cc1bf6
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 220 deletions.
5 changes: 5 additions & 0 deletions .changeset/cyan-sheep-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/node': patch
---

Fix malformed URLs crashing the server in certain cases
2 changes: 1 addition & 1 deletion packages/astro/test/fixtures/ssr-api-route/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/node": "^1.1.0",
"@astrojs/node": "workspace:*",
"astro": "workspace:*"
}
}
22 changes: 19 additions & 3 deletions packages/integrations/node/src/http-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,32 @@ interface CreateServerOptions {
removeBase: (pathname: string) => string;
}

function parsePathname(pathname: string, host: string | undefined, port: number) {
try {
const urlPathname = new URL(pathname, `http://${host}:${port}`).pathname;
return decodeURI(encodeURI(urlPathname));
} catch (err) {
return undefined;
}
}

export function createServer(
{ client, port, host, removeBase }: CreateServerOptions,
handler: http.RequestListener
) {
const listener: http.RequestListener = (req, res) => {
if (req.url) {
let pathname = removeBase(req.url);
let pathname: string | undefined = removeBase(req.url);
pathname = pathname[0] === '/' ? pathname : '/' + pathname;
pathname = new URL(pathname, `http://${host}:${port}`).pathname;
const stream = send(req, encodeURI(decodeURI(pathname)), {
const encodedURI = parsePathname(pathname, host, port);

if (!encodedURI) {
res.writeHead(400);
res.end('Bad request.');
return res;
}

const stream = send(req, encodedURI, {
root: fileURLToPath(client),
dotfiles: pathname.startsWith('/.well-known/') ? 'allow' : 'deny',
});
Expand Down
46 changes: 46 additions & 0 deletions packages/integrations/node/test/bad-urls.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { expect } from 'chai';
import nodejs from '../dist/index.js';
import { loadFixture } from './test-utils.js';

describe('API routes', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
let devPreview;

before(async () => {
fixture = await loadFixture({
root: './fixtures/bad-urls/',
output: 'server',
adapter: nodejs({ mode: 'standalone' }),
});
await fixture.build();
devPreview = await fixture.preview();
});

after(async () => {
await devPreview.stop();
});

it('Does not crash on bad urls', async () => {
const weirdURLs = [
'/\\xfs.bxss.me%3Fastrojs.com/hello-world',
'/asdasdasd@ax_zX=.zxczas🐥%/úadasd000%/',
'%',
'%80',
'%c',
'%c0%80',
'%20foobar%',
];

for (const weirdUrl of weirdURLs) {
const fetchResult = await fixture.fetch(weirdUrl);
expect([400, 500]).to.include(
fetchResult.status,
`${weirdUrl} returned something else than 400 or 500`
);
}
const stillWork = await fixture.fetch('/');
const text = await stillWork.text();
expect(text).to.equal('<!DOCTYPE html>\nHello!');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@test/nodejs-badurls",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/node": "workspace:*"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello!
Loading

0 comments on commit 4cc1bf6

Please sign in to comment.