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 <devel@cybermind.fr>
This commit is contained in:
CyberMind-FR 2026-07-18 07:41:00 +02:00
parent 8becec8480
commit f16c23e043
2 changed files with 19 additions and 0 deletions

View File

@ -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:

View File

@ -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"