Changeset 3dd99d3 in Klonkt for src/routes/posts.js


Ignore:
Timestamp:
06/25/2026 09:52:25 AM (3 months ago)
Author:
Robin Genis <roboburr@…>
Branches:
main
Children:
c648b04
Parents:
4b5223f
Message:

harden(fediverse): SSRF guard, remote-URL XSS scheme-guard, scoped Delete, gate-by-default + queue fixes

From a 3-agent hardening review of this session's fediverse code:

  • SSRF: all outbound fetches (deliver/fetchActor/webfingerResolve) now go through safeFetch — http(s)-only, rejects hosts resolving to private/loopback/link-local ranges on the initial host AND every redirect hop (redirect:manual), + actor-doc size cap. Blocks inbox-driven SSRF to cloud-metadata/internal services.
  • Stored XSS: remote actor url/icon, timeline media + author urls, and remote-note images/object_uri are now run through an http(s) scheme-guard before storage, so a malicious actor can't smuggle javascript:/data: into owner-only-rendered href/src.
  • Cross-actor Delete: inbound Delete is now scoped to the signing actor (can't wipe another actor's replies/timeline rows).
  • Gate-by-default: Add/Remove/Update added to the signature-enforced activity list.
  • Delivery queue: re-entrancy guard (30 rows x 8s can exceed the 60s tick -> no double-delivery) + backoff off-by-one fix (1-min first retry no longer skipped).
  • Scheduler: delete-before-insert on FTS so a re-flipped post has no duplicate row.
  • /meldingen: don't mark-seen for a viewer (GET-side mutation the global guard misses).
  • Activity ids get a random suffix to avoid same-millisecond collisions.

Co-Authored-By: Claude <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/routes/posts.js

    r4b5223f r3dd99d3  
    77import ejs from 'ejs';
    88import db from '../config/database.js';
    9 import { requireAuth, requireSiteManager } from '../middleware/auth.js';
     9import { requireAuth, requireSiteManager, isViewer } from '../middleware/auth.js';
    1010import { renderPage } from '../middleware/render.js';
    1111import { recordPageview, recordPostView } from '../services/StatsService.js';
     
    591591  const site = res.locals.site;
    592592  const items = site ? ActivityPubService.getNotifications(site.slug, 80) : [];
    593   if (site) ActivityPubService.markNotificationsSeen(site.slug); // viewing = seen → clears the bell badge
     593  // viewing = seen → clears the bell badge. A viewer (kijker) may look but must not
     594  // mutate state (the global write-guard only catches non-GET, not this GET-side effect).
     595  if (site && !isViewer(req.session.user)) ActivityPubService.markNotificationsSeen(site.slug);
    594596  renderPage(req, res, 'pages/fedi-notifications', { pageTitle: 'Meldingen', bodyClass: 'on-special', items });
    595597});
Note: See TracChangeset for help on using the changeset viewer.