Files
VacatureRadar/docs/quality/THREAT_MODEL.md

16 KiB

Threat model

Scope en assets

Te beschermen:

  • gebruikersaccount, sessies en zoekvoorkeuren;
  • mailbox-, SMTP-, database- en registrycredentials;
  • sollicitatiedossiers, notities en eventuele documenten;
  • integriteit van vacatureclusters, scores en feedback;
  • beschikbaarheid van de Unraid-host en het thuisnetwerk;
  • bronbeleid en bewijs dat alleen toegestane bronnen worden benaderd.

Niet vertrouwd:

  • iedere vacaturetekst, HTML, JSON-LD, feed en e-mail;
  • alle links en redirectdoelen;
  • externe DNS/HTTP/TLS-responses;
  • AI-modeloutput, ook lokaal;
  • browserinput en handmatige URL's;
  • logs of exports die buiten de applicatie worden geopend.

Trust boundaries

Internet/mailbox
  -> URL/source policy + IMAP boundary
  -> raw document quarantine/retention
  -> parser + sanitizer
  -> normalized domain model
  -> deterministic rules/scoring
  -> authenticated UI / digest

User browser
  -> TLS/reverse proxy
  -> Django session + CSRF + object scoping
  -> database

Django/Celery
  -> Redis (queue, geen waarheid)
  -> PostgreSQL (bron van waarheid)
  -> optionele Ollama boundary

Belangrijkste dreigingen en mitigaties

