From c96214f59d811e5722e217d980ec29499df8b5e0 Mon Sep 17 00:00:00 2001 From: Sten Tijhuis <102481635+Stensel8@users.noreply.github.com> Date: Fri, 10 Apr 2026 22:39:31 +0200 Subject: [PATCH 01/11] chore: update dependencies and workflows, renew system information --- .github/dependabot.yml | 21 +++++++++ .github/renovate.json | 34 +++++++++++++++ .github/workflows/check-dependencies.yml | 43 +++++++++++++++++++ .github/workflows/codeql-analysis.yml | 32 ++++++++++++++ .github/workflows/hugo.yml | 9 ++-- .github/workflows/python-checks.yml | 31 +++++++++++++ .github/workflows/trivy-scan.yml | 18 ++++++++ README.md | 39 +++++++++++++++++ README.nl.md | 39 +++++++++++++++++ content/_index.md | 4 +- content/_index.nl.md | 4 +- content/docs/applications.md | 4 +- content/docs/hardware/asusctl-rog-control.md | 2 +- content/docs/known-issues.md | 2 +- content/docs/known-issues.nl.md | 2 +- go.mod | 2 +- go.sum | 4 +- static/images/system-info.avif | Bin 25603 -> 39429 bytes 18 files changed, 274 insertions(+), 16 deletions(-) create mode 100644 .github/renovate.json create mode 100644 .github/workflows/check-dependencies.yml create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/python-checks.yml create mode 100644 .github/workflows/trivy-scan.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index db7b71b..10d83d2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,3 +13,24 @@ updates: interval: "weekly" commit-message: prefix: "chore" + + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "chore" + + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "chore" + + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "chore" diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..207a04f --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,34 @@ +{ + "extends": ["config:base"], + "automerge": false, + "timezone": "Europe/Amsterdam", + "labels": ["dependencies"], + "schedule": ["before 3am on Monday"], + "prHourlyLimit": 2, + "prConcurrentLimit": 5, + "packageRules": [ + { + "managers": ["gomod"], + "groupName": "go modules", + "schedule": ["before 3am on Monday"] + }, + { + "managers": ["pip"], + "groupName": "python packages", + "schedule": ["before 3am on Monday"] + }, + { + "managers": ["github-actions"], + "groupName": "github actions", + "automerge": true, + "automergeType": "minor", + "schedule": ["before 3am on Monday"] + }, + { + "managers": ["docker"], + "groupName": "docker images", + "schedule": ["before 3am on Monday"], + "automerge": false + } + ] +} diff --git a/.github/workflows/check-dependencies.yml b/.github/workflows/check-dependencies.yml new file mode 100644 index 0000000..3400f2e --- /dev/null +++ b/.github/workflows/check-dependencies.yml @@ -0,0 +1,43 @@ +name: "Check dependencies" + +on: + schedule: + - cron: '0 4 * * 1' + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + go-updates: + name: Check Go module updates + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: '1.26' + + - name: Install jq + run: sudo apt-get update && sudo apt-get install -y jq + + - name: Check Go module updates + id: go_updates + run: | + go list -m -u -json all | jq -r '. | select(.Update) | "\(.Path): \(.Version) -> \(.Update.Version)"' > deps-updates.txt || true + if [ -s deps-updates.txt ]; then + echo "found=true" >> $GITHUB_OUTPUT + else + echo "found=false" >> $GITHUB_OUTPUT + fi + + - name: Create issue for Go updates + if: steps.go_updates.outputs.found == 'true' + uses: peter-evans/create-issue-from-file@v4 + with: + title: "Dependency updates available: Go modules" + content-filepath: deps-updates.txt + labels: "dependencies,automated" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..2e902a6 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,32 @@ +name: "CodeQL Analysis" + +on: + push: + branches: [ main, development ] + pull_request: + branches: [ main, development ] + schedule: + - cron: '0 3 * * 0' + +permissions: + contents: read + security-events: write + +jobs: + analyze: + name: Analyze (CodeQL) + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: go + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/hugo.yml b/.github/workflows/hugo.yml index 4b1722b..f2835ce 100644 --- a/.github/workflows/hugo.yml +++ b/.github/workflows/hugo.yml @@ -22,12 +22,13 @@ jobs: build: runs-on: ubuntu-latest env: - HUGO_VERSION: 0.152.2 + HUGO_VERSION: 0.160.0 steps: - name: Install Hugo CLI - run: | - wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ - && sudo dpkg -i ${{ runner.temp }}/hugo.deb + uses: peaceiris/actions-hugo@v3.0.0 + with: + hugo-version: '0.160.1' + extended: true - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/python-checks.yml b/.github/workflows/python-checks.yml new file mode 100644 index 0000000..10df25f --- /dev/null +++ b/.github/workflows/python-checks.yml @@ -0,0 +1,31 @@ +name: Python Checks + +on: + push: + branches: [ main, development ] + pull_request: + branches: [ main, development ] + schedule: + - cron: '0 5 * * 0' + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6.0.2 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.14' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 bandit + + - name: Lint with flake8 + run: flake8 static/scripts/saxion-eduroam.py --max-line-length=120 + + - name: Security scan with bandit + run: bandit -r static/scripts/saxion-eduroam.py diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml new file mode 100644 index 0000000..2d50434 --- /dev/null +++ b/.github/workflows/trivy-scan.yml @@ -0,0 +1,18 @@ +name: "Trivy filesystem scan" + +on: + schedule: + - cron: '0 2 * * 0' + workflow_dispatch: + +jobs: + trivy-scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Run Trivy filesystem scan + uses: aquasecurity/trivy-action@v0.35.0 + with: + scan-type: fs + severity: CRITICAL,HIGH + format: table diff --git a/README.md b/README.md index c8b573f..3b75ab3 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,45 @@ CachyOS on the ASUS ROG Zephyrus G16 GA605WV (2024). My personal setup log: docu **Browse the full documentation site: [zephyrus-linux.stensel.nl](https://zephyrus-linux.stensel.nl/)** +## System information + +``` +❯ fish + .-------------------------: sten@Sten-Laptop + .+=========================. ---------------- + :++===++==================- :++- OS: CachyOS x86_64 + :*++====+++++=============- .==: Host: ROG Zephyrus G16 GA605WV_GA605WV (1.0) + -*+++=====+***++==========: Kernel: Linux 6.19.11-1-cachyos + =*++++========------------: Uptime: 3 hours, 49 mins + =*+++++=====- ... Packages: 1695 (pacman), 22 (flatpak) + .+*+++++=-===: .=+++=: Shell: fish 4.6.0 + :++++=====-==: -*****+ Display (LQ160R1JW02): 2560x1600 @ 1.33x in 16", 240 Hz [Built-in] + :++========-=. .=+**+. DE: GNOME 50.0 +.+==========-. . WM: Mutter (Wayland) + :+++++++====- .--==-. WM Theme: Adwaita + :++==========. :+++++++: Theme: Adwaita [GTK2/3/4] + .-===========. =*****+*+ Icons: Adwaita [GTK2/3/4] + .-===========: .+*****+: Font: Adwaita Sans (11pt) [GTK2/3/4] + -=======++++:::::::::::::::::::::::::-: .---: Cursor: Adwaita (24px) + :======++++====+++******************=. Terminal: GNOME Console 50.0 + :=====+++==========++++++++++++++*- Terminal Font: Adwaita Mono (11pt) + .====++==============++++++++++*- CPU: AMD Ryzen AI 9 HX 370 (24) @ 5.16 GHz + .===+==================+++++++: GPU 1: AMD Radeon 890M Graphics [Integrated] + .-=======================+++: GPU 2: NVIDIA GeForce RTX 4060 Max-Q / Mobile [Discrete] + .......................... Memory: 10.24 GiB / 28.98 GiB (35%) + Swap: 1.28 MiB / 28.98 GiB (0%) + Disk (/): 270.70 GiB / 951.85 GiB (28%) - btrfs + Local IP (wlan0): 192.168.0.72/24 + Battery (A32-K55): 100% [AC Connected] + Locale: en_US.UTF-8 + + + +~ +❯ +``` + + ## About this project This is my personal setup log for running CachyOS on this laptop. I'm not a software engineer or developer: just someone who switched to Linux and ran into a lot of things that didn't work out of the box. I figured I'd write it all down so others don't have to go through the same trial and error I did. diff --git a/README.nl.md b/README.nl.md index 2dc1493..0f99dc9 100644 --- a/README.nl.md +++ b/README.nl.md @@ -7,6 +7,45 @@ CachyOS op de ASUS ROG Zephyrus G16 GA605WV (2024). Mijn persoonlijke setup-log: **Bekijk de volledige documentatiesite: [zephyrus-linux.stensel.nl](https://zephyrus-linux.stensel.nl/nl/)** +## Systeeminformatie + +``` +❯ fish + .-------------------------: sten@Sten-Laptop + .+=========================. ---------------- + :++===++==================- :++- OS: CachyOS x86_64 + :*++====+++++=============- .==: Host: ROG Zephyrus G16 GA605WV_GA605WV (1.0) + -*+++=====+***++==========: Kernel: Linux 6.19.11-1-cachyos + =*++++========------------: Uptime: 3 hours, 49 mins + =*+++++=====- ... Packages: 1695 (pacman), 22 (flatpak) + .+*+++++=-===: .=+++=: Shell: fish 4.6.0 + :++++=====-==: -*****+ Display (LQ160R1JW02): 2560x1600 @ 1.33x in 16", 240 Hz [Built-in] + :++========-=. .=+**+. DE: GNOME 50.0 +.+==========-. . WM: Mutter (Wayland) + :+++++++====- .--==-. WM Theme: Adwaita + :++==========. :+++++++: Theme: Adwaita [GTK2/3/4] + .-===========. =*****+*+ Icons: Adwaita [GTK2/3/4] + .-===========: .+*****+: Font: Adwaita Sans (11pt) [GTK2/3/4] + -=======++++:::::::::::::::::::::::::-: .---: Cursor: Adwaita (24px) + :======++++====+++******************=. Terminal: GNOME Console 50.0 + :=====+++==========++++++++++++++*- Terminal Font: Adwaita Mono (11pt) + .====++==============++++++++++*- CPU: AMD Ryzen AI 9 HX 370 (24) @ 5.16 GHz + .===+==================+++++++: GPU 1: AMD Radeon 890M Graphics [Integrated] + .-=======================+++: GPU 2: NVIDIA GeForce RTX 4060 Max-Q / Mobile [Discrete] + .......................... Memory: 10.24 GiB / 28.98 GiB (35%) + Swap: 1.28 MiB / 28.98 GiB (0%) + Disk (/): 270.70 GiB / 951.85 GiB (28%) - btrfs + Local IP (wlan0): 192.168.0.72/24 + Battery (A32-K55): 100% [AC Connected] + Locale: en_US.UTF-8 + + + +~ +❯ +``` + + ## Over dit project Dit is mijn persoonlijke setup-log voor CachyOS op deze laptop. Ik ben geen software-engineer of developer: gewoon iemand die naar Linux is overgestapt en daarna tegen van alles aanliep wat niet meteen werkte. Ik heb alles opgeschreven zodat anderen niet hetzelfde hoeven uitzoeken als ik. diff --git a/content/_index.md b/content/_index.md index 536d51f..81cdb7e 100644 --- a/content/_index.md +++ b/content/_index.md @@ -38,8 +38,8 @@ toc: false | **iGPU** | AMD Radeon 890M | | **dGPU** | NVIDIA GeForce RTX 4060 Laptop (Max-Q) | | **OS** | CachyOS (Arch) | -| **Kernel** | 6.19.8-1-cachyos | -| **Display Server** | Wayland (GNOME 49) | +| **Kernel** | 6.19.11-1-cachyos | +| **Display Server** | Wayland (GNOME 50) | | **CPU Scheduler** | scx_lavd (sched_ext) | | **Secure Boot** | Enabled | diff --git a/content/_index.nl.md b/content/_index.nl.md index 5a96bf7..5d95780 100644 --- a/content/_index.nl.md +++ b/content/_index.nl.md @@ -38,8 +38,8 @@ toc: false | **iGPU** | AMD Radeon 890M | | **dGPU** | NVIDIA GeForce RTX 4060 Laptop (Max-Q) | | **OS** | CachyOS (Arch) | -| **Kernel** | 6.19.8-1-cachyos | -| **Display Server** | Wayland (GNOME 49) | +| **Kernel** | 6.19.11-1-cachyos | +| **Display Server** | Wayland (GNOME 50) | | **CPU Scheduler** | scx_lavd (sched_ext) | | **Secure Boot** | Ingeschakeld | diff --git a/content/docs/applications.md b/content/docs/applications.md index 8e8eddf..eb99849 100644 --- a/content/docs/applications.md +++ b/content/docs/applications.md @@ -39,7 +39,7 @@ Nothing special here, just set the hostname via System Settings so the machine h ### GNOME window buttons: adding minimize & maximize back -By default, GNOME 49 only shows the close button. One command fixes it: +By default, GNOME 50 only shows the close button. One command fixes it: ```bash gsettings set org.gnome.desktop.wm.preferences button-layout 'appmenu:minimize,maximize,close' @@ -91,7 +91,7 @@ Using Just Perfection alone without the `gsettings` change may still leave edge ### Touchpad scroll speed: no native GNOME setting (yet) -As of GNOME 49, there is simply **no native setting** for touchpad scroll speed anywhere in the Settings panel. KDE Plasma has had this for years. There are merge requests open in [mutter](https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1840) and [GNOME Control Center](https://gitlab.gnome.org/GNOME/gnome-control-center/-/merge_requests/991) to add it, but they've been sitting there for years. See the [GNOME Discourse thread](https://discourse.gnome.org/t/adding-scroll-speed-setting-in-gnome/25893) for context. +As of GNOME 50, there is simply **no native setting** for touchpad scroll speed anywhere in the Settings panel. KDE Plasma has had this for years. There are merge requests open in [mutter](https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1840) and [GNOME Control Center](https://gitlab.gnome.org/GNOME/gnome-control-center/-/merge_requests/991) to add it, but they've been sitting there for years. See the [GNOME Discourse thread](https://discourse.gnome.org/t/adding-scroll-speed-setting-in-gnome/25893) for context. [libinput-config](https://github.com/lz42/libinput-config) by lz42 is a third-party workaround that intercepts libinput events and applies a scroll multiplier. diff --git a/content/docs/hardware/asusctl-rog-control.md b/content/docs/hardware/asusctl-rog-control.md index 79786a3..41b2f52 100644 --- a/content/docs/hardware/asusctl-rog-control.md +++ b/content/docs/hardware/asusctl-rog-control.md @@ -304,7 +304,7 @@ Known issues and troubleshooting for asusctl & ROG Control Center are documented ### Kernel 6.19: asus-armoury driver lands in mainline -The `asus-armoury` driver has been [merged into Linux 6.19](https://www.phoronix.com/news/ASUS-Armoury-Driver-Linux-6.19). This new `platform/x86` driver replaces parts of the older `asus-wmi` with a cleaner sysfs-based API, enabling panel mode switching, APU memory allocation, PPT tuning, and more directly from the kernel. The driver is entirely community-developed by the [asus-linux team](https://asus-linux.org/), with no involvement from ASUS themselves. CachyOS ships kernel 6.19.8-1-cachyos which includes this driver and additional ASUS-specific patches. +The `asus-armoury` driver has been [merged into Linux 6.19](https://www.phoronix.com/news/ASUS-Armoury-Driver-Linux-6.19). This new `platform/x86` driver replaces parts of the older `asus-wmi` with a cleaner sysfs-based API, enabling panel mode switching, APU memory allocation, PPT tuning, and more directly from the kernel. The driver is entirely community-developed by the [asus-linux team](https://asus-linux.org/), with no involvement from ASUS themselves. CachyOS ships kernel 6.19.11-1-cachyos which includes this driver and additional ASUS-specific patches. **Before**: basic asusctl controls without Armoury settings: diff --git a/content/docs/known-issues.md b/content/docs/known-issues.md index d31a9a9..95ae14d 100644 --- a/content/docs/known-issues.md +++ b/content/docs/known-issues.md @@ -396,7 +396,7 @@ This issue has since resolved itself. Steam now launches normally; the `__GL_CON ROG Control Center shows a warning that the `asus-armoury` kernel driver is not loaded. Some advanced features (PPT power limits, APU memory allocation, MUX switch control) are unavailable. **Cause:** -The `asus-armoury` driver was merged into the Linux mainline kernel in version 6.19. CachyOS ships kernel 6.19.8-1-cachyos which includes this driver, so it should be available. +The `asus-armoury` driver was merged into the Linux mainline kernel in version 6.19. CachyOS ships kernel 6.19.11-1-cachyos which includes this driver, so it should be available. **Fix:** Verify the driver is loaded: diff --git a/content/docs/known-issues.nl.md b/content/docs/known-issues.nl.md index 94647e5..9f1bdfc 100644 --- a/content/docs/known-issues.nl.md +++ b/content/docs/known-issues.nl.md @@ -396,7 +396,7 @@ Dit probleem heeft zichzelf opgelost. Steam start nu gewoon op; de `__GL_CONSTAN ROG Control Center toont een melding dat de `asus-armoury` kerneldriver niet is geladen. Geavanceerde functies (PPT-vermogensgrenzen, APU-geheugenallocatie, MUX-switchbesturing) zijn niet beschikbaar. **Oorzaak:** -De `asus-armoury`-driver is samengevoegd in de Linux mainline-kernel in versie 6.19. CachyOS levert kernel 6.19.8-1-cachyos inclusief deze driver, dus hij zou beschikbaar moeten zijn. +De `asus-armoury`-driver is samengevoegd in de Linux mainline-kernel in versie 6.19. CachyOS levert kernel 6.19.11-1-cachyos inclusief deze driver, dus hij zou beschikbaar moeten zijn. **Fix:** Verifieer dat de driver is geladen: diff --git a/go.mod b/go.mod index 6a5d915..982a6d6 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/Stensel8/Zephyrus-Linux go 1.26 -require github.com/imfing/hextra v0.12.0 // indirect +require github.com/imfing/hextra v0.12.1 // indirect diff --git a/go.sum b/go.sum index 4f6a829..a889b91 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,2 @@ -github.com/imfing/hextra v0.12.0 h1:f6y35hW/WDJEcx9S0dOmbICOBxYE0PmP6IJFsTUgVyY= -github.com/imfing/hextra v0.12.0/go.mod h1:YAv8XRNSmcqjieFwI7fVQK1AoY2Do+45DO9HGqxSGu4= +github.com/imfing/hextra v0.12.1 h1:3t1n0bmJbDzSTVfht93UDcfF1BXMRjeFojA071ri2l8= +github.com/imfing/hextra v0.12.1/go.mod h1:vi+yhpq8YPp/aghvJlNKVnJKcPJ/VyAEcfC1BSV9ARo= diff --git a/static/images/system-info.avif b/static/images/system-info.avif index 66521995c24ce8b49dd75b0e322f1232def118c6..cf799b4842c6eb79740f06b2aedaabd85b34c9a3 100644 GIT binary patch literal 39429 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 X<*vBw^*L9nMJMdY3@Y-&AAE8^i&3Cc From bde0c6fab4f7143af65952b4f93ac0831c330c83 Mon Sep 17 00:00:00 2001 From: Sten Tijhuis <102481635+Stensel8@users.noreply.github.com> Date: Fri, 10 Apr 2026 22:53:04 +0200 Subject: [PATCH 02/11] chore: update dependencies, workflows, and security fixes - Updated Hugo workflow to use peaceiris/actions-hugo@v3.0.0 and Hugo 0.160.1 - Updated Go workflow to use actions/setup-go@v6 - Updated Trivy workflow to use aquasecurity/trivy-action@v0.35.0 - Added Python workflow for linting and security checks - Added Python ecosystem to Dependabot and Renovate - Fixed clear-text logging of sensitive information in saxion-eduroam.py - Updated checksums for saxion-eduroam.py in documentation --- .../eduroam-network-installation.md | 2 +- .../eduroam-network-installation.nl.md | 2 +- static/scripts/saxion-eduroam.py | 22 ++++++++++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/content/docs/networking/eduroam-network-installation.md b/content/docs/networking/eduroam-network-installation.md index b7373f1..0e3e418 100644 --- a/content/docs/networking/eduroam-network-installation.md +++ b/content/docs/networking/eduroam-network-installation.md @@ -59,7 +59,7 @@ A Python script automates the full `nmcli` connection setup for Saxion: curl -LO https://zephyrus-linux.stensel.nl/scripts/saxion-eduroam.py # 2. Verify checksum -echo "8dd2f2120ddebdfd9d764e04954322307dccb8c855c691de7600f2a8a71db42b saxion-eduroam.py" | sha256sum -c +echo "c8d5eb6551807ae5e2b8b1b38e8edd02fc13f9c3c62edf5626f2f8845c916021 saxion-eduroam.py" | sha256sum -c # 3. Run python3 saxion-eduroam.py diff --git a/content/docs/networking/eduroam-network-installation.nl.md b/content/docs/networking/eduroam-network-installation.nl.md index f20586f..62d21c0 100644 --- a/content/docs/networking/eduroam-network-installation.nl.md +++ b/content/docs/networking/eduroam-network-installation.nl.md @@ -59,7 +59,7 @@ Een Python-script automatiseert de volledige `nmcli`-verbindingsconfiguratie voo curl -LO https://zephyrus-linux.stensel.nl/scripts/saxion-eduroam.py # 2. Controleer de checksum -echo "8dd2f2120ddebdfd9d764e04954322307dccb8c855c691de7600f2a8a71db42b saxion-eduroam.py" | sha256sum -c +echo "c8d5eb6551807ae5e2b8b1b38e8edd02fc13f9c3c62edf5626f2f8845c916021 saxion-eduroam.py" | sha256sum -c # 3. Uitvoeren python3 saxion-eduroam.py diff --git a/static/scripts/saxion-eduroam.py b/static/scripts/saxion-eduroam.py index a00b240..19b7be6 100644 --- a/static/scripts/saxion-eduroam.py +++ b/static/scripts/saxion-eduroam.py @@ -55,12 +55,27 @@ def _detect_gui(self) -> str | None: return tool return None + def _sanitize_for_log(self, text: str) -> str: + """ + Sanitize text to remove potential sensitive information before logging. + Masks usernames, passwords, and other sensitive data. + """ + # Mask Saxion usernames (e.g., user@saxion.nl) + text = re.sub(r'\b[a-zA-Z0-9._-]+@([a-zA-Z0-9-]+\.)*saxion\.nl\b', '[REDACTED]', text, flags=re.IGNORECASE) + # Mask generic email addresses + text = re.sub(r'\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b', '[REDACTED]', text, flags=re.IGNORECASE) + # Mask passwords (generic pattern) + text = re.sub(r'\bpassword[=: ]*[^\s]+', 'password=[REDACTED]', text, flags=re.IGNORECASE) + return text + def show_message(self, text: str, is_error: bool = False): if self.silent: if is_error: - print(f"Error: {text}", file=sys.stderr) + sanitized_text = self._sanitize_for_log(text) + print(f"Error: {sanitized_text}", file=sys.stderr) else: - print(text) + sanitized_text = self._sanitize_for_log(text) + print(sanitized_text) return if not self.gui_tool: @@ -151,7 +166,8 @@ def run_nmcli(self, cmd: list[str]) -> bool: return False # Log full nmcli error to stderr (terminal only — never into GUI subprocess args). - print(f"NetworkManager error:\n{res.stderr.strip()}", file=sys.stderr) + sanitized_error = self._sanitize_for_log(res.stderr.strip()) + print(f"NetworkManager error:\n{sanitized_error}", file=sys.stderr) # Fatal error: show a static message to the GUI to avoid passing # nmcli output (which may echo user input) into a subprocess argument From 7f786445ecbe7765cc38cdefe5a9f72ea4563c22 Mon Sep 17 00:00:00 2001 From: Sten Tijhuis <102481635+Stensel8@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:05:23 +0200 Subject: [PATCH 03/11] fix: resolve flake8 errors in saxion-eduroam.py --- static/scripts/saxion-eduroam.py | 34 ++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/static/scripts/saxion-eduroam.py b/static/scripts/saxion-eduroam.py index 19b7be6..b53dfa8 100644 --- a/static/scripts/saxion-eduroam.py +++ b/static/scripts/saxion-eduroam.py @@ -61,11 +61,26 @@ def _sanitize_for_log(self, text: str) -> str: Masks usernames, passwords, and other sensitive data. """ # Mask Saxion usernames (e.g., user@saxion.nl) - text = re.sub(r'\b[a-zA-Z0-9._-]+@([a-zA-Z0-9-]+\.)*saxion\.nl\b', '[REDACTED]', text, flags=re.IGNORECASE) + text = re.sub( + r'\b[a-zA-Z0-9._-]+@([a-zA-Z0-9-]+\.)*saxion\.nl\b', + '[REDACTED]', + text, + flags=re.IGNORECASE + ) # Mask generic email addresses - text = re.sub(r'\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b', '[REDACTED]', text, flags=re.IGNORECASE) + text = re.sub( + r'\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b', + '[REDACTED]', + text, + flags=re.IGNORECASE + ) # Mask passwords (generic pattern) - text = re.sub(r'\bpassword[=: ]*[^\s]+', 'password=[REDACTED]', text, flags=re.IGNORECASE) + text = re.sub( + r'\bpassword[=: ]*[^\s]+', + 'password=[REDACTED]', + text, + flags=re.IGNORECASE + ) return text def show_message(self, text: str, is_error: bool = False): @@ -214,7 +229,7 @@ def install(self): if ca_path: cmd_secure = cmd + ["802-1x.ca-cert", ca_path] success = self.run_nmcli(cmd_secure) - + # Fallback if CA fails or is not found (still secure via domain suffix validation) if not success: print("Note: Using system default trust store (implicit validation).") @@ -230,9 +245,16 @@ def install(self): ) # Attempt to activate the connection and show only relevant output - res = subprocess.run(["nmcli", "connection", "up", CON_NAME], capture_output=True, text=True) + res = subprocess.run( + ["nmcli", "connection", "up", CON_NAME], + capture_output=True, + text=True + ) if res.returncode == 0: - print("[INFO] Connection attempt started. If you entered your password, you should be connected to eduroam.") + print( + "[INFO] Connection attempt started. If you entered your " + "password, you should be connected to eduroam." + ) else: print("[ERROR] Could not activate eduroam connection:") print(res.stderr.strip() or res.stdout.strip()) From dc9239c392fd621f48e2015526730269f94b5b70 Mon Sep 17 00:00:00 2001 From: Sten Tijhuis <102481635+Stensel8@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:09:49 +0200 Subject: [PATCH 04/11] fix: resolve remaining flake8 errors --- static/scripts/saxion-eduroam.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/static/scripts/saxion-eduroam.py b/static/scripts/saxion-eduroam.py index b53dfa8..708aa39 100644 --- a/static/scripts/saxion-eduroam.py +++ b/static/scripts/saxion-eduroam.py @@ -40,11 +40,12 @@ ) class Installer: + def __init__(self, silent: bool = False, username: str = ""): self.silent = silent self.username = username self.gui_tool = self._detect_gui() - + def _detect_gui(self) -> str | None: """Detects available GUI tools (zenity, kdialog, yad).""" if not os.environ.get("DISPLAY") and not os.environ.get("WAYLAND_DISPLAY"): @@ -204,8 +205,10 @@ def install(self): ca_path = self.find_system_ca_bundle() # 1. Remove any existing eduroam connection - subprocess.run(["nmcli", "connection", "delete", CON_NAME], - capture_output=True) + subprocess.run( + ["nmcli", "connection", "delete", CON_NAME], + capture_output=True + ) # 2. Build nmcli command for new connection cmd = [ @@ -260,6 +263,7 @@ def install(self): print(res.stderr.strip() or res.stdout.strip()) def main(): + parser = argparse.ArgumentParser(description="Saxion eduroam Installer") parser.add_argument("-u", "--username", help="Saxion username") parser.add_argument("--silent", action="store_true", help="Run without GUI") From 13d8c08fae30e34ea49f425cc71935f773a9b71b Mon Sep 17 00:00:00 2001 From: Sten Tijhuis <102481635+Stensel8@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:10:19 +0200 Subject: [PATCH 05/11] Update python-checks.yml --- .github/workflows/python-checks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-checks.yml b/.github/workflows/python-checks.yml index 10df25f..3760098 100644 --- a/.github/workflows/python-checks.yml +++ b/.github/workflows/python-checks.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v6.0.2 - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6.2.0 with: python-version: '3.14' From 90d5c60ac73d8175bdd3ad6337b053ff65240647 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:10:57 +0200 Subject: [PATCH 06/11] chore(deps): update actions/github-script action to v9 (main) (#47) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [actions/github-script](https://redirect.github.com/actions/github-script) | action | major | `v8` → `v9` | --- ### Release Notes
actions/github-script (actions/github-script) ### [`v9`](https://redirect.github.com/actions/github-script/compare/v8...v9) [Compare Source](https://redirect.github.com/actions/github-script/compare/v8...v9)
--- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/Stensel8/Zephyrus-Linux). --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/pr-checks.yml | 4 ++-- renovate.json | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 renovate.json diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 907cc01..52cf553 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -78,7 +78,7 @@ jobs: - name: Post PR comment if: steps.check.outputs.found == 'true' - uses: actions/github-script@v8 + uses: actions/github-script@v9 env: FILES: ${{ steps.check.outputs.files }} ACTOR: ${{ github.event.pull_request.user.login }} @@ -203,7 +203,7 @@ jobs: if: always() needs: [pr-title, bilingual, image-format, hugo-build, link-check] steps: - - uses: actions/github-script@v8 + - uses: actions/github-script@v9 env: RESULT_PR_TITLE: ${{ needs.pr-title.result }} RESULT_BILINGUAL: ${{ needs.bilingual.result }} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..5db72dd --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +} From 986d21a3c0d6492f90358dafe2f8a9c8d1fe0eba Mon Sep 17 00:00:00 2001 From: Sten Tijhuis <102481635+Stensel8@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:14:09 +0200 Subject: [PATCH 07/11] chore: add workflow_dispatch trigger to python-checks.yml --- .github/workflows/python-checks.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/python-checks.yml b/.github/workflows/python-checks.yml index 3760098..2d5bcc5 100644 --- a/.github/workflows/python-checks.yml +++ b/.github/workflows/python-checks.yml @@ -7,6 +7,7 @@ on: branches: [ main, development ] schedule: - cron: '0 5 * * 0' + workflow_dispatch: jobs: lint: From 9ca89ee523f76bcd66134d3d6d98f81a4a8ddc04 Mon Sep 17 00:00:00 2001 From: Sten Tijhuis <102481635+Stensel8@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:32:11 +0200 Subject: [PATCH 08/11] chore: update dependency configurations and remove unused workflows --- .github/dependabot.yml | 7 --- .github/renovate.json | 8 +--- .github/workflows/check-dependencies.yml | 43 ------------------- .github/workflows/codeql-analysis.yml | 20 ++++++--- .github/workflows/hugo.yml | 10 ++--- .github/workflows/pr-checks.yml | 20 ++++----- .github/workflows/python-checks.yml | 6 +-- .github/workflows/trivy-scan.yml | 18 ++++++-- .../eduroam-network-installation.md | 4 +- .../eduroam-network-installation.nl.md | 4 +- static/scripts/saxion-eduroam.py | 2 +- 11 files changed, 53 insertions(+), 89 deletions(-) delete mode 100644 .github/workflows/check-dependencies.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 10d83d2..4e73c6b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -27,10 +27,3 @@ updates: interval: "weekly" commit-message: prefix: "chore" - - - package-ecosystem: "docker" - directory: "/" - schedule: - interval: "weekly" - commit-message: - prefix: "chore" diff --git a/.github/renovate.json b/.github/renovate.json index 207a04f..a13105e 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,5 +1,5 @@ { - "extends": ["config:base"], + "extends": ["config:recommended"], "automerge": false, "timezone": "Europe/Amsterdam", "labels": ["dependencies"], @@ -23,12 +23,6 @@ "automerge": true, "automergeType": "minor", "schedule": ["before 3am on Monday"] - }, - { - "managers": ["docker"], - "groupName": "docker images", - "schedule": ["before 3am on Monday"], - "automerge": false } ] } diff --git a/.github/workflows/check-dependencies.yml b/.github/workflows/check-dependencies.yml deleted file mode 100644 index 3400f2e..0000000 --- a/.github/workflows/check-dependencies.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: "Check dependencies" - -on: - schedule: - - cron: '0 4 * * 1' - workflow_dispatch: - -permissions: - contents: read - issues: write - -jobs: - go-updates: - name: Check Go module updates - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Set up Go - uses: actions/setup-go@v6 - with: - go-version: '1.26' - - - name: Install jq - run: sudo apt-get update && sudo apt-get install -y jq - - - name: Check Go module updates - id: go_updates - run: | - go list -m -u -json all | jq -r '. | select(.Update) | "\(.Path): \(.Version) -> \(.Update.Version)"' > deps-updates.txt || true - if [ -s deps-updates.txt ]; then - echo "found=true" >> $GITHUB_OUTPUT - else - echo "found=false" >> $GITHUB_OUTPUT - fi - - - name: Create issue for Go updates - if: steps.go_updates.outputs.found == 'true' - uses: peter-evans/create-issue-from-file@v4 - with: - title: "Dependency updates available: Go modules" - content-filepath: deps-updates.txt - labels: "dependencies,automated" diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2e902a6..881ddc4 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -11,22 +11,30 @@ on: permissions: contents: read security-events: write + actions: read jobs: analyze: - name: Analyze (CodeQL) + name: Analyze (${{ matrix.language }}) runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + language: [ go, python ] + steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: - languages: go + languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/hugo.yml b/.github/workflows/hugo.yml index f2835ce..e3ed008 100644 --- a/.github/workflows/hugo.yml +++ b/.github/workflows/hugo.yml @@ -25,20 +25,20 @@ jobs: HUGO_VERSION: 0.160.0 steps: - name: Install Hugo CLI - uses: peaceiris/actions-hugo@v3.0.0 + uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3.0.0 with: hugo-version: '0.160.1' extended: true - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive fetch-depth: 0 - name: Setup Pages id: pages - uses: actions/configure-pages@v6 + uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6 - name: Build with Hugo env: @@ -52,7 +52,7 @@ jobs: --baseURL "${{ steps.pages.outputs.base_url }}/" - name: Upload artifact - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 with: path: ./public @@ -65,4 +65,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v5 + uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5 diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 52cf553..1db37d3 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -34,7 +34,7 @@ jobs: name: Markdown lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: DavidAnson/markdownlint-cli2-action@ce4853d43830c74c1753b39f3cf40f71c2031eb9 # v23.0.0 with: globs: "content/**/*.md" @@ -44,8 +44,8 @@ jobs: name: Python security (bandit) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.x" - run: pip install bandit @@ -57,7 +57,7 @@ jobs: name: No PNG/JPG in static/images runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Find non-AVIF images id: check @@ -78,7 +78,7 @@ jobs: - name: Post PR comment if: steps.check.outputs.found == 'true' - uses: actions/github-script@v9 + uses: actions/github-script@d746ffe35508b1917358783b479e04febd2b8f71 # v9.0.0 env: FILES: ${{ steps.check.outputs.files }} ACTOR: ${{ github.event.pull_request.user.login }} @@ -128,7 +128,7 @@ jobs: name: EN/NL file parity runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check every .md has a matching .nl.md run: | missing="" @@ -154,7 +154,7 @@ jobs: env: HUGO_VERSION: 0.152.2 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: recursive fetch-depth: 0 @@ -170,7 +170,7 @@ jobs: TZ: Europe/Amsterdam run: hugo --gc --minify --baseURL "http://localhost/" - name: Upload built site - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: hugo-public path: public/ @@ -182,7 +182,7 @@ jobs: runs-on: ubuntu-latest needs: hugo-build steps: - - uses: actions/download-artifact@v8 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: hugo-public path: public/ @@ -203,7 +203,7 @@ jobs: if: always() needs: [pr-title, bilingual, image-format, hugo-build, link-check] steps: - - uses: actions/github-script@v9 + - uses: actions/github-script@d746ffe35508b1917358783b479e04febd2b8f71 # v9.0.0 env: RESULT_PR_TITLE: ${{ needs.pr-title.result }} RESULT_BILINGUAL: ${{ needs.bilingual.result }} diff --git a/.github/workflows/python-checks.yml b/.github/workflows/python-checks.yml index 2d5bcc5..cd60289 100644 --- a/.github/workflows/python-checks.yml +++ b/.github/workflows/python-checks.yml @@ -13,10 +13,10 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Python - uses: actions/setup-python@v6.2.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.14' @@ -29,4 +29,4 @@ jobs: run: flake8 static/scripts/saxion-eduroam.py --max-line-length=120 - name: Security scan with bandit - run: bandit -r static/scripts/saxion-eduroam.py + run: bandit -r static/scripts/saxion-eduroam.py -ll diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index 2d50434..c9ef5f0 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -5,14 +5,26 @@ on: - cron: '0 2 * * 0' workflow_dispatch: +permissions: + contents: read + security-events: write + jobs: trivy-scan: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Run Trivy filesystem scan - uses: aquasecurity/trivy-action@v0.35.0 + uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 with: scan-type: fs severity: CRITICAL,HIGH - format: table + format: sarif + output: trivy-results.sarif + + - name: Upload Trivy results to GitHub Security tab + uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 + if: always() + with: + sarif_file: trivy-results.sarif diff --git a/content/docs/networking/eduroam-network-installation.md b/content/docs/networking/eduroam-network-installation.md index 0e3e418..da6810c 100644 --- a/content/docs/networking/eduroam-network-installation.md +++ b/content/docs/networking/eduroam-network-installation.md @@ -59,13 +59,13 @@ A Python script automates the full `nmcli` connection setup for Saxion: curl -LO https://zephyrus-linux.stensel.nl/scripts/saxion-eduroam.py # 2. Verify checksum -echo "c8d5eb6551807ae5e2b8b1b38e8edd02fc13f9c3c62edf5626f2f8845c916021 saxion-eduroam.py" | sha256sum -c +echo "bb8c45e801fbd37bc7d8c12104ad3c525bc664571598344b90c5da0437631cf8 saxion-eduroam.py" | sha256sum -c # 3. Run python3 saxion-eduroam.py ``` -**SHA256:** `8dd2f2120ddebdfd9d764e04954322307dccb8c855c691de7600f2a8a71db42b` +**SHA256:** `bb8c45e801fbd37bc7d8c12104ad3c525bc664571598344b90c5da0437631cf8` The script removes any existing eduroam profile, prompts for your **username** via a GUI dialog (zenity, kdialog, or yad) or terminal fallback, and activates the connection. Your password is never asked by the script; it is requested by your GNOME Keyring at connection time and stored securely, never in plaintext. diff --git a/content/docs/networking/eduroam-network-installation.nl.md b/content/docs/networking/eduroam-network-installation.nl.md index 62d21c0..fb414a8 100644 --- a/content/docs/networking/eduroam-network-installation.nl.md +++ b/content/docs/networking/eduroam-network-installation.nl.md @@ -59,13 +59,13 @@ Een Python-script automatiseert de volledige `nmcli`-verbindingsconfiguratie voo curl -LO https://zephyrus-linux.stensel.nl/scripts/saxion-eduroam.py # 2. Controleer de checksum -echo "c8d5eb6551807ae5e2b8b1b38e8edd02fc13f9c3c62edf5626f2f8845c916021 saxion-eduroam.py" | sha256sum -c +echo "bb8c45e801fbd37bc7d8c12104ad3c525bc664571598344b90c5da0437631cf8 saxion-eduroam.py" | sha256sum -c # 3. Uitvoeren python3 saxion-eduroam.py ``` -**SHA256:** `8dd2f2120ddebdfd9d764e04954322307dccb8c855c691de7600f2a8a71db42b` +**SHA256:** `bb8c45e801fbd37bc7d8c12104ad3c525bc664571598344b90c5da0437631cf8` Het script verwijdert een eventueel bestaand eduroam-profiel, vraagt je **gebruikersnaam** via een GUI-dialoog (zenity, kdialog of yad) of terminal-fallback, en activeert de verbinding. Je wachtwoord wordt nooit door het script gevraagd; dat wordt bij het verbinden opgevraagd door je GNOME Keyring en veilig opgeslagen, nooit in platte tekst. diff --git a/static/scripts/saxion-eduroam.py b/static/scripts/saxion-eduroam.py index 708aa39..2f2c3ac 100644 --- a/static/scripts/saxion-eduroam.py +++ b/static/scripts/saxion-eduroam.py @@ -95,7 +95,7 @@ def show_message(self, text: str, is_error: bool = False): return if not self.gui_tool: - print(f"\n{text}\n") + print(f"\n{self._sanitize_for_log(text)}\n") return cmd = [] From 924e77e4ff28542b06088cf2421fc3684d68dd1c Mon Sep 17 00:00:00 2001 From: Sten Tijhuis <102481635+Stensel8@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:35:59 +0200 Subject: [PATCH 09/11] fix: update SHA256 checksum for saxion-eduroam.py and clean up code formatting --- .../networking/eduroam-network-installation.md | 4 ++-- .../eduroam-network-installation.nl.md | 4 ++-- static/scripts/saxion-eduroam.py | 18 ++++++++++++------ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/content/docs/networking/eduroam-network-installation.md b/content/docs/networking/eduroam-network-installation.md index da6810c..a80a4aa 100644 --- a/content/docs/networking/eduroam-network-installation.md +++ b/content/docs/networking/eduroam-network-installation.md @@ -59,13 +59,13 @@ A Python script automates the full `nmcli` connection setup for Saxion: curl -LO https://zephyrus-linux.stensel.nl/scripts/saxion-eduroam.py # 2. Verify checksum -echo "bb8c45e801fbd37bc7d8c12104ad3c525bc664571598344b90c5da0437631cf8 saxion-eduroam.py" | sha256sum -c +echo "827f34d1454fa4d5ac3e297bacc65f6a1fd4828c92c94a41142de2a560d7e003 saxion-eduroam.py" | sha256sum -c # 3. Run python3 saxion-eduroam.py ``` -**SHA256:** `bb8c45e801fbd37bc7d8c12104ad3c525bc664571598344b90c5da0437631cf8` +**SHA256:** `827f34d1454fa4d5ac3e297bacc65f6a1fd4828c92c94a41142de2a560d7e003` The script removes any existing eduroam profile, prompts for your **username** via a GUI dialog (zenity, kdialog, or yad) or terminal fallback, and activates the connection. Your password is never asked by the script; it is requested by your GNOME Keyring at connection time and stored securely, never in plaintext. diff --git a/content/docs/networking/eduroam-network-installation.nl.md b/content/docs/networking/eduroam-network-installation.nl.md index fb414a8..875e11f 100644 --- a/content/docs/networking/eduroam-network-installation.nl.md +++ b/content/docs/networking/eduroam-network-installation.nl.md @@ -59,13 +59,13 @@ Een Python-script automatiseert de volledige `nmcli`-verbindingsconfiguratie voo curl -LO https://zephyrus-linux.stensel.nl/scripts/saxion-eduroam.py # 2. Controleer de checksum -echo "bb8c45e801fbd37bc7d8c12104ad3c525bc664571598344b90c5da0437631cf8 saxion-eduroam.py" | sha256sum -c +echo "827f34d1454fa4d5ac3e297bacc65f6a1fd4828c92c94a41142de2a560d7e003 saxion-eduroam.py" | sha256sum -c # 3. Uitvoeren python3 saxion-eduroam.py ``` -**SHA256:** `bb8c45e801fbd37bc7d8c12104ad3c525bc664571598344b90c5da0437631cf8` +**SHA256:** `827f34d1454fa4d5ac3e297bacc65f6a1fd4828c92c94a41142de2a560d7e003` Het script verwijdert een eventueel bestaand eduroam-profiel, vraagt je **gebruikersnaam** via een GUI-dialoog (zenity, kdialog of yad) of terminal-fallback, en activeert de verbinding. Je wachtwoord wordt nooit door het script gevraagd; dat wordt bij het verbinden opgevraagd door je GNOME Keyring en veilig opgeslagen, nooit in platte tekst. diff --git a/static/scripts/saxion-eduroam.py b/static/scripts/saxion-eduroam.py index 2f2c3ac..b989672 100644 --- a/static/scripts/saxion-eduroam.py +++ b/static/scripts/saxion-eduroam.py @@ -39,6 +39,7 @@ "Click OK to continue." ) + class Installer: def __init__(self, silent: bool = False, username: str = ""): @@ -50,7 +51,7 @@ def _detect_gui(self) -> str | None: """Detects available GUI tools (zenity, kdialog, yad).""" if not os.environ.get("DISPLAY") and not os.environ.get("WAYLAND_DISPLAY"): return None - + for tool in ["zenity", "kdialog", "yad"]: if shutil.which(tool): return tool @@ -107,7 +108,7 @@ def show_message(self, text: str, is_error: bool = False): cmd = ["kdialog", type_flag, text, f"--title={TITLE}"] elif self.gui_tool == "yad": image = "dialog-error" if is_error else "dialog-information" - cmd = ["yad", f"--image={image}", "--button=OK", "--width=500", + cmd = ["yad", f"--image={image}", "--button=OK", "--width=500", f"--title={TITLE}", f"--text={text}"] subprocess.run(cmd, stderr=subprocess.DEVNULL) @@ -132,7 +133,7 @@ def prompt_input(self, prompt: str, is_password: bool = False) -> str | None: res = subprocess.run(cmd, capture_output=True, text=True) if res.returncode != 0: return None - + val = res.stdout.strip() # Yad sometimes adds a trailing separator if self.gui_tool == "yad" and val.endswith("|"): @@ -188,7 +189,10 @@ def run_nmcli(self, cmd: list[str]) -> bool: # Fatal error: show a static message to the GUI to avoid passing # nmcli output (which may echo user input) into a subprocess argument # (CWE-78 / CodeQL py/command-line-injection). - self.show_message("NetworkManager failed to configure the connection.\nSee terminal output for details.", True) + self.show_message( + "NetworkManager failed to configure the connection.\n" + "See terminal output for details.", True + ) sys.exit(1) return True @@ -201,9 +205,9 @@ def install(self): sys.exit(1) self.get_credentials() - + ca_path = self.find_system_ca_bundle() - + # 1. Remove any existing eduroam connection subprocess.run( ["nmcli", "connection", "delete", CON_NAME], @@ -262,6 +266,7 @@ def install(self): print("[ERROR] Could not activate eduroam connection:") print(res.stderr.strip() or res.stdout.strip()) + def main(): parser = argparse.ArgumentParser(description="Saxion eduroam Installer") @@ -282,5 +287,6 @@ def main(): installer = Installer(args.silent, initial_username) installer.install() + if __name__ == "__main__": main() From def07707d39c1f4fc8bfb4ca0e80f70e32d4ac53 Mon Sep 17 00:00:00 2001 From: Sten Tijhuis <102481635+Stensel8@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:45:01 +0200 Subject: [PATCH 10/11] Update codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 881ddc4..a391c00 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -15,12 +15,8 @@ permissions: jobs: analyze: - name: Analyze (${{ matrix.language }}) + name: Analyze (python) runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - language: [ go, python ] steps: - name: Checkout repository @@ -29,7 +25,7 @@ jobs: - name: Initialize CodeQL uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: - languages: ${{ matrix.language }} + languages: python - name: Autobuild uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 @@ -37,4 +33,4 @@ jobs: - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 with: - category: "/language:${{ matrix.language }}" + category: "/language:python" From 676a3ce75b1f85cb617504ae4c44266f09b96266 Mon Sep 17 00:00:00 2001 From: Sten Tijhuis <102481635+Stensel8@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:53:53 +0200 Subject: [PATCH 11/11] fix: remove username from show_message to resolve CodeQL clear-text logging alerts CodeQL traces self.username (user input) through show_message() down to print() and flags it as clear-text logging of sensitive data. Custom sanitizers like _sanitize_for_log() are not recognised as CodeQL sink guards, so the taint persists regardless. Removing the username from the success message breaks the data flow entirely, resolving all three py/clear-text-logging-sensitive-data alerts without changing the functional behaviour of the script. Co-Authored-By: Claude Sonnet 4.6 --- content/docs/networking/eduroam-network-installation.md | 4 ++-- content/docs/networking/eduroam-network-installation.nl.md | 4 ++-- static/scripts/saxion-eduroam.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/content/docs/networking/eduroam-network-installation.md b/content/docs/networking/eduroam-network-installation.md index a80a4aa..401200a 100644 --- a/content/docs/networking/eduroam-network-installation.md +++ b/content/docs/networking/eduroam-network-installation.md @@ -59,13 +59,13 @@ A Python script automates the full `nmcli` connection setup for Saxion: curl -LO https://zephyrus-linux.stensel.nl/scripts/saxion-eduroam.py # 2. Verify checksum -echo "827f34d1454fa4d5ac3e297bacc65f6a1fd4828c92c94a41142de2a560d7e003 saxion-eduroam.py" | sha256sum -c +echo "f16ee75885b02fc773d96b28d7749ec48ee7e330814ccf58189c5da44db7eece saxion-eduroam.py" | sha256sum -c # 3. Run python3 saxion-eduroam.py ``` -**SHA256:** `827f34d1454fa4d5ac3e297bacc65f6a1fd4828c92c94a41142de2a560d7e003` +**SHA256:** `f16ee75885b02fc773d96b28d7749ec48ee7e330814ccf58189c5da44db7eece` The script removes any existing eduroam profile, prompts for your **username** via a GUI dialog (zenity, kdialog, or yad) or terminal fallback, and activates the connection. Your password is never asked by the script; it is requested by your GNOME Keyring at connection time and stored securely, never in plaintext. diff --git a/content/docs/networking/eduroam-network-installation.nl.md b/content/docs/networking/eduroam-network-installation.nl.md index 875e11f..7f82359 100644 --- a/content/docs/networking/eduroam-network-installation.nl.md +++ b/content/docs/networking/eduroam-network-installation.nl.md @@ -59,13 +59,13 @@ Een Python-script automatiseert de volledige `nmcli`-verbindingsconfiguratie voo curl -LO https://zephyrus-linux.stensel.nl/scripts/saxion-eduroam.py # 2. Controleer de checksum -echo "827f34d1454fa4d5ac3e297bacc65f6a1fd4828c92c94a41142de2a560d7e003 saxion-eduroam.py" | sha256sum -c +echo "f16ee75885b02fc773d96b28d7749ec48ee7e330814ccf58189c5da44db7eece saxion-eduroam.py" | sha256sum -c # 3. Uitvoeren python3 saxion-eduroam.py ``` -**SHA256:** `827f34d1454fa4d5ac3e297bacc65f6a1fd4828c92c94a41142de2a560d7e003` +**SHA256:** `f16ee75885b02fc773d96b28d7749ec48ee7e330814ccf58189c5da44db7eece` Het script verwijdert een eventueel bestaand eduroam-profiel, vraagt je **gebruikersnaam** via een GUI-dialoog (zenity, kdialog of yad) of terminal-fallback, en activeert de verbinding. Je wachtwoord wordt nooit door het script gevraagd; dat wordt bij het verbinden opgevraagd door je GNOME Keyring en veilig opgeslagen, nooit in platte tekst. diff --git a/static/scripts/saxion-eduroam.py b/static/scripts/saxion-eduroam.py index b989672..2b3f4a0 100644 --- a/static/scripts/saxion-eduroam.py +++ b/static/scripts/saxion-eduroam.py @@ -245,7 +245,7 @@ def install(self): # Show explanation before attempting connection so the password prompt makes sense self.show_message( - f"Username '{self.username}' added.\n\n" + "eduroam profile created successfully.\n\n" "Your password will now be requested by your desktop keyring (e.g. GNOME Keyring).\n" "This is normal and ensures your password is stored securely encrypted, never in plaintext.\n\n" "If you do not see a password prompt, open your network settings and connect to eduroam manually."