← На главную

Security Policy

Truthful, controls-mapped, audit-ready posture for the WECO SPV-disclosure platform.

Effective: 2026-05-11 · Last revised: 2026-05-11 · Applies to release ≥ v1.0.0. Detailed traceability: see dist repo + docs/AUDITOR_TRACEABILITY.md in the release tarball.

How to read this page Every claim is annotated with a status pill: Shipped the control is implemented and verifiable in the release artifact; Partial the control is partly built or under known limitation; Planned the control is on the roadmap with a target quarter; Out of scope the control is not WECO's responsibility (boundary explained). We do not mark a control "Shipped" unless a customer can verify it from `ff-admin-vX.Y.Z.tar.gz`.

1. Threat model

WECO publishes regulatory disclosures for SPVs / SFOs / fund vehicles. The threats we explicitly defend against, in priority order:

  1. Public-site tamper — an attacker modifies a published disclosure (PDF replacement, content edit, defacement). Mitigations: append-only audit log, atomic deploy with rollback dir, per-slug advisory lock, signed releases, optional FS-WORM target for backups.
  2. Admin-VM compromise — credential theft, account takeover, lateral movement. Mitigations: bcrypt cost 12, rate-limited login + account lockout, email 2FA (TOTP — Phase 2), per-host SSH-key encryption, append-only audit, optional KMS for `MASTER_SECRET`.
  3. Supply-chain — backdoored dependency or release. Mitigations: minisign-signed release tarball, SHA-256 chain, public dist repo, SBOM published per release, npm package-lock pinned, install.sh self-checks SHA before running, FF_ALLOW_UNVERIFIED rejected on tagged releases.
  4. License/JWT abuse — pirated or replayed license. Mitigations: Ed25519 signing key in offline laptop (production) / file-mode-600 on issuance server (trial), per-customer JWT with expiry, revocation list (Phase 2), tier-cap enforcement in DB layer.

Threats we explicitly do not defend against — see §6 "Out of scope".

2. Controls — what's actually shipped today

2.1 Authentication

ControlStatusEvidence
Email + bcrypt password (cost 12, min 12 chars)Shippedadmin/scripts/seed-admin.ts, admin/src/lib/auth.ts
Account lockout: 5 attempts → 15-minute lockShippedadmin/src/app/api/auth/password-step/route.ts
Per-IP rate limit: 10 attempts / 15 minShippedadmin/src/lib/rate-limit.ts (in-memory LRU; Redis backend = Phase 2)
Email 2FA (one-time code via SMTP)Shippedadmin/src/lib/twofa.ts
TOTP 2FA (RFC 6238 — authenticator app)Shipped v0.0.19via otplib + AES-GCM-encrypted seeds, recovery codes. FF_TOTP_ONLY=1 (v0.0.30) refuses email-2FA enrollment for AAL2.
WebAuthn / FIDO2 passkeys (AAL3)Shipped v0.0.37via @simplewebauthn/server. userVerification=required for AAL3 phishing-resistance. Roaming security keys + platform authenticators.
Sealed-mode MFA enforcementShipped v0.0.32FF_SEALED_MODE=1 + FF_TOTP_ONLY=1 = no privileged user logs in without TOTP. Grace window via FF_SEALED_MODE_GRACE_DAYS.
Step-up auth on destructive routesShipped v0.0.36FF_REQUIRE_STEP_UP=1. Cookie minted from fresh TOTP, 5-min TTL, scope-aware.
WebAuthn / FIDO2 / hardware keyPlanned 2027Driven by AAL3 customer demand.
SSO — SAML 2.0 SPShipped v0.0.35via @node-saml/node-saml. SP metadata at /api/auth/saml/metadata; ACS, login, JIT user provisioning, group → role map.
SSO — OIDC RP (PKCE S256)Shipped v0.0.35via openid-client. JWKS auto-rotation. State + code-verifier cookie binding.
SCIM 2.0 provisioningShipped v0.0.37RFC 7644 Users + ServiceProviderConfig + ResourceTypes. Joiner/mover/leaver pushed by customer's IdP.
FF_SSO_ONLY sealed modeShipped v0.0.35Local password login refused; only SAML / OIDC / WebAuthn paths mint sessions.

2.2 Authorization

ControlStatusEvidence
Two roles: superadmin / adminShippedadmin/prisma/schema.prismarole String @default("admin")
License-tier caps enforced at DB-write timeShippedadmin/src/lib/license.ts → requireCapacity()
Per-site / per-host RBAC grantsShipped v0.0.21UserHostGrant + UserSiteGrant + requireSiteAccess() / requireHostAccess() guards in lib/grant-guard.ts. CI-enforced: every protected route opens with the guard.
4-eyes JIT for destructive actionsShipped v0.0.38FF_JIT_REQUIRED=1. Different superadmin must approve site.delete / host.rotate_credentials / self-update. Single-use, time-boxed.
Break-glass accountShipped v0.0.36npm run break-glass:create. Random password + TOTP secret + 10 recovery codes printed ONCE for sealed-envelope storage.