ID Dreiging Impact Mitigaties Verificatie
T-01 SSRF via bron- of vacature-URL toegang tot LAN, metadata of beheerinterfaces schema/host/IP/poortcontrole, DNS-resolutie, redirecthercontrole, denylist, geen credentials in URL tests/security/test_url_security.py, test_policy.py, fetchertests
T-02 DNS rebinding/tijd-van-controle request bereikt ander IP dan beoordeeld iedere fetch en redirect valideert DNS opnieuw; het werkelijk verbonden peer-IP wordt als publiek gecontroleerd wanneer de transportlaag dit exposeert, anders geldt conservatieve DNS-set-overlap VR-115, tests/unit/test_fetcher.py, aanvullende fetcher negative tests
T-03 XSS/HTML-injectie sessiediefstal of ongewenste navigatie Bleach allowlist, platte tekst, template autoescaping, CSP/securityheaders en uitsluitend lokale externe scripts zonder unsafe-inline tests/security/test_sanitize.py, auth- en viewtests
T-04 Prompt injection in vacature AI verandert beleid of voert tools uit vacaturetekst in data-envelope, schema-output, evidence-validatie, cachefouten met foutcategorie, geen toolrechten, deterministische harde regels, AI optioneel AI-tests, ADR-0003, VR-109 evaluaties
T-05 Malafide/oversized document geheugen/CPU/diskuitputting contenttype-, byte-, timeout- en redirectlimieten; korte retentie fetchertests, source-runmetrics
T-06 Crawlstorm of ban externe overlast, IP-blokkade expliciet allowbeleid, per-origin minimuminterval, concurrencylimiet, leasegebaseerde taakclaim met TTL + crash recovery, conditional requests, backoff, quarantaine tasktests; VR-103, VR-112
T-07 Direct platformscrapen voorwaarden-/blokkaderisico centrale denylist, e-mailingress, geen loginbot/CAPTCHA policytests, ADR-0002/0005
T-08 E-mailparserlink naar phishing/private host ongewenste fetch of gebruiker misleid linkextractie slaat unsubscribe over, follow-up opnieuw door policy/SSRF, bronlabel zichtbaar e-mail- en policytests
T-09 MIME/HTML-bom of malformed mail resource-uitputting/parsercrash IMAPbatchlimiet, bodylimiet bij vervolgtaak, robuuste parser, idempotente recordidentiteit VR-115, e-mailfixtures
T-10 Cross-user objecttoegang lek van sollicitaties/feedback LoginRequired, user-scoped querysets/get_or_create, autorisatietests integratie-viewtests
T-11 CSRF op feedback/profiel ongewenste status- of profielmutatie Django CSRF-middleware, POST-only acties, SameSitecookies viewtests en frameworkcheck
T-12 Credentiallek in Git/logs/database/UI account- of infrastructuurcompromis app-wachtwoorden met MultiFernet en afzonderlijke environmentkey versleuteld; nooit terugtonen; generieke fouttekst; gitignore, secretrotatierunbook tests/unit/test_mailbox_credentials.py, tests/integration/test_mailbox_connections.py, CI-secret scan VR-115
T-13 Onveilige reverse proxy sessie over HTTP, host-header-/originproblemen TLS, exact ALLOWED_HOSTS/CSRF origins, secure cookies, proxychecklist deployment smoke-test
T-14 Redis als waarheid behandeld dataverlies/dubbele verwerking PostgreSQL-state, idempotente taken/outbox, acks-late integratietests
T-15 Dubbele Celery-uitvoering dubbele vacatures/mails unieke keys, transacties, message identity, outbox-uniciteit pipeline/digesttests
T-16 Dedupe voegt verschillende jobs samen gemiste kans/verkeerde data exact vóór fuzzy, conservatieve drempel, bronaliassen/provenance, herstelbare clusterbeslissing gepland dedupetests; VR-116 evaluaties
T-17 AI hallucineert ontbrekend feit foutieve ranking onbekend blijft onbekend, evidence/confidence, geen hard exclusion door AI scoring/AI-tests
T-18 Feedback poisoning/self-reinforcement steeds smallere resultaten begrensde gewichten, opt-in standaard uit, per-feature minimum samples, metadata-logging en resetpad profieltests; VR-110
T-19 Automatisch solliciteren/massamail reputatie/privacy/juridisch risico geen submitendpoint/task, expliciete ADR en non-goal codezoekreview, AC-043
T-20 Kwaadaardig bestand in sollicitatiedossier host/browsercompromis uploads nog niet actief; vóór implementatie allowlist, sniffing, size limit, niet-uitvoerbare opslag vereist VR-113, nieuwe threat review
T-21 Back-up bevat secrets/PII langdurig datalek encryptie, gescheiden opslag, korte retentie, herstelprocedure runbook en hersteltest
T-22 Supply-chainpackage/image code execution exacte versies/lockfile, beperkte dependencies, pinned baseimages/digests, taggestuurde immutable release als enig productiepad, gescheiden package read/write-tokens en SBOM/checksums CI, VR-115, VR-230, VR-231
T-23 Onbevoegde admin op thuisnetwerk volledige datatoegang uniek wachtwoord, TLS/VPN, geen defaultcredentials, sessiebeveiliging Unraidchecklist
T-24 Source terms wijzigen ongewenste voortgezette crawling reviewdatum, source health en policy expiry gepland VR-102, VR-112
T-31 Sollicitatiedossier-export bevat niet-gewenste payload data-lek of onbedoeld dossieroverdragen export bevat alleen het gekozen dossier, ZIP-inhoud is beperkt tot snapshot/tijdlijn/print-HTML, bestandsnaam is geslugified zonder padseparators tests/integration/test_applications.py
T-32 XML external entity/entity-expansion via sitemap lokale data-uitlezing of parseruitputting sitemap-XML wordt met defusedxml verwerkt; fetcherlimieten blijven vóór parsing gelden tests/unit/test_discovery_rss.py, Ruff securitycheck
T-33 Externe UI-CDN of niet-vertrouwde Stitch-interactie supply-chain-XSS, tracking of acties buiten Django-beleid productie gebruikt alleen lokale CSS/JS en inline SVG-symbolen; Stitch-code is documentatiereferentie, alle mutaties blijven Django POST+CSRF; themavoorkeur bevat geen persoonsgegevens en opslagfouten worden afgevangen tests/e2e/test_vr114_browser_and_accessibility.py, browserconsole- en assetcontrole VR-118
T-34 Publieke ATS-feed of robotsredirect wijzigt van host/identiteit SSRF, verkeerd werkgeverslabel, ongewenst sollicitatie-endpoint of voortgezet fetchen na voorwaardenwijziging iedere robotsredirect wordt opnieuw als publieke URL gevalideerd; feeds gebruiken alleen gereviewde GET-endpoints; werkgeveridentiteit komt uit source-config; reviews verlopen fail-closed en apply-endpoints zijn geen fetchbron tests/security/test_robots.py, tests/integration/test_pipeline.py, tests/unit/test_ats_adapters.py, VR-120
T-35 Regionale vacaturehub bevat een gemanipuleerde off-domain kaart of gedeelde ATS-host overschrijft een andere werkgever phishinglink, foutieve herkomst, verlies van bronbeleid of verkeerde inventaris regionale adapter accepteert alleen same-host /job/-links; bronuniciteit gebruikt type + exacte publieke base-URL; per-origin lease en minimuminterval blijven over alle ATS-tenants gedeeld tests/unit/test_regional_adapters.py, tests/unit/test_source_models.py, tests/integration/test_seed_sources.py, VR-121
T-36 Kwaadaardige/oversized platformmail of linkinjectie in een jobalert parseruitputting, batchuitval, phishing, foutieve vacatures of verborgen scraping IMAP-batch is begrensd; foutisolatie en idempotente identity per mailbox; platformparser accepteert alleen gelabelde HTTPS-links op het gekozen jobboarddomein en verwerpt externe/adminlinks; alle brede jobboards blijven op de directe-fetchdenylist tests/integration/test_email_import_notifications.py, tests/unit/test_email_adapter.py, tests/security/test_policy.py, VR-122/123/124/125
T-37 Lokale werkgeverslijst wijzigt markup, injecteert een externe kaart of vermengt functies buiten de regionale scope phishing, foutieve locatie, te brede radar of onbetrouwbare ranking lokale adapter is beperkt tot vijf gereviewde host/padcombinaties, accepteert alleen same-host details en past waar beschikbaar een expliciete Hasselt-filter toe; retire-seeding schakelt buitenregionale bronnen uit zonder provenance te wissen tests/unit/test_regional_adapters.py, tests/integration/test_seed_sources.py, VR-125
T-38 Cross-user mailboxbeheer, dubbele polls of IMAP naar private host credentialmisbruik, duplicaten, toegang tot interne diensten alle views scopen op gebruiker en vereisen CSRF; unieke user+platformkoppeling; transactionele lease; hostvorm en publieke DNS/IP-validatie vóór TLS-IMAP tests/integration/test_mailbox_connections.py, tests/unit/test_mailbox_credentials.py, apps/sources/services/imap_import.py, VR-123
T-39 Mol-/Kempenbron vermengt een andere werkgever, campus of vestiging foutieve afstand, phishing of buitenregionale resultaten exacte HTTPS-host/pad/GUID-routes; same-host details; expliciete plaatsfilters voor regionale en gedeelde lijsten; postcodecentra blijven als benadering herkenbaar; waaklijstbronnen worden niet gescand tests/unit/test_regional_adapters.py, tests/integration/test_tasks_and_more_views.py, VR-126/201
T-40 Verouderde lokale CA-store of ongeldige serverketen lokt een TLS-bypass uit onderschepte vacature- of mailboxdata en credentialdiefstal expliciet gepinde certifi-CA-bundel voor HTTP, robots en IMAP; hostnaamcontrole en CERT_REQUIRED; nooit verify=False; dependency- en negatieve TLS-tests bij update tests/unit/test_tls.py, tests/security/test_robots.py, apps/sources/services/tls.py, VR-201
T-41 Gemodificeerde next-parameter na vacaturefeedback open redirect naar phishingdomein na een geldige gebruikersactie terugkeer-URL moet dezelfde host en het actuele HTTPS-beleid respecteren; anders veilige detailfallback tests/integration/test_tasks_and_more_views.py, VR-206
T-42 Gespoofte proxyheaders of ontbrekende publieke hostconfiguratie host-headerfouten, onjuiste HTTPS-detectie of omzeilde IP-rate-limiting PUBLIC_BASE_URL, exacte host/CSRF-validatie, proxy moet headers overschrijven, forwarded client-IP alleen vanaf TRUSTED_PROXY_CIDRS tests/unit/test_public_configuration.py, tests/unit/test_network.py, VR-225
T-43 Gedeeld demoaccount wijzigt persistente data of activeert echte mailboxpolling bezoekers beïnvloeden elkaars ervaring, echte data of externe accounts server-side read-only middleware, korte sessie, geen bruikbaar wachtwoord, uitsluitend fictieve example.test-mailboxen; de loginseed herstelt begrensd en idempotent en de scheduler sluit de demo-user expliciet uit tests/integration/test_auth_login.py, tests/integration/test_tasks_and_more_views.py, tests/unit/test_settings_security.py, VR-225/229

