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
7 changes: 5 additions & 2 deletions MethodenAnalyser3.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import argparse
import ast
import builtins
import codecs
import collections
import datetime
import difflib
Expand Down Expand Up @@ -1092,7 +1093,7 @@ def analyze_file(path: str) -> AnalysisResult:

# Datei lesen
try:
with open(path, "r", encoding="utf-8") as f:
with open(path, "r", encoding="utf-8-sig") as f:
code = f.read()
except UnicodeDecodeError:
try:
Expand Down Expand Up @@ -1611,8 +1612,10 @@ def auto_fix_unused_imports(output_widget: scrolledtext.ScrolledText, status_wid
# Datei lesen mit Encoding-Fallback — erkanntes Encoding für Schreibzugriff merken
detected_encoding = "utf-8"
original_bytes = pathlib.Path(_last_analysis_path).read_bytes()
if original_bytes.startswith(codecs.BOM_UTF8):
detected_encoding = "utf-8-sig"
try:
original_text = original_bytes.decode("utf-8")
original_text = original_bytes.decode(detected_encoding)
except UnicodeDecodeError:
detected_encoding = "latin-1"
original_text = original_bytes.decode("latin-1")
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<p align="center">
<a href="https://github.com/dev-bricks/MethodenAnalyser/releases/tag/v3.0.2"><img src="https://img.shields.io/badge/Version-3.0.2-blue?style=for-the-badge" alt="Version 3.0.2"></a>
<a href="https://github.com/dev-bricks/MethodenAnalyser/actions/workflows/tests.yml"><img src="https://img.shields.io/badge/CI-Passing-success?style=for-the-badge&logo=githubactions&logoColor=white" alt="CI Status"></a>
<a href="#development--testing"><img src="https://img.shields.io/badge/Tests-203%20Passed-success?style=for-the-badge&logo=pytest&logoColor=white" alt="Tests 100% Green"></a>
<a href="#development--testing"><img src="https://img.shields.io/badge/Tests-210%20Passed-success?style=for-the-badge&logo=pytest&logoColor=white" alt="Tests 100% Green"></a>
<img src="https://img.shields.io/badge/Python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-yellow?style=for-the-badge&logo=python&logoColor=white" alt="Python 3.10-3.13">
<img src="https://img.shields.io/badge/Platform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey?style=for-the-badge" alt="Cross-Platform">
<img src="https://img.shields.io/badge/GUI-Tkinter%20%2B%20CLI%20%2B%20PWA-orange?style=for-the-badge" alt="GUI Desktop + CLI + PWA">
Expand Down Expand Up @@ -173,7 +173,7 @@ MethodenAnalyser enforces 10 strict architectural, runtime, and security invaria
| **INV-ENCODING-04** | Lossless Multi-Encoding Resilience | Default UTF-8 reading with automatic Latin-1 fallback. Never corrupts original character encodings, UTF-8 BOMs, or German umlauts. |
| **INV-ISOLATION-05** | Loopback Web Companion Isolation | Local helper companion binds strictly to `127.0.0.1` by default. Deliberate `--host 0.0.0.0` LAN test mode requires explicit user configuration. |
| **INV-TRAVERSAL-06** | Anti-Path-Traversal & ZIP Safety | Web companion rejects path traversal (`..`), bounds member counts, and enforces maximum archive byte limits to prevent ZIP bombs. |
| **INV-AUTOFIX-07** | Reversible Auto-Fix with Backup | Auto-Fix requires explicit confirmation, rechecks current source usage and publishes a byte-exact backup without replacing existing copies (`.bak`, `.bak.1`, …), then atomically replaces the source from a flushed temporary file. Detected source changes abort the write; encoding, newline style and file mode are retained. |
| **INV-AUTOFIX-07** | Reversible Auto-Fix with Backup | Auto-Fix requires explicit confirmation, rechecks current source usage and publishes a byte-exact backup without replacing existing copies (`.bak`, `.bak.1`, …), then atomically replaces the source from a flushed temporary file. Detected source changes abort the write; encoding (including a UTF-8 BOM), newline style and file mode are retained. |
| **INV-PLATFORM-08** | Multi-Platform Parity | 100% standard library core verified across Windows Server 2025, Ubuntu Linux, and macOS 26. |
| **INV-SYNC-09** | Cloud-Sync & Multi-Agent Lock Safety | Fails closed on lock detection (`LOCK.*`), ignores cloud sync conflict files (`*-conflict-*`), preventing concurrency races. |
| **INV-SLA-10** | 48h Security SLA & 5-Day Triage | Strict vulnerability commitment: 48-hour initial response SLA and 5-business-day triage commitment with coordinated disclosure. |
Expand Down
4 changes: 2 additions & 2 deletions README_de.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<p align="center">
<a href="https://github.com/dev-bricks/MethodenAnalyser/releases/tag/v3.0.2"><img src="https://img.shields.io/badge/Version-3.0.2-blue?style=for-the-badge" alt="Version 3.0.2"></a>
<a href="https://github.com/dev-bricks/MethodenAnalyser/actions/workflows/tests.yml"><img src="https://img.shields.io/badge/CI-Bestanden-success?style=for-the-badge&logo=githubactions&logoColor=white" alt="CI-Status"></a>
<a href="#entwicklung--tests"><img src="https://img.shields.io/badge/Tests-203%20Bestanden-success?style=for-the-badge&logo=pytest&logoColor=white" alt="Tests 100% Grün"></a>
<a href="#entwicklung--tests"><img src="https://img.shields.io/badge/Tests-210%20Bestanden-success?style=for-the-badge&logo=pytest&logoColor=white" alt="Tests 100% Grün"></a>
<img src="https://img.shields.io/badge/Python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-yellow?style=for-the-badge&logo=python&logoColor=white" alt="Python 3.10-3.13">
<img src="https://img.shields.io/badge/Plattform-Windows%20%7C%20Linux%20%7C%20macOS-lightgrey?style=for-the-badge" alt="Plattformübergreifend">
<img src="https://img.shields.io/badge/GUI-Tkinter%20%2B%20CLI%20%2B%20PWA-orange?style=for-the-badge" alt="GUI Desktop + CLI + PWA">
Expand Down Expand Up @@ -173,7 +173,7 @@ MethodenAnalyser garantiert 10 verbindliche Architektur-, Laufzeit- und Sicherhe
| **INV-ENCODING-04** | Verlustfreie Multi-Encoding-Resilienz | Liest Dateien standardmäßig als UTF-8 mit automatischem Latin-1 Fallback. Verändert niemals Original-Encodings oder deutsche Umlaute. |
| **INV-ISOLATION-05** | Loopback Web-Begleiter-Isolation | Der lokale Hilfsserver bindet sich standardmäßig ausschließlich an `127.0.0.1`. Der LAN-Modus `--host 0.0.0.0` erfordert explizite Aktivierung. |
| **INV-TRAVERSAL-06** | Pfad-Traversal- & ZIP-Sicherheit | Der Web-Begleiter blockiert Directory-Traversal (`..`), begrenzt Dateizahlen und unkomprimierte Byte-Mengen zur Abwehr von ZIP-Bomben. |
| **INV-AUTOFIX-07** | Reversibler Auto-Fix mit Backup | Auto-Fix erfordert eine explizite Bestätigung, prüft die aktuelle Verwendung erneut und veröffentlicht eine bytegenaue Sicherung ohne bestehende Backups zu überschreiben (`.bak`, `.bak.1`, …) und ersetzt die Quelle anschließend atomar aus einer vollständig geschriebenen temporären Datei. Erkannte Quelländerungen brechen den Schreibvorgang ab; Kodierung, Zeilenumbruchstil und Dateimodus bleiben erhalten. |
| **INV-AUTOFIX-07** | Reversibler Auto-Fix mit Backup | Auto-Fix erfordert eine explizite Bestätigung, prüft die aktuelle Verwendung erneut und veröffentlicht eine bytegenaue Sicherung ohne bestehende Backups zu überschreiben (`.bak`, `.bak.1`, …) und ersetzt die Quelle anschließend atomar aus einer vollständig geschriebenen temporären Datei. Erkannte Quelländerungen brechen den Schreibvorgang ab; Kodierung einschließlich UTF-8-BOM, Zeilenumbruchstil und Dateimodus bleiben erhalten. |
| **INV-PLATFORM-08** | Plattformübergreifende Parität | Vollständige Standardbibliothek-Kompatibilität, verifiziert auf Windows Server 2025, Ubuntu Linux und macOS 26. |
| **INV-SYNC-09** | Cloud-Sync- & Lock-Resilienz | Beachtet Dateisperren (`LOCK.*`) im Fail-Closed-Verfahren und ignoriert Cloud-Sync-Konfliktdateien (`*-conflict-*`). |
| **INV-SLA-10** | 48h Sicherheits-SLA & 5-Tage-Triage | Verbindliches Sicherheitsversprechen: Eingangsbestätigung binnen 48 Stunden, Triage und Behebungsplan binnen 5 Werktagen. |
Expand Down
2 changes: 1 addition & 1 deletion llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The tool strictly adheres to 10 architectural and security invariants:
- INV-ENCODING-04: Lossless Multi-Encoding Resilience (UTF-8 default with Latin-1 fallback)
- INV-ISOLATION-05: Loopback Web Companion Isolation (binds to 127.0.0.1 by default)
- INV-TRAVERSAL-06: Path Traversal & ZIP Safety Bounds (blocks `..` and bounds member sizes)
- INV-AUTOFIX-07: Reversible Auto-Fix with `.bak` Backup; current source is rechecked after confirmation, and only approved imports that remain unused are removed. Import spans preserve neighboring statements; empty suites retain pass, and the result is syntax-checked before writing. Auto-Fix stages and flushes complete byte payloads, exclusively publishes numbered backups via same-directory hard links and atomically replaces the source. Detected source changes abort; original bytes remain in the backup. Unsupported backup publication fails before source replacement.
- INV-AUTOFIX-07: Reversible Auto-Fix with `.bak` Backup; current source is rechecked after confirmation, and only approved imports that remain unused are removed. Import spans preserve neighboring statements; empty suites retain pass, and the result is syntax-checked before writing. Auto-Fix stages and flushes complete byte payloads, exclusively publishes numbered backups via same-directory hard links and atomically replaces the source. Detected source changes abort; original bytes remain in the backup. Unsupported backup publication fails before source replacement. File analysis accepts a leading UTF-8 BOM; Auto-Fix preserves the signature and byte-exact recovery copy.
- INV-PLATFORM-08: Cross-Platform Parity across Windows, Linux, and macOS
- INV-SYNC-09: Cloud-Sync & Multi-Agent Lock Resilience (fail-closed against `LOCK.*`)
- INV-SLA-10: 48h Initial Response & 5-Business-Day Triage SLA
Expand Down
57 changes: 57 additions & 0 deletions tests/test_utf8_bom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""UTF-8 BOM is a valid Python signature and must survive import cleanup."""
import codecs
import json
import subprocess
import sys
import warnings
from pathlib import Path
from unittest.mock import Mock

import pytest

import MethodenAnalyser3 as m


@pytest.mark.parametrize("newline", ["\n", "\r\n", "\r"])
def test_analysis_accepts_utf8_bom_without_fallback(tmp_path, newline):
path = tmp_path / "signed.py"
source = newline.join(["import os", "label = 'Grüße'", "print(label)", ""])
path.write_bytes(source.encode("utf-8-sig"))
with warnings.catch_warnings(record=True) as caught:
result = m.analyze_file(str(path))
assert not caught
assert result.unused_imports == ["os"]


@pytest.mark.parametrize("source,expected", [
("import os\r\nanswer = 42\r\n", "answer = 42\r\n"),
("label = 'Grüße'; import os; answer = 42\n", "label = 'Grüße'; pass; answer = 42\n"),
("import os\n", ""),
])
def test_auto_fix_preserves_bom_and_exact_backup(monkeypatch, tmp_path, source, expected):
path = tmp_path / "signed.py"
raw = source.encode("utf-8-sig")
path.write_bytes(raw)
monkeypatch.setattr(m, "_last_analysis_path", str(path))
monkeypatch.setattr(m, "_last_analysis_result", m.analyze_source(source))
dialogs = Mock()
dialogs.askyesno.return_value = True
monkeypatch.setattr(m, "messagebox", dialogs)
m.auto_fix_unused_imports(Mock())
dialogs.showerror.assert_not_called()
assert path.read_bytes() == expected.encode("utf-8-sig")
assert path.read_bytes().startswith(codecs.BOM_UTF8)
assert path.with_suffix(".py.bak").read_bytes() == raw
assert not list(tmp_path.glob(".*.autofix-*"))


def test_cli_reports_bom_file_findings(tmp_path):
path = tmp_path / "signed.py"
path.write_bytes("import os\nprint('Grüße')\n".encode("utf-8-sig"))
report = tmp_path / "report.json"
script = Path(m.__file__).resolve()
result = subprocess.run([sys.executable, str(script), "--lang", "de", "--file", str(path),
"--json-output", str(report)], capture_output=True, check=False)
assert result.returncode == m.EXIT_FINDINGS, result.stderr
payload = json.loads(report.read_text(encoding="utf-8"))
assert payload["unused_imports"] == {"signed.py": ["os"]}
Loading