# Security Notes ## Current safety boundary - **Automated outreach is disabled.** The Compose file sets `AUTOMATED_OUTREACH_ENABLED=false` for both services. The MVP sends no email, SMS, or other outbound communication. - Phase 5 source handling is **registered, approval-gated, and fail-closed**. The current runtime has no live network source or network discovery implementation. CSV and manual reference adapters are operator-supplied only; neither proves a fact or grants contact permission. - Source registry entries must have an accountable terms owner, permitted purpose, approval status/expiry, rate/concurrency limits, retention class, and health/circuit policy before an adapter can be enabled. - Discovery queries must be tenant-scoped, bounded, auditable, and explicit about execution mode. `dry_run` validates/plans without contacting a source or writing prospect facts. No live source may run without recorded product/legal/security approval and explicit operational enablement. - Raw source records are sensitive lineage data: retain only the minimum needed to reproduce a normalized result, under the approved retention class, with tenant/source/query IDs, capture time, adapter version, and redaction metadata. Never store secrets or unnecessary personal/contact data. - Phase 3 intelligence is **manual and provenance-first**. Operators enter child intelligence/evidence records; the platform does not perform automated prospect discovery, DNS resolution, website/HTTP scanning, or external enrichment. - Every business, child record, note, pipeline transition, and audit/activity read or write must be constrained to the authenticated user's organization. A child identifier must never bypass the parent/tenant check. Cross-tenant misses should be indistinguishable from an absent record. - Evidence provenance (source reference/label, captured or observed time, actor, and confidence where supported) is data lineage, not proof that the platform independently verified the source. Do not fabricate provenance or silently upgrade an observation to a verified fact. - List and child-record APIs use bounded pagination and server-side filters. Bounds must be enforced before query execution and filters must be combined with the tenant predicate; never use client-side filtering as an authorization control. - Pipeline and notes are collaboration metadata. A reviewed pipeline state does not authorize contact, and suppression takes precedence over every other state. Audit entries should capture actor, tenant, target, action, timestamp, and safe details without secrets or unnecessary contact data. - No credentials are committed. `.env.example` contains non-secret names and local defaults only. - Authentication uses server-side sessions for browser clients. The session identifier is carried in an `HttpOnly` cookie; logout/revocation must invalidate the server-side session. Health endpoints are deliberately public and must remain usable without a session. - Containers run as an unprivileged user, drop Linux capabilities, use `no-new-privileges`, and use read-only root filesystems. The API data volume is the only intended writable persistent location. - Phase 4 job/live-log controls are not enabled in the current runtime. If added, job IDs, idempotency keys, status, cancellation, retries, and event cursors must all be authorized against the authenticated organization; never accept a job or child identifier as authorization by itself. - Persisted job events must be append-only, sequence-ordered per job, replayable from a cursor, and redacted to safe operational data. Never emit credentials, session cookies, API keys, full request bodies, or unnecessary contact/prospect data in polling responses, SSE frames, logs, or error details. ## Phase 6 normalization and deduplication controls - Treat normalization as a deterministic, versioned derivation, never as identity proof. SA phone normalization must use an explicit `+27` context for local `0` forms and retain the original/display value; location normalization must retain raw input and flag missing or ambiguous province/municipality/city rather than guessing. - Exact duplicate keys and fuzzy suggestions must be reproducible from the same tenant data, normalization version, and algorithm version. Expose the score and reasons, and use `>=0.90` for a strong suggestion, `0.75–0.8999` for a review suggestion, and `<0.75` for no suggestion. Thresholds are triage policy, not permission to merge. - Never auto-merge. Require an authenticated, authorized human to select the survivor and explicitly confirm each merge. Enforce this server-side; a hidden UI control, submitted score, or candidate ID is not authorization. - Create a tenant-scoped merge snapshot before mutation. It must cover the complete parent/child graph, contacts, domains/websites, evidence and provenance, source-record links/raw lineage, notes, pipeline state, and audit context, plus actor/time, algorithm/normalization versions, score/reasons, and conflict decisions. Protect snapshots from ordinary mutation and make them sufficient for reversal. - Preserve child, evidence, provenance, and source-record identity by re-parenting or retaining links; never discard a losing observation or silently overwrite conflicting values. A merge or reversal must be transactional or fail closed. - Audit candidate generation (without leaking cross-tenant data), suggestion/rejection, confirmation, snapshot creation, merge, conflict resolution, and reversal with tenant, actor, target IDs, timestamp, versions, and safe details. Audit reads and snapshots require the same tenant predicate as business reads; cross-tenant candidates are never compared. The MVP provides deterministic match suggestions, an explicit human confirmation dialog, a tenant-scoped merge ledger/snapshot, and reversal routes. These are pilot controls, not a compliance-grade merge system: add a dedicated merge permission, server-verifiable confirmation/re-authentication, complete parent/conflict snapshots, transactional concurrency protection, and tamper-evident retention before production. Do not present derived normalization or a suggestion as verified identity, discovery, enrichment, or outreach permission. ## Phase 7 domain-intelligence controls - Registrable-domain derivation must use a pinned/versioned PSL. Never use a naive last-two-label fallback. Preserve the raw input and return unresolved for unknown/private suffixes, public suffixes, malformed/single-label names, localhost, and IP literals; handle IDN/punycode deterministically and treat subdomains conservatively. - DNS status is not a business fact. Expose `not_checked`, `pending`, `resolved`, `nxdomain`, `no_data`, `timeout`, `servfail`, `blocked`, and `error` separately. A failed, blocked, stale, or empty response must not be upgraded to “no domain,” “no email,” or “unowned.” - MX, NS, and TXT observations require independent provenance, resolver/source, observed time, TTL, truncation/partial and uncertainty/error metadata. MX absence does not prove mail inability; NS does not prove control; TXT does not prove ownership. Treat TXT values as untrusted input and redact secrets where applicable. - DNS caches must be bounded and keyed by normalized name/type/class plus resolver policy and PSL version where relevant. Honor received TTLs without extending authority, expose freshness/staleness, invalidate on policy/PSL changes, and prevent one tenant's cached evidence from being disclosed to another. A cache hit is not a fresh check. - Association confidence is an explainable, versioned review signal separate from DNS status and duplicate score. Candidate generation must enforce tenant scope before comparison, reject public-suffix-only/malformed/IP candidates, flag shared/parked/wildcard/homograph/sibling-subdomain and conflicting-evidence cases, and require authorized human accept/reject decisions. Never auto-attach or infer ownership. - Domain availability must remain `unknown` unless an explicitly authorized provider is registered and checked at execution time for current product/legal/security approval, terms, tenant scope, rate/concurrency limits, retention, and health/circuit state. DNS `nxdomain`/`no_data`, stale cache, timeout, or provider failure is never availability. Fail closed; do not purchase, reserve, contact, or report availability from DNS alone. No Phase 7 resolver, cache, or availability provider is enabled in the current Compose runtime. Before production, add egress/SSRF controls, provider and PSL update review, retention/deletion handling, monitoring, permission/audit coverage, and failure/rollback tests for all domain observations. ## Phase 8 website-scanning controls - Scanning is an authenticated, tenant-scoped observation. Allow only `http` and `https`; reject credentials, unsupported schemes, malformed/localhost/single-label hosts, and disallowed IP literals. Never allow `file:`, `ftp:`, `gopher:`, `data:`, `javascript:`, or equivalent protocol smuggling. - Resolve immediately before connection and validate the actual destination address. Block loopback, private, link-local, multicast, reserved, and cloud-metadata ranges for IPv4 and IPv6. Re-run protocol, hostname, DNS, and IP checks on every redirect and protect against DNS rebinding; do not rely on an initial DNS check or an HTTP `Host` header. - Enforce hard budgets for connect/read/total time, response and decompressed bytes, retained body size, redirects, crawl depth/links, retries, and concurrency. Abort on budget exhaustion. Do not allow compression, redirects, or retries to bypass limits. - Fetch only explicitly allowed content types and links. Never submit forms, send user cookies/credentials/authorization headers, execute JavaScript, run plugins, or make arbitrary subresource requests. Treat fetched content and all TXT/HTML/script text as untrusted input and escape it on display. - Classify conservatively: `unknown`, `blocked`, `partial`, `timeout`, and `error` are not empty success and are not negative business facts. A classification is evidence of bounded content only—not ownership, identity, consent, deliverability, security, or permission to contact. - Persist scan history/cache with tenant isolation, normalized URL, policy/scanner version, redirect policy, observed time, freshness/expiry, applied budgets, and uncertainty/error metadata. Bound size/retention, redact secrets and response bodies, and invalidate/re-evaluate after policy, DNS, or scanner-version changes. A cache hit must be visibly non-fresh. No production-grade scanner egress proxy, isolated worker, or durable scan store is supplied by the current Compose runtime. Before enabling scanning in production, add SSRF/DNS-rebinding/redirect-chain tests, egress deny-by-default policy, abuse/rate controls, authenticated history authorization, retention/deletion, monitoring, and incident procedures. Scans must never cause form submission, acquisition, verification, enrichment, or outreach. ## Phase 9 public official-site contact extraction controls - Extraction is passive and suggestion-only. Scope it to an explicitly approved/public official-site origin and bounded same-site contact/about pages; never use it as general search, unrestricted crawling, identity verification, or enrichment. - Apply the Phase 8 SSRF, redirect, content-type, timeout, byte, page, URL, candidate, and concurrency limits. Fail closed on disabled/unapproved scope, limit exhaustion, partial content, blocked/unsafe destinations, or resolver errors, with an explicit status and uncertainty reason rather than an empty success. - Parse only permitted public HTML/visible contact values and `mailto:` links. Never submit forms, execute JavaScript, send credentials/cookies, probe SMTP or SMTP `VRFY`/`EXPN`, send validation email, or make any outbound contact. Treat HTML, attributes, scripts, and extracted text as untrusted input. - Retain provenance for every candidate: source/page URL and context, extraction method, observed time, extractor/policy version, confidence algorithm/version, and uncertainty reasons. Confidence is a triage signal—not ownership, consent, deliverability, or contact permission. - Keep `syntax_valid`/`syntax_invalid` separate from role classification and free-mail classification. Role/person/unknown and free-mail/business-domain/unknown are labels only. MX/DNS must remain independently uncertain with resolver/source, observed time, TTL/freshness, and explicit `not_checked`, `resolved`, `nxdomain`, `no_data`, `timeout`, `servfail`, `blocked`, or `error` states; no MX outcome proves deliverability. - Exclude asset/file-name addresses, script/style/comment text, example/test/placeholder domains, tracking/telemetry addresses, malformed schemes, and unrelated third-party content. Suppression matching must happen before persistence, response, export, cache, or review queue insertion; a match is immutable do-not-contact until an authorized suppression change, and suppression always wins. - Minimize and protect contact values and lineage. Bound retention for raw/extracted values, provenance, MX/DNS observations, caches, and audit records; redact full addresses and page content from logs where a safe hash/identifier suffices. Tenant predicates and deletion handling apply to every result, cache, export, and audit read. No SMTP probing or outreach capability is permitted by this phase. A public address remains an unverified observation requiring human review and separate future product/legal/security approval before any contact workflow could exist. ## Phase 5 source security controls Source adapters are a security boundary, not a generic fetch facility. Registry review must verify the source identity, terms/robots and licensing owner, permitted collection purpose, approval expiry, tenant scope, rate/concurrency budget, raw-record retention/deletion policy, and circuit thresholds. Keep these controls server-side and auditable; a UI flag or client-supplied source ID is not authorization. - `dry_run` is the safe default: validate a bounded discovery query and produce a plan without network I/O, adapter side effects, or prospect-fact writes. - CSV and manual reference adapters accept operator-supplied material only. Preserve citation/reference, actor, capture time, adapter/version, and normalization lineage; label it as supplied/observed rather than verified. - Raw source records must be access-controlled, tenant-scoped, minimally retained, immutable enough for replay/audit, and redacted for secrets and unnecessary personal data. Apply the approved retention class and deletion schedule. - Enforce per-source request, concurrency, byte, timeout, and retry limits. Rate-limit responses must not be bypassed by rotating identities or silently selecting another source. - Health controls must record success/failure/latency signals and use a circuit breaker with `closed`, `open`, and guarded `half-open` states. Open circuits fail closed, suppress live attempts, and surface a safe deferred/unavailable outcome. - No live network source is allowed without explicit product, legal, and security approval plus operational enablement of the registered adapter. Approval must be checked at execution time and expire safely. If a future approved adapter fetches URLs, apply the SSRF requirements below in addition to source approval. Network discovery is not implemented by this documentation or by the current Compose stack. ## Phase 10 scoring security controls - Treat score, priority, and eligibility as separate security-relevant outputs. A score or priority band is ranking metadata only and must never authorize contact, export, enrichment, or another side effect. - Rule sets must be named, versioned, tenant-scoped, explicitly approved/activated, and immutable once used for a calculation. Store weights, thresholds, required signals, freshness windows, suppression precedence, algorithm version, and deterministic rounding/tie-breaking; do not permit clients to submit or override them. - Make every result reproducible from a tenant-scoped input/evidence snapshot, normalized values, rule-set/version, algorithm/version, and calculation timestamp/freshness context. Explanations must identify contributing factors, points/weights, exclusions, evidence references, and uncertainty/staleness reasons without leaking another tenant's data or unnecessary personal data. - Evaluate eligibility independently and fail closed. Suppression/do-not-contact always yields ineligible and remains visible; stale, expired, missing, blocked, partial, or uncertain required evidence must be explicit and cannot be silently treated as zero, false, or positive. Never let recalculation revive a suppressed value. - Recalculation must be authenticated, authorized, tenant-scoped, idempotent or safely retryable, and auditable. Record actor/job, rule-set and input versions, request reason, start/end, before/after outputs, explanation changes, counts, failures, and partial/incomplete status. Preserve prior results and audit history; do not rewrite history in place. - Protect rule-set, explanation, recalculation, and audit reads with the same organization predicate as business data. Cross-tenant rule IDs, job IDs, evidence references, and business IDs must not disclose existence. Ensure background workers carry tenant context and cannot process an unscoped batch. Phase 10 is not production-ready until rule-set lifecycle permissions/approval, immutable snapshots, audit tamper resistance, retention/deletion policy, concurrency/rollback behavior, and regression tests for suppression precedence, stale/uncertain handling, replay/reproducibility, and tenant isolation are complete. ## Phase 11 dashboard and review workflow security controls - Saved filters are tenant-owned data, not authorization. Validate and bound every predicate server-side; scope list/read/write/delete by the authenticated organization, reject unknown fields and unbounded limits, and keep filter definitions free of secrets. Any shared filter must remain within the tenant with explicit read/write permissions. - Review-queue membership and counts must be derived under the caller's tenant and permission predicate. A count, filter ID, cursor, or selected business ID must never grant access. Suppression/do-not-contact always overrides score, review status, or bulk selection; merged/non-active records are not merge-eligible. - Bulk actions require a server-enforced batch maximum, explicit selection or immutable filter snapshot, preview plus confirmation, authorization and state re-checks at execution, and idempotent/safely retryable behavior. Return bounded per-record outcomes and fail closed on cross-tenant, suppressed, stale, or ineligible records. Bulk review actions must not become an outreach or automatic-merge path. - Clickable counts must preserve the exact tenant-scoped predicate used to calculate them and clearly distinguish page counts from full matching-set counts. Never use a client-side count as a security decision; loading/error/unavailable must not collapse to zero and leak scope or state. - Audit every saved-filter mutation, queue assignment/decision, bulk preview/confirmation/execution, skip/failure, suppression/eligibility decision, and merge/reversal. Record tenant, actor, action, timestamp, filter/selection snapshot or safe hash, bounded counts, per-item outcomes, policy/version, and correlation/idempotency ID. Protect audit reads with the same tenant predicate, redact secrets/full contact values where unnecessary, and define retention/deletion and tamper-evidence before production. Phase 11 is present in the current Compose source with durable saved-filter storage, a bounded review queue, clickable dashboard metadata, and explicit bulk review actions. It is not production-ready: saved-filter update/delete routes are not wired, queue/count responses lack complete predicate and matching-set semantics, bulk actions lack preview/idempotency/per-item outcomes, and audit coverage is aggregate for a batch. Do not infer stronger guarantees from the UI. Before release, add cross-tenant, suppression-precedence, merge-eligibility, count-scope, replay/idempotency, partial-failure, and audit-completeness tests. ## Phase 12 CRM security controls - Treat pipeline state, interaction history, outcomes, reports, exports, and suppressions as tenant data. Enforce `organization_id` on every query, join, cache key, background job, report, and export; cross-tenant identifiers must not disclose existence. - Validate pipeline transitions server-side against the canonical lifecycle `new` → `contacted` → `qualified` → `proposal` → `negotiation` → `won`/`lost`; any paused/disqualified state must be explicitly configured, reasoned, and audited before use or reopening. Reject direct jumps, client-submitted history, edits to historical events, same-record mutations after merge/inactivation, and unaudited state changes. Same-state retries must be idempotent. - Keep interactions append-only and bounded. Record actor, channel, business/contact reference, occurred and recorded times, provenance, safe redacted summary, outcome, and correlation/idempotency lineage. The normalized outcome set is `connected`, `no_answer`, `left_message`, `meeting_booked`, `meeting_held`, `qualified`, `disqualified`, `won`, `lost`, or `other`; `other` must not be treated as success or failure. Corrections append a superseding event and preserve the original; free text is untrusted input and must be size-limited and escaped. - Normalize outcomes into `connected`, `no_answer`, `left_message`, `meeting_booked`, `meeting_held`, `qualified`, `disqualified`, `won`, `lost`, and `other`. Treat `other` as an explicit catch-all, not success or failure. `do_not_contact` is a separate unconditional deny state and cannot be overridden by a later outcome, score, stage, verification, or client payload. - Define report semantics explicitly: bounded date range, timezone, `as_of`, freshness, filter snapshot, latest-state versus event-time aggregation, distinct-business versus event counts, and treatment of suppressed/merged/inactive/unknown records. Page/matching counts are not authorization. Tenant-key report caches and exports, authorize them independently, and redact contact values/free text. - Apply suppression before persistence, response, cache, report eligibility, export, queueing, or any future side effect. Normalize email/domain/phone matching server-side; retain source, reason, scope, actor, and effective timestamps. Unsuppression/removal requires authorization, reason, audit, and re-evaluation. Preserve suppressed records as visible safety state rather than silently deleting them. - Audit every transition, interaction/outcome write or correction, suppression decision/change, report/export request and result, including before/after or bounded result, actor/tenant, time, policy/version, correlation/idempotency ID, and safe reason. Protect audit history from ordinary edits and apply explicit retention, deletion, and legal-hold rules. - Outreach remains prohibited: no send endpoint, SMTP probing, validation mail, campaign, delivery scheduler, automated follow-up, or consent inference. Any future outreach requires separate product/legal/security approval, deny-by-default configuration, rate/abuse controls, suppression re-checks, and independent audit. Phase 12 is not production-ready until transition and outcome invariants, suppression precedence at every boundary, report reproducibility/timezone semantics, export authorization, retention/deletion, idempotent retry, and cross-tenant isolation are covered by integration tests and operational monitoring. ## Known limitations before production 1. **Password storage:** production passwords must be hashed with Argon2id using a reviewed cost/memory/parallelism policy. Never store plaintext or reversible passwords, and never log bootstrap credentials. Rehash on login when the policy changes. 2. **MFA:** require phishing-resistant or TOTP MFA for administrator accounts in production, including the bootstrap admin before granting ongoing administrative access. Define recovery, enrollment, reset, and revocation procedures; do not treat a password-only bootstrap as production-ready. 3. **Authentication and authorization:** enforce authorization server-side on every protected route, including every child-record, note, pipeline, and audit route. Rotate/regenerate sessions at login and privilege changes, expire idle/absolute sessions, revoke on logout/password reset, and test tenant isolation. 4. **Cookies and CSRF:** use `HttpOnly`, `Secure` (production HTTPS), and an appropriate `SameSite` policy. Browser state-changing endpoints require CSRF tokens (or a rigorously reviewed equivalent); do not rely on CORS or cookie flags alone. 5. **SSRF and scanner production hardening:** the Phase 8 scanner is bounded and conservative, but production still requires an egress proxy/isolation boundary, DNS-rebinding and redirect-chain regression tests, deny-by-default network policy, abuse controls, and durable scan-history/cache retention. Allow only `http`/`https`; never submit forms or execute JavaScript. 6. **Input/output safety:** validate schema and content types, bound request and note/evidence sizes, parameterize database queries, escape output, and reject unsafe provenance URLs or markup. Treat operator-entered notes and sources as untrusted data. 7. **Audit and retention:** the current audit/activity behavior is an MVP trail, not an immutable compliance log. Define append-only guarantees, retention, redaction/deletion rules, access controls, alerting, and export procedures before production. 8. **Secrets:** inject production secrets from a secret manager or orchestrator secret store. Do not place them in images, Compose files, source, CI logs, or committed `.env` files. Remove bootstrap variables after first-run provisioning. 9. **Transport and perimeter:** terminate TLS at a trusted ingress, restrict exposed ports, add network policy, and place admin surfaces behind appropriate access controls. 10. **Data protection:** define retention and deletion rules for prospect/contact data and provenance, restrict volume access, encrypt backups, and maintain a tested access/audit trail. 11. **Jobs and idempotency:** require tenant-scoped idempotency keys for side-effecting job creation and bind each key to a request fingerprint; reject conflicting reuse and make retries safe against duplicate side effects. Persist lifecycle transitions transactionally and define lease/timeout/recovery behavior. 12. **Live delivery:** SSE, if introduced, must authenticate before opening the stream, enforce tenant scope on every replay query, bound event/backlog size, support `Last-Event-ID`/cursor replay, send heartbeats, and provide polling fallback. Treat event-stream connections as untrusted clients and avoid cross-tenant timing/detail leaks. 13. **Worker boundary:** the current SQLite/in-process MVP is not durable or horizontally safe. A production worker migration requires reviewed queue semantics, leases, visibility timeouts, dead-letter handling, concurrency limits, cancellation races, metrics, and deployment isolation. Redis/Celery are not implemented today. 14. **Domain intelligence:** implement and security-review PSL pin/update handling, bounded DNS resolution, TTL-aware cache isolation/invalidation, uncertainty-preserving MX/NS/TXT parsing, tenant-scoped association decisions, and an authorized availability provider before exposing any live domain status. No DNS response may authorize acquisition, ownership, outreach, or verification. 15. **Official-site contacts:** production extraction requires approved official-site scoping, false-positive fixtures, syntax/role/free-mail classification tests, explicit MX uncertainty handling, pre-persistence suppression tests, hard limit/retention/deletion controls, provenance/audit coverage, and a permanent prohibition on SMTP probing, validation mail, and outreach. ## Source and contact policy Treat manually supplied business information, notes, and source references as potentially personal or copyrighted data. Collect only what is needed for the documented product purpose, preserve source attribution where required, respect site terms and robots/access policies, and provide suppression/deletion handling. Do not infer consent to contact from public availability or a reviewed pipeline state. Any future outreach or discovery feature requires explicit product/legal and security review and must remain off by default. ## CI/dependency hygiene Pin or review base-image and dependency updates, scan images before release, use least-privilege GitHub tokens, and avoid printing environment values. CI may validate Compose with empty optional bootstrap variables and call unauthenticated health checks; it is not a substitute for authorization/tenant-isolation tests, provenance policy review, Argon2id parameter review, MFA testing, or a security assessment. Report vulnerabilities privately to the repository maintainers; do not include live credentials or personal data in an issue.