0
Fork 0
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:
Emanuele Stoppa 2024-05-08 15:05:03 +01:00 committed by GitHub
parent a020d0028d
commit f0acd30a12
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 22 additions and 75 deletions

View file

@ -0,0 +1,5 @@
---
"@astrojs/vercel": minor
---
Implements the vercel skew protection

View file

@ -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;

View file

@ -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);
}; };

View file

@ -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
};
"
`;