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:
- the web server or CDN itself, which you keep patched or leave to the host.
- anything you add that does run per request: form handlers, serverless functions, edge middleware. Each one brings back a runtime and should be treated as one.
- the browser. Third-party scripts and any HTML that reaches the page unescaped can still produce XSS. A Content-Security-Policy that allows only your own scripts limits the damage.
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.
- Commit the lockfile and install from it (
npm ci), so the build runs the code you reviewed. - Watch for install scripts.
npm install --ignore-scriptsis worth trying, though some packages need their scripts. - Give the build only the secrets it needs. A static build often needs none.
- Keep dependencies updated with Dependabot or Renovate, and run
npm auditin CI. - Don’t expose secrets to builds of pull requests from forks.
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.
- Raw HTML in Markdown. Astro passes it through, so a
<script>in a Markdown file reaches the page. Hugo’s default Goldmark renderer drops raw HTML unlessmarkup.goldmark.renderer.unsafeis true. Know which your generator does. - MDX is JavaScript. An expression in an
.mdxfile runs at build time with the build’s access. Restrict who can edit MDX, or keep editors to a declared set of components. - Frontmatter that ends up in a template unescaped, such as a
set:htmlordangerouslySetInnerHTML, is an injection point like any other.
The git host
The git host replaces the admin login, so its access controls are the ones that matter.
- Require two-factor authentication for everyone with write access.
- Protect the branch that deploys to production. Require a review, or restrict pushes to the people who approve releases.
- Use fine-grained tokens and short-lived credentials for CI, scoped to the repository.
- Review who has access. Former contributors and old deploy keys are the usual leftovers.
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.