mirror of
https://github.com/withastro/astro.git
synced 2024-12-23 21:53:55 -05:00
feat(vercel): skew protection (#10761)
* feat(vercel): skew protection * feat(vercel): skew protection
This commit is contained in:
parent
a020d0028d
commit
f0acd30a12
4 changed files with 22 additions and 75 deletions
5
.changeset/honest-games-bathe.md
Normal file
5
.changeset/honest-games-bathe.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"@astrojs/vercel": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Implements the vercel skew protection
|
|
@ -72,16 +72,18 @@ function getAdapter({
|
||||||
edgeMiddleware,
|
edgeMiddleware,
|
||||||
functionPerRoute,
|
functionPerRoute,
|
||||||
middlewareSecret,
|
middlewareSecret,
|
||||||
|
skewProtection,
|
||||||
}: {
|
}: {
|
||||||
edgeMiddleware: boolean;
|
edgeMiddleware: boolean;
|
||||||
functionPerRoute: boolean;
|
functionPerRoute: boolean;
|
||||||
middlewareSecret: string;
|
middlewareSecret: string;
|
||||||
|
skewProtection: boolean;
|
||||||
}): AstroAdapter {
|
}): AstroAdapter {
|
||||||
return {
|
return {
|
||||||
name: PACKAGE_NAME,
|
name: PACKAGE_NAME,
|
||||||
serverEntrypoint: `${PACKAGE_NAME}/entrypoint`,
|
serverEntrypoint: `${PACKAGE_NAME}/entrypoint`,
|
||||||
exports: ['default'],
|
exports: ['default'],
|
||||||
args: { middlewareSecret },
|
args: { middlewareSecret, skewProtection },
|
||||||
adapterFeatures: {
|
adapterFeatures: {
|
||||||
edgeMiddleware,
|
edgeMiddleware,
|
||||||
functionPerRoute,
|
functionPerRoute,
|
||||||
|
@ -139,6 +141,10 @@ export interface VercelServerlessConfig {
|
||||||
|
|
||||||
/** Whether to cache on-demand rendered pages in the same way as static files. */
|
/** Whether to cache on-demand rendered pages in the same way as static files. */
|
||||||
isr?: boolean | VercelISRConfig;
|
isr?: boolean | VercelISRConfig;
|
||||||
|
/**
|
||||||
|
* It enables Vercel skew protection: https://vercel.com/docs/deployments/skew-protection
|
||||||
|
*/
|
||||||
|
skewProtection?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VercelISRConfig {
|
interface VercelISRConfig {
|
||||||
|
@ -180,6 +186,7 @@ export default function vercelServerless({
|
||||||
edgeMiddleware = false,
|
edgeMiddleware = false,
|
||||||
maxDuration,
|
maxDuration,
|
||||||
isr = false,
|
isr = false,
|
||||||
|
skewProtection = false,
|
||||||
}: VercelServerlessConfig = {}): AstroIntegration {
|
}: VercelServerlessConfig = {}): AstroIntegration {
|
||||||
if (maxDuration) {
|
if (maxDuration) {
|
||||||
if (typeof maxDuration !== 'number') {
|
if (typeof maxDuration !== 'number') {
|
||||||
|
@ -277,7 +284,9 @@ export default function vercelServerless({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAdapter(getAdapter({ functionPerRoute, edgeMiddleware, middlewareSecret }));
|
setAdapter(
|
||||||
|
getAdapter({ functionPerRoute, edgeMiddleware, middlewareSecret, skewProtection })
|
||||||
|
);
|
||||||
|
|
||||||
_config = config;
|
_config = config;
|
||||||
_buildTempFolder = config.build.server;
|
_buildTempFolder = config.build.server;
|
||||||
|
|
|
@ -12,7 +12,7 @@ applyPolyfills();
|
||||||
|
|
||||||
export const createExports = (
|
export const createExports = (
|
||||||
manifest: SSRManifest,
|
manifest: SSRManifest,
|
||||||
{ middlewareSecret }: { middlewareSecret: string }
|
{ middlewareSecret, skewProtection }: { middlewareSecret: string; skewProtection: boolean }
|
||||||
) => {
|
) => {
|
||||||
const app = new NodeApp(manifest);
|
const app = new NodeApp(manifest);
|
||||||
const handler = async (req: IncomingMessage, res: ServerResponse) => {
|
const handler = async (req: IncomingMessage, res: ServerResponse) => {
|
||||||
|
@ -38,6 +38,11 @@ export const createExports = (
|
||||||
// hide the secret from the rest of user code
|
// hide the secret from the rest of user code
|
||||||
delete req.headers[ASTRO_MIDDLEWARE_SECRET_HEADER];
|
delete req.headers[ASTRO_MIDDLEWARE_SECRET_HEADER];
|
||||||
|
|
||||||
|
// https://vercel.com/docs/deployments/skew-protection#supported-frameworks
|
||||||
|
if (skewProtection && process.env.VERCEL_SKEW_PROTECTION_ENABLED === '1') {
|
||||||
|
req.headers['x-deployment-id'] = process.env.VERCEL_DEPLOYMENT_ID;
|
||||||
|
}
|
||||||
|
|
||||||
const webResponse = await app.render(req, { addCookieHeader: true, clientAddress, locals });
|
const webResponse = await app.render(req, { addCookieHeader: true, clientAddress, locals });
|
||||||
await NodeApp.writeResponse(webResponse, res);
|
await NodeApp.writeResponse(webResponse, res);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Middleware with handler file 1`] = `
|
|
||||||
"// test/fixtures/middleware-with-edge-file/src/vercel-edge-middleware.js
|
|
||||||
function vercel_edge_middleware_default({ request, context }) {
|
|
||||||
return {
|
|
||||||
title: \\"Hello world\\"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// test/fixtures/middleware-with-edge-file/dist/middleware2.mjs
|
|
||||||
var onRequest = async (context, next) => {
|
|
||||||
const response = await next();
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
|
|
||||||
// <stdin>
|
|
||||||
import { createContext, trySerializeLocals } from \\"astro/middleware\\";
|
|
||||||
async function middleware(request, context) {
|
|
||||||
const url = new URL(request.url);
|
|
||||||
const ctx = createContext({
|
|
||||||
request,
|
|
||||||
params: {}
|
|
||||||
});
|
|
||||||
ctx.locals = vercel_edge_middleware_default({ request, context });
|
|
||||||
const next = async () => {
|
|
||||||
const response = await fetch(url, {
|
|
||||||
headers: {
|
|
||||||
\\"x-astro-locals\\": trySerializeLocals(ctx.locals)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
return onRequest(ctx, next);
|
|
||||||
}
|
|
||||||
export {
|
|
||||||
middleware as default
|
|
||||||
};
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Middleware without handler file 1`] = `
|
|
||||||
"// test/fixtures/middleware-without-edge-file/dist/middleware2.mjs
|
|
||||||
var onRequest = async (context, next) => {
|
|
||||||
const response = await next();
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
|
|
||||||
// <stdin>
|
|
||||||
import { createContext, trySerializeLocals } from \\"astro/middleware\\";
|
|
||||||
async function middleware(request, context) {
|
|
||||||
const url = new URL(request.url);
|
|
||||||
const ctx = createContext({
|
|
||||||
request,
|
|
||||||
params: {}
|
|
||||||
});
|
|
||||||
ctx.locals = {};
|
|
||||||
const next = async () => {
|
|
||||||
const response = await fetch(url, {
|
|
||||||
headers: {
|
|
||||||
\\"x-astro-locals\\": trySerializeLocals(ctx.locals)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
return onRequest(ctx, next);
|
|
||||||
}
|
|
||||||
export {
|
|
||||||
middleware as default
|
|
||||||
};
|
|
||||||
"
|
|
||||||
`;
|
|
Loading…
Reference in a new issue