{"record":{"id":"1c6514b417c8702e","repo":"github/spec-kit","slug":"command-entry-file-must-be-a-relative-path-witho","errorCode":null,"errorMessage":"Command entry 'file' must be a relative path without '..': {cmd_file}","messagePattern":"Command entry 'file' must be a relative path without '\\.\\.': (.+?)","errorType":"validation","errorClass":"IntegrationDescriptorError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/integrations/catalog.py","lineNumber":804,"sourceCode":"                raise IntegrationDescriptorError(\n                    \"Each command entry must be a mapping\"\n                )\n            if \"name\" not in cmd or \"file\" not in cmd:\n                raise IntegrationDescriptorError(\n                    \"Command entry missing 'name' or 'file'\"\n                )\n            cmd_name = cmd[\"name\"]\n            cmd_file = cmd[\"file\"]\n            if not isinstance(cmd_name, str) or not cmd_name.strip():\n                raise IntegrationDescriptorError(\n                    \"Command entry 'name' must be a non-empty string\"\n                )\n            if not isinstance(cmd_file, str) or not cmd_file.strip():\n                raise IntegrationDescriptorError(\n                    \"Command entry 'file' must be a non-empty string\"\n                )\n            if os.path.isabs(cmd_file) or \"..\" in Path(cmd_file).parts or Path(cmd_file).drive or Path(cmd_file).anchor:\n                raise IntegrationDescriptorError(\n                    f\"Command entry 'file' must be a relative path without '..': {cmd_file}\"\n                )\n        for script_entry in scripts:\n            if not isinstance(script_entry, str) or not script_entry.strip():\n                raise IntegrationDescriptorError(\n                    \"Script entry must be a non-empty string\"\n                )\n            if os.path.isabs(script_entry) or \"..\" in Path(script_entry).parts or Path(script_entry).drive or Path(script_entry).anchor:\n                raise IntegrationDescriptorError(\n                    f\"Script entry must be a relative path without '..': {script_entry}\"\n                )\n\n    # -- Property accessors -----------------------------------------------\n\n    @property\n    def id(self) -> str:\n        return self.data[\"integration\"][\"id\"]\n","sourceCodeStart":786,"sourceCodeEnd":822,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/integrations/catalog.py#L786-L822","documentation":"Raised by IntegrationDescriptor._validate() (src/specify_cli/integrations/catalog.py:804) when a provides.commands 'file' path is absolute, contains '..' segments, or has a drive/anchor component. The library only accepts simple relative paths so command files cannot point outside the integration package (path-traversal defense). The offending path is included in the message.","triggerScenarios":"A command entry with 'file: /etc/passwd', 'file: ../shared/plan.md', or 'file: C:\\\\cmds\\\\plan.md'; the combined isabs/parts/drive/anchor check trips and raises with the concrete cmd_file value.","commonSituations":"Using an absolute path to a shared template during local development; referencing a file in a sibling package with '../'; porting a descriptor from Windows with a drive letter; symlink-free escape attempts.","solutions":["Change the 'file' value to a plain relative path inside the integration package (e.g. 'commands/plan.md').","Move the referenced template into the integration's own directory tree instead of pointing elsewhere.","Remove any '..' segments, leading '/', and drive letters; re-validate."],"exampleFix":"# before\n- name: plan\n  file: ../shared/plan.md\n\n# after\n- name: plan\n  file: commands/plan.md","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef is_safe_relative(p: str) -> bool:\n    path = Path(p)\n    return (\n        not path.is_absolute()\n        and \"..\" not in path.parts\n        and not path.drive\n        and not path.anchor\n    )","typeGuard":"def is_safe_command_file(entry: dict) -> bool:\n    f = entry.get(\"file\")\n    return isinstance(f, str) and is_safe_relative(f)","tryCatchPattern":null,"preventionTips":["Keep all command templates inside the integration package directory.","Use plain relative paths without '..' segments.","Never reference files by absolute or Windows drive paths in descriptors."],"tags":["path-traversal","security","yaml","validation","spec-kit"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}