All checks were successful
Build and deploy / Validate source (push) Successful in 1m28s
Build and deploy / Integration suite on a real stack (push) Successful in 4m3s
Build and deploy / Secret scan and release gate (push) Successful in 11s
Build and deploy / Publish images and notify Portainer (push) Has been skipped
181 lines
11 KiB
Markdown
181 lines
11 KiB
Markdown
# 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. As of 2026-09-23 the
|
|
API and customer picker reject larger files before transfer. Supporting them
|
|
requires a deliberate resource/timeout design, not simply increasing the
|
|
transport limit.
|
|
|
|
Run:
|
|
|
|
```bash
|
|
docker compose -f compose.local.yaml exec -T api python3 -m ops.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 `infra/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.
|
|
|
|
Updated 2026-09-23: pushes to `main` run checks only. A manual workflow run on
|
|
`main` requires the source preflight and a configured Portainer webhook before
|
|
building. It scans built images before publication; CRITICAL findings block and
|
|
HIGH findings are reported. The browser suites run in a required Compose Chrome
|
|
container. `PORTAINER.md` holds the authoritative gate table. A permitted release
|
|
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.
|