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
+197
View File
@@ -0,0 +1,197 @@
"""Best-effort file locking for the real-time download gate.
Windows has no user-mode API that lets an ordinary process intercept every
``CreateFile`` (that is a kernel minifilter). What this module *can* do is
hold the download file open with ``dwShareMode = 0`` while ClamAV scans a
copy, which makes Windows refuse other opens for that period. If the file
was already open elsewhere, we fall back to scanning a copy and say so in
the event detail. This is a meaningful speed bump, not a guarantee against
a kernel-level bypass.
"""
from __future__ import annotations
import ctypes
import logging
import os
import shutil
from ctypes import wintypes
from pathlib import Path
logger = logging.getLogger(__name__)
class LockError(Exception):
pass
if os.name == "nt":
_kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
_CreateFileW = _kernel32.CreateFileW
_CreateFileW.argtypes = [wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD,
wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD,
wintypes.HANDLE]
_CreateFileW.restype = wintypes.HANDLE
_ReadFile = _kernel32.ReadFile
_ReadFile.argtypes = [wintypes.HANDLE, wintypes.LPVOID, wintypes.DWORD,
ctypes.POINTER(wintypes.DWORD), wintypes.LPVOID]
_ReadFile.restype = wintypes.BOOL
_CloseHandle = _kernel32.CloseHandle
_CloseHandle.argtypes = [wintypes.HANDLE]
_CloseHandle.restype = wintypes.BOOL
_SetFileInformationByHandle = _kernel32.SetFileInformationByHandle
_SetFileInformationByHandle.argtypes = [wintypes.HANDLE, ctypes.c_int,
wintypes.LPVOID, wintypes.DWORD]
_SetFileInformationByHandle.restype = wintypes.BOOL
_GENERIC_READ = 0x80000000
_DELETE = 0x00010000
_OPEN_EXISTING = 3
_FILE_ATTRIBUTE_NORMAL = 0x80
_FILE_DISPOSITION_INFO_CLASS = 4
_INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value
class _FILE_DISPOSITION_INFO(ctypes.Structure):
_fields_ = [("DeleteFile", wintypes.BOOL)]
else:
_kernel32 = None
_FILE_DISPOSITION_INFO = None
class ExclusiveFile:
"""Open a file (share-deny on Windows) and copy it while held."""
def __init__(self, path: Path) -> None:
self.path = Path(path)
self._handle = None
self._file = None
self._delete_access = False
self._delete_on_close = False
self._open = False
@property
def exclusive(self) -> bool:
return bool(self._open and os.name == "nt")
def open(self) -> None:
if self._open:
return
if os.name == "nt":
handle = _CreateFileW(
str(self.path), _GENERIC_READ | _DELETE, 0, None, _OPEN_EXISTING,
_FILE_ATTRIBUTE_NORMAL, None,
)
if handle == _INVALID_HANDLE_VALUE:
handle = _CreateFileW(
str(self.path), _GENERIC_READ, 0, None, _OPEN_EXISTING,
_FILE_ATTRIBUTE_NORMAL, None,
)
self._delete_access = False
else:
self._delete_access = True
if handle == _INVALID_HANDLE_VALUE:
error = ctypes.get_last_error()
raise LockError(f"Could not open {self.path.name} exclusively (error {error}).")
self._handle = handle
else:
try:
self._file = open(self.path, "rb")
except OSError as exc:
raise LockError(f"Could not open {self.path.name}: {exc}") from exc
self._open = True
def copy_to(self, destination: Path) -> None:
if not self._open:
self.open()
destination = Path(destination)
destination.parent.mkdir(parents=True, exist_ok=True)
try:
if os.name == "nt":
buffer = ctypes.create_string_buffer(1024 * 1024)
read = wintypes.DWORD()
with open(destination, "wb") as output:
while True:
ok = _ReadFile(self._handle, buffer, len(buffer),
ctypes.byref(read), None)
if not ok:
raise LockError(
f"Could not read {self.path.name} (error "
f"{ctypes.get_last_error()}).")
if read.value == 0:
break
output.write(buffer.raw[:read.value])
else:
with open(destination, "wb") as output:
shutil.copyfileobj(self._file, output, length=1024 * 1024)
except OSError as exc:
raise LockError(f"Could not copy {self.path.name}: {exc}") from exc
def mark_delete_on_close(self) -> bool:
if os.name != "nt" or not self._delete_access or self._handle is None:
return False
info = _FILE_DISPOSITION_INFO(True)
ok = _SetFileInformationByHandle(
self._handle, _FILE_DISPOSITION_INFO_CLASS, ctypes.byref(info),
ctypes.sizeof(info),
)
if not ok:
logger.warning("Could not mark %s delete-on-close (error %s)",
self.path, ctypes.get_last_error())
return False
self._delete_on_close = True
return True
def close(self) -> None:
if not self._open:
return
if os.name == "nt":
if self._handle is not None:
_CloseHandle(self._handle)
self._handle = None
else:
if self._file is not None:
self._file.close()
self._file = None
self._open = False
def __enter__(self) -> "ExclusiveFile":
self.open()
return self
def __exit__(self, exc_type, exc, traceback) -> None:
self.close()
def copy_for_scan(path: Path, destination: Path, attempts: int = 8,
delay: float = 0.4) -> tuple[ExclusiveFile, bool]:
"""Copy *path* while holding it where possible.
Returns ``(locker, exclusive)``. The caller must keep ``locker`` open
until the scan finishes, then close it. If the file cannot be opened
exclusively (another process is still writing or reading it), this
retries briefly and then copies without the lock.
"""
import time
last_error: Exception | None = None
for _ in range(max(1, attempts)):
locker = ExclusiveFile(path)
try:
locker.open()
locker.copy_to(destination)
return locker, locker.exclusive
except LockError as exc:
last_error = exc
try:
locker.close()
except Exception:
pass
time.sleep(delay)
# Last resort: a plain copy. The watcher already waited for the size to
# stop changing, so this is still normally a complete file.
try:
shutil.copy2(path, destination)
except OSError as exc:
raise LockError(f"Could not copy {path.name}: {exc}") from exc
logger.warning("Real-time scan could not exclusively lock %s: %s", path, last_error)
return ExclusiveFile(path), False