BoltClam 1.0.0: ClamAV desktop protection with Bolt sign-in

Adds the PyQt6 Windows client (Homepage, Scan & results, About & updates, Settings), ClamAV scanning and quarantine, browser-download watch with an exclusive scan gate, Bolt loopback/PKCE sign-in with DPAPI key storage, Gitea release feed with checksum verification, opt-in diagnostics, and Windows/portable build tooling.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
TheMajesticMagician
2026-10-25 16:21:14 -06:00
co-authored by Claude Code
commit addd00364d
76 changed files with 12503 additions and 0 deletions
+198
View File
@@ -0,0 +1,198 @@
from __future__ import annotations
import base64
import hashlib
import http.server
import json
import socket
import threading
import time
import urllib.error
import urllib.request
from pathlib import Path
from urllib.parse import parse_qs, urlparse
import pytest
from boltclam.auth import (
AuthError,
AuthService,
CredentialStore,
LoopbackAuthReceiver,
pkce_pair,
)
from boltclam.config import AccountCache
ACCOUNT = {
"user": {"id": "1", "email": "aaron@example.com", "name": "Aaron",
"username": "aaron"},
"subscription": {"tier": "premium", "label": "Premium", "status": "active"},
"balance": {"weekly_remaining": 100, "topup": 0, "allowed": True},
"features": {"licensed": True},
}
class _Handler(http.server.BaseHTTPRequestHandler):
def _json(self, status: int, payload: dict) -> None:
data = json.dumps(payload).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def do_GET(self) -> None: # noqa: N802
if self.path.startswith("/boltclam/account"):
key = self.headers.get("X-Desk-Api-Key", "")
if key in ("bolt_live_good", "bolt_live_new"):
self._json(200, ACCOUNT)
else:
self._json(401, {"error": "invalid_credentials"})
return
self._json(404, {"error": "not_found"})
def do_POST(self) -> None: # noqa: N802
length = int(self.headers.get("Content-Length") or 0)
raw = self.rfile.read(length) if length else b"{}"
if self.path == "/desk/auth/oauth/exchange":
payload = json.loads(raw or b"{}")
self.server.exchanges.append(payload) # type: ignore[attr-defined]
self._json(200, {"api_key": "bolt_live_new", "user": ACCOUNT["user"]})
return
if self.path == "/desk/auth/logout":
self._json(200, {})
return
self._json(404, {"error": "not_found"})
def log_message(self, fmt: str, *args) -> None:
return
@pytest.fixture
def web_server():
server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), _Handler)
server.exchanges = [] # type: ignore[attr-defined]
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
yield server
finally:
server.shutdown()
server.server_close()
def _auth(tmp_path, base_url) -> AuthService:
return AuthService(
base_url, install_id="test-install",
credentials=CredentialStore(tmp_path / "credentials.dat"),
account_cache=AccountCache(tmp_path / "account.json"))
def test_pkce_pair_matches_s256():
verifier, challenge = pkce_pair()
expected = base64.urlsafe_b64encode(
hashlib.sha256(verifier.encode()).digest()).rstrip(b"=").decode()
assert challenge == expected
assert 43 <= len(verifier) <= 128
def test_credential_store_fallback_round_trip(tmp_path):
store = CredentialStore(tmp_path / "credentials.dat")
store.save("bolt_live_secret")
assert store.load() == "bolt_live_secret"
store.delete()
assert store.load() is None
def test_key_login_restore_and_logout(tmp_path, web_server):
base = f"http://127.0.0.1:{web_server.server_address[1]}"
auth = _auth(tmp_path, base)
session = auth.login_with_key("bolt_live_good")
assert session.api_key == "bolt_live_good"
assert session.account["user"]["email"] == "aaron@example.com"
assert auth.stored_key() == "bolt_live_good"
restored = auth.restore()
assert restored.account["subscription"]["tier"] == "premium"
assert restored.offline is False
auth.logout()
assert auth.stored_key() is None
assert auth.account_cache.load() is None
def test_key_login_rejects_a_bad_key(tmp_path, web_server):
base = f"http://127.0.0.1:{web_server.server_address[1]}"
auth = _auth(tmp_path, base)
with pytest.raises(AuthError):
auth.login_with_key("bolt_live_wrong")
def test_browser_login_completes_the_loopback_pkce_handoff(tmp_path, web_server):
base = f"http://127.0.0.1:{web_server.server_address[1]}"
auth = _auth(tmp_path, base)
opened: dict[str, str] = {}
def open_url(url: str) -> bool:
opened["url"] = url
parsed = urlparse(url)
query = parse_qs(parsed.query)
redirect_uri = query["redirect_uri"][0]
state = query["state"][0]
def callback() -> None:
time.sleep(0.2)
with urllib.request.urlopen(
f"{redirect_uri}?code=one-time&state={state}", timeout=5) as response:
response.read()
threading.Thread(target=callback, daemon=True).start()
return True
session = auth.browser_login(open_url, timeout=10)
assert session.api_key == "bolt_live_new"
assert auth.stored_key() == "bolt_live_new"
exchange = web_server.exchanges[-1] # type: ignore[attr-defined]
assert exchange["client"] == "boltclam"
assert exchange["code"] == "one-time"
assert exchange["code_verifier"]
assert exchange["redirect_uri"].startswith("http://127.0.0.1:")
assert "device_name=BoltClam" in opened["url"]
def test_loopback_rejects_the_wrong_state():
receiver = LoopbackAuthReceiver("expected-state")
receiver.start()
try:
try:
urllib.request.urlopen(
f"{receiver.redirect_uri}?code=bad&state=wrong", timeout=5).read()
assert False, "expected a 400"
except urllib.error.HTTPError as exc:
assert exc.code == 400
assert receiver.event.is_set() is False
with urllib.request.urlopen(
f"{receiver.redirect_uri}?code=good&state=expected-state",
timeout=5) as response:
assert response.status == 200
result = receiver.wait(2)
assert result["code"] == "good"
finally:
receiver.close()
def test_restore_offline_uses_the_cached_account(tmp_path):
with socket.socket() as sock:
sock.bind(("127.0.0.1", 0))
port = sock.getsockname()[1]
credential_path = tmp_path / "credentials.dat"
CredentialStore(credential_path).save("bolt_live_cached")
cache = AccountCache(tmp_path / "account.json")
cache.save(ACCOUNT)
auth = AuthService(
f"http://127.0.0.1:{port}", install_id="offline",
credentials=CredentialStore(credential_path), account_cache=cache)
session = auth.restore()
assert session.offline is True
assert session.account["user"]["email"] == "aaron@example.com"