Support private npm packages that contain `/` in the name
While working with PackageURL.from_string, I came across a purl string that looked like this: pkg:npm/@stencil/core/[email protected].
PackageURL.from_string("pkg:npm/@stencil/core/[email protected]") failed with the following error:
Traceback (most recent call last):
File "/repos/sw-factory/hoppr/hoppr/./test.py", line 7, in <module>
purl = PackageURL.from_string(unquote("pkg:npm/@stencil/core/[email protected]"))
File "/repos/sw-factory/hoppr/hoppr/venv/lib/python3.10/site-packages/packageurl/__init__.py", line 538, in from_string
raise ValueError(f"purl is missing the required name component: {repr(purl)}")
ValueError: purl is missing the required name component: 'pkg:npm/@stencil/core/[email protected]'
I know that the purl should be pkg:npm/%40stencil/core%[email protected] according to the spec. But, I also noticed that PackageURL.from_string already has special logic to handle npm purl types whose namespace begins with @. But, the special logic breaks down in the case where the name field contains a /.
In the if block there are two cases where name could end up being empty. The first is if remainder is an empty string after the version logic. The second is if namespace is set and len(ns_name_parts) is greater than 1. I would expect the former to be an actual error while the latter is a weird corner case when dealing with invalid purls like pkg:npm/@stencil/core/[email protected].
So, I was hoping to fix the weird corner case by treating ns_name as the name field if namespace has already been set via the if type == "npm" and path.startswith("@") step.
name = ""
if not namespace and len(ns_name_parts) > 1:
name = ns_name_parts[-1]
ns = ns_name_parts[0:-1]
namespace = "/".join(ns)
# If a namespace has already been set then the remainder is the name
elif namespace:
name = ns_name
elif len(ns_name_parts) == 1:
name = ns_name_parts[0]