summaryrefslogtreecommitdiff
path: root/spec/integration
diff options
context:
space:
mode:
authormo khan <mo.khan@gmail.com>2020-04-14 12:50:13 -0600
committermo khan <mo.khan@gmail.com>2020-04-14 12:50:13 -0600
commitbae02b6ae73dda47dc86590b73c21a85bb7273a5 (patch)
tree148f331085f123903cbf3635ea8b20b5c279d964 /spec/integration
parent2b69afb35bd1b123e00d3efabce0d4c4aefdd008 (diff)
Migrate specs from gitlab-org/security-products/license-management
Diffstat (limited to 'spec/integration')
-rw-r--r--spec/integration/dotnet/examples_spec.rb12
-rw-r--r--spec/integration/dotnet/nuget_spec.rb102
-rw-r--r--spec/integration/go/modules_spec.rb53
-rw-r--r--spec/integration/java/gradle_spec.rb77
-rw-r--r--spec/integration/java/maven_spec.rb81
-rw-r--r--spec/integration/js/npm_spec.rb5
-rw-r--r--spec/integration/js/yarn_spec.rb5
-rw-r--r--spec/integration/php/composer_spec.rb121
-rw-r--r--spec/integration/python/pip_spec.rb160
-rw-r--r--spec/integration/python/pipenv_spec.rb224
-rw-r--r--spec/integration/ruby/bundler_spec.rb142
11 files changed, 982 insertions, 0 deletions
diff --git a/spec/integration/dotnet/examples_spec.rb b/spec/integration/dotnet/examples_spec.rb
new file mode 100644
index 0000000..13f4712
--- /dev/null
+++ b/spec/integration/dotnet/examples_spec.rb
@@ -0,0 +1,12 @@
+require 'spec_helper'
+
+RSpec.describe ".NET Core" do
+ it 'scans https://github.com/microsoft/RockPaperScissorsLizardSpock.git' do
+ runner.clone('https://github.com/microsoft/RockPaperScissorsLizardSpock.git')
+ report = runner.scan(env: { 'LICENSE_FINDER_CLI_OPTS' => '--recursive' })
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:licenses].count).not_to be_zero
+ expect(report[:dependencies].count).not_to be_zero
+ end
+end
diff --git a/spec/integration/dotnet/nuget_spec.rb b/spec/integration/dotnet/nuget_spec.rb
new file mode 100644
index 0000000..6eeb261
--- /dev/null
+++ b/spec/integration/dotnet/nuget_spec.rb
@@ -0,0 +1,102 @@
+require 'spec_helper'
+
+RSpec.describe "nuget" do
+ include_examples "each report version", "csharp", "nuget-dotnetcore"
+
+ context "when a project has a nuget packages.config file in the root" do
+ let(:packages_config) do
+ <<-XML
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.0" targetFramework="net46" />
+ <package id="Microsoft.Net.Compilers" version="1.0.0" targetFramework="net46" developmentDependency="true" />
+ <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net46" />
+ <package id="Microsoft.Web.Xdt" version="2.1.1" targetFramework="net46" />
+ <package id="Newtonsoft.Json" version="8.0.3" allowedVersions="[8,10)" targetFramework="net46" />
+ <package id="NuGet.Core" version="2.11.1" targetFramework="net46" />
+ <package id="NuGet.Server" version="2.11.2" targetFramework="net46" />
+ <package id="RouteMagic" version="1.3" targetFramework="net46" />
+ <package id="WebActivatorEx" version="2.1.0" targetFramework="net46" />
+ <package id="jive" version="0.1.0" />
+</packages>
+ XML
+ end
+
+ it 'produces a valid report' do
+ runner.add_file('packages.config', packages_config)
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:licenses].count).not_to be_zero
+ expect(report.dependency_names).to match_array([
+ "Microsoft.CodeDom.Providers.DotNetCompilerPlatform",
+ "Microsoft.Net.Compilers",
+ "Microsoft.Web.Infrastructure",
+ "Microsoft.Web.Xdt",
+ "Newtonsoft.Json",
+ "NuGet.Core",
+ "NuGet.Server",
+ "RouteMagic",
+ "WebActivatorEx",
+ "jive"
+ ])
+ end
+ end
+
+ context "when a project has multiple nuget packages.config in different sub directories" do
+ let(:console_packages) do
+ <<-XML
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="jive" version="0.1.0" />
+</packages>
+ XML
+ end
+
+ let(:winforms_packages) do
+ <<-XML
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="MvcMailer" version="4.5.0" />
+</packages>
+ XML
+ end
+
+ it 'produces a report including dependencies from each sub directory' do
+ runner.add_file('console/packages.config', console_packages)
+ runner.add_file('winforms/packages.config', winforms_packages)
+ report = runner.scan(env: { 'LICENSE_FINDER_CLI_OPTS' => '--recursive' })
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report.licenses_for('jive')).to match_array(['MIT'])
+ expect(report.licenses_for('MvcMailer')).to match_array(['MIT'])
+ end
+ end
+
+ context "when a project has a dependency that has it's own dependencies" do
+ let(:packages) do
+ <<-XML
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="NHibernate" version="5.2.6" />
+</packages>
+ XML
+ end
+
+ pending 'produces a report that includes the dependencies of each dependency' do
+ runner.add_file('packages.config', packages)
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:licenses].count).not_to be_zero
+ expect(report[:licenses].map { |x| x[:id] }.uniq).to match_array(['LGPL-2.1', 'Apache-2.0', 'BSD-3-Clause'])
+ expect(report.dependency_names).to match_array([
+ 'Iesi.Collections',
+ 'Remotion.Linq',
+ 'Remotion.Linq.EagerFetching',
+ "Antlr3.Runtime",
+ "NHibernate"
+ ])
+ end
+ end
+end
diff --git a/spec/integration/go/modules_spec.rb b/spec/integration/go/modules_spec.rb
new file mode 100644
index 0000000..143d8ea
--- /dev/null
+++ b/spec/integration/go/modules_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+RSpec.describe "modules" do
+ include_examples "each report version", "go", "modules"
+
+ context "when scanning a customers go.mod and go.sum files" do
+ let(:report) { runner.scan }
+
+ before do
+ runner.add_file('main.go', fixture_file_content('go/main.go'))
+ runner.add_file('go.mod', fixture_file_content('go/go.mod'))
+ runner.add_file('go.sum', fixture_file_content('go/go.sum'))
+ end
+
+ specify { expect(report).to match_schema(version: '2.0') }
+ specify { expect(report[:licenses]).not_to be_empty }
+
+ specify do
+ expect(report.dependency_names).to match_array([
+ "github.com/davecgh/go-spew",
+ "github.com/dimfeld/httptreemux/v5",
+ "github.com/go-logfmt/logfmt",
+ "github.com/golang/protobuf",
+ "github.com/google/uuid",
+ "github.com/pmezard/go-difflib",
+ "github.com/stretchr/objx",
+ "golang.org/x/net",
+ "golang.org/x/oauth2",
+ "google.golang.org/appengine",
+ "gopkg.in/yaml.v2",
+ 'github.com/stretchr/testify'
+ ])
+ end
+
+ specify { expect(report.licenses_for('github.com/dimfeld/httptreemux/v5')).to match_array(['MIT']) }
+ specify { expect(report.licenses_for('github.com/go-logfmt/logfmt')).to match_array(['MIT']) }
+ specify { expect(report.licenses_for('github.com/google/uuid')).to match_array(['BSD-3-Clause']) }
+ specify { expect(report.licenses_for('github.com/stretchr/testify')).to match_array(['MIT']) }
+ specify { expect(report.licenses_for('golang.org/x/oauth2')).to match_array(['BSD-3-Clause']) }
+ end
+
+ context "when scanning the `gitaly` project" do
+ let(:report) { runner.scan }
+
+ before do
+ runner.clone('https://gitlab.com/gitlab-org/gitaly.git')
+ end
+
+ specify { expect(report).to match_schema(version: '2.0') }
+ specify { expect(report[:licenses]).not_to be_empty }
+ specify { expect(report[:dependencies]).not_to be_empty }
+ end
+end
diff --git a/spec/integration/java/gradle_spec.rb b/spec/integration/java/gradle_spec.rb
new file mode 100644
index 0000000..1b566bb
--- /dev/null
+++ b/spec/integration/java/gradle_spec.rb
@@ -0,0 +1,77 @@
+require 'spec_helper'
+
+RSpec.describe "gradle" do
+ include_examples "each report version", "java", "gradle"
+
+ context "when running a default gradle build" do
+ it 'scans a gradle project' do
+ content = <<~GRADLE
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * This is a general purpose Gradle build.
+ * Learn how to create Gradle builds at https://guides.gradle.org/creating-new-gradle-builds
+ */
+plugins {
+ id "com.github.hierynomus.license" version "0.15.0"
+}
+ GRADLE
+ runner.add_file('build.gradle', content)
+
+ report = runner.scan
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:licenses]).to be_empty
+ expect(report[:dependencies]).to be_empty
+ end
+ end
+
+ context 'when scanning a gradle project that does not include the `com.github.hierynomus.license` plugin' do
+ let(:project_url) { 'https://gitlab.com/one-touch-pipeline/otp.git' }
+ let(:result) { runner.scan }
+
+ before do
+ runner.clone(project_url)
+ end
+
+ it 'is able to detect licenses' do
+ expect(result).to match_schema(version: '2.0')
+ expect(result[:licenses]).not_to be_empty
+
+ [
+ { name: 'ant', licenses: ['Apache-2.0'] },
+ { name: 'activation', licenses: ['CDDL-1.0'] },
+ { name: 'xml-apis', licenses: ['Apache-2.0', 'SAX-PD', 'W3C-20150513'] },
+ { name: 'sitemesh', licenses: ['Apache-1.1'] },
+ { name: 'hibernate-jpa-2.1-api', licenses: ['BSD-3-Clause', 'EPL-1.0'] }
+ ].each do |dependency|
+ expect(result.licenses_for(dependency[:name])).to match_array(dependency[:licenses])
+ end
+ end
+ end
+
+ [
+ '6.2',
+ '5.6',
+ '4.9',
+ '3.5',
+ '2.9',
+ '1.9'
+ ].each do |gradle_version|
+ %w[8 11].each do |java_version|
+ context "when scanning a gradle (v#{gradle_version}) project that uses a kotlin build script" do
+ let(:build_file_content) { fixture_file_content("java/build.gradle.kts") }
+
+ it 'scans a gradle project' do
+ runner.add_file('build.gradle.kts', build_file_content)
+ runner.add_file('settings.gradle.kts', 'rootProject.name = "example"')
+ runner.add_file('.tool-versions', "gradle #{gradle_version}")
+
+ report = runner.scan(env: { 'LM_JAVA_VERSION' => java_version })
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:licenses]).to be_empty
+ expect(report[:dependencies]).to be_empty
+ end
+ end
+ end
+ end
+end
diff --git a/spec/integration/java/maven_spec.rb b/spec/integration/java/maven_spec.rb
new file mode 100644
index 0000000..92444e8
--- /dev/null
+++ b/spec/integration/java/maven_spec.rb
@@ -0,0 +1,81 @@
+require 'spec_helper'
+
+RSpec.describe "maven" do
+ include_examples "each report version", "java", "maven"
+ include_examples "each report version", "java", "maven-multimodules"
+
+ describe "When the maven dependencies come from a custom public maven repository" do
+ it 'is able to detect some of the licenses' do
+ runner.add_file('pom.xml', fixture_file_content('java/pom-public-gitlab-repository.xml'))
+
+ report = runner.scan(env: {
+ 'CI_PROJECT_ID' => '17523603'
+ })
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:dependencies]).to match_array([{ name: 'mvn-spike', url: '', description: '', paths: ['.'], licenses: ['MIT'] }])
+ end
+
+ it 'downloads packages from by using a custom `settings.xml`' do
+ runner.add_file('pom.xml', fixture_file_content('java/pom-public-gitlab-repository.xml'))
+ runner.add_file('my_settings.xml', fixture_file_content('java/custom-maven-settings.xml'))
+
+ report = runner.scan(env: {
+ 'CI_PROJECT_ID' => 'invalid',
+ 'MAVEN_CLI_OPTS' => "--settings my_settings.xml"
+ })
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:dependencies]).to match_array([{ name: 'mvn-spike', url: '', description: '', paths: ['.'], licenses: ['MIT'] }])
+ end
+ end
+
+ describe "When using the `SETUP_CMD`" do
+ it 'executes the custom script' do
+ runner.add_file('custom.sh') do
+ <<~SCRIPT
+ #!/bin/bash -l
+
+ echo 'hello'
+ SCRIPT
+ end
+
+ report = runner.scan(env: {
+ 'SETUP_CMD' => 'bash custom.sh'
+ })
+
+ expect(report).to match_schema(version: '2.0')
+ end
+ end
+
+ describe "When scanning a project with multiple modules" do
+ before do
+ runner.mount(dir: fixture_file('java/maven-multimodule'))
+ end
+
+ it 'detects dependences from each module' do
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:dependencies]).not_to be_empty
+
+ [
+ { name: "asm", licenses: ["BSD-4-Clause"] },
+ { name: "asm-commons", licenses: ["BSD-4-Clause"] },
+ { name: "jackson-annotations", licenses: ["Apache-2.0"] },
+ { name: "jackson-core", licenses: ["Apache-2.0"] },
+ { name: "jackson-databind", licenses: ["Apache-2.0"] },
+ { name: "jackson-dataformat-xml", licenses: ["Apache-2.0"] },
+ { name: "jackson-module-jaxb-annotations", licenses: ["Apache-2.0"] },
+ { name: "log4j-api", licenses: ["Apache-2.0"] },
+ { name: "log4j-core", licenses: ["Apache-2.0"] },
+ { name: "netty-all", licenses: ["Apache-2.0"] },
+ { name: "stax2-api", licenses: ["BSD-4-Clause"] }
+ ].each do |dependency|
+ expect(report.licenses_for(dependency[:name])).to match_array(dependency[:licenses])
+ end
+
+ expect(report.dependency_names).not_to include('junit')
+ end
+ end
+end
diff --git a/spec/integration/js/npm_spec.rb b/spec/integration/js/npm_spec.rb
new file mode 100644
index 0000000..1c272bf
--- /dev/null
+++ b/spec/integration/js/npm_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+RSpec.describe "npm" do
+ include_examples "each report version", "js", "npm"
+end
diff --git a/spec/integration/js/yarn_spec.rb b/spec/integration/js/yarn_spec.rb
new file mode 100644
index 0000000..0756f9d
--- /dev/null
+++ b/spec/integration/js/yarn_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+RSpec.describe "yarn" do
+ include_examples "each report version", "js", "yarn"
+end
diff --git a/spec/integration/php/composer_spec.rb b/spec/integration/php/composer_spec.rb
new file mode 100644
index 0000000..62ff598
--- /dev/null
+++ b/spec/integration/php/composer_spec.rb
@@ -0,0 +1,121 @@
+require 'spec_helper'
+
+RSpec.describe "composer" do
+ include_examples "each report version", "php", "composer"
+
+ context "when the project's dependencies require php-gd e.g. in the case of Drupal" do
+ it 'installs the required dependencies and produces a valid report' do
+ # composer.json from https://git.drupalcode.org/project/drupal/raw/8.7.x/core/composer.json
+ runner.add_file('composer.json', fixture_file_content('php/drupal_composer.json'))
+
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:version]).not_to be_empty
+ expect(report[:licenses]).not_to be_empty
+ expect(report.dependency_names).to match_array(%w[
+ asm89/stack-cors
+ behat/mink
+ behat/mink-browserkit-driver
+ behat/mink-goutte-driver
+ behat/mink-selenium2-driver
+ brumann/polyfill-unserialize
+ composer/semver
+ doctrine/annotations
+ doctrine/cache
+ doctrine/collections
+ doctrine/common
+ doctrine/event-manager
+ doctrine/inflector
+ doctrine/instantiator
+ doctrine/lexer
+ doctrine/persistence
+ doctrine/reflection
+ drupal/coder
+ easyrdf/easyrdf
+ egulias/email-validator
+ fabpot/goutte
+ guzzlehttp/guzzle
+ guzzlehttp/promises
+ guzzlehttp/psr7
+ instaclick/php-webdriver
+ jcalderonzumba/gastonjs
+ jcalderonzumba/mink-phantomjs-driver
+ justinrainbow/json-schema
+ masterminds/html5
+ mikey179/vfsstream
+ myclabs/deep-copy
+ paragonie/random_compat
+ pear/archive_tar
+ pear/console_getopt
+ pear/pear-core-minimal
+ pear/pear_exception
+ phar-io/manifest
+ phar-io/version
+ phpdocumentor/reflection-common
+ phpdocumentor/reflection-docblock
+ phpdocumentor/type-resolver
+ phpspec/prophecy
+ phpunit/php-code-coverage
+ phpunit/php-file-iterator
+ phpunit/php-text-template
+ phpunit/php-timer
+ phpunit/php-token-stream
+ phpunit/phpunit
+ phpunit/phpunit-mock-objects
+ psr/container
+ psr/http-message
+ psr/log
+ ralouphie/getallheaders
+ sebastian/code-unit-reverse-lookup
+ sebastian/comparator
+ sebastian/diff
+ sebastian/environment
+ sebastian/exporter
+ sebastian/global-state
+ sebastian/object-enumerator
+ sebastian/object-reflector
+ sebastian/recursion-context
+ sebastian/resource-operations
+ sebastian/version
+ squizlabs/php_codesniffer
+ stack/builder
+ symfony-cmf/routing
+ symfony/browser-kit
+ symfony/class-loader
+ symfony/console
+ symfony/css-selector
+ symfony/debug
+ symfony/dependency-injection
+ symfony/dom-crawler
+ symfony/event-dispatcher
+ symfony/http-foundation
+ symfony/http-kernel
+ symfony/phpunit-bridge
+ symfony/polyfill-ctype
+ symfony/polyfill-iconv
+ symfony/polyfill-intl-idn
+ symfony/polyfill-mbstring
+ symfony/polyfill-php56
+ symfony/polyfill-php70
+ symfony/polyfill-php72
+ symfony/polyfill-util
+ symfony/process
+ symfony/psr-http-message-bridge
+ symfony/routing
+ symfony/serializer
+ symfony/translation
+ symfony/validator
+ symfony/yaml
+ theseer/tokenizer
+ twig/twig
+ typo3/phar-stream-wrapper
+ webmozart/assert
+ zendframework/zend-diactoros
+ zendframework/zend-escaper
+ zendframework/zend-feed
+ zendframework/zend-stdlib
+ ])
+ end
+ end
+end
diff --git a/spec/integration/python/pip_spec.rb b/spec/integration/python/pip_spec.rb
new file mode 100644
index 0000000..8e3ec3d
--- /dev/null
+++ b/spec/integration/python/pip_spec.rb
@@ -0,0 +1,160 @@
+require 'spec_helper'
+
+RSpec.describe "pip" do
+ 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(version: '2.0')
+ 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-4-Clause"])
+ 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(version: '2.0')
+ expect(report[:version]).to start_with('2')
+ expect(report[:licenses]).not_to be_empty
+ expect(report[:dependencies]).not_to be_empty
+ end
+ end
+
+ [{ version: '2', commit: '04dce91b' }, { version: '3', commit: '48e250a1' }].each do |python|
+ ['1.0', '1.1', '2.0'].each do |report_version|
+ context "when generating a `#{report_version}` report using Python `#{python[:version]}`" do
+ 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 }
+
+ it 'matches the expected report' do
+ runner.clone(url, branch: python[:commit])
+ report = runner.scan(env: environment)
+
+ expect(JSON.pretty_generate(report.to_h)).to eq(expected_content)
+ expect(report).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')
+ end
+
+ it 'detects the software licenses' do
+ report = runner.scan(env: { 'SETUP_CMD' => 'pip install -r requirements.txt' })
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report.licenses_for('six')).to match_array(["MIT"])
+ expect(report.dependency_names).to contain_exactly('six')
+ 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://#{private_pypi_host}/simple" }
+ let(:bundle) { fixture_file_content('python/pypi.crt') }
+
+ 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' => bundle,
+ '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
diff --git a/spec/integration/python/pipenv_spec.rb b/spec/integration/python/pipenv_spec.rb
new file mode 100644
index 0000000..b54ee99
--- /dev/null
+++ b/spec/integration/python/pipenv_spec.rb
@@ -0,0 +1,224 @@
+require 'spec_helper'
+
+RSpec.describe "pipenv" do
+ include_examples "each report version", "python", "pipenv", "pip-file-lock"
+
+ context "when a project depends on a version 6 Pipfile.lock" do
+ let(:pipfile_lock_content) do
+ JSON.pretty_generate({
+ "_meta": {
+ "hash": { "sha256": "" },
+ "pipfile-spec": 6,
+ "requires": { "python_version": "3.8" },
+ "sources": [{ "name": "pypi", "url": "https://pypi.org/simple", "verify_ssl": true }]
+ },
+ "default": {
+ "six": { "hashes": [], "index": "pypi", "version": "==1.13.0" }
+ },
+ "develop": {}
+ })
+ end
+
+ it 'produces a valid report' do
+ runner.add_file('Pipfile.lock', pipfile_lock_content)
+
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:version]).not_to be_empty
+ expect(report[:licenses]).not_to be_empty
+ expect(report.dependency_names).to contain_exactly("six")
+ end
+ end
+
+ context "when a project depends on a version 3.2.1 Pipfile.lock" do
+ let(:pipfile_lock_content) do
+ JSON.pretty_generate({
+ "default": {
+ "crayons": { "version": "==0.1.2", "hash": "" },
+ "requirements-parser": { "version": "==0.1.0", "hash": "" },
+ "pexpect": { "version": "==4.2.1", "hash": "" },
+ "delegator.py": { "version": "==0.0.8", "hash": "" },
+ "backports.shutil_get_terminal_size": { "version": "==1.0.0", "hash": "" },
+ "ptyprocess": { "version": "==0.5.1", "hash": "" },
+ "parse": { "version": "==1.6.6", "hash": "" },
+ "toml": { "version": "==0.9.2", "hash": "" },
+ "colorama": { "version": "==0.3.7", "hash": "" },
+ "requests": { "version": "==2.13.0", "hash": "" },
+ "click": { "version": "==6.7", "hash": "" }
+ },
+ "develop": {
+ "packaging": { "version": "==16.8", "hash": "" },
+ "pytest": { "version": "==3.0.6", "hash": "" },
+ "setuptools": { "version": "==34.0.2", "hash": "" },
+ "pyparsing": { "version": "==2.1.10", "hash": "" },
+ "py": { "version": "==1.4.32", "hash": "" },
+ "six": { "version": "==1.10.0", "hash": "" },
+ "appdirs": { "version": "==1.4.0", "hash": "" }
+ },
+ "_meta": {
+ "sources": [{ "url": "https://pypi.python.org/simple", "verify_ssl": true }],
+ "requires": {},
+ "Pipfile-sha256": "24f12b631b7c40b8c5eff934a1aef263ed04f5eaffb4acf4706442f3d23cba36"
+ }
+ })
+ end
+
+ it 'produces a valid report' do
+ runner.add_file('Pipfile.lock', pipfile_lock_content)
+
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:version]).not_to be_empty
+ expect(report[:licenses]).not_to be_empty
+ expect(report.dependency_names).to match_array([
+ "backports.shutil_get_terminal_size",
+ "click",
+ "colorama",
+ "crayons",
+ "delegator.py",
+ "parse",
+ "pexpect",
+ "ptyprocess",
+ "requests",
+ "requirements-parser",
+ "toml"
+ ])
+ end
+ end
+
+ context "when a project depends on a version 5 Pipfile.lock" do
+ let(:pipfile_lock_content) do
+ JSON.pretty_generate({
+ "_meta": {
+ "hash": { "sha256": "" },
+ "host-environment-markers": {
+ "implementation_name": "cpython",
+ "implementation_version": "3.6.1",
+ "os_name": "posix",
+ "platform_machine": "x86_64",
+ "platform_python_implementation": "CPython",
+ "platform_release": "16.7.0",
+ "platform_system": "Darwin",
+ "platform_version": "Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64",
+ "python_full_version": "3.6.1",
+ "python_version": "3.6",
+ "sys_platform": "darwin"
+ },
+ "pipfile-spec": 5,
+ "requires": {},
+ "sources": [{ "name": "pypi", "url": "https://pypi.python.org/simple", "verify_ssl": true }]
+ },
+ "default": {
+ "certifi": { "hashes": ["", ""], "version": "==2017.7.27.1" },
+ "chardet": { "hashes": ["", ""], "version": "==3.0.4" },
+ "idna": { "hashes": ["", ""], "version": "==2.6" },
+ "requests": { "hashes": ["", ""], "version": "==2.18.4" },
+ "urllib3": { "hashes": ["", ""], "version": "==1.22" }
+ },
+ "develop": {
+ "py": { "hashes": ["", ""], "version": "==1.4.34" },
+ "pytest": { "hashes": ["", ""], "version": "==3.2.2" }
+ }
+ })
+ end
+
+ it 'produces a valid report' do
+ runner.add_file('Pipfile.lock', pipfile_lock_content)
+
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:version]).not_to be_empty
+ expect(report[:licenses]).not_to be_empty
+ expect(report.dependency_names).to match_array(%w[
+ certifi
+ chardet
+ idna
+ requests
+ urllib3
+ ])
+ end
+ end
+
+ context "when fetching metadata from a custom source" do
+ let(:pipfile_lock_content) do
+ JSON.pretty_generate({
+ "_meta": {
+ "hash": { "sha256": "" },
+ "pipfile-spec": 6,
+ "requires": { "python_version": "3.8" },
+ "sources": [{ "name": "pypi", "url": "https://test.pypi.org/simple", "verify_ssl": true }]
+ },
+ "default": {
+ "six": { "hashes": [], "index": "pypi", "version": "==1.13.0" }
+ },
+ "develop": {}
+ })
+ end
+
+ before do
+ runner.add_file('Pipfile.lock', pipfile_lock_content)
+ end
+
+ it 'produces a valid report' do
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:licenses]).not_to be_empty
+ expect(report[:dependencies].count).to be(1)
+ expect(report.find('six')).not_to be_nil
+ end
+ end
+
+ context "when scanning a simple Pipfile project" do
+ let(:lockfile_content) { fixture_file_content('python/simple-Pipfile.lock') }
+ let(:lockfile_hash) { JSON.parse(lockfile_content) }
+
+ before do
+ runner.add_file('Pipfile', fixture_file_content('python/simple-Pipfile'))
+ runner.add_file('Pipfile.lock', lockfile_content)
+ end
+
+ [2, 3].each do |version|
+ context "when scanning a Python #{version} project" do
+ let(:report) { runner.scan(env: { 'LM_PYTHON_VERSION' => version.to_s }) }
+
+ specify { expect(report).to match_schema(version: '2.0') }
+
+ it 'includes dependencies in the default group' do
+ lockfile_hash['default'].keys.each do |key|
+ expect(report.find(key)).not_to be_nil
+ end
+ end
+
+ it 'excludes dependencies in the development group' do
+ lockfile_hash['develop'].keys.each do |key|
+ expect(report.find(key)).to be_nil
+ end
+ end
+ end
+ end
+ end
+
+ context "when connecting to a private package repository with self signed certificate" do
+ let(:index_url) { "https://#{private_pypi_host}/simple" }
+ let(:bundle) { fixture_file_content('python/pypi.crt') }
+
+ before do
+ runner.add_file('Pipfile', fixture_file_content('python/airgap-Pipfile.erb', index_url: index_url))
+ runner.add_file('Pipfile.lock', fixture_file_content('python/airgap-Pipfile.lock.erb', index_url: index_url))
+ end
+
+ it 'downloads the packages and trusts the certificate' do
+ report = runner.scan(env: {
+ 'ADDITIONAL_CA_CERT_BUNDLE' => bundle,
+ 'PIP_INDEX_URL' => index_url
+ })
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report.dependency_names).to include('requests')
+ end
+ end
+end
diff --git a/spec/integration/ruby/bundler_spec.rb b/spec/integration/ruby/bundler_spec.rb
new file mode 100644
index 0000000..5236adf
--- /dev/null
+++ b/spec/integration/ruby/bundler_spec.rb
@@ -0,0 +1,142 @@
+require 'spec_helper'
+
+RSpec.describe "bundler" do
+ include_examples "each report version", "ruby", "bundler"
+
+ context "when the project depends on an older version of ruby specified in a `.ruby-version` file" do
+ it 'installs the required ruby and produces a valid report' do
+ runner.add_file('.ruby-version', 'ruby-2.4.9')
+ runner.add_file('Gemfile') do
+ <<~RAW
+source 'https://rubygems.org'
+
+gem 'saml-kit'
+ RAW
+ end
+
+ report = runner.scan
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:licenses]).not_to be_empty
+ expect(report[:dependencies].map { |x| x[:name] }).to include("saml-kit")
+ end
+ end
+
+ context "when a project depends on an older version of bundler" do
+ it 'produces a valid report' do
+ runner.add_file('Gemfile') do
+ <<~RAW
+source 'https://rubygems.org'
+
+gem 'saml-kit'
+ RAW
+ end
+ runner.add_file('Gemfile.lock') do
+ <<~RAW
+GEM
+ remote: https://rubygems.org/
+ specs:
+ activemodel (6.0.2.1)
+ activesupport (= 6.0.2.1)
+ activesupport (6.0.2.1)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 0.7, < 2)
+ minitest (~> 5.1)
+ tzinfo (~> 1.1)
+ zeitwerk (~> 2.2)
+ builder (3.2.4)
+ concurrent-ruby (1.1.5)
+ i18n (1.7.1)
+ concurrent-ruby (~> 1.0)
+ mini_portile2 (2.4.0)
+ minitest (5.13.0)
+ net-hippie (0.2.7)
+ nokogiri (1.10.7)
+ mini_portile2 (~> 2.4.0)
+ saml-kit (1.1.0)
+ activemodel (>= 4.2.0)
+ net-hippie (~> 0.1)
+ xml-kit (>= 0.3.0, < 1.0.0)
+ thread_safe (0.3.6)
+ tilt (2.0.10)
+ tzinfo (1.2.6)
+ thread_safe (~> 0.1)
+ xml-kit (0.4.0)
+ activemodel (>= 4.2.0)
+ builder (~> 3.2)
+ nokogiri (~> 1.10)
+ tilt (>= 1.4.1)
+ xmldsig (~> 0.6)
+ xmldsig (0.6.6)
+ nokogiri (>= 1.6.8, < 2.0.0)
+ zeitwerk (2.2.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ saml-kit
+
+BUNDLED WITH
+ 1.17.3
+ RAW
+ end
+
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:licenses]).not_to be_empty
+ expect(report.dependency_names).to include("saml-kit")
+ end
+ end
+
+ context "when a project depends on bundler `~> 2.0`" do
+ it 'produces a valid report' do
+ runner.add_file('Gemfile') do
+ <<~RAW
+source 'https://rubygems.org'
+
+gem 'net-hippie'
+ RAW
+ end
+ runner.add_file('Gemfile.lock') do
+ <<~RAW
+GEM
+ remote: https://rubygems.org/
+ specs:
+ net-hippie (0.3.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ net-hippie
+
+BUNDLED WITH
+ 2.1.4
+ RAW
+ end
+
+ report = runner.scan
+
+ expect(report).to match_schema(version: '2.0')
+ expect(report[:licenses]).not_to be_empty
+ expect(report.find('net-hippie')).to eql({
+ name: 'net-hippie',
+ description: "net/http for hippies. ☮️",
+ url: "https://github.com/mokhan/net-hippie/",
+ paths: ['.'],
+ licenses: ['MIT']
+ })
+ end
+ end
+
+ context "when passing custom options to license finder" do
+ it 'forwards the options to license finder' do
+ report = runner.scan(env: {
+ 'LICENSE_FINDER_CLI_OPTS' => "--debug --aggregate-paths=. ruby"
+ })
+
+ expect(report).to match_schema(version: '2.0')
+ end
+ end
+end