Skip to content

Commit ff1dc87

Browse files
author
wlanboy
committed
Upgrade debs, add ruff, pyright and pytest
1 parent 056e5eb commit ff1dc87

6 files changed

Lines changed: 304 additions & 53 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
jobs:
11+
check:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v6
16+
17+
- name: Setup uv
18+
uses: astral-sh/setup-uv@v7
19+
with:
20+
version: "latest"
21+
enable-cache: true # Enormer Speed-Boost für Folgeruns
22+
23+
- name: Install dependencies
24+
# --frozen stellt sicher, dass exakt deine uv.lock verwendet wird
25+
run: uv sync --frozen
26+
27+
- name: Lint & Syntax (Ruff)
28+
# Ersetzt das manuelle py_compile für jede Datei.
29+
# Prüft das gesamte Projekt (inkl. scanner, extractor, etc.) auf einmal.
30+
run: uv run ruff check .
31+
32+
- name: Type Check (Pyright)
33+
# profitiert extrem von Type-Checks für Tools und Resources
34+
run: uv run pyright .
35+
36+
- name: Run Tests
37+
# Führt deine Tests aus (erwartet pytest im Projekt)
38+
run: uv run pytest
39+
40+
- name: Verify app imports
41+
# Letzter Check: Lässt sich die App ohne Environment-Fehler importieren?
42+
run: |
43+
uv run python -c "from main import app; print('🚀 app OK')"

main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from cryptography.x509.oid import NameOID
1212
from cryptography.hazmat.primitives import hashes, serialization
1313
from cryptography.hazmat.primitives.asymmetric import rsa, ec
14+
from cryptography.hazmat.primitives.asymmetric.types import CertificateIssuerPrivateKeyTypes
1415

1516
app = FastAPI()
1617
templates = Jinja2Templates(directory="templates")
@@ -97,6 +98,7 @@ async def create_cert(
9798
ca_cert = x509.load_pem_x509_certificate(f.read())
9899
with open(CA_KEY, "rb") as f:
99100
ca_key = serialization.load_pem_private_key(f.read(), password=None)
101+
assert isinstance(ca_key, CertificateIssuerPrivateKeyTypes)
100102

101103
# CSR bauen
102104
subject = x509.Name(

pyproject.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,10 @@ dependencies = [
1111
"python-multipart>=0.0.20",
1212
"uvicorn>=0.38.0",
1313
]
14+
15+
[dependency-groups]
16+
dev = [
17+
"pyright>=1.1.408",
18+
"pytest>=9.0.2",
19+
"ruff>=0.15.2",
20+
]

tests/__init__.py

Whitespace-only changes.

tests/test_main.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import pytest
2+
from fastapi import HTTPException
3+
from cryptography import x509
4+
from cryptography.hazmat.primitives.asymmetric import rsa, ec
5+
6+
from main import validate_safe_name, parse_san_entry, generate_key
7+
8+
9+
class TestValidateSafeName:
10+
def test_valid_hostname(self):
11+
assert validate_safe_name("example.com") is True
12+
13+
def test_valid_with_dash_and_underscore(self):
14+
assert validate_safe_name("my-host_01") is True
15+
16+
def test_empty_string(self):
17+
assert validate_safe_name("") is False
18+
19+
def test_path_traversal_dotdot(self):
20+
assert validate_safe_name("..") is False
21+
22+
def test_path_traversal_embedded(self):
23+
assert validate_safe_name("foo/../bar") is False
24+
25+
def test_slash(self):
26+
assert validate_safe_name("foo/bar") is False
27+
28+
def test_space(self):
29+
assert validate_safe_name("foo bar") is False
30+
31+
def test_semicolon(self):
32+
assert validate_safe_name("foo;bar") is False
33+
34+
def test_wildcard(self):
35+
assert validate_safe_name("foo*bar") is False
36+
37+
38+
class TestParseSanEntry:
39+
def test_ipv4(self):
40+
result = parse_san_entry("192.168.1.1")
41+
assert isinstance(result, x509.IPAddress)
42+
43+
def test_ipv6(self):
44+
result = parse_san_entry("::1")
45+
assert isinstance(result, x509.IPAddress)
46+
47+
def test_dns_name(self):
48+
result = parse_san_entry("example.com")
49+
assert isinstance(result, x509.DNSName)
50+
51+
def test_wildcard_dns(self):
52+
result = parse_san_entry("*.example.com")
53+
assert isinstance(result, x509.DNSName)
54+
55+
56+
class TestGenerateKey:
57+
def test_rsa2048(self):
58+
key = generate_key("rsa2048")
59+
assert isinstance(key, rsa.RSAPrivateKey)
60+
assert key.key_size == 2048
61+
62+
def test_rsa4096(self):
63+
key = generate_key("rsa4096")
64+
assert isinstance(key, rsa.RSAPrivateKey)
65+
assert key.key_size == 4096
66+
67+
def test_secp256r1(self):
68+
key = generate_key("secp256r1")
69+
assert isinstance(key, ec.EllipticCurvePrivateKey)
70+
assert key.curve.name == "secp256r1"
71+
72+
def test_secp384r1(self):
73+
key = generate_key("secp384r1")
74+
assert isinstance(key, ec.EllipticCurvePrivateKey)
75+
assert key.curve.name == "secp384r1"
76+
77+
def test_secp521r1(self):
78+
key = generate_key("secp521r1")
79+
assert isinstance(key, ec.EllipticCurvePrivateKey)
80+
assert key.curve.name == "secp521r1"
81+
82+
def test_invalid_key_type_raises(self):
83+
with pytest.raises(HTTPException) as exc_info:
84+
generate_key("invalid")
85+
assert exc_info.value.status_code == 400

0 commit comments

Comments
 (0)