Misbruikscenario's

Vacature zegt “negeer eerdere instructies”

De tekst wordt gesanitized en als veldwaarde doorgegeven. Een eventuele AI-call krijgt een vast schema en geen tools. De output kan geen bron activeren, URL openen, harde regel verwijderen, mail sturen of sollicitatie indienen.

Publieke pagina redirect naar 192.168.1.1

De redirectlocatie wordt opnieuw gevalideerd. Het request naar het private adres wordt niet uitgevoerd; de bronrun krijgt een policy/securityfout en de bron kan in quarantaine gaan.

De adapter gebruikt de gekozen platformidentiteit, accepteert alleen gelabelde HTTPS-links op dat jobboarddomein en negeert externe tracking-, account-, privacy- en uitschrijflinks. De platformlink wordt hoogstens als alias opgeslagen. Automatisch volgen vereist opnieuw een toegestane source policy; denylistplatformen worden geweigerd.

Aanvaller laat scheduler dezelfde job herhalen

Canonieke sleutels, databaseconstraints, transacties, idempotente message identity en outboxuniciteit beperken duplicaten. Taken zijn ontworpen voor at-least-once in plaats van exact-once aannames.

Security gates bij nieuwe functionaliteit

Een wijziging aan netwerk, mail, AI, rendering, auth, exports of uploads vereist:

  1. nieuwe/gewijzigde threat in deze tabel;
  2. negatieve test die de aanval representeert;
  3. begrensde input/output en foutcategorie;
  4. logreview op secrets/PII;
  5. runbook- en configuratie-update;
  6. volledige codex_verify-gate.

