# frozen_string_literal: true require 'spec_helper' RSpec.describe "pip" do before do system('rm -fr /opt/asdf/installs/python') end context "when a project depends on the latest version of pip" do let(:requirements) { "sentry-sdk>=0.7.7" } it 'produces a valid report' do runner.add_file('requirements.txt', requirements) report = runner.scan expect(report).to match_schema expect(report[:version]).to start_with('2') expect(report.dependency_names).to include("sentry-sdk") expect(report.licenses_for('sentry-sdk')).to match_array(["bsd"]) end end context "when the project has a dependency that depends on a minimum of python 3.6" do let(:requirements) do [ 'boto3', 'aws-lambda-context>=1.0.0', 'jsonschema>=3.0.0', 'python-json-logger>=0.1.10', 'sentry-sdk>=0.7.7', 'ptvsd', 'pylint', 'flake8', 'bandit', 'pydocstyle' ].join("\n") end it 'produces a valid report' do runner.add_file('requirements.txt', requirements) report = runner.scan expect(report).to match_schema expect(report[:version]).to start_with('2') expect(report[:licenses]).not_to be_empty expect(report[:dependencies]).not_to be_empty end end context 'with BSD licenses dependencies' do let(:requirements) { %w[webencodings idna click EasyProcess PyVirtualDisplay].join("\n") } it 'finds proper versions of BSD license' do runner.add_file('requirements.txt', requirements) report = runner.scan expect(report.licenses_for('webencodings')).to eq(['bsd']) expect(report.licenses_for('idna')).to eq(['bsd']) expect(report.licenses_for('EasyProcess')).to eq(['bsd']) expect(report.licenses_for('PyVirtualDisplay')).to eq(['bsd']) expect(report.licenses_for('click')).to eq(['bsd']) end end [{ version: '2', commit: '04dce91b' }, { version: '3', commit: '48e250a1' }].each do |python| ['1.0', '1.1', '2.0', '2.1'].each do |report_version| context "when generating a `#{report_version}` report using Python `#{python[:version]}`" do subject { runner.scan(env: environment) } let(:url) { "https://gitlab.com/gitlab-org/security-products/tests/#{language}-#{package_manager}.git" } let(:language) { 'python' } let(:package_manager) { 'pip' } let(:environment) { { 'LM_REPORT_VERSION' => report_version, 'LM_PYTHON_VERSION' => python[:version] } } let(:expected_content) { fixture_file_content("expected/#{language}/#{python[:version]}/#{package_manager}/v#{report_version}.json").chomp } before do runner.clone(url, branch: python[:commit]) end it 'matches the expected report' do expect(JSON.pretty_generate(subject.to_h)).to eq(expected_content) expect(subject).to match_schema(version: report_version) end end end end context "when scanning projects with a `setup.py` and does not have a `requirements.txt` file" do it 'detects licenses in a simple `setup.py`' do runner.add_file('setup.py', fixture_file_content('python/simple-setup.py')) report = runner.scan expect(report).to match_schema(version: '2.0') expect(report[:dependencies]).not_to be_empty expect(report.licenses_for('boto3')).to match_array(['Apache-2.0']) end it 'detects licenses in a more complicated `setup.py`' do runner.clone('https://github.com/pypa/sampleproject.git', branch: 'd09af3dbd851d385e56f0aed29875bfa3d3df230') report = runner.scan expect(report).to match_schema(version: '2.0') expect(report[:dependencies]).not_to be_empty expect(report.licenses_for('peppercorn')).to match_array(['BSD-2-Clause']) end end context "when scanning projects that have a custom index-url" do before do runner.add_file('requirements.txt', 'six') end it 'detects the licenses from the custom index' do report = runner.scan(env: { 'PIP_INDEX_URL' => 'https://test.pypi.org/simple/' }) expect(report).to match_schema(version: '2.0') expect(report.licenses_for('six')).to match_array(["MIT"]) end end context "when a project uses a custom `SETUP_CMD`" do before do runner.add_file('requirements.txt', 'six==1.14.0') runner.add_file('.python-version', '3.8.4') runner.add_file('custom.sh') do <<~SCRIPT #!/bin/bash -lx asdf install python 3.8.4 asdf reshim pip install -r requirements.txt SCRIPT end end it 'detects the software licenses' do report = runner.scan(env: { 'SETUP_CMD' => 'bash custom.sh' }) expect(report).to match_schema(version: '2.0') expect(report.dependency_names).to contain_exactly('six') expect(report.licenses_for('six')).to match_array(["MIT"]) end end context "when a projects is running in airgap mode" do before do runner.add_file('requirements.txt', '') end it 'is able to scan the project' do report = runner.scan(env: { 'PIP_INDEX_URL' => 'https://localhost/simple/' }) expect(report).to match_schema(version: '2.0') expect(report[:licenses]).to be_empty expect(report[:dependencies]).to be_empty end end context "when connecting to a private package repository with self signed certificate" do let(:index_url) { "https://pypi.test/simple" } before do runner.add_file('setup.py') do <<~RAW from setuptools import setup, find_packages setup( name='gitlab-sp-test-python-pip', version='1.2.0', packages=find_packages(), install_requires=['requests'], ) RAW end end it 'downloads the packages and trusts the certificate' do report = runner.scan(env: { 'ADDITIONAL_CA_CERT_BUNDLE' => x509_certificate.read, 'PIP_INDEX_URL' => index_url }) expect(report).to match_schema(version: '2.0') expect(report.dependency_names).to include('requests') expect(report.licenses_for('requests')).to match_array(['Apache-2.0']) end end end