Skip to content

Commit 0a32984

Browse files
committed
Provide insecure parsing for top level dependencies
Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent 111b6db commit 0a32984

File tree

5 files changed

+461
-6
lines changed

5 files changed

+461
-6
lines changed

src/python_inspector/resolution.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,25 @@ def get_requirements_from_distribution(
9393
handler: BasePypiHandler,
9494
location: str,
9595
) -> List[Requirement]:
96+
"""
97+
Return a list of requirements from a source distribution or wheel at
98+
``location`` using the provided ``handler`` DatafileHandler for parsing.
99+
"""
100+
if not location:
101+
return []
102+
if not os.path.exists(location):
103+
return []
104+
reqs = []
105+
for package_data in handler.parse(location):
106+
dependencies = package_data.dependencies
107+
reqs.extend(get_requirements_from_dependencies(dependencies=dependencies))
108+
return reqs
109+
110+
111+
def get_deps_from_distribution(
112+
handler: BasePypiHandler,
113+
location: str,
114+
) -> List[DependentPackage]:
96115
"""
97116
Return a list of requirements from a source distribution or wheel at
98117
``location`` using the provided ``handler`` DatafileHandler for parsing.
@@ -104,7 +123,7 @@ def get_requirements_from_distribution(
104123
deps = []
105124
for package_data in handler.parse(location):
106125
dependencies = package_data.dependencies
107-
deps.extend(get_requirements_from_dependencies(dependencies=dependencies))
126+
deps.extend(dependencies=dependencies)
108127
return deps
109128

110129

@@ -133,7 +152,7 @@ def contain_string(string: str, files: List) -> bool:
133152
return False
134153

135154

136-
def parse_setup_py_insecurely(setup_py):
155+
def parse_reqs_from_setup_py_insecurely(setup_py):
137156
"""
138157
Yield requirements from the setup.py file at ``setup_py``.
139158
"""
@@ -143,6 +162,27 @@ def parse_setup_py_insecurely(setup_py):
143162
yield Requirement(req)
144163

145164

165+
def parse_deps_from_setup_py_insecurely(setup_py):
166+
"""
167+
Yield requirements from the setup.py file at ``setup_py``.
168+
"""
169+
if not os.path.exists(setup_py):
170+
return []
171+
for req in iter_requirements(level="", extras=[], setup_file=setup_py):
172+
parsed_req = Requirement(req)
173+
yield DependentPackage(
174+
purl=str(
175+
PackageURL(
176+
type="pypi",
177+
name=parsed_req.name,
178+
)
179+
),
180+
extracted_requirement=req,
181+
scope="install",
182+
is_runtime=False,
183+
)
184+
185+
146186
def is_valid_version(
147187
parsed_version: Union[LegacyVersion, Version],
148188
requirements: Dict,
@@ -685,7 +725,7 @@ def get_setup_dependencies(location, analyze_setup_py_insecurely=False, use_requ
685725
string="_require", files=[setup_py_location, setup_cfg_location]
686726
):
687727
if analyze_setup_py_insecurely:
688-
yield from parse_setup_py_insecurely(setup_py=setup_py_location)
728+
yield from parse_reqs_from_setup_py_insecurely(setup_py=setup_py_location)
689729
else:
690730
raise Exception("Unable to collect setup.py dependencies securely")
691731

src/python_inspector/resolve_cli.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,20 @@
1919
from tinynetrc import Netrc
2020

2121
from _packagedcode.models import DependentPackage
22+
from _packagedcode.pypi import PipRequirementsFileHandler
2223
from _packagedcode.pypi import PythonSetupPyHandler
2324
from _packagedcode.pypi import can_process_dependent_package
2425
from python_inspector import dependencies
2526
from python_inspector import utils
2627
from python_inspector import utils_pypi
2728
from python_inspector.cli_utils import FileOptionType
2829
from python_inspector.package_data import get_pypi_data_from_purl
30+
from python_inspector.resolution import contain_string
31+
from python_inspector.resolution import get_deps_from_distribution
2932
from python_inspector.resolution import get_environment_marker_from_environment
3033
from python_inspector.resolution import get_python_version_from_env_tag
3134
from python_inspector.resolution import get_resolved_dependencies
35+
from python_inspector.resolution import parse_deps_from_setup_py_insecurely
3236

3337
TRACE = False
3438

@@ -283,6 +287,33 @@ def resolve_dependencies(
283287
if dep.scope == "install":
284288
direct_dependencies.append(dep)
285289

290+
if not package_data.dependencies:
291+
has_deps = False
292+
if contain_string(string="requirements.txt", files=[setup_py_file]):
293+
# Look in requirements file if and only if thy are refered in setup.py or setup.cfg
294+
# And no deps have been yielded by requirements file.
295+
296+
location = os.path.dirname(setup_py_file)
297+
requirement_location = os.path.join(
298+
location,
299+
"requirements.txt",
300+
)
301+
deps = get_deps_from_distribution(
302+
handler=PipRequirementsFileHandler,
303+
location=requirement_location,
304+
)
305+
if deps:
306+
has_deps = True
307+
direct_dependencies.extend(deps)
308+
309+
if not has_deps and contain_string(string="_require", files=[setup_py_file]):
310+
if analyze_setup_py_insecurely:
311+
direct_dependencies.extend(
312+
parse_deps_from_setup_py_insecurely(setup_py=setup_py_file)
313+
)
314+
else:
315+
raise Exception("Unable to collect setup.py dependencies securely")
316+
286317
if not direct_dependencies:
287318
click.secho("Error: no requirements requested.")
288319
ctx.exit(1)

0 commit comments

Comments
 (0)