{"record":{"id":"5032ec375c6a120e","repo":"pypa/pip","slug":"invalid-script-entry-point-name-entry-name-r-th","errorCode":null,"errorMessage":"Invalid script entry point name {entry.name!r}: the script would be installed outside the scripts directory ({scripts_dir}).","messagePattern":"Invalid script entry point name (.+?): the script would be installed outside the scripts directory \\((.+?)\\)\\.","errorType":"exception","errorClass":"InstallationError","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/operations/install/wheel.py","lineNumber":417,"sourceCode":"            \"information.\"\n        )\n\n\ndef _raise_for_invalid_entrypoint(specification: str, scripts_dir: str) -> None:\n    entry = get_export_entry(specification)\n    if entry is None:\n        return\n\n    if entry.suffix is None:\n        raise MissingCallableSuffix(str(entry))\n\n    # distlib joins the entry point name onto the scripts directory, so a name\n    # with path separators or ``..`` components can resolve elsewhere. The script\n    # must resolve to a path strictly inside the scripts directory.\n    dest = os.path.join(scripts_dir, entry.name)\n    resolves_to_scripts_dir = os.path.abspath(dest) == os.path.abspath(scripts_dir)\n    if resolves_to_scripts_dir or not is_within_directory(scripts_dir, dest):\n        raise InstallationError(\n            f\"Invalid script entry point name {entry.name!r}: the script \"\n            f\"would be installed outside the scripts directory ({scripts_dir}).\"\n        )\n\n\nclass PipScriptMaker(ScriptMaker):\n    # Override distlib's default script template with one that\n    # doesn't import `re` module, allowing scripts to load faster.\n    script_template = textwrap.dedent(\"\"\"\\\n        import sys\n        from %(module)s import %(import_name)s\n        if __name__ == '__main__':\n            sys.argv[0] = sys.argv[0].removesuffix('.exe')\n            sys.exit(%(func)s())\n\"\"\")\n\n    def make(\n        self, specification: str, options: dict[str, Any] | None = None","sourceCodeStart":399,"sourceCodeEnd":435,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_internal/operations/install/wheel.py#L399-L435","documentation":"Raised as InstallationError by _raise_for_invalid_entrypoint when a console_scripts entry point name contains path separators or '..' components such that os.path.join(scripts_dir, entry.name) resolves outside the scripts directory (or exactly to it). This is a path-traversal guard preventing a malicious/buggy wheel from writing a script outside the scripts dir.","triggerScenarios":"entry_points.txt defines a name like '../bin/x', '/abs/path', or 'sub/dir/x'; is_within_directory(scripts_dir, dest) is false, so the install is aborted before distlib writes anything.","commonSituations":"Malicious wheel attempting directory escape; malformed build config producing entry-point names with slashes; non-standard packaging tools.","solutions":["Remove path separators and '..' from the console_scripts entry name.","Rebuild the wheel with a plain, single-component entry name.","Do not install wheels from untrusted sources that exhibit this."],"exampleFix":"# before\n[console_scripts]\n../evil = pkg.mod:main\n\n# after\n[console_scripts]\nevil = pkg.mod:main","handlingStrategy":"validation","validationCode":"import os, re\n\ndef safe_entry_name(name: str) -> bool:\n    # must be a single path component, no separators, no traversal\n    return bool(re.fullmatch(r\"[^/\\\\]+\", name)) and not name.startswith(\".\")\n\n# assert safe_entry_name(\"mycli\")\n# assert not safe_entry_name(\"../evil\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use single-component, plain entry-point names for console_scripts.","Reject path separators or '..' in entry names during wheel builds.","Do not install wheels from untrusted sources."],"tags":["security","wheel","path-traversal","entry-points"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}