Skip to content

Commit f947d98

Browse files
Fix sourcePath leak of absolute filesystem path in frontmatter parser
Assisted-by: GitHub Copilot (model: claude-sonnet-5, autonomous) Co-authored-by: nicolehaugen <10600161+nicolehaugen@users.noreply.github.com>
1 parent 35565b8 commit f947d98

2 files changed

Lines changed: 29 additions & 12 deletions

File tree

‎src/specify_cli/core/__init__.py‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ def _package_relative(path: str) -> str:
154154
return path
155155

156156

157-
def _read_leading_frontmatter(md_path: Path, *, kind: str, name: str) -> dict[str, Any]:
157+
def _read_leading_frontmatter(
158+
md_path: Path, *, kind: str, name: str, source_path: str
159+
) -> dict[str, Any]:
158160
"""Parse the ``---``-fenced YAML block at the head of ``md_path``.
159161
160162
Returns ``{}`` if the file has no frontmatter block. Raises
@@ -166,10 +168,10 @@ def _read_leading_frontmatter(md_path: Path, *, kind: str, name: str) -> dict[st
166168
except OSError as exc:
167169
raise CoreInventoryError(
168170
error="core_inventory.read_failed",
169-
message=f"Could not read {kind} file: {exc}",
171+
message=f"Could not read {kind} file: {exc.strerror or exc}",
170172
kind=kind,
171173
name=name,
172-
source_path=str(md_path),
174+
source_path=source_path,
173175
) from exc
174176

175177
if not text.startswith("---"):
@@ -190,7 +192,7 @@ def _read_leading_frontmatter(md_path: Path, *, kind: str, name: str) -> dict[st
190192
),
191193
kind=kind,
192194
name=name,
193-
source_path=str(md_path),
195+
source_path=source_path,
194196
)
195197

196198
import yaml # lazy import per constitution IV
@@ -206,7 +208,7 @@ def _read_leading_frontmatter(md_path: Path, *, kind: str, name: str) -> dict[st
206208
),
207209
kind=kind,
208210
name=name,
209-
source_path=str(md_path),
211+
source_path=source_path,
210212
) from exc
211213

212214
if parsed is None:
@@ -220,7 +222,7 @@ def _read_leading_frontmatter(md_path: Path, *, kind: str, name: str) -> dict[st
220222
),
221223
kind=kind,
222224
name=name,
223-
source_path=str(md_path),
225+
source_path=source_path,
224226
)
225227
return parsed
226228

@@ -293,7 +295,9 @@ def _build_command_entry(name: str, layout: _CoreLayout) -> dict[str, Any]:
293295
name=name,
294296
source_path=source_path,
295297
)
296-
frontmatter = _read_leading_frontmatter(file_path, kind="command", name=name)
298+
frontmatter = _read_leading_frontmatter(
299+
file_path, kind="command", name=name, source_path=source_path
300+
)
297301
description = _normalize_description(
298302
frontmatter.get("description"),
299303
kind="command",
@@ -345,7 +349,9 @@ def _extract_template_description(md_path: Path, *, name: str, source_path: str)
345349
``# `` H1 heading. Raises :class:`CoreInventoryError` if neither yields
346350
a non-empty string.
347351
"""
348-
frontmatter = _read_leading_frontmatter(md_path, kind="template", name=name)
352+
frontmatter = _read_leading_frontmatter(
353+
md_path, kind="template", name=name, source_path=source_path
354+
)
349355
fm_desc = frontmatter.get("description")
350356
if isinstance(fm_desc, str):
351357
candidate = fm_desc.strip()

‎tests/test_core_inventory.py‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,30 +136,41 @@ def test_package_relative_rejects_absolute() -> None:
136136
def test_parse_command_frontmatter_success(tmp_path: Path) -> None:
137137
md = tmp_path / "cmd.md"
138138
md.write_text("---\ndescription: hi\nartifact: foo\n---\nbody\n", encoding="utf-8")
139-
parsed = core_pkg._read_leading_frontmatter(md, kind="command", name="cmd")
139+
parsed = core_pkg._read_leading_frontmatter(
140+
md, kind="command", name="cmd", source_path="commands/cmd.md"
141+
)
140142
assert parsed == {"description": "hi", "artifact": "foo"}
141143

142144

143145
def test_parse_command_frontmatter_raises_on_unclosed_fence(tmp_path: Path) -> None:
144146
md = tmp_path / "cmd.md"
145147
md.write_text("---\ndescription: hi\n\nbody with no close\n", encoding="utf-8")
146148
with pytest.raises(CoreInventoryError) as excinfo:
147-
core_pkg._read_leading_frontmatter(md, kind="command", name="cmd")
149+
core_pkg._read_leading_frontmatter(
150+
md, kind="command", name="cmd", source_path="commands/cmd.md"
151+
)
148152
assert excinfo.value.error == "core_inventory.frontmatter_parse"
149153
assert excinfo.value.kind == "command"
150154

151155

152156
def test_parse_command_frontmatter_returns_empty_when_absent(tmp_path: Path) -> None:
153157
md = tmp_path / "cmd.md"
154158
md.write_text("# just markdown, no frontmatter\n", encoding="utf-8")
155-
assert core_pkg._read_leading_frontmatter(md, kind="command", name="cmd") == {}
159+
assert (
160+
core_pkg._read_leading_frontmatter(
161+
md, kind="command", name="cmd", source_path="commands/cmd.md"
162+
)
163+
== {}
164+
)
156165

157166

158167
def test_parse_command_frontmatter_rejects_non_mapping(tmp_path: Path) -> None:
159168
md = tmp_path / "cmd.md"
160169
md.write_text("---\n- one\n- two\n---\nbody\n", encoding="utf-8")
161170
with pytest.raises(CoreInventoryError):
162-
core_pkg._read_leading_frontmatter(md, kind="command", name="cmd")
171+
core_pkg._read_leading_frontmatter(
172+
md, kind="command", name="cmd", source_path="commands/cmd.md"
173+
)
163174

164175

165176
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)