diff --git a/.audit-allowlist.json b/.audit-allowlist.json new file mode 100644 index 0000000..d1f1ed0 --- /dev/null +++ b/.audit-allowlist.json @@ -0,0 +1,17 @@ +{ + "_comment": "Befristete Ausnahmen für scripts/audit-gate.mjs. Grundsätzlich nur Findings ohne Upstream-Fix im Build-Tooling (nicht im App-Bundle); Ausnahmen davon sind im reason begründet. Nach Ablauf schlägt das Gate fehl -> neu bewerten (DrawFromMemory#348).", + "advisories": [ + { + "id": "GHSA-mrhx-c7xc-m3w5", + "package": "zod", + "reason": "Speicher-DoS bei übergroßen Collections: nur noch zod@3 via expo -> @expo/cli (Build-Tooling, validiert lokale Konfiguration); kein gepatchter 3.x-Release, @expo/cli verlangt ^3.25.76. Root-zod (eslint-plugin-react-hooks) auf 4.6.7 gehoben.", + "expires": "2026-12-31" + }, + { + "id": "GHSA-qr28-fpjm-wq7f", + "package": "decode-uri-component", + "reason": "Quadratische Laufzeit bei langen %-kodierten Sequenzen. AUSNAHME: im App-Bundle (expo-router -> query-string@7 -> decode-uri-component@0.2.2). Fix erst in 0.5.1 (ESM-only, inkompatibel mit query-string@7); npm audit schlägt nur expo-router 58 (SDK-Major) vor. Risiko gering: betrifft nur das Parsen von App-URLs/Deep-Links auf dem Gerät des Nutzers selbst (kein Server). Mit dem nächsten SDK-Upgrade auflösen.", + "expires": "2026-12-31" + } + ] +} diff --git "a/.claude/commands/aufr\303\244umen.md" "b/.claude/commands/aufr\303\244umen.md" index fd2bb4f..8180233 100644 --- "a/.claude/commands/aufr\303\244umen.md" +++ "b/.claude/commands/aufr\303\244umen.md" @@ -4,6 +4,13 @@ Führe den täglichen Cleanup-Workflow durch: ## 0. Branch-Übersicht: testing vs. main (alle Repos) +**Nur lokal anwendbar** — der Pfad `/Users/svenstrohkark/...` existiert nur auf +diesem Mac. In Remote-Execution-Sessions trifft `[ -d "$dir/.git" ]` nie zu, die +Schleife läuft ohne Ausgabe und ohne Fehler durch. Läuft die Session nicht lokal +auf diesem Rechner: Abschnitt überspringen und dem Nutzer explizit sagen, dass +diese Prüfung hier nicht durchgeführt werden konnte — nicht stillschweigend +weiterlaufen. + Erstelle zu Beginn eine Tabelle aller Repositories mit dem Stand von `testing` gegenüber `main`: ```bash @@ -40,9 +47,47 @@ Zeige das Ergebnis als Markdown-Tabelle: ## 2. Branches aufräumen -- Liste alle merged Feature Branches (lokal und remote) -- Frage ob diese gelöscht werden sollen -- Lösche approved Branches +**Wichtig:** `git branch --merged` und `git merge-base --is-ancestor` sind bei +Squash-Merge-Policy unbrauchbar — sie melden gemergte Branches als „nicht gemergt". +Maßgeblich ist die PR-Historie, und dort das Feld `merged_at` (nicht `merged`; +letzteres ist über MCP-GitHub-Tools unzuverlässig und liefert teils `false` +auch bei nachweislich gemergten PRs). + +```bash +git fetch --all --prune -q +for b in $(git branch -r --format='%(refname:short)' \ + | grep -vE 'origin/(HEAD|main|testing|staging|gh-pages)$'); do + br=${b#origin/} + pr=$(gh pr list --head "$br" --state merged --json number,mergedAt --limit 1) + if [ "$(echo "$pr" | jq 'length')" -gt 0 ]; then + echo "✅ $br → PR #$(echo "$pr" | jq -r '.[0].number') gemergt" + else + echo "⚠️ $br → kein gemergter PR — Inhalt prüfen" + fi +done +``` + +Bei `⚠️` zweite Stufe, bevor gelöscht wird — prüft, ob der Branch-Inhalt +inhaltlich schon im Ziel liegt (z.B. bei umbenanntem Squash-Commit): + +```bash +mb=$(git merge-base origin/testing origin/$br) +files=$(git diff --name-only $mb origin/$br) +[ -n "$files" ] && git diff --name-only origin/testing origin/$br -- $files +# leere Ausgabe = Inhalt vollständig in testing → löschbar +``` + +- **Vor dem Löschen fragen**, nur bestätigte Branches löschen +- `main`, `testing`, `staging`, `gh-pages` **nie** löschen — auch nicht beim Bulk-Delete +- Schlägt `git push --delete` mit HTTP 403 fehl (Remote-Execution-Umgebungen): + Exit-Code ist trotzdem 0 und die letzte Zeile lautet „Everything up-to-date". + Nicht als Erfolg werten — stattdessen den fertigen Löschbefehl für die lokale + Ausführung ausgeben. +- Feature-Branches nach Merge löschen: `gh pr merge --delete-branch` explizit + verwenden. `scripts/apply-rulesets.sh` setzt `delete_branch_on_merge=false` + zentral (seit Issue #122 Korrektur 4, siehe `dev-standards/global-policy.md`, + „Merge-Methode zentral erzwingen") — GitHubs Auto-Delete löscht sonst auch + den Head-Branch von Release-PRs (`testing → main`), also `testing` selbst ## 3. GitHub Actions Status @@ -62,6 +107,38 @@ Zeige das Ergebnis als Markdown-Tabelle: - Zeige kürzlich geschlossene Issues (heute) - Weise auf Issues ohne Label hin +### 5a. Erledigte Issues aufspüren (Issue #111) + +`Closes #X` schließt ein Issue nur beim Merge in den **Default-Branch** (`main`). +Da alle PRs nach `testing` gehen, greift das Keyword faktisch nie — Issues bleiben +offen, obwohl der Code längst gemergt ist. + +Gemergte `testing`-PRs gegen die offenen Issues abgleichen: + +```bash +SLUG=$(git remote get-url origin | sed -E 's#.*github.com[:/]##; s#\.git$##') +OPEN=$(gh issue list -R "$SLUG" --state open --limit 100 --json number -q '.[].number' | tr '\n' ' ') +gh pr list -R "$SLUG" --state merged --base testing --limit 60 --json number,body \ + --jq '.[] | (.body // "") as $b + | ($b|[scan("(?i)(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\\s+#(\\d+)")]|flatten|unique) as $r + | select($r|length>0) | "\(.number) \($r|join(" "))"' | +while read -r pr refs; do + for i in $refs; do + case " $OPEN " in *" $i "*) echo "Issue #$i erledigt durch PR #$pr";; esac + done +done +``` + +**Nur vorschlagen, nicht automatisch schließen.** Sammel-/Meta-Issues ("Backlog", +"Maßnahmenkatalog", "Tracking") werden von Teil-PRs oft fälschlich mit `Closes` +referenziert, sind aber nicht erledigt. Vor dem Schließen den Issue-Titel lesen. + +Beim Schließen den Grund vermerken, damit die Historie nachvollziehbar bleibt: + +```bash +gh issue close -R "$SLUG" -c "Umgesetzt in #, gemergt nach \`testing\`." +``` + ## 6. Dependencies & Security - Prüfe ob `package.json` Updates braucht (via npm outdated) diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ecdbfb7 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,34 @@ +# Dependabot – kostenlose Dependency-Updates + Security-Alerts. +# Ersetzt zusammen mit codeql.yml den kostenpflichtigen Anthropic-API-Scan. + +version: 2 +updates: + # GitHub-Actions-Versionen (gilt für ALLE Repos, auch ohne package.json). + - package-ecosystem: 'github-actions' + directory: '/' + target-branch: 'testing' + schedule: + interval: 'monthly' + open-pull-requests-limit: 5 + commit-message: + prefix: 'ci' + groups: + # Kein update-types-Filter → Major landet mit im Sammel-PR. + actions-all: + patterns: + - '*' + + # NPM/Yarn/PNPM-Abhängigkeiten. + - package-ecosystem: 'npm' + directory: '/' + target-branch: 'testing' + schedule: + interval: 'monthly' + open-pull-requests-limit: 5 + commit-message: + prefix: 'chore' + groups: + # Kein update-types-Filter → Major landet mit im Sammel-PR. + npm-all: + patterns: + - '*' diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 3a0fd02..c5cdb1a 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -4,13 +4,20 @@ on: push: branches: [main, testing] pull_request: - branches: [main] + branches: [main, testing] # Security: Explicitly set minimal permissions permissions: contents: read jobs: + # ============================================================================ + # JOB 0: Workflow-Linting (Issue #445) + # ============================================================================ + actionlint: + name: ⚙️ Workflow Linting + uses: S540d/project-templates/.github/workflows/reusable-actionlint.yml@v2 + # ============================================================================ # JOB 1: Code Quality & Linting # ============================================================================ @@ -82,7 +89,7 @@ jobs: run: npm run validate:svg-counts - name: TypeScript Type Check - run: npx tsc --noEmit || true + run: npx tsc --noEmit # ============================================================================ # JOB 2: Unit Tests & Coverage @@ -196,8 +203,9 @@ jobs: with: node-version: '20' - - name: Run npm audit - run: npm audit --audit-level=high + # Gate mit befristeter Allowlist (.audit-allowlist.json) für Findings ohne Upstream-Fix (#348). + - name: Run npm audit (Gate) + run: node scripts/audit-gate.mjs --level=high - name: Check for sensitive data run: | diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..515a43e --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,53 @@ +name: 'CodeQL' + +# Kostenlose statische Code-Analyse (SAST) via GitHub CodeQL. +# Zusammen mit .github/dependabot.yml der Ersatz für den kostenpflichtigen +# Anthropic-API-Scan. build-mode: none — CodeQL analysiert den Quellcode +# direkt, ohne Autobuild (bei RN-/Expo-Projekten schlägt Autobuild sonst fehl). + +on: + push: + branches: [main] + pull_request: + branches: [main, testing] + schedule: + # Wöchentlicher Full-Scan: fängt neu bekannt gewordene CVEs ohne Code-Änderung. + - cron: '30 5 * * 1' + +permissions: + actions: read + contents: read + security-events: write + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + include: + - language: javascript-typescript + build-mode: none + - language: actions + build-mode: none + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: '/language:${{matrix.language}}' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 01d8c12..61968c0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -38,7 +38,7 @@ jobs: run: node scripts/update-cache-version.js - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./dist diff --git a/.github/workflows/standards-audit.yml b/.github/workflows/standards-audit.yml index f8832e0..7623fb2 100644 --- a/.github/workflows/standards-audit.yml +++ b/.github/workflows/standards-audit.yml @@ -21,12 +21,12 @@ permissions: jobs: security-scan: - uses: S540d/project-templates/.github/workflows/reusable-security-scan.yml@v1 + uses: S540d/project-templates/.github/workflows/reusable-security-scan.yml@v2 with: fail_on_findings: true gitignore-audit: - uses: S540d/project-templates/.github/workflows/reusable-gitignore-audit.yml@v1 + uses: S540d/project-templates/.github/workflows/reusable-gitignore-audit.yml@v2 with: fail_on_missing: true @@ -35,4 +35,4 @@ jobs: # auf uses:-Jobs nicht erlaubt – die Non-Blocking-Semantik muss daher im Template # bleiben; security-scan und gitignore-audit (fail_on_*: true) gaten den Merge. dev-standards-audit: - uses: S540d/project-templates/.github/workflows/reusable-dev-standards-audit.yml@v1 + uses: S540d/project-templates/.github/workflows/reusable-dev-standards-audit.yml@v2 diff --git a/.gitignore b/.gitignore index 53d392a..214bd19 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,7 @@ yarn-error.log* .claude/CLAUDE.md .claude/settings.json .claude/settings.local.json -.claude/memory/ +.claude/memory.md .claude/todos/ .claude/cache/ .claude/data/ diff --git a/CHANGELOG.md b/CHANGELOG.md index cc64686..81384af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,20 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.9.0] - 2026-11-03 (versionCode 71) + +Änderungen seit der Play-Store-Version 1.8.0 (versionCode 69). + ### Added -- Auffindbarkeit außerhalb des Play Stores: Play-Store-Badge + deutsche Kurzbeschreibung im README, Web-only Install-Banner (`WebInstallBanner`) im sichtbaren Bereich der Startseite, deutsche Keywords/Open-Graph-/JSON-LD-Auszeichnung der Landing Page +- Pause-Vorschlag nach jedem 5. abgeschlossenen Level (5, 10, 15) mit „Hauptmenü" und „Weiter" (#337) +- Impressum und Datenschutz im Über-Dialog der Einstellungen, in allen 7 Sprachen (#330) +- Micro-Animationen auf den Kern-Screens: Press-Feedback auf Kacheln und Buttons, `prefers-reduced-motion`-aware (`PressableScale`, `PulseView`) +- Auffindbarkeit außerhalb des Play Stores: Play-Store-Badge + deutsche Kurzbeschreibung im README, Web-only Install-Hinweis auf der Startseite, deutsche Keywords/Open-Graph-/JSON-LD-Auszeichnung der Landing Page - Attributions-Parameter (`referrer` mit UTM-Tags) an allen Play-Store-Links — Installationen aus der Web-Demo werden in der Play Console als eigene Quelle sichtbar (`constants/ExternalLinks.ts`) - Crawlbarer `