Resterende risico's

  • De productiefetcher blokkeert fail-closed wanneer httpcore het werkelijk verbonden peer-IP niet exposeert en weigert ieder niet-publiek peer-IP. Alleen een expliciet geïnjecteerde test/custom client mag terugvallen op DNS-set-overlap; die clientgrens mag daarom niet voor productie-fetches worden gebruikt.

  • Brute-force of herhaalde foutieve mutatiepogingen blijven mogelijk, maar zijn in productie begrensd via gedeelde Redis-rate-limiting en blokkades op login/manual-import. Correcte individuele client-IPherkenning vereist het exacte proxy-CIDR.

  • Bronvoorwaarden vereisen menselijke/externe review per domein; automatisering kan dat niet juridisch beslissen.

  • Fuzzy dedupe en featureheuristieken kunnen inhoudelijk verkeerd zijn; provenance, feedback en benchmarkevaluaties beperken maar elimineren dit niet.

  • Een persoonlijke Unraid-host blijft afhankelijk van correcte netwerksegmentatie, patching en back-ups door de beheerder.

  • T-25: Verloren of verlopen policyreview

    • Impact: bron kan op onbedoelde wijze doorgaan met crawlen
    • Mitigatie: SourcePolicyReview met decision/expiry + policy gate in assess_url
    • Verificatie: security/integration tests rond verlopen review in fail-closed gedrag
  • T-26: Handmatige URL/tekstimport

    • Impact: ongewild ophalen van geblokkeerde of niet-ingesteste informatie, XSS via ingevulde tekst of overschrijding van limieten
    • Mitigatie: CSRF-only import endpoint, allowlist-domeinvrije policy checks, sanitize op tekstveld, byte-limiet en max-size/timeout gedrag
    • Verificatie: integration/security tests rond rejectie van geblokkeerde URL's en overschrijding van paste-limiet
  • T-27: Reminder-flood of dubbele reminder-mails

    • Impact: gebruiker ontvangt herhaalde of ongewenste herinneringen
    • Mitigatie: stabiele dedupe_key, idempotente outbox met statusmachine, reminder_type-types en task-driven verzending
    • Verificatie: tests/integration/test_reminders.py
  • T-28: Onterecht verzenden zonder ontvanger/uitgeschakelde reminderinstelling

    • Impact: onnodige fout- of testmails, verwarring over afmeldgedrag
    • Mitigatie: master- en topmatchschakelaar per profiel, SKIPPED-status bij lege ontvanger en foutlogica met retries op taakniveau
    • Verificatie: tests/integration/test_reminders.py
  • T-29: Bronhealth zet bronnen te vroeg in quarantaine bij incidentele netwerkruis

    • Impact: tijdelijke uitval of incidentele bronfouten blokkeren een gezonde bron
    • Mitigatie: duidelijke foutcategoriedrempels, gescheiden policy/security- en tijdelijke-foutlogica, en canaryherstel via trialpad
    • Verificatie: tests/integration/test_source_health.py
  • T-30: Geautomatiseerde canaryherstel blijft onbegrensd draaien

    • Impact: herhaalde herstarts van dezelfde bron veroorzaken extra belasting
    • Mitigatie: cooldown plus metadataflag voor eenmalige canary, en trialstatus met beperkte runtrigger
    • Verificatie: tests/integration/test_source_health.py

Microsoft 365 OAuth

  • Exchange Online gebruikt app-only XOAUTH2; Basic Auth en app-wachtwoorden zijn uitgesloten.
  • Client secrets leven alleen in de productie-secretstore en tokenfouten bevatten geen token of secret.
  • De serviceprincipal krijgt uitsluitend IMAP/SMTP-toegang tot de bedoelde mailbox; IMAP-selectie blijft read-only.