← Blog

The attack surface of a static site built from Markdown in git

On 21 October PHP shipped fixes for CVE-2026-109618 and CVE-2026-109619, two unauthenticated remote code execution flaws scored 9.2 under CVSS v4.0 (advisory). One comes from out-of-memory handling interrupting PHP while request data is half initialised. The other leaves request-owned state alive after its memory is freed, for a later request on the same worker to use. Every web SAPI is affected, and the PHP team demonstrated an exploit on mod_php that fires before any application code runs.

That last detail matters most here. Keeping WordPress and its plugins patched did nothing for this bug, because the vulnerable code sits in the runtime that handles every request. The fix is PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35. The interim mitigations in the advisory (for example session.upload_progress.enabled=0 set in php.ini or the FPM pool, then restarting every worker) reduce exposure without removing it.

This post looks at the same question for a static site whose content is Markdown in a git repository: which code can an attacker reach, and when does it run?

Request time

Once built, the site is a directory of HTML, CSS, JS and images behind a web server or CDN. A request is a file lookup. There is no application runtime, no session handling and no database connection on the request path, so the class of bug in the PHP advisory has nothing to land on.

What remains at request time:

Hybrid frameworks blur this. Next.js and Nuxt can export a fully static site (output: 'export', nuxi generate), but server rendering, API routes and middleware put a Node runtime back on the request path. CVE-2025-29927 let a crafted x-middleware-subrequest header skip Next.js middleware entirely, auth checks included, on self-hosted apps. Astro is static by default; an adapter with output: 'server' or server islands changes that.

Build time

The build is where code runs now, and it runs with more privilege than a request handler ever had: your dependencies execute with the build’s environment variables and whatever tokens the CI job holds.

Content as code

Anyone who can push to the repository can change what the build produces, so content is closer to code than it looks.

The git host

The git host replaces the admin login, so its access controls are the ones that matter.

Headless CMS sources

If content comes from a headless CMS at build time, that CMS is a request-time server again, with its own login page. A PHP-based one, WordPress used headless for example, needed this month’s PHP update like any other. Restricting its API to the build host’s addresses, or keeping it off the public internet, narrows its exposure.

Orbit MD

Orbit MD sits on the editor’s machine. It reads and writes Markdown and data files through git, using each editor’s own credentials, and approvals map onto the git host’s branch protection. It adds a config file and a requests file to the repository, and nothing it adds runs at build or request time.

For a less technical take on this, read A website with no admin login.

All posts