pipeguard-cli 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. pipeguard_cli-0.1.0/.github/release-drafter.yml +52 -0
  2. pipeguard_cli-0.1.0/.github/workflows/ci.yml +50 -0
  3. pipeguard_cli-0.1.0/.github/workflows/deploy.yml +47 -0
  4. pipeguard_cli-0.1.0/.github/workflows/release-drafter.yml +19 -0
  5. pipeguard_cli-0.1.0/.github/workflows/update-cve-db.yml +58 -0
  6. pipeguard_cli-0.1.0/.gitignore +45 -0
  7. pipeguard_cli-0.1.0/CLAUDE.md +102 -0
  8. pipeguard_cli-0.1.0/LICENSE +178 -0
  9. pipeguard_cli-0.1.0/PKG-INFO +277 -0
  10. pipeguard_cli-0.1.0/README.md +258 -0
  11. pipeguard_cli-0.1.0/assets/demo.gif +0 -0
  12. pipeguard_cli-0.1.0/assets/logo-dark.svg +42 -0
  13. pipeguard_cli-0.1.0/assets/logo-light.svg +42 -0
  14. pipeguard_cli-0.1.0/pipeguard/__init__.py +5 -0
  15. pipeguard_cli-0.1.0/pipeguard/_version.py +34 -0
  16. pipeguard_cli-0.1.0/pipeguard/cli.py +92 -0
  17. pipeguard_cli-0.1.0/pipeguard/output/__init__.py +0 -0
  18. pipeguard_cli-0.1.0/pipeguard/output/autofix.py +37 -0
  19. pipeguard_cli-0.1.0/pipeguard/output/formatter.py +133 -0
  20. pipeguard_cli-0.1.0/pipeguard/scanner/__init__.py +3 -0
  21. pipeguard_cli-0.1.0/pipeguard/scanner/base.py +24 -0
  22. pipeguard_cli-0.1.0/pipeguard/scanner/github_actions/__init__.py +0 -0
  23. pipeguard_cli-0.1.0/pipeguard/scanner/github_actions/action_inventory.py +59 -0
  24. pipeguard_cli-0.1.0/pipeguard/scanner/github_actions/actionlint_runner.py +43 -0
  25. pipeguard_cli-0.1.0/pipeguard/scanner/github_actions/cve_check.py +88 -0
  26. pipeguard_cli-0.1.0/pipeguard/scanner/github_actions/cve_db.json +110 -0
  27. pipeguard_cli-0.1.0/pipeguard/scanner/github_actions/permissions.py +235 -0
  28. pipeguard_cli-0.1.0/pipeguard/scanner/github_actions/pull_request_target.py +108 -0
  29. pipeguard_cli-0.1.0/pipeguard/scanner/github_actions/secrets_flow.py +155 -0
  30. pipeguard_cli-0.1.0/pipeguard/scanner/github_actions/sha_pinning.py +90 -0
  31. pipeguard_cli-0.1.0/pipeguard/scanner/github_actions/supply_chain.py +103 -0
  32. pipeguard_cli-0.1.0/pyproject.toml +56 -0
  33. pipeguard_cli-0.1.0/scripts/update_cve_db.py +178 -0
  34. pipeguard_cli-0.1.0/tests/__init__.py +0 -0
  35. pipeguard_cli-0.1.0/tests/fixtures/cve_workflow.yml +20 -0
  36. pipeguard_cli-0.1.0/tests/fixtures/id_token_unused.yml +15 -0
  37. pipeguard_cli-0.1.0/tests/fixtures/id_token_used.yml +18 -0
  38. pipeguard_cli-0.1.0/tests/fixtures/insecure_workflow.yml +23 -0
  39. pipeguard_cli-0.1.0/tests/fixtures/permissions_missing.yml +10 -0
  40. pipeguard_cli-0.1.0/tests/fixtures/permissions_ok.yml +13 -0
  41. pipeguard_cli-0.1.0/tests/fixtures/permissions_overprovision.yml +12 -0
  42. pipeguard_cli-0.1.0/tests/fixtures/pr_target_pwn.yml +16 -0
  43. pipeguard_cli-0.1.0/tests/fixtures/pr_target_safe.yml +16 -0
  44. pipeguard_cli-0.1.0/tests/fixtures/reusable_pinned.yml +12 -0
  45. pipeguard_cli-0.1.0/tests/fixtures/reusable_unpinned.yml +12 -0
  46. pipeguard_cli-0.1.0/tests/fixtures/secure_workflow.yml +27 -0
  47. pipeguard_cli-0.1.0/tests/test_action_inventory.py +55 -0
  48. pipeguard_cli-0.1.0/tests/test_cve_check.py +62 -0
  49. pipeguard_cli-0.1.0/tests/test_debug_leak.py +103 -0
  50. pipeguard_cli-0.1.0/tests/test_id_token.py +74 -0
  51. pipeguard_cli-0.1.0/tests/test_permissions.py +87 -0
  52. pipeguard_cli-0.1.0/tests/test_pull_request_target.py +94 -0
  53. pipeguard_cli-0.1.0/tests/test_scan_folder.py +71 -0
  54. pipeguard_cli-0.1.0/tests/test_sha_pinning.py +92 -0
  55. pipeguard_cli-0.1.0/tests/test_supply_chain.py +64 -0
  56. pipeguard_cli-0.1.0/uv.lock +660 -0
