Compare commits

...
4 Commits
Author SHA1 Message Date
Boria138 c02366d76a bump to v1.3.2
Build AppImage, Arch, Fedora and Debian Packages / Build AppImage (push) Successful in 4m27s
Build AppImage, Arch, Fedora and Debian Packages / Build Arch Package (push) Successful in 1m39s
Build AppImage, Arch, Fedora and Debian Packages / Build Fedora RPM (42) (push) Successful in 4m32s
Build AppImage, Arch, Fedora and Debian Packages / Build Fedora RPM (43) (push) Successful in 1m2s
Build AppImage, Arch, Fedora and Debian Packages / Build Fedora RPM (44) (push) Successful in 1m23s
Build AppImage, Arch, Fedora and Debian Packages / Build Fedora RPM (rawhide) (push) Successful in 2m49s
Build AppImage, Arch, Fedora and Debian Packages / Build Debian Package (push) Successful in 2m26s
Build AppImage, Arch, Fedora and Debian Packages / Create and Publish Release (push) Successful in 30s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-08-10 22:03:44 +05:00
Boria138 742e4e646c fix: detect Steam user without MostRecent flag
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-08-10 22:01:55 +05:00
Boria138 b134c73d2a bump to v1.3.1
Build AppImage, Arch, Fedora and Debian Packages / Build AppImage (push) Successful in 4m37s
Build AppImage, Arch, Fedora and Debian Packages / Build Arch Package (push) Successful in 1m15s
Build AppImage, Arch, Fedora and Debian Packages / Build Fedora RPM (rawhide) (push) Successful in 1m15s
Build AppImage, Arch, Fedora and Debian Packages / Build Debian Package (push) Successful in 2m32s
Build AppImage, Arch, Fedora and Debian Packages / Build Fedora RPM (42) (push) Successful in 3m55s
Build AppImage, Arch, Fedora and Debian Packages / Build Fedora RPM (43) (push) Successful in 1m2s
Build AppImage, Arch, Fedora and Debian Packages / Build Fedora RPM (44) (push) Successful in 1m9s
Build AppImage, Arch, Fedora and Debian Packages / Create and Publish Release (push) Successful in 15s
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-07-18 17:22:25 +05:00
Boria138 0d969f40de fix CVE-2026-59678
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
2026-07-18 17:22:25 +05:00
18 changed files with 179 additions and 36 deletions

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ on:
env:
# Common version, will be used for tagging the release
VERSION: 1.3.0
VERSION: 1.3.2
PKGDEST: "/tmp/portprotonqt"
PACKAGE: "portprotonqt"
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
+17
View File
@@ -3,6 +3,23 @@
Все заметные изменения в этом проекте фиксируются в этом файле.
Формат основан на [Keep a Changelog](https://keepachangelog.com/) и придерживается принципов [Semantic Versioning](https://semver.org/).
## [1.3.2] - 2026-08-10
### Fixed
- Исправлено определение SteamID по `AutoLogin` или единственному профилю `userdata`, если в `loginusers.vdf` отсутствует `MostRecent`
---
## [1.3.1] - 2026-07-18
### Fixed
- Исправлено CVE-2026-59678: уязвимость повышения привилегий в Polkit-правилах udisks и NetworkManager, где проверка командной строки через `ps` позволяла локальным пользователям обойти авторизацию
### Changed
- `--ppqtos` теперь требует что бы пользователь был в группе `portprotonqt`
---
## [1.3.0] - 2026-07-13
### Added
+1 -1
View File
@@ -1,5 +1,5 @@
pkgname=('portprotonqt')
pkgver=1.3.0
pkgver=1.3.2
pkgrel=1
pkgdesc="Modern GUI for managing and launching games from PortProton and Steam"
arch=('any')
+2 -1
View File
@@ -1,5 +1,5 @@
%global pypi_name portprotonqt
%global pypi_version 1.3.0
%global pypi_version 1.3.2
%global oname PortProtonQt
%global build_timestamp %(date +"%Y%m%d")
%global _python_no_extras_requires 1
@@ -111,6 +111,7 @@ install -Dpm 0644 ./completions/_portprotonqt -t %{buildroot}%{zsh_completions_d
%{_datadir}/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg
%{_metainfodir}/ru.linux_gaming.PortProtonQt.metainfo.xml
%{_udevrulesdir}/60-portprotonqt.rules
%{_sysusersdir}/portprotonqt.conf
%{_datadir}/polkit-1/rules.d/ru.linux_gaming.PortProtonQt.rules
%{_datadir}/applications/ru.linux_gaming.PortProtonQt.desktop
%{_datadir}/mime/packages/ru.linux_gaming.PortProtonQt.xml
+2 -1
View File
@@ -1,5 +1,5 @@
%global pypi_name portprotonqt
%global pypi_version 1.3.0
%global pypi_version 1.3.2
%global oname PortProtonQt
%global _python_no_extras_requires 1
@@ -109,6 +109,7 @@ install -Dpm 0644 ./completions/_portprotonqt -t %{buildroot}%{zsh_completions_d
%{_datadir}/icons/hicolor/scalable/apps/ru.linux_gaming.PortProtonQt.svg
%{_metainfodir}/ru.linux_gaming.PortProtonQt.metainfo.xml
%{_udevrulesdir}/60-portprotonqt.rules
%{_sysusersdir}/portprotonqt.conf
%{_datadir}/polkit-1/rules.d/ru.linux_gaming.PortProtonQt.rules
%{_datadir}/applications/ru.linux_gaming.PortProtonQt.desktop
%{_datadir}/mime/packages/ru.linux_gaming.PortProtonQt.xml
@@ -0,0 +1 @@
g portprotonqt - -
@@ -24,9 +24,15 @@
</developer>
<releases>
<release version="1.3.0" date="2026-07-13">
<release version="1.3.2" date="2026-08-10">
<description></description>
</release>
<release version="1.3.1" date="2026-07-17">
<description/>
</release>
<release version="1.3.0" date="2026-07-13">
<description/>
</release>
<release version="1.2.0" date="2026-06-10">
<description/>
</release>
@@ -1,21 +1,15 @@
polkit.addRule(function(action, subject) {
var cmd_line = polkit.spawn(["ps", "-o", "args=", "-p", String(subject.pid)]);
var ppid = polkit.spawn(["ps", "-o", "ppid=", "-p", String(subject.pid)]).trim();
var parent_cmd_line = "";
if (ppid) {
parent_cmd_line = polkit.spawn(["ps", "-o", "args=", "-p", ppid]);
if (!subject.local || !subject.active ||
!subject.isInGroup("portprotonqt")) {
return polkit.Result.NOT_HANDLED;
}
var is_ppqt_call =
cmd_line.includes("ppqtos") ||
parent_cmd_line.includes("ppqtos");
if ( (
action.id === "org.freedesktop.NetworkManager.settings.modify.system" ||
action.id == "org.freedesktop.udisks2.filesystem-mount-system" ||
action.id == "org.freedesktop.udisks2.filesystem-mount-other-seat" ||
action.id == "org.freedesktop.udisks2.filesystem-unmount-others" ) &&
is_ppqt_call)
{
return polkit.Result.YES;
if (action.id === "org.freedesktop.NetworkManager.settings.modify.system" ||
action.id === "org.freedesktop.udisks2.filesystem-mount-system" ||
action.id === "org.freedesktop.udisks2.filesystem-mount-other-seat" ||
action.id === "org.freedesktop.udisks2.filesystem-unmount-others") {
return polkit.Result.YES;
}
return polkit.Result.NOT_HANDLED;
});
+12
View File
@@ -1,3 +1,15 @@
portprotonqt (1.3.2) unstable; urgency=medium
* New upstream release.
-- Ivan Mazhukin <vanyamajukin@outlook.com> Mon, 10 Aug 2026 22:02:11 +0500
portprotonqt (1.3.1) unstable; urgency=medium
* New upstream release.
-- Ivan Mazhukin <vanyamajukin@outlook.com> Fri, 17 Jul 2026 19:46:59 +0500
portprotonqt (1.3.0) unstable; urgency=medium
* New upstream release.
+1
View File
@@ -1,6 +1,7 @@
usr/bin/portprotonqt
usr/bin/vk_gpu_info
usr/lib/python3/dist-packages/portprotonqt/
usr/lib/sysusers.d/portprotonqt.conf
usr/lib/udev/rules.d/60-portprotonqt.rules
usr/share/applications/ru.linux_gaming.PortProtonQt.desktop
usr/share/bash-completion/completions/portprotonqt
+3
View File
@@ -19,3 +19,6 @@ execute_after_dh_auto_install:
debian/tmp/usr/share/zsh/site-functions/_portprotonqt
install -Dpm 0644 completions/portprotonqt.fish \
debian/tmp/usr/share/fish/vendor_completions.d/portprotonqt.fish
execute_after_dh_install:
dh_installsysusers
+6 -1
View File
@@ -1,5 +1,5 @@
project('portprotonqt', 'c',
version: '1.3.0',
version: '1.3.2',
meson_version: '>= 0.61.2',
license: 'GPL-3.0',
)
@@ -139,3 +139,8 @@ install_data(
'build-aux/share/polkit-1/rules.d/ru.linux_gaming.PortProtonQt.rules',
install_dir: prefix / 'share' / 'polkit-1' / 'rules.d',
)
install_data(
'build-aux/lib/sysusers.d/portprotonqt.conf',
install_dir: prefix / 'lib' / 'sysusers.d',
)
+1 -1
View File
@@ -18,7 +18,7 @@ try:
APP_VERSION = version_module.APP_VERSION
except ImportError:
APP_COMMIT = ""
APP_VERSION = "1.3.0"
APP_VERSION = "1.3.2"
__app_version__ = os.environ.get("PORTPROTONQT_VERSION", APP_VERSION)
+32 -10
View File
@@ -23,6 +23,7 @@ STEAM_DATA_DIRS = (
APPINFO_MAGIC_V40 = 0x07564428
APPINFO_MAGIC_V41 = 0x07564429
APPINFO_ENTRY_METADATA_SIZE = 60
STEAM_ID64_INDIVIDUAL_BASE = 76561197960265728
def safe_vdf_load(path: str | Path) -> dict:
@@ -254,18 +255,39 @@ def get_last_steam_user(steam_home: Path) -> dict | None:
"""Return data for last Steam user from loginusers.vdf."""
loginusers_path = steam_home / "config/loginusers.vdf"
data = safe_vdf_load(loginusers_path)
if not data:
return None
users = data.get('users', {})
users = data.get('users', {}) if data else {}
selected_user_id = None
for user_id, user_info in users.items():
if user_info.get('MostRecent') == '1':
try:
return {'SteamID': int(user_id)}
except ValueError:
logger.error(f"Invalid SteamID format: {user_id}")
return None
logger.info("No user found with MostRecent=1")
return None
selected_user_id = user_id
break
if selected_user_id is None and user_info.get('AutoLogin') == '1':
selected_user_id = user_id
if selected_user_id is None:
userdata_dir = steam_home / "userdata"
try:
candidates = [
entry for entry in userdata_dir.iterdir()
if entry.name.isdigit()
and entry.name != '0'
and not entry.is_symlink()
and entry.is_dir()
and (entry / "config/localconfig.vdf").is_file()
]
except OSError as e:
logger.info("Could not inspect Steam userdata: %s", e)
return None
if len(candidates) != 1:
logger.info("No unambiguous Steam userdata profile found")
return None
selected_user_id = str(
STEAM_ID64_INDIVIDUAL_BASE + int(candidates[0].name)
)
try:
return {'SteamID': int(selected_user_id)}
except ValueError:
logger.error(f"Invalid SteamID format: {selected_user_id}")
return None
def convert_steam_id(steam_id: int) -> int:
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "portprotonqt"
version = "1.3.0"
version = "1.3.2"
description = "A project to rewrite PortProton (PortWINE) using PySide"
requires-python = ">=3.10"
readme = "README.md"
+21
View File
@@ -0,0 +1,21 @@
from pathlib import Path
def test_polkit_rule_does_not_grant_application_privileges() -> None:
rule_path = Path(__file__).parents[1] / (
"build-aux/share/polkit-1/rules.d/ru.linux_gaming.PortProtonQt.rules"
)
rule = rule_path.read_text()
assert "polkit.spawn" not in rule
assert "ppqtos" not in rule
assert "!subject.local || !subject.active ||" in rule
assert '!subject.isInGroup("portprotonqt")' in rule
def test_portprotonqt_group_is_provisioned() -> None:
sysusers_path = Path(__file__).parents[1] / (
"build-aux/lib/sysusers.d/portprotonqt.conf"
)
assert sysusers_path.read_text() == "g portprotonqt - -\n"
+59
View File
@@ -6,6 +6,7 @@ from unittest.mock import patch
from portprotonqt.steam_api.utils import (
APPINFO_ENTRY_METADATA_SIZE,
APPINFO_MAGIC_V41,
STEAM_ID64_INDIVIDUAL_BASE,
normalize_name,
is_valid_candidate,
filter_candidates,
@@ -16,6 +17,7 @@ from portprotonqt.steam_api.utils import (
get_steam_launch_commands,
get_steam_compatibilitytools_dir,
get_steam_compat_tool,
get_last_steam_user,
get_local_steam_cover,
get_steam_installed_games,
_iter_existing_steam_data_dirs,
@@ -305,6 +307,63 @@ class TestConvertSteamId:
assert convert_steam_id(0) == 0
def test_get_last_steam_user_falls_back_to_autologin(tmp_path: Path):
config_dir = tmp_path / "config"
config_dir.mkdir()
(config_dir / "loginusers.vdf").write_text(
'"users"\n{\n'
' "76561198012003723"\n'
' {\n'
' "AccountName" "x"\n'
' "AutoLogin" "1"\n'
' }\n'
'}\n',
encoding="utf-8",
)
assert get_last_steam_user(tmp_path) == {"SteamID": 76561198012003723}
def test_get_last_steam_user_ignores_allow_autologin(tmp_path: Path):
config_dir = tmp_path / "config"
config_dir.mkdir()
(config_dir / "loginusers.vdf").write_text(
'"users"\n{\n'
' "76561198012003723"\n'
' {\n'
' "AllowAutoLogin" "1"\n'
' }\n'
'}\n',
encoding="utf-8",
)
assert get_last_steam_user(tmp_path) is None
def test_get_last_steam_user_falls_back_to_only_userdata(tmp_path: Path):
account_id = 51737995
localconfig = (
tmp_path / "userdata" / str(account_id) / "config/localconfig.vdf"
)
localconfig.parent.mkdir(parents=True)
localconfig.write_text('"UserLocalConfigStore"\n{\n}\n', encoding="utf-8")
assert get_last_steam_user(tmp_path) == {
"SteamID": STEAM_ID64_INDIVIDUAL_BASE + account_id
}
def test_get_last_steam_user_rejects_multiple_userdata(tmp_path: Path):
for account_id in (51737995, 51737996):
localconfig = (
tmp_path / "userdata" / str(account_id) / "config/localconfig.vdf"
)
localconfig.parent.mkdir(parents=True)
localconfig.touch()
assert get_last_steam_user(tmp_path) is None
class TestDecodeText:
def test_html_entities(self):
assert decode_text("&amp;") == "&"
Generated
+2 -2
View File
@@ -214,7 +214,7 @@ name = "exceptiongroup"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
wheels = [
@@ -492,7 +492,7 @@ wheels = [
[[package]]
name = "portprotonqt"
version = "1.3.0"
version = "1.3.2"
source = { editable = "." }
dependencies = [
{ name = "babel" },