Files
PersonalBlog/README.md
T
implementer 86aa3afbbf
CI / Run tests (pull_request) Successful in 15s
CI / Secret scan (gitleaks) (pull_request) Failing after 12s
refactor: newsletter signup posts no credential (SEC-14-R1 option a)
Rework PR #15 per security review SEC-14-R1: the client no longer carries
an API token. js/newsletter-config.js ships only the non-secret endpoint,
enforced https-only at config load time via validateEndpoint() (mirroring
the protocol allowlist in js/reading-list.js); js/newsletter.js POSTs
email-only with no Authorization header. Failure paths keep the single
user-safe message that never leaks token, endpoint, status, or raw body;
success still shows the confirmation. CI gains a gitleaks step that fails
on any secret hit; README documents the server-side token, the residual
signup-abuse risk, and the authoritative server-side validation follow-up.
2026-08-26 11:27:11 +00:00

3.8 KiB

Personal Blog

A small static personal blog. No build step, no backend — plain HTML, CSS, and vanilla JavaScript.

Pages

  • index.html — home page
  • reading.html — reading list page (curated links grouped by category, rendered from data/reading-list.js)
  • contact.html — contact page (opens the visitor's mail client with the form fields pre-filled via a mailto: link)
  • newsletter.html — newsletter signup page (posts the visitor's email to a configured serverless endpoint; no self-hosted backend or subscriber list management)

Newsletter signup

The signup form on newsletter.html POSTs the visitor's email to a serverless endpoint. The client ships only the endpoint URL — no credential is ever sent in the request:

  • NEWSLETTER_ENDPOINT — the serverless endpoint the form POSTs to. This is the only thing configured in js/newsletter-config.js, and it is enforced https://-only at config load time and by tests (mirroring the protocol allowlist in js/reading-list.js).

Authentication is server-side only. The serverless function reads its API token from platform env/secrets at deploy time — never from this repository and never from any client-served asset. Do not add a token to js/newsletter-config.js or anywhere else in the tree; anything committed here is public.

When the endpoint is unavailable or rejects the submission, the visitor sees a fixed, user-safe error message — the server-held token, the endpoint, the status code, and any raw response body are never surfaced. After a successful signup a confirmation message is shown.

Server-side validation (required follow-up on the function, not this diff): the serverless function must authoritatively validate submissions before processing — email syntax, length caps, pinned Content-Type, and rejection of unknown fields. Client-side checks here are UX only and are bypassable by direct API calls.

Residual signup-abuse risk (accepted, out of scope): this pass adds no spam filtering or captcha. Scripted signups remain possible, so the serverless function must mitigate abuse server-side with rate limiting and an origin allowlist.

Reading list

The reading list is a single data file: data/reading-list.js. The page code renders whatever is in it, so it can grow to hundreds of entries without code changes.

To add a link, edit data/reading-list.js only — add an entry with a title, a url, a category, and an optional one-line note:

{
  category: "Engineering",
  title: "A new article",
  url: "https://example.com/article",
  note: "Optional one-liner shown under the title.",
},

Categories appear on the page in the order they are first used; entries keep the order they are listed in.

Development

Serve the directory with any static file server, e.g.:

python3 -m http.server 8000

Then open http://localhost:8000.

Tests

Tests use Node's built-in test runner (no dependencies to install):

npm test

Project layout

index.html        Home page
reading.html      Reading list page
contact.html      Contact page
newsletter.html   Newsletter signup page
css/style.css     Global + responsive styles
data/reading-list.js  Curated reading list data (edit to add links)
js/newsletter-config.js  Newsletter endpoint (non-secret, https-only) — edit at deploy time
js/newsletter.js  Newsletter signup wiring: credential-free POST + user-safe errors (tested)
js/mailto.js      Pure mailto: URL builder (unit tested)
js/contact.js     Contact form wiring (browser + tests)
js/reading-list.js    Reading list renderer: data file -> grouped HTML (tested)
tests/            Node built-in test suite
.gitea/workflows/ ci.yml runs `npm test` plus a gitleaks secret scan (fails on any hit) on PRs and pushes to main