{"record":{"id":"a74d175a39a4bfde","repo":"github/spec-kit","slug":"invalid-alias-alias-r-for-command-cmd-name","errorCode":null,"errorMessage":"Invalid alias {alias!r} for command '{cmd['name']}': {alias_reason}","messagePattern":"Invalid alias (.+?) for command '(.+?)': (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":508,"sourceCode":"            # Validate alias types; no pattern enforcement on aliases — they are\n            # intentionally free-form to preserve community extension compatibility\n            # (e.g. 'speckit.verify' short aliases used by existing extensions).\n            aliases = cmd.get(\"aliases\")\n            if aliases is None:\n                cmd[\"aliases\"] = []\n                aliases = []\n            if not isinstance(aliases, list):\n                raise ValidationError(\n                    f\"Aliases for command '{cmd['name']}' must be a list\"\n                )\n            for alias in aliases:\n                if not isinstance(alias, str):\n                    raise ValidationError(\n                        f\"Aliases for command '{cmd['name']}' must be strings\"\n                    )\n                alias_reason = relative_extension_path_violation(alias)\n                if alias_reason:\n                    raise ValidationError(\n                        f\"Invalid alias {alias!r} for command \"\n                        f\"'{cmd['name']}': {alias_reason}\"\n                    )\n\n        # Rewrite any hook command references that pointed at a renamed command or\n        # an alias-form ref (ext.cmd → speckit.ext.cmd).  Always emit a warning when\n        # the reference is changed so extension authors know to update the manifest.\n        for hook_name, hook_data in self.data.get(\"hooks\", {}).items():\n            for entry in coerce_hook_entries(hook_data):\n                if not isinstance(entry, dict):\n                    raise ValidationError(\n                        f\"Hook '{hook_name}' must be a mapping or list of mappings, \"\n                        f\"got {type(entry).__name__}\"\n                    )\n                command_ref = entry.get(\"command\")\n                if not isinstance(command_ref, str):\n                    continue\n                # Step 1: apply any rename from the auto-correction pass.","sourceCodeStart":490,"sourceCodeEnd":526,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L490-L526","documentation":"An alias string failed the same relative_extension_path_violation() policy used for command files (src/specify_cli/_utils.py:21): aliases are free-form but must remain safe relative output paths. Rejected shapes include absolute/anchored paths (leading '/', drive letters, UNC), '..' traversal segments, backslash separators, leading/trailing whitespace, empty values, and trailing directory slashes, plus platform-reserved components.","triggerScenarios":"\"aliases\": [\"../evil\"], \"aliases\": [\"/etc/passwd\"], \"aliases\": [\"C:\\\\tmp\"], or \"aliases\": [\"build/\"]. Raised in the per-alias loop right after the string-type check.","commonSituations":"Attempting path-traversal via an alias in a third-party extension (security gate); mistakenly treating the alias field as a file path; aliases ending in '/' copied from path constants.","solutions":["Make the alias a plain relative slug with no slashes or '..' segments, e.g. \"build\" or \"myext.build\".","Strip whitespace and drop any leading '/', drive letters, or backslashes.","Audit third-party extension manifests for traversal-shaped aliases before installing them."],"exampleFix":"// before\n\"aliases\": [\"../commands/build\"]\n// after\n\"aliases\": [\"build\"]","handlingStrategy":"validation","validationCode":"from specify_cli._utils import relative_extension_path_violation\n\nfor c in manifest[\"provides\"][\"commands\"]:\n    for a in (c.get(\"aliases\") or []):\n        if isinstance(a, str):\n            reason = relative_extension_path_violation(a)\n            assert reason is None, f\"unsafe alias {a!r}: {reason}\"","typeGuard":"def is_safe_alias(alias: str) -> bool:\n    from specify_cli._utils import relative_extension_path_violation\n    return relative_extension_path_violation(alias) is None","tryCatchPattern":"try:\n    ExtensionManifest.load(path)\nexcept ValidationError as e:\n    if \"Invalid alias\" in str(e):\n        # replace the traversal-shaped alias with a plain slug and retry\n        ...","preventionTips":["Treat aliases as identifiers, not paths — no slashes or '..'.","Audit third-party manifests for traversal-shaped aliases before install.","Reuse the library's own relative_extension_path_violation() in CI lint."],"tags":["extensions","manifest","security","path-traversal","aliases"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}