fix(cron): repair a scalar 'jobs' field instead of returning it from load_jobs
{"jobs": null} (or a string, number or bool) escaped load_jobs unchanged, so every reader crashed the same way the non-object list entries did. Replace it with an empty list and persist the repair. Ported from #123405.
(cherry picked from commit 1f380ec62c2ce65fd1d31656850b611234e3385e)
This commit is contained in:
@@ -1368,6 +1368,12 @@ def load_jobs() -> List[Dict[str, Any]]:
|
||||
", ".join(map(repr, skipped)))
|
||||
jobs = [{**v, "id": v.get("id") or k} for k, v in jobs.items() if isinstance(v, dict)]
|
||||
repair = "id-keyed jobs map flattened to list"
|
||||
elif not isinstance(jobs, list):
|
||||
logger.warning(
|
||||
"Replacing invalid jobs.json 'jobs' field (%s) with an empty list",
|
||||
type(jobs).__name__)
|
||||
jobs = []
|
||||
repair = "invalid jobs field replaced with list"
|
||||
elif isinstance(data, list):
|
||||
jobs = data
|
||||
repair = "bare list wrapped as dict"
|
||||
|
||||
@@ -1749,6 +1749,21 @@ class TestJobsJsonIdKeyedMap:
|
||||
assert json.loads(JOBS_FILE.read_text(encoding="utf-8"))["jobs"] == []
|
||||
assert "sk-leaked-value" not in caplog.text
|
||||
|
||||
@pytest.mark.parametrize("bad_jobs", [None, "not-a-list", 42, True])
|
||||
def test_invalid_jobs_field_is_repaired_to_empty_list(self, tmp_cron_dir, bad_jobs, caplog):
|
||||
"""A dict root with a scalar jobs value must not escape the load boundary."""
|
||||
import json
|
||||
from cron.jobs import JOBS_FILE, ensure_dirs, load_jobs
|
||||
|
||||
ensure_dirs()
|
||||
JOBS_FILE.write_text(json.dumps({"jobs": bad_jobs}), encoding="utf-8")
|
||||
|
||||
with caplog.at_level("WARNING", logger="cron.jobs"):
|
||||
assert load_jobs() == []
|
||||
assert list_jobs(include_disabled=True) == []
|
||||
assert json.loads(JOBS_FILE.read_text(encoding="utf-8"))["jobs"] == []
|
||||
assert type(bad_jobs).__name__ in caplog.text
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user