Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/workflows/bandit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# SPDX-FileNotice: Part of the Telemetry addon.

################################################################################
# #
# © 2026 FreeCAD Project Association #
# #
# This addon is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Lesser General Public License as #
# published by the Free Software Foundation, either version 2.1 #
# of the License, or (at your option) any later version. #
# #
# This addon is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty #
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. #
# See the GNU Lesser General Public License for more details. #
# #
# You should have received a copy of the GNU Lesser General Public #
# License along with this addon. If not, see https://www.gnu.org/licenses #
# #
################################################################################

# Static security analysis of the Telemetry addon with Bandit. Uses the same
# configuration as the FreeCAD addon quality report (FreeCAD/Addon-Reports).

name: Bandit security scan

permissions:
contents: read

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
bandit:
name: Bandit
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install Bandit
run: |
python -m pip install --upgrade pip
pip install bandit

- name: Run Bandit
run: bandit -c bandit.yaml -r .
20 changes: 15 additions & 5 deletions Resources/translations/run_translation_cycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
import os
import shutil
import stat
import subprocess

# Audited: subprocess is only used to run the Qt lupdate/lrelease tools with fixed argument lists
# and no shell (added nosec B404)
import subprocess # nosec B404
import sys
import tempfile
import time
Expand Down Expand Up @@ -85,7 +88,8 @@ def _make_api_req(self, url, extra_headers=None, method="GET", data=None):
data = json.dumps(data).encode("utf-8")

request = Request(url, headers=headers, method=method, data=data)
request_result = urlopen(request)
# Audited: every URL is built on the hardcoded HTTPS CROWDIN_API_URL (added nosec B310)
request_result = urlopen(request) # nosec B310
if request_result.getcode() >= 300:
print(f"Failed to make API request {url}: return code {request_result.getcode()}")
raise Exception("Failed to make API request")
Expand Down Expand Up @@ -135,7 +139,11 @@ def status(self):
def download(self, build_id):
filename = f"{self.project_identifier}.zip"
response = self._make_project_api_req(f"/translations/builds/{build_id}/download")
urlretrieve(response["url"], filename)
download_url = response["url"]
if not download_url.startswith("https://"):
raise Exception(f"Refusing to download from non-HTTPS URL {download_url}")
# Audited: only HTTPS URLs reach this point (added nosec B310)
urlretrieve(download_url, filename) # nosec B310
print("download of " + filename + " complete")

def build(self):
Expand Down Expand Up @@ -185,7 +193,8 @@ def process_single_translation_file(source_path: str, target_path: str):

print("Generating qm file for", basename, "...")
try:
subprocess.run(
# Audited: fixed arguments, no shell; lrelease is expected on PATH (added nosec B603, B607)
subprocess.run( # nosec B603 B607
[
"lrelease",
new_path,
Expand Down Expand Up @@ -348,7 +357,8 @@ def run_and_download_build(crowdin_updater: CrowdinUpdater):
"-ts",
os.path.join(TS_FILE_PATH, CROWDIN_FILE_NAME),
]
result = subprocess.run(
# Audited: fixed arguments, no shell; lupdate is expected on PATH (added nosec B603)
result = subprocess.run( # nosec B603
args,
timeout=30,
check=True,
Expand Down
3 changes: 2 additions & 1 deletion TelemetryPreferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ def _remove_user_data() -> (bool, str):
url = f"https://www.freecad.org/deletetelemetry.php?person_id={uuid}"
req = urllib.request.Request(url, method="DELETE")
try:
with urllib.request.urlopen(req) as response:
# Audited: the URL is a hardcoded HTTPS endpoint (added nosec B310)
with urllib.request.urlopen(req) as response: # nosec B310
if 200 <= response.status < 300:
return True, FreeCAD.Qt.translate(
"Telemetry", "Your user data was successfully removed from the database."
Expand Down
23 changes: 23 additions & 0 deletions bandit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# SPDX-FileNotice: Part of the Telemetry addon.

# Configuration for Bandit static security analysis. Run locally with:
# bandit -c bandit.yaml -r .
#
# The settings mirror the ones used by the FreeCAD addon quality report
# (https://github.com/FreeCAD/Addon-Reports), so a clean run here means a clean
# entry there. Any finding must either be fixed or carry an audited "nosec"
# marker explaining why it is safe.
#
# B101 (assert_used) and B110 (try_except_pass) are informational style checks
# rather than security defects, and the addon report skips them as well.
#
# Bandit treats each exclude entry as a plain substring of the file path, so the
# patterns are anchored with path separators.

exclude_dirs:
- "*/.venv/*"

skips:
- B101
- B110
2 changes: 1 addition & 1 deletion package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
<name>Telemetry</name>
<description>Help improve FreeCAD by sending basic metrics to the development team.</description>
<version>1.0.6</version>
<version>1.0.7</version>
<date>2026-6-2</date>
<maintainer email="telemetry@freecad.org">The FreeCAD project association AISBL</maintainer>
<license file="LICENSE-Code">LGPL-2.1-or-later</license>
Expand Down
Loading