2.3 Encryption at rest

ControlStatusEvidence
AES-256-GCM with AAD binding on all sensitive columns (host SSH keys, host bearers, 2FA seeds)Shippedadmin/src/lib/crypto.ts (encrypt/decrypt API). CI lint npm run check:security rejects encrypt-calls without AAD.
Master key (MASTER_SECRET) — pluggable provider chainShipped v0.0.235 providers: file:, env-cmd:, vault: (HashiCorp Vault Transit), aws-kms:, gcp-kms:. Default file: for dev. FF_KEY_LOCAL_FORBID=1 (v0.0.34) refuses file:/env-cmd: in production. Master-secret rotation CLI: npm run rotate:master-secret (v0.0.34).
Audit chain HMAC + Ed25519 witnessShipped v0.0.30 / v0.0.31 / v0.0.32Every AuditEvent row HMAC-chained to its predecessor + Ed25519-signed by a witness key whose private half lives DISJOINT from FF_AUDIT_SECRET. Verifier: npm run audit:verify. Independent off-system witness via FF_AUDIT_SIEM_URL with back-pressure queue (v0.0.36).
Backup encryption with separate KEKShipped v0.0.28 / v0.0.34AES-256-GCM, scrypt-derived. Refuses BACKUP_PASSPHRASE == MASTER_SECRET (v0.0.34) so a single-secret leak cannot decrypt both DB and backup.
Full-disk encryption (LUKS / BitLocker)Customer responsibilitySet at VM creation; install.sh does not touch disk encryption. Documented in §3 of the Customer Deployment Guide.

2.4 Encryption in transit

ControlStatusEvidence
TLS 1.2+ on admin nginx (Mozilla intermediate config)Shippedadmin/nginx/admin.conf + Let's Encrypt
Admin → host: SSH local port-forward; host-api binds 127.0.0.1 onlyShippedadmin/src/lib/tunnel.ts
HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-PolicyShippednginx vhost templates
Full-disk encryption gate (refuse install if LUKS not detected)Shipped v0.0.34install.sh:check_disk_encryption blocks install on bare VMs without LUKS. Override: FF_ALLOW_UNENCRYPTED=1 (dev only) or FF_DISK_ENCRYPTION=cloud-managed.
Mandatory HTTPS gateShipped v0.0.20Stage 2: HTTPS is mandatory. --https-mode=letsencrypt|selfsigned|provided|none. none is dev-only.
Cloudflare WAF integration (customer-side)Shipped v0.0.33install.sh --cloudflare writes nginx real_ip_header CF-Connecting-IP + Cloudflare IP allowlist. Customer signs the Cloudflare contract.
Egress allowlist (iptables)Shipped v0.0.36scripts/install-egress-allowlist.sh: outbound restricted to KMS / SIEM / GitHub / DNS / NTP / apt mirror.

2.5 Audit and integrity

ControlStatusEvidence
Append-only audit log: every mutating action recorded with actor, target, IP, timestamp, metadataShippedadmin/src/lib/audit.ts · table AuditEvent
Audit-export — HMAC chain + Ed25519 witnessShipped v0.0.30-v0.0.32Every row HMAC-chained + Ed25519-signed. Verifier: npm run audit:verify --witness-pub PATH. Tamper detection: any DELETE / UPDATE breaks the chain at first failure.
Anomaly detection rulesShipped v0.0.37Built-in: failed-login burst (10/60s), bearer rotation storm (5/5min), off-hours deploy. Forwarded to SIEM as anomaly.* events.
Evidence pack (compliance binder export)Shipped v0.0.34npm run evidence:pack produces a tar.gz with audit-verify-report, audit-export-30d, SBOM, env-redacted, controls.md, threat-model. SOC-2-style ready for auditor handoff.
ClamAV upload scan + Ghostscript flattenShipped v0.0.38Every PDF scanned + re-rendered through gs -dSAFER on upload. Drops embedded JS / forms / file attachments. FF_REQUIRE_AV=1 (sealed mode) makes a virus hit a hard 422.
WORM / immutable backend for audit storagePlanned 2027Customer can mount a WORM target (S3 Object Lock, Azure Immutable Blob) for the audit table dumps; out-of-the-box integration is on the 2027 roadmap.
Append-only host-api audit at /var/log/ff-host-audit.jsonlShippedvms/host-api/src/audit.ts — never logs tokens, request bodies, or file bytes.

2.6 Supply chain

ControlStatusEvidence
Minisign-signed release tarballShippedEvery release has .tar.gz.minisig. Public key baked into install.sh.
SHA-256 of tarball + install.sh self-checkShippedadmin/install.sh:self_check() — refuses to run on hash mismatch. FF_ALLOW_UNVERIFIED=1 is rejected on tagged releases.
SBOM (CycloneDX) per releaseShipped from v0.0.18ff-admin-vX.Y.Z.sbom.json as a release asset; also at SBOM.cdx.json inside the tarball.
CI workflows shipped inside release tarballShipped from v0.0.18.github/workflows/ included so licensee can verify our security-invariants lint, schema-drift check, and release pipeline.
Dependabot / Snyk / npm-audit gatingPartialnpm-audit runs in CI from v0.0.18. Dependabot enabled on antonorlov888/ff_webpage_constuctor_admin. Snyk integration = Phase 2.
Source snapshot pushed to public dist repo per releaseShippedgithub.com/antonorlov888/orox_siteconstuctor_admin-dist — orphan branch snapshot-vX.Y.Z per release.

