From f16c23e043a5eb305f795df3b04fd2ddc2350939 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Sat, 18 Jul 2026 07:41:00 +0200 Subject: [PATCH] fix(cve-triage): preserve waf-rules.json mode on wafgen rewrite tempfile.mkstemp() always creates its temp file 0600 regardless of umask; os.replace() would make waf-rules.json inherit that, silently tightening it from its live mode (typically 0644) on every regen and breaking hot-reload if the WAF reader runs as a different user. Co-Authored-By: Gerald KERMA --- packages/secubox-cve-triage/api/wafgen/emit.py | 7 +++++++ packages/secubox-cve-triage/tests/test_emit.py | 12 ++++++++++++ 2 files changed, 19 insertions(+) diff --git a/packages/secubox-cve-triage/api/wafgen/emit.py b/packages/secubox-cve-triage/api/wafgen/emit.py index e6ca7de3..cfee3bf9 100644 --- a/packages/secubox-cve-triage/api/wafgen/emit.py +++ b/packages/secubox-cve-triage/api/wafgen/emit.py @@ -17,6 +17,7 @@ from __future__ import annotations import json import os import re +import stat import tempfile from pathlib import Path @@ -55,6 +56,7 @@ def write_category(rules_path: Path, candidates: list[Candidate], *, now: str) - (CLI) décide quoi en faire ; on ne crée pas un fichier de règles vide.""" rules_path = Path(rules_path) data = json.loads(rules_path.read_text(encoding="utf-8")) + orig_mode = os.stat(rules_path).st_mode cats = data.setdefault("categories", {}) cats[CATEGORY_KEY] = build_category(candidates, now=now) @@ -67,6 +69,11 @@ def write_category(rules_path: Path, candidates: list[Candidate], *, now: str) - try: with os.fdopen(fd, "w", encoding="utf-8") as f: f.write(json.dumps(data, indent=2, ensure_ascii=False) + "\n") + # tempfile.mkstemp() always creates 0600 regardless of umask; os.replace() + # would make waf-rules.json inherit that, silently tightening it from its + # original mode (typically 0644) and breaking the WAF reader if it runs + # as a different user. Restore the original bits before the swap. + os.chmod(tmp_name, stat.S_IMODE(orig_mode)) os.replace(tmp_name, rules_path) except BaseException: try: diff --git a/packages/secubox-cve-triage/tests/test_emit.py b/packages/secubox-cve-triage/tests/test_emit.py index 344e512f..825d7993 100644 --- a/packages/secubox-cve-triage/tests/test_emit.py +++ b/packages/secubox-cve-triage/tests/test_emit.py @@ -4,6 +4,7 @@ # See LICENCE-CMSD-1.0.md for terms. import json import os +import stat import pytest @@ -121,6 +122,17 @@ def test_write_category_raises_on_missing_rules_file(tmp_path): write_category(missing, [cand()], now="2026-07-18T00:00:00Z") +def test_write_preserves_original_file_permission_mode(tmp_path): + # tempfile.mkstemp() always creates its file 0600 regardless of umask; + # os.replace() would make waf-rules.json inherit that, silently + # tightening it from its live mode (typically 0644) and breaking the + # WAF reader if it runs as a different user (permission-drift outage). + p = _rules_file(tmp_path) + os.chmod(p, 0o644) + write_category(p, [cand()], now="2026-07-18T00:00:00Z") + assert stat.S_IMODE(os.stat(p).st_mode) == 0o644 + + def test_write_category_raises_on_corrupt_rules_file_and_leaves_it_unchanged(tmp_path): p = tmp_path / "waf-rules.json" corrupt = "{ not valid json"