Skip to content

Commit 79c0088

Browse files
committed
Terse fence docstring; warn on and test out-of-folder file= refs
1 parent 2c1f854 commit 79c0088

2 files changed

Lines changed: 22 additions & 5 deletions

File tree

script/sync_esphome_devices.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -440,18 +440,19 @@ def _split_frontmatter(text: str) -> tuple[dict[str, Any] | None, str]:
440440

441441
def _resolve_fenced_yaml(info: str, inline: str, device_dir: Path) -> str | None:
442442
"""
443-
YAML text for a ``yaml`` fence, following a ``file=`` info attribute.
443+
YAML for a ``yaml`` fence; a ``file=`` ref reads the sibling, traversal-guarded.
444444
445-
Pages may keep their config in a sibling file (``yaml file=config.yaml``)
446-
that the rendered site inlines; read it so those devices aren't dropped.
447-
Traversal-guarded like image refs. A ``url=`` ref points off-repo and
448-
falls through to the inline body (empty), so url-backed pages still skip.
445+
``url=`` and absent refs return the inline body; a guarded or missing
446+
``file=`` returns ``None``.
449447
"""
450448
match = _FENCE_FILE_RE.search(info)
451449
if match is None:
452450
return inline
453451
ref = match.group(1).removeprefix("./")
454452
if "/" in ref or "\\" in ref or ".." in ref:
453+
# Same single-folder scope as image refs; warn so the resulting
454+
# drop is diagnosable instead of a silent "no config" skip.
455+
_LOGGER.warning("ignoring out-of-folder file= config ref %r (%s)", ref, device_dir.name)
455456
return None
456457
path = device_dir / ref
457458
try:

tests/test_sync_esphome_devices_config_fence.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,19 @@ def test_rejects_path_traversal(tmp_path: Path) -> None:
5959
(tmp_path.parent / "secret.yaml").write_text("esphome:\n name: leak\n")
6060
body = "```yaml file=../secret.yaml\n```\n"
6161
assert _first_config_yaml(body, tmp_path) is None
62+
63+
64+
def test_rejects_subdirectory_reference(tmp_path: Path) -> None:
65+
sub = tmp_path / "configs"
66+
sub.mkdir()
67+
(sub / "device.yaml").write_text("esphome:\n name: x\nbk72xx:\n board: cb2s\n")
68+
body = "```yaml file=configs/device.yaml\n```\n"
69+
assert _first_config_yaml(body, tmp_path) is None
70+
71+
72+
def test_strips_dot_slash_prefix(tmp_path: Path) -> None:
73+
(tmp_path / "config.yaml").write_text("esphome:\n name: x\nbk72xx:\n board: cb2s\n")
74+
body = "```yaml file=./config.yaml\n```\n"
75+
parsed = _first_config_yaml(body, tmp_path)
76+
assert parsed is not None
77+
assert parsed[0]["bk72xx"]["board"] == "cb2s"

0 commit comments

Comments
 (0)