8OI-515 — verify ``OC_DIAGNOSTICS_*`` env-var containment rules.
10``_safe_env_path`` rejects empty, NUL-bearing, and over-length paths so
11an operator-supplied (or attacker-supplied) env var cannot push pathological
12values into the config loader or log directory resolver.
14``_load_debug_config`` additionally refuses to read a config file larger
15than ``_MAX_CONFIG_BYTES`` (10 MiB) and falls back to the in-module
16defaults, so a pathological config can't blow memory at import time.
26- ``_safe_env_path`` must never return a value containing ``\\0``.
27- The config loader must not raise when the file exceeds the cap; it
28 must fall back to defaults and mark ``config_source`` accordingly.
31from __future__
import annotations
37from pathlib
import Path
39_REPO_ROOT = Path(__file__).resolve().parents[1]
40if str(_REPO_ROOT)
not in sys.path:
41 sys.path.insert(0, str(_REPO_ROOT))
43from oc_diagnostics
import unified_debug
as _ud
47 """OI-515: typical paths round-trip unchanged through _safe_env_path."""
48 monkeypatch.setenv(
"OC_DIAGNOSTICS_CONFIG",
"/tmp/debug_config.json")
49 assert _ud._safe_env_path(
"OC_DIAGNOSTICS_CONFIG") ==
"/tmp/debug_config.json"
53 """OI-515: NUL bytes are refused and the env var is treated as unset.
55 ``os.environ`` itself refuses to store ``\\x00`` on most platforms, so
56 we patch ``os.environ.get`` directly to simulate the hostile input.
58 def _fake_get(name, default=""):
59 return "/tmp/evil\x00payload" if name ==
"OC_DIAGNOSTICS_CONFIG" else default
61 monkeypatch.setattr(_ud.os.environ,
"get", _fake_get)
62 with warnings.catch_warnings(record=
True)
as captured:
63 warnings.simplefilter(
"always")
64 assert _ud._safe_env_path(
"OC_DIAGNOSTICS_CONFIG") ==
""
65 assert any(
"NUL" in str(w.message)
for w
in captured), captured
69 """OI-515: values over _MAX_ENV_PATH_LEN chars are refused."""
70 monkeypatch.setenv(
"OC_DIAGNOSTICS_CONFIG",
"x" * (_ud._MAX_ENV_PATH_LEN + 1))
71 with warnings.catch_warnings(record=
True)
as captured:
72 warnings.simplefilter(
"always")
73 assert _ud._safe_env_path(
"OC_DIAGNOSTICS_CONFIG") ==
""
74 assert any(
"exceeds" in str(w.message)
for w
in captured), captured
78 """OI-515: missing or whitespace-only env var returns ``''``."""
79 monkeypatch.delenv(
"OC_DIAGNOSTICS_CONFIG", raising=
False)
80 assert _ud._safe_env_path(
"OC_DIAGNOSTICS_CONFIG") ==
""
81 monkeypatch.setenv(
"OC_DIAGNOSTICS_CONFIG",
" ")
82 assert _ud._safe_env_path(
"OC_DIAGNOSTICS_CONFIG") ==
""
86 """OI-515: a >10 MiB config file is skipped; defaults are returned."""
87 with tempfile.TemporaryDirectory()
as tmp:
88 oversized = Path(tmp) /
"debug_config.json"
90 oversized.write_bytes(b
"0" * (_ud._MAX_CONFIG_BYTES + 1))
91 monkeypatch.setenv(
"OC_DIAGNOSTICS_CONFIG", str(oversized))
92 with warnings.catch_warnings(record=
True)
as captured:
93 warnings.simplefilter(
"always")
94 levels, _colors, _settings, source = _ud._load_debug_config()
95 assert "DEFAULTS" in source, source
96 assert any(
"OI-515" in str(w.message)
for w
in captured), captured
98 assert "GENERAL" in levels
102 """OI-515: a reasonably-sized config still loads normally."""
103 cfg = tmp_path /
"debug_config.json"
104 cfg.write_text(
'{"DEBUG_LEVELS": {"TEST": 2}}', encoding=
"utf-8")
105 monkeypatch.setenv(
"OC_DIAGNOSTICS_CONFIG", str(cfg))
106 levels, _colors, _settings, source = _ud._load_debug_config()
107 assert str(cfg)
in source
108 assert levels.get(
"TEST") == 2
test_safe_env_path_accepts_normal_value(monkeypatch)
test_oversized_config_falls_back_to_defaults(monkeypatch)
test_safe_env_path_empty_returns_empty(monkeypatch)
test_safe_env_path_rejects_over_length(monkeypatch)
test_normal_sized_config_still_loads(monkeypatch, tmp_path)
test_safe_env_path_rejects_nul_byte(monkeypatch)