Changeset 3dd99d3 in Klonkt for src/services/Scheduler.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/services/Scheduler.js

    r4b5223f r3dd99d3  
    2424      "UPDATE posts SET status = 'published', published_at = COALESCE(published_at, publish_at, CURRENT_TIMESTAMP) WHERE id = ?"
    2525    );
     26    const ftsDel = db.prepare('DELETE FROM posts_fts WHERE post_id = ?');
    2627    const fts = db.prepare('INSERT INTO posts_fts(content, title, author, post_id) VALUES (?, ?, ?, ?)');
    2728    const siteStmt = db.prepare('SELECT * FROM sites WHERE id = ?');
    2829    for (const p of due) {
    2930      upd.run(p.id);
    30       try { fts.run(HtmlSanitizerService.toPlainText(p.content || ''), p.title || '', p.username || '', p.id); } catch { /* FTS failure is non-fatal */ }
     31      // Delete-before-insert so a re-scheduled (previously published) post doesn't
     32      // get a duplicate FTS row → duplicate search hits.
     33      try { ftsDel.run(p.id); fts.run(HtmlSanitizerService.toPlainText(p.content || ''), p.title || '', p.username || '', p.id); } catch { /* FTS failure is non-fatal */ }
    3134      // ActivityPub: federate the now-published post to followers.
    3235      if (!p.fan_only) {
Note: See TracChangeset for help on using the changeset viewer.