2.7 Install-time outbound network calls — full disclosure

The installer makes the following outbound HTTPS calls during initial install only (zero at runtime). Each is replaceable in the air-gap variant (Phase 2 Q3 2026) or via operator allowlisting:

EndpointPurposeMitigation
github.com/antonorlov888/orox_siteconstuctor_admin-dist/releases/download/<tag>/...Fetch signed release tarball + install.shReplace with internal mirror; verify SHA-256 + minisign before extract.
deb.nodesource.com/setup_20.x + deb.nodesource.com/node_20.x/...Install Node 20 from NodeSource apt repoPin to a customer-controlled apt mirror via FF_NODE_APT_REPO=...; or air-gap mode bundles Node binary directly.
acme-v02.api.letsencrypt.org + acme-staging-v02.api.letsencrypt.orgACME challenge for HTTPS certReplace with internal ACME server (Smallstep CA, Boulder, step-ca) via FF_ACME_SERVER=...; or use --no-tls for air-gap with operator-supplied certs.
api.ipify.orgDiscover public IP for ACME challengeRemoved in v0.0.18 — replaced with VPS metadata (cloud-init / DigitalOcean / Hetzner / Yandex) or operator-supplied FF_PUBLIC_IP=....
archive.ubuntu.com (apt deps: postgres, nginx, certbot, etc.)OS package installInternal apt mirror; air-gap mode bundles deb packages directly.

No outbound calls at runtime. No telemetry, no auto-update, no analytics, no error reporting. Verifiable by inspecting the running VM with tcpdump -i any -nn after install completes.

3. Vulnerability disclosure

Send a description + reproduction steps + (if you have one) PoC to anton.orlov@nexaxt.com with subject [WECO-SECURITY]. Encrypt sensitive details with our PGP key (fingerprint published at /.well-known/pgp-key.asc from v0.0.18).

Our commitments to a reporter:

Bounty programme is in scope for Phase 2 (Q4 2026).

4. Audit trail of security claims

Every claim on this page maps to a file or table in the release tarball. To verify independently:

# Download
curl -LO https://github.com/antonorlov888/orox_siteconstuctor_admin-dist/releases/download/v0.0.18/ff-admin-v0.0.18.tar.gz
curl -LO https://github.com/antonorlov888/orox_siteconstuctor_admin-dist/releases/download/v0.0.18/ff-admin-v0.0.18.tar.gz.minisig
curl -LO https://github.com/antonorlov888/orox_siteconstuctor_admin-dist/releases/download/v0.0.18/ff-admin-v0.0.18.sbom.json

# Verify provenance
minisign -V -P 'RWQHQhnHIdSeftw6PLpDHYqFQRzDvTslvKw5mluIjFjyYfwumLT+9siX' \
         -m ff-admin-v0.0.18.tar.gz

# Verify SBOM (CycloneDX)
cat ff-admin-v0.0.18.sbom.json | jq '.components | length'

# Inspect security posture and CI
tar -xzf ff-admin-v0.0.18.tar.gz
cd ff-admin-v0.0.18
cat SECURITY.md          # this document, source of truth
cat LICENSE              # FSL 1.1 / Apache 2.0 future
cat EULA.md              # operational terms
ls .github/workflows/    # CI configs
cat admin/scripts/check-aad-binding.ts   # the AAD-binding lint that gates encrypt() calls

# Run the AAD-binding lint yourself
cd admin && npm ci && npm run check:security

5. Compliance posture

WECO is not independently certified at SOC 2, ISO 27001, or PCI-DSS as of 2026-04-25. We are realistic about this — a sole-developer, source-available product cannot ship pre-certified, and we do not claim what we do not have.

We are a viable building block for customers who hold their own SOC 2 / ISO 27001 / regulator certifications and need a compliance-publishing layer they can install inside their certified perimeter. The audit-export, append-only audit log, signed releases, and on-prem-only architecture are designed to map onto the customer's existing control framework.

For customers who require a vendor-side certification before procurement: SOC 2 Type II is on our 2027 roadmap, conditional on revenue. Until then, the alternative is the standard Vendor Security Questionnaire — we respond within 5 business days with citations into the source tree.

6. Out of scope (boundary statements)

The following are customer responsibilities, documented in §3 of the Customer Deployment Guide. We mark them out of scope here for honesty, not deflection — every WECO contract makes these explicit.

7. What this product does NOT do

For procurement-side honesty:

8. Contact

Security disclosures: anton.orlov@nexaxt.com · subject [WECO-SECURITY].

This document tracks tag-aligned changes in the dev repo at SECURITY.md. The web copy is updated within 7 calendar days of each release.