Repository: trailofbits/pip-audit (python)

Issue:
Bug: assertion tripped when auditing a URL-style requirement without egg marker
I noticed this while root-causing #349.

The following:

```bash
pip-audit -r <(echo 'git+https://github.com/unbit/uwsgi.git@1bb9ad77c6d2d310c2d6d1d9ad62de61f725b824')
```

fails with:

```
Traceback (most recent call last):
  File "/Users/william/devel/pip-audit/env/bin/pip-audit", line 8, in <module>
    sys.exit(audit())
  File "/Users/william/devel/pip-audit/pip_audit/_cli.py", line 432, in audit
    for (spec, vulns) in auditor.audit(source):
  File "/Users/william/devel/pip-audit/pip_audit/_audit.py", line 66, in audit
    for dep, vulns in self._service.query_all(specs):
  File "/Users/william/devel/pip-audit/pip_audit/_service/interface.py", line 150, in query_all
    for spec in specs:
  File "/Users/william/devel/pip-audit/pip_audit/_dependency_source/requirement.py", line 104, in collect
    assert isinstance(req, EditableRequirement)
AssertionError
```

...which is:

```python
for req in rf.requirements:
    if req.req is None:
        # For editable requirements that don't have an egg fragment that lists the
        # the package name and version, `pip-requirements-parser` won't attach a
        # `Requirement` object to the `InstallRequirement`.
        #
        # In this case, we can't audit the dependency so we should signal to the
        # caller that we're skipping it.
        assert isinstance(req, EditableRequirement)
        yield SkippedDependency(
            name=req.requirement_line.line,
            skip_reason="could not deduce package/specifier pair from requirement, "
            "please specify them with #egg=your_package_name==your_package_version",
        )
        continue
```

The `assert` here feels spurious -- we should support URL requirements that aren't marked as editable.

CC @tetsuo-cpp for thoughts, since I think this was your change.

Pull request description:
requirement: Remove incorrect assertion around URL requirements without egg fragment
Closes #350 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 176db8f..3790227 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,10 @@ All versions prior to 0.0.9 are untracked.
 * Fixed an issue where packages on PyPI with no published versions trigger a
   dependency resolution failure instead of being skipped
   ([#357](https://github.com/trailofbits/pip-audit/pull/357))
+  
+* Fixed an incorrect assertion triggering for non-editable URL requirements that
+  don't have an egg fragment
+  ([#359](https://github.com/trailofbits/pip-audit/pull/359))
 
 ## [2.4.3]
 
diff --git a/pip_audit/_dependency_source/requirement.py b/pip_audit/_dependency_source/requirement.py
index 3f683e7..d4a1b25 100644
--- a/pip_audit/_dependency_source/requirement.py
+++ b/pip_audit/_dependency_source/requirement.py
@@ -14,12 +14,7 @@ from typing import IO, Dict, Iterator, List, Set, Tuple, cast
 from packaging.requirements import Requirement
 from packaging.specifiers import SpecifierSet
 from packaging.version import Version
-from pip_requirements_parser import (
-    EditableRequirement,
-    InstallRequirement,
-    InvalidRequirementLine,
-    RequirementsFile,
-)
+from pip_requirements_parser import InstallRequirement, InvalidRequirementLine, RequirementsFile
 
 from pip_audit._dependency_source import (
     DependencyFixError,
@@ -95,13 +90,12 @@ class RequirementSource(DependencySource):
                 req_names: Set[str] = set()
                 for req in rf.requirements:
                     if req.req is None:
-                        # For editable requirements that don't have an egg fragment that lists the
-                        # the package name and version, `pip-requirements-parser` won't attach a
+                        # For URL requirements that don't have an egg fragment that lists the
+                        # package name and version, `pip-requirements-parser` won't attach a
                         # `Requirement` object to the `InstallRequirement`.
                         #
                         # In this case, we can't audit the dependency so we should signal to the
                         # caller that we're skipping it.
-                        assert isinstance(req, EditableRequirement)
                         yield SkippedDependency(
                             name=req.requirement_line.line,
                             skip_reason="could not deduce package/specifier pair from requirement, "


Test patch:
diff --git a/test/dependency_source/test_requirement.py b/test/dependency_source/test_requirement.py
index 4a2ef2b..786cb27 100644
--- a/test/dependency_source/test_requirement.py
+++ b/test/dependency_source/test_requirement.py
@@ -140,6 +140,26 @@ def test_requirement_source_editable_without_egg_fragment(monkeypatch):
     )
 
 
+def test_requirement_source_non_editable_without_egg_fragment(monkeypatch):
+    source = requirement.RequirementSource([Path("requirements1.txt")], ResolveLibResolver())
+
+    monkeypatch.setattr(
+        pip_requirements_parser,
+        "get_file_content",
+        lambda _: "git+https://github.com/unbit/uwsgi.git@1bb9ad77c6d2d310c2d6d1d9ad62de61f725b824",
+    )
+
+    specs = list(source.collect())
+    assert (
+        SkippedDependency(
+            name="git+https://github.com/unbit/uwsgi.git@1bb9ad77c6d2d310c2d6d1d9ad62de61f725b824",
+            skip_reason="could not deduce package/specifier pair from requirement, please specify "
+            "them with #egg=your_package_name==your_package_version",
+        )
+        in specs
+    )
+
+
 def _check_fixes(
     input_reqs: List[str],
     expected_reqs: List[str],