Skip to content
ERP Preflight

Data handling in detail

How uploads are checked, scanned, redacted, stored per tenant and deleted — the technical controls behind the security overview.

Content reviewed September 26, 2026

Tenant isolation in the database

Every organization is a tenant. Tenant tables carry the organization id and have PostgreSQL row-level security enabled and forced. For each request the API sets the verified organization as app.current_tenant_id on the database transaction, and the policies only return and accept rows of that organization. In production, tenant transactions run under a database role without superuser or BYPASSRLS rights, so the policies apply even when the connection user owns the tables. Application queries filter by organization in addition.

The analysis service has no access to the user, billing or tenant tables; see Security & data handling.

Storage scoping

Files are kept in S3-compatible object storage under keys per organization and project: tenants/{organizationId}/projects/{projectId}/…. Files uploaded through a pre-signed link are held in a separate quarantine bucket until they are checked; direct uploads are checked in memory. Only files that pass every check are written to the clean bucket. Generated reports are stored under the same organization and project prefix. Pre-signed upload and download links expire after at most 15 minutes, and download links for artifacts are only issued for CLEAN files.

Upload checks

Every upload — from the web app, the API or the CLI — passes the same pipeline:

  1. Content check: the file extension selects the expected format and the content has to match it: the ZIP signature, an XML declaration or root element (XML with entity declarations or an external DTD is rejected), valid JSON, the XDP namespace, the PDF header; CSV, ABAP and text files must not contain binary null bytes. Files that begin with an executable header (Windows PE, ELF, Mach-O, Java class) are rejected whatever their extension.
  2. Archive check: a ZIP archive is rejected if it has more than 10 000 entries, an entry larger than 250 MB, more than 500 MB uncompressed in total, a compression ratio above 100:1 (checked from 10 MB uncompressed) or entry paths that are absolute or contain ../. When the analysis service opens an archive, it enforces the ratio and size limits on the bytes actually decompressed and allows at most 2 levels of nested archives.
  3. Malware scan and 4. secret redaction, described below.

The API accepts the extensions zip, xml, wsdl, edmx, xsd, xdp, json, csv, abap, txt, prog, incl and pdf, up to 100 MB per file unless the operator set another limit; the Artifact Dropzone of a project offers .xml, .json, .csv, .zip and .abap. The SHA-256 checksum of each file is recorded with it.

Malware scanning and quarantine

Every file is scanned with ClamAV before it can be used. Infected files are marked QUARANTINED and never reach the clean bucket. The scan fails closed: if the scanner cannot be reached, times out or gives an unexpected answer, the file is quarantined as well. Files that fail the content or archive checks are marked REJECTED.

Only CLEAN files can be analysed or downloaded. In production, scanning with ClamAV is the default; a mock scanner exists for development and has to be switched on explicitly.

Secret redaction

Before a text file is written to the clean bucket, secrets in it are replaced by masks of the form [REDACTED:SECRET:<hash>]. This applies to XML, XSD, WSDL, EDMX, XDP, JSON, CSV, ABAP and plain-text sources (.txt, .prog, .incl). Once a file has been promoted, an unredacted copy in the quarantine bucket is deleted.

  • Detection combines patterns — private keys, JSON Web Tokens, Authorization, cookie and other sensitive HTTP headers, credentials in URLs, query strings and connection strings, SAP RFC and SAProuter passwords, AWS, GitHub and OpenAI keys, generic API keys — with an entropy scan for random-looking tokens. Common SAP table and ABAP names are exempt.
  • The mask is an HMAC with a key per organization: the same secret yields the same mask within your organization and a different one in any other.
  • The number of redactions is stored with the file and reported for the upload.
  • ZIP archives and PDF files are stored as uploaded once they pass the checks; their content is not rewritten. Independently of the file type, the analysis service redacts every evidence snippet before a finding leaves the service.
  • The local agent redacts on your own host before anything is sent.

Retention and deletion

Organization owners and security admins set retention under Settings → Data & reports:

  • Artifacts: kept until the project is deleted (default), deleted right after the analysis that used them (uploads that are never analysed are removed after 24 hours), or deleted after a number of days.
  • Reports: kept (default) or deleted after a number of days.

The maximum number of days depends on your plan. A background job applies the policies every hour by default; owners and security admins can also start a purge right away (POST /api/v1/retention/purge). Objects are deleted from storage first, then their database rows, and every purge is recorded in the audit log. Findings and evidence hashes remain for traceability. While a legal hold is set for the organization, no purge runs.

Exporting personal data and deleting an account are described under Security & data handling.

Encryption of stored credentials

Credentials that ERP Preflight has to use later — connector credentials, the client secret of your identity provider and webhook signing secrets — are encrypted with AES-256-GCM before they are stored; the key is derived per purpose from the operator's master key. API keys and SCIM tokens are not stored at all, only their SHA-256 hashes.

ERP Preflight does not add an encryption layer of its own to stored files; encryption at rest of the object storage depends on its configuration.

Related pages

Data handling in detail — ERP Preflight Docs