bundler-resolutions 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5a9bc7c0b4191b262edcc52111b78fb8c67b74b0c9642ddec0036142d46c2989
4
+ data.tar.gz: bb5c955b6db7a14e56e8d5048c98709a36f634f25364a14cbe22d6606d3faeab
5
+ SHA512:
6
+ metadata.gz: 2d0f016d3a21dd98fe3d175d015fd4e526a514f32acf8283205db330ac9e52df4444e59a12e761882580790c8bccc1599739d3375291f598b810b4e7800b0705
7
+ data.tar.gz: cf1c8bee485fa053ec3de1ac094638381d9153796de7e77f5add4ee31953f41889f3563f6413830a5351633cb2f7960d5557447f66d52aca69ac175293696646
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ bundler-resolutions
2
+ ===================
3
+
4
+ [![Gem Version](https://img.shields.io/gem/v/bundler-resolutions?color=green)](https://rubygems.org/gems/bundler-resolutions)
5
+ [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ [bundler-resolutions](https://github.com/hlascelles/bundler-resolutions) is a [bundler](https://bundler.io/)
8
+ plugin that allows you to specify gem version requirements in your `Gemfile` without explicitly declaring
9
+ a concrete dependency on those gems. It acts much like the
10
+ [resolutions](https://classic.yarnpkg.com/lang/en/docs/selective-version-resolutions/) feature in
11
+ [Yarn](https://yarnpkg.com/).
12
+
13
+ > [!WARNING]
14
+ > This is an experimental project and neither its API stability nor correctness should be assumed
15
+
16
+ ## Usage
17
+
18
+ Add `bundler-resolutions` to your Gemfile, and add a `resolutions` group to specify the gems you
19
+ want to specify versions requirements for.
20
+
21
+ The resulting `Gemfile.lock` in this example will have nokogiri locked to `1.16.5` or above.
22
+
23
+ ```ruby
24
+ plugin 'bundler-resolutions'
25
+
26
+ gem "rails"
27
+
28
+ group :resolutions do
29
+ gem "nokogiri", ">= 1.16.5" # CVE-2024-34459
30
+ end
31
+ ```
32
+
33
+ However the `Gemfile.lock` from this example will not have nokogiri at all, as it is neither
34
+ explicitly declared, nor brought in as a transitive dependency.
35
+
36
+ ```ruby
37
+ plugin 'bundler-resolutions'
38
+
39
+ group :resolutions do
40
+ gem "nokogiri", ">= 1.16.5" # CVE-2024-34459
41
+ end
42
+ ```
43
+
44
+ ## Detail
45
+
46
+ `bundler-resolutions` allows you to specify version requirements using standard gem syntax in your
47
+ Gemfile to indicate that you have version requirements for those gems *if* they were to be brought
48
+ in as transitive dependencies, but that you don't depend on them yourself directly.
49
+
50
+ An example use case is in the Gemfile given below. Here we are saying that although we do not use nokogiri
51
+ specifically ourselves, we want to ensure that if it is pulled in by other gems then it will
52
+ always be above the know version with a CVE.
53
+
54
+ ```ruby
55
+ source "https://rubygems.org"
56
+
57
+ plugin 'bundler-resolutions'
58
+
59
+ gem "rails"
60
+
61
+ group :resolutions do
62
+ gem "nokogiri", ">= 1.16.5" # CVE-2024-34459
63
+ end
64
+ ```
65
+
66
+ The big difference between doing this and just declaring it in your Gemfile is that it will only
67
+ be used in resolutions (and be written to your lock file) if the gems you do directly depend on
68
+ continue to use it. If they stop using it, then your resolutions will take no part in the
69
+ bundler lock resolution.
70
+
71
+ The other difference is that even if it does take part in the resolutions, it will not be
72
+ present in the `DEPENDENCIES` section of the lock file, as it is not a direct dependency.
73
+
74
+ ## Use cases
75
+
76
+ There are a number of reasons you may want to prevent the usage of some gem versions, without
77
+ direct use, such as:
78
+
79
+ 1. You have learnt of a CVE of a gem.
80
+ 2. You have internal processes that mandate the usage of certain gem versions for legal or sign off reasons.
81
+ 3. You know of gem incompatibilities in later versions.
82
+ 4. You know that different OS architectures do not work with some versions.
83
+
84
+ ## How it works
85
+
86
+ `bundler-resolutions` works by patching the Gemfile DSL to allow for special processing
87
+ of the `resolutions` group. It also patches the bundler `filtered_versions_for` method to
88
+ allow for the resolution restrictions from the versions specified in the `resolutions` group.
89
+
90
+ This is a very early version, and it should be considered experimental.
91
+
92
+ Future work may include relating this to bundler-audit, and other security tools, so you
93
+ will automatically gain version restrictions against known CVEs.
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../resolutions"
4
+
5
+ module Bundler
6
+ class Resolutions
7
+ VERSION = "0.1.0"
8
+ end
9
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ class Resolutions
5
+ GROUP_NAME = :resolutions
6
+
7
+ attr_reader :resolutions
8
+
9
+ def initialize
10
+ @resolutions = {}
11
+ end
12
+
13
+ # This method is called by the DSL to set the resolution for a given gem. It is effectively
14
+ # an override of the normal gem method.
15
+ def gem(name, requirements)
16
+ resolutions[name.to_sym] = requirements
17
+ end
18
+
19
+ class << self
20
+ def instance = @instance ||= new
21
+ end
22
+
23
+ # A module we prepend to Bundler::Resolutions::Resolver
24
+ module Resolver
25
+ # This overrides the default behaviour of the resolver to filter out versions that don't
26
+ # satisfy the requirements specified in RESOLVER_RESOLUTIONS.
27
+ def filtered_versions_for(package)
28
+ super.select do |pkg|
29
+ req = Bundler::Resolutions.instance.resolutions[package.name.to_sym]
30
+ req ? Gem::Requirement.new(*req.split(",")).satisfied_by?(pkg.version) : true
31
+ end
32
+ end
33
+ end
34
+
35
+ # A module we prepend to Bundler::Dsl
36
+ module Dsl
37
+ # Here we override the normal group function to capture the resolutions, and ensure any
38
+ # gem calls are effectively a no-op as regards to adding them to dependencies.
39
+ def group(*args, &blk)
40
+ args.first == GROUP_NAME ? Bundler::Resolutions.instance.instance_eval(&blk) : super
41
+ end
42
+ end
43
+ end
44
+ end
45
+
46
+ Bundler::Resolver.prepend(Bundler::Resolutions::Resolver)
47
+ Bundler::Dsl.prepend(Bundler::Resolutions::Dsl)
data/plugins.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/resolutions"
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bundler-resolutions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Harry Lascelles
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-10-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A bundler plugin to enforce resolutions without specifying a concrete
14
+ dependency
15
+ email:
16
+ - harry@harrylascelles.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - lib/bundler/resolutions.rb
23
+ - lib/bundler/resolutions/version.rb
24
+ - plugins.rb
25
+ homepage: https://github.com/hlascelles/bundler-resolutions
26
+ licenses:
27
+ - MIT
28
+ metadata:
29
+ homepage_uri: https://github.com/hlascelles/bundler-resolutions
30
+ documentation_uri: https://github.com/hlascelles/bundler-resolutions
31
+ changelog_uri: https://github.com/hlascelles/bundler-resolutions/blob/master/CHANGELOG.md
32
+ source_code_uri: https://github.com/hlascelles/bundler-resolutions/
33
+ bug_tracker_uri: https://github.com/hlascelles/bundler-resolutions/issues
34
+ rubygems_mfa_required: 'true'
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubygems_version: 3.5.11
51
+ signing_key:
52
+ specification_version: 4
53
+ summary: A bundler plugin to enforce resolutions without specifying a concrete dependency
54
+ test_files: []