-
Notifications
You must be signed in to change notification settings - Fork 0
Security
Harpyx is designed to host sensitive customer documents, so security is treated as a cross-cutting concern — authentication, authorization, upload validation, malware scanning, encryption at rest, secret handling, and audit are all first-class subsystems rather than afterthoughts.
This page focuses on the content-security and secret-handling concerns. For the identity and role model, see Authentication and Authorization.
For operational database and object-storage encryption setup, including SQL Server TDE, SQL TLS, MinIO/object-storage encryption, and backup handling, see Data Encryption. For EU data-protection governance, hosting-region choices, subprocessors, DPIA, retention, and breach-response planning, see GDPR Compliance.
Every uploaded file passes through a three-stage gate before it is accepted:
Validates extension and MIME type against UploadSecurity:AllowedExtensions / UploadSecurity:AllowedContentTypes in appsettings.json. Unexpected formats are rejected immediately, before any bytes are persisted. The allowlist is intentionally explicit rather than using a denylist — new formats need an explicit opt-in.
When MalwareScan:Enabled=true, uploads are streamed through ClamAV's clamd over TCP (MalwareScan:Host:Port). The scanner runs in fail-closed mode by default — if the scanner is unreachable or times out (MalwareScan:TimeoutSeconds), the upload is rejected. Administrators who need fail-open behavior in non-production must flip MalwareScan:FailClosed explicitly.
Scan outcomes:
| Outcome | Document state | Behavior |
|---|---|---|
| Clean | Pending |
Proceeds to ingestion as usual |
| Infected | Quarantined |
Enqueued job is skipped with job_skipped_security
|
| Scanner unreachable + fail-closed | Rejected |
Upload rejected outright, never persisted |
First container startup can take longer while ClamAV downloads virus signatures; subsequent scans are fast.
The WebApp reads uploads as streams and honors ASP.NET Core's request-size limits. URL-based ingestion (UrlFetcherService) additionally enforces UrlFetch:TimeoutSeconds, UrlFetch:MaxContentBytes, and UrlFetch:AllowHttp (default false) to prevent runaway fetches or accidental plain-HTTP egress.
Harpyx uses layered encryption controls. Application-level encryption protects selected secrets, while database, object-storage, disk, and backup encryption are operational controls configured on the infrastructure. The deployment checklist for those controls lives in Data Encryption.
AesEncryptionService encrypts BYO and hosted LLM credentials using AES-256-GCM before persistence. The master key is read from Encryption:MasterKey (a base64-encoded 32-byte key) and stored only in process memory. Rotating the master key requires re-encrypting existing LlmConnection rows that contain encrypted API keys.
The plaintext key is never logged, never serialized to telemetry, and never returned from the API to clients.
Cookies and antiforgery tokens are protected by ASP.NET Data Protection. In production, keys are persisted to the filesystem under DataProtection:KeysPath (default App_Data/keys). For multi-instance deployments, this path should point to a shared volume so all instances decrypt each other's cookies. DataProtection:ApplicationName (Harpyx) ensures key rings are not accidentally shared across applications.
-
No secrets in source control.
.envis in.gitignore;.env.exampleis the template. - Uniform override chain. Secrets enter through environment variables in dev and Azure Key Vault in production, hitting the same configuration keys — see Configuration.
-
Admin bootstrap is explicit.
SeedOptions:AdminUsersseeds known platform admins by provider-specific stable id; keep this list small and manage day-to-day role changes through the admin UI.
All security-relevant events are persisted as append-only AuditEvent rows via IAuditService.RecordAsync. Current event types:
| Event | Fired on |
|---|---|
login |
Successful authentication |
access_denied |
Authenticated user not on the allowlist |
upload |
File accepted by the upload pipeline |
job_start |
Worker picks up a ParseDocumentJob
|
job_end |
Job completes successfully |
job_error |
Job fails with an exception |
job_skipped_security |
Job bypassed because the document was quarantined or rejected |
Because every subsystem funnels through IAuditService, the audit trail is uniform across WebApp and Worker.
Configured in AddHarpyxWebAppServices and the pipeline extensions:
-
Forwarded headers —
ForwardedHeaders.XForwardedFor | XForwardedProtowithKnownProxies/KnownNetworksallowlists.ReverseProxy:ForwardLimitprevents spoofed chains. -
CSRF —
AutoValidateAntiforgeryTokenAttributeapplied globally to controllers. -
Rate limiting — two fixed-window policies:
api(120 req/min per IP) andauth(12 req/min per IP). Responses includeRetry-Afterheaders; rejections return RFC 7807 Problem Details. -
Problem Details — all error responses carry
traceIdto correlate user-visible errors with the server-side trace.
Harpyx is designed against:
- Untrusted upload content (malicious files, zip bombs, malformed PDFs) — mitigated by ClamAV, extension/MIME allowlists, library-level hardening (PdfPig, OpenXml, SharpCompress), and size/timeout limits.
- Opportunistic cookie theft — mitigated by Data Protection, cookie hardening defaults, and CSRF.
- Rate-limit / brute-force attacks against auth endpoints — mitigated by the
authrate-limit policy. - Accidental secret exposure — mitigated by the override chain and the zero-secret
.env.example. - Insider access beyond tenant scope — mitigated by
ITenantScopeService+ allowlist + audit.
It does not defend against a compromised host running the WebApp/Worker processes: a root-level attacker on the host has access to the Data Protection keys and the in-memory encryption master key. Defending against that level of threat requires host-level controls (hardware TPMs, HSM-backed Key Vault, confidential computing) outside Harpyx's scope.