Skip to content

Commit 72a876c

Browse files
Pigbibicodex
andcommitted
fix: reject unsafe present package config values
Co-Authored-By: Codex <noreply@openai.com>
1 parent 9aa858a commit 72a876c

2 files changed

Lines changed: 66 additions & 6 deletions

File tree

src/quant_strategy_plugins/tqqq_market_regime_control_present.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,14 @@ def _sensitive_key(key: object) -> bool:
213213
return lowered.startswith("ai_audit_") or any(part in lowered for part in SENSITIVE_KEY_PARTS)
214214

215215

216+
def _contains_sensitive_key(value: object) -> bool:
217+
if isinstance(value, Mapping):
218+
return any(_sensitive_key(key) or _contains_sensitive_key(child) for key, child in value.items())
219+
if isinstance(value, (list, tuple)):
220+
return any(_contains_sensitive_key(child) for child in value)
221+
return False
222+
223+
216224
def _select_config(config_path: Path, as_of: str) -> tuple[dict[str, object], Path, Path | None, str | Path]:
217225
try:
218226
document = tomllib.loads(config_path.read_text(encoding="utf-8"))
@@ -233,12 +241,12 @@ def _select_config(config_path: Path, as_of: str) -> tuple[dict[str, object], Pa
233241
selected = candidates[0]
234242
if selected.get("enabled") is not True or selected.get("mode", document.get("default_mode")) != "shadow":
235243
_fail(ERROR_INPUT, 2)
236-
if any(_sensitive_key(key) for key in selected):
244+
if _contains_sensitive_key(selected):
237245
_fail(ERROR_INPUT, 2)
238246
configured_as_of = selected.get("as_of")
239247
if configured_as_of is not None and configured_as_of != as_of:
240248
_fail(ERROR_INPUT, 2)
241-
if "prices" not in selected or "output_dir" not in selected:
249+
if "prices" not in selected or not isinstance(selected.get("output_dir"), str):
242250
_fail(ERROR_INPUT, 2)
243251
prices = _regular_csv(selected["prices"])
244252
external = None if not selected.get("external_context") else _regular_csv(selected["external_context"])

tests/test_tqqq_market_regime_control_present.py

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ def _identity_error(callable_) -> None:
3535
assert raised.value.exit_code == 2
3636

3737

38+
def _input_error(callable_) -> None:
39+
with pytest.raises(PresentError) as raised:
40+
callable_()
41+
assert raised.value.code == "T2B2_PRODUCER_INPUT_INVALID"
42+
assert raised.value.exit_code == 2
43+
44+
3845
def _payload(as_of: str) -> bytes:
3946
value = {
4047
"as_of": as_of, "audit_summary": {}, "arbiter": {}, "canonical_route": "no_action", "component_signals": {},
@@ -47,22 +54,30 @@ def _payload(as_of: str) -> bytes:
4754
return json.dumps(value, separators=(",", ":"), sort_keys=True).encode()
4855

4956

50-
def _capture_checkout(tmp_path: Path, *, inside_checkout: bool = False) -> tuple[Path, Path, str, Path]:
57+
def _capture_checkout(
58+
tmp_path: Path,
59+
*,
60+
inside_checkout: bool = False,
61+
output_dir: object | None = None,
62+
extra_config: str = "",
63+
) -> tuple[Path, Path, str, Path]:
5164
checkout = _checkout(tmp_path)
52-
output_dir = checkout / "data" / "output" / "packages" if inside_checkout else tmp_path / "external" / "packages"
65+
expected_output_dir = checkout / "data" / "output" / "packages" if inside_checkout else tmp_path / "external" / "packages"
66+
configured_output_dir = expected_output_dir if output_dir is None else output_dir
5367
(checkout / "src" / "quant_strategy_plugins").mkdir(parents=True)
5468
(checkout / "src" / "quant_strategy_plugins" / "tqqq_market_regime_control_present.py").write_text("# tracked\n", encoding="utf-8")
5569
prices = checkout / "prices.csv"
5670
prices.write_text("symbol,as_of,close\nQQQ,2026-01-02,1\n", encoding="utf-8")
5771
config = checkout / "config.toml"
5872
config.write_text(
5973
"[[strategy_plugins]]\nstrategy = 'tqqq_growth_income'\nplugin = 'market_regime_control'\nenabled = true\nmode = 'shadow'\n"
60-
f"[strategy_plugins.inputs]\nprices = {str(prices)!r}\n[strategy_plugins.outputs]\noutput_dir = {str(output_dir)!r}\n",
74+
f"[strategy_plugins.inputs]\nprices = {json.dumps(str(prices))}\n[strategy_plugins.outputs]\n"
75+
f"output_dir = {json.dumps(str(configured_output_dir)) if isinstance(configured_output_dir, Path) else json.dumps(configured_output_dir)}\n{extra_config}",
6176
encoding="utf-8",
6277
)
6378
_git("add", ".", cwd=checkout)
6479
_git("-c", "user.email=t@example.invalid", "-c", "user.name=Test", "commit", "-qm", "capture", cwd=checkout)
65-
return checkout, config, _git("rev-parse", "HEAD", cwd=checkout).stdout.strip(), output_dir
80+
return checkout, config, _git("rev-parse", "HEAD", cwd=checkout).stdout.strip(), expected_output_dir
6681

6782

6883
def _fake_producer(counter: list[int]):
@@ -221,3 +236,40 @@ def test_unrelated_dirt_fails_before_output_admission(tmp_path: Path, monkeypatc
221236

222237
assert calls == []
223238
assert not checkout_output.exists()
239+
240+
241+
def test_nested_sensitive_config_key_fails_before_producer_or_package(
242+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
243+
) -> None:
244+
import quant_strategy_plugins.tqqq_market_regime_control_present as subject
245+
246+
sensitive_value = "do-not-package-this"
247+
checkout, config, expected, checkout_output = _capture_checkout(
248+
tmp_path,
249+
extra_config=f"[strategy_plugins.settings.audit]\napi_token = {sensitive_value!r}\n",
250+
)
251+
calls: list[int] = []
252+
monkeypatch.setattr(subject, "run_market_regime_control_plugin", _fake_producer(calls))
253+
254+
_input_error(lambda: _capture(subject, checkout, config, expected, monkeypatch))
255+
256+
captured = capsys.readouterr()
257+
assert calls == []
258+
assert not checkout_output.exists()
259+
assert sensitive_value not in captured.out
260+
assert sensitive_value not in captured.err
261+
262+
263+
def test_non_string_output_dir_fails_as_input_before_side_effects(
264+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
265+
) -> None:
266+
import quant_strategy_plugins.tqqq_market_regime_control_present as subject
267+
268+
checkout, config, expected, checkout_output = _capture_checkout(tmp_path, output_dir=7)
269+
calls: list[int] = []
270+
monkeypatch.setattr(subject, "run_market_regime_control_plugin", _fake_producer(calls))
271+
272+
_input_error(lambda: _capture(subject, checkout, config, expected, monkeypatch))
273+
274+
assert calls == []
275+
assert not checkout_output.exists()

0 commit comments

Comments
 (0)