usbguard-gui 0.3.1__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.
- usbguard_gui-0.3.1/.claude/settings.local.json +22 -0
- usbguard_gui-0.3.1/.copr/Makefile +8 -0
- usbguard_gui-0.3.1/.editorconfig +22 -0
- usbguard_gui-0.3.1/.github/workflows/makefile.yml +23 -0
- usbguard_gui-0.3.1/.gitignore +31 -0
- usbguard_gui-0.3.1/AGENTS.md +176 -0
- usbguard_gui-0.3.1/CHANGELOG.md +196 -0
- usbguard_gui-0.3.1/CLAUDE.md +3 -0
- usbguard_gui-0.3.1/DESIGN.md +203 -0
- usbguard_gui-0.3.1/LICENSE +15 -0
- usbguard_gui-0.3.1/Makefile +155 -0
- usbguard_gui-0.3.1/PKG-INFO +200 -0
- usbguard_gui-0.3.1/PyGObject.md +8 -0
- usbguard_gui-0.3.1/README.md +171 -0
- usbguard_gui-0.3.1/TODO.md +8 -0
- usbguard_gui-0.3.1/pyproject.toml +75 -0
- usbguard_gui-0.3.1/release.py +139 -0
- usbguard_gui-0.3.1/rpm/70-usbguard_gui.rules +27 -0
- usbguard_gui-0.3.1/rpm/usbguard_gui-autostart.desktop +10 -0
- usbguard_gui-0.3.1/rpm/usbguard_gui.desktop +12 -0
- usbguard_gui-0.3.1/rpm/usbguard_gui.spec.in +115 -0
- usbguard_gui-0.3.1/rpm/usbguard_gui.svg +19 -0
- usbguard_gui-0.3.1/screenshot_20260411_091821.png +0 -0
- usbguard_gui-0.3.1/screenshot_20260411_091821.xcf +0 -0
- usbguard_gui-0.3.1/src/usbguard_gui/__init__.py +9 -0
- usbguard_gui-0.3.1/src/usbguard_gui/__main__.py +6 -0
- usbguard_gui-0.3.1/src/usbguard_gui/app.py +397 -0
- usbguard_gui-0.3.1/src/usbguard_gui/dbus_client.py +299 -0
- usbguard_gui-0.3.1/src/usbguard_gui/device.py +196 -0
- usbguard_gui-0.3.1/src/usbguard_gui/device_dialog.py +130 -0
- usbguard_gui-0.3.1/src/usbguard_gui/device_list.py +275 -0
- usbguard_gui-0.3.1/src/usbguard_gui/introspection/org.freedesktop.ScreenSaver.xml +9 -0
- usbguard_gui-0.3.1/src/usbguard_gui/introspection/org.usbguard.Devices1.xml +30 -0
- usbguard_gui-0.3.1/src/usbguard_gui/introspection/org.usbguard.Policy1.xml +12 -0
- usbguard_gui-0.3.1/src/usbguard_gui/screensaver.py +248 -0
- usbguard_gui-0.3.1/src/usbguard_gui/settings.py +23 -0
- usbguard_gui-0.3.1/tests/test_app.py +332 -0
- usbguard_gui-0.3.1/tests/test_async_api.py +221 -0
- usbguard_gui-0.3.1/tests/test_dbus_client.py +457 -0
- usbguard_gui-0.3.1/tests/test_device.py +160 -0
- usbguard_gui-0.3.1/tests/test_device_list.py +171 -0
- usbguard_gui-0.3.1/tests/test_release.py +321 -0
- usbguard_gui-0.3.1/tox.ini +13 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(git -C /home/dgunchev/github/gunchev/usbguard_gui/ log --oneline -3)",
|
|
5
|
+
"Read(//home/dgunchev/github/gunchev/**)",
|
|
6
|
+
"Bash(uv sync:*)",
|
|
7
|
+
"Bash(uv run:*)",
|
|
8
|
+
"Bash(git -C /home/dgunchev/github/gunchev/usbguard_gui/ status)",
|
|
9
|
+
"Bash(git:*)",
|
|
10
|
+
"Bash(make:*)",
|
|
11
|
+
"Bash(uv pip:*)",
|
|
12
|
+
"Bash(uv add:*)",
|
|
13
|
+
"Bash(ls *.py release*)",
|
|
14
|
+
"Bash(grep -E \"\\\\.\\(toml|cfg|ini|txt\\)$\")",
|
|
15
|
+
"Bash(xargs ls:*)",
|
|
16
|
+
"Bash(rpm -ql python3-gobject-base)",
|
|
17
|
+
"Bash(python3:*)",
|
|
18
|
+
"Bash(wc:*)",
|
|
19
|
+
"Bash(python:*)"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export TOP:=$(shell dirname "$(abspath $(lastword $(MAKEFILE_LIST)))")
|
|
2
|
+
outdir ?= dist
|
|
3
|
+
|
|
4
|
+
.PHONY: srpm
|
|
5
|
+
srpm:
|
|
6
|
+
dnf -y install uv python3 python3-build python3-hatchling
|
|
7
|
+
$(MAKE) -C $(TOP)/.. srpm
|
|
8
|
+
if test -n "$(outdir)"; then cp -a $(TOP)/../dist/*.src.rpm "$(outdir)"; fi
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# EditorConfig is awesome: https://EditorConfig.org
|
|
2
|
+
|
|
3
|
+
# top-most EditorConfig file
|
|
4
|
+
root = true
|
|
5
|
+
|
|
6
|
+
[*]
|
|
7
|
+
charset = utf-8
|
|
8
|
+
end_of_line = lf
|
|
9
|
+
insert_final_newline = true
|
|
10
|
+
trim_trailing_whitespace = true
|
|
11
|
+
max_line_length = 120
|
|
12
|
+
|
|
13
|
+
[*.{cfg,ini,py,rst,toml,txt}]
|
|
14
|
+
indent_style = space
|
|
15
|
+
indent_size = 4
|
|
16
|
+
|
|
17
|
+
[*.{htm,html,js,json,md,xml,yaml,yml}]
|
|
18
|
+
indent_style = space
|
|
19
|
+
indent_size = 2
|
|
20
|
+
|
|
21
|
+
[{Makefile,*.go}]
|
|
22
|
+
indent_style = tab
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Makefile CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ "master" ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ "master" ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Run tox (py310–py314) on Fedora
|
|
18
|
+
run: |
|
|
19
|
+
sudo apt-get update && sudo apt-get install -y podman
|
|
20
|
+
podman run --rm -v "$PWD:/work" -w "/work" quay.io/fedora/fedora:latest bash -c '
|
|
21
|
+
dnf -y install uv python3 python3.10 python3.11 python3.12 python3.13 make gcc fedora-packager python3-devel python3-cairo cairo-gobject-devel cairo-devel libglvnd-glx libglvnd-egl
|
|
22
|
+
UV_PYTHON_DOWNLOADS=never QT_QPA_PLATFORM=offscreen uv run tox
|
|
23
|
+
'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/dist/
|
|
2
|
+
/build/
|
|
3
|
+
/htmlcov/
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*.egg-info/
|
|
7
|
+
*.egg
|
|
8
|
+
.eggs/
|
|
9
|
+
.tox/
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
.coverage
|
|
12
|
+
*.py,cover
|
|
13
|
+
site.py
|
|
14
|
+
__pypackages__/
|
|
15
|
+
|
|
16
|
+
# uv
|
|
17
|
+
/.venv/
|
|
18
|
+
/uv.lock
|
|
19
|
+
|
|
20
|
+
# Local wheels cache
|
|
21
|
+
/whl_local/
|
|
22
|
+
|
|
23
|
+
# Packaging
|
|
24
|
+
/*.spec
|
|
25
|
+
/VERSION
|
|
26
|
+
|
|
27
|
+
# Editor
|
|
28
|
+
/.idea/
|
|
29
|
+
/.vscode/
|
|
30
|
+
*.swp
|
|
31
|
+
*~
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Instructions for agentic coding agents working in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
KDE/Qt system tray GUI for USBGuard — responds to USB device insertions with Allow, Block, or Reject actions.
|
|
8
|
+
|
|
9
|
+
- **Language**: Python >= 3.10
|
|
10
|
+
- **UI Framework**: PyQt6
|
|
11
|
+
- **IPC**: D-Bus (via dbus-fast)
|
|
12
|
+
- **Layout**: src-layout (sources in `src/usbguard_gui/`)
|
|
13
|
+
|
|
14
|
+
## Build & Run Commands
|
|
15
|
+
|
|
16
|
+
### Using uv (recommended)
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv run usbguard_gui # run the app
|
|
20
|
+
uv run python -m usbguard_gui # alternative entry point
|
|
21
|
+
uv run pytest # run all tests
|
|
22
|
+
uv run pytest tests/test_file.py # run single test file
|
|
23
|
+
uv run pytest -k test_name # run single test by name
|
|
24
|
+
uv run pytest -v --cov . --cov-report=term-missing # coverage
|
|
25
|
+
uv run ruff check src/ tests/ # lint
|
|
26
|
+
uv run autopep8 --in-place --recursive src/ tests/ # format
|
|
27
|
+
uv run tox # test across Python versions
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Using Make
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
make test # run all tests (uv run pytest -v)
|
|
34
|
+
make check # lint + test
|
|
35
|
+
make lint # ruff check + autopep8 format check
|
|
36
|
+
make format # auto-format with autopep8
|
|
37
|
+
make coverage # test with coverage report
|
|
38
|
+
make build # build wheel package
|
|
39
|
+
make clean # clean build artifacts
|
|
40
|
+
make run # sync dev deps and run app
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Code Style Guidelines
|
|
44
|
+
|
|
45
|
+
### Formatting
|
|
46
|
+
|
|
47
|
+
- **Line length**: 120 characters maximum
|
|
48
|
+
- **Indentation**: 4 spaces for Python files
|
|
49
|
+
- **Line endings**: LF
|
|
50
|
+
- **Formatter**: autopep8
|
|
51
|
+
- **Charset**: UTF-8
|
|
52
|
+
- **Trailing whitespace**: trimmed
|
|
53
|
+
- **Final newline**: required
|
|
54
|
+
- See `.editorconfig` for additional editor-specific settings
|
|
55
|
+
|
|
56
|
+
### Linter/Formatter Configuration (pyproject.toml)
|
|
57
|
+
|
|
58
|
+
```toml
|
|
59
|
+
[tool.ruff]
|
|
60
|
+
line-length = 120
|
|
61
|
+
target-version = "py310"
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint]
|
|
64
|
+
select = ["E", "F", "W", "UP", "B", "SIM", "RUF"]
|
|
65
|
+
|
|
66
|
+
[tool.autopep8]
|
|
67
|
+
max_line_length = 120
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Imports
|
|
71
|
+
|
|
72
|
+
- Always use `from __future__ import annotations` for postponed annotations
|
|
73
|
+
- Group imports in order: stdlib, third-party, local
|
|
74
|
+
- Use absolute imports: `from usbguard_gui.device import ...`
|
|
75
|
+
- Sort imports with ruff (I001)
|
|
76
|
+
|
|
77
|
+
### Type Hints
|
|
78
|
+
|
|
79
|
+
- Required on all public APIs (functions, classes, methods)
|
|
80
|
+
- Use Python 3.10+ syntax (`X | None` over `Optional[X]`)
|
|
81
|
+
- Return type annotations: always present on public methods
|
|
82
|
+
|
|
83
|
+
### Naming Conventions
|
|
84
|
+
|
|
85
|
+
- **Classes**: `PascalCase` (e.g., `USBGuardClient`, `DeviceActionDialog`)
|
|
86
|
+
- **Functions/methods**: `snake_case` (e.g., `apply_device_policy`, `_handle_disconnect`)
|
|
87
|
+
- **Constants**: `SCREAMING_SNAKE_CASE` (e.g., `USBGUARD_BUS_NAME`)
|
|
88
|
+
- **Private members**: leading underscore (e.g., `self._client`, `self._connected`)
|
|
89
|
+
|
|
90
|
+
### Docstrings
|
|
91
|
+
|
|
92
|
+
- Module-level: `"""Module description."""`
|
|
93
|
+
- Classes: `"""Short description.\n\nExtended explanation if needed.\n"""`
|
|
94
|
+
- Methods: `"""One-line description."""` or multi-line for complex methods
|
|
95
|
+
|
|
96
|
+
### Error Handling
|
|
97
|
+
|
|
98
|
+
- Use logging (`log = logging.getLogger(__name__)`) for errors and warnings
|
|
99
|
+
- D-Bus errors: catch `DBusError` from `dbus_fast.error`
|
|
100
|
+
- Permission errors: check error names via `_is_permission_error()` pattern
|
|
101
|
+
- Never expose secrets or credentials in logs
|
|
102
|
+
|
|
103
|
+
### Dataclasses & Enums
|
|
104
|
+
|
|
105
|
+
- Use `@dataclass` for data models with `field(default_factory=...)` for mutable defaults
|
|
106
|
+
- Use `IntEnum` for integer-backed enums (e.g., `DeviceTarget`, `PresenceEvent`)
|
|
107
|
+
- Prefer `.name` over string comparison when available
|
|
108
|
+
|
|
109
|
+
### Qt/PyQt6 Patterns
|
|
110
|
+
|
|
111
|
+
- Subclass `QObject` for classes that emit signals
|
|
112
|
+
- Use `pyqtSignal` for typed signals
|
|
113
|
+
- Parent parameter in `__init__(self, parent: QObject | None = None)`
|
|
114
|
+
- Connect signals in `_connect_signals()` method
|
|
115
|
+
- Use `QTimer.singleShot()` for deferred actions
|
|
116
|
+
|
|
117
|
+
## Project Structure
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
src/usbguard_gui/
|
|
121
|
+
__init__.py # Package init (can export VERSION)
|
|
122
|
+
__main__.py # python -m entry point
|
|
123
|
+
app.py # Main tray application
|
|
124
|
+
dbus_client.py # D-Bus client for USBGuard daemon
|
|
125
|
+
device.py # Device model and rule parsing
|
|
126
|
+
device_dialog.py # Device action dialog window
|
|
127
|
+
device_list.py # Device list window
|
|
128
|
+
screensaver.py # Screensaver state monitoring
|
|
129
|
+
settings.py # Application settings
|
|
130
|
+
|
|
131
|
+
tests/
|
|
132
|
+
test_app.py # Tests for main tray application
|
|
133
|
+
test_device.py # Tests for device model
|
|
134
|
+
test_device_list.py # Tests for device list window
|
|
135
|
+
test_dbus_client.py # Tests for D-Bus client
|
|
136
|
+
test_async_api.py # Tests for async signal-based API
|
|
137
|
+
test_release.py # Tests for release scripts
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Testing Conventions
|
|
141
|
+
|
|
142
|
+
- Use `pytest` with `pytest-mock` and `pytest-cov`
|
|
143
|
+
- Test files: `tests/test_<module>.py`
|
|
144
|
+
- Test classes: `TestClassName` with descriptive methods
|
|
145
|
+
- Test methods: `test_<what_is_tested>`
|
|
146
|
+
- Use `pytest.mark.parametrize` for multiple test cases
|
|
147
|
+
- Private helper methods in tests prefixed with `_`
|
|
148
|
+
- Use sentinel pattern (`object()`) for special default values
|
|
149
|
+
|
|
150
|
+
### Example Test Structure
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
"""Tests for the device model and rule parser."""
|
|
154
|
+
|
|
155
|
+
from usbguard_gui.device import Device, DeviceTarget, parse_device_rule
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class TestParseDeviceRule:
|
|
159
|
+
"""Test rule string parsing."""
|
|
160
|
+
|
|
161
|
+
RULE_ALLOW = 'allow id 1d6b:0002 serial "..." name "..." ...'
|
|
162
|
+
|
|
163
|
+
def test_allow_rule(self):
|
|
164
|
+
result = parse_device_rule(self.RULE_ALLOW)
|
|
165
|
+
assert result["rule"] == "allow"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Pre-commit Checklist
|
|
169
|
+
|
|
170
|
+
Before submitting changes:
|
|
171
|
+
|
|
172
|
+
1. Run `make check` (lint + tests)
|
|
173
|
+
2. Ensure all new public APIs have type hints
|
|
174
|
+
3. Add tests for new functionality
|
|
175
|
+
4. Update docstrings for user-facing APIs
|
|
176
|
+
5. No commented-out code in final submissions
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
## 0.3.1 — 2026-04-11
|
|
2
|
+
|
|
3
|
+
### Changes since v0.3.0
|
|
4
|
+
|
|
5
|
+
- dd7565d Try integrating tags with COPR builds.
|
|
6
|
+
- 004266e COPR take 3.
|
|
7
|
+
- 393d0bf Fix spec file version sed-s.
|
|
8
|
+
- 2acf64f Makefile: fix RPM_VER fallback for empty git output
|
|
9
|
+
- d884b58 Makefile: fallback RPM_VER from __version__ if git fails
|
|
10
|
+
- 05617c2 Fedora COPR Makefile integration take 2.
|
|
11
|
+
|
|
12
|
+
## 0.3.0 — 2026-04-11
|
|
13
|
+
|
|
14
|
+
### Changes since v0.2.3
|
|
15
|
+
|
|
16
|
+
- a047831 Fix ScreenSaver inhibited detection, update dcs.
|
|
17
|
+
- 1b51b2a Fast-fail D-Bus scheduler calls when disconnected
|
|
18
|
+
- 6e1c049 Update the RPM spec file description.
|
|
19
|
+
|
|
20
|
+
## 0.2.3 — 2026-04-11
|
|
21
|
+
|
|
22
|
+
### Changes since v0.2.2
|
|
23
|
+
|
|
24
|
+
- 72264b5 Fix f-string without placeholders in test_app.py and improve AGENTS.md
|
|
25
|
+
- fbd4070 Increase HID lock delay from 3 to 4 seconds
|
|
26
|
+
- bfd0eac Add HID disable option, fix config names, UI polish, and bug fixes
|
|
27
|
+
|
|
28
|
+
## 0.2.2 — 2026-04-11
|
|
29
|
+
|
|
30
|
+
### Changes since v0.2.1
|
|
31
|
+
|
|
32
|
+
- d527c45 Add upgrade restart mechanism with SIGUSR1
|
|
33
|
+
|
|
34
|
+
## 0.2.1 — 2026-04-11
|
|
35
|
+
|
|
36
|
+
### Changes since v0.2.0
|
|
37
|
+
|
|
38
|
+
- 5a70f2d Fedora COPR Makefile integration?
|
|
39
|
+
|
|
40
|
+
## 0.2.0 — 2026-04-11
|
|
41
|
+
|
|
42
|
+
### Changes since v0.1.0
|
|
43
|
+
|
|
44
|
+
- 0aa9fd7 Release 0.2.0
|
|
45
|
+
- f5ed81f Add option to disable special HID device treatment
|
|
46
|
+
- cd2c7d5 Claude Opus fixes.
|
|
47
|
+
- 89d6b07 Cleanup.
|
|
48
|
+
- 5508d9b fix: Screen lock not triggered on HID device insertion
|
|
49
|
+
- f947d8a fix: Treat composite HID devices with the same security path as pure HID
|
|
50
|
+
- 3179df3 chore: Replace ruff format with autopep8 for formatting
|
|
51
|
+
|
|
52
|
+
## 0.2.0 — 2026-04-11
|
|
53
|
+
|
|
54
|
+
### Changes since v0.1.0
|
|
55
|
+
|
|
56
|
+
- f5ed81f Add option to disable special HID device treatment
|
|
57
|
+
- cd2c7d5 Claude Opus fixes.
|
|
58
|
+
- 89d6b07 Cleanup.
|
|
59
|
+
- 5508d9b fix: Screen lock not triggered on HID device insertion
|
|
60
|
+
- f947d8a fix: Treat composite HID devices with the same security path as pure HID
|
|
61
|
+
- 3179df3 chore: Replace ruff format with autopep8 for formatting
|
|
62
|
+
|
|
63
|
+
## 0.1.0 — 2026-04-10
|
|
64
|
+
|
|
65
|
+
### Changes since v0.0.13
|
|
66
|
+
|
|
67
|
+
- e00caa3 feat: Toggle device list visibility on tray icon click
|
|
68
|
+
- 91f6cd9 OpenCode is no Anthropic ;-)
|
|
69
|
+
- ebaf3ac CI: add libglvnd-egl for libEGL.so.1 required by PyQt6
|
|
70
|
+
- 0c2d19c Return some needed deps.
|
|
71
|
+
- 4fad4fa Fix tox: add pytest-qt, tox-uv; run all Pythons in CI via Fedora packages
|
|
72
|
+
- 567e4da fix: Fix ImportError: libGL.so.1, CI
|
|
73
|
+
|
|
74
|
+
## 0.0.13 — 2026-04-10
|
|
75
|
+
|
|
76
|
+
### Changes since v0.0.12
|
|
77
|
+
|
|
78
|
+
- dff22be Fix clean exit and persist device list window state
|
|
79
|
+
|
|
80
|
+
## 0.0.12 — 2026-04-10
|
|
81
|
+
|
|
82
|
+
### Changes since v0.0.11
|
|
83
|
+
|
|
84
|
+
- bc80337 Enforce single application instance using QLockFile
|
|
85
|
+
- 34bedf1 Improve devices dialog: sortable, resizable, and reorderable columns
|
|
86
|
+
- 2cfd0f5 Add documentation on how the application works and its architecture
|
|
87
|
+
- cda1066 Clean up test_device_list.py: remove unused variables and noqa comments
|
|
88
|
+
|
|
89
|
+
## 0.0.11 — 2026-04-05
|
|
90
|
+
|
|
91
|
+
### Changes since v0.0.10
|
|
92
|
+
|
|
93
|
+
- 0d5f69e Fix device list never populated due to two independent bugs
|
|
94
|
+
- 2d15e00 Fix device list refresh by using refresh ID for signal correlation
|
|
95
|
+
- 7f02094 Fix device list not updating due to missing pending_devices storage
|
|
96
|
+
- b835a3a Add tests for async signal-based API
|
|
97
|
+
- 520b74b Fix app.py and device_list.py for async API
|
|
98
|
+
- dbf08f2 Add DESIGN.md documenting the PyGObject replacement approaches
|
|
99
|
+
- 3f83c62 Replace PyGObject dependency with dbus-fast (QThread+asyncio)
|
|
100
|
+
- db4f6db Update GitHub Actions workflow to use Podman
|
|
101
|
+
|
|
102
|
+
## 0.0.10 — 2026-04-05
|
|
103
|
+
|
|
104
|
+
### Changes since v0.0.9
|
|
105
|
+
|
|
106
|
+
- 3a94720 fix: release.py
|
|
107
|
+
- 5b72884 Configure ruff to prefer compact formatting: (by Qwen3.6 Plus Free)
|
|
108
|
+
- 059df0f Remove empty file.
|
|
109
|
+
- 7af2f9c Fix medium-priority issues: (by Qwen3.6 Plus Free)
|
|
110
|
+
- 025d8a1 Fix high-priority issues: (by Qwen3.6 Plus Free)
|
|
111
|
+
- 2d8f605 Fix critical bugs: by Qwen3.6 Plus Free - release.py build_impl crash - HID stale device reference - dialog
|
|
112
|
+
WA_DeleteOnClose access
|
|
113
|
+
- ef68b4c Fix TODO.md: add trailing newline
|
|
114
|
+
- 3d586a3 Add TODO.md with circuit breaker pattern suggestion for future improvements
|
|
115
|
+
- 805d07d Improve error handling: Add logging, exponential backoff, and enhanced error context
|
|
116
|
+
- f4e668d Fix bug in device.py, add type hints, add __all__ exports, add pyqt6-stubs
|
|
117
|
+
- 4cd31a5 expand dbus_client tests to 100% coverage (by big-pickle/OpenCode)
|
|
118
|
+
- 3ce112f refactor release.py for testability, add comprehensive tests (by big-pickle/OpenCode)
|
|
119
|
+
- 81a1213 make CLAUDE.md a redirect to AGENTS.md
|
|
120
|
+
|
|
121
|
+
## 0.0.9 — 2026-04-05
|
|
122
|
+
|
|
123
|
+
### Changes since v0.0.8
|
|
124
|
+
|
|
125
|
+
- f604cac dynamic versioning from __init__.py (by big-pickle/OpenCode)
|
|
126
|
+
|
|
127
|
+
## 0.0.8 — 2026-04-05
|
|
128
|
+
|
|
129
|
+
### Changes since v0.0.7
|
|
130
|
+
|
|
131
|
+
- 6ac501c release.py: remove automatic dev version bump after release (by big-pickle/OpenCode)
|
|
132
|
+
- ad32718 release.py: fix version mismatch after release, format fixes, add AGENTS.md
|
|
133
|
+
- 797fb54 README: add credits section
|
|
134
|
+
- 69be8be Start 0.0.8-dev
|
|
135
|
+
|
|
136
|
+
## 0.0.7 — 2026-04-04
|
|
137
|
+
|
|
138
|
+
### Changes since v0.0.6
|
|
139
|
+
|
|
140
|
+
- 1e4fc6d rpm: install XDG autostart entry for session auto-start
|
|
141
|
+
- e983a43 Start 0.0.7-dev
|
|
142
|
+
|
|
143
|
+
## 0.0.6 — 2026-04-04
|
|
144
|
+
|
|
145
|
+
### Changes since v0.0.5
|
|
146
|
+
|
|
147
|
+
- 0be0a9c app: use usbguard_gui theme icon with fallback to source-tree SVG
|
|
148
|
+
- e41aa82 device_list: remove permanent rule when demoting to temporary allow
|
|
149
|
+
- 85808c5 device_list: refresh immediately after context-menu action
|
|
150
|
+
- b347e57 device_list: blue background for temporarily allowed devices
|
|
151
|
+
- ebd9daa app: fix spurious device dialogs for allowed/non-insert events
|
|
152
|
+
- dbb035c app: add detailed debug logging for device presence events
|
|
153
|
+
- e638935 Makefile: use python -m usbguard_gui in run target
|
|
154
|
+
- df30bd7 app: fix spurious device dialogs for allowed/non-insert events
|
|
155
|
+
- 713e99b Start 0.0.6-dev
|
|
156
|
+
|
|
157
|
+
## 0.0.5 — 2026-04-04
|
|
158
|
+
|
|
159
|
+
### Changes since v0.0.4
|
|
160
|
+
|
|
161
|
+
- 96bdf78 app: use usbguard_gui theme icon with fallback
|
|
162
|
+
- 8c2b9d7 Start 0.0.5-dev
|
|
163
|
+
|
|
164
|
+
## 0.0.4 — 2026-04-04
|
|
165
|
+
|
|
166
|
+
### Changes since v0.0.3
|
|
167
|
+
|
|
168
|
+
- 28396f0 Add whl_local/.gitignore
|
|
169
|
+
- 49fbc4d rpm: fix %preun, drop %postun and unused systemd macros
|
|
170
|
+
- 7b50b6f rpm: force-enable usbguard-dbus.service on install
|
|
171
|
+
- f40e501 Start 0.0.4-dev
|
|
172
|
+
|
|
173
|
+
## 0.0.3 — 2026-04-04
|
|
174
|
+
|
|
175
|
+
### Changes since v0.0.2
|
|
176
|
+
|
|
177
|
+
- 49c55d1 device_list: use dark row colors with white text
|
|
178
|
+
- ff03739 rpm: require usbguard-dbus, enable its service on install
|
|
179
|
+
- 552a629 Fix duplicate signals, auth errors, add polkit rule
|
|
180
|
+
- 58e907d Start 0.0.3-dev
|
|
181
|
+
|
|
182
|
+
## 0.0.2 — 2026-04-04
|
|
183
|
+
|
|
184
|
+
### Changes since v0.0.1
|
|
185
|
+
|
|
186
|
+
- b2408ca Add RPM packaging
|
|
187
|
+
- 430cd16 Start 0.0.2-dev
|
|
188
|
+
|
|
189
|
+
## 0.0.1 — 2026-04-04
|
|
190
|
+
|
|
191
|
+
### Changes since beginning
|
|
192
|
+
|
|
193
|
+
- a071a82 Rename to usbguard_gui, python things...
|
|
194
|
+
- 76f5233 Add release.py.
|
|
195
|
+
- 83838a4 Add Makefile
|
|
196
|
+
- adc75c8 Initial implementation of usbguard_gui
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# Replacing PyGObject with dbus-fast
|
|
2
|
+
|
|
3
|
+
This document describes the migration from `dasbus + PyGObject` to `dbus-fast` to eliminate the PyGObject dependency.
|
|
4
|
+
|
|
5
|
+
## Motivation
|
|
6
|
+
|
|
7
|
+
dasbus uses PyGObject (gi) internally for GLib type system integration. Even though the application uses PyQt6 (not
|
|
8
|
+
GTK), dasbus requires GLib's type introspection layer. PyGObject is a heavy dependency that many systems may not have
|
|
9
|
+
pre-installed.
|
|
10
|
+
|
|
11
|
+
## Solution: dbus-fast
|
|
12
|
+
|
|
13
|
+
`dbus-fast` is a pure-Python asyncio D-Bus library with no GLib dependency. Two integration approaches were explored:
|
|
14
|
+
|
|
15
|
+
### Branch 1: `dev-glib`
|
|
16
|
+
|
|
17
|
+
Uses `dbus_fast.glib` which integrates with GLib's main loop.
|
|
18
|
+
|
|
19
|
+
### Branch 2: `dev-qthread` (This branch)
|
|
20
|
+
|
|
21
|
+
Uses `dbus_fast.aio` in a dedicated QThread with its own asyncio event loop.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Implementation Details
|
|
26
|
+
|
|
27
|
+
### Dependencies Changed
|
|
28
|
+
|
|
29
|
+
**Before:**
|
|
30
|
+
|
|
31
|
+
```toml
|
|
32
|
+
dependencies = [
|
|
33
|
+
"PyQt6>=6.5",
|
|
34
|
+
"dasbus>=1.7",
|
|
35
|
+
"PyGObject>=3.42",
|
|
36
|
+
]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**After:**
|
|
40
|
+
|
|
41
|
+
```toml
|
|
42
|
+
dependencies = [
|
|
43
|
+
"PyQt6>=6.5",
|
|
44
|
+
"dbus-fast>=1.0",
|
|
45
|
+
]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### New Files
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
src/usbguard_gui/introspection/
|
|
52
|
+
├── org.usbguard.Devices1.xml # USBGuard Devices interface
|
|
53
|
+
├── org.usbguard.Policy1.xml # USBGuard Policy interface
|
|
54
|
+
└── org.freedesktop.ScreenSaver.xml # ScreenSaver interface
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### API Changes
|
|
58
|
+
|
|
59
|
+
| dasbus | dbus-fast |
|
|
60
|
+
|-------------------------------|--------------------------------------------------------------|
|
|
61
|
+
| `SystemMessageBus()` | `MessageBus(bus_type=BusType.SYSTEM)` |
|
|
62
|
+
| `bus.get_proxy(name, path)` | `bus.get_proxy_object(name, path, xml).get_interface(iface)` |
|
|
63
|
+
| `proxy.listDevices()` | `proxy.call_list_devices()` |
|
|
64
|
+
| `proxy.Signal.connect(cb)` | `proxy.on_signal(cb)` |
|
|
65
|
+
| `proxy.Signal.disconnect(cb)` | `proxy.off_signal(cb)` |
|
|
66
|
+
| `DBusError.error_name` | `DBusError.type` |
|
|
67
|
+
|
|
68
|
+
### Error Handling
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
# dasbus
|
|
72
|
+
def _is_permission_error(e: DBusError) -> bool:
|
|
73
|
+
name = getattr(e, "error_name", "") or ""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# dbus-fast
|
|
77
|
+
def _is_permission_error(e: DBusError) -> bool:
|
|
78
|
+
error_name = getattr(e, "type", "") or ""
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## This Branch: QThread + asyncio
|
|
84
|
+
|
|
85
|
+
This implementation uses a dedicated QThread running an asyncio event loop to handle D-Bus communication.
|
|
86
|
+
|
|
87
|
+
### Architecture
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
USBGuardClient (QObject) _DBusThread (QThread)
|
|
91
|
+
├── Public API (unchanged) ├── Runs asyncio event loop
|
|
92
|
+
├── Receives results via signals ├── Connects to D-Bus
|
|
93
|
+
└── Forwards signals to GUI ├── Schedules commands from queue
|
|
94
|
+
└── Emits results as Qt signals
|
|
95
|
+
|
|
96
|
+
ScreensaverMonitor (QObject) _ScreensaverThread (QThread)
|
|
97
|
+
├── Public API (unchanged) ├── Runs asyncio event loop
|
|
98
|
+
├── Receives active_changed via signal └── Connects to session D-Bus
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Key Components
|
|
102
|
+
|
|
103
|
+
**`_DBusThread`**: QThread subclass that:
|
|
104
|
+
|
|
105
|
+
- Creates a new asyncio event loop
|
|
106
|
+
- Connects to system D-Bus using `dbus_fast.aio.MessageBus`
|
|
107
|
+
- Maintains proxy objects for USBGuard interfaces
|
|
108
|
+
- Subscribes to D-Bus signals (DevicePresenceChanged, DevicePolicyChanged)
|
|
109
|
+
- Processes commands from a queue (thread-safe communication)
|
|
110
|
+
- Emits Qt signals for device events and method results
|
|
111
|
+
|
|
112
|
+
**Command Queue Pattern**:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
def list_devices(self, query: str = "match") -> None:
|
|
116
|
+
if self._devices_iface and self._loop:
|
|
117
|
+
self._schedule(self._do_list_devices(query))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _schedule(self, coro: asyncio.coroutine) -> None:
|
|
121
|
+
if self._loop and self._running:
|
|
122
|
+
self._loop.call_soon_threadsafe(asyncio.ensure_future, coro)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### API Changes in This Branch
|
|
126
|
+
|
|
127
|
+
Methods are now **asynchronous fire-and-forget**:
|
|
128
|
+
|
|
129
|
+
- `client.list_devices()` → returns via `list_devices_result` signal
|
|
130
|
+
- `client.apply_device_policy()` → returns via `apply_policy_result` signal
|
|
131
|
+
- `client.list_rules()` → returns via `list_rules_result` signal
|
|
132
|
+
- `client.remove_rule()` → returns via `remove_rule_result` signal
|
|
133
|
+
|
|
134
|
+
### New Signals Added
|
|
135
|
+
|
|
136
|
+
| Signal | Parameters | Description |
|
|
137
|
+
|-----------------------|-------------------------|---------------------------------|
|
|
138
|
+
| `list_devices_result` | `list[Device]` | Result of list_devices() |
|
|
139
|
+
| `apply_policy_result` | `int \| None` | Result of apply_device_policy() |
|
|
140
|
+
| `list_rules_result` | `list[tuple[int, str]]` | Result of list_rules() |
|
|
141
|
+
| `remove_rule_result` | `bool` | Result of remove_rule() |
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Approach Comparison
|
|
146
|
+
|
|
147
|
+
### dev-glib: GLib Integration
|
|
148
|
+
|
|
149
|
+
**Pros:**
|
|
150
|
+
|
|
151
|
+
- Simpler implementation
|
|
152
|
+
- No additional threads needed
|
|
153
|
+
- Direct signal/callback integration with Qt
|
|
154
|
+
- Synchronous methods (return values directly)
|
|
155
|
+
|
|
156
|
+
**Cons:**
|
|
157
|
+
|
|
158
|
+
- Still requires GLib dependency
|
|
159
|
+
|
|
160
|
+
### dev-qthread: QThread + asyncio (This branch)
|
|
161
|
+
|
|
162
|
+
**Pros:**
|
|
163
|
+
|
|
164
|
+
- Pure asyncio, no GLib dependency
|
|
165
|
+
- Clear separation of async and sync code
|
|
166
|
+
- More control over event loop
|
|
167
|
+
|
|
168
|
+
**Cons:**
|
|
169
|
+
|
|
170
|
+
- More complex architecture
|
|
171
|
+
- Command queue pattern required for thread-safe communication
|
|
172
|
+
- Asynchronous methods become fire-and-forget (results via signals)
|
|
173
|
+
- Additional Qt signals needed for results
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Files Modified
|
|
178
|
+
|
|
179
|
+
| File | Changes |
|
|
180
|
+
|-----------------------------|-----------------------------------------------------------|
|
|
181
|
+
| `pyproject.toml` | Replace dasbus + PyGObject with dbus-fast |
|
|
182
|
+
| `dbus_client.py` | Add `_DBusThread` class, refactor to async pattern |
|
|
183
|
+
| `screensaver.py` | Add `_ScreensaverThread` class, refactor to async pattern |
|
|
184
|
+
| `tests/test_dbus_client.py` | Update mocks for new architecture |
|
|
185
|
+
|
|
186
|
+
## Verification
|
|
187
|
+
|
|
188
|
+
Both branches pass:
|
|
189
|
+
|
|
190
|
+
- All unit tests (75 tests)
|
|
191
|
+
- Lint checks (ruff)
|
|
192
|
+
- Format checks (ruff)
|
|
193
|
+
|
|
194
|
+
## Recommendation
|
|
195
|
+
|
|
196
|
+
**Use `dev-glib`** for production. It's simpler, has fewer components, and the GLib dependency is already present on
|
|
197
|
+
most Linux systems that would run USBGuard.
|
|
198
|
+
|
|
199
|
+
Use `dev-qthread` only if:
|
|
200
|
+
|
|
201
|
+
- GLib dependency must be completely avoided
|
|
202
|
+
- Maximum isolation of async code is required
|
|
203
|
+
- Debugging async behavior is easier with a separate thread
|