Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf8f905767 | ||
|
|
f02de2b6c8 | ||
|
|
9de84f6994 | ||
|
|
77ecf42365 | ||
|
|
ab7a03386c | ||
|
|
87f5fe20f9 |
@@ -0,0 +1,38 @@
|
|||||||
|
name: Supply-Chain-Scan (Go)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- "**/*.go"
|
||||||
|
- "go.mod"
|
||||||
|
- "go.sum"
|
||||||
|
pull_request: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
govulncheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: "1.22"
|
||||||
|
- name: govulncheck installieren
|
||||||
|
# @latest kann eine govulncheck-Version verlangen, die neuer ist als
|
||||||
|
# die hier verwendete Go-Toolchain (z.B. "requires go >= 1.25.0") —
|
||||||
|
# feste, bekannt kompatible Version statt @latest (siehe
|
||||||
|
# scripts/verify-supply-chain-gate.sh, Fund vom 2026-08-28 auf dem Testhost).
|
||||||
|
run: go install golang.org/x/vuln/cmd/govulncheck@v1.1.3
|
||||||
|
- name: Go-Module auf bekannte Schwachstellen pruefen
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# pipefail ist Pflicht: sonst liefert "govulncheck | tee" den Exit-Code
|
||||||
|
# von tee (immer 0) statt den von govulncheck zurueck — der Gate-Zweck
|
||||||
|
# (Akzeptanzkriterium 3: Fund blockiert den Merge) waere sonst wirkungslos.
|
||||||
|
set -o pipefail
|
||||||
|
govulncheck ./... | tee govulncheck-report.txt
|
||||||
|
- name: Scan-Bericht als Artefakt ablegen
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: govulncheck-report
|
||||||
|
path: govulncheck-report.txt
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
name: Mail-Pflichttest-Gate
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "mail/**"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
pflichttest-gate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: "1.22"
|
|
||||||
- name: Gate bauen
|
|
||||||
working-directory: mail
|
|
||||||
run: go build -o /tmp/pflichttestgate ./cmd/pflichttestgate
|
|
||||||
- name: Geänderte Dateien gegen Pflichttest-Regel prüfen
|
|
||||||
run: |
|
|
||||||
git diff --name-only "origin/${{ github.base_ref }}...HEAD" | /tmp/pflichttestgate
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
name: Supply-Chain-Scan (npm)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- "web/**/package.json"
|
||||||
|
- "web/**/package-lock.json"
|
||||||
|
pull_request: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
npm-audit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "22"
|
||||||
|
- name: Alle Next.js-Frontends auf bekannte Schwachstellen pruefen
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -o pipefail
|
||||||
|
status=0
|
||||||
|
for pkg in $(find web -maxdepth 2 -name package.json); do
|
||||||
|
dir=$(dirname "$pkg")
|
||||||
|
echo "=== npm audit: $dir ==="
|
||||||
|
(cd "$dir" && npm install --package-lock-only --no-audit --no-fund \
|
||||||
|
&& npm audit --audit-level=high) || status=1
|
||||||
|
done
|
||||||
|
# Erst nach Durchlauf ALLER Frontends fehlschlagen (Akzeptanzkriterium 2/3):
|
||||||
|
# ein einzelner Fund darf nicht verhindern, dass die uebrigen Frontends
|
||||||
|
# ebenfalls geprueft und im Bericht sichtbar werden.
|
||||||
|
exit $status
|
||||||
@@ -44,3 +44,53 @@ Keine Commits in dieser Session.
|
|||||||
Keine Änderungen ermittelbar.
|
Keine Änderungen ermittelbar.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
## 2026-08-28 23:00 – 23:01 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** nexarch
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- ab7a033 OPS-06: fix — fixture ruft tatsaechlich verwundbaren symbolpfad auf (ParseAcceptLanguage statt Parse, GO-2022-1059 statt falscher advisory-id)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- testdata/vulnfixture-go/go.mod | 8 +++++---
|
||||||
|
- testdata/vulnfixture-go/main.go | 7 +++++--
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-08-28 23:02 – 23:03 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** code
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- 77ecf42 OPS-06: fix — verify-supply-chain-gate.sh unterscheidet jetzt echten Fund von Werkzeugfehler (pruefte vorher nur exit-code, 'command not found' galt faelschlich als bestanden)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- DEVLOG.md | 12 ++++++++++++
|
||||||
|
- scripts/verify-supply-chain-gate.sh | 52 +++++++++++++++++++++++++++++++++++++---------------
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-08-28 23:04 – 23:05 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** code
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
- 9de84f6 OPS-06: fix — fehlendes fail=1 im govulncheck-unavailable-zweig, govulncheck-version gepinnt statt @latest (Go-Versionskonflikt auf 131 gefunden)
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- .gitea/workflows/govulncheck.yml | 6 +++++-
|
||||||
|
- DEVLOG.md | 12 ++++++++++++
|
||||||
|
- scripts/verify-supply-chain-gate.sh | 13 +++++++++++--
|
||||||
|
|
||||||
|
---
|
||||||
|
## 2026-08-28 23:05 – 23:05 (0m)
|
||||||
|
**Beschreibung:** Claude Code Session
|
||||||
|
**Projekt:** code
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
Keine Commits in dieser Session.
|
||||||
|
|
||||||
|
### Geänderte Dateien
|
||||||
|
- .gitea/workflows/govulncheck.yml | 6 +++++-
|
||||||
|
- DEVLOG.md | 12 ++++++++++++
|
||||||
|
- scripts/verify-supply-chain-gate.sh | 13 +++++++++++--
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
// Command pflichttestgate ist das CI-Gate aus docs/TESTSTRATEGIE-MAIL.md
|
|
||||||
// Abschnitt 4. Aufruf: pflichttestgate < geänderte-dateien.txt
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"gitea.perlbach24.de/scripte/nexarch/mail/internal/pflichttestgate"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var changedFiles []string
|
|
||||||
scanner := bufio.NewScanner(os.Stdin)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
if line != "" {
|
|
||||||
changedFiles = append(changedFiles, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "pflichttestgate: eingabe konnte nicht gelesen werden: %v\n", err)
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
violations := pflichttestgate.CheckDiff(changedFiles)
|
|
||||||
if len(violations) == 0 {
|
|
||||||
fmt.Println("pflichttestgate: bestanden — alle sicherheitskritischen Änderungen haben begleitende Tests.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintln(os.Stderr, "pflichttestgate: FEHLGESCHLAGEN — Pflichttest fehlt für:")
|
|
||||||
for _, v := range violations {
|
|
||||||
fmt.Fprintf(os.Stderr, " - Package %q (Datei %q hat keine begleitende _test.go-Änderung)\n", v.Package, v.ChangedFile)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(os.Stderr, "\nSiehe docs/TESTSTRATEGIE-MAIL.md Abschnitt 4.")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
# NEXARCH Mail – Teststrategie
|
|
||||||
|
|
||||||
Stand: 2026-08-30. Ticket: QA-01. Vorbild: Core `QA-01` (`docs/TESTSTRATEGIE-CORE.md`,
|
|
||||||
Fertig) — dieselbe Struktur, für das Mail-Modul übernommen, wo sinnvoll um
|
|
||||||
protokollspezifische Aspekte (IMAP/SMTP/MIME) ergänzt.
|
|
||||||
|
|
||||||
## 1. Warum dieses Dokument existiert
|
|
||||||
|
|
||||||
archivmail (Vorgängerprojekt) testete 2 von 18 Modulen trotz hoher Kritikalität
|
|
||||||
(Compliance-/Protokoll-Logik). Kein zentrales Issue-Tracking — Bugs wurden nur als
|
|
||||||
`BUG-N`-Kommentare im Code festgehalten (`known-issues-archivmail.md`). NEXARCH Mail
|
|
||||||
übernimmt denselben Grundsatz wie Core: **Testpflicht für Auth, Tenant-Scoping und
|
|
||||||
Protokoll-/Compliance-kritische Logik ist ein Merge-Gate, keine Nachrüstung.**
|
|
||||||
|
|
||||||
## 2. Testpyramide
|
|
||||||
|
|
||||||
| Ebene | Werkzeug | Umfang |
|
|
||||||
|---|---|---|
|
|
||||||
| Unit | `go test` (Standardbibliothek) | Einzelne Funktionen/Typen, keine externe Abhängigkeit (DB, Netzwerk, IMAP/SMTP-Socket) |
|
|
||||||
| Integration | `go test` gegen echte PostgreSQL-Instanz (`nexarch_test`-Rolle) | Repository-/Handler-Schicht, Tenant-Scoping, Objekt-Speicher |
|
|
||||||
| Protokoll-Zustandsmaschinen | `go test` gegen echten IMAP-/SMTP-Client-Roundtrip (kein reiner Parser-Unit-Test) | ING-01/ING-02/ING-03: Login-Zustände, Befehlssequenzen, Fehlerpfade |
|
|
||||||
| E2E | Echter HTTP-Roundtrip (`httptest.Server`) bis zum ersten Mail-Frontend-Ticket, danach Playwright/Jest gegen die echte UI | Vollständiger Request-Response-Zyklus, kein reiner Funktionsaufruf |
|
|
||||||
| Vertragstests | Analog Core `QA-07`/DMS-Äquivalent, sobald Mail öffentliche Modul-Adapter-Schnittstellen (RET-05-Konsument, siehe `ARC-11`) hat | Wire-Contract-Stabilität |
|
|
||||||
|
|
||||||
**E2E-Zwischenlösung begründet:** Mail hat aktuell kein Frontend-Ticket (0/66 Board).
|
|
||||||
Playwright/Jest bräuchte eine echte Browser-UI zum Testen — bis zum ersten
|
|
||||||
Mail-Frontend-Ticket ist ein echter HTTP-Roundtrip (kein reiner In-Process-Funktionsaufruf)
|
|
||||||
die ehrliche, tatsächlich verfügbare Untergrenze für "E2E". Siehe Beispiel in
|
|
||||||
Abschnitt 3.
|
|
||||||
|
|
||||||
## 3. Beispieltests je Testart (Akzeptanzkriterium/Pflichtprüfung 2)
|
|
||||||
|
|
||||||
`mail/internal/example` — kein Wegwerf-Demo, sondern eine kleine, tatsächlich nützliche
|
|
||||||
Funktion (E-Mail-Adress-Normalisierung), die spätere Ticket ohnehin brauchen:
|
|
||||||
|
|
||||||
- **Unit:** `normalize_test.go` — `TestNormalizeAddress_*`, keine externe Abhängigkeit.
|
|
||||||
- **Integration:** `store_integration_test.go` — `TestAddressStore_SaveAndCheckExists`,
|
|
||||||
echte Postgres-Instanz, `TEST_TENANT_DSN`, `t.Cleanup`.
|
|
||||||
- **E2E:** `handler_e2e_test.go` — `TestNormalizeHandler_RealHTTPRoundTrip`, echter
|
|
||||||
`httptest.Server`-Roundtrip (TCP, nicht nur Funktionsaufruf).
|
|
||||||
|
|
||||||
Alle sechs Tests real ausgeführt (siehe Prüfungen, Abschnitt 6).
|
|
||||||
|
|
||||||
## 4. Pflichttests als Merge-Gate (Akzeptanzkriterium 3/4)
|
|
||||||
|
|
||||||
Verbindlich für jeden Pull Request, der Dateien in einem der folgenden Bereiche ändert:
|
|
||||||
|
|
||||||
- **Auth** (`mail/internal/auth/` — sobald durch ein späteres Ticket angelegt)
|
|
||||||
- **Tenant-Scoping** (`mail/internal/tenant/`, jede Repository-Schicht mit Mandanten-Bezug)
|
|
||||||
- **Protokoll-kritisch** (`mail/internal/ingest/`, `mail/internal/imap/`,
|
|
||||||
`mail/internal/smtp/` — Zustandsmaschinen, Auth-Handshakes der Protokolle selbst)
|
|
||||||
- **Compliance-kritisch** (`mail/internal/arc/` oder gleichwertig — RET-05-Konsument,
|
|
||||||
Löschung/Archivierung, siehe `ARC-11`)
|
|
||||||
|
|
||||||
Regel (identisch zu Core `QA-01`): **jede geänderte `.go`-Datei in einem dieser
|
|
||||||
Bereiche muss von einer geänderten oder neuen `_test.go`-Datei im selben Package
|
|
||||||
begleitet sein.**
|
|
||||||
|
|
||||||
`mail/internal/pflichttestgate` implementiert das Gate (Code-Kopie des Musters aus
|
|
||||||
Core `internal/pflichttestgate`, mit mail-spezifischen Pfadmustern statt Core-Pfaden
|
|
||||||
— bewusst keine Cross-Modul-Abhängigkeit, da Mail als eigenständiges Go-Modul Core
|
|
||||||
nicht importieren kann). `.gitea/workflows/mail-pflichttest-gate.yml` führt es gegen
|
|
||||||
jeden PR-Diff aus.
|
|
||||||
|
|
||||||
Negativtest des Gates selbst (Prüfung 1 dieses Tickets):
|
|
||||||
`mail/internal/pflichttestgate/gate_test.go` simuliert einen Diff mit geänderter
|
|
||||||
`mail/internal/auth/login.go` ohne begleitende Testdatei und erwartet, dass das Gate
|
|
||||||
das als Verstoß erkennt.
|
|
||||||
|
|
||||||
## 5. Bug-Tracking (Akzeptanzkriterium 3)
|
|
||||||
|
|
||||||
**Konvention: Gitea-Issues** auf `gitea.perlbach24.de/scripte/nexarch`, Label `mail`
|
|
||||||
plus Schweregrad-Label (`bug-kritisch`/`bug-normal`/`bug-kosmetisch`). Durchsuchbar
|
|
||||||
über Gitea-Suche/Label-Filter — explizit KEIN Code-Kommentar-Tracking (`BUG-N` wie in
|
|
||||||
archivmail), das laut `known-issues-archivmail.md` genau diese Sichtbarkeitslücke
|
|
||||||
verursacht hat.
|
|
||||||
|
|
||||||
**Realer Durchspiel-Nachweis (Prüfung 3):** Diese Session (nicht Mail-spezifisch, aber
|
|
||||||
derselbe reale Vorgang) fand mehrere echte Bugs, dokumentiert nach exakt diesem
|
|
||||||
Muster in den jeweiligen `*-PRUEFPROTOKOLL.md`-Dateien statt als Code-Kommentar, z. B.
|
|
||||||
`archive/docs/RET-10-PRUEFPROTOKOLL.md`: fehlende CORS-Header bei RET-06-API,
|
|
||||||
gefunden bei einer Sichtprüfung, Symptom (Browser hätte Fetch blockiert), Ursache
|
|
||||||
(kein `Access-Control-Allow-Origin`), Fix (RET-10-Ticket), Nachweis (curl-Test vorher/
|
|
||||||
nachher) — alles durchsuchbar in der Protokolldatei, nicht im Quelltext verstreut.
|
|
||||||
|
|
||||||
**Ehrlich vermerkt:** Ein ECHTER Gitea-Issue konnte in dieser Session nicht angelegt
|
|
||||||
werden (kein Gitea-API-Token verfügbar, nur Git-SSH/HTTPS-Push-Zugriff). Das oben
|
|
||||||
verlinkte Beispiel demonstriert das Vorgehen strukturell (Symptom → Ursache → Fix →
|
|
||||||
Nachweis, durchsuchbar abgelegt), aber NICHT über die Gitea-Issue-Oberfläche selbst.
|
|
||||||
Sobald ein Gitea-Zugriffstoken verfügbar ist, sollte mindestens ein Test-Issue real
|
|
||||||
angelegt werden, um die Konvention vollständig nachzuweisen — offener Punkt, siehe
|
|
||||||
Abschnitt 7.
|
|
||||||
|
|
||||||
## 6. Prüfungen (real durchgeführt)
|
|
||||||
|
|
||||||
| # | Prüfung | Ergebnis |
|
|
||||||
|---|---|---|
|
|
||||||
| 1 | Dokument liegt vor und wurde von zweiter Person gegengelesen | **bestanden** — Dokument von der Nutzerin/dem Nutzer (zweite Person) gegengelesen und freigegeben (2026-08-30) |
|
|
||||||
| 2 | Stichprobe: mindestens ein Beispieltest je benannter Testart ist umgesetzt | **bestanden** — 6 Tests real ausgeführt auf 131: `go test ./mail/internal/example/... -v -p 1`, alle grün (3 Unit, 1 Integration, 2 E2E) |
|
|
||||||
| 3 | Bug-Tracking-Vorgehen wurde einmal exemplarisch für einen realen Befund durchgespielt | **teilweise bestanden** — Vorgehen strukturell durchgespielt anhand eines realen, bereits dokumentierten Befunds (RET-10), aber NICHT über die echte Gitea-Issue-Oberfläche (kein API-Token verfügbar). Siehe Abschnitt 5, offener Punkt in Abschnitt 7 |
|
|
||||||
|
|
||||||
## 7. Offene Punkte
|
|
||||||
|
|
||||||
- Echter Gitea-Issue als Nachweis der Bug-Tracking-Konvention noch nicht angelegt
|
|
||||||
(fehlendes API-Token in dieser Session). Sollte nachgeholt werden, sobald Zugriff
|
|
||||||
besteht.
|
|
||||||
- `mail/internal/auth/`, `mail/internal/tenant/`, `mail/internal/ingest/` etc. existieren
|
|
||||||
noch nicht — die Pflichttest-Gate-Pfadmuster sind auf Basis der geplanten
|
|
||||||
Modulstruktur vordefiniert, nicht an echtem Code verifiziert. Erste Nagelprobe: das
|
|
||||||
erste Ticket, das einen dieser Pfade tatsächlich anlegt (voraussichtlich `ING-01`).
|
|
||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
module gitea.perlbach24.de/scripte/nexarch/mail
|
|
||||||
|
|
||||||
go 1.22
|
|
||||||
|
|
||||||
require github.com/jackc/pgx/v5 v5.6.0
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
|
||||||
golang.org/x/crypto v0.17.0 // indirect
|
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
|
||||||
golang.org/x/text v0.14.0 // indirect
|
|
||||||
)
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package example
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NormalizeHandler ist das E2E-Test-Beispiel (QA-01): ein echter
|
|
||||||
// HTTP-Endpunkt, gegen den ein Test einen vollständigen Request-Response-
|
|
||||||
// Zyklus fährt (httptest.Server, echter TCP-Roundtrip, kein reiner
|
|
||||||
// Funktionsaufruf). Sobald das erste Mail-Frontend-Ticket eine echte
|
|
||||||
// Browser-UI mitbringt, wird die E2E-Ebene um Playwright/Jest ergänzt
|
|
||||||
// (siehe QA-01-Teststrategiedokument, Abschnitt 2) — bis dahin ist ein
|
|
||||||
// echter HTTP-Roundtrip die ehrliche, verfügbare Untergrenze für "E2E".
|
|
||||||
type normalizeRequest struct {
|
|
||||||
Address string `json:"address"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type normalizeResponse struct {
|
|
||||||
Normalized string `json:"normalized"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NormalizeHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var req normalizeRequest
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
||||||
http.Error(w, "ungültiger request-body", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
normalized, err := NormalizeAddress(req.Address)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
_ = json.NewEncoder(w).Encode(normalizeResponse{Normalized: normalized})
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
// E2E-Test-Beispiel (QA-01 Akzeptanzkriterium 1/Prüfung 2): echter
|
|
||||||
// HTTP-Request über einen laufenden httptest.Server (TCP-Roundtrip),
|
|
||||||
// nicht nur ein Funktionsaufruf im selben Prozess.
|
|
||||||
package example
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNormalizeHandler_RealHTTPRoundTrip(t *testing.T) {
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(NormalizeHandler))
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
body, _ := json.Marshal(normalizeRequest{Address: "Kunde@Beispiel.DE"})
|
|
||||||
resp, err := http.Post(server.URL, "application/json", bytes.NewReader(body))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("post: %v", err)
|
|
||||||
}
|
|
||||||
defer func() { _ = resp.Body.Close() }()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
t.Fatalf("status = %d, want 200", resp.StatusCode)
|
|
||||||
}
|
|
||||||
var out normalizeResponse
|
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
|
|
||||||
t.Fatalf("antwort dekodieren: %v", err)
|
|
||||||
}
|
|
||||||
if out.Normalized != "Kunde@beispiel.de" {
|
|
||||||
t.Fatalf("got %q", out.Normalized)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeHandler_InvalidAddressReturns400(t *testing.T) {
|
|
||||||
server := httptest.NewServer(http.HandlerFunc(NormalizeHandler))
|
|
||||||
defer server.Close()
|
|
||||||
|
|
||||||
body, _ := json.Marshal(normalizeRequest{Address: "keine-gueltige-adresse"})
|
|
||||||
resp, err := http.Post(server.URL, "application/json", bytes.NewReader(body))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("post: %v", err)
|
|
||||||
}
|
|
||||||
defer func() { _ = resp.Body.Close() }()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusBadRequest {
|
|
||||||
t.Fatalf("status = %d, want 400", resp.StatusCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
// Package example dient QA-01 (Mail): liefert je Testart (Unit,
|
|
||||||
// Integration, E2E) genau EIN reales, lauffähiges Beispiel, an dem sich
|
|
||||||
// spätere Mail-Tickets orientieren können — keine Wegwerf-Demo, sondern
|
|
||||||
// eine tatsächlich nützliche, kleine Funktion (Adress-Normalisierung),
|
|
||||||
// die spätere Ticket (z. B. ING-01/ING-04) ohnehin brauchen werden.
|
|
||||||
package example
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrInvalidAddress = errors.New("example: keine gültige e-mail-adresse")
|
|
||||||
|
|
||||||
// NormalizeAddress normalisiert eine E-Mail-Adresse für den
|
|
||||||
// tenant-scoped Vergleich: Kleinschreibung der Domain-Hälfte
|
|
||||||
// (lokaler Teil bleibt case-sensitive, RFC 5321), Leerraum entfernt.
|
|
||||||
func NormalizeAddress(addr string) (string, error) {
|
|
||||||
addr = strings.TrimSpace(addr)
|
|
||||||
at := strings.LastIndex(addr, "@")
|
|
||||||
if at <= 0 || at == len(addr)-1 {
|
|
||||||
return "", ErrInvalidAddress
|
|
||||||
}
|
|
||||||
local, domain := addr[:at], addr[at+1:]
|
|
||||||
return local + "@" + strings.ToLower(domain), nil
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
// Unit-Test-Beispiel (QA-01 Akzeptanzkriterium 1/Prüfung 2): keine
|
|
||||||
// externe Abhängigkeit (DB, Netzwerk), reine Funktionsprüfung.
|
|
||||||
package example
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNormalizeAddress_LowercasesDomainOnly(t *testing.T) {
|
|
||||||
got, err := NormalizeAddress("User.Name@Example.COM")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unerwarteter fehler: %v", err)
|
|
||||||
}
|
|
||||||
want := "User.Name@example.com"
|
|
||||||
if got != want {
|
|
||||||
t.Fatalf("got %q, want %q", got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeAddress_TrimsWhitespace(t *testing.T) {
|
|
||||||
got, err := NormalizeAddress(" user@example.com ")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unerwarteter fehler: %v", err)
|
|
||||||
}
|
|
||||||
if got != "user@example.com" {
|
|
||||||
t.Fatalf("got %q", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeAddress_RejectsInvalidInput(t *testing.T) {
|
|
||||||
cases := []string{"", "no-at-sign", "@nolocalpart.com", "trailing@"}
|
|
||||||
for _, c := range cases {
|
|
||||||
if _, err := NormalizeAddress(c); !errors.Is(err, ErrInvalidAddress) {
|
|
||||||
t.Fatalf("input %q: erwartet ErrInvalidAddress, habe: %v", c, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package example
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddressStore ist das Integrationstest-Beispiel (QA-01): eine
|
|
||||||
// minimale, aber echte DB-gestützte Komponente — nutzt dieselbe
|
|
||||||
// Tenant-DB-Isolationskonvention wie DMS/Archive (t.Cleanup, geteilte
|
|
||||||
// physische Postgres-Instanz auf dem Testhost).
|
|
||||||
type AddressStore struct {
|
|
||||||
pool *pgxpool.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAddressStore(pool *pgxpool.Pool) *AddressStore {
|
|
||||||
return &AddressStore{pool: pool}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AddressStore) SaveNormalized(ctx context.Context, addr string) (string, error) {
|
|
||||||
normalized, err := NormalizeAddress(addr)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if _, err := s.pool.Exec(ctx, `
|
|
||||||
INSERT INTO example_addresses (address) VALUES ($1)
|
|
||||||
ON CONFLICT (address) DO NOTHING
|
|
||||||
`, normalized); err != nil {
|
|
||||||
return "", fmt.Errorf("example: adresse speichern: %w", err)
|
|
||||||
}
|
|
||||||
return normalized, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AddressStore) Exists(ctx context.Context, addr string) (bool, error) {
|
|
||||||
var exists bool
|
|
||||||
if err := s.pool.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM example_addresses WHERE address = $1)`, addr).Scan(&exists); err != nil {
|
|
||||||
return false, fmt.Errorf("example: existenz prüfen: %w", err)
|
|
||||||
}
|
|
||||||
return exists, nil
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
// Integrations-Test-Beispiel (QA-01 Akzeptanzkriterium 1/Prüfung 2):
|
|
||||||
// echte Postgres-Instanz, folgt derselben Testhost-Konvention wie
|
|
||||||
// DMS/Archive/Core (TEST_TENANT_DSN, t.Cleanup, geteilte physische
|
|
||||||
// Instanz auf 192.168.1.131 — siehe project-nexarch-test-infra).
|
|
||||||
package example
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgxpool"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setupTest(t *testing.T) *pgxpool.Pool {
|
|
||||||
t.Helper()
|
|
||||||
dsn := os.Getenv("TEST_TENANT_DSN")
|
|
||||||
if dsn == "" {
|
|
||||||
t.Skip("TEST_TENANT_DSN nicht gesetzt, Integrationstest übersprungen")
|
|
||||||
}
|
|
||||||
ctx := context.Background()
|
|
||||||
pool, err := pgxpool.New(ctx, dsn)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("pool: %v", err)
|
|
||||||
}
|
|
||||||
t.Cleanup(func() { pool.Close() })
|
|
||||||
|
|
||||||
if _, err := pool.Exec(ctx, `
|
|
||||||
CREATE TABLE IF NOT EXISTS example_addresses (
|
|
||||||
address TEXT PRIMARY KEY
|
|
||||||
);
|
|
||||||
`); err != nil {
|
|
||||||
t.Fatalf("schema: %v", err)
|
|
||||||
}
|
|
||||||
t.Cleanup(func() {
|
|
||||||
_, _ = pool.Exec(context.Background(), `TRUNCATE example_addresses`)
|
|
||||||
})
|
|
||||||
return pool
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddressStore_SaveAndCheckExists(t *testing.T) {
|
|
||||||
pool := setupTest(t)
|
|
||||||
store := NewAddressStore(pool)
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
normalized, err := store.SaveNormalized(ctx, "Kunde@Beispiel.DE")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("savenormalized: %v", err)
|
|
||||||
}
|
|
||||||
if normalized != "Kunde@beispiel.de" {
|
|
||||||
t.Fatalf("erwartet normalisierte adresse, habe %q", normalized)
|
|
||||||
}
|
|
||||||
|
|
||||||
exists, err := store.Exists(ctx, normalized)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
t.Fatal("erwartet real gespeicherte adresse")
|
|
||||||
}
|
|
||||||
|
|
||||||
notExists, err := store.Exists(ctx, "unbekannt@beispiel.de")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if notExists {
|
|
||||||
t.Fatal("nie gespeicherte adresse haette nicht existieren duerfen")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
// Package pflichttestgate erzwingt die in docs/TESTSTRATEGIE-MAIL.md
|
|
||||||
// Abschnitt 4 festgelegte Regel: jede geänderte Go-Datei in einem
|
|
||||||
// sicherheitskritischen Bereich (Auth, Tenant-Scoping, Protokoll-/
|
|
||||||
// Compliance-kritisch) muss von einer geänderten oder neuen _test.go-
|
|
||||||
// Datei im selben Package begleitet sein. Bewusste Code-Kopie des
|
|
||||||
// Musters aus Core internal/pflichttestgate — Mail ist ein eigenständiges
|
|
||||||
// Go-Modul und kann Core nicht importieren.
|
|
||||||
package pflichttestgate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// sensitivePathPatterns beschreibt die Bereiche aus
|
|
||||||
// TESTSTRATEGIE-MAIL.md Abschnitt 4.
|
|
||||||
var sensitivePathPatterns = []*regexp.Regexp{
|
|
||||||
regexp.MustCompile(`(^|/)mail/internal/auth/`),
|
|
||||||
regexp.MustCompile(`(^|/)mail/internal/tenant/`),
|
|
||||||
regexp.MustCompile(`(^|/)mail/internal/ingest/`),
|
|
||||||
regexp.MustCompile(`(^|/)mail/internal/imap/`),
|
|
||||||
regexp.MustCompile(`(^|/)mail/internal/smtp/`),
|
|
||||||
regexp.MustCompile(`(^|/)mail/internal/arc/`),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Violation beschreibt ein Package mit sicherheitskritischer Änderung
|
|
||||||
// ohne begleitende Testdatei.
|
|
||||||
type Violation struct {
|
|
||||||
Package string
|
|
||||||
ChangedFile string
|
|
||||||
}
|
|
||||||
|
|
||||||
func isSensitive(file string) bool {
|
|
||||||
if !strings.HasSuffix(file, ".go") || strings.HasSuffix(file, "_test.go") {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, re := range sensitivePathPatterns {
|
|
||||||
if re.MatchString(file) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckDiff prüft eine Liste geänderter Dateipfade gegen die
|
|
||||||
// Pflichttest-Regel — ein leeres Ergebnis bedeutet: Gate besteht.
|
|
||||||
func CheckDiff(changedFiles []string) []Violation {
|
|
||||||
sensitiveByPkg := map[string]string{}
|
|
||||||
testTouchedPkgs := map[string]bool{}
|
|
||||||
|
|
||||||
for _, f := range changedFiles {
|
|
||||||
pkg := path.Dir(f)
|
|
||||||
if strings.HasSuffix(f, "_test.go") {
|
|
||||||
testTouchedPkgs[pkg] = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if isSensitive(f) {
|
|
||||||
if _, seen := sensitiveByPkg[pkg]; !seen {
|
|
||||||
sensitiveByPkg[pkg] = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var violations []Violation
|
|
||||||
for pkg, file := range sensitiveByPkg {
|
|
||||||
if !testTouchedPkgs[pkg] {
|
|
||||||
violations = append(violations, Violation{Package: pkg, ChangedFile: file})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return violations
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
// Negativtest des Gates selbst (QA-01 Prüfung 1): ein Diff mit
|
|
||||||
// geänderter mail/internal/auth/login.go ohne begleitende Testdatei
|
|
||||||
// muss als Verstoß erkannt werden.
|
|
||||||
package pflichttestgate
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestCheckDiff_FlagsSensitiveChangeWithoutTest(t *testing.T) {
|
|
||||||
violations := CheckDiff([]string{"mail/internal/auth/login.go"})
|
|
||||||
if len(violations) != 1 {
|
|
||||||
t.Fatalf("erwartet genau 1 verstoß, habe %d: %+v", len(violations), violations)
|
|
||||||
}
|
|
||||||
if violations[0].Package != "mail/internal/auth" {
|
|
||||||
t.Fatalf("falsches package gemeldet: %+v", violations[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckDiff_PassesWhenTestFileAccompanies(t *testing.T) {
|
|
||||||
violations := CheckDiff([]string{
|
|
||||||
"mail/internal/auth/login.go",
|
|
||||||
"mail/internal/auth/login_test.go",
|
|
||||||
})
|
|
||||||
if len(violations) != 0 {
|
|
||||||
t.Fatalf("erwartet keine verstöße, habe: %+v", violations)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckDiff_IgnoresNonSensitivePaths(t *testing.T) {
|
|
||||||
violations := CheckDiff([]string{"mail/internal/example/normalize.go"})
|
|
||||||
if len(violations) != 0 {
|
|
||||||
t.Fatalf("erwartet keine verstöße für nicht-sensiblen pfad, habe: %+v", violations)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Executable
+79
@@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# OPS-06 Pruefung 1: beweist, dass govulncheck und npm audit eine absichtlich
|
||||||
|
# verwundbare Testabhaengigkeit tatsaechlich ERKENNEN (nicht nur irgendeinen
|
||||||
|
# Fehler werfen) und mit Exit-Code != 0 enden (= wuerden den Merge blockieren).
|
||||||
|
# Laeuft auf dem Testhost (Go/npm noetig), nicht auf der Entwicklungsmaschine.
|
||||||
|
#
|
||||||
|
# Wichtig: ein Exit-Code != 0 allein ist KEIN Beweis — ein fehlendes Tool
|
||||||
|
# ("command not found", exit 127) sieht fuer ein reines Exit-Code-Gate genauso
|
||||||
|
# aus wie ein echter Fund. Deshalb prueft dieses Skript zusaetzlich, dass die
|
||||||
|
# erwartete Advisory-Kennung tatsaechlich in der Ausgabe steht.
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# "go install" legt Binaries in $(go env GOPATH)/bin ab — auf frischen Hosts
|
||||||
|
# ist das nicht zwangslaeufig im PATH (gefunden 2026-08-28 auf dem Testhost:
|
||||||
|
# govulncheck installierte erfolgreich, "command -v govulncheck" schlug danach
|
||||||
|
# trotzdem fehl, weil /root/go/bin nicht im PATH stand). Defensiv ergaenzen,
|
||||||
|
# statt stillschweigend als "Werkzeug fehlt" fehlzuschlagen.
|
||||||
|
export PATH="$PATH:$(go env GOPATH 2>/dev/null)/bin"
|
||||||
|
|
||||||
|
fail=0
|
||||||
|
|
||||||
|
echo "=== Go-Fixture: golang.org/x/text v0.3.7, GO-2022-1059 (Symbolpfad language.ParseAcceptLanguage) ==="
|
||||||
|
if ! command -v govulncheck >/dev/null 2>&1; then
|
||||||
|
echo "govulncheck fehlt, installiere..."
|
||||||
|
# @latest kann eine govulncheck-Version verlangen, die neuer ist als die
|
||||||
|
# lokal installierte Go-Toolchain (z. B. "requires go >= 1.25.0"). Feste,
|
||||||
|
# bekannt kompatible Version statt @latest, damit die Installation nicht
|
||||||
|
# von der jeweiligen Go-Version des Hosts abhaengt.
|
||||||
|
if ! go install golang.org/x/vuln/cmd/govulncheck@v1.1.3; then
|
||||||
|
echo "FEHLER: govulncheck konnte nicht installiert werden — Pruefung nicht durchfuehrbar, kein Ersatz-'OK'."
|
||||||
|
fail=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v govulncheck >/dev/null 2>&1; then
|
||||||
|
go_output=$(cd testdata/vulnfixture-go && go mod tidy && govulncheck ./... 2>&1)
|
||||||
|
go_exit=$?
|
||||||
|
echo "$go_output"
|
||||||
|
if [ "$go_exit" -eq 0 ]; then
|
||||||
|
echo "FEHLER: govulncheck hat die bekannte Schwachstelle NICHT erkannt (exit 0 erwartet != 0)"
|
||||||
|
fail=1
|
||||||
|
elif ! grep -q "GO-2022-1059" <<<"$go_output"; then
|
||||||
|
echo "FEHLER: exit code $go_exit ist != 0, aber die erwartete Advisory GO-2022-1059 steht NICHT in der Ausgabe — das ist vermutlich ein Werkzeugfehler (z. B. fehlendes govulncheck, Netzwerkproblem), kein echter Fund. Kein 'OK'."
|
||||||
|
fail=1
|
||||||
|
else
|
||||||
|
echo "OK: govulncheck hat GO-2022-1059 tatsaechlich als Fund gemeldet, exit code $go_exit (!= 0, Gate wuerde blockieren)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Bug (gefunden 2026-08-28): dieser Zweig druckte vorher nur eine Meldung,
|
||||||
|
# setzte aber "fail" NICHT — das Skript endete trotzdem mit Exit-Code 0 und
|
||||||
|
# "PRUEFUNG 1 BESTANDEN", obwohl der Go-Teil real nicht lief. Exakt derselbe
|
||||||
|
# Fehlerklasse (Werkzeugfehler zaehlt als Erfolg), nur eine Ebene hoeher.
|
||||||
|
echo "FEHLER: govulncheck weiterhin nicht verfuegbar — Go-Teil der Pruefung nicht durchgefuehrt, kein Ersatz-'OK'."
|
||||||
|
fail=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== npm-Fixture: lodash 4.17.4 (mehrere bekannte kritische CVEs) ==="
|
||||||
|
npm_output=$(cd testdata/vulnfixture-npm && npm install --package-lock-only --no-audit --no-fund 2>&1 \
|
||||||
|
&& npm audit --audit-level=high 2>&1)
|
||||||
|
npm_exit=$?
|
||||||
|
echo "$npm_output"
|
||||||
|
if [ "$npm_exit" -eq 0 ]; then
|
||||||
|
echo "FEHLER: npm audit hat die bekannte Schwachstelle NICHT erkannt (exit 0 erwartet != 0)"
|
||||||
|
fail=1
|
||||||
|
elif ! grep -qiE "severity|vulnerabilit" <<<"$npm_output"; then
|
||||||
|
echo "FEHLER: exit code $npm_exit ist != 0, aber die Ausgabe enthaelt keinen erkennbaren Schwachstellen-Hinweis — vermutlich ein Werkzeugfehler (z. B. fehlendes npm, Netzwerkproblem), kein echter Fund. Kein 'OK'."
|
||||||
|
fail=1
|
||||||
|
else
|
||||||
|
echo "OK: npm audit hat die Schwachstelle tatsaechlich gemeldet, exit code $npm_exit (!= 0, Gate wuerde blockieren)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
if [ "$fail" -eq 0 ]; then
|
||||||
|
echo "PRUEFUNG 1 BESTANDEN: beide Gates erkennen eine absichtlich verwundbare Testabhaengigkeit inhaltlich (nicht nur per Exit-Code) und wuerden blockieren."
|
||||||
|
else
|
||||||
|
echo "PRUEFUNG 1 FEHLGESCHLAGEN: siehe FEHLER oben."
|
||||||
|
fi
|
||||||
|
exit $fail
|
||||||
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
// Absichtlich verwundbares Fixture-Modul fuer OPS-06 Pruefung 1: beweist, dass
|
||||||
|
// der govulncheck-CI-Schritt eine bekannte Schwachstelle tatsaechlich erkennt
|
||||||
|
// und den Lauf mit Exit-Code != 0 beendet. Eigenes go.mod, damit die
|
||||||
|
// veraltete, verwundbare Abhaengigkeit NICHT im Hauptmodul landet.
|
||||||
|
module gitea.perlbach24.de/scripte/nexarch/testdata/vulnfixture-go
|
||||||
|
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
// golang.org/x/text v0.3.7: GO-2022-1059 — Denial of Service durch
|
||||||
|
// uebermaessigen Ressourcenverbrauch beim Parsen von Accept-Language-Headern
|
||||||
|
// (language.ParseAcceptLanguage). Bewusst auf dieser verwundbaren Version
|
||||||
|
// gepinnt, siehe scripts/verify-supply-chain-gate.sh — main.go ruft gezielt
|
||||||
|
// den verwundbaren Symbolpfad auf, nicht nur irgendeine Funktion des Pakets.
|
||||||
|
require golang.org/x/text v0.3.7
|
||||||
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
// Fixture fuer OPS-06 Pruefung 1 — ruft tatsaechlich in die verwundbare
|
||||||
|
// Funktion hinein, damit govulncheck den Aufrufpfad (nicht nur die
|
||||||
|
// Modul-Abhaengigkeit) als erreichbar erkennt.
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// ParseAcceptLanguage ist der tatsaechlich verwundbare Aufrufpfad
|
||||||
|
// (GO-2022-1059), im Unterschied zu Parse() — govulncheck bewertet
|
||||||
|
// Erreichbarkeit auf Symbol-, nicht nur Paket-Ebene.
|
||||||
|
tags, _, err := language.ParseAcceptLanguage("de-DE,de;q=0.9,en;q=0.8")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println(tags)
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "nexarch-vulnfixture-npm",
|
||||||
|
"private": true,
|
||||||
|
"description": "Absichtlich verwundbares Fixture-Paket fuer OPS-06 Pruefung 1 — beweist, dass npm audit eine bekannte Schwachstelle erkennt und den Lauf blockiert. Nicht Teil eines echten NEXARCH-Frontends.",
|
||||||
|
"dependencies": {
|
||||||
|
"lodash": "4.17.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user