220 lines
15 KiB
Markdown
220 lines
15 KiB
Markdown
# Security and performance follow-up — 8 September 2026
|
||
|
||
This follows the [original review](SECURITY_PERFORMANCE_REVIEW_2026-09-08.md)
|
||
and its post-release issue reconciliation. It describes new source work after
|
||
the frozen 0.1.45 release; it does not change published tags, packages, signed
|
||
catalogs or deployed images. Gitea remains the canonical state log.
|
||
|
||
## Implemented source slices
|
||
|
||
- [Core #297](https://git.add-ideas.de/GovOPlaN/govoplan-core/issues/297):
|
||
a shared disposable-process runner enforces wall/CPU/address-space/input/output
|
||
limits, bounded stderr, process-group cleanup and non-queuing per-process
|
||
admission. A private binary codec bounds decoding before allocating a full
|
||
object graph and preserves explicitly supported data types without pickle.
|
||
Read the owning Core `docs/BOUNDED_PROCESS_CONTRACT.md` before adding callers.
|
||
- Connectors XLSX parsing, Templates rendering, Files ZIP/TAR inspection and
|
||
extraction, and Dataflow reference previews/development execution now use
|
||
that boundary. Existing authorization, sessions, provider credentials,
|
||
idempotency and persistence remain in the parent. No unprotected inline
|
||
fallback is used. Each module contributes static EN/DE user/admin limits and
|
||
operational consequences through its manifest.
|
||
- Files snapshots authorized sources inside shared admission, validates private
|
||
staged members, and acknowledges each persisted member before decoding the
|
||
next. Numeric progress remains available. The acknowledgement is event-driven,
|
||
not a fixed sleep per member. Reads allocate by validated actual file size,
|
||
not by the configured ceiling. Failures reap children, clear private staging
|
||
and retain the existing transaction/blob cleanup and explicit retry behavior.
|
||
- Dataflow's normal reference preview formerly bypassed the backend wrapper;
|
||
it now enters the worker too. Nested source configurations cannot collide
|
||
merely because subflows reuse node IDs. Combined reference-source data is
|
||
checked before creating further columnar copies, while individual providers
|
||
retain their own authorized-read bounds. Staging/production still require
|
||
DuckDB; this change does not replace that separate backend.
|
||
- [Access #22](https://git.add-ideas.de/GovOPlaN/govoplan-access/issues/22):
|
||
current-password change, session/CSRF rotation, cross-tenant session and human
|
||
API-key revocation, and optional administrator-assisted recovery. Recovery
|
||
codes are hashed, single-use, expire after 15 minutes, require a current local
|
||
System owner and explicit identity verification, and recheck current account,
|
||
membership, tenant and issuer authority at redemption. A password change also
|
||
invalidates outstanding codes issued by that account for other people. Audit
|
||
evidence and validation/error responses do not contain passwords or codes.
|
||
External-provider and service-account rules remain separate.
|
||
- The Access UI provides first-login/required change, self-service change,
|
||
policy-aware sign-in help, public code redemption and eligible owner issuance.
|
||
Core consumes an optional lazy auth-action capability rather than importing
|
||
Access internals. The required-action gate fails closed if its UI is missing.
|
||
- [Workflow Engine #3](https://git.add-ideas.de/GovOPlaN/govoplan-workflow-engine/issues/3):
|
||
full-history lists batch pinned revisions, while new summary and bounded
|
||
step/event endpoints preserve authorization and explicit pagination. Existing
|
||
full-history responses are not silently truncated. Exact inbox total semantics
|
||
are retained and their counting cost is documented.
|
||
- [Meta #55](https://git.add-ideas.de/GovOPlaN/govoplan/issues/55):
|
||
shared version/planning helpers recognize the existing nested developer
|
||
package, not invented root metadata. All tag batches enforce trusted private
|
||
source ownership, registered origins and clean main/upstream state. Meta
|
||
batches additionally require exact composition and matching Core evidence.
|
||
Whole-batch preflight,
|
||
frozen source receipts, annotated immutable tags, object-pinned atomic
|
||
publication and post-effect remote checks are covered with temporary local
|
||
repositories. Hidden Git index flags, unsafe ancestry, alternates and changed
|
||
Git-directory identities are rejected. Selected version/composition metadata
|
||
must be tracked, so ignored files cannot describe bytes absent from a tag.
|
||
Applicable unselected Core WebUI inputs have bounded, frozen read receipts;
|
||
backend-only releases do not read them. The existing local module-candidate
|
||
exception remains intact. The weaker legacy mutation path was removed.
|
||
Canonical whole-package preview and receipt-bound apply now cover Meta's
|
||
version preparation too. Core must already match the target. Preparation
|
||
requires a separate trusted checkout, explicit out-of-run confirmation and
|
||
unchanged source/tooling receipts; it cannot rewrite the running operator.
|
||
Plans place Meta after Core and explain the manual preparation/publication
|
||
steps instead of promising a durable self-update. Ambiguous partial writes
|
||
require reconciliation, without automatic rollback or retry.
|
||
- [Runtime-image follow-up](RUNTIME_IMAGE_REMEDIATION_2026-09-08.md): eight new
|
||
registry-only scans cover four exact candidates on amd64 and arm64. New
|
||
installer specifications select the patched same-line HAProxy digest;
|
||
existing specifications retain their explicit image. Other candidates and
|
||
unresolved inventory/deployment gates remain visible, not blanket-approved.
|
||
|
||
## Verification record
|
||
|
||
Targeted checks include actual child execution, catastrophic regex CPU,
|
||
aggregate memory exhaustion, TAR extension metadata, noisy output, malformed
|
||
transport/staging data, Unicode allocation limits, cancellation/callback
|
||
failures, descendant cleanup, rollback and explicit retries. Local mixed-owner
|
||
composition tests completed nine real children, rejected six overlapping
|
||
requests as busy, observed at most one unreaped child and recovered all slots.
|
||
This is local admission evidence, not a target deployment load certification.
|
||
|
||
Workflow fixtures serialize 40 different pinned revisions with five SQL reads;
|
||
summary lists use one query for 40 ordinary rows. Exact inbox totals for
|
||
40/400/4,000 candidates used one query, with measured local costs approximately
|
||
0.011/0.057/0.492 seconds. These are fixture measurements, not production SLOs.
|
||
|
||
The broader Core API smoke suite exposed three stale campaign assertions.
|
||
All three failures were reproduced against the unchanged private frozen 0.1.45
|
||
sources. Updated fixtures verify recipient-summary projection, detailed payload
|
||
separation and explicit fenced recovery of a confirmed stopped runtime; observing
|
||
SENDING alone must not make a claim recoverable. All 76 smoke tests then passed.
|
||
No production Campaign behavior was changed to satisfy these tests.
|
||
|
||
The final release-tool suite passed 279 tests and 68 subtests, including
|
||
temporary local remotes and adversarial source/tag/receipt changes. Rechecking
|
||
the whole batch before effects is deliberately conservative: its repeated
|
||
filesystem/Git/remote work grows quadratically with batch size. It is not a
|
||
new unattended publication path or permission to execute unreviewed source.
|
||
|
||
Strict interface inventory now reports no unclassified endpoints and exact
|
||
contextual help for all 133 high-risk controls. Seventeen password browser cases
|
||
include actual F1 help from the restricted screen, empty workspace scopes,
|
||
EN/DE layouts, Unicode boundaries and no credential values in help URLs.
|
||
The initial production bundle remains within the unchanged limits (512,036
|
||
raw bytes and 162,415 gzip bytes; 1,713 gzip bytes below its ceiling), with
|
||
46 optional descriptors and no eager optional-module imports.
|
||
|
||
The focused checker now includes the new Core process, mixed-owner admission,
|
||
Access password, Templates and Files worker tests, the repaired campaign smoke
|
||
cases, and browser-side auth/password transport contracts. The full focused run
|
||
passed, including 63 production module/build permutations and all 230 browser
|
||
cases. Its two opt-in Datasources PostgreSQL cases were skipped in that run
|
||
and subsequently passed against the isolated real database described below.
|
||
The final Meta preparation gate was added after that full run and verified
|
||
with the owning release-tool suite and the focused release-gate command.
|
||
Manifest validation passed for all 72 modules. The full focused log is
|
||
`/mnt/DATA/tmp/govoplan-security-followup-20260908-focused.log`.
|
||
|
||
The first follow-up quick audit captured an unchanged 79-repository snapshot
|
||
in `/mnt/DATA/tmp/govoplan-security-followup-quick-20260908-7s8Sgh/`.
|
||
All four required scanners completed, with zero missing/execution reports;
|
||
all 168 report checksums and 163 machine-readable reports were validated.
|
||
Gitleaks found no secrets in all 79 histories and 79 worktrees. Local Semgrep
|
||
rules reported zero findings. Production Bandit reported 65 low and four medium
|
||
warnings, and production Ruff retained 54 warnings. The two added Bandit
|
||
warnings identify the new Core subprocess import and invocation: trusted
|
||
server-owned arguments, no shell, and the documented resource/process boundary
|
||
were reviewed; warnings remain visible. This is report-only evidence, not a
|
||
warning-free audit or a penetration test. A final snapshot follows the
|
||
cross-module declaration/contextual-help corrections and release-tool checks.
|
||
|
||
That final audit completed on 8 September, 05:59:46–06:02:18 UTC, in
|
||
`/mnt/DATA/tmp/govoplan-security-final-quick-20260908-vAwQIh/`. All 79 start/end
|
||
source fingerprints were identical; all four scanners completed, all 168
|
||
registered report checksums matched, and all 163 JSON/SARIF reports parsed.
|
||
There were no missing reports or scanner execution errors. Semgrep and both
|
||
Gitleaks scopes again reported zero findings. Production counts were unchanged
|
||
from the first follow-up: Bandit 65 low/four medium and Ruff 54. Test-only
|
||
counts were Bandit 140 low/34 medium and Ruff 136. A separate frozen scan of
|
||
all ten changed Meta release/deployment Python files reported seven low Bandit
|
||
and four Ruff S603 warnings, with no execution errors. Its four argv-only
|
||
subprocess sites were reviewed; the preparation additions introduced no new
|
||
warnings. No findings were hidden or severity-wide exceptions added.
|
||
The audit manifest SHA-256 is
|
||
`a997b786239cd11443cb665d5f9041a968cc38f9d49171e68bb868bf2bd73310`;
|
||
its report-checksum list SHA-256 is
|
||
`dc590ca5b0a4e445019a05536d410226088d67b40d61cd7657bdef4a4eae56d8`.
|
||
|
||
The audit includes the eight committed feature/website source changes and the
|
||
final uncommitted Meta source. Only this evidence document was updated after
|
||
the source freeze ended; the final Meta commit and remote publication are
|
||
recorded in the linked Gitea issues, not inferred from local audit completion.
|
||
|
||
Fresh dependency audits are retained in
|
||
`/mnt/DATA/tmp/govoplan-dependency-final-20260908-d24LEK/`: all four full npm
|
||
lockfile audits (Core WebUI, Mail root/WebUI and website) report zero known
|
||
vulnerabilities. Installed Python auditing covers 137 distributions with zero
|
||
known vulnerabilities; 51 local GovOPlaN distributions lack PyPI advisory
|
||
coverage. Core's 46 linked packages are likewise not claimed covered by public
|
||
registry advisories. All 12 dependency-file hashes and the installed inventory
|
||
were unchanged. No packages were installed or automatically fixed.
|
||
|
||
Managed PostgreSQL 16.15 fixtures used private Unix sockets, synthetic roles
|
||
and databases, no TCP listener, per-case schemas and bounded SQL/lock waits.
|
||
Both previously skipped Datasources races passed. Twenty-one existing Access
|
||
password HTTP tests and four additional races passed on PostgreSQL: single-use
|
||
redemption, stale-session/password replacement, competing issuance, and issuer
|
||
password revocation during redemption. Four release/development migration checks
|
||
also passed for Access and Workflow, including credential preservation and
|
||
idempotent indexes. The four races are now owning opt-in Access regressions;
|
||
see `govoplan-access/docs/PASSWORD_RECOVERY_POSTGRES_TESTS.md`. These local
|
||
database checks do not certify a deployment, fleet load or external recovery
|
||
handover. With both explicit PostgreSQL test URLs enabled, the full Access suite
|
||
passed 122 tests and 18 subtests, and the full Datasources suite passed 57 tests,
|
||
without skips. Access retained 12 existing SQLite datetime-adapter warnings in
|
||
its separate SQLite migration cases. Both temporary PostgreSQL fixtures were
|
||
stopped and independently verified: no server process, private socket,
|
||
generated schema or synthetic cluster remains. Scripts, logs and shutdown
|
||
receipts are retained under `/home/zemion/.cache/govoplan-pg-security-20260908.RAUitg/`
|
||
and `/home/zemion/.cache/govoplan-pg-promoted-20260908.JZcIKL/`.
|
||
|
||
## Adoption and remaining gates
|
||
|
||
1. `AUTH_LOCAL_PASSWORD_RECOVERY_ENABLED` remains **false** by default. The
|
||
existing flag is still advisory until an operator explicitly adopts and
|
||
enables the complete recovery policy. Confirm who verifies identity and how
|
||
the one-time code is handed over; automated email recovery is not enabled.
|
||
Test first-login, lost-password, code expiry and administrator availability
|
||
in the target environment before enforcement.
|
||
2. Access migration `e9a2c5f8b1d4` adds recovery evidence; Workflow migration
|
||
`9e6b3f8a2c7d` adds summary-pagination indexes. Use normal backed-up upgrade
|
||
procedures and account for index-build cost. No manual live migration or
|
||
server restart was performed during this work. The user's existing devserver
|
||
has automatic reload, so live schema state must not be assumed unchanged.
|
||
3. Release preparation must assign new source/package versions and require a
|
||
Core version containing the new worker/auth contracts in the affected module
|
||
metadata, including matching WebUI assets. The old immutable release must
|
||
not be relabelled or treated as containing these APIs.
|
||
4. Resource limits are not an arbitrary-code, filesystem or network sandbox.
|
||
Admission is per API/worker process, not fleet-wide. Validate Linux/cgroup
|
||
memory, disk quotas, process counts, cancellation and legitimate large-file
|
||
workloads on the intended runtime before increasing concurrency. Core #297
|
||
retains this target-evidence follow-up.
|
||
5. Meta #52 and website #9 retain runtime-image/publication/deployment holds.
|
||
Docker/Podman/HAProxy executables are unavailable here. Final built images,
|
||
binary/source inventories, ingress behavior, migration/readiness/worker
|
||
smoke checks and the website's target/operator authority remain outstanding.
|
||
Zero findings in a detected package inventory is not full image coverage.
|
||
|
||
No real messages, IMAP appends, password resets, provider operations or deployment
|
||
actions were used as test fixtures. Development tests use temporary databases,
|
||
private temporary files, mock transports and managed test-browser servers.
|