papers 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +5 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.md +163 -0
- data/Rakefile +8 -0
- data/bin/papers +8 -0
- data/lib/papers.rb +25 -0
- data/lib/papers/cli.rb +41 -0
- data/lib/papers/configuration.rb +46 -0
- data/lib/papers/dependency_specification.rb +35 -0
- data/lib/papers/dependency_specification/gem.rb +32 -0
- data/lib/papers/dependency_specification/javascript.rb +24 -0
- data/lib/papers/license_validator.rb +62 -0
- data/lib/papers/manifest_generator.rb +92 -0
- data/lib/papers/version.rb +8 -0
- data/papers.gemspec +32 -0
- data/spec/papers_spec.rb +206 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MjJiMDlhOTlmYjAzNjcwNDE1N2QxN2IyOGU2MTllOGNhNjVlMDI3Yg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MDBlNGZjZTRmMGQ5NTQ2Mjg4YWNjYzM0Y2M1MzNlM2RiMWY2NTBiYw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NGFhMTE2ZDg5MTg4ZDQ4NThhMDZjZTE3MzVkNDUwMDE4YzA0YzhhNDY3NWJk
|
10
|
+
ZDYwNGQzM2ExZWM1ZGU3ZmJjODgzNGM2NzRlYzBjMjhlOWFkYjI5OGFmODI4
|
11
|
+
NzQzZjI0ZWYxMzcyZDM1M2QyMmE3ZDgyOWFkMDQ3ZWUyNzg5NTU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OWRkODEwYmFmNTRkZjE5OTg3OGUwZjU0MTJiNTVhNWVkZTgxMGIwNDc1MTEw
|
14
|
+
YzU1ZGU0ZGIyODQ0ZDNjODllZjBkYWM2MjdhMDJlYmI5ZWNiNWU1MzQ5ZDZm
|
15
|
+
YWUzZDBlM2Q0ZjBlNzA1YjE4YjRmNWMxMzgxYTI1ZDg3ZmQ4ZDY=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 New Relic
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
# Papers
|
2
|
+
|
3
|
+
> "Papers, please."
|
4
|
+
|
5
|
+
Check that your Ruby/Rails project's dependencies are licensed with only the licenses you specify. **Papers** will validate that your gems and JavaScript files conform to a whitelist of software licenses. Don't get caught flat-footed by the GPL.
|
6
|
+
|
7
|
+
# Contents
|
8
|
+
* [Usage](#usage)
|
9
|
+
* [Validations](#validations)
|
10
|
+
* [Configuration](#configuration)
|
11
|
+
* [Structure of Dependency Manifest](#dependency-manifest-structure)
|
12
|
+
* [License](#license)
|
13
|
+
* [Contributing](#contributing)
|
14
|
+
|
15
|
+
|
16
|
+
# Usage
|
17
|
+
|
18
|
+
tl;dr -- add gem, generate dependency manifest, run spec
|
19
|
+
|
20
|
+
### 0. Add gem to Gemfile
|
21
|
+
|
22
|
+
```
|
23
|
+
gem 'papers'
|
24
|
+
```
|
25
|
+
### 1. Generate Dependency Manifest from your bundled gems and JS
|
26
|
+
|
27
|
+
```
|
28
|
+
$ papers
|
29
|
+
```
|
30
|
+
### 2. Create [Validation Spec](#testing-with-rspec)
|
31
|
+
|
32
|
+
### 3. Run the specs
|
33
|
+
|
34
|
+
```
|
35
|
+
$ rake spec spec/integration/papers_license_validation_spec.rb
|
36
|
+
...
|
37
|
+
Failures:
|
38
|
+
|
39
|
+
1) Papers License Validation finds no errors during license validation
|
40
|
+
Failure/Error: expect(validator.errors).to eq([])
|
41
|
+
|
42
|
+
expected: []
|
43
|
+
got: ["sass-3.2.12 is licensed under GPL, which is not whitelisted"]
|
44
|
+
|
45
|
+
(compared using ==)
|
46
|
+
# ./spec/integration/papers_license_validation_spec.rb:14:in `block (2 levels) in <top (required)>'
|
47
|
+
|
48
|
+
2) Papers License Validation knows and is satisfied by all dependency licenses
|
49
|
+
Failure/Error: expect(validator.valid?).to be_true
|
50
|
+
expected: true value
|
51
|
+
got: false
|
52
|
+
# ./spec/integration/papers_license_validation_spec.rb:9:in `block (2 levels) in <top (required)>'
|
53
|
+
|
54
|
+
Finished in 0.01043 seconds
|
55
|
+
2 examples, 2 failures
|
56
|
+
...
|
57
|
+
```
|
58
|
+
|
59
|
+
# Validations
|
60
|
+
|
61
|
+
## testing with RSpec
|
62
|
+
|
63
|
+
```
|
64
|
+
# => spec/integration/papers_license_validation_spec.rb
|
65
|
+
|
66
|
+
require 'spec_helper'
|
67
|
+
require 'papers'
|
68
|
+
|
69
|
+
describe 'Papers License Validation' do
|
70
|
+
|
71
|
+
let(:validator) { Papers::LicenseValidator.new }
|
72
|
+
|
73
|
+
it 'knows and is satisfied by all dependency licenses' do
|
74
|
+
expect(validator.valid?).to be_true
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'finds no errors during license validation' do
|
78
|
+
validator.valid?
|
79
|
+
expect(validator.errors).to eq([])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
## testing with MiniTest
|
85
|
+
|
86
|
+
```
|
87
|
+
# => test/integration/papers_license_validation_test.rb
|
88
|
+
|
89
|
+
require 'test_helper'
|
90
|
+
require 'papers'
|
91
|
+
|
92
|
+
class PapersLicenseValidationTest < ActiveSupport::TestCase
|
93
|
+
def test_know_and_be_satisfied_by_all_licenses
|
94
|
+
validator = Papers::LicenseValidator.new
|
95
|
+
|
96
|
+
assert validator.valid?, "License validator failed:\n#{validator.errors.join("\n")}"
|
97
|
+
|
98
|
+
assert_equal validator.errors, []
|
99
|
+
end
|
100
|
+
end
|
101
|
+
```
|
102
|
+
|
103
|
+
# Configuration
|
104
|
+
|
105
|
+
The default whitelist allows for permissive licensing for proprietary or commercial usage while avoiding strong copyleft licenses.
|
106
|
+
|
107
|
+
```
|
108
|
+
@license_whitelist = [
|
109
|
+
'MIT',
|
110
|
+
'BSD',
|
111
|
+
'Apache 2.0',
|
112
|
+
'Apache-2.0',
|
113
|
+
'LGPLv2.1',
|
114
|
+
'LGPLv3',
|
115
|
+
'Ruby',
|
116
|
+
'Manually Reviewed',
|
117
|
+
'Unlicensed'
|
118
|
+
]
|
119
|
+
```
|
120
|
+
|
121
|
+
## Available configuration options
|
122
|
+
|
123
|
+
To configure the Papers gem, pass options to ```Papers.configure``` before initialization of LicenseValidator:
|
124
|
+
|
125
|
+
```
|
126
|
+
Papers.configure do |c|
|
127
|
+
c.license_whitelist << 'New Relic'
|
128
|
+
c.manifest_file = File.join('some','other','dependency_manifest.yml')
|
129
|
+
c.validate_gems = true
|
130
|
+
c.validate_javascript = true
|
131
|
+
c.javascript_paths << File.join('some','other','javascripts')
|
132
|
+
end
|
133
|
+
|
134
|
+
validator = Papers::LicenseValidator.new
|
135
|
+
...
|
136
|
+
```
|
137
|
+
|
138
|
+
# Dependency Manifest structure
|
139
|
+
|
140
|
+
```
|
141
|
+
# => config/papers_manifest.yml
|
142
|
+
---
|
143
|
+
gems:
|
144
|
+
sqlite3-1.3.7:
|
145
|
+
license: MIT
|
146
|
+
license_url: https://github.com/luislavena/sqlite3-ruby
|
147
|
+
project_url: https://github.com/luislavena/sqlite3-ruby/blob/master/LICENSE
|
148
|
+
...
|
149
|
+
|
150
|
+
javascripts:
|
151
|
+
app/assets/javascripts/application.js:
|
152
|
+
license: New Relic
|
153
|
+
license_url: http://newrelic.com
|
154
|
+
project_url: http://newrelic.com
|
155
|
+
```
|
156
|
+
|
157
|
+
# License
|
158
|
+
|
159
|
+
The Papers Gem is licensed under the __MIT License__. See [MIT-LICENSE](https://github.com/newrelic/papers/blob/master/MIT-LICENSE) for full text.
|
160
|
+
|
161
|
+
# Contributing
|
162
|
+
|
163
|
+
You are welcome to send pull requests to us - however, by doing so you agree that you are granting New Relic a non-exclusive, non-revokable, no-cost license to use the code, algorithms, patents, and ideas in that code in our products if we so choose. You also agree the code is provided as-is and you provide no warranties as to its fitness or correctness for any purpose.
|
data/Rakefile
ADDED
data/bin/papers
ADDED
data/lib/papers.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'papers/configuration'
|
2
|
+
require 'papers/license_validator'
|
3
|
+
require 'papers/manifest_generator'
|
4
|
+
require 'papers/cli'
|
5
|
+
require 'papers/version'
|
6
|
+
|
7
|
+
require 'papers/dependency_specification'
|
8
|
+
require 'papers/dependency_specification/gem'
|
9
|
+
require 'papers/dependency_specification/javascript'
|
10
|
+
|
11
|
+
module Papers
|
12
|
+
def self.configure(&block)
|
13
|
+
yield configuration
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configuration
|
17
|
+
@config ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
# alias
|
21
|
+
def self.config
|
22
|
+
configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/lib/papers/cli.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Papers
|
4
|
+
|
5
|
+
class CLI
|
6
|
+
|
7
|
+
def run
|
8
|
+
if parse_options[:generate]
|
9
|
+
begin
|
10
|
+
generator = Papers::ManifestGenerator.new
|
11
|
+
generator.generate!
|
12
|
+
rescue Papers::FileExistsError => e
|
13
|
+
warn "Error: 'papers_manifest.yml' already exists at '#{e.message}'. Aborting..."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse_options
|
21
|
+
options = {}
|
22
|
+
OptionParser.new do |opts|
|
23
|
+
opts.banner = "Usage: papers [options]"
|
24
|
+
|
25
|
+
opts.on("-g", "--generate", "Generate papers_manifest.yml") do |v|
|
26
|
+
options[:generate] = v
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on_tail( '-h', '--help', 'Display this screen' ) do |v|
|
30
|
+
p opts
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
@avail_opts = opts
|
34
|
+
end.parse!
|
35
|
+
|
36
|
+
p @avail_opts if options.empty?
|
37
|
+
|
38
|
+
return options
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Papers
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :license_whitelist
|
4
|
+
|
5
|
+
attr_accessor :manifest_file
|
6
|
+
|
7
|
+
attr_accessor :validate_gems
|
8
|
+
|
9
|
+
attr_accessor :validate_javascript
|
10
|
+
|
11
|
+
attr_accessor :javascript_paths
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@license_whitelist = [
|
15
|
+
'MIT',
|
16
|
+
'BSD',
|
17
|
+
'Apache 2.0',
|
18
|
+
'Apache-2.0',
|
19
|
+
'LGPLv2.1',
|
20
|
+
'LGPLv3',
|
21
|
+
'Ruby',
|
22
|
+
'Manually Reviewed',
|
23
|
+
'Unlicensed'
|
24
|
+
]
|
25
|
+
|
26
|
+
@manifest_file = File.join(Dir.pwd, 'config', 'papers_manifest.yml')
|
27
|
+
|
28
|
+
@validate_gems = true
|
29
|
+
@validate_javascript = true
|
30
|
+
|
31
|
+
@javascript_paths = [
|
32
|
+
File.join(Dir.pwd, 'app', 'assets', 'javascripts'),
|
33
|
+
File.join(Dir.pwd, 'lib', 'assets', 'javascripts'),
|
34
|
+
File.join(Dir.pwd, 'vendor', 'assets', 'javascripts')
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_gems?
|
39
|
+
!!@validate_gems
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate_javascript?
|
43
|
+
!!@validate_javascript
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Papers
|
2
|
+
class DependencySpecification
|
3
|
+
attr_accessor :name, :license, :license_url, :project_url
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
@name = options[:name]
|
7
|
+
@license = options[:license]
|
8
|
+
@license_url = options[:license_url]
|
9
|
+
@project_url = options[:project_url]
|
10
|
+
end
|
11
|
+
|
12
|
+
def acceptable_license?
|
13
|
+
Papers.config.license_whitelist.include?(license)
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def self.all_from_manifest(manifest)
|
19
|
+
(manifest[manifest_key] || []).map do |name, info|
|
20
|
+
license_url = info['license_url']
|
21
|
+
license = info['license']
|
22
|
+
project_url = info['project_url']
|
23
|
+
self.new(name: name, license: license, license_url: license_url, project_url: project_url)
|
24
|
+
end.sort { |a, b| a.name.downcase <=> b.name.downcase }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.missing_from_manifest(manifest)
|
28
|
+
introspected.to_set - all_from_manifest(manifest).map(&:name).to_set
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.unknown_in_manifest(manifest)
|
32
|
+
all_from_manifest(manifest).map(&:name).to_set - introspected.to_set
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Papers
|
2
|
+
class Gem < DependencySpecification
|
3
|
+
def pretty_hash
|
4
|
+
{
|
5
|
+
name: name_without_version,
|
6
|
+
license: license,
|
7
|
+
license_url: @license_url,
|
8
|
+
project_url: @project_url
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def name_without_version
|
13
|
+
return @name unless @name.include?('-')
|
14
|
+
@name.split('-')[0..-2].join('-')
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.introspected
|
18
|
+
Bundler.load.specs.map do |spec|
|
19
|
+
# bundler versions aren't controlled by the Gemfile
|
20
|
+
if spec.name == "bundler"
|
21
|
+
spec.name
|
22
|
+
else
|
23
|
+
"#{spec.name}-#{spec.version}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.manifest_key
|
29
|
+
"gems"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Papers
|
2
|
+
class Javascript < DependencySpecification
|
3
|
+
def pretty_hash
|
4
|
+
{
|
5
|
+
name: @name,
|
6
|
+
license: license,
|
7
|
+
license_url: @license_url,
|
8
|
+
project_url: @project_url
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.introspected
|
13
|
+
dirs = Papers.config.javascript_paths
|
14
|
+
|
15
|
+
# TODO: add logic for determining rails. Is Rails.root better than Dir.pwd for such a case?
|
16
|
+
root_regexp = /^#{Regexp.escape Dir.pwd.to_s}\//
|
17
|
+
dirs.map { |dir| Dir["#{dir}/**/*.js"] }.flatten.map { |name| name.sub(root_regexp, '') }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.manifest_key
|
21
|
+
"javascripts"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require 'papers/dependency_specification'
|
4
|
+
require 'papers/dependency_specification/gem'
|
5
|
+
require 'papers/dependency_specification/javascript'
|
6
|
+
|
7
|
+
module Papers
|
8
|
+
class LicenseValidator
|
9
|
+
attr_reader :errors
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@errors = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?
|
16
|
+
@errors = []
|
17
|
+
|
18
|
+
validate_gems if Papers.config.validate_gems?
|
19
|
+
validate_js if Papers.config.validate_javascript?
|
20
|
+
|
21
|
+
@errors.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def manifest
|
25
|
+
@manifest ||= YAML.load_file(Papers.config.manifest_file)
|
26
|
+
end
|
27
|
+
|
28
|
+
def pretty_gem_list
|
29
|
+
Gem.all_from_manifest(manifest).map(&:pretty_hash)
|
30
|
+
end
|
31
|
+
|
32
|
+
def pretty_js_list
|
33
|
+
Javascript.all_from_manifest(manifest).map(&:pretty_hash)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def validate_spec_type(spec_type)
|
39
|
+
spec_type.missing_from_manifest(manifest).each do |name|
|
40
|
+
errors << "#{name} is included in the application, but not in the manifest"
|
41
|
+
end
|
42
|
+
|
43
|
+
spec_type.unknown_in_manifest(manifest).each do |name|
|
44
|
+
errors << "#{name} is included in the manifest, but not in the application"
|
45
|
+
end
|
46
|
+
|
47
|
+
spec_type.all_from_manifest(manifest).each do |spec|
|
48
|
+
unless spec.acceptable_license?
|
49
|
+
errors << "#{spec.name} is licensed under #{spec.license}, which is not whitelisted"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_gems
|
55
|
+
validate_spec_type Gem
|
56
|
+
end
|
57
|
+
|
58
|
+
def validate_js
|
59
|
+
validate_spec_type Javascript
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'yaml'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Papers
|
6
|
+
|
7
|
+
class FileExistsError < StandardError;
|
8
|
+
attr_reader :manifest_path
|
9
|
+
|
10
|
+
def initialize(path)
|
11
|
+
@manifest_path = path
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ManifestGenerator
|
17
|
+
|
18
|
+
def generate!(args = ARGV)
|
19
|
+
@manifest_path = File.join('config','papers_manifest.yml')
|
20
|
+
|
21
|
+
raise Papers::FileExistsError.new(@manifest_path) if manifest_exists?
|
22
|
+
|
23
|
+
begin
|
24
|
+
if FileUtils.mkdir_p(File.dirname(@manifest_path))
|
25
|
+
File.open(@manifest_path, 'w') do |file|
|
26
|
+
file.write(build_header)
|
27
|
+
file.write(YAML.dump(build_manifest))
|
28
|
+
end
|
29
|
+
puts "Created #{@manifest_path}!"
|
30
|
+
end
|
31
|
+
rescue RuntimeError => e
|
32
|
+
warn "Failure! #{e}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def build_manifest
|
39
|
+
manifest = {
|
40
|
+
"gems" => get_installed_gems,
|
41
|
+
"javascripts" => get_installed_javascripts
|
42
|
+
}
|
43
|
+
return manifest
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_installed_gems
|
47
|
+
gems = {}
|
48
|
+
Bundler.load.specs.each do |spec|
|
49
|
+
if spec.name == 'bundler'
|
50
|
+
name_and_version = spec.name
|
51
|
+
else
|
52
|
+
name_and_version = "#{spec.name}-#{spec.version}"
|
53
|
+
end
|
54
|
+
|
55
|
+
gems[name_and_version] = {
|
56
|
+
'license' => spec.license || 'Unknown',
|
57
|
+
'license_url' => nil,
|
58
|
+
'project_url' => spec.homepage || nil
|
59
|
+
# TODO: add support for multiple licenses? some gemspecs have dual licensing
|
60
|
+
}
|
61
|
+
end
|
62
|
+
return gems
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_installed_javascripts
|
66
|
+
js = {}
|
67
|
+
Javascript.introspected.each do |entry|
|
68
|
+
js[entry] = {
|
69
|
+
'license' => 'Unknown',
|
70
|
+
'license_url' => nil,
|
71
|
+
'project_url' => nil
|
72
|
+
}
|
73
|
+
end
|
74
|
+
js.empty? ? nil : js
|
75
|
+
end
|
76
|
+
|
77
|
+
def manifest_exists?
|
78
|
+
!!File.exist?(@manifest_path)
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_header
|
82
|
+
[
|
83
|
+
"# Dependency Manifest for the Papers gem",
|
84
|
+
"# Used to test your gems and javascript against license whitelist",
|
85
|
+
"#",
|
86
|
+
"# http://github.com/newrelic/papers\n"
|
87
|
+
].join("\n")
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
data/papers.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
4
|
+
|
5
|
+
require 'papers/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = 'papers'
|
9
|
+
s.version = Papers::VERSION
|
10
|
+
s.summary = 'Validate the licenses of software dependencies you use'
|
11
|
+
|
12
|
+
s.description = <<-DESCRIPTION
|
13
|
+
Validate that the licenses used by your Ruby project's dependencies (both gems
|
14
|
+
and javascript libraries) conform to a software license whitelist. Don't get
|
15
|
+
caught flat-footed by the GPL.
|
16
|
+
DESCRIPTION
|
17
|
+
|
18
|
+
s.authors = ['Ralph Bodenner', 'Jade Rubick', 'Andrew Bloomgarden', 'Lucas Charles']
|
19
|
+
s.email = 'support@newrelic.com'
|
20
|
+
s.license = 'MIT'
|
21
|
+
s.homepage = 'http://github.com/newrelic/papers'
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split($/)
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split($/)
|
25
|
+
s.executables = s.files.grep(/^bin\//) { |f| File.basename(f) }
|
26
|
+
|
27
|
+
s.require_paths = ['lib']
|
28
|
+
|
29
|
+
# dependencies
|
30
|
+
s.add_dependency 'rake', '~> 10.1'
|
31
|
+
s.add_development_dependency 'rspec', '~> 2.14'
|
32
|
+
end
|
data/spec/papers_spec.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'rspec'
|
3
|
+
require_relative '../lib/papers'
|
4
|
+
|
5
|
+
describe 'Papers' do
|
6
|
+
|
7
|
+
let(:validator) { Papers::LicenseValidator.new }
|
8
|
+
|
9
|
+
it 'validates a manifest with empty values and set of dependencies' do
|
10
|
+
Papers::LicenseValidator.any_instance.stub(:manifest).and_return({
|
11
|
+
"javascripts" => {},
|
12
|
+
"gems" => {}
|
13
|
+
})
|
14
|
+
Papers::Gem.stub(:introspected).and_return([])
|
15
|
+
|
16
|
+
expect(validator.valid?).to be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'detects mismatched gems' do
|
20
|
+
Papers::LicenseValidator.any_instance.stub(:manifest).and_return({
|
21
|
+
"javascripts" => {},
|
22
|
+
"gems" => {
|
23
|
+
"foo-1.2" => {
|
24
|
+
'license' => "MIT",
|
25
|
+
'license_url' => nil,
|
26
|
+
'project_url' => nil
|
27
|
+
},
|
28
|
+
"baz-1.3" => {
|
29
|
+
'license' => "BSD",
|
30
|
+
'license_url' => nil,
|
31
|
+
'project_url' => nil
|
32
|
+
}
|
33
|
+
}
|
34
|
+
})
|
35
|
+
Papers::Gem.stub(:introspected).and_return(["bar-1.2", "baz-1.3"])
|
36
|
+
|
37
|
+
expect(validator.valid?).to be_false
|
38
|
+
|
39
|
+
expect(validator.errors).to eq([
|
40
|
+
"bar-1.2 is included in the application, but not in the manifest",
|
41
|
+
"foo-1.2 is included in the manifest, but not in the application"
|
42
|
+
])
|
43
|
+
|
44
|
+
validator.valid?
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'detects mismatched gem versions' do
|
48
|
+
Papers::Configuration.any_instance.stub(:validate_javascript?).and_return(true)
|
49
|
+
|
50
|
+
expect(validator).to receive(:validate_js).at_least(:once)
|
51
|
+
|
52
|
+
Papers::LicenseValidator.any_instance.stub(:manifest).and_return({
|
53
|
+
"javascripts" => {},
|
54
|
+
"gems" => {
|
55
|
+
"foo-1.2" => {
|
56
|
+
'license' => "MIT",
|
57
|
+
'license_url' => nil,
|
58
|
+
'project_url' => nil
|
59
|
+
},
|
60
|
+
"baz-1.3" => {
|
61
|
+
'license' => "BSD",
|
62
|
+
'license_url' => nil,
|
63
|
+
'project_url' => nil
|
64
|
+
}
|
65
|
+
}
|
66
|
+
})
|
67
|
+
Papers::Gem.stub(:introspected).and_return(["foo-1.2", "baz-1.2"])
|
68
|
+
|
69
|
+
expect(validator.valid?).to be_false
|
70
|
+
|
71
|
+
expect(validator.errors).to eq([
|
72
|
+
"baz-1.2 is included in the application, but not in the manifest",
|
73
|
+
"baz-1.3 is included in the manifest, but not in the application"
|
74
|
+
])
|
75
|
+
validator.valid?
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'is OK with matching gem sets' do
|
79
|
+
Papers::LicenseValidator.any_instance.stub(:manifest).and_return({
|
80
|
+
"javascripts" => {},
|
81
|
+
"gems" => {
|
82
|
+
"foo-1.2" => {
|
83
|
+
'license' => "MIT",
|
84
|
+
'license_url' => nil,
|
85
|
+
'project_url' => nil
|
86
|
+
},
|
87
|
+
"baz-1.3" => {
|
88
|
+
'license' => "BSD",
|
89
|
+
'license_url' => nil,
|
90
|
+
'project_url' => nil
|
91
|
+
}
|
92
|
+
},
|
93
|
+
})
|
94
|
+
Papers::Gem.stub(:introspected).and_return(["foo-1.2", "baz-1.3"])
|
95
|
+
|
96
|
+
expect(validator.valid?).to be_true
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'is OK with matching gem sets but complain about a license issue' do
|
100
|
+
Papers::LicenseValidator.any_instance.stub(:manifest).and_return({
|
101
|
+
"javascripts" => {},
|
102
|
+
"gems" => {
|
103
|
+
"foo-1.2" => {
|
104
|
+
'license' => "MIT",
|
105
|
+
'license_url' => nil,
|
106
|
+
'project_url' => nil
|
107
|
+
},
|
108
|
+
"baz-1.3" => {
|
109
|
+
'license' => "GPL",
|
110
|
+
'license_url' => nil,
|
111
|
+
'project_url' => nil
|
112
|
+
}
|
113
|
+
},
|
114
|
+
})
|
115
|
+
Papers::Gem.stub(:introspected).and_return(["foo-1.2", "baz-1.3"])
|
116
|
+
|
117
|
+
expect(validator.valid?).to be_false
|
118
|
+
|
119
|
+
expect(validator.errors).to eq([
|
120
|
+
"baz-1.3 is licensed under GPL, which is not whitelisted"
|
121
|
+
])
|
122
|
+
|
123
|
+
validator.valid?
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'displays gem licenses in a pretty format without versions' do
|
127
|
+
Papers::LicenseValidator.any_instance.stub(:manifest).and_return({
|
128
|
+
"javascripts" => {},
|
129
|
+
"gems" => {
|
130
|
+
"foo-1.2" => {
|
131
|
+
'license' => "MIT",
|
132
|
+
'license_url' => nil,
|
133
|
+
'project_url' => nil
|
134
|
+
},
|
135
|
+
"baz-1.3" => {
|
136
|
+
'license' => "BSD",
|
137
|
+
'license_url' => nil,
|
138
|
+
'project_url' => nil
|
139
|
+
},
|
140
|
+
"with-hyphens-1.4" => {
|
141
|
+
'license' => "MIT",
|
142
|
+
'license_url' => nil,
|
143
|
+
'project_url' => nil
|
144
|
+
}
|
145
|
+
},
|
146
|
+
})
|
147
|
+
expect(validator.pretty_gem_list).to eq([
|
148
|
+
{
|
149
|
+
:name=>"baz",
|
150
|
+
:license=>"BSD",
|
151
|
+
:license_url => nil,
|
152
|
+
:project_url => nil
|
153
|
+
},
|
154
|
+
{
|
155
|
+
:name=>"foo",
|
156
|
+
:license=>"MIT",
|
157
|
+
:license_url => nil,
|
158
|
+
:project_url => nil
|
159
|
+
},
|
160
|
+
{
|
161
|
+
:name=>"with-hyphens",
|
162
|
+
:license=>"MIT",
|
163
|
+
:license_url => nil,
|
164
|
+
:project_url => nil
|
165
|
+
}
|
166
|
+
])
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'displays JS libraries in a pretty format without versions' do
|
170
|
+
Papers::LicenseValidator.any_instance.stub(:manifest).and_return({
|
171
|
+
"javascripts" => {
|
172
|
+
"/path/to/foo.js" => {
|
173
|
+
'license' => "MIT",
|
174
|
+
'license_url' => nil,
|
175
|
+
'project_url' => nil
|
176
|
+
},
|
177
|
+
"/path/to/newrelic.js" => {
|
178
|
+
'license' => "New Relic",
|
179
|
+
'license_url' => nil,
|
180
|
+
'project_url' => nil
|
181
|
+
}
|
182
|
+
},
|
183
|
+
"gems" => {}
|
184
|
+
})
|
185
|
+
expect(validator.pretty_js_list).to eq([
|
186
|
+
{
|
187
|
+
:name =>"/path/to/foo.js",
|
188
|
+
:license =>"MIT",
|
189
|
+
:license_url => nil,
|
190
|
+
:project_url => nil
|
191
|
+
},
|
192
|
+
{
|
193
|
+
:name =>"/path/to/newrelic.js",
|
194
|
+
:license =>"New Relic",
|
195
|
+
:license_url => nil,
|
196
|
+
:project_url => nil
|
197
|
+
}
|
198
|
+
])
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'displays the gem name when the gemspec does not specify a version' do
|
202
|
+
gemspec = Papers::Gem.new(name: 'foo')
|
203
|
+
expect('foo').to eq(gemspec.name_without_version)
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: papers
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ralph Bodenner
|
8
|
+
- Jade Rubick
|
9
|
+
- Andrew Bloomgarden
|
10
|
+
- Lucas Charles
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2013-12-10 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rake
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ~>
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '10.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '10.1'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ~>
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '2.14'
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.14'
|
44
|
+
description: ! 'Validate that the licenses used by your Ruby project''s dependencies
|
45
|
+
(both gems
|
46
|
+
|
47
|
+
and javascript libraries) conform to a software license whitelist. Don''t get
|
48
|
+
|
49
|
+
caught flat-footed by the GPL.
|
50
|
+
|
51
|
+
'
|
52
|
+
email: support@newrelic.com
|
53
|
+
executables:
|
54
|
+
- papers
|
55
|
+
extensions: []
|
56
|
+
extra_rdoc_files: []
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- Gemfile
|
60
|
+
- MIT-LICENSE
|
61
|
+
- README.md
|
62
|
+
- Rakefile
|
63
|
+
- bin/papers
|
64
|
+
- lib/papers.rb
|
65
|
+
- lib/papers/cli.rb
|
66
|
+
- lib/papers/configuration.rb
|
67
|
+
- lib/papers/dependency_specification.rb
|
68
|
+
- lib/papers/dependency_specification/gem.rb
|
69
|
+
- lib/papers/dependency_specification/javascript.rb
|
70
|
+
- lib/papers/license_validator.rb
|
71
|
+
- lib/papers/manifest_generator.rb
|
72
|
+
- lib/papers/version.rb
|
73
|
+
- papers.gemspec
|
74
|
+
- spec/papers_spec.rb
|
75
|
+
homepage: http://github.com/newrelic/papers
|
76
|
+
licenses:
|
77
|
+
- MIT
|
78
|
+
metadata: {}
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
requirements: []
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 2.1.5
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: Validate the licenses of software dependencies you use
|
99
|
+
test_files:
|
100
|
+
- spec/papers_spec.rb
|
101
|
+
has_rdoc:
|