Option C oc_diagnostics
Loading...
Searching...
No Matches
test_env_containment.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# tests/test_env_containment.py
4
5"""
6Purpose
7-------
8OI-515 — verify ``OC_DIAGNOSTICS_*`` env-var containment rules.
9
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.
13
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.
17
18Diagnostics
19-----------
20Domain: TEST
21Levels:
22 L2 — test lifecycle
23
24Contracts
25---------
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.
29"""
30
31from __future__ import annotations
32
33import os
34import sys
35import tempfile
36import warnings
37from pathlib import Path
38
39_REPO_ROOT = Path(__file__).resolve().parents[1]
40if str(_REPO_ROOT) not in sys.path:
41 sys.path.insert(0, str(_REPO_ROOT))
42
43from oc_diagnostics import unified_debug as _ud # noqa: E402
44
45
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"
50
51
53 """OI-515: NUL bytes are refused and the env var is treated as unset.
54
55 ``os.environ`` itself refuses to store ``\\x00`` on most platforms, so
56 we patch ``os.environ.get`` directly to simulate the hostile input.
57 """
58 def _fake_get(name, default=""):
59 return "/tmp/evil\x00payload" if name == "OC_DIAGNOSTICS_CONFIG" else default
60
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
66
67
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
75
76
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") == ""
83
84
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"
89 # Write 10 MiB + 1 byte of junk.
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
97 # Default fallback exposes the minimal "GENERAL" domain only.
98 assert "GENERAL" in levels
99
100
101def test_normal_sized_config_still_loads(monkeypatch, tmp_path):
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)