diffense 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +25 -0
- data/.coditsu/ci.yml +3 -0
- data/.gitignore +51 -0
- data/.ruby-version +1 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +20 -0
- data/LICENSE +165 -0
- data/README.md +19 -0
- data/certs/mensfeld.pem +25 -0
- data/diffense.gemspec +32 -0
- data/lib/bundler/security.rb +69 -0
- data/lib/bundler/security/commands.rb +13 -0
- data/lib/bundler/security/config/fetcher.rb +29 -0
- data/lib/bundler/security/config/file_finder.rb +43 -0
- data/lib/bundler/security/errors.rb +15 -0
- data/lib/bundler/security/version.rb +10 -0
- data/lib/bundler/security/voting.rb +72 -0
- data/lib/bundler/security/voting/build_failure.rb +59 -0
- data/lib/bundler/security/voting/build_success.rb +55 -0
- data/lib/bundler/security/voting/build_unsafe_gem.rb +73 -0
- data/lib/bundler/security/voting/gem_policy.rb +67 -0
- data/lib/bundler/security/voting/remote_policy.rb +24 -0
- data/lib/bundler/security/voting/request.rb +88 -0
- data/lib/bundler/security/voting/versions/local.rb +107 -0
- data/lib/bundler/security/voting/versions/remote.rb +60 -0
- data/plugins.rb +5 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1421ed8ba3bcde63fa7a0e15afc0448aa26e399eff063583859eddda19b99bda
|
4
|
+
data.tar.gz: 5c60bdf597124c0246f0d96a503858fa8c5b43b7fa2624f9c2dad5a8cf86fae8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '049401c348f364422039d6979e0cae7d6eb8d67164868cffb859287839147c920c74e37cc45ed4f9995a37c22d466fc740bd876979a60f58b3d876530e470743'
|
7
|
+
data.tar.gz: 5d2e5fad1bc5a9ed30e6b64cf55f7755b5835a98d56f0063ae1bb875efaeddf87c61e8b081f2901fc9fdffc987a963ec0df4a797da7b58abe746bd6a2b81a52a
|
@@ -0,0 +1,25 @@
|
|
1
|
+
version: 2
|
2
|
+
|
3
|
+
jobs:
|
4
|
+
coditsu:
|
5
|
+
machine: true
|
6
|
+
steps:
|
7
|
+
- checkout
|
8
|
+
- run: \curl -sSL https://api.coditsu.io/run/ci | bash
|
9
|
+
|
10
|
+
workflows:
|
11
|
+
version: 2
|
12
|
+
build:
|
13
|
+
jobs:
|
14
|
+
- coditsu
|
15
|
+
|
16
|
+
nightly:
|
17
|
+
triggers:
|
18
|
+
- schedule:
|
19
|
+
cron: '0 0 * * *'
|
20
|
+
filters:
|
21
|
+
branches:
|
22
|
+
only:
|
23
|
+
- master
|
24
|
+
jobs:
|
25
|
+
- coditsu
|
data/.coditsu/ci.yml
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
|
13
|
+
# Used by dotenv library to load environment variables.
|
14
|
+
# .env
|
15
|
+
|
16
|
+
## Specific to RubyMotion:
|
17
|
+
.dat*
|
18
|
+
.repl_history
|
19
|
+
build/
|
20
|
+
*.bridgesupport
|
21
|
+
build-iPhoneOS/
|
22
|
+
build-iPhoneSimulator/
|
23
|
+
|
24
|
+
## Specific to RubyMotion (use of CocoaPods):
|
25
|
+
#
|
26
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
27
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
28
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
29
|
+
#
|
30
|
+
# vendor/Pods/
|
31
|
+
|
32
|
+
## Documentation cache and generated files:
|
33
|
+
/.yardoc/
|
34
|
+
/_yardoc/
|
35
|
+
/doc/
|
36
|
+
/rdoc/
|
37
|
+
|
38
|
+
## Environment normalization:
|
39
|
+
/.bundle/
|
40
|
+
/vendor/bundle
|
41
|
+
/lib/bundler/man/
|
42
|
+
|
43
|
+
# for a library or gem, you might want to ignore these files since the code is
|
44
|
+
# intended to run in multiple environments; otherwise, check them in:
|
45
|
+
# Gemfile.lock
|
46
|
+
# .ruby-version
|
47
|
+
# .ruby-gemset
|
48
|
+
|
49
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
50
|
+
.rvmrc
|
51
|
+
.coditsu/local.yml
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.7.1
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
GNU LESSER GENERAL PUBLIC LICENSE
|
2
|
+
Version 3, 29 June 2007
|
3
|
+
|
4
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
5
|
+
Everyone is permitted to copy and distribute verbatim copies
|
6
|
+
of this license document, but changing it is not allowed.
|
7
|
+
|
8
|
+
|
9
|
+
This version of the GNU Lesser General Public License incorporates
|
10
|
+
the terms and conditions of version 3 of the GNU General Public
|
11
|
+
License, supplemented by the additional permissions listed below.
|
12
|
+
|
13
|
+
0. Additional Definitions.
|
14
|
+
|
15
|
+
As used herein, "this License" refers to version 3 of the GNU Lesser
|
16
|
+
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
17
|
+
General Public License.
|
18
|
+
|
19
|
+
"The Library" refers to a covered work governed by this License,
|
20
|
+
other than an Application or a Combined Work as defined below.
|
21
|
+
|
22
|
+
An "Application" is any work that makes use of an interface provided
|
23
|
+
by the Library, but which is not otherwise based on the Library.
|
24
|
+
Defining a subclass of a class defined by the Library is deemed a mode
|
25
|
+
of using an interface provided by the Library.
|
26
|
+
|
27
|
+
A "Combined Work" is a work produced by combining or linking an
|
28
|
+
Application with the Library. The particular version of the Library
|
29
|
+
with which the Combined Work was made is also called the "Linked
|
30
|
+
Version".
|
31
|
+
|
32
|
+
The "Minimal Corresponding Source" for a Combined Work means the
|
33
|
+
Corresponding Source for the Combined Work, excluding any source code
|
34
|
+
for portions of the Combined Work that, considered in isolation, are
|
35
|
+
based on the Application, and not on the Linked Version.
|
36
|
+
|
37
|
+
The "Corresponding Application Code" for a Combined Work means the
|
38
|
+
object code and/or source code for the Application, including any data
|
39
|
+
and utility programs needed for reproducing the Combined Work from the
|
40
|
+
Application, but excluding the System Libraries of the Combined Work.
|
41
|
+
|
42
|
+
1. Exception to Section 3 of the GNU GPL.
|
43
|
+
|
44
|
+
You may convey a covered work under sections 3 and 4 of this License
|
45
|
+
without being bound by section 3 of the GNU GPL.
|
46
|
+
|
47
|
+
2. Conveying Modified Versions.
|
48
|
+
|
49
|
+
If you modify a copy of the Library, and, in your modifications, a
|
50
|
+
facility refers to a function or data to be supplied by an Application
|
51
|
+
that uses the facility (other than as an argument passed when the
|
52
|
+
facility is invoked), then you may convey a copy of the modified
|
53
|
+
version:
|
54
|
+
|
55
|
+
a) under this License, provided that you make a good faith effort to
|
56
|
+
ensure that, in the event an Application does not supply the
|
57
|
+
function or data, the facility still operates, and performs
|
58
|
+
whatever part of its purpose remains meaningful, or
|
59
|
+
|
60
|
+
b) under the GNU GPL, with none of the additional permissions of
|
61
|
+
this License applicable to that copy.
|
62
|
+
|
63
|
+
3. Object Code Incorporating Material from Library Header Files.
|
64
|
+
|
65
|
+
The object code form of an Application may incorporate material from
|
66
|
+
a header file that is part of the Library. You may convey such object
|
67
|
+
code under terms of your choice, provided that, if the incorporated
|
68
|
+
material is not limited to numerical parameters, data structure
|
69
|
+
layouts and accessors, or small macros, inline functions and templates
|
70
|
+
(ten or fewer lines in length), you do both of the following:
|
71
|
+
|
72
|
+
a) Give prominent notice with each copy of the object code that the
|
73
|
+
Library is used in it and that the Library and its use are
|
74
|
+
covered by this License.
|
75
|
+
|
76
|
+
b) Accompany the object code with a copy of the GNU GPL and this license
|
77
|
+
document.
|
78
|
+
|
79
|
+
4. Combined Works.
|
80
|
+
|
81
|
+
You may convey a Combined Work under terms of your choice that,
|
82
|
+
taken together, effectively do not restrict modification of the
|
83
|
+
portions of the Library contained in the Combined Work and reverse
|
84
|
+
engineering for debugging such modifications, if you also do each of
|
85
|
+
the following:
|
86
|
+
|
87
|
+
a) Give prominent notice with each copy of the Combined Work that
|
88
|
+
the Library is used in it and that the Library and its use are
|
89
|
+
covered by this License.
|
90
|
+
|
91
|
+
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
92
|
+
document.
|
93
|
+
|
94
|
+
c) For a Combined Work that displays copyright notices during
|
95
|
+
execution, include the copyright notice for the Library among
|
96
|
+
these notices, as well as a reference directing the user to the
|
97
|
+
copies of the GNU GPL and this license document.
|
98
|
+
|
99
|
+
d) Do one of the following:
|
100
|
+
|
101
|
+
0) Convey the Minimal Corresponding Source under the terms of this
|
102
|
+
License, and the Corresponding Application Code in a form
|
103
|
+
suitable for, and under terms that permit, the user to
|
104
|
+
recombine or relink the Application with a modified version of
|
105
|
+
the Linked Version to produce a modified Combined Work, in the
|
106
|
+
manner specified by section 6 of the GNU GPL for conveying
|
107
|
+
Corresponding Source.
|
108
|
+
|
109
|
+
1) Use a suitable shared library mechanism for linking with the
|
110
|
+
Library. A suitable mechanism is one that (a) uses at run time
|
111
|
+
a copy of the Library already present on the user's computer
|
112
|
+
system, and (b) will operate properly with a modified version
|
113
|
+
of the Library that is interface-compatible with the Linked
|
114
|
+
Version.
|
115
|
+
|
116
|
+
e) Provide Installation Information, but only if you would otherwise
|
117
|
+
be required to provide such information under section 6 of the
|
118
|
+
GNU GPL, and only to the extent that such information is
|
119
|
+
necessary to install and execute a modified version of the
|
120
|
+
Combined Work produced by recombining or relinking the
|
121
|
+
Application with a modified version of the Linked Version. (If
|
122
|
+
you use option 4d0, the Installation Information must accompany
|
123
|
+
the Minimal Corresponding Source and Corresponding Application
|
124
|
+
Code. If you use option 4d1, you must provide the Installation
|
125
|
+
Information in the manner specified by section 6 of the GNU GPL
|
126
|
+
for conveying Corresponding Source.)
|
127
|
+
|
128
|
+
5. Combined Libraries.
|
129
|
+
|
130
|
+
You may place library facilities that are a work based on the
|
131
|
+
Library side by side in a single library together with other library
|
132
|
+
facilities that are not Applications and are not covered by this
|
133
|
+
License, and convey such a combined library under terms of your
|
134
|
+
choice, if you do both of the following:
|
135
|
+
|
136
|
+
a) Accompany the combined library with a copy of the same work based
|
137
|
+
on the Library, uncombined with any other library facilities,
|
138
|
+
conveyed under the terms of this License.
|
139
|
+
|
140
|
+
b) Give prominent notice with the combined library that part of it
|
141
|
+
is a work based on the Library, and explaining where to find the
|
142
|
+
accompanying uncombined form of the same work.
|
143
|
+
|
144
|
+
6. Revised Versions of the GNU Lesser General Public License.
|
145
|
+
|
146
|
+
The Free Software Foundation may publish revised and/or new versions
|
147
|
+
of the GNU Lesser General Public License from time to time. Such new
|
148
|
+
versions will be similar in spirit to the present version, but may
|
149
|
+
differ in detail to address new problems or concerns.
|
150
|
+
|
151
|
+
Each version is given a distinguishing version number. If the
|
152
|
+
Library as you received it specifies that a certain numbered version
|
153
|
+
of the GNU Lesser General Public License "or any later version"
|
154
|
+
applies to it, you have the option of following the terms and
|
155
|
+
conditions either of that published version or of any later version
|
156
|
+
published by the Free Software Foundation. If the Library as you
|
157
|
+
received it does not specify a version number of the GNU Lesser
|
158
|
+
General Public License, you may choose any version of the GNU Lesser
|
159
|
+
General Public License ever published by the Free Software Foundation.
|
160
|
+
|
161
|
+
If the Library as you received it specifies that a proxy can decide
|
162
|
+
whether future versions of the GNU Lesser General Public License shall
|
163
|
+
apply, that proxy's public statement of acceptance of any version is
|
164
|
+
permanent authorization for you to choose that version for the
|
165
|
+
Library.
|
data/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Coditsu::BundlerSecurity
|
2
|
+
|
3
|
+
A bundler plugin that checks if it's secure to install your dependencies.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
$ bundle plugin install bundler-security
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
It automatically hooks up to your `bundle` commands.
|
12
|
+
|
13
|
+
## Contributing
|
14
|
+
|
15
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/coditsu/bundler-security.
|
16
|
+
|
17
|
+
## License
|
18
|
+
|
19
|
+
The gem is available as open source under the terms of the [LGPL-3.0](https://github.com/coditsu/bundler-security/blob/master/LICENSE).
|
data/certs/mensfeld.pem
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIEODCCAqCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDDBhtYWNp
|
3
|
+
ZWovREM9bWVuc2ZlbGQvREM9cGwwHhcNMTkwNzMwMTQ1NDU0WhcNMjAwNzI5MTQ1
|
4
|
+
NDU0WjAjMSEwHwYDVQQDDBhtYWNpZWovREM9bWVuc2ZlbGQvREM9cGwwggGiMA0G
|
5
|
+
CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC9fCwtaHZG2SyyNXiH8r0QbJQx/xxl
|
6
|
+
dkvwWz9QGJO+O8rEx20FB1Ab+MVkfOscwIv5jWpmk1U9whzDPl1uFtIbgu+sk+Zb
|
7
|
+
uQlZyK/DPN6c+/BbBL+RryTBRyvkPLoCVwm7uxc/JZ1n4AI6eF4cCZ2ieZ9QgQbU
|
8
|
+
MQs2QPqs9hT50Ez/40GnOdadVfiDDGz+NME2C4ms0BriXwZ1tcRTfJIHe2xjIbbb
|
9
|
+
y5qRGfsLKcgMzvLQR24olixyX1MR0s4+Wveq3QL/gBhL4veUcv+UABJA8IJR0kyB
|
10
|
+
seHHutusiwZ1v3SjjjW1xLLrc2ARV0mgCb0WaK2T4iA3oFTGLh6Ydz8LNl31KQFv
|
11
|
+
94nRd8IhmJxrhQ6dQ/WT9IXoa5S9lfT5lPJeINemH4/6QPABzf9W2IZlCdI9wCdB
|
12
|
+
TBaw57MKneGAYZiKjw6OALSy2ltQUCl3RqFl3VP7n8uFy1U987Q5VIIQ3O1UUsQD
|
13
|
+
Oe/h+r7GUU4RSPKgPlrwvW9bD/UQ+zF51v8CAwEAAaN3MHUwCQYDVR0TBAIwADAL
|
14
|
+
BgNVHQ8EBAMCBLAwHQYDVR0OBBYEFJNIBHdfEUD7TqHqIer2YhWaWhwcMB0GA1Ud
|
15
|
+
EQQWMBSBEm1hY2llakBtZW5zZmVsZC5wbDAdBgNVHRIEFjAUgRJtYWNpZWpAbWVu
|
16
|
+
c2ZlbGQucGwwDQYJKoZIhvcNAQELBQADggGBAKA4eqko6BTNhlysip6rfBkVTGri
|
17
|
+
ZXsL+kRb2hLvsQJS/kLyM21oMlu+LN0aPj3qEFR8mE/YeDD8rLAfruBRTltPNbR7
|
18
|
+
xA5eE1gkxY5LfExUtK3b2wPqfmo7mZgfcsMwfYg/tUXw1WpBCnrhAJodpGH6SXmp
|
19
|
+
A40qFUZst0vjiOoO+aTblIHPmMJXoZ3K42dTlNKlEiDKUWMRKSgpjjYGEYalFNWI
|
20
|
+
hHfCz2r8L2t+dYdMZg1JGbEkq4ADGsAA8ioZIpJd7V4hI17u5TCdi7X5wh/0gN0E
|
21
|
+
CgP+nLox3D+l2q0QuQEkayr+auFYkzTCkF+BmEk1D0Ru4mcf3F4CJvEmW4Pzbjqt
|
22
|
+
i1tsCWPtJ4E/UUKnKaWKqGbjrjHJ0MuShYzHkodox5IOiCXIQg+1+YSzfXUV6WEK
|
23
|
+
KJG/fhg1JV5vVDdVy6x+tv5SQ5ctU0feCsVfESi3rE3zRd+nvzE9HcZ5aXeL1UtJ
|
24
|
+
nT5Xrioegu2w1jPyVEgyZgTZC5rvD0nNS5sFNQ==
|
25
|
+
-----END CERTIFICATE-----
|
data/diffense.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'bundler/security/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'diffense'
|
9
|
+
spec.version = Bundler::Security::VERSION
|
10
|
+
spec.authors = ['Tomasz Pajor']
|
11
|
+
spec.email = ['tomek@coditsu.io']
|
12
|
+
|
13
|
+
spec.summary = 'Bundler Security'
|
14
|
+
spec.description = 'Bundler Security'
|
15
|
+
spec.homepage = Bundler::Security::HOMEPAGE
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
19
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
20
|
+
if spec.respond_to?(:metadata)
|
21
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
22
|
+
else
|
23
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
24
|
+
'public gem pushes.'
|
25
|
+
end
|
26
|
+
|
27
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
|
28
|
+
spec.require_paths = %w[lib]
|
29
|
+
|
30
|
+
spec.add_development_dependency 'bundler'
|
31
|
+
spec.add_development_dependency 'rake'
|
32
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
%w[
|
4
|
+
bundler
|
5
|
+
].each(&method(:require))
|
6
|
+
|
7
|
+
%w[
|
8
|
+
config/fetcher
|
9
|
+
config/file_finder
|
10
|
+
commands
|
11
|
+
version
|
12
|
+
errors
|
13
|
+
voting
|
14
|
+
].each { |file| require "bundler/security/#{file}" }
|
15
|
+
|
16
|
+
%w[
|
17
|
+
build_unsafe_gem
|
18
|
+
build_success
|
19
|
+
build_failure
|
20
|
+
gem_policy
|
21
|
+
remote_policy
|
22
|
+
request
|
23
|
+
versions/local
|
24
|
+
versions/remote
|
25
|
+
].each { |file| require "bundler/security/voting/#{file}" }
|
26
|
+
|
27
|
+
# Bundler main namespace
|
28
|
+
module Bundler
|
29
|
+
# Plugin responsible for safe gem installation
|
30
|
+
module Security
|
31
|
+
class << self
|
32
|
+
# Registers the plugin and add before install all hook
|
33
|
+
def register
|
34
|
+
return if defined?(@registered) && @registered
|
35
|
+
|
36
|
+
@registered = true
|
37
|
+
|
38
|
+
Bundler::Plugin.add_hook('before-install-all') do |_|
|
39
|
+
Bundler::Security::Voting.call(
|
40
|
+
command,
|
41
|
+
build_definition
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Build clean instance of bundler definition, as we don't want to pollute the main one
|
47
|
+
#
|
48
|
+
# @return [Bundler::Definition]
|
49
|
+
def build_definition
|
50
|
+
Bundler.configure
|
51
|
+
|
52
|
+
Bundler::Definition.build(
|
53
|
+
Bundler.default_gemfile,
|
54
|
+
Bundler.default_lockfile,
|
55
|
+
true
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Command that was run with bundle
|
60
|
+
#
|
61
|
+
# @return [String]
|
62
|
+
def command
|
63
|
+
ARGV
|
64
|
+
.first
|
65
|
+
.then { |value| value || Bundler::Security::Commands::INSTALL }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Security
|
5
|
+
# Module for all the components related to setting up the config
|
6
|
+
module Config
|
7
|
+
# Class responsible for fetching the config from .coditsu.yml
|
8
|
+
module Fetcher
|
9
|
+
class << self
|
10
|
+
# @param build_path [String] path of the current build
|
11
|
+
# @return [OpenStruct] open struct with config details
|
12
|
+
# @example
|
13
|
+
# details = Fetcher.new.call('./')
|
14
|
+
# details.build_path #=> './'
|
15
|
+
def call(build_path)
|
16
|
+
FileFinder
|
17
|
+
.call(build_path)
|
18
|
+
.then(&File.method(:read))
|
19
|
+
.then(&ERB.method(:new))
|
20
|
+
.then(&:result)
|
21
|
+
.then(&YAML.method(:load))
|
22
|
+
.then { |data| data.merge(build_path: build_path) }
|
23
|
+
.then(&OpenStruct.method(:new))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Security
|
5
|
+
module Config
|
6
|
+
# Class used to figure out the file from which we should load the settings
|
7
|
+
module FileFinder
|
8
|
+
# Names of the files or paths where we will look for the settings
|
9
|
+
#
|
10
|
+
# @note We do the double dot trick, to look outside of the current dir because when
|
11
|
+
# executed from a docker container, we copy the local uncommitted settings into the
|
12
|
+
# dir above the app location not to pollute the reset state of the git repo
|
13
|
+
#
|
14
|
+
# @note Order is important, as for local env we should load from
|
15
|
+
# local file (if present first)
|
16
|
+
FILE_NAMES = %w[
|
17
|
+
.coditsu/local.yml
|
18
|
+
.coditsu/ci.yml
|
19
|
+
.coditsu/*.yml
|
20
|
+
.coditsu.yml
|
21
|
+
].map { |name| ["../#{name}", name] }.tap(&:flatten!).freeze
|
22
|
+
|
23
|
+
private_constant :FILE_NAMES
|
24
|
+
|
25
|
+
class << self
|
26
|
+
# Looks for coditsu settings file for a given env
|
27
|
+
#
|
28
|
+
# @param build_path [String] path of the current build
|
29
|
+
#
|
30
|
+
# @return [String] path to the file from which we should load all the settings
|
31
|
+
def call(build_path)
|
32
|
+
FILE_NAMES
|
33
|
+
.map { |name| File.join(build_path, name) }
|
34
|
+
.map { |name| Dir[name] }
|
35
|
+
.find { |selection| !selection.empty? }
|
36
|
+
.tap { |path| path || raise(Errors::MissingConfigurationFile) }
|
37
|
+
.first
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Security
|
5
|
+
# Build runner app errors
|
6
|
+
module Errors
|
7
|
+
# Base error class from which all the errors should inherit
|
8
|
+
BaseError = Class.new(StandardError)
|
9
|
+
# Raised when we couldn't find a valid configuration file
|
10
|
+
MissingConfigurationFile = Class.new(BaseError)
|
11
|
+
# When we receive invalid remote versions type
|
12
|
+
InvalidRemoteVersionsType = Class.new(BaseError)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Security
|
5
|
+
# Verifies voting verdicts for gems
|
6
|
+
module Voting
|
7
|
+
class << self
|
8
|
+
# Build verdict
|
9
|
+
#
|
10
|
+
# @param command [String] either install or update
|
11
|
+
# @param definition [Bundler::Definition] definition for your source
|
12
|
+
def call(command, definition)
|
13
|
+
remote_data(command, definition)
|
14
|
+
.then { |policy, gems| [policy, build_gems(policy, gems)] }
|
15
|
+
.then { |policy, errors| build_status(policy.type, command, errors) }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Build gems that don't have enough approvals
|
19
|
+
#
|
20
|
+
# @param policy [Voting::RemotePolicy] remote policy settings
|
21
|
+
# @param gems [Hash] remote gem statistics
|
22
|
+
#
|
23
|
+
# @return [Array] gems that don't have enough approvals based on remote policy
|
24
|
+
def build_gems(policy, gems)
|
25
|
+
gems.each_with_object([]) do |(name, data), errors|
|
26
|
+
gem_policy = GemPolicy.new(name, data, policy)
|
27
|
+
|
28
|
+
next if gem_policy.approved?
|
29
|
+
next unless gem_policy.rejected?
|
30
|
+
|
31
|
+
errors << BuildUnsafeGem.call(gem_policy)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Fetch data from the differ
|
36
|
+
#
|
37
|
+
# @param command [String] either install or update
|
38
|
+
# @param definition [Bundler::Definition]
|
39
|
+
def remote_data(command, definition)
|
40
|
+
Versions::Remote
|
41
|
+
.call(command, definition)
|
42
|
+
.yield_self { |response| [build_remote_policy(response['policy']), response['gems']] }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Build remote policy based on Coditsu differ settings
|
46
|
+
#
|
47
|
+
# @param policy [Hash] remote policy settings
|
48
|
+
#
|
49
|
+
# @return [Voting::RemotePolicy]
|
50
|
+
def build_remote_policy(policy)
|
51
|
+
RemotePolicy.new(
|
52
|
+
policy['type'], policy['threshold']
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Build security verdict
|
57
|
+
#
|
58
|
+
# @param remote_policy_type [String]
|
59
|
+
# @param command [String] either install or update
|
60
|
+
# @param errors [Array] detected security errors
|
61
|
+
def build_status(remote_policy_type, command, errors)
|
62
|
+
if errors.empty?
|
63
|
+
BuildSuccess.call(remote_policy_type, command)
|
64
|
+
else
|
65
|
+
BuildFailure.call(remote_policy_type, command, errors)
|
66
|
+
exit 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Security
|
5
|
+
module Voting
|
6
|
+
# Build failure security verdict
|
7
|
+
module BuildFailure
|
8
|
+
class << self
|
9
|
+
# Prints failure security verdict
|
10
|
+
#
|
11
|
+
# @param policy_type [String]
|
12
|
+
# @param command [String] either install or update
|
13
|
+
# @param errors [Array] detected security errors
|
14
|
+
def call(policy_type, command, errors)
|
15
|
+
Bundler.ui.error(
|
16
|
+
build(policy_type, command, errors)
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Builds failure security verdict message
|
21
|
+
#
|
22
|
+
# @param policy_type [String]
|
23
|
+
# @param command [String] either install or update
|
24
|
+
# @param errors [Array] detected security errors
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
def build(policy_type, command, errors)
|
28
|
+
[
|
29
|
+
"\n",
|
30
|
+
message_type(policy_type),
|
31
|
+
", blocking #{command}",
|
32
|
+
"\n\n",
|
33
|
+
errors.join("\n"),
|
34
|
+
"\n\n"
|
35
|
+
].join
|
36
|
+
end
|
37
|
+
|
38
|
+
# Builds a message based on policy type
|
39
|
+
#
|
40
|
+
# @param policy_type [String]
|
41
|
+
#
|
42
|
+
# @return [String]
|
43
|
+
#
|
44
|
+
# @raise InvalidPolicyType if policy type was not recognized
|
45
|
+
def message_type(policy_type)
|
46
|
+
case policy_type
|
47
|
+
when 'organization'
|
48
|
+
'Not enough reviews on your organization'
|
49
|
+
when 'community'
|
50
|
+
'Not enough reviews in the community'
|
51
|
+
else
|
52
|
+
raise InvalidPolicyType, policy_type
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Security
|
5
|
+
module Voting
|
6
|
+
# Build successful security verdict
|
7
|
+
module BuildSuccess
|
8
|
+
class << self
|
9
|
+
# Prints successful security verdict
|
10
|
+
#
|
11
|
+
# @param policy_type [String]
|
12
|
+
# @param command [String] either install or update
|
13
|
+
def call(policy_type, command)
|
14
|
+
Bundler.ui.confirm(
|
15
|
+
build(policy_type, command)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Builds successful security verdict message
|
20
|
+
#
|
21
|
+
# @param policy_type [String]
|
22
|
+
# @param command [String] either install or update
|
23
|
+
#
|
24
|
+
# @return [String]
|
25
|
+
def build(policy_type, command)
|
26
|
+
[
|
27
|
+
"\n",
|
28
|
+
message_type(policy_type),
|
29
|
+
", commencing #{command}",
|
30
|
+
"\n\n"
|
31
|
+
].join
|
32
|
+
end
|
33
|
+
|
34
|
+
# Builds a message based on policy type
|
35
|
+
#
|
36
|
+
# @param policy_type [String]
|
37
|
+
#
|
38
|
+
# @return [String]
|
39
|
+
#
|
40
|
+
# @raise InvalidPolicyType if policy type was not recognized
|
41
|
+
def message_type(policy_type)
|
42
|
+
case policy_type
|
43
|
+
when 'organization'
|
44
|
+
'All gems approved by your organization'
|
45
|
+
when 'community'
|
46
|
+
'All gems approved by community'
|
47
|
+
else
|
48
|
+
raise InvalidPolicyType, policy_type
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Security
|
5
|
+
module Voting
|
6
|
+
# Module responsible for building unsafe gem details
|
7
|
+
module BuildUnsafeGem
|
8
|
+
# Differ url
|
9
|
+
DIFF_URL = 'https://diff.coditsu.io/gems'
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# Builds details of an unsafe gem
|
13
|
+
#
|
14
|
+
# @param gem_policy [Voting::GemPolicy]
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
def call(gem_policy)
|
18
|
+
[
|
19
|
+
gem_policy.name,
|
20
|
+
gem_policy.new_version? ? gem_policy.new_version : gem_policy.current_version,
|
21
|
+
'-',
|
22
|
+
[
|
23
|
+
approved(gem_policy.remote_policy, gem_policy),
|
24
|
+
rejected(gem_policy.remote_policy, gem_policy)
|
25
|
+
].compact.join(', ')
|
26
|
+
].join(' ')
|
27
|
+
end
|
28
|
+
|
29
|
+
# Builds safe details
|
30
|
+
#
|
31
|
+
# @param remote_policy [Voting::RemotePolicy]
|
32
|
+
# @param gem_policy [Voting::GemPolicy]
|
33
|
+
#
|
34
|
+
# @return [String]
|
35
|
+
def approved(remote_policy, gem_policy)
|
36
|
+
return if gem_policy.approved?
|
37
|
+
|
38
|
+
array = []
|
39
|
+
array << "approved (#{gem_policy.approved} expected #{remote_policy.approved})"
|
40
|
+
array << differ_url(gem_policy)
|
41
|
+
array.join(' ')
|
42
|
+
end
|
43
|
+
|
44
|
+
# Builds malicious details
|
45
|
+
#
|
46
|
+
# @param remote_policy [Voting::RemotePolicy]
|
47
|
+
# @param gem_policy [Voting::GemPolicy]
|
48
|
+
#
|
49
|
+
# @return [String]
|
50
|
+
def rejected(remote_policy, gem_policy)
|
51
|
+
return if gem_policy.rejected?
|
52
|
+
|
53
|
+
array = []
|
54
|
+
array << "rejected (#{gem_policy.rejected} expected #{remote_policy.rejected})"
|
55
|
+
array << differ_url(gem_policy)
|
56
|
+
array.join(' ')
|
57
|
+
end
|
58
|
+
|
59
|
+
# Builds differ url for gem with version details
|
60
|
+
#
|
61
|
+
# @param gem_policy [Voting::GemPolicy]
|
62
|
+
#
|
63
|
+
# @return [String]
|
64
|
+
def differ_url(gem_policy)
|
65
|
+
array = [DIFF_URL, gem_policy.name, gem_policy.current_version]
|
66
|
+
array << gem_policy.new_version if gem_policy.new_version?
|
67
|
+
array.join('/')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Security
|
5
|
+
module Voting
|
6
|
+
# Gem policy with statistics from Coditsu differ
|
7
|
+
class GemPolicy
|
8
|
+
attr_reader :name, :current_version, :new_version, :remote_policy
|
9
|
+
|
10
|
+
# Build gem policy
|
11
|
+
#
|
12
|
+
# @param name [String] gem name
|
13
|
+
# @param gem_data [Array] gem version and statistics from Coditsu
|
14
|
+
# @param remote_policy [Voting::RemotePolicy]
|
15
|
+
def initialize(name, gem_data, remote_policy)
|
16
|
+
@name = name
|
17
|
+
@new_version = nil
|
18
|
+
|
19
|
+
versions = gem_data.first
|
20
|
+
|
21
|
+
raise Errors::InvalidRemoteVersionsType, versions.class unless versions.is_a?(Array)
|
22
|
+
|
23
|
+
@current_version = versions.first.empty? ? versions.last : versions.first
|
24
|
+
@new_version = versions.last if @current_version != versions.last
|
25
|
+
|
26
|
+
@remote_policy = remote_policy
|
27
|
+
@threshold = gem_data.last[remote_policy.type]
|
28
|
+
end
|
29
|
+
|
30
|
+
# How many time gem was marked as safe
|
31
|
+
#
|
32
|
+
# @return [Integer]
|
33
|
+
def approved
|
34
|
+
@threshold['up'].to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
# How many time gem was marked as malicious
|
38
|
+
#
|
39
|
+
# @return [Integer]
|
40
|
+
def rejected
|
41
|
+
@threshold['down'].to_i
|
42
|
+
end
|
43
|
+
|
44
|
+
# Checks if a gem is safe based on a remote policy
|
45
|
+
#
|
46
|
+
# @return [Boolean] true if it's safe, false otherwise
|
47
|
+
def approved?
|
48
|
+
approved >= @remote_policy.approved
|
49
|
+
end
|
50
|
+
|
51
|
+
# Checks if a gem is malicious based on a remote policy
|
52
|
+
#
|
53
|
+
# @return [Boolean] true if it's malicious, false otherwise
|
54
|
+
def rejected?
|
55
|
+
@remote_policy.rejected >= rejected
|
56
|
+
end
|
57
|
+
|
58
|
+
# Check if a new version was requested
|
59
|
+
#
|
60
|
+
# @return [Boolean] true if new version was requested, false otherwise
|
61
|
+
def new_version?
|
62
|
+
!@new_version.nil?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Security
|
5
|
+
module Voting
|
6
|
+
# Remote policy settings from Coditsu
|
7
|
+
RemotePolicy = Struct.new(:type, :threshold) do
|
8
|
+
# How many time gem was marked as safe
|
9
|
+
#
|
10
|
+
# @return [Integer]
|
11
|
+
def approved
|
12
|
+
threshold['up'].to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
# How many time gem was marked as malicious
|
16
|
+
#
|
17
|
+
# @return [Integer]
|
18
|
+
def rejected
|
19
|
+
threshold['down'].to_i
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'openssl'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module Bundler
|
8
|
+
module Security
|
9
|
+
module Voting
|
10
|
+
# Module responsible for doing request to Coditsu differ
|
11
|
+
module Request
|
12
|
+
# Differ endpoint url
|
13
|
+
ENDPOINT_URL = 'https://diff.coditsu.io/api/bundler.json'
|
14
|
+
# Request headers
|
15
|
+
HEADERS = { 'Content-Type': 'application/json' }.freeze
|
16
|
+
|
17
|
+
private_constant :ENDPOINT_URL, :HEADERS
|
18
|
+
|
19
|
+
class << self
|
20
|
+
# Execute request to the differ
|
21
|
+
#
|
22
|
+
# @param config [OpenStruct] Coditsu config
|
23
|
+
# @param payload [Hash] with versions to check
|
24
|
+
#
|
25
|
+
# @return [Net::HTTPResponse] response from Coditsu differ
|
26
|
+
def call(config, payload)
|
27
|
+
build_http do |http, uri|
|
28
|
+
http.request(build_request(uri, config, payload))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Builds http connection object
|
33
|
+
def build_http
|
34
|
+
uri = URI(differ_url)
|
35
|
+
|
36
|
+
Net::HTTP.start(
|
37
|
+
uri.host,
|
38
|
+
uri.port,
|
39
|
+
use_ssl: uri.scheme == 'https',
|
40
|
+
verify_mode: OpenSSL::SSL::VERIFY_NONE
|
41
|
+
) { |http| yield(http, uri) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Build http post request and assigns headers and payload
|
45
|
+
#
|
46
|
+
# @param uri [URI::HTTPS]
|
47
|
+
# @param config [OpenStruct] Coditsu config
|
48
|
+
# @param payload [Hash] with versions to check
|
49
|
+
#
|
50
|
+
# @return [Net::HTTP::Post]
|
51
|
+
def build_request(uri, config, payload)
|
52
|
+
Net::HTTP::Post
|
53
|
+
.new(uri.request_uri, HEADERS)
|
54
|
+
.tap { |request| assign_auth(request, config) }
|
55
|
+
.tap { |request| assign_payload(request, payload) }
|
56
|
+
end
|
57
|
+
|
58
|
+
# Assigns basic authorization if provided in the config
|
59
|
+
#
|
60
|
+
# @param request [Net::HTTP::Post] prepared http post
|
61
|
+
# @param config [OpenStruct] Coditsu config
|
62
|
+
def assign_auth(request, config)
|
63
|
+
return unless config
|
64
|
+
return unless config.api_key
|
65
|
+
return unless config.api_secret
|
66
|
+
|
67
|
+
request.basic_auth(config.api_key, config.api_secret)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Assigns payload as json
|
71
|
+
#
|
72
|
+
# @param request [Net::HTTP::Post] prepared http post
|
73
|
+
# @param payload [Hash] with versions to check
|
74
|
+
def assign_payload(request, payload)
|
75
|
+
request.body = JSON.dump(payload)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Provides differ endpoint url
|
79
|
+
#
|
80
|
+
# @return [String]
|
81
|
+
def differ_url
|
82
|
+
ENV['CODITSU_DIFFER_URL'] || ENDPOINT_URL
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
module Security
|
5
|
+
module Voting
|
6
|
+
# Module responsible for handling both local and remote gem versions
|
7
|
+
module Versions
|
8
|
+
# Module responsible for preparing current or current/new versions of gems
|
9
|
+
module Local
|
10
|
+
class << self
|
11
|
+
# Definition of a local path, if it matches it means that we are the source
|
12
|
+
ME_PATH = '.'
|
13
|
+
# Sources that we expect to match ourselves too
|
14
|
+
ME_SOURCES = [
|
15
|
+
Bundler::Source::Gemspec,
|
16
|
+
Bundler::Source::Path
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
# @param command [String] either install or update
|
20
|
+
# @param definition [Bundler::Definition] definition for your source
|
21
|
+
def call(command, definition)
|
22
|
+
Bundler.ui.silence { definition.resolve_remotely! }
|
23
|
+
|
24
|
+
case command
|
25
|
+
when Commands::INSTALL then build_install(definition)
|
26
|
+
when Commands::UPDATE then build_update(definition)
|
27
|
+
else
|
28
|
+
raise ArgumentError, "invalid command: #{command}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# @param definition [Bundler::Definition] definition for your source
|
35
|
+
def build_install(definition)
|
36
|
+
requested_specs = definition.requested_specs
|
37
|
+
# Support case without Gemfile.lock
|
38
|
+
if definition.locked_gems
|
39
|
+
locked_specs = definition.locked_gems.specs
|
40
|
+
introduced = requested_specs.map(&:name) - locked_specs.map(&:name)
|
41
|
+
introduced_specs = requested_specs.select { |spec| introduced.include?(spec.name) }
|
42
|
+
introduced_specs.concat(locked_specs)
|
43
|
+
else
|
44
|
+
introduced_specs = requested_specs
|
45
|
+
end
|
46
|
+
|
47
|
+
introduced_specs.each_with_object({}) do |spec, hash|
|
48
|
+
next if skip?(spec.source)
|
49
|
+
|
50
|
+
hash[spec.name] = ['', spec.version.to_s]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param definition [Bundler::Definition] definition for your source
|
55
|
+
def build_update(definition)
|
56
|
+
locked_specs = definition.locked_gems.specs
|
57
|
+
|
58
|
+
definition.requested_specs.each_with_object({}) do |spec, hash|
|
59
|
+
next if skip?(spec.source)
|
60
|
+
|
61
|
+
locked_spec = locked_specs.find { |s| s.name == spec.name }
|
62
|
+
|
63
|
+
hash[spec.name] = if locked_spec
|
64
|
+
[locked_spec.version.to_s, spec.version.to_s]
|
65
|
+
else
|
66
|
+
['', spec.version.to_s]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Checks if we should skip a source
|
72
|
+
#
|
73
|
+
# @param source [Bundler::Source::Git, Bundler::Source::Rubygems]
|
74
|
+
#
|
75
|
+
# @return [Boolean] true if we should skip this source, false otherwise
|
76
|
+
def skip?(source)
|
77
|
+
return true if git?(source)
|
78
|
+
return true if me?(source)
|
79
|
+
|
80
|
+
false
|
81
|
+
end
|
82
|
+
|
83
|
+
# Checks if it's a git source
|
84
|
+
#
|
85
|
+
# @param source [Bundler::Source::Git, Bundler::Source::Rubygems]
|
86
|
+
#
|
87
|
+
# @return [Boolean] true if it's a git source, false otherwise
|
88
|
+
def git?(source)
|
89
|
+
source.instance_of?(Bundler::Source::Git)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Checks if it's a self source, this happens for repositories that are a gem
|
93
|
+
#
|
94
|
+
# @param source [Bundler::Source::Path,Bundler::Source::Git,Bundler::Source::Rubygems]
|
95
|
+
#
|
96
|
+
# @return [Boolean] true if it's a self source, false otherwise
|
97
|
+
def me?(source)
|
98
|
+
return false unless ME_SOURCES.include?(source.class)
|
99
|
+
|
100
|
+
source.path.to_s == ME_PATH
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Bundler
|
6
|
+
module Security
|
7
|
+
module Voting
|
8
|
+
# Module responsible for handling both local and remote gem versions
|
9
|
+
module Versions
|
10
|
+
# Module responsible for fetching safe/malicious votes
|
11
|
+
# for current or current/new versions of gems
|
12
|
+
module Remote
|
13
|
+
# Differ bundler url
|
14
|
+
ENDPOINT_URL = 'https://diff.coditsu.io/api/bundler.json'
|
15
|
+
|
16
|
+
private_constant :ENDPOINT_URL
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# @param command [String] either install or update
|
20
|
+
# @param definition [Bundler::Definition] definition for your source
|
21
|
+
def call(command, definition)
|
22
|
+
config = fetch_config
|
23
|
+
|
24
|
+
Request
|
25
|
+
.call(config, payload(command, config&.repository_id, definition))
|
26
|
+
.then { |response| JSON.parse(response.body) }
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param command [String] either install or update
|
30
|
+
# @param repository_id [String] coditsu repository_id
|
31
|
+
# @param definition [Bundler::Definition] definition for your source
|
32
|
+
#
|
33
|
+
# @return [Hash] payload for differ bundler endpoint
|
34
|
+
def payload(command, repository_id, definition)
|
35
|
+
Local.call(command, definition).each_with_object({}) do |(name, versions), hash|
|
36
|
+
hash[:data] ||= {}
|
37
|
+
hash[:data][:repository_id] = repository_id if repository_id
|
38
|
+
hash[:data][:gems] ||= {}
|
39
|
+
hash[:data][:gems][name] = versions
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Fetch coditsu config file
|
44
|
+
#
|
45
|
+
# @return [OpenStruct, nil] configuration object
|
46
|
+
#
|
47
|
+
# @raise [Errors::MissingConfigurationFile] when no config file
|
48
|
+
def fetch_config
|
49
|
+
Config::Fetcher.call(
|
50
|
+
File.expand_path('..', Bundler.bin_path)
|
51
|
+
)
|
52
|
+
rescue Errors::MissingConfigurationFile
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/plugins.rb
ADDED
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: diffense
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tomasz Pajor
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Bundler Security
|
42
|
+
email:
|
43
|
+
- tomek@coditsu.io
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".circleci/config.yml"
|
49
|
+
- ".coditsu/ci.yml"
|
50
|
+
- ".gitignore"
|
51
|
+
- ".ruby-version"
|
52
|
+
- Gemfile
|
53
|
+
- Gemfile.lock
|
54
|
+
- LICENSE
|
55
|
+
- README.md
|
56
|
+
- certs/mensfeld.pem
|
57
|
+
- diffense.gemspec
|
58
|
+
- lib/bundler/security.rb
|
59
|
+
- lib/bundler/security/commands.rb
|
60
|
+
- lib/bundler/security/config/fetcher.rb
|
61
|
+
- lib/bundler/security/config/file_finder.rb
|
62
|
+
- lib/bundler/security/errors.rb
|
63
|
+
- lib/bundler/security/version.rb
|
64
|
+
- lib/bundler/security/voting.rb
|
65
|
+
- lib/bundler/security/voting/build_failure.rb
|
66
|
+
- lib/bundler/security/voting/build_success.rb
|
67
|
+
- lib/bundler/security/voting/build_unsafe_gem.rb
|
68
|
+
- lib/bundler/security/voting/gem_policy.rb
|
69
|
+
- lib/bundler/security/voting/remote_policy.rb
|
70
|
+
- lib/bundler/security/voting/request.rb
|
71
|
+
- lib/bundler/security/voting/versions/local.rb
|
72
|
+
- lib/bundler/security/voting/versions/remote.rb
|
73
|
+
- plugins.rb
|
74
|
+
homepage: https://diff.coditsu.io
|
75
|
+
licenses:
|
76
|
+
- MIT
|
77
|
+
metadata:
|
78
|
+
allowed_push_host: https://rubygems.org
|
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
|
+
rubygems_version: 3.1.2
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: Bundler Security
|
98
|
+
test_files: []
|