bp3-core 0.3.0 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 76165d42f4b28714655519eeddedc6d08dd0b5bc9a5e919b389f173aa0bf20a0
4
- data.tar.gz: dba935fa44898891607c50b377cbb4d8636300c7285592838826331cc47d5da6
3
+ metadata.gz: 8003ea2f7141b2360e20e0e09fb9dc8281025600b924e91609df721df5f301f9
4
+ data.tar.gz: a6be8300ea89743527193fd112da8b85808d1b9203e0eac33fa3b303e4407656
5
5
  SHA512:
6
- metadata.gz: 548dd80c8000de996d2abe85c4e158b979e9c809957f1403f15519280ee2dcb3c1fa865bf4355bc6fa25d9a6cf19791bc09cf3d6d0a84b7c0a7888cdd4960c21
7
- data.tar.gz: 6dbc07299dcc22c84643250fd8fe018dc2b8c94de6a30fe303431d61f38438ffa32c897f43132a49d4fa9d75491ceb33cf11e988c9c2a88e21406a7dd33e6abc
6
+ metadata.gz: be789a83b3e8ffd1891f624f730d1fd6bd1967a248b7a9de43a7d1d8df888b89d0f3ef4cefac5dabe0ca8198f984412b2ef9c3798c48ead18d20c0aac0721539
7
+ data.tar.gz: babac34019d6a4ad03722f91e8ff2b6fbe1d774415ea55a9cd84ebe80f006ba4137fe06b9f6a69b90b40d5ea0f74d9bbe4d4e2f2453e58487305b9bcb8e4054e
data/CHANGELOG.md CHANGED
@@ -1,8 +1,22 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2026-10-30
4
+
5
+ ### Security
6
+
7
+ - Tighten ransack allowlist. Add `Bp3::Core::Ransackable::Explore`, a stricter answer to ransack's allowlist
8
+ questions. Asked with `Bp3::Core::Ransackable::EXPLORE` as the `auth_object`, `Ransackable` no longer allows every
9
+ column and association: it refuses encrypted attributes, columns matching `filter_parameters`, columns
10
+ of a `json`, `jsonb`, `binary`, `tsvector`, `vector` or `halfvec` type, and what a model names in
11
+ `unexplorable_columns`, and allows only the associations a model names in `explorable_associations`.
12
+ Any other `auth_object` gets the answer it always got
13
+ - A model that must not allow plain ransack includes `Explore` alone; it answers nothing to any other
14
+ `auth_object`
15
+
3
16
  ## [0.3.0] - 2026-10-26
4
17
 
5
18
  ### Breaking
19
+
6
20
  - Scope Tenantable by the primary login
7
21
  Tenantable default scopes reach as far as the primary login, the request state's
8
22
  `current_login` (the least privileged one signed in), rather than as far as any root or admin signed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bp3-core (0.3.0)
4
+ bp3-core (0.4.0)
5
5
  actionview (~> 8.1)
6
6
  activesupport (~> 8.1)
