Hardening j7: an efficiency + security audit log

Hardening j7: an efficiency + security audit log

logo dev3lop ·
  • #engineering
  • #security
  • #performance

We ran a three-pass audit of the j7 codebase — one sweep each for the backend, the web/SEO layer, and data integrity — then shipped every fix the same day. This is the engineering log.

The one that mattered: a stored XSS

Our inline-markdown renderer escaped &, <, and > before turning [text](url) into a link — but not the double-quote. A crafted URL could close the href attribute and inject an event handler that runs in a reader's session. Because posts have a collaborator invite flow, this was a genuine stored XSS, not merely self-XSS. The fix: escape quotes before applying markup and exclude quotes from the URL match — in both the Astro and React renderers, which stay in lockstep.

Highs — one request should never be able to hurt the box

j7's backend runs on a deliberately tiny box, so the bar is simple: no single request may pin the CPU or exhaust memory.

  • Image uploads reject over-40MP inputs before decode (a small-on-disk decompression bomb used to expand to over a gigabyte of raw pixels) and optimize one at a time.
  • A hard 16MB request-body ceiling, an error-net so a stray throw can't crash a path, and a SQLite busy-timeout instead of instant lock errors.
  • The public compute-heavy endpoints (leaderboard, stats) are cached; the on-demand social-card renderer sheds load past a concurrency cap.
  • Owner lookups moved from scanning every space to an indexed column, and 'where is this image used' is now owner-scoped and auth-gated.

Before / after

AreaBeforeAfter
Image uploaddecode bombs could OOM40MP cap + serialized
Leaderboard / statsfull scan every hitcached (5m / 2m)
Owner lookupscan all spaces in JSindexed column search
File servingsync full-file bufferstreamed byte-range
Table / list sizeunboundedcapped at save time

Content is now bounded

Every block that holds a collection — table rows, list items, schedule events — now has a size cap enforced at save time, and event colors are validated to a hex value so they can't be used as a CSS-injection vector. A malformed or oversized draft is rejected before it can bloat storage or every future page render.

The quiet efficiency wins

  • Space resolution is cached, so a cold-cache render stops re-asking the backend who owns the host.
  • A/B-experiment lookups are negative-cached for 60s — most posts have no experiment, so that per-view round-trip is gone.
  • Every cross-service fetch now has a timeout, so a hung backend can't stall a page render forever.
  • The editor's crash-recovery backstop no longer serializes the whole document to disk on every keystroke.

None of this changes what j7 does. It changes how much it can take before it flinches — which is the whole point of a hardening pass. Onward.


j7, by dev3lopcom, llc