diff --git a/src/skillspector/nodes/build_context.py b/src/skillspector/nodes/build_context.py index fa8ae93c..6932159f 100644 --- a/src/skillspector/nodes/build_context.py +++ b/src/skillspector/nodes/build_context.py @@ -1455,6 +1455,9 @@ def _string_list(value: object) -> list[str]: raise _ManifestSchemaError(type(description).__name__) _consume(description) manifest["description"] = description + version = data.get("version") + if version is not None: + manifest["version"] = _scalar_text(version) manifest["triggers"] = _string_list(data.get("triggers", [])) manifest["permissions"] = _string_list(data.get("permissions", [])) @@ -1515,8 +1518,9 @@ def _parse_manifest( ) -> dict[str, object]: """Parse SKILL.md or skill.md YAML frontmatter into a manifest dict. - Returns dict with name, description, triggers (list), permissions (list), - allowed-tools (list), parameters (list). Returns {} if no file or parse fails. + Returns dict with name, description, version, triggers (list), permissions + (list), allowed-tools (list), parameters (list). Returns {} if no file or + parse fails. Parsing is restricted to a bounded byte prefix, including for direct helper callers that do not provide the bundle's already-bounded raw cache. """ diff --git a/tests/test_mcp_rug_pull.py b/tests/test_mcp_rug_pull.py index c3173264..0e5627a2 100644 --- a/tests/test_mcp_rug_pull.py +++ b/tests/test_mcp_rug_pull.py @@ -18,6 +18,7 @@ from __future__ import annotations from skillspector.nodes.analyzers.mcp_rug_pull import node +from skillspector.nodes.build_context import build_context from skillspector.state import SkillspectorState @@ -135,6 +136,20 @@ def test_rp3_version_wildcard(): assert len(rp3) >= 1 +def test_rp3_version_wildcard_from_skill_frontmatter(tmp_path): + """RP3 receives the version projected from real skill frontmatter.""" + (tmp_path / "SKILL.md").write_text( + '---\nname: test-skill\ndescription: For tests\nversion: "*"\n---\n', + encoding="utf-8", + ) + + result = node(build_context({"skill_path": str(tmp_path)})) + + rp3 = [finding for finding in result["findings"] if finding.rule_id == "RP3"] + assert len(rp3) == 1 + assert rp3[0].matched_text == "*" + + def test_rp3_version_ok_no_finding(): """RP3 does not fire on pinned version.""" result = node(