Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ai-testgen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ on:
pull_request:
workflow_dispatch:

# Least privilege (CodeQL actions/missing-workflow-permissions, 2026-10-21): the jobs
# here only read the repository; artefact upload and the Actions cache use the
# runtime token, not GITHUB_TOKEN, so read access to the contents is all it needs.
permissions:
contents: read

jobs:
harness:
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

# Least privilege (CodeQL actions/missing-workflow-permissions, 2026-10-21): the jobs
# here only read the repository; artefact upload and the Actions cache use the
# runtime token, not GITHUB_TOKEN, so read access to the contents is all it needs.
permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ on:
- cron: '0 2 * * *' # nightly, 02:00 UTC
workflow_dispatch:

# Least privilege (CodeQL actions/missing-workflow-permissions, 2026-10-21): the jobs
# here only read the repository; artefact upload and the Actions cache use the
# runtime token, not GITHUB_TOKEN, so read access to the contents is all it needs.
permissions:
contents: read

jobs:
e2e:
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/legacy-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ on:
pull_request:
workflow_dispatch:

# Least privilege (CodeQL actions/missing-workflow-permissions, 2026-10-21): the jobs
# here only read the repository; artefact upload and the Actions cache use the
# runtime token, not GITHUB_TOKEN, so read access to the contents is all it needs.
permissions:
contents: read

jobs:
legacy-build:
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/modern-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ on:
pull_request:
workflow_dispatch:

# Least privilege (CodeQL actions/missing-workflow-permissions, 2026-10-21): the jobs
# here only read the repository; artefact upload and the Actions cache use the
# runtime token, not GITHUB_TOKEN, so read access to the contents is all it needs.
permissions:
contents: read

jobs:
modern-build:
runs-on: ubuntu-latest
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/playbook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ on:
- '.github/workflows/playbook.yml'
workflow_dispatch:

# Least privilege (CodeQL actions/missing-workflow-permissions, 2026-10-21): the jobs
# here only read the repository; artefact upload and the Actions cache use the
# runtime token, not GITHUB_TOKEN, so read access to the contents is all it needs.
permissions:
contents: read

