mongoid 7.6.0 → 7.6.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 +4 -4
- data/lib/mongoid/clients/factory.rb +4 -0
- data/lib/mongoid/extensions/hash.rb +27 -1
- data/lib/mongoid/version.rb +1 -1
- data/spec/mongoid/clients/factory_spec.rb +31 -0
- data/spec/mongoid/extensions/hash_spec.rb +236 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d6b4091f7d54b2e65649f2c41aa1ae9d79ba5d45eda1f25402a977921af378b
|
|
4
|
+
data.tar.gz: 11c42c8f504d870e6d49a823874855c88bec5cae51254b2e3246d4e6fe3b5992
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 42b67f419f6e4632f25d6b498e774627d0f7111c7d05cdab5d02fe9f33189448a7abb572b2c351d97d2ce97a22cf2cdc3e96e61b3163337a6d4e6f921c69522a
|
|
7
|
+
data.tar.gz: 48991d86f760c6f3a1b5cd87b8a19f501a1c7ebbb5cf07c5f9164c116e999ae0e4304b92b55225e7b3ca1252ee9675e4987f5998cd627f0fa1db451ddf524b50
|
|
@@ -95,6 +95,10 @@ module Mongoid
|
|
|
95
95
|
[MONGOID_WRAPPING_LIBRARY] + options[:wrapping_libraries]
|
|
96
96
|
else
|
|
97
97
|
[MONGOID_WRAPPING_LIBRARY]
|
|
98
|
+
end.tap do |wrap|
|
|
99
|
+
if defined?(::Rails) && ::Rails.respond_to?(:version)
|
|
100
|
+
wrap << { name: 'Rails', version: ::Rails.version }
|
|
101
|
+
end
|
|
98
102
|
end
|
|
99
103
|
options[:wrapping_libraries] = wrap_lib
|
|
100
104
|
end
|
|
@@ -163,6 +163,28 @@ module Mongoid
|
|
|
163
163
|
true
|
|
164
164
|
end
|
|
165
165
|
|
|
166
|
+
ALLOWED_TO_CRITERIA_METHODS = %i[
|
|
167
|
+
all all_in all_of and any_in any_of asc ascending
|
|
168
|
+
batch_size between
|
|
169
|
+
collation comment cursor_type
|
|
170
|
+
desc descending
|
|
171
|
+
elem_match eq exists extras
|
|
172
|
+
geo_spatial group gt gte
|
|
173
|
+
hint
|
|
174
|
+
in includes
|
|
175
|
+
limit lt lte
|
|
176
|
+
max_distance max_scan max_time_ms merge mod
|
|
177
|
+
ne near near_sphere nin no_timeout none none_of nor not not_in
|
|
178
|
+
offset only or order order_by
|
|
179
|
+
project
|
|
180
|
+
raw read reorder
|
|
181
|
+
scoped skip slice snapshot
|
|
182
|
+
text_search type
|
|
183
|
+
unscoped unwind
|
|
184
|
+
where with_size with_type without
|
|
185
|
+
].freeze
|
|
186
|
+
private_constant :ALLOWED_TO_CRITERIA_METHODS
|
|
187
|
+
|
|
166
188
|
# Convert this hash to a criteria. Will iterate over each keys in the
|
|
167
189
|
# hash which must correspond to method on a criteria object. The hash
|
|
168
190
|
# must also include a "klass" key.
|
|
@@ -174,7 +196,11 @@ module Mongoid
|
|
|
174
196
|
def to_criteria
|
|
175
197
|
criteria = Criteria.new(delete(:klass) || delete("klass"))
|
|
176
198
|
each_pair do |method, args|
|
|
177
|
-
|
|
199
|
+
method_sym = method.to_sym
|
|
200
|
+
unless ALLOWED_TO_CRITERIA_METHODS.include?(method_sym)
|
|
201
|
+
raise ArgumentError, "Method '#{method}' is not allowed in to_criteria"
|
|
202
|
+
end
|
|
203
|
+
criteria = criteria.public_send(method_sym, args)
|
|
178
204
|
end
|
|
179
205
|
criteria
|
|
180
206
|
end
|
data/lib/mongoid/version.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# rubocop:todo all
|
|
2
3
|
|
|
3
4
|
require "spec_helper"
|
|
4
5
|
|
|
@@ -29,6 +30,34 @@ describe Mongoid::Clients::Factory do
|
|
|
29
30
|
end
|
|
30
31
|
end
|
|
31
32
|
|
|
33
|
+
shared_examples_for 'includes rails wrapping library' do
|
|
34
|
+
context 'when Rails is available' do
|
|
35
|
+
around do |example|
|
|
36
|
+
rails_was_defined = defined?(::Rails)
|
|
37
|
+
|
|
38
|
+
if !rails_was_defined || !::Rails.respond_to?(:version)
|
|
39
|
+
module ::Rails
|
|
40
|
+
def self.version
|
|
41
|
+
'6.1.0'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
example.run
|
|
47
|
+
|
|
48
|
+
if !rails_was_defined
|
|
49
|
+
Object.send(:remove_const, :Rails) if defined?(::Rails)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'adds Rails as another wrapping library' do
|
|
54
|
+
expect(client.options[:wrapping_libraries]).to include(
|
|
55
|
+
{'name' => 'Rails', 'version' => '6.1.0'},
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
32
61
|
describe ".create" do
|
|
33
62
|
|
|
34
63
|
context "when provided a name" do
|
|
@@ -116,6 +145,8 @@ describe Mongoid::Clients::Factory do
|
|
|
116
145
|
]
|
|
117
146
|
end
|
|
118
147
|
end
|
|
148
|
+
|
|
149
|
+
it_behaves_like 'includes rails wrapping library'
|
|
119
150
|
end
|
|
120
151
|
end
|
|
121
152
|
|
|
@@ -467,4 +467,240 @@ describe Mongoid::Extensions::Hash do
|
|
|
467
467
|
|
|
468
468
|
it_behaves_like 'unsatisfiable criteria method'
|
|
469
469
|
end
|
|
470
|
+
|
|
471
|
+
describe '#to_criteria' do
|
|
472
|
+
subject(:criteria) { hash.to_criteria }
|
|
473
|
+
|
|
474
|
+
context 'when klass is specified' do
|
|
475
|
+
let(:hash) do
|
|
476
|
+
{ klass: Band, where: { name: 'Songs Ohia' } }
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
it 'returns a criteria' do
|
|
480
|
+
expect(criteria).to be_a(Mongoid::Criteria)
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
it 'sets the klass' do
|
|
484
|
+
expect(criteria.klass).to eq(Band)
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
it 'sets the selector' do
|
|
488
|
+
expect(criteria.selector).to eq({ 'name' => 'Songs Ohia' })
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
context 'when klass is missing' do
|
|
493
|
+
let(:hash) do
|
|
494
|
+
{ where: { name: 'Songs Ohia' } }
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
it 'returns a criteria' do
|
|
498
|
+
expect(criteria).to be_a(Mongoid::Criteria)
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
it 'has klass nil' do
|
|
502
|
+
expect(criteria.klass).to be_nil
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
it 'sets the selector' do
|
|
506
|
+
expect(criteria.selector).to eq({ 'name' => 'Songs Ohia' })
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
context 'with allowed methods' do
|
|
511
|
+
context 'when using multiple query methods' do
|
|
512
|
+
let(:hash) do
|
|
513
|
+
{
|
|
514
|
+
klass: Band,
|
|
515
|
+
where: { active: true },
|
|
516
|
+
limit: 10,
|
|
517
|
+
skip: 5,
|
|
518
|
+
order_by: { name: 1 }
|
|
519
|
+
}
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
it 'applies all methods successfully' do
|
|
523
|
+
expect(criteria.selector).to eq({ 'active' => true })
|
|
524
|
+
expect(criteria.options[:limit]).to eq(10)
|
|
525
|
+
expect(criteria.options[:skip]).to eq(5)
|
|
526
|
+
expect(criteria.options[:sort]).to eq({ 'name' => 1 })
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
context 'when using query selector methods' do
|
|
531
|
+
let(:hash) do
|
|
532
|
+
{
|
|
533
|
+
klass: Band,
|
|
534
|
+
gt: { members: 2 },
|
|
535
|
+
in: { genre: ['rock', 'metal'] }
|
|
536
|
+
}
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
it 'applies selector methods' do
|
|
540
|
+
expect(criteria.selector['members']).to eq({ '$gt' => 2 })
|
|
541
|
+
expect(criteria.selector['genre']).to eq({ '$in' => ['rock', 'metal'] })
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
context 'when using aggregation methods' do
|
|
546
|
+
let(:hash) do
|
|
547
|
+
{
|
|
548
|
+
klass: Band,
|
|
549
|
+
project: { name: 1, members: 1 }
|
|
550
|
+
}
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
it 'applies aggregation methods' do
|
|
554
|
+
expect { criteria }.not_to raise_error
|
|
555
|
+
end
|
|
556
|
+
end
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
context 'with disallowed methods' do
|
|
560
|
+
context 'when attempting to call create' do
|
|
561
|
+
let(:hash) do
|
|
562
|
+
{ klass: Band, create: { name: 'Malicious' } }
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
it 'raises ArgumentError' do
|
|
566
|
+
expect { criteria }.to raise_error(ArgumentError, "Method 'create' is not allowed in to_criteria")
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
context 'when attempting to call create!' do
|
|
571
|
+
let(:hash) do
|
|
572
|
+
{ klass: Band, 'create!': { name: 'Malicious' } }
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
it 'raises ArgumentError' do
|
|
576
|
+
expect { criteria }.to raise_error(ArgumentError, "Method 'create!' is not allowed in to_criteria")
|
|
577
|
+
end
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
context 'when attempting to call build' do
|
|
581
|
+
let(:hash) do
|
|
582
|
+
{ klass: Band, build: { name: 'Malicious' } }
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
it 'raises ArgumentError' do
|
|
586
|
+
expect { criteria }.to raise_error(ArgumentError, "Method 'build' is not allowed in to_criteria")
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
context 'when attempting to call find' do
|
|
591
|
+
let(:hash) do
|
|
592
|
+
{ klass: Band, find: 'some_id' }
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
it 'raises ArgumentError' do
|
|
596
|
+
expect { criteria }.to raise_error(ArgumentError, "Method 'find' is not allowed in to_criteria")
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
context 'when attempting to call execute_or_raise' do
|
|
601
|
+
let(:hash) do
|
|
602
|
+
{ klass: Band, execute_or_raise: ['id1', 'id2'] }
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
it 'raises ArgumentError' do
|
|
606
|
+
expect { criteria }.to raise_error(ArgumentError, "Method 'execute_or_raise' is not allowed in to_criteria")
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
context 'when attempting to call new' do
|
|
611
|
+
let(:hash) do
|
|
612
|
+
{ klass: Band, new: { name: 'Test' } }
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
it 'raises ArgumentError' do
|
|
616
|
+
expect { criteria }.to raise_error(ArgumentError, "Method 'new' is not allowed in to_criteria")
|
|
617
|
+
end
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
context 'when allowed method is combined with disallowed method' do
|
|
621
|
+
let(:hash) do
|
|
622
|
+
{
|
|
623
|
+
klass: Band,
|
|
624
|
+
where: { active: true },
|
|
625
|
+
create: { name: 'Malicious' }
|
|
626
|
+
}
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
it 'raises ArgumentError before executing any methods' do
|
|
630
|
+
expect { criteria }.to raise_error(ArgumentError, "Method 'create' is not allowed in to_criteria")
|
|
631
|
+
end
|
|
632
|
+
end
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
context 'security validation' do
|
|
636
|
+
# This test ensures that ALL public methods not in the allowlist are blocked
|
|
637
|
+
it 'blocks all dangerous public methods' do
|
|
638
|
+
dangerous_methods = %i[
|
|
639
|
+
build create create! new
|
|
640
|
+
find find_or_create_by find_or_create_by! find_or_initialize_by
|
|
641
|
+
first_or_create first_or_create! first_or_initialize
|
|
642
|
+
execute_or_raise multiple_from_db for_ids
|
|
643
|
+
documents= inclusions= scoping_options=
|
|
644
|
+
initialize freeze as_json
|
|
645
|
+
]
|
|
646
|
+
|
|
647
|
+
dangerous_methods.each do |method|
|
|
648
|
+
hash = { klass: Band, method => 'arg' }
|
|
649
|
+
expect { hash.to_criteria }.to raise_error(
|
|
650
|
+
ArgumentError,
|
|
651
|
+
"Method '#{method}' is not allowed in to_criteria"
|
|
652
|
+
), "Expected method '#{method}' to be blocked but it was allowed"
|
|
653
|
+
end
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
it 'blocks dangerous inherited methods from Object' do
|
|
657
|
+
# Critical security test: block send, instance_eval, etc.
|
|
658
|
+
inherited_dangerous = %i[
|
|
659
|
+
send __send__ instance_eval instance_exec
|
|
660
|
+
instance_variable_set method
|
|
661
|
+
]
|
|
662
|
+
|
|
663
|
+
inherited_dangerous.each do |method|
|
|
664
|
+
hash = { klass: Band, method => 'arg' }
|
|
665
|
+
expect { hash.to_criteria }.to raise_error(
|
|
666
|
+
ArgumentError,
|
|
667
|
+
"Method '#{method}' is not allowed in to_criteria"
|
|
668
|
+
), "Expected inherited method '#{method}' to be blocked"
|
|
669
|
+
end
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
it 'blocks Enumerable execution methods' do
|
|
673
|
+
# to_criteria should build queries, not execute them
|
|
674
|
+
enumerable_methods = %i[each map select count sum]
|
|
675
|
+
|
|
676
|
+
enumerable_methods.each do |method|
|
|
677
|
+
hash = { klass: Band, method => 'arg' }
|
|
678
|
+
expect { hash.to_criteria }.to raise_error(
|
|
679
|
+
ArgumentError,
|
|
680
|
+
"Method '#{method}' is not allowed in to_criteria"
|
|
681
|
+
), "Expected Enumerable method '#{method}' to be blocked"
|
|
682
|
+
end
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
it 'allows all whitelisted methods' do
|
|
686
|
+
# Sample of allowed methods from each category
|
|
687
|
+
allowed_sample = {
|
|
688
|
+
where: { name: 'Test' }, # Query selector
|
|
689
|
+
limit: 10, # Query option
|
|
690
|
+
skip: 5, # Query option
|
|
691
|
+
gt: { age: 18 }, # Query selector
|
|
692
|
+
in: { status: ['active'] }, # Query selector
|
|
693
|
+
ascending: :name, # Sorting
|
|
694
|
+
includes: :notes, # Eager loading
|
|
695
|
+
merge: { klass: Band }, # Merge
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
allowed_sample.each do |method, args|
|
|
699
|
+
hash = { klass: Band, method => args }
|
|
700
|
+
expect { hash.to_criteria }.not_to raise_error,
|
|
701
|
+
"Expected method '#{method}' to be allowed but it was blocked"
|
|
702
|
+
end
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
end
|
|
470
706
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mongoid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.6.
|
|
4
|
+
version: 7.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The MongoDB Ruby Team
|
|
@@ -1130,7 +1130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
1130
1130
|
- !ruby/object:Gem::Version
|
|
1131
1131
|
version: 1.3.6
|
|
1132
1132
|
requirements: []
|
|
1133
|
-
rubygems_version: 4.0.
|
|
1133
|
+
rubygems_version: 4.0.4
|
|
1134
1134
|
specification_version: 4
|
|
1135
1135
|
summary: Elegant Persistence in Ruby for MongoDB.
|
|
1136
1136
|
test_files:
|