first commit
This commit is contained in:
175
SECURITY_REPORT.md
Normal file
175
SECURITY_REPORT.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# Local security closeout
|
||||
|
||||
Date: 2026-09-15
|
||||
|
||||
## Scope and conclusion
|
||||
|
||||
This report covers the localhost-only Site, Portal/API, Kanban, PostgreSQL,
|
||||
MinIO, ClamAV, and fake integration worker. It does not approve production use
|
||||
or real Mercado Pago, R2, Tiny/Olist, WhatsApp, freight, or other providers.
|
||||
Malware scanning is a quarantine control; it is not artwork or print pre-flight.
|
||||
|
||||
The local stack is healthy and its security regressions pass. Existing orders
|
||||
and the named PostgreSQL/MinIO volumes were preserved. Open dependency and image
|
||||
findings remain and are recorded below; there is no claim of zero vulnerabilities,
|
||||
complete OWASP compliance, or production readiness.
|
||||
|
||||
## Implemented controls
|
||||
|
||||
- Loopback-only published ports, local-only adapter guards, internal API/worker/
|
||||
database/scanner network, Host checks, cross-origin write rejection, CSP,
|
||||
frame denial, MIME sniffing protection, and no interactive API documentation.
|
||||
- Escaped untrusted filenames and browser regression coverage with CSP bypassed.
|
||||
- Expiring, revocable, hashed HttpOnly operator sessions. No Basic credentials or
|
||||
operator password are retained in browser storage.
|
||||
- Seven-day revocable customer sessions, stronger scrypt password hashes, legacy
|
||||
verification and upgrade, comparable missing-account password work, and login
|
||||
throttling.
|
||||
- Upload extension/size/count quotas, storage quotas, private UUID object keys,
|
||||
exact signed multipart `Content-Length`, completion/ownership checks, and
|
||||
five-minute signed downloads.
|
||||
- Files start `pending`. Only `clean` files may be quoted, approved, paid,
|
||||
downloaded, attached as final files, or moved into queue/printing states.
|
||||
Unknown, scanner error, unsafe/encrypted, over-limit, and malware outcomes fail
|
||||
closed. Rejected/error files expire within three days.
|
||||
- Restricted PostgreSQL and MinIO runtime identities provisioned separately from
|
||||
administrator credentials. Containers use read-only filesystems/capability
|
||||
drops where compatible.
|
||||
- Structured redacted security logs plus a 30-day PostgreSQL event table. Worker
|
||||
health includes outbox progress, scan-thread liveness, and a live ClamAV PING.
|
||||
The alert summary also reports signature age.
|
||||
- Logout revokes server sessions and clears checkout keys and IndexedDB File blobs
|
||||
across open Site tabs.
|
||||
- Production delivery definitions use unprivileged application/web users,
|
||||
read-only filesystems, dropped capabilities, external Swarm secrets, private
|
||||
service networking, immutable base images, commit-SHA rollback tags,
|
||||
health-monitored rolling updates, and automatic application rollback. A
|
||||
fail-closed source/configuration gate prevents the current local-only runtime
|
||||
from being released as production.
|
||||
|
||||
## Verified checks
|
||||
|
||||
All results below were observed against the final application source. Smoke and
|
||||
workflow were repeated after the refreshed PostgreSQL 17 image was activated.
|
||||
|
||||
| Check | Result |
|
||||
|---|---|
|
||||
| Pricing parity against Site JavaScript | Pass: all 4,444 comparisons plus invalid inputs |
|
||||
| Security regression | Pass: headers, Host/origin, sessions, quotas, multipart signing, throttling |
|
||||
| Real ClamAV EICAR regression | Pass: rejected and blocked from download/quote; clean control released |
|
||||
| Smoke workflow | Pass: multipart, ownership, all modes, pricing/freight, payment idempotency, states/history |
|
||||
| Customer/final-file workflow | Pass: identity, revocation, corrections, secure downloads, invalidation and queue gate |
|
||||
| Runtime security | Pass: PostgreSQL/MinIO least privilege, hashes, quarantine and scanner failure behavior |
|
||||
| Retention | Pass: expired bytes removed, live bytes preserved, metadata retained |
|
||||
| Browser workflow | Pass: end-to-end flow, filename XSS probe, no stored operator password, logout blob cleanup |
|
||||
| Dependency lock | Pass: 26 exact packages, SHA-256 hashes on every entry, direct-pin parity, hash-enforced image build, `pip check` |
|
||||
| Staging readiness gate | Pass with synthetic non-secret metadata in a network-disabled container; unsafe/incomplete regression cases rejected |
|
||||
| Production delivery definitions | Pass: unit/syntax/YAML checks, Swarm render, immutable-base image builds, non-root read-only web runtime and Host rejection |
|
||||
| Production source preflight | Expected block: local-only/fake adapters and Docker secret-file loading are not production implementations |
|
||||
| Production ClamAV runtime | Pass: UID 100:101, read-only filesystem, no capabilities/no-new-privileges, live PONG |
|
||||
| Repository secret scan | Pass: no HIGH/CRITICAL Trivy secret findings; release workflow enforces the same gate |
|
||||
|
||||
## Malware-scanner operations
|
||||
|
||||
The scanner image is digest-pinned and has no external network route. It uses
|
||||
the signatures bundled into that image. On closeout it reported ClamAV
|
||||
`1.5.4/28122/Sun Sep 13 06:26:25 2026`; the summary calculated 31.8 hours of age,
|
||||
below the seven-day alert threshold.
|
||||
|
||||
The multipart transport supports uploads up to 5 GiB, but `SCAN_MAX_BYTES` and
|
||||
ClamAV stream limits release at most 128 MiB by default. Larger files remain
|
||||
blocked. Supporting larger files requires a deliberate resource/timeout design,
|
||||
not simply increasing the upload limit.
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
docker compose exec -T api python -m local.security_status
|
||||
```
|
||||
|
||||
An exit status of 1 requires review. At closeout, attention was expected because
|
||||
the regression suite produced two rate-limit alerts, 20 synthetic failed operator
|
||||
logins, and two rejected `SECURITY-EICAR.cdr` fixtures. Both rejected fixtures
|
||||
expire on 2026-09-17. ClamAV was available and its signatures were not stale.
|
||||
Do not automatically dismiss later alerts: verify filename, timestamp, test run,
|
||||
scanner availability, and signature age. Treat non-test rejected files, scanner
|
||||
errors, unexplained authentication bursts, or stale signatures as incidents.
|
||||
|
||||
## Dependency and image audit
|
||||
|
||||
`pip-audit` inspected the exact packages installed in the hash-enforced rebuilt API
|
||||
and found no known Python advisories on 2026-09-15. All 26 direct and transitive
|
||||
runtime packages are pinned with artifact hashes in `local/requirements.lock`.
|
||||
`local/lock_dependencies.sh` regenerates it in a disposable Python 3.12 container.
|
||||
This is a point-in-time package-database result, not proof that the dependencies
|
||||
or image are vulnerability-free. Scheduled lock refresh and audit automation remain
|
||||
unfinished.
|
||||
|
||||
Trivy JSON reports are in `output/security/`. HIGH/CRITICAL results after the
|
||||
available custom-image package upgrades were:
|
||||
|
||||
| Image | Findings | Qualification |
|
||||
|---|---:|---|
|
||||
| API | 46 HIGH | 44 Debian records had no fix; Trivy's two Python records named `msgpack` and `setuptools`, which `pip list` confirmed are absent from the runtime. Trivy warned that the third-party SBOM may be inaccurate. |
|
||||
| Site | 5 HIGH | Nginx package records with fixes listed by Trivy, but the current official `nginx:1.28-alpine` image/repository did not supply them. |
|
||||
| PostgreSQL 17 | 30 HIGH, 1 CRITICAL | Nine Alpine library records and 22 records in `/usr/local/bin/gosu`; presence is confirmed, reachability through this local stack is not. |
|
||||
| MinIO | 102 HIGH, 6 CRITICAL | Findings span the MinIO and `mc` binaries and two OS packages. A verified local clean-object archive now exists, but the old pinned release was not changed without a tested version-migration and rollback plan. |
|
||||
| ClamAV | 0 HIGH/CRITICAL | This scan result is not a guarantee that no vulnerability exists. |
|
||||
| Production API validation image | 55 HIGH, 3 CRITICAL | 44 records had no fix. Fourteen listed fixes, but the `msgpack` and `setuptools` records came from a third-party SBOM and both packages were confirmed absent with `pip list`; the remaining fixed records are Debian packages. |
|
||||
| Production web validation image | 52 HIGH, 2 CRITICAL | All 54 records listed fixed Alpine versions. A current approved Nginx base digest must replace the locally available validation base before release. |
|
||||
|
||||
Scanner presence is evidence, while exploitability/reachability requires separate
|
||||
analysis. MinIO and the remaining base-image findings block any production-readiness
|
||||
claim even though ports are loopback-only here. The production validation reports
|
||||
are `production-api-container-audit.json` and
|
||||
`production-web-container-audit.json`; both make the release workflow's
|
||||
HIGH/CRITICAL gate fail. Counts reflect the 2026-09-15 Trivy database and can
|
||||
change when the advisory database or selected base digest changes.
|
||||
|
||||
## Production delivery security boundary
|
||||
|
||||
The files in `deploy/` and `.gitea/workflows/` are a guarded delivery mechanism,
|
||||
not an approval to operate the current application on the public internet. The
|
||||
single Gitea workflow requires a protected Docker runner, exact commit checkout,
|
||||
digest-pinned base/scanner images, regressions, and HIGH/CRITICAL Trivy gates.
|
||||
It publishes both `latest` and the full commit SHA, then calls the Portainer
|
||||
webhook. Application/provider secrets are created directly as versioned external
|
||||
Swarm secrets and never cross the workflow. Rollback selects the prior commit SHA
|
||||
in Portainer and does not roll back the database.
|
||||
|
||||
The gate currently identifies real blockers: production adapters and authenticated
|
||||
payment webhooks are absent, the runtime intentionally rejects production/R2,
|
||||
Docker secret-file configuration is not implemented, approvals are unset, and
|
||||
the existing image findings are unresolved. These checks must be satisfied by
|
||||
implementation and review, not by replacing the checks with permissive values.
|
||||
|
||||
## PDF.js review
|
||||
|
||||
The Site loads version 3.11.174 from a versioned CDN URL. That version is affected
|
||||
by [GHSA-wgrm-67xf-hhpq](https://github.com/mozilla/pdf.js/security/advisories/GHSA-wgrm-67xf-hhpq),
|
||||
whose documented workaround is `isEvalSupported:false`; the existing Site call
|
||||
already sets that value, so the known eval path is mitigated and is not reported
|
||||
as unmitigated. The newer
|
||||
[GHSA-hq66-cqwq-w95j](https://github.com/mozilla/pdf.js/security/advisories/GHSA-hq66-cqwq-w95j)
|
||||
affects versions from 5.6.83 up to the patched 6.2.108 and therefore does not
|
||||
include 3.11.174.
|
||||
|
||||
Version 3.11.174 is nevertheless old, remotely loaded, and not a satisfactory
|
||||
long-term dependency posture. Plan a compatibility-tested upgrade and preferably
|
||||
vendor or integrity-pin the asset. Keep script execution disabled and do not
|
||||
weaken CSP merely to support a preview.
|
||||
|
||||
## Before any staging or production work
|
||||
|
||||
- Resolve or formally accept current MinIO, PostgreSQL, Nginx, and Debian image
|
||||
findings. Use the verified local clean-object bundle to rehearse a MinIO
|
||||
migration and rollback; it is not a scheduled, offsite, or production backup.
|
||||
- Schedule controlled dependency-lock refreshes and exact-runtime audits; review
|
||||
and test every resulting version change.
|
||||
- Establish a signature update/rebuild process that works without giving the
|
||||
scanner an unrestricted external network route.
|
||||
- Replace disposable local secrets; add TLS, production session/cookie settings,
|
||||
account verification/recovery, centralized monitoring, and complete backup/
|
||||
restore procedures.
|
||||
- Re-run every security, malware, workflow, browser, retention, dependency, and
|
||||
image check in the target staging architecture.
|
||||
Reference in New Issue
Block a user