jobs:
playbook-pdf:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion ai-testgen/testbed/modern/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.12</version>
<version>42.7.14</version>
</dependency>
<!-- Added 2026-08-05 because stage 6 added them to modern/pom.xml, and the code
under test is whatever that module is today. PomDriftGuardTest caught the
Expand Down
2 changes: 1 addition & 1 deletion docs/DEVIATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Status values: **deferred(stage)** = lands in a named stage · **deferred(post-v
| Standard (DoD) | Status | Disposition |
|---|---|---|
| Coverage-Gate ≥ 80 % auf Neucode | **met** (2026-08-02, G6) — line only, branch still open | Armed as a **ratchet** throughout, always just below a *measured* value and then pulled up. 2026-07-31: the module's first suite reached 37.3 % line / 14.2 % branch → gate 0.35. 2026-08-02: the repaired G6 corpus-B tests were adopted into `modern/src/test` under [ADR-0011](adr/0011-adopting-generated-tests.md) — 88 test methods, 99 green in the module — and coverage measured **81.3 % line / 58.3 % branch** → gate raised to **0.80**. That is the first time §3's 80 % is *reached* rather than declared. **Two things stay open and are not hidden by the number:** the gate limits **lines only** (branch coverage lags 23 points behind and gets a limit in the same pass that improves the God-class suite), and the adopted `WerkstattService` suite has a **44.1 % mutation score** — high line coverage over the riskiest class with half the injected faults surviving. Coverage went up; the God class is still the weak spot, by measurement. |
| Security-Scan in CI | **met** (2026-08-05, G7) — image scanning is **reporting-only**, stated not hidden | Since 2026-07-30: Dependabot (modern/e2e/characterization + actions; since 2026-07-31 also **npm for `modern/frontend`** — stage 5 had opened the ecosystem without scanning it, caught by review session 10; `legacy/` **excluded on purpose** — its EOL dependencies are the exhibit). 2026-08-05: **Trivy** scans the built modern image in `modern-ci` for CRITICAL/HIGH in OS packages and Java dependencies — the layer Dependabot structurally cannot see, because a CVE in the JRE base image appears in no manifest and ships anyway. Two limits belong next to that: it runs with `exit-code: 0` (**reporting, not blocking** — a fresh CRITICAL in a base image arrives without any commit of ours, and a gate that reddens master overnight for something nobody caused is a gate people learn to bypass), and a separate OWASP dependency-check was **not** added, because it would duplicate what Dependabot and Trivy already cover and now needs an NVD API key to run at usable speed. **It paid for itself on the first CI run:** one HIGH finding, **CVE-2026-54291** in `org.postgresql:postgresql` 42.7.11 — a man-in-the-middle protection bypass via a SCRAM-SHA-256-PLUS downgrade, i.e. precisely the authentication mechanism PostgreSQL 18 uses, which stage 6 had just switched the modern stand to. Fixed the same day by pinning 42.7.12 (the minimum fixed version, not the newest — this is a security fix, not a version bump). Fixing it exposed a second, quieter hole: the G6 testbed and `modern/pom.xml` both left the driver version to Boot's dependency management, and a property override travels through Boot's `<parent>` but **not** through the testbed's imported BOM — so `PomDriftGuardTest` stayed green while the two classpaths silently disagreed. Both now declare the version literally, which is what makes the guard able to see it. After the pin the scan reports **0 findings** across the base layer, the jar and the Go binary. |
| Security-Scan in CI | **met** (2026-08-05, G7) — image scanning is **reporting-only**, stated not hidden | Since 2026-07-30: Dependabot (modern/e2e/characterization + actions; since 2026-07-31 also **npm for `modern/frontend`** — stage 5 had opened the ecosystem without scanning it, caught by review session 10; `legacy/` **excluded on purpose** — its EOL dependencies are the exhibit). 2026-08-05: **Trivy** scans the built modern image in `modern-ci` for CRITICAL/HIGH in OS packages and Java dependencies — the layer Dependabot structurally cannot see, because a CVE in the JRE base image appears in no manifest and ships anyway. Two limits belong next to that: it runs with `exit-code: 0` (**reporting, not blocking** — a fresh CRITICAL in a base image arrives without any commit of ours, and a gate that reddens master overnight for something nobody caused is a gate people learn to bypass), and a separate OWASP dependency-check was **not** added, because it would duplicate what Dependabot and Trivy already cover and now needs an NVD API key to run at usable speed. **It paid for itself on the first CI run:** one HIGH finding, **CVE-2026-54291** in `org.postgresql:postgresql` 42.7.11 — a man-in-the-middle protection bypass via a SCRAM-SHA-256-PLUS downgrade, i.e. precisely the authentication mechanism PostgreSQL 18 uses, which stage 6 had just switched the modern stand to. Fixed the same day by pinning 42.7.12 (the minimum fixed version, not the newest — this is a security fix, not a version bump). Fixing it exposed a second, quieter hole: the G6 testbed and `modern/pom.xml` both left the driver version to Boot's dependency management, and a property override travels through Boot's `<parent>` but **not** through the testbed's imported BOM — so `PomDriftGuardTest` stayed green while the two classpaths silently disagreed. Both now declare the version literally, which is what makes the guard able to see it. After the pin the scan reports **0 findings** across the base layer, the jar and the Go binary. *2026-10-21:* the same scan had climbed back to **12 fixable CRITICAL/HIGH** — 8 in the jar (Tomcat 11.0.24: 3 CRITICAL; Jackson 3.1.5: 5 HIGH) and 4 in a Go binary — none caused by a commit of ours, which is exactly the overnight drift the reporting-only decision anticipated. Fixed by pinning the minimum fixed versions over Boot 4.1.1's (Tomcat 11.0.25, Jackson 3.1.7) plus postgresql 42.7.14. **Measured after the fix: 4 HIGH, 0 CRITICAL, jar 0.** All four are Go standard-library CVEs in `/usr/bin/pebble` — Canonical's service manager, shipped by the `eclipse-temurin:25-jre` base image (Ubuntu 26.04), built with Go 1.27.1 (fixed in 1.27.3). Our entrypoint never starts it. Not removed in our Dockerfile: deleting a base-image file in a later layer hides it from the scanner without removing it from the image, which would turn an honest count into a cosmetic one; it closes when the base image is rebuilt. The current count is also on `/nachweis`. |
| AuthN/AuthZ (OAuth2/OIDC, Keycloak) — §4 | **G7's hard requirement met** (2026-08-05) at the edge · full OIDC still **owner re-scoping needed** | The 2016 app has no auth at all; adding it changes every pinned contract, so it cannot happen as a side effect (ADR-0004 gate). Recorded honestly instead of silently (review session 10): the modern stand — including the destructive `POST /admin/bereinigen` the JSP wart B16 bequeathed — is unauthenticated by inherited design. The G7 requirement was *"protect `/admin` at minimum (reverse-proxy auth counts)"*, and that is exactly what shipped: Basic auth in a Traefik overlay covering `/admin`, `/api/admin` and `/actuator` ([ADR-0014](adr/0014-authentifizierung-am-edge.md)), measured 401 without credentials and 200 with them, wrong password 401. **What that is not:** the application itself is still unauthenticated, so anyone who can reach its port bypasses the lock — which is why a real host must not publish it (`deployment.md` §12). The measured price of doing it in the application instead: a matcher scoped to `/admin` alone turns 4 safety-net tests red, each one an ADR-0004 sanctioned-divergence decision. Full OAuth2/OIDC per §4 remains an owner-scoped stage of its own plus the §9 auth ADR. |
| Security-Header, Rate Limiting — §4 | **met at the edge** (2026-08-05, G7) for the modern stand · never for `legacy/` | Delivered by the Traefik overlay ([ADR-0014](adr/0014-authentifizierung-am-edge.md)), verified by `modern/edge/verify-edge.sh`, not asserted: `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`, a CSP whose `script-src` is strict, and a token-bucket rate limit measured firing (200 concurrent requests → 103–173 × 429 across five runs; the same burst straight at the application → 0). HSTS ships **off** (`stsSeconds: 0`) on purpose: enabled over plain HTTP it teaches the browser to refuse `http://localhost` for a year; the TLS-terminating host sets it. `legacy/` gets none of this — it is the exhibit. |
| CSRF-Schutz — §4 | **deliberately not implemented**, reason recorded | Not an oversight and not deferred-by-drift: the application has no session, no cookie and no ambient authority, so a classic CSRF token would protect nothing. What stage 6 *did* introduce is Basic auth at the edge, and browsers cache those credentials — a cross-site POST could ride them. That residual risk is written down in `SECURITY.md` and accepted for a demonstration stand; the real fix (session-based OIDC plus token) belongs to the owner-scoped auth stage below, not to an ops stage. Enabling Spring Security's CSRF today has a measured price: 17 characterization write calls plus every E2E write scenario turn red, i.e. it is a pinned-contract change under the ADR-0004 gate. |
Expand Down
14 changes: 12 additions & 2 deletions modern/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@
Never raise this above a measured value: a gate nobody can reach is the reason
-DskipTests exists. See docs/DEVIATIONS.md. -->
<jacoco.line.minimum>0.80</jacoco.line.minimum>
<!-- Security overrides of Boot 4.1.1's managed versions (2026-10-21), each held at the
minimum version that fixes what the image scan reported, not at the newest:
- Tomcat 11.0.24 → 11.0.25: CVE-2026-65182, CVE-2026-65905, CVE-2026-68525 (CRITICAL)
- Jackson 3 3.1.5 → 3.1.7: CVE-2026-89407, -89425, -68497, -91776, -91777 (HIGH).
3.1.7 stays on Boot's managed minor; 3.2.2 would NOT close -89425/-91776/-91777
(fixed in 3.1.7 or 3.2.3), and a minor jump touches the wire format ADR-0005 pins.
Drop both properties when the Boot parent manages these versions or newer. -->
<tomcat.version>11.0.25</tomcat.version>
<jackson-bom.version>3.1.7</jackson-bom.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -65,7 +74,8 @@
fixed in 42.7.12. Not theoretical: PostgreSQL 18 authenticates with SCRAM, so
this is the mechanism the modern stand actually uses. Held at the minimum fixed
version rather than the newest (42.7.13) so this is a security fix and nothing
else — Dependabot owns routine drift.
else — Dependabot owns routine drift. 2026-10-21: 42.7.14 (Dependabot #101),
testbed pom moved in the same commit.
Declared as a literal here AND in ai-testgen/testbed/modern/pom.xml rather than
as a property: PomDriftGuardTest compares DECLARED coordinates, and a property
override does not travel through the testbed's imported BOM the way it travels
Expand All @@ -74,7 +84,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.12</version>
<version>42.7.14</version>
</dependency>

<!-- G7 (stage 6): schema management moves out of the hand-run SQL files
Expand Down
Loading