0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-01-13 22:11:20 -05:00

Merge commit from fork

* fix: enforce check origin logic

* address feedback
This commit is contained in:
Emanuele Stoppa 2024-12-02 15:39:22 +00:00 committed by GitHub
parent 07b9ca802e
commit 315c5f3b2a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 52 additions and 15 deletions

View file

@ -25,22 +25,43 @@ export function createOriginCheckMiddleware(): MiddlewareHandler {
if (isPrerendered) { if (isPrerendered) {
return next(); return next();
} }
const contentType = request.headers.get('content-type'); if (request.method === "GET") {
if (contentType) { return next();
if (FORM_CONTENT_TYPES.includes(contentType.toLowerCase())) { }
const forbidden = const sameOrigin =
(request.method === 'POST' || (request.method === 'POST' ||
request.method === 'PUT' || request.method === 'PUT' ||
request.method === 'PATCH' || request.method === 'PATCH' ||
request.method === 'DELETE') && request.method === 'DELETE') &&
request.headers.get('origin') !== url.origin; request.headers.get('origin') === url.origin;
if (forbidden) {
return new Response(`Cross-site ${request.method} form submissions are forbidden`, { const hasContentType = request.headers.has('content-type')
status: 403, if (hasContentType) {
}); const formLikeHeader = hasFormLikeHeader(request.headers.get('content-type'));
} if (formLikeHeader && !sameOrigin) {
return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
status: 403,
});
}
} else {
if (!sameOrigin) {
return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
status: 403,
});
} }
} }
return next();
return next()
}); });
} }
function hasFormLikeHeader(contentType: string | null): boolean {
if (contentType) {
for (const FORM_CONTENT_TYPE of FORM_CONTENT_TYPES) {
if (contentType.toLowerCase().includes(FORM_CONTENT_TYPE)) {
return true;
}
}
}
return false;
}

View file

@ -46,6 +46,22 @@ describe('CSRF origin check', () => {
}); });
response = await app.render(request); response = await app.render(request);
assert.equal(response.status, 403); assert.equal(response.status, 403);
request = new Request('http://example.com/api/', {
headers: { origin: 'http://loreum.com', 'content-type': 'application/x-www-form-urlencoded; some-other-value' },
method: 'POST',
});
response = await app.render(request);
assert.equal(response.status, 403);
request = new Request('http://example.com/api/', {
headers: { origin: 'http://loreum.com', },
method: 'POST',
credentials: 'include',
body: new Blob(["a=b"],{})
});
response = await app.render(request);
assert.equal(response.status, 403);
}); });
it("return 403 when the origin doesn't match and calling a PUT", async () => { it("return 403 when the origin doesn't match and calling a PUT", async () => {