@@ -0,0 +1,52 @@
1
+ name-template: "v$RESOLVED_VERSION"
2
+ tag-template: "v$RESOLVED_VERSION"
3
+
4
+ categories:
5
+ - title: "Features"
6
+ labels:
7
+ - "feature"
8
+ - "enhancement"
9
+ - title: "Bug Fixes"
10
+ labels:
11
+ - "bug"
12
+ - "fix"
13
+ - title: "Security"
14
+ labels:
15
+ - "security"
16
+ - title: "Documentation"
17
+ labels:
18
+ - "documentation"
19
+ - "docs"
20
+ - title: "Maintenance"
21
+ labels:
22
+ - "chore"
23
+ - "dependencies"
24
+ - "ci"
25
+
26
+ change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
27
+ change-title-escapes: '\<*_&'
28
+
29
+ version-resolver:
30
+ major:
31
+ labels:
32
+ - "breaking-change"
33
+ minor:
34
+ labels:
35
+ - "feature"
36
+ - "enhancement"
37
+ patch:
38
+ labels:
39
+ - "bug"
40
+ - "fix"
41
+ - "security"
42
+ - "documentation"
43
+ - "chore"
44
+ default: patch
45
+
46
+ template: |
47
+ ## Changes
48
+
49
+ $CHANGES
50
+
51
+ ---
52
+ **Full Changelog**: $PREVIOUS_TAG...v$RESOLVED_VERSION
@@ -0,0 +1,50 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ lint:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
19
+ with:
20
+ python-version: "3.11"
21
+
22
+ - name: Install dependencies
23
+ run: pip install -e ".[dev]"
24
+
25
+ - name: Ruff
26
+ run: ruff check pipeguard tests
27
+
28
+ - name: Mypy
29
+ run: mypy pipeguard
30
+
31
+ test:
32
+ runs-on: ubuntu-latest
33
+ strategy:
34
+ fail-fast: false
35
+ matrix:
36
+ python-version: ["3.11", "3.12", "3.13"]
37
+
38
+ steps:
39
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
40
+
41
+ - name: Set up Python ${{ matrix.python-version }}
42
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
43
+ with:
44
+ python-version: ${{ matrix.python-version }}
45
+
46
+ - name: Install dependencies
47
+ run: pip install -e ".[dev]"
48
+
49
+ - name: Test
50
+ run: pytest --cov=pipeguard --cov-report=term-missing
@@ -0,0 +1,47 @@
1
+ name: Deploy to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions:
9
+ contents: write # required for publishing GitHub release
10
+ id-token: write # required for PyPI trusted publishing
11
+
12
+ jobs:
13
+ build-and-publish:
14
+ runs-on: ubuntu-latest
15
+ environment: pypi
16
+
17
+ steps:
18
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
19
+ with:
20
+ fetch-depth: 0 # required for hatch-vcs version detection
21
+
22
+ - name: Set up Python
23
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
24
+ with:
25
+ python-version: "3.11"
26
+
27
+ - name: Install build tools
28
+ run: pip install hatch hatch-vcs
29
+
30
+ - name: Run tests
31
+ run: |
32
+ pip install -e ".[dev]"
33
+ pytest
34
+
35
+ - name: Build package
36
+ run: hatch build
37
+
38
+ - name: Publish to PyPI
39
+ uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4
40
+
41
+ - name: Publish GitHub release
42
+ uses: release-drafter/release-drafter@67e173cadb2fbd3de94f4a861e0c48c913b462ae # v6.4.0
43
+ with:
44
+ publish: true
45
+ tag: ${{ github.ref_name }}
46
+ env:
47
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,19 @@
1
+ name: Release Drafter
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ types: [opened, reopened, synchronize]
8
+
9
+ permissions:
10
+ contents: write
11
+ pull-requests: write
12
+
13
+ jobs:
14
+ draft-release:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: release-drafter/release-drafter@67e173cadb2fbd3de94f4a861e0c48c913b462ae # v6.4.0
18
+ env:
19
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,58 @@
1
+ name: Update CVE Database
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 6 * * *" # daily at 06:00 UTC
6
+ workflow_dispatch: # allow manual trigger
7
+
8
+ permissions:
9
+ contents: write
10
+ pull-requests: write
11
+
12
+ jobs:
13
+ update:
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
21
+ with:
22
+ python-version: "3.11"
23
+
24
+ - name: Fetch new CVEs from NVD
25
+ env:
26
+ NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
27
+ run: |
28
+ python scripts/update_cve_db.py \
29
+ --db pipeguard/scanner/github_actions/cve_db.json \
30
+ --readme README.md \
31
+ --min-cvss 9.0
32
+
33
+ - name: Check for changes
34
+ id: diff
35
+ run: |
36
+ if git diff --quiet pipeguard/scanner/github_actions/cve_db.json README.md; then
37
+ echo "changed=false" >> $GITHUB_OUTPUT
38
+ else
39
+ echo "changed=true" >> $GITHUB_OUTPUT
40
+ echo "### CVE DB changes:" >> $GITHUB_STEP_SUMMARY
41
+ git diff pipeguard/scanner/github_actions/cve_db.json README.md >> $GITHUB_STEP_SUMMARY
42
+ fi
43
+
44
+ - name: Create pull request
45
+ if: steps.diff.outputs.changed == 'true'
46
+ env:
47
+ GH_TOKEN: ${{ github.token }}
48
+ run: |
49
+ BRANCH="chore/cve-db-$(date +%Y-%m-%d)"
50
+ git config user.name "github-actions[bot]"
51
+ git config user.email "github-actions[bot]@users.noreply.github.com"
52
+ git checkout -b "$BRANCH"
53
+ git add pipeguard/scanner/github_actions/cve_db.json README.md
54
+ git commit -m "chore: update CVE database $(date +%Y-%m-%d)"
55
+ git push origin "$BRANCH"
56
+ gh pr create \
57
+ --title "chore: update CVE database $(date +%Y-%m-%d)" \
58
+ --body "Automated daily CVE database update from NVD."
@@ -0,0 +1,45 @@
1
+ # Business context
2
+ CLAUDE.private.md
3
+ docs/private/
4
+
5
+ # Secrets & Umgebung
6
+ .env
7
+ .env.local
8
+ .env.*.local
9
+
10
+ # Python
11
+ pipeguard/_version.py
12
+ __pycache__/
13
+ *.py[cod]
14
+ *.pyo
15
+ .venv/
16
+ venv/
17
+ env/
18
+ dist/
19
+ build/
20
+ *.egg-info/
21
+ .eggs/
22
+ *.egg
23
+
24
+ # Testing & Coverage
25
+ .pytest_cache/
26
+ .coverage
27
+ htmlcov/
28
+ .tox/
29
+
30
+ # IDE
31
+ .vscode/settings.json
32
+ .idea/
33
+ *.swp
34
+ *.swo
35
+
36
+ # PipeGuard-spezifisch
37
+ .pipeguard-cache/
38
+ .pipeguard/tmp/
39
+
40
+ # macOS
41
+ .DS_Store
42
+
43
+ # Distribution
44
+ *.tar.gz
45
+ *.whl
@@ -0,0 +1,102 @@
1
+ # PipeGuard — CLAUDE.md
2
+
3
+ > **"Catch problems before they reach your runners — not after."**
4
+ > Pre-commit security scanner + cloud sandbox for GitHub Actions workflows.
5
+
6
+ ---
7
+
8
+ ## Was ist PipeGuard?
9
+
10
+ PipeGuard analysiert GitHub Actions Workflows **vor dem Push** auf Security-Risiken und testet sie in einer vollwertigen Cloud-Sandbox. Es positioniert sich als fehlende Schicht zwischen Authoring und Execution — nicht als Ersatz für actionlint, StepSecurity oder act, sondern als Ergänzung.
11
+
12
+ ---
13
+
14
+ ## Architektur & Module
15
+
16
+ ### Modul A: Deep Security Scanner (Pre-Commit)
17
+ Statische Analyse in der IDE oder als Git-Hook, bevor Code gepusht wird.
18
+
19
+ - **Action Supply-Chain Audit** — Dependency-Graph aller Actions inkl. transitiver Abhängigkeiten mit Risiko-Score
20
+ - **Permission-Explosion Detector** — simuliert effektiven Permission-Scope, warnt bei Over-Provisioning
21
+ - **Secrets-Flow-Analyse** — trackt Datenfluss von Secrets durch Steps, erkennt potenzielle Lecks
22
+ - **Maintainer Trust Score** — bewertet Third-Party Actions nach Aktivität, CVE-Historie, Repository-Hygiene
23
+ - **Auto-Fix PRs** — generiert automatisch PRs für SHA-Pinning und Permission-Einschränkungen
24
+
25
+ ### Modul B: Workflow Test Lab (Cloud-Sandbox)
26
+ Vollwertiges Workflow-Testing ohne Commit/Push-Zyklus.
27
+
28
+ - **Full-Fidelity Cloud-Sandbox** — GitHub-Actions-kompatible Umgebung inkl. Services, Caches, Artifacts, Matrix-Strategien
29
+ - **One-Click Test aus der IDE** — VS Code / JetBrains: Workflow ändern → Strg+Shift+T → Run in Sandbox
30
+ - **Interactive Debugging** — Breakpoints setzen, SSH / Web-Terminal auf Runner
31
+ - **Cost Estimator** — schätzt Kosten vor Ausführung (Runner-Typ, Matrix-Größe, historische Daten)
32
+ - **Diff & Replay** — zwei Workflow-Versionen vergleichen, fehlgeschlagene Runs wiederholen
33
+
34
+ ---
35
+
36
+ ## Tech Stack
37
+
38
+ ```
39
+ CLI: Python — ruft actionlint als subprocess auf, erweitert die Ausgabe
40
+ IDE-Plugin: VS Code Extension (TypeScript) + JetBrains Plugin (Kotlin) — geplant
41
+ Backend: TBD — API für Cloud-Sandbox, Security-Analyse-Engine
42
+ Cloud-Infra: Partner-Runner für Sandbox-Umgebung (TBD)
43
+ Auth: GitHub OAuth (Personal / Org-Level)
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Repo-Struktur
49
+
50
+ ```
51
+ pipeguard/
52
+ ├── CLAUDE.md ← dieser File
53
+ ├── CLAUDE.private.md
54
+ ├── README.md
55
+ ├── pyproject.toml
56
+ ├── pipeguard/
57
+ │ ├── __init__.py
58
+ │ ├── cli.py (Einstiegspunkt: pipeguard scan)
59
+ │ ├── scanner/
60
+ │ │ ├── actionlint_runner.py (ruft actionlint als subprocess auf)
61
+ │ │ ├── sha_pinning.py (prüft & pinnt Action-Versionen)
62
+ │ │ ├── supply_chain.py (Dependency-Graph, Trust Score)
63
+ │ │ └── secrets_flow.py (Secrets-Leak-Erkennung)
64
+ │ └── output/
65
+ │ ├── formatter.py (Terminal-Output, JSON, SARIF)
66
+ │ └── autofix.py (generiert Fix-Vorschläge)
67
+ ├── tests/
68
+ │ └── fixtures/ (Beispiel-Workflows für Tests)
69
+ └── .github/
70
+ └── workflows/
71
+ └── ci.yml
72
+ ```
73
+
74
+ ---
75
+
76
+ ## Design-Prinzipien
77
+
78
+ - **Standard-YAML-Kompatibilität** — kein proprietäres Format, kein Lock-in
79
+ - **Security first** — Security-Features haben Priorität vor UX-Features
80
+ - **OSS-Kern bleibt kostenlos** — Basis-Security-Checks sind immer öffentlich und gratis
81
+ - **Aufbauen, nicht ersetzen** — actionlint erweitern
82
+ - **Fail fast, fail loud** — Fehler sollen früh und klar sichtbar sein, nie still ignoriert werden
83
+
84
+ ---
85
+
86
+ ## Coding-Regeln
87
+
88
+ - Alle neuen Scanner-Module brauchen Fixtures in `tests/fixtures/` — echte Workflow-Beispiele, keine synthetischen
89
+ - Output immer in drei Formaten unterstützen: Terminal (human-readable), JSON (maschinenlesbar), SARIF (IDE-Integration)
90
+ - Externe Abhängigkeiten minimal halten — actionlint muss lokal installiert sein, alles andere optional
91
+ - Keine Secrets oder API-Keys im Code oder in Tests — Fixtures verwenden ausschließlich Mock-Daten
92
+ - Vor jedem neuen Feature: Issue öffnen mit konkretem Workflow-Beispiel das das Problem demonstriert
93
+
94
+ ---
95
+
96
+ ## Wichtige Referenzen
97
+
98
+ - [actionlint](https://github.com/rhysd/actionlint) — Basis-Linter, auf dem PipeGuard aufbaut
99
+ - [StepSecurity Harden-Runner](https://github.com/step-security/harden-runner) — komplementäres Runtime-Tool
100
+ - [nektos/act](https://github.com/nektos/act) — lokales Testing-Tool (Inspiration + Limitation)
101
+ - [CVE-2025-30066](https://github.com/advisories/GHSA-mrrh-fwg3-99v7) — tj-actions Supply-Chain-Angriff, Hauptmotivation für PipeGuard
102
+ - [CISA Advisory](https://www.cisa.gov/news-events/alerts) — offizielle Warnungen zu GitHub Actions Security
@@ -0,0 +1,178 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship made available under
36
+ the License, as indicated by a copyright notice that is included in
37
+ or attached to the work (an example is provided in the Appendix below).
38
+
39
+ "Derivative Works" shall mean any work, whether in Source or Object
40
+ form, that is based on (or derived from) the Work and for which the
41
+ editorial revisions, annotations, elaborations, or other
42
+ transformations represent, as a whole, an original work of authorship.
43
+ For the purposes of this License, Derivative Works shall not include
44
+ works that remain separable from, or merely link (or bind by name)
45
+ to the interfaces of, the Work and Derivative Works thereof.
46
+
47
+ "Contribution" shall mean, as submitted to the Licensor for inclusion
48
+ in the Work by the copyright owner or by an individual or Legal Entity
49
+ authorized to submit on behalf of the copyright owner. For the purposes
50
+ of this definition, "submitted" means any form of electronic, verbal,
51
+ or written communication sent to the Licensor or its representatives,
52
+ including but not limited to communication on electronic mailing lists,
53
+ source code control systems, and issue tracking systems that are managed
54
+ by, or on behalf of, the Licensor for the purpose of developing the Work,
55
+ but excluding communication that is conspicuously marked or designated in
56
+ writing by the copyright owner as "Not a Contribution."
57
+
58
+ "Contributor" shall mean Licensor and any Legal Entity on behalf of
59
+ whom a Contribution has been received by the Licensor and included
60
+ within the Work.
61
+
62
+ 2. Grant of Copyright License. Subject to the terms and conditions of
63
+ this License, each Contributor hereby grants to You a perpetual,
64
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
65
+ copyright license to reproduce, prepare Derivative Works of,
66
+ publicly display, publicly perform, sublicense, and distribute the
67
+ Work and such Derivative Works in Source or Object form.
68
+
69
+ 3. Grant of Patent License. Subject to the terms and conditions of
70
+ this License, each Contributor hereby grants to You a perpetual,
71
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
72
+ (except as stated in this section) patent license to make, have made,
73
+ use, offer to sell, sell, import, and otherwise transfer the Work,
74
+ where such license applies only to those patent claims licensable
75
+ by such Contributor that are necessarily infringed by their
76
+ Contribution(s) alone or by the combination of their Contribution(s)
77
+ with the Work to which such Contribution(s) was submitted. If You
78
+ institute patent litigation against any entity (including a cross-claim
79
+ or counterclaim in a lawsuit) alleging that the Work or any Work
80
+ incorporated within the Work constitutes direct or contributory patent
81
+ infringement, then any patent licenses granted to You under this License
82
+ for that Work shall terminate as of the date such litigation is filed.
83
+
84
+ 4. Redistribution. You may reproduce and distribute copies of the Work
85
+ or Derivative Works thereof in any medium, with or without
86
+ modifications, and in Source or Object form, provided that You meet
87
+ the following conditions:
88
+
89
+ (a) You must give any other recipients of the Work or Derivative Works
90
+ a copy of this License; and
91
+
92
+ (b) You must cause any modified files to carry prominent notices stating
93
+ that You changed the files; and
94
+
95
+ (c) You must retain, in the Source form of any Derivative Works that
96
+ You distribute, all copyright, patent, trademark, and attribution
97
+ notices from the Source form of the Work, excluding those notices
98
+ that do not pertain to any part of the Derivative Works; and
99
+
100
+ (d) If the Work includes a "NOTICE" text file as part of its
101
+ distribution, You must include a readable copy of the attribution
102
+ notices contained within such NOTICE file, in at least one of the
103
+ following places: within a NOTICE text file distributed as part of
104
+ the Derivative Works; within the Source form or documentation, if
105
+ provided along with the Derivative Works; or, within a display
106
+ generated by the Derivative Works, if and wherever such third-party
107
+ notices normally appear. The contents of the NOTICE file are for
108
+ informational purposes only and do not modify the License. You may
109
+ add Your own attribution notices within Derivative Works that You
110
+ distribute, alongside or in addition to the NOTICE text from the
111
+ Work, provided that such additional attribution notices cannot be
112
+ construed as modifying the License.
113
+
114
+ You may add Your own license statement for Your modifications and may
115
+ provide additional grant of rights to use, copy, modify, merge,
116
+ publish, distribute, sublicense, and/or sell copies of the Work,
117
+ and to permit persons to whom the Work is furnished to do so.
118
+
119
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
120
+ any Contribution intentionally submitted for inclusion in the Work by
121
+ You to the Licensor shall be under the terms and conditions of this
122
+ License, without any additional terms or conditions. Notwithstanding
123
+ the above, nothing herein shall supersede or modify the terms of any
124
+ separate license agreement you may have executed with Licensor regarding
125
+ such Contributions.
126
+
127
+ 6. Trademarks. This License does not grant permission to use the trade
128
+ names, trademarks, service marks, or product names of the Licensor,
129
+ except as required for reasonable and customary use in describing the
130
+ origin of the Work and reproducing the content of the NOTICE file.
131
+
132
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed
133
+ to in writing, Licensor provides the Work (and each Contributor
134
+ provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES
135
+ OR CONDITIONS OF ANY KIND, either express or implied, including,
136
+ without limitation, any warranties or conditions of TITLE,
137
+ NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
138
+ You are solely responsible for determining the appropriateness of using
139
+ or reproducing the Work and assume any risks associated with Your
140
+ exercise of permissions under this License.
141
+
142
+ 8. Limitation of Liability. In no event and under no legal theory, whether
143
+ in tort (including negligence), contract, or otherwise, unless required
144
+ by applicable law (such as deliberate and grossly negligent acts) or
145
+ agreed to in writing, shall any Contributor be liable to You for
146
+ damages, including any direct, indirect, special, incidental, or
147
+ consequential damages of any character arising as a result of this
148
+ License or out of the use or inability to use the Work (including but
149
+ not limited to damages for loss of goodwill, work stoppage, computer
150
+ failure or malfunction, or all other commercial damages or losses), even
151
+ if such Contributor has been advised of the possibility of such damages.
152
+
153
+ 9. Accepting Warranty or Liability. While redistributing the Work or
154
+ Derivative Works thereof, You may choose to offer, and charge a fee for,
155
+ acceptance of support, warranty, indemnity, or other liability
156
+ obligations and/or rights consistent with this License. However, in
157
+ accepting such obligations, You may offer such obligations only on Your
158
+ own behalf and on Your behalf of each Contributor, and on Your sole
159
+ responsibility, not on behalf of any other Contributor, and only if You
160
+ agree to indemnify, defend, and hold each Contributor harmless for any
161
+ liability incurred by, or claims asserted against, such Contributor by
162
+ reason of your accepting any such warranty or liability.
163
+
164
+ END OF TERMS AND CONDITIONS
165
+
166
+ Copyright 2026 Alex Jung
167
+
168
+ Licensed under the Apache License, Version 2.0 (the "License");
169
+ you may not use this file except in compliance with the License.
170
+ You may obtain a copy of the License at
171
+
172
+ http://www.apache.org/licenses/LICENSE-2.0
173
+
174
+ Unless required by applicable law or agreed to in writing, software
175
+ distributed under the License is distributed on an "AS IS" BASIS,
176
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
177
+ See the License for the specific language governing permissions and
178
+ limitations under the License.