You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Date: 2026-03-07 Reviewer: SWE / DevOps / Security
PROJECT OVERVIEW
This is a Gatsby 5 static blog / personal site for a security researcher ("Eno Leriand"). It publishes MDX-based articles and CTF write-ups, rendered with React 18 and styled with Tailwind CSS v3 + the typography plugin. Content is stored under content/ (articles, CTFs, pages) and source code lives in src/. The site is deployed automatically to GitHub Pages via a GitHub Actions workflow on every push to master. Serverless Gatsby Functions in src/api/ provide a reaction system (FaunaDB), newsletter subscription (ConvertKit via axios), and a Google Analytics UA visitor-map endpoint.
ISSUES FOUND
π΄ CRITICAL β Build will fail / Security risk
#
Location
Issue
1
package.json
gatsby-plugin-mdx@^3.20.0 is incompatible with gatsby@^5.x. The v3 plugin targets Gatsby 4. Gatsby 5 requires gatsby-plugin-mdx@^5.x. This mismatch causes a hard build failure.
2
.gitignore
.env is tracked by git β the gitignore only excludes .env.development and .env.production. The base .env (with real credentials in production) will be committed to the repository.
3
.github/workflows/gatsby.yml
Double build: after the artifact upload step, a second yarn run build is unconditionally run, rebuilding the entire site after it has already been uploaded.
4
.github/workflows/gatsby.yml
NODE_TLS_REJECT_UNAUTHORIZED: 0 disables TLS certificate verification for the entire build, creating an MITM attack surface in CI.
π HIGH β Broken functionality / Missing dependencies
#
Location
Issue
5
package.json
dotenv is missing from dependencies. gatsby-config.js calls require('dotenv').config(β¦) at the top level. Relying on it as an undeclared transitive dependency is fragile.
6
package.json
axios is missing from dependencies. src/api/newsletter.js does const axios = require('axios') but axios is nowhere in package.json. The newsletter endpoint will throw at runtime.
7
src/api/ua-analytics.js
Uses the deprecated Universal Analytics (UA) v3 API (google.analytics('v3')). Google shut down UA data access on July 1, 2024. Every call to this endpoint returns an error. Needs migration to the GA4 Data API (@google-analytics/data).
8
src/api/fauna-add-reaction.js
No input validation: JSON.parse(req.body) throws an unhandled exception on malformed input. Field values are written to FaunaDB without sanitisation.
π‘ MEDIUM β Incorrect configuration / Bad patterns
#
Location
Issue
9
gatsby-config.js
pathPrefix: "/z0rs.github.io" is wrong for a user page. The CNAME file contains z0rs.github.io, indicating this is a GitHub user site (not a project site). With the prefix set, all internal links resolve to https://z0rs.github.io/z0rs.github.io/β¦ β a double-path URL.
10
.github/workflows/gatsby.yml
Ineffective cache key: hashFiles('public') hashes the public/ directory, which does not exist before the build runs. The cache will never produce a warm hit on a clean runner. Should hash the lockfile instead.
11
src/api/*.js (all 5 files)
Mixed CJS require() and ESM export default in the same file. This works today because Gatsby transpiles them, but it is an inconsistent anti-pattern that will break if the code is ever moved out of the Gatsby Functions pipeline.
12
package.json
react and react-dom pinned to exact 18.0.0 (no caret). Patch and security releases (e.g. 18.3.x) are silently excluded. Should be ^18.0.0.
13
gatsby-config.js
siteUrl: process.env.URL but URL is not defined in .env or the CI env vars, so gatsby-plugin-sitemap generates an invalid sitemap with an undefined base URL.
π‘ MEDIUM β Typos causing silent failures
#
Location
Issue
14
tailwind.config.js (Γ7)
fontWieght typo (should be fontWeight) in the typography plugin configuration β h1 through h6 and a elements. Tailwind silently ignores the unknown key, so no heading is actually bold.
π΅ LOW β Unused / outdated packages
#
Location
Issue
15
package.json
gatsby-plugin-gatsby-cloud@^5.0.0 is a deployment adapter for Gatsby Cloud, which is unrelated to GitHub Pages and adds unnecessary install/build overhead.
16
package.json
gatsby-plugin-google-analytics@^5.0.0 is listed as a dependency but is not included in the plugins array in gatsby-config.js. It is dead weight.
17
package.json
@mdx-js/mdx@^1.6.22 and @mdx-js/react@^1.6.22 are MDX v1 packages. gatsby-plugin-mdx@3 uses MDX v2 internally β these v1 packages are version-mismatched peer dependencies.
18
package.json
cmdk@^0.1.18 β latest stable is 1.x. The 0.1.x API is substantially different; upgrading is a minor breaking change, but the outdated version should be noted.
19
.nvmrc
Pins v18.0.0 exactly. CI uses "18" (resolves to latest 18.x). They should match.
FIXES
Fix 1 β gatsby-plugin-mdx version (CRITICAL)
Upgrade from v3 to v5 to match Gatsby 5. Also remove the mismatched @mdx-js/mdx and @mdx-js/react v1 packages; gatsby-plugin-mdx@5 bundles its own MDX v3.
--- a/gatsby-config.js+++ b/gatsby-config.js@@ -6,8 +6,8 @@
module.exports = {
- pathPrefix: "/z0rs.github.io",- // flags: {+ // pathPrefix removed: CNAME sets domain to z0rs.github.io (user page β no prefix needed).+ // Re-enable only if deploying as a project page under a different username.+ // flags: {
// FAST_DEV: true
// },
trailingSlash: 'always',
siteMetadata: {
name: 'Eno Leriand',
description: 'Security & Research',
...
- siteUrl: process.env.URL,+ siteUrl: process.env.SITE_URL || 'https://z0rs.github.io',
defaultImage: '/images/76135196.jpeg'
},
plugins: [
- 'gatsby-plugin-gatsby-cloud',
'gatsby-plugin-image',
...
--- a/src/api/fauna-add-reaction.js+++ b/src/api/fauna-add-reaction.js@@ -1,9 +1,22 @@
const faunadb = require('faunadb');
+const ALLOWED_REACTIONS = ['heart', 'thumbsup', 'fire', 'party'];+const MAX_STRING_LEN = 500;+
export default async function handler(req, res) {
- const { title, slug, reaction, date } = JSON.parse(req.body);+ let body;+ try {+ body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body;+ } catch {+ return res.status(400).json({ message: 'Invalid JSON body' });+ }++ const { title, slug, reaction, date } = body || {};++ if (!slug || typeof slug !== 'string' || slug.length > MAX_STRING_LEN) {+ return res.status(400).json({ message: 'Invalid or missing slug' });+ }+ if (!reaction || !ALLOWED_REACTIONS.includes(reaction)) {+ return res.status(400).json({ message: `reaction must be one of: ${ALLOWED_REACTIONS.join(', ')}` });+ }+ if (title && (typeof title !== 'string' || title.length > MAX_STRING_LEN)) {+ return res.status(400).json({ message: 'Invalid title' });+ }
const q = faunadb.query;
const client = new faunadb.Client({ secret: process.env.FAUNA_KEY });
--- a/.env+++ b/.env.example@@ -1,6 +1,9 @@+# Copy this file to .env and fill in your values.+# .env is git-ignored. Never commit real credentials.
GATSBY_API_URL=
GATSBY_TWITTER_USERNAME=
GATSBY_GA_MEASUREMENT_ID=
CK_FORM_ID=
CK_API_KEY=
+SITE_URL=https://z0rs.github.io
FAUNA_KEY=
SUMMARY TABLE
Severity
Count
Fixed in patch
π΄ Critical
4
4
π High
4
3 (UA analytics requires full rewrite)
π‘ Medium
6
4
π΅ Low
5
3
Manual action required (not auto-patchable)
src/api/ua-analytics.js β UA (v3) API is permanently shut down. Must rewrite to use the GA4 Data API (@google-analytics/data, already in package.json). This is a significant API change and needs the GA4 property ID from your Google Analytics account.
src/api/*.js β Standardise on ESM (import/export) or CJS (require/module.exports) β do not mix them in the same file.
content/articles/*.mdx β 19 of 19 articles are missing a status field in frontmatter. The GraphQL query in gatsby-node.js filters { status: { ne: "draft" } }. Without the field, articles rely on null β "draft" being truthy, which works but is fragile. Add status: published to each frontmatter block.
package.json β After applying the gatsby-plugin-mdx@^5 upgrade, run yarn install and verify that the wrapESMPlugin shim in gatsby-config.js is no longer needed (v5 handles ESM plugins natively).