7
7
  bp3-request_state (~> 0.1)
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require 'active_support/core_ext/class/attribute'
5
+ require 'active_support/parameter_filter'
6
+
7
+ module Bp3
8
+ module Core
9
+ module Ransackable
10
+ EXPLORE = :explore
11
+
12
+ # Explore answers ransack's allowlist questions when asked with EXPLORE as the auth_object. It
13
+ # allows the columns and the ransackers, but never an encrypted attribute, a column matching
14
+ # filter_parameters, a column of a type that cannot be grouped or read as a value, or one the
15
+ # model names in unexplorable_columns; and only the associations the model names in
16
+ # explorable_associations. Ransack asks a joined model with the same auth_object, so the target
17
+ # of an allowed association is held to the same rules.
18
+ #
19
+ # Ransackable includes this. A model that must not allow plain ransack includes this alone: it
20
+ # answers nothing to any other auth_object, since defining these methods is what ransack takes
21
+ # as an allowlist.
22
+ module Explore
23
+ extend ActiveSupport::Concern
24
+
25
+ UNEXPLORABLE_TYPES = %i[json jsonb binary tsvector vector halfvec].freeze
26
+
27
+ included do
28
+ # Override in subclasses: the associations an explore filter may join through
29
+ class_attribute :explorable_associations, instance_accessor: false, default: []
30
+ # Override in subclasses: columns explore may not touch that no other rule catches
31
+ class_attribute :unexplorable_columns, instance_accessor: false, default: []
32
+ end
33
+
34
+ class_methods do
35
+ def ransackable_attributes(auth_object = nil)
36
+ return [] unless auth_object == EXPLORE
37
+
38
+ exceptions = Ransackable.attribute_exceptions.map(&:to_s)
39
+ column_names.map(&:to_s) + _ransackers.keys - exceptions - unexplorable_attributes
40
+ end
41
+
42
+ def ransackable_associations(auth_object = nil)
43
+ return [] unless auth_object == EXPLORE
44
+
45
+ reflect_on_all_associations.map { |reflection| reflection.name.to_s } & explorable_associations.map(&:to_s)
46
+ end
47
+
48
+ def unexplorable_attributes
49
+ filter = ActiveSupport::ParameterFilter.new(Ransackable.filter_parameters + filter_attributes)
50
+ names = column_names.map(&:to_s)
51
+ filtered = names.reject { |name| filter.filter(name => name)[name] == name }
52
+ typed = columns.select { |column| UNEXPLORABLE_TYPES.include?(column.type) }.map(&:name)
53
+ encrypted_attributes.to_a.map(&:to_s) + filtered + typed + unexplorable_columns.map(&:to_s)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,14 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'ransackable/explore'
4
+
3
5
  module Bp3
4
- # Bp3::Ransackable provides class methods expected by models that use ransack
6
+ # Bp3::Ransackable provides class methods expected by models that use ransack. It allows every column
7
+ # and association, except when asked with EXPLORE as the auth_object: Explore answers that.
5
8
  module Core
6
9
  module Ransackable
7
10
  extend ActiveSupport::Concern
8
11
 
12
+ include Explore
13
+
9
14
  mattr_accessor :attribute_exceptions, default: []
10
15
  mattr_accessor :association_exceptions, default: []
11
16
 
17
+ def self.filter_parameters
18
+ defined?(Rails) ? Rails.application.config.filter_parameters : []
19
+ end
20
+
12
21
  class_methods do
13
22
  def ransackable_fields(auth_object = nil)
14
23
  fields =
@@ -18,12 +27,16 @@ module Bp3
18
27
  fields.map(&:to_sym).uniq
19
28
  end
20
29
 
21
- def ransackable_attributes(_auth_object = nil)
30
+ def ransackable_attributes(auth_object = nil)
31
+ return super if auth_object == EXPLORE
32
+
22
33
  except = attribute_exceptions.map(&:to_s)
23
34
  column_names.map(&:to_s) - except
24
35
  end
25
36
 
26
- def ransackable_associations(_auth_object = nil)
37
+ def ransackable_associations(auth_object = nil)
38
+ return super if auth_object == EXPLORE
39
+
27
40
  except = association_exceptions.map(&:to_s)
28
41
  reflect_on_all_associations.map(&:name).map(&:to_s) - except
29
42
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Bp3
4
4
  module Core
5
- VERSION = '0.3.0'
5
+ VERSION = '0.4.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bp3-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wim den Braven
@@ -146,6 +146,7 @@ files:
146
146
  - lib/bp3/core/displayable.rb
147
147
  - lib/bp3/core/feature_flags.rb
148
148
  - lib/bp3/core/ransackable.rb
149
+ - lib/bp3/core/ransackable/explore.rb
149
150
  - lib/bp3/core/rqid.rb
150
151
  - lib/bp3/core/settings.rb
151
152
  - lib/bp3/core/sqnr.rb