Compare commits

...
Sign in to create a new pull request.

40 commits

Author SHA1 Message Date
KMY(雪あすか)
122d55a4d9
Merge pull request #771 from kmycode/kb-draft-12.3
Release: 12.3
2024-07-05 08:55:02 +09:00
KMY
fbcb5a728e Fix test 2024-07-05 08:39:40 +09:00
KMY
9d329c8382 Fix test 2024-07-05 08:11:24 +09:00
KMY
efa0c376bc Fix rubocop 2024-07-05 07:34:53 +09:00
KMY
6b59ce6985 Fix Gemfile 2024-07-05 07:30:57 +09:00
KMY
f77d638995 Fix Gemfile.lock 2024-07-05 07:17:14 +09:00
KMY
2927cbd153 Bump version to 12.3 2024-07-05 07:11:24 +09:00
Claire
ff8058642c Bump version to v4.3.0-alpha.5 (#30920) 2024-07-05 07:10:57 +09:00
Claire
e4784b4f69 Merge pull request from GHSA-xjvf-fm67-4qc3 2024-07-05 07:10:48 +09:00
Claire
21b716db12 Merge pull request from GHSA-58x8-3qxw-6hm7
* Fix insufficient permission checking for public timeline endpoints

Note that this changes unauthenticated access failure code from 401 to 422

* Add more tests for public timelines

* Require user token in `/api/v1/statuses/:id/translate` and `/api/v1/scheduled_statuses`
2024-07-05 07:10:33 +09:00
Claire
ca1e2e6131 Merge pull request from GHSA-vp5r-5pgw-jwqx
* Fix streaming sessions not being closed when revoking access to an app

* Add tests for GHSA-7w3c-p9j8-mq3x
2024-07-05 07:07:56 +09:00
Daniel M Brasil
e29c435011 fix: Return HTTP 422 when scheduled status time is less than 5 minutes (#30584) 2024-07-05 07:05:03 +09:00
David Roetzel
650875a820 Improve encoding detection for link cards (#30780) 2024-07-05 07:04:42 +09:00
Eugen Rochko
f60b7fe905 Change search modifiers to be case-insensitive (#30865) 2024-07-05 07:04:20 +09:00
KMY
0370a72dbd Add size limit for link preview URLs (#30854) 2024-07-05 07:03:49 +09:00
Tim Rogers
e53f6a50d2 Added check for STATSD_ADDR setting to emit a warning and proceed rather than crashing if the address is unreachable (#30691) 2024-07-05 07:02:11 +09:00
renovate[bot]
98f36f7fd7 chore(deps): update dependency charlock_holmes to v0.7.8 (#30870)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-05 07:01:34 +09:00
KMY
f3ac508cf7 Fix /admin/accounts/:account_id/statuses/:id for edited posts with media attachments (#30819) 2024-07-05 06:56:14 +09:00
Claire
ae7ec2bbd9 Update dependency rails 2024-07-05 06:52:16 +09:00
Claire
1e2f401c98 Fix duplicate @context attribute in user export (#30653) 2024-07-05 06:44:41 +09:00
KMY(雪あすか)
3c08e48a70
Merge pull request #754 from kmycode/kbtopic-fix-emoji-reaction-rack-attack-for-12
Release: 12.2
2024-06-03 07:46:13 +09:00
KMY
58040c5aa7 Bump version to 12.2 2024-06-02 11:21:08 +09:00
KMY
0bd26af2dd Fix: 絵文字リアクションに厳しいレートリミットが適用される問題 2024-06-02 11:19:00 +09:00
KMY(雪あすか)
652d037440
Merge pull request #750 from kmycode/kb-draft-12.1
Release: 12.1
2024-05-31 08:39:15 +09:00
KMY
7615e12e88 Fix test 2024-05-31 08:11:57 +09:00
KMY
35bd3ea5b7 Fix dicker-compose 2024-05-31 08:08:12 +09:00
KMY
28b74eabac Bump version to 12.1 2024-05-30 23:36:44 +09:00
Claire
d65f8a1196 Bump version to v4.3.0-alpha.4 (#30482) 2024-05-30 23:36:33 +09:00
Claire
2a05566c5c Fix rate-limiting incorrectly triggering a session cookie on most endpoints (#30483) 2024-05-30 23:35:24 +09:00
Claire
b75b26bab4 Merge pull request from GHSA-c2r5-cfqr-c553
* Add hardening monkey-patch to prevent IP spoofing on misconfigured installations

* Remove rack-attack safelist
2024-05-30 23:35:17 +09:00
Claire
993bae2850 Merge pull request from GHSA-q3rg-xx5v-4mxh 2024-05-30 23:35:11 +09:00
Claire
4bfcf0d3f0 Merge pull request from GHSA-5fq7-3p3j-9vrf 2024-05-30 23:35:03 +09:00
Emelia Smith
8e788e260e Fix: remove broken OAuth Application vacuuming & throttle OAuth Application registrations (#30316)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
2024-05-30 23:34:50 +09:00
KMY
5ba5aa5c5c Normalize language code of incoming posts (#30403) 2024-05-30 23:33:59 +09:00
Claire
3807dd2352 Fix leaking Elasticsearch connections in Sidekiq processes (#30450) 2024-05-30 23:31:18 +09:00
Claire
2432d870f5 Update dependency rexml to 3.2.8 2024-05-30 23:29:59 +09:00
KMY(雪あすか)
420316fa1a
Merge pull request #732 from kmycode/kb-draft-12.0
Release: 12.0
2024-05-08 06:53:08 +09:00
KMY
1e25b59ed8 Merge branch 'kb_development' into kb-draft-12.0 2024-04-27 09:01:28 +09:00
KMY
86adad74b4 Bump version to 12.0 2024-04-25 18:32:21 +09:00
KMY
78a20bbc2d Bump ruby version to 3.2.4 2024-04-25 18:31:41 +09:00
70 changed files with 1009 additions and 360 deletions

View file

@ -2,6 +2,92 @@
All notable changes to this project will be documented in this file.
## [4.2.10] - 2024-07-04
### Security
- Fix incorrect permission checking on multiple API endpoints ([GHSA-58x8-3qxw-6hm7](https://github.com/mastodon/mastodon/security/advisories/GHSA-58x8-3qxw-6hm7))
- Fix incorrect authorship checking when processing some activities (CVE-2024-37903, [GHSA-xjvf-fm67-4qc3](https://github.com/mastodon/mastodon/security/advisories/GHSA-xjvf-fm67-4qc3))
- Fix ongoing streaming sessions not being invalidated when application tokens get revoked ([GHSA-vp5r-5pgw-jwqx](https://github.com/mastodon/mastodon/security/advisories/GHSA-vp5r-5pgw-jwqx))
- Update dependencies
### Added
- Add yarn version specification to avoid confusion with Yarn 3 and Yarn 4
### Changed
- Change preview cards generation to skip unusually long URLs ([oneiros](https://github.com/mastodon/mastodon/pull/30854))
- Change search modifiers to be case-insensitive ([Gargron](https://github.com/mastodon/mastodon/pull/30865))
- Change `STATSD_ADDR` handling to emit a warning rather than crashing if the address is unreachable ([timothyjrogers](https://github.com/mastodon/mastodon/pull/30691))
- Change PWA start URL from `/home` to `/` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27377))
### Removed
- Removed dependency on `posix-spawn` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18559))
### Fixed
- Fix scheduled statuses scheduled in less than 5 minutes being immediately published ([danielmbrasil](https://github.com/mastodon/mastodon/pull/30584))
- Fix encoding detection for link cards ([oneiros](https://github.com/mastodon/mastodon/pull/30780))
- Fix `/admin/accounts/:account_id/statuses/:id` for edited posts with media attachments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30819))
- Fix duplicate `@context` attribute in user archive export ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30653))
## [4.2.9] - 2024-05-30
### Security
- Update dependencies
- Fix private mention filtering ([GHSA-5fq7-3p3j-9vrf](https://github.com/mastodon/mastodon/security/advisories/GHSA-5fq7-3p3j-9vrf))
- Fix password change endpoint not being rate-limited ([GHSA-q3rg-xx5v-4mxh](https://github.com/mastodon/mastodon/security/advisories/GHSA-q3rg-xx5v-4mxh))
- Add hardening around rate-limit bypass ([GHSA-c2r5-cfqr-c553](https://github.com/mastodon/mastodon/security/advisories/GHSA-c2r5-cfqr-c553))
### Added
- Add rate-limit on OAuth application registration ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30316))
- Add fallback redirection when getting a webfinger query `WEB_DOMAIN@WEB_DOMAIN` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28592))
- Add `digest` attribute to `Admin::DomainBlock` entity in REST API ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/29092))
### Removed
- Remove superfluous application-level caching in some controllers ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29862))
- Remove aggressive OAuth application vacuuming ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30316))
### Fixed
- Fix leaking Elasticsearch connections in Sidekiq processes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30450))
- Fix language of remote posts not being recognized when using unusual casing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30403))
- Fix off-by-one in `tootctl media` commands ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30306))
- Fix removal of allowed domains (in `LIMITED_FEDERATION_MODE`) not being recorded in the audit log ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/30125))
- Fix not being able to block a subdomain of an already-blocked domain through the API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30119))
- Fix `Idempotency-Key` being ignored when scheduling a post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30084))
- Fix crash when supplying the `FFMPEG_BINARY` environment variable ([timothyjrogers](https://github.com/mastodon/mastodon/pull/30022))
- Fix improper email address validation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29838))
- Fix results/query in `api/v1/featured_tags/suggestions` ([mjankowski](https://github.com/mastodon/mastodon/pull/29597))
- Fix unblocking internationalized domain names under certain conditions ([tribela](https://github.com/mastodon/mastodon/pull/29530))
- Fix admin account created by `mastodon:setup` not being auto-approved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29379))
- Fix reference to non-existent var in CLI maintenance command ([mjankowski](https://github.com/mastodon/mastodon/pull/28363))
## [4.2.8] - 2024-02-23
### Added
- Add hourly task to automatically require approval for new registrations in the absence of moderators ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29318), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/29355))
In order to prevent future abandoned Mastodon servers from being used for spam, harassment and other malicious activity, Mastodon will now automatically switch new user registrations to require moderator approval whenever they are left open and no activity (including non-moderation actions from apps) from any logged-in user with permission to access moderation reports has been detected in a full week.
When this happens, users with the permission to change server settings will receive an email notification.
This feature is disabled when `EMAIL_DOMAIN_ALLOWLIST` is used, and can also be disabled with `DISABLE_AUTOMATIC_SWITCHING_TO_APPROVED_REGISTRATIONS=true`.
### Changed
- Change registrations to be closed by default on new installations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29280))
If you are running a server and never changed your registrations mode from the default, updating will automatically close your registrations.
Simply re-enable them through the administration interface or using `tootctl settings registrations open` if you want to enable them again.
### Fixed
- Fix processing of remote ActivityPub actors making use of `Link` objects as `Image` `url` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29335))
- Fix link verifications when page size exceeds 1MB ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29358))
## [4.2.7] - 2024-02-16
### Fixed

42
Gemfile
View file

@ -9,9 +9,6 @@ gem 'rack', '~> 2.2.7'
gem 'rails', '~> 7.1.1'
gem 'thor', '~> 1.2'
# For why irb is in the Gemfile, see: https://ruby.social/@st0012/111444685161478182
gem 'irb', '~> 1.8'
gem 'dotenv'
gem 'haml-rails', '~>2.0'
gem 'pg', '~> 1.5'
@ -23,11 +20,12 @@ gem 'fog-core', '<= 2.4.0'
gem 'fog-openstack', '~> 1.0', require: false
gem 'kt-paperclip', '~> 7.2'
gem 'md-paperclip-azure', '~> 2.2', require: false
gem 'ruby-vips', '~> 2.2', require: false
gem 'active_model_serializers', '~> 0.10'
gem 'addressable', '~> 2.8'
gem 'bootsnap', '~> 1.18.0', require: false
gem 'browser'
gem 'browser', '< 6' # https://github.com/fnando/browser/issues/543
gem 'charlock_holmes', '~> 0.7.7'
gem 'chewy', '~> 7.3'
gem 'devise', '~> 4.9'
@ -56,10 +54,11 @@ gem 'hiredis', '~> 0.6'
gem 'htmlentities', '~> 4.3'
gem 'http', '~> 5.2.0'
gem 'http_accept_language', '~> 2.1'
gem 'httplog', '~> 1.6.2'
gem 'i18n', '1.14.1' # TODO: Remove version when resolved: https://github.com/glebm/i18n-tasks/issues/552 / https://github.com/ruby-i18n/i18n/pull/688
gem 'httplog', '~> 1.7.0'
gem 'i18n'
gem 'idn-ruby', require: 'idn'
gem 'inline_svg'
gem 'irb', '~> 1.8'
gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0'
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
@ -70,7 +69,8 @@ gem 'oj', '~> 3.14'
gem 'ox', '~> 2.14'
gem 'parslet'
gem 'premailer-rails'
gem 'public_suffix', '~> 5.0'
gem 'private_address_check', '~> 0.5'
gem 'public_suffix', '~> 6.0'
gem 'pundit', '~> 2.3'
gem 'rack-attack', '~> 6.6'
gem 'rack-cors', '~> 2.0', require: 'rack/cors'
@ -101,9 +101,30 @@ gem 'json-ld'
gem 'json-ld-preloaded', '~> 3.2'
gem 'rdf-normalize', '~> 0.5'
gem 'private_address_check', '~> 0.5'
gem 'opentelemetry-api', '~> 1.2.5'
group :opentelemetry do
gem 'opentelemetry-exporter-otlp', '~> 0.28.0', require: false
gem 'opentelemetry-instrumentation-active_job', '~> 0.7.1', require: false
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.20.1', require: false
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.21.2', require: false
gem 'opentelemetry-instrumentation-excon', '~> 0.22.0', require: false
gem 'opentelemetry-instrumentation-faraday', '~> 0.24.1', require: false
gem 'opentelemetry-instrumentation-http', '~> 0.23.2', require: false
gem 'opentelemetry-instrumentation-http_client', '~> 0.22.3', require: false
gem 'opentelemetry-instrumentation-net_http', '~> 0.22.4', require: false
gem 'opentelemetry-instrumentation-pg', '~> 0.27.1', require: false
gem 'opentelemetry-instrumentation-rack', '~> 0.24.1', require: false
gem 'opentelemetry-instrumentation-rails', '~> 0.31.0', require: false
gem 'opentelemetry-instrumentation-redis', '~> 0.25.3', require: false
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.25.2', require: false
gem 'opentelemetry-sdk', '~> 1.4', require: false
end
group :test do
# Enable usage of all available CPUs/cores during spec runs
gem 'flatware-rspec'
# Adds RSpec Error/Warning annotations to GitHub PRs on the Files tab
gem 'rspec-github', '~> 2.4', require: false
@ -114,7 +135,7 @@ group :test do
gem 'email_spec'
# Extra RSpec extension methods and helpers for sidekiq
gem 'rspec-sidekiq', '~> 4.0'
gem 'rspec-sidekiq', '~> 5.0'
# Browser integration testing
gem 'capybara', '~> 3.39'
@ -150,6 +171,7 @@ group :development do
gem 'rubocop-performance', require: false
gem 'rubocop-rails', require: false
gem 'rubocop-rspec', require: false
gem 'rubocop-rspec_rails', require: false
# Annotates modules with schema
gem 'annotate', '~> 3.2'
@ -160,7 +182,7 @@ group :development do
# Preview mail in the browser
gem 'letter_opener', '~> 1.8'
gem 'letter_opener_web', '~> 2.0'
gem 'letter_opener_web', '~> 3.0'
# Security analysis CLI tools
gem 'brakeman', '~> 6.0', require: false

View file

@ -10,35 +10,35 @@ GIT
GEM
remote: https://rubygems.org/
specs:
actioncable (7.1.3.2)
actionpack (= 7.1.3.2)
activesupport (= 7.1.3.2)
actioncable (7.1.3.4)
actionpack (= 7.1.3.4)
activesupport (= 7.1.3.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
actionmailbox (7.1.3.2)
actionpack (= 7.1.3.2)
activejob (= 7.1.3.2)
activerecord (= 7.1.3.2)
activestorage (= 7.1.3.2)
activesupport (= 7.1.3.2)
actionmailbox (7.1.3.4)
actionpack (= 7.1.3.4)
activejob (= 7.1.3.4)
activerecord (= 7.1.3.4)
activestorage (= 7.1.3.4)
activesupport (= 7.1.3.4)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.1.3.2)
actionpack (= 7.1.3.2)
actionview (= 7.1.3.2)
activejob (= 7.1.3.2)
activesupport (= 7.1.3.2)
actionmailer (7.1.3.4)
actionpack (= 7.1.3.4)
actionview (= 7.1.3.4)
activejob (= 7.1.3.4)
activesupport (= 7.1.3.4)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.2)
actionpack (7.1.3.2)
actionview (= 7.1.3.2)
activesupport (= 7.1.3.2)
actionpack (7.1.3.4)
actionview (= 7.1.3.4)
activesupport (= 7.1.3.4)
nokogiri (>= 1.8.5)
racc
rack (>= 2.2.4)
@ -46,15 +46,15 @@ GEM
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
actiontext (7.1.3.2)
actionpack (= 7.1.3.2)
activerecord (= 7.1.3.2)
activestorage (= 7.1.3.2)
activesupport (= 7.1.3.2)
actiontext (7.1.3.4)
actionpack (= 7.1.3.4)
activerecord (= 7.1.3.4)
activestorage (= 7.1.3.4)
activesupport (= 7.1.3.4)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.1.3.2)
activesupport (= 7.1.3.2)
actionview (7.1.3.4)
activesupport (= 7.1.3.4)
builder (~> 3.1)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
@ -64,22 +64,22 @@ GEM
activemodel (>= 4.1)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (7.1.3.2)
activesupport (= 7.1.3.2)
activejob (7.1.3.4)
activesupport (= 7.1.3.4)
globalid (>= 0.3.6)
activemodel (7.1.3.2)
activesupport (= 7.1.3.2)
activerecord (7.1.3.2)
activemodel (= 7.1.3.2)
activesupport (= 7.1.3.2)
activemodel (7.1.3.4)
activesupport (= 7.1.3.4)
activerecord (7.1.3.4)
activemodel (= 7.1.3.4)
activesupport (= 7.1.3.4)
timeout (>= 0.4.0)
activestorage (7.1.3.2)
actionpack (= 7.1.3.2)
activejob (= 7.1.3.2)
activerecord (= 7.1.3.2)
activesupport (= 7.1.3.2)
activestorage (7.1.3.4)
actionpack (= 7.1.3.4)
activejob (= 7.1.3.4)
activerecord (= 7.1.3.4)
activesupport (= 7.1.3.4)
marcel (~> 1.0)
activesupport (7.1.3.2)
activesupport (7.1.3.4)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
@ -89,32 +89,32 @@ GEM
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
aes_key_wrap (1.1.0)
android_key_attestation (0.3.0)
annotate (3.2.0)
activerecord (>= 3.2, < 8.0)
rake (>= 10.4, < 14.0)
ast (2.4.2)
attr_encrypted (4.0.0)
attr_encrypted (4.1.0)
encryptor (~> 3.0.0)
attr_required (1.0.2)
awrence (1.2.1)
aws-eventstream (1.3.0)
aws-partitions (1.916.0)
aws-sdk-core (3.192.1)
aws-partitions (1.951.0)
aws-sdk-core (3.201.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.8)
jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.79.0)
aws-sdk-core (~> 3, >= 3.191.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.147.0)
aws-sdk-core (~> 3, >= 3.192.0)
aws-sdk-kms (1.88.0)
aws-sdk-core (~> 3, >= 3.201.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.156.0)
aws-sdk-core (~> 3, >= 3.201.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.8)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.8.0)
aws-eventstream (~> 1, >= 1.0.2)
azure-storage-blob (2.0.3)
@ -132,14 +132,7 @@ GEM
erubi (>= 1.0.0)
rack (>= 0.9.0)
rouge (>= 1.0.0)
better_html (2.1.1)
actionview (>= 6.0)
activesupport (>= 6.0)
ast (~> 2.0)
erubi (~> 1.4)
parser (>= 2.4)
smart_properties
bigdecimal (3.1.7)
bigdecimal (3.1.8)
bindata (2.5.0)
binding_of_caller (1.0.1)
debug_inspector (>= 1.2.0)
@ -152,7 +145,7 @@ GEM
brpoplpush-redis_script (0.1.3)
concurrent-ruby (~> 1.0, >= 1.0.5)
redis (>= 1.0, < 6)
builder (3.2.4)
builder (3.3.0)
bundler-audit (0.9.1)
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
@ -168,16 +161,16 @@ GEM
case_transform (0.2)
activesupport
cbor (0.5.9.8)
charlock_holmes (0.7.7)
chewy (7.5.1)
charlock_holmes (0.7.8)
chewy (7.6.0)
activesupport (>= 5.2)
elasticsearch (>= 7.12.0, < 7.14.0)
elasticsearch (>= 7.14.0, < 8)
elasticsearch-dsl
chunky_png (1.4.0)
climate_control (1.2.0)
cocoon (1.2.15)
color_diff (0.1)
concurrent-ruby (1.2.3)
concurrent-ruby (1.3.3)
connection_pool (2.4.1)
cose (1.3.0)
cbor (~> 0.5.9)
@ -218,31 +211,32 @@ GEM
activerecord (>= 4.2, < 8)
docile (1.4.0)
domain_name (0.6.20240107)
doorkeeper (5.6.9)
doorkeeper (5.7.1)
railties (>= 5)
dotenv (3.1.0)
dotenv (3.1.2)
drb (2.2.1)
ed25519 (1.3.0)
elasticsearch (7.13.3)
elasticsearch-api (= 7.13.3)
elasticsearch-transport (= 7.13.3)
elasticsearch-api (7.13.3)
elasticsearch (7.17.11)
elasticsearch-api (= 7.17.11)
elasticsearch-transport (= 7.17.11)
elasticsearch-api (7.17.11)
multi_json
elasticsearch-dsl (0.1.10)
elasticsearch-transport (7.13.3)
faraday (~> 1)
elasticsearch-transport (7.17.11)
base64
faraday (>= 1, < 3)
multi_json
email_spec (2.2.2)
htmlentities (~> 4.3.3)
launchy (~> 2.1)
mail (~> 2.7)
encryptor (3.0.0)
erubi (1.12.0)
erubi (1.13.0)
et-orbi (1.2.11)
tzinfo
excon (0.110.0)
fabrication (2.31.0)
faker (3.3.1)
faker (3.4.1)
i18n (>= 1.8.11, < 2)
faraday (1.10.3)
faraday-em_http (~> 1.0)
@ -271,10 +265,24 @@ GEM
faraday (~> 1.0)
fast_blank (1.0.1)
fastimage (2.3.1)
ffi (1.16.3)
ffi (1.17.0-aarch64-linux-gnu)
ffi (1.17.0-aarch64-linux-musl)
ffi (1.17.0-arm-linux-gnu)
ffi (1.17.0-arm-linux-musl)
ffi (1.17.0-arm64-darwin)
ffi (1.17.0-x86-linux-gnu)
ffi (1.17.0-x86-linux-musl)
ffi (1.17.0-x86_64-darwin)
ffi (1.17.0-x86_64-linux-gnu)
ffi (1.17.0-x86_64-linux-musl)
ffi-compiler (1.3.2)
ffi (>= 1.15.5)
rake
flatware (2.3.2)
thor (< 2.0)
flatware-rspec (2.3.2)
flatware (= 2.3.2)
rspec (>= 3.6)
fog-core (2.4.0)
builder
excon (~> 0.71)
@ -283,18 +291,38 @@ GEM
fog-json (1.2.0)
fog-core
multi_json (~> 1.10)
fog-openstack (1.1.0)
fog-openstack (1.1.3)
fog-core (~> 2.1)
fog-json (>= 1.0)
formatador (1.1.0)
fugit (1.10.1)
et-orbi (~> 1, >= 1.2.7)
fugit (1.11.0)
et-orbi (~> 1, >= 1.2.11)
raabro (~> 1.4)
fuubar (2.5.1)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
globalid (1.2.1)
activesupport (>= 6.1)
google-protobuf (4.27.2)
bigdecimal
rake (>= 13)
google-protobuf (4.27.2-aarch64-linux)
bigdecimal
rake (>= 13)
google-protobuf (4.27.2-arm64-darwin)
bigdecimal
rake (>= 13)
google-protobuf (4.27.2-x86-linux)
bigdecimal
rake (>= 13)
google-protobuf (4.27.2-x86_64-darwin)
bigdecimal
rake (>= 13)
google-protobuf (4.27.2-x86_64-linux)
bigdecimal
rake (>= 13)
googleapis-common-protos-types (1.15.0)
google-protobuf (>= 3.18, < 5.a)
haml (6.3.0)
temple (>= 0.8.2)
thor
@ -304,7 +332,7 @@ GEM
activesupport (>= 5.1)
haml (>= 4.0.6)
railties (>= 5.1)
haml_lint (0.57.0)
haml_lint (0.58.0)
haml (>= 5.0)
parallel (~> 1.10)
rainbow
@ -324,20 +352,19 @@ GEM
http-cookie (~> 1.0)
http-form_data (~> 2.2)
llhttp-ffi (~> 0.5.0)
http-cookie (1.0.5)
http-cookie (1.0.6)
domain_name (~> 0.5)
http-form_data (2.3.0)
http_accept_language (2.1.1)
httpclient (2.8.3)
httplog (1.6.3)
httplog (1.7.0)
rack (>= 2.0)
rainbow (>= 2.0.0)
i18n (1.14.1)
i18n (1.14.5)
concurrent-ruby (~> 1.0)
i18n-tasks (1.0.13)
i18n-tasks (1.0.14)
activesupport (>= 4.0.2)
ast (>= 2.1.0)
better_html (>= 1.0, < 3.0)
erubi
highline (>= 2.0.0)
i18n
@ -350,8 +377,8 @@ GEM
activesupport (>= 3.0)
nokogiri (>= 1.6)
io-console (0.7.2)
irb (1.12.0)
rdoc
irb (1.13.2)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
jmespath (1.6.2)
json (2.7.2)
@ -374,7 +401,8 @@ GEM
json-schema (4.3.0)
addressable (>= 2.8)
jsonapi-renderer (0.2.2)
jwt (2.7.1)
jwt (2.8.2)
base64
kaminari (1.2.2)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.2)
@ -398,15 +426,16 @@ GEM
addressable (~> 2.8)
letter_opener (1.10.0)
launchy (>= 2.2, < 4)
letter_opener_web (2.0.0)
actionmailer (>= 5.2)
letter_opener (~> 1.7)
railties (>= 5.2)
letter_opener_web (3.0.0)
actionmailer (>= 6.1)
letter_opener (~> 1.9)
railties (>= 6.1)
rexml
link_header (0.0.8)
llhttp-ffi (0.5.0)
ffi-compiler (~> 1.0)
rake (~> 13.0)
logger (1.6.0)
lograge (0.14.0)
actionpack (>= 4)
activesupport (>= 4)
@ -428,22 +457,21 @@ GEM
addressable (~> 2.5)
azure-storage-blob (~> 2.0.1)
hashie (~> 5.0)
memory_profiler (1.0.1)
memory_profiler (1.0.2)
mime-types (3.5.2)
mime-types-data (~> 3.2015)
mime-types-data (3.2024.0305)
mime-types-data (3.2024.0702)
mini_mime (1.1.5)
mini_portile2 (2.8.6)
minitest (5.22.3)
minitest (5.24.1)
msgpack (1.7.2)
multi_json (1.15.0)
multipart-post (2.4.0)
multipart-post (2.4.1)
mutex_m (0.2.0)
net-http (0.4.1)
uri
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
net-imap (0.4.10)
net-imap (0.4.14)
date
net-protocol
net-ldap (0.19.0)
@ -453,16 +481,25 @@ GEM
timeout
net-smtp (0.5.0)
net-protocol
nio4r (2.7.1)
nokogiri (1.16.4)
mini_portile2 (~> 2.8.2)
nio4r (2.7.3)
nokogiri (1.16.6-aarch64-linux)
racc (~> 1.4)
nokogiri (1.16.6-arm-linux)
racc (~> 1.4)
nokogiri (1.16.6-arm64-darwin)
racc (~> 1.4)
nokogiri (1.16.6-x86-linux)
racc (~> 1.4)
nokogiri (1.16.6-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.16.6-x86_64-linux)
racc (~> 1.4)
nsa (0.3.0)
activesupport (>= 4.2, < 7.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
sidekiq (>= 3.5)
statsd-ruby (~> 1.4, >= 1.4.0)
oj (3.16.3)
oj (3.16.4)
bigdecimal (>= 3.0)
omniauth (2.1.2)
hashie (>= 3.4.6)
@ -472,7 +509,7 @@ GEM
addressable (~> 2.8)
nokogiri (~> 1.12)
omniauth (~> 2.1)
omniauth-rails_csrf_protection (1.0.1)
omniauth-rails_csrf_protection (1.0.2)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth-saml (2.1.0)
@ -495,17 +532,106 @@ GEM
openssl (3.2.0)
openssl-signature_algorithm (1.3.0)
openssl (> 2.0)
opentelemetry-api (1.2.5)
opentelemetry-common (0.21.0)
opentelemetry-api (~> 1.0)
opentelemetry-exporter-otlp (0.28.0)
google-protobuf (>= 3.18)
googleapis-common-protos-types (~> 1.3)
opentelemetry-api (~> 1.1)
opentelemetry-common (~> 0.20)
opentelemetry-sdk (~> 1.2)
opentelemetry-semantic_conventions
opentelemetry-helpers-sql-obfuscation (0.1.1)
opentelemetry-common (~> 0.21)
opentelemetry-instrumentation-action_mailer (0.1.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (~> 0.1)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-action_pack (0.9.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rack (~> 0.21)
opentelemetry-instrumentation-action_view (0.7.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-active_support (~> 0.1)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-active_job (0.7.2)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-active_model_serializers (0.20.1)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-active_record (0.7.2)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-active_support (0.6.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-base (0.22.4)
opentelemetry-api (~> 1.0)
opentelemetry-common (~> 0.21)
opentelemetry-registry (~> 0.1)
opentelemetry-instrumentation-concurrent_ruby (0.21.3)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-excon (0.22.3)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-faraday (0.24.5)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-http (0.23.3)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-http_client (0.22.6)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-net_http (0.22.6)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-pg (0.27.3)
opentelemetry-api (~> 1.0)
opentelemetry-helpers-sql-obfuscation
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rack (0.24.5)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-rails (0.31.0)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-action_mailer (~> 0.1.0)
opentelemetry-instrumentation-action_pack (~> 0.9.0)
opentelemetry-instrumentation-action_view (~> 0.7.0)
opentelemetry-instrumentation-active_job (~> 0.7.0)
opentelemetry-instrumentation-active_record (~> 0.7.0)
opentelemetry-instrumentation-active_support (~> 0.6.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-redis (0.25.6)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-instrumentation-sidekiq (0.25.6)
opentelemetry-api (~> 1.0)
opentelemetry-instrumentation-base (~> 0.22.1)
opentelemetry-registry (0.3.1)
opentelemetry-api (~> 1.1)
opentelemetry-sdk (1.4.1)
opentelemetry-api (~> 1.1)
opentelemetry-common (~> 0.20)
opentelemetry-registry (~> 0.2)
opentelemetry-semantic_conventions
opentelemetry-semantic_conventions (1.10.0)
opentelemetry-api (~> 1.0)
orm_adapter (0.5.0)
ox (2.14.18)
parallel (1.24.0)
parser (3.3.0.5)
parallel (1.25.1)
parser (3.3.3.0)
ast (~> 2.4.1)
racc
parslet (2.0.0)
pastel (0.8.0)
tty-color (~> 0.5)
pg (1.5.6)
pghero (3.4.1)
pghero (3.5.0)
activerecord (>= 6)
premailer (1.23.0)
addressable
@ -516,20 +642,20 @@ GEM
net-smtp
premailer (~> 1.7, >= 1.7.9)
private_address_check (0.5.0)
propshaft (0.8.0)
propshaft (0.9.0)
actionpack (>= 7.0.0)
activesupport (>= 7.0.0)
rack
railties (>= 7.0.0)
psych (5.1.2)
stringio
public_suffix (5.0.5)
public_suffix (6.0.0)
puma (6.4.2)
nio4r (~> 2.0)
pundit (2.3.1)
pundit (2.3.2)
activesupport (>= 3.0.0)
raabro (1.4.0)
racc (1.7.3)
racc (1.8.0)
rack (2.2.9)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
@ -553,20 +679,20 @@ GEM
rackup (1.0.0)
rack (< 3)
webrick
rails (7.1.3.2)
actioncable (= 7.1.3.2)
actionmailbox (= 7.1.3.2)
actionmailer (= 7.1.3.2)
actionpack (= 7.1.3.2)
actiontext (= 7.1.3.2)
actionview (= 7.1.3.2)
activejob (= 7.1.3.2)
activemodel (= 7.1.3.2)
activerecord (= 7.1.3.2)
activestorage (= 7.1.3.2)
activesupport (= 7.1.3.2)
rails (7.1.3.4)
actioncable (= 7.1.3.4)
actionmailbox (= 7.1.3.4)
actionmailer (= 7.1.3.4)
actionpack (= 7.1.3.4)
actiontext (= 7.1.3.4)
actionview (= 7.1.3.4)
activejob (= 7.1.3.4)
activemodel (= 7.1.3.4)
activerecord (= 7.1.3.4)
activestorage (= 7.1.3.4)
activesupport (= 7.1.3.4)
bundler (>= 1.15.0)
railties (= 7.1.3.2)
railties (= 7.1.3.4)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
@ -581,9 +707,9 @@ GEM
rails-i18n (7.0.9)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
railties (7.1.3.2)
actionpack (= 7.1.3.2)
activesupport (= 7.1.3.2)
railties (7.1.3.4)
actionpack (= 7.1.3.4)
activesupport (= 7.1.3.4)
irb
rackup (>= 1.0.0)
rake (>= 12.2)
@ -596,7 +722,7 @@ GEM
link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.7.0)
rdf (~> 3.3)
rdoc (6.6.3.1)
rdoc (6.7.0)
psych (>= 4.0.0)
redcarpet (3.6.0)
redis (4.8.1)
@ -604,33 +730,38 @@ GEM
redis (>= 4)
redlock (1.3.2)
redis (>= 3.0.0, < 6.0)
regexp_parser (2.9.0)
reline (0.5.2)
regexp_parser (2.9.2)
reline (0.5.9)
io-console (~> 0.5)
request_store (1.6.0)
request_store (1.7.0)
rack (>= 1.4)
responders (3.1.1)
actionpack (>= 5.2)
railties (>= 5.2)
rexml (3.2.6)
rexml (3.3.1)
strscan
rotp (6.3.0)
rouge (4.2.1)
rouge (4.3.0)
rpam2 (4.0.2)
rqrcode (2.2.0)
chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
rqrcode_core (1.2.0)
rspec (3.13.0)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.0)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.0)
rspec-expectations (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-github (2.4.0)
rspec-core (~> 3.0)
rspec-mocks (3.13.0)
rspec-mocks (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-rails (6.1.2)
rspec-rails (6.1.3)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
@ -638,13 +769,13 @@ GEM
rspec-expectations (~> 3.13)
rspec-mocks (~> 3.13)
rspec-support (~> 3.13)
rspec-sidekiq (4.2.0)
rspec-sidekiq (5.0.0)
rspec-core (~> 3.0)
rspec-expectations (~> 3.0)
rspec-mocks (~> 3.0)
sidekiq (>= 5, < 8)
rspec-support (3.13.1)
rubocop (1.63.3)
rubocop (1.64.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
@ -655,46 +786,45 @@ GEM
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.2)
parser (>= 3.3.0.4)
rubocop-capybara (2.20.0)
rubocop-ast (1.31.3)
parser (>= 3.3.1.0)
rubocop-capybara (2.21.0)
rubocop (~> 1.41)
rubocop-factory_bot (2.25.1)
rubocop (~> 1.41)
rubocop-performance (1.21.0)
rubocop-performance (1.21.1)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rails (2.24.1)
rubocop-rails (2.25.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rspec (2.29.1)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
rubocop-rspec_rails (~> 2.28)
rubocop-rspec_rails (2.28.3)
rubocop (~> 1.40)
rubocop-rspec (3.0.2)
rubocop (~> 1.61)
rubocop-rspec_rails (2.30.0)
rubocop (~> 1.61)
rubocop-rspec (~> 3, >= 3.0.1)
ruby-prof (1.7.0)
ruby-progressbar (1.13.0)
ruby-saml (1.16.0)
nokogiri (>= 1.13.10)
rexml
ruby-vips (2.2.1)
ffi (~> 1.12)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
rufus-scheduler (3.9.1)
fugit (~> 1.1, >= 1.1.6)
safety_net_attestation (0.4.0)
jwt (~> 2.0)
sanitize (6.1.0)
sanitize (6.1.1)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
scenic (1.8.0)
activerecord (>= 4.0.0)
railties (>= 4.0.0)
selenium-webdriver (4.20.1)
selenium-webdriver (4.22.0)
base64 (~> 0.2)
logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
@ -705,10 +835,10 @@ GEM
redis (>= 4.5.0, < 5)
sidekiq-bulk (0.2.0)
sidekiq
sidekiq-scheduler (5.0.3)
sidekiq-scheduler (5.0.5)
rufus-scheduler (~> 3.2)
sidekiq (>= 6, < 8)
tilt (>= 1.4.0)
tilt (>= 1.4.0, < 3)
sidekiq-unique-jobs (7.1.33)
brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
concurrent-ruby (~> 1.0, >= 1.0.5)
@ -717,7 +847,7 @@ GEM
thor (>= 0.20, < 3.0)
simple-navigation (4.4.0)
activesupport (>= 2.3.2)
simple_form (5.3.0)
simple_form (5.3.1)
actionpack (>= 5.2)
activemodel (>= 5.2)
simplecov (0.22.0)
@ -727,14 +857,14 @@ GEM
simplecov-html (0.12.3)
simplecov-lcov (0.8.0)
simplecov_json_formatter (0.1.4)
smart_properties (1.17.0)
stackprof (0.2.26)
statsd-ruby (1.5.0)
stoplight (4.1.0)
redlock (~> 1.0)
stringio (3.1.0)
stringio (3.1.1)
strong_migrations (1.8.0)
activerecord (>= 5.2)
strscan (3.1.0)
swd (1.3.0)
activesupport (>= 3)
attr_required (>= 0.0.5)
@ -745,9 +875,9 @@ GEM
unicode-display_width (>= 1.1.1, < 3)
terrapin (1.0.1)
climate_control
test-prof (1.3.3)
test-prof (1.3.3.1)
thor (1.3.1)
tilt (2.3.0)
tilt (2.4.0)
timeout (0.4.1)
tpm-key_attestation (0.12.0)
bindata (~> 2.4)
@ -795,7 +925,7 @@ GEM
webfinger (1.2.0)
activesupport
httpclient (>= 2.4)
webmock (3.23.0)
webmock (3.23.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@ -813,10 +943,22 @@ GEM
xorcist (1.1.3)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.13)
zeitwerk (2.6.16)
PLATFORMS
ruby
aarch64-linux
aarch64-linux-gnu
aarch64-linux-musl
arm-linux
arm-linux-gnu
arm-linux-musl
arm64-darwin
x86-linux
x86-linux-gnu
x86-linux-musl
x86_64-darwin
x86_64-linux-gnu
x86_64-linux-musl
DEPENDENCIES
active_model_serializers (~> 0.10)
@ -828,7 +970,7 @@ DEPENDENCIES
blurhash (~> 0.1)
bootsnap (~> 1.18.0)
brakeman (~> 6.0)
browser
browser (< 6)
bundler-audit (~> 0.9)
capybara (~> 3.39)
charlock_holmes (~> 0.7.7)
@ -853,6 +995,7 @@ DEPENDENCIES
faker (~> 3.2)
fast_blank (~> 1.0)
fastimage
flatware-rspec
fog-core (<= 2.4.0)
fog-openstack (~> 1.0)
fuubar (~> 2.5)
@ -863,8 +1006,8 @@ DEPENDENCIES
htmlentities (~> 4.3)
http (~> 5.2.0)
http_accept_language (~> 2.1)
httplog (~> 1.6.2)
i18n (= 1.14.1)
httplog (~> 1.7.0)
i18n
i18n-tasks (~> 1.0)
idn-ruby
inline_svg
@ -875,7 +1018,7 @@ DEPENDENCIES
kaminari (~> 1.2)
kt-paperclip (~> 7.2)
letter_opener (~> 1.8)
letter_opener_web (~> 2.0)
letter_opener_web (~> 3.0)
link_header (~> 0.0)
lograge (~> 0.12)
mail (~> 2.8)
@ -893,6 +1036,22 @@ DEPENDENCIES
omniauth-rails_csrf_protection (~> 1.0)
omniauth-saml (~> 2.0)
omniauth_openid_connect (~> 0.6.1)
opentelemetry-api (~> 1.2.5)
opentelemetry-exporter-otlp (~> 0.28.0)
opentelemetry-instrumentation-active_job (~> 0.7.1)
opentelemetry-instrumentation-active_model_serializers (~> 0.20.1)
opentelemetry-instrumentation-concurrent_ruby (~> 0.21.2)
opentelemetry-instrumentation-excon (~> 0.22.0)
opentelemetry-instrumentation-faraday (~> 0.24.1)
opentelemetry-instrumentation-http (~> 0.23.2)
opentelemetry-instrumentation-http_client (~> 0.22.3)
opentelemetry-instrumentation-net_http (~> 0.22.4)
opentelemetry-instrumentation-pg (~> 0.27.1)
opentelemetry-instrumentation-rack (~> 0.24.1)
opentelemetry-instrumentation-rails (~> 0.31.0)
opentelemetry-instrumentation-redis (~> 0.25.3)
opentelemetry-instrumentation-sidekiq (~> 0.25.2)
opentelemetry-sdk (~> 1.4)
ox (~> 2.14)
parslet
pg (~> 1.5)
@ -900,7 +1059,7 @@ DEPENDENCIES
premailer-rails
private_address_check (~> 0.5)
propshaft
public_suffix (~> 5.0)
public_suffix (~> 6.0)
puma (~> 6.3)
pundit (~> 2.3)
rack (~> 2.2.7)
@ -917,14 +1076,16 @@ DEPENDENCIES
rqrcode (~> 2.2)
rspec-github (~> 2.4)
rspec-rails (~> 6.0)
rspec-sidekiq (~> 4.0)
rspec-sidekiq (~> 5.0)
rubocop
rubocop-capybara
rubocop-performance
rubocop-rails
rubocop-rspec
rubocop-rspec_rails
ruby-prof
ruby-progressbar (~> 1.13)
ruby-vips (~> 2.2)
rubyzip (~> 2.3)
sanitize (~> 6.0)
scenic (~> 1.7)
@ -952,7 +1113,7 @@ DEPENDENCIES
xorcist (~> 1.1)
RUBY VERSION
ruby 3.2.3p157
ruby 3.2.4p170
BUNDLED WITH
2.5.9

View file

@ -6,6 +6,7 @@ class Api::V1::ScheduledStatusesController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, except: [:update, :destroy]
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:update, :destroy]
before_action :require_user!
before_action :set_statuses, only: :index
before_action :set_status, except: :index

View file

@ -2,6 +2,7 @@
class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
before_action :require_user!
before_action :set_translation
rescue_from TranslationService::NotConfiguredError, with: :not_found

View file

@ -3,8 +3,14 @@
class Api::V1::Timelines::BaseController < Api::BaseController
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
before_action :require_user!, if: :require_auth?
private
def require_auth?
!Setting.timeline_preview
end
def pagination_collection
@statuses
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
before_action :require_user!, only: [:show], if: :require_auth?
before_action -> { authorize_if_got_token! :read, :'read:statuses' }
PERMITTED_PARAMS = %i(local remote limit only_media).freeze
@ -15,10 +15,6 @@ class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
private
def require_auth?
!Setting.timeline_preview
end
def load_statuses
cached_public_statuses_page
end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true
class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth?
before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :load_tag
PERMITTED_PARAMS = %i(local limit only_media).freeze

View file

@ -44,7 +44,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
end
def build_resource(hash = nil)
super(hash)
super
resource.locale = I18n.locale
resource.invite_code = @invite&.code if resource.invite_code.blank?

View file

@ -17,6 +17,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
def destroy
Web::PushSubscription.unsubscribe_for(params[:id], current_resource_owner)
Doorkeeper::Application.find_by(id: params[:id])&.close_streaming_sessions(current_resource_owner)
super
end

View file

@ -26,7 +26,7 @@ module RegistrationLimitationHelper
end
def today_increase_user_count_value
User.confirmed.enabled.where('users.created_at >= ?', Time.now.utc.beginning_of_day).joins(:account).merge(Account.without_suspended).count
User.confirmed.enabled.where(users: { created_at: Time.now.utc.beginning_of_day.. }).joins(:account).merge(Account.without_suspended).count
end
def registrations_in_time?

View file

@ -118,7 +118,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def find_existing_status
status = status_from_uri(object_uri)
status ||= Status.find_by(uri: @object['atomUri']) if @object['atomUri'].present?
status
status if status&.account_id == @account.id
end
def process_status_params

View file

@ -3,6 +3,8 @@
class ActivityPub::Parser::StatusParser
include JsonLdHelper
NORMALIZED_LOCALE_NAMES = LanguagesHelper::SUPPORTED_LOCALES.keys.index_by(&:downcase).freeze
# @param [Hash] json
# @param [Hash] options
# @option options [String] :followers_collection
@ -118,10 +120,13 @@ class ActivityPub::Parser::StatusParser
end
def language
@language ||= original_language || (misskey_software? ? 'ja' : nil)
lang = raw_language_code || (misskey_software? ? 'ja' : nil)
lang.presence && NORMALIZED_LOCALE_NAMES.fetch(lang.downcase.to_sym, lang)
end
def original_language
private
def raw_language_code
if content_language_map?
@object['contentMap'].keys.first
elsif name_language_map?
@ -131,8 +136,6 @@ class ActivityPub::Parser::StatusParser
end
end
private
def audience_to
as_array(@object['to'] || @json['to']).map { |x| value_or_id(x) }
end

View file

@ -33,6 +33,6 @@ class ActivityPub::Serializer < ActiveModel::Serializer
adapter_options[:named_contexts].merge!(_named_contexts)
adapter_options[:context_extensions].merge!(_context_extensions)
end
super(adapter_options, options, adapter_instance)
super
end
end

View file

@ -16,17 +16,25 @@ module ApplicationExtension
# dependent: delete_all, which means the ActiveRecord callback in
# AccessTokenExtension is not run, so instead we manually announce to
# streaming that these tokens are being deleted.
before_destroy :push_to_streaming_api, prepend: true
before_destroy :close_streaming_sessions, prepend: true
end
def confirmation_redirect_uri
redirect_uri.lines.first.strip
end
def push_to_streaming_api
def redirect_uris
# Doorkeeper stores the redirect_uri value as a newline delimeted list in
# the database:
redirect_uri.split
end
def close_streaming_sessions(resource_owner = nil)
# TODO: #28793 Combine into a single topic
payload = Oj.dump(event: :kill)
access_tokens.in_batches do |tokens|
scope = access_tokens
scope = scope.where(resource_owner_id: resource_owner.id) unless resource_owner.nil?
scope.in_batches do |tokens|
redis.pipelined do |pipeline|
tokens.ids.each do |id|
pipeline.publish("timeline:access_token:#{id}", payload)

View file

@ -5,7 +5,7 @@ require_relative 'shared_timed_stack'
class ConnectionPool::SharedConnectionPool < ConnectionPool
def initialize(options = {}, &block)
super(options, &block)
super
@available = ConnectionPool::SharedTimedStack.new(@size, &block)
end

View file

@ -28,8 +28,8 @@ class Importer::PublicStatusesIndexImporter < Importer::BaseImporter
def scope
to_index = Status.indexable.reorder(nil)
to_index = to_index.where('statuses.created_at >= ?', @from) if @from.present?
to_index = to_index.where('statuses.created_at < ?', @to) if @to.present?
to_index = to_index.where(statuses: { created_at: @from.. }) if @from.present?
to_index = to_index.where(statuses: { created_at: ...@to }) if @to.present?
to_index
end
end

View file

@ -17,8 +17,8 @@ class Importer::StatusesIndexImporter < Importer::BaseImporter
bulk = ActiveRecord::Base.connection_pool.with_connection do
to_index = index.adapter.default_scope.where(id: status_ids)
to_index = to_index.where('created_at >= ?', @from) if @from.present?
to_index = to_index.where('created_at < ?', @to) if @to.present?
to_index = to_index.where(created_at: @from..) if @from.present?
to_index = to_index.where(created_at: ...@to) if @to.present?
crutches = Chewy::Index::Crutch::Crutches.new index, to_index
to_index.map do |object|
# This is unlikely to happen, but the post may have been

View file

@ -265,16 +265,21 @@ class LinkDetailsExtractor
end
def document
@document ||= Nokogiri::HTML(@html, nil, encoding)
@document ||= detect_encoding_and_parse_document
end
def encoding
@encoding ||= begin
guess = detector.detect(@html, @html_charset)
guess&.fetch(:confidence, 0).to_i > 60 ? guess&.fetch(:encoding, nil) : nil
def detect_encoding_and_parse_document
[detect_encoding, nil, @html_charset, 'UTF-8'].uniq.each do |encoding|
document = Nokogiri::HTML(@html, nil, encoding)
return document if document.to_s.valid_encoding?
end
end
def detect_encoding
guess = detector.detect(@html, @html_charset)
guess&.fetch(:confidence, 0).to_i > 60 ? guess&.fetch(:encoding, nil) : nil
end
def detector
@detector ||= CharlockHolmes::EncodingDetector.new.tap do |detector|
detector.strip_tags = true

View file

@ -2,7 +2,7 @@
class RSS::Channel < RSS::Element
def initialize
super()
super
@root = create_element('channel')
end

View file

@ -2,7 +2,7 @@
class RSS::Item < RSS::Element
def initialize
super()
super
@root = create_element('item')
end

View file

@ -412,7 +412,7 @@ class SearchQueryTransformer < Parslet::Transform
end
rule(clause: subtree(:clause)) do
prefix = clause[:prefix][:term].to_s if clause[:prefix]
prefix = clause[:prefix][:term].to_s.downcase if clause[:prefix]
operator = clause[:operator]&.to_s
term = clause[:phrase] ? clause[:phrase].map { |term| term[:term].to_s }.join(' ') : clause[:term].to_s

View file

@ -1,10 +0,0 @@
# frozen_string_literal: true
class Vacuum::ApplicationsVacuum
def perform
Doorkeeper::Application.where(owner_id: nil)
.where.missing(:created_users, :access_tokens, :access_grants)
.where(created_at: ...1.day.ago)
.in_batches.delete_all
end
end

View file

@ -9,10 +9,10 @@ class Vacuum::ImportsVacuum
private
def clean_unconfirmed_imports!
BulkImport.state_unconfirmed.where('created_at <= ?', 10.minutes.ago).reorder(nil).in_batches.delete_all
BulkImport.state_unconfirmed.where(created_at: ..10.minutes.ago).reorder(nil).in_batches.delete_all
end
def clean_old_imports!
BulkImport.where('created_at <= ?', 1.week.ago).reorder(nil).in_batches.delete_all
BulkImport.where(created_at: ..1.week.ago).reorder(nil).in_batches.delete_all
end
end

View file

@ -12,6 +12,6 @@ class Vacuum::ListStatusesVacuum
private
def vacuum_list_statuses!
ListStatus.where('created_at < ?', LIST_STATUS_LIFE_DURATION.ago).in_batches.destroy_all
ListStatus.where(created_at: ...LIST_STATUS_LIFE_DURATION.ago).in_batches.destroy_all
end
end

View file

@ -12,7 +12,7 @@ class Vacuum::NgHistoriesVacuum
private
def vacuum_histories!
NgwordHistory.where('created_at < ?', HISTORY_LIFE_DURATION.ago).in_batches.destroy_all
NgRuleHistory.where('created_at < ?', HISTORY_LIFE_DURATION.ago).in_batches.destroy_all
NgwordHistory.where(created_at: ...HISTORY_LIFE_DURATION.ago).in_batches.destroy_all
NgRuleHistory.where(created_at: ...HISTORY_LIFE_DURATION.ago).in_batches.destroy_all
end
end

View file

@ -34,7 +34,7 @@ class Vacuum::StatusesVacuum
def statuses_scope
scope = Status.unscoped.kept
.joins(:account).merge(Account.remote)
.where('statuses.id < ?', retention_period_as_id)
.where(statuses: { id: ...retention_period_as_id })
if Setting.delete_content_cache_without_reaction
scope = scope.where.not(id: favourited_statuses)

View file

@ -212,7 +212,7 @@ module Account::Interactions
return false unless local?
scope = followers
scope = scope.where('follows.created_at < ?', since) if since.present?
scope = scope.where(follows: { created_at: ...since }) if since.present?
scope.exists?(domain: other_domain)
end

View file

@ -23,7 +23,7 @@ module Attachmentable
included do
def self.has_attached_file(name, options = {}) # rubocop:disable Naming/PredicateName
super(name, options)
super
send(:"before_#{name}_validate", prepend: true) do
attachment = send(name)

View file

@ -4,7 +4,7 @@ module Expireable
extend ActiveSupport::Concern
included do
scope :expired, -> { where.not(expires_at: nil).where('expires_at < ?', Time.now.utc) }
scope :expired, -> { where.not(expires_at: nil).where(expires_at: ...Time.now.utc) }
def expires_in
return @expires_in if defined?(@expires_in)

View file

@ -24,7 +24,7 @@ class Invite < ApplicationRecord
belongs_to :user, inverse_of: :invites
has_many :users, inverse_of: :invite, dependent: nil
scope :available, -> { where(expires_at: nil).or(where('expires_at >= ?', Time.now.utc)) }
scope :available, -> { where(expires_at: nil).or(where(expires_at: Time.now.utc..)) }
validates :comment, length: { maximum: COMMENT_SIZE_LIMIT }

View file

@ -394,7 +394,7 @@ class Status < ApplicationRecord
end
def add_status_referred_by_count!(diff)
update_status_stat!(status_referred_by_count: [public_send(:status_referred_by_count) + diff, 0].max)
update_status_stat!(status_referred_by_count: [status_referred_by_count + diff, 0].max)
end
def emoji_reactions_grouped_by_name(account = nil, **options)

View file

@ -42,8 +42,8 @@ class StatusEdit < ApplicationRecord
scope :ordered, -> { order(id: :asc) }
delegate :local?, :application, :edited?, :edited_at, :language,
:discarded?, :visibility, to: :status
delegate :local?, :application, :edited?, :edited_at,
:discarded?, :visibility, :language, to: :status
def emojis
return @emojis if defined?(@emojis)

View file

@ -4,6 +4,6 @@ class BackupPolicy < ApplicationPolicy
MIN_AGE = 6.days
def create?
user_signed_in? && current_user.backups.where('created_at >= ?', MIN_AGE.ago).count.zero?
user_signed_in? && current_user.backups.where(created_at: MIN_AGE.ago..).count.zero?
end
end

View file

@ -19,8 +19,8 @@ class BackupService < BaseService
def build_outbox_json!(file)
skeleton = serialize(collection_presenter, ActivityPub::CollectionSerializer)
skeleton[:@context] = full_context
skeleton[:orderedItems] = ['!PLACEHOLDER!']
skeleton['@context'] = full_context
skeleton['orderedItems'] = ['!PLACEHOLDER!']
skeleton = Oj.dump(skeleton)
prepend, append = skeleton.split('"!PLACEHOLDER!"')
add_comma = false

View file

@ -16,6 +16,9 @@ class FetchLinkCardService < BaseService
)
}iox
# URL size limit to safely store in PosgreSQL's unique indexes
BYTESIZE_LIMIT = 2692
def call(status)
@status = status
@original_url = parse_urls
@ -106,7 +109,7 @@ class FetchLinkCardService < BaseService
def bad_url?(uri)
# Avoid local instance URLs and invalid URLs
uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme) ||
uri.host.blank? || TagManager.instance.local_url?(uri.to_s) || !%w(http https).include?(uri.scheme) || uri.to_s.bytesize > BYTESIZE_LIMIT ||
referenced_urls.include?(uri.to_s) || Setting.stop_link_preview_domains&.include?(uri.host)
end

View file

@ -150,6 +150,9 @@ class NotifyService < BaseService
end
def statuses_that_mention_sender
# This queries private mentions from the recipient to the sender up in the thread.
# This allows up to 100 messages that do not match in the thread, allowing conversations
# involving multiple people.
Status.count_by_sql([<<-SQL.squish, id: @notification.target_status.in_reply_to_id, recipient_id: @recipient.id, sender_id: @sender.id, depth_limit: 100])
WITH RECURSIVE ancestors(id, in_reply_to_id, mention_id, path, depth) AS (
SELECT s.id, s.in_reply_to_id, m.id, ARRAY[s.id], 0
@ -157,16 +160,17 @@ class NotifyService < BaseService
LEFT JOIN mentions m ON m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id
WHERE s.id = :id
UNION ALL
SELECT s.id, s.in_reply_to_id, m.id, st.path || s.id, st.depth + 1
FROM ancestors st
JOIN statuses s ON s.id = st.in_reply_to_id
LEFT JOIN mentions m ON m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id
WHERE st.mention_id IS NULL AND NOT s.id = ANY(path) AND st.depth < :depth_limit
SELECT s.id, s.in_reply_to_id, m.id, ancestors.path || s.id, ancestors.depth + 1
FROM ancestors
JOIN statuses s ON s.id = ancestors.in_reply_to_id
/* early exit if we already have a mention matching our requirements */
LEFT JOIN mentions m ON m.silent = FALSE AND m.account_id = :sender_id AND m.status_id = s.id AND s.account_id = :recipient_id
WHERE ancestors.mention_id IS NULL AND NOT s.id = ANY(path) AND ancestors.depth < :depth_limit
)
SELECT COUNT(*)
FROM ancestors st
JOIN statuses s ON s.id = st.id
WHERE st.mention_id IS NOT NULL AND s.visibility = 3
FROM ancestors
JOIN statuses s ON s.id = ancestors.id
WHERE ancestors.mention_id IS NOT NULL AND s.account_id = :recipient_id AND s.visibility = 3
SQL
end
end

View file

@ -327,7 +327,7 @@ class PostStatusService < BaseService
end
def scheduled_in_the_past?
@scheduled_at.present? && @scheduled_at <= Time.now.utc + MIN_SCHEDULE_OFFSET
@scheduled_at.present? && @scheduled_at <= Time.now.utc
end
def bump_potential_friendship!

View file

@ -16,11 +16,11 @@ class Scheduler::IpCleanupScheduler
private
def clean_ip_columns!
SessionActivation.where('updated_at < ?', SESSION_RETENTION_PERIOD.ago).in_batches.destroy_all
SessionActivation.where('updated_at < ?', IP_RETENTION_PERIOD.ago).in_batches.update_all(ip: nil)
User.where('current_sign_in_at < ?', IP_RETENTION_PERIOD.ago).in_batches.update_all(sign_up_ip: nil)
LoginActivity.where('created_at < ?', IP_RETENTION_PERIOD.ago).in_batches.destroy_all
Doorkeeper::AccessToken.where('last_used_at < ?', IP_RETENTION_PERIOD.ago).in_batches.update_all(last_used_ip: nil)
SessionActivation.where(updated_at: ...SESSION_RETENTION_PERIOD.ago).in_batches.destroy_all
SessionActivation.where(updated_at: ...IP_RETENTION_PERIOD.ago).in_batches.update_all(ip: nil)
User.where(current_sign_in_at: ...IP_RETENTION_PERIOD.ago).in_batches.update_all(sign_up_ip: nil)
LoginActivity.where(created_at: ...IP_RETENTION_PERIOD.ago).in_batches.destroy_all
Doorkeeper::AccessToken.where(last_used_at: ...IP_RETENTION_PERIOD.ago).in_batches.update_all(last_used_ip: nil)
end
def clean_expired_ip_blocks!

View file

@ -27,11 +27,11 @@ class Scheduler::ScheduledStatusesScheduler
end
def due_statuses
ScheduledStatus.where('scheduled_at <= ?', Time.now.utc + PostStatusService::MIN_SCHEDULE_OFFSET)
ScheduledStatus.where(scheduled_at: ..(Time.now.utc + PostStatusService::MIN_SCHEDULE_OFFSET))
end
def expired_statuses
ScheduledExpirationStatus.where('scheduled_at <= ?', Time.now.utc + PostStatusService::MIN_SCHEDULE_OFFSET)
ScheduledExpirationStatus.where(scheduled_at: ..(Time.now.utc + PostStatusService::MIN_SCHEDULE_OFFSET))
end
def publish_scheduled_announcements!

View file

@ -22,7 +22,7 @@ class Scheduler::UserCleanupScheduler
end
def clean_discarded_statuses!
Status.unscoped.discarded.where('deleted_at <= ?', 30.days.ago).find_in_batches do |statuses|
Status.unscoped.discarded.where(deleted_at: ..30.days.ago).find_in_batches do |statuses|
RemovalWorker.push_bulk(statuses) do |status|
[status.id, { 'immediate' => true, 'skip_streaming' => true }]
end

View file

@ -22,7 +22,6 @@ class Scheduler::VacuumScheduler
preview_cards_vacuum,
backups_vacuum,
access_tokens_vacuum,
applications_vacuum,
feeds_vacuum,
imports_vacuum,
list_statuses_vacuum,
@ -62,10 +61,6 @@ class Scheduler::VacuumScheduler
Vacuum::ImportsVacuum.new
end
def applications_vacuum
Vacuum::ApplicationsVacuum.new
end
def ng_histories_vacuum
Vacuum::NgHistoriesVacuum.new
end

View file

@ -47,6 +47,7 @@ require_relative '../lib/chewy/strategy/bypass_with_warning'
require_relative '../lib/webpacker/manifest_extensions'
require_relative '../lib/webpacker/helper_extensions'
require_relative '../lib/rails/engine_extensions'
require_relative '../lib/action_dispatch/remote_ip_extensions'
require_relative '../lib/active_record/database_tasks_extensions'
require_relative '../lib/active_record/batches'
require_relative '../lib/simple_navigation/item_extensions'

View file

@ -37,6 +37,10 @@ class Rack::Attack
authenticated_token&.id
end
def warden_user_id
@env['warden']&.user&.id
end
def unauthenticated?
!authenticated_user_id
end
@ -58,10 +62,6 @@ class Rack::Attack
end
end
Rack::Attack.safelist('allow from localhost') do |req|
req.remote_ip == '127.0.0.1' || req.remote_ip == '::1'
end
Rack::Attack.blocklist('deny from blocklist') do |req|
IpBlock.blocked?(req.remote_ip)
end
@ -105,6 +105,10 @@ class Rack::Attack
req.authenticated_user_id if (req.post? && req.path.match?(API_DELETE_REBLOG_REGEX)) || (req.delete? && req.path.match?(API_DELETE_STATUS_REGEX))
end
throttle('throttle_oauth_application_registrations/ip', limit: 5, period: 10.minutes) do |req|
req.throttleable_remote_ip if req.post? && req.path == '/api/v1/apps'
end
throttle('throttle_sign_up_attempts/ip', limit: 25, period: 5.minutes) do |req|
req.throttleable_remote_ip if req.post? && req.path_matches?('/auth')
end
@ -137,6 +141,12 @@ class Rack::Attack
req.session[:attempt_user_id] || req.params.dig('user', 'email').presence if req.post? && req.path_matches?('/auth/sign_in')
end
API_CREATE_EMOJI_REACTION_REGEX = %r{\A/api/v1/statuses/\d+/emoji_reactions}
throttle('throttle_password_change/account', limit: 10, period: 10.minutes) do |req|
req.warden_user_id if (req.put? && !req.path.match?(API_CREATE_EMOJI_REACTION_REGEX)) || (req.patch? && req.path_matches?('/auth'))
end
self.throttled_responder = lambda do |request|
now = Time.now.utc
match_data = request.env['rack.attack.match_data']

View file

@ -3,13 +3,17 @@
if ENV['STATSD_ADDR'].present?
host, port = ENV['STATSD_ADDR'].split(':')
statsd = Statsd.new(host, port)
statsd.namespace = ENV.fetch('STATSD_NAMESPACE') { ['Mastodon', Rails.env].join('.') }
begin
statsd = Statsd.new(host, port)
statsd.namespace = ENV.fetch('STATSD_NAMESPACE') { ['Mastodon', Rails.env].join('.') }
NSA.inform_statsd(statsd) do |informant|
informant.collect(:action_controller, :web)
informant.collect(:active_record, :db)
informant.collect(:active_support_cache, :cache)
informant.collect(:sidekiq, :sidekiq) if ENV['STATSD_SIDEKIQ'] == 'true'
NSA.inform_statsd(statsd) do |informant|
informant.collect(:action_controller, :web)
informant.collect(:active_record, :db)
informant.collect(:active_support_cache, :cache)
informant.collect(:sidekiq, :sidekiq) if ENV['STATSD_SIDEKIQ'] == 'true'
end
rescue
Rails.logger.warn("statsd address #{ENV['STATSD_ADDR']} not reachable, proceeding without statsd")
end
end

View file

@ -1,4 +1,3 @@
version: '3'
services:
db:
restart: always
@ -56,7 +55,7 @@ services:
web:
build: .
image: ghcr.io/mastodon/mastodon:v4.2.7
image: ghcr.io/mastodon/mastodon:v4.2.10
restart: always
env_file: .env.production
command: bundle exec puma -C config/puma.rb
@ -76,10 +75,8 @@ services:
- ./public/system:/mastodon/public/system
streaming:
build:
context: .
dockerfile: streaming/Dockerfile
image: ghcr.io/mastodon/mastodon:v4.2.7
build: .
image: ghcr.io/mastodon/mastodon:v4.2.10
restart: always
env_file: .env.production
command: node ./streaming
@ -97,7 +94,7 @@ services:
sidekiq:
build: .
image: ghcr.io/mastodon/mastodon:v4.2.7
image: ghcr.io/mastodon/mastodon:v4.2.10
restart: always
env_file: .env.production
command: bundle exec sidekiq

View file

@ -6,8 +6,8 @@ Install Ruby
EOF
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 3.2.3
rbenv global 3.2.3
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 3.2.4
rbenv global 3.2.4
cat << EOF

View file

@ -6,8 +6,8 @@ Install Ruby
EOF
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 3.2.3
rbenv global 3.2.3
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 3.2.4
rbenv global 3.2.4
cat << EOF

View file

@ -0,0 +1,72 @@
# frozen_string_literal: true
# Mastodon is not made to be directly accessed without a reverse proxy.
# This monkey-patch prevents remote IP address spoofing when being accessed
# directly.
#
# See PR: https://github.com/rails/rails/pull/51610
# In addition to the PR above, it also raises an error if a request with
# `X-Forwarded-For` or `Client-Ip` comes directly from a client without
# going through a trusted proxy.
# rubocop:disable all -- This is a mostly vendored file
module ActionDispatch
class RemoteIp
module GetIpExtensions
def calculate_ip
# Set by the Rack web server, this is a single value.
remote_addr = ips_from(@req.remote_addr).last
# Could be a CSV list and/or repeated headers that were concatenated.
client_ips = ips_from(@req.client_ip).reverse!
forwarded_ips = ips_from(@req.x_forwarded_for).reverse!
# `Client-Ip` and `X-Forwarded-For` should not, generally, both be set. If they
# are both set, it means that either:
#
# 1) This request passed through two proxies with incompatible IP header
# conventions.
#
# 2) The client passed one of `Client-Ip` or `X-Forwarded-For`
# (whichever the proxy servers weren't using) themselves.
#
# Either way, there is no way for us to determine which header is the right one
# after the fact. Since we have no idea, if we are concerned about IP spoofing
# we need to give up and explode. (If you're not concerned about IP spoofing you
# can turn the `ip_spoofing_check` option off.)
should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
if should_check_ip && !forwarded_ips.include?(client_ips.last)
# We don't know which came from the proxy, and which from the user
raise IpSpoofAttackError, "IP spoofing attack?! " \
"HTTP_CLIENT_IP=#{@req.client_ip.inspect} " \
"HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}"
end
# NOTE: Mastodon addition to make sure we don't get requests from a non-trusted client
if @check_ip && (forwarded_ips.last || client_ips.last) && !@proxies.any? { |proxy| proxy === remote_addr }
raise IpSpoofAttackError, "IP spoofing attack?! client #{remote_addr} is not a trusted proxy " \
"HTTP_CLIENT_IP=#{@req.client_ip.inspect} " \
"HTTP_X_FORWARDED_FOR=#{@req.x_forwarded_for.inspect}"
end
# We assume these things about the IP headers:
#
# - X-Forwarded-For will be a list of IPs, one per proxy, or blank
# - Client-Ip is propagated from the outermost proxy, or is blank
# - REMOTE_ADDR will be the IP that made the request to Rack
ips = forwarded_ips + client_ips
ips.compact!
# If every single IP option is in the trusted list, return the IP that's
# furthest away
filter_proxies([remote_addr] + ips).first || ips.last || remote_addr
end
end
end
end
ActionDispatch::RemoteIp::GetIp.prepend(ActionDispatch::RemoteIp::GetIpExtensions)
# rubocop:enable all

View file

@ -29,7 +29,7 @@ module Mastodon::CLI
link = options[:link] ? 'link-type ' : ''
scope = PreviewCard.cached
scope = scope.where(type: :link) if options[:link]
scope = scope.where('updated_at < ?', time_ago)
scope = scope.where(updated_at: ...time_ago)
processed, aggregate = parallelize_with_progress(scope) do |preview_card|
next if preview_card.image.blank?

View file

@ -8,6 +8,7 @@ class Mastodon::SidekiqMiddleware
rescue Mastodon::HostValidationError
# Do not retry
rescue => e
clean_up_elasticsearch_connections!
limit_backtrace_and_raise(e)
ensure
clean_up_sockets!
@ -25,6 +26,32 @@ class Mastodon::SidekiqMiddleware
clean_up_statsd_socket!
end
# This is a hack to immediately free up unused Elasticsearch connections.
#
# Indeed, Chewy creates one `Elasticsearch::Client` instance per thread,
# and each such client manages its long-lasting connection to
# Elasticsearch.
#
# As far as I know, neither `chewy`, `elasticsearch-transport` or even
# `faraday` provide a reliable way to immediately close a connection, and
# rely on the underlying object to be garbage-collected instead.
#
# Furthermore, `sidekiq` creates a new thread each time a job throws an
# exception, meaning that each failure will create a new connection, and
# the old one will only be closed on full garbage collection.
def clean_up_elasticsearch_connections!
return unless Chewy.enabled? && Chewy.current[:chewy_client].present?
Chewy.client.transport.transport.connections.each do |connection|
# NOTE: This bit of code is tailored for the HTTPClient Faraday adapter
connection.connection.app.instance_variable_get(:@client)&.reset_all
end
Chewy.current.delete(:chewy_client)
rescue
nil
end
def clean_up_redis_socket!
RedisConfiguration.pool.checkin if Thread.current[:redis]
Thread.current[:redis] = nil

View file

@ -9,13 +9,13 @@ module Mastodon
end
def kmyblue_minor
0
3
end
def kmyblue_flag
# 'LTS'
'dev'
# nil
# 'dev'
nil
end
def major
@ -31,7 +31,7 @@ module Mastodon
end
def default_prerelease
'alpha.3'
'alpha.5'
end
def prerelease

View file

@ -7,7 +7,7 @@ describe Rack::Attack, type: :request do
Rails.application
end
shared_examples 'throttled endpoint' do
shared_context 'with throttled endpoint base' do
before do
# Rack::Attack periods are not rolling, so avoid flaky tests by setting the time in a way
# to avoid crossing period boundaries.
@ -19,6 +19,30 @@ describe Rack::Attack, type: :request do
travel_to Time.zone.at(counter_prefix * period.seconds)
end
def below_limit
limit - 1
end
def above_limit
limit * 2
end
def throttle_count
described_class.cache.read("#{counter_prefix}:#{throttle}:#{discriminator}") || 0
end
def counter_prefix
(Time.now.to_i / period.seconds).to_i
end
def increment_counter
described_class.cache.count("#{throttle}:#{discriminator}", period)
end
end
shared_examples 'throttled endpoint' do
include_examples 'with throttled endpoint base'
context 'when the number of requests is lower than the limit' do
before do
below_limit.times { increment_counter }
@ -46,29 +70,37 @@ describe Rack::Attack, type: :request do
expect(response).to_not have_http_status(429)
end
end
end
def below_limit
limit - 1
shared_examples 'does not throttle endpoint' do
include_examples 'with throttled endpoint base'
context 'when the number of requests is lower than the limit' do
before do
below_limit.times { increment_counter }
end
it 'does not change the request status' do
expect { request.call }.to change { throttle_count }.by(0)
expect(response).to_not have_http_status(429)
end
end
def above_limit
limit * 2
end
context 'when the number of requests is higher than the limit' do
before do
above_limit.times { increment_counter }
end
def throttle_count
described_class.cache.read("#{counter_prefix}:#{throttle}:#{remote_ip}") || 0
end
def counter_prefix
(Time.now.to_i / period.seconds).to_i
end
def increment_counter
described_class.cache.count("#{throttle}:#{remote_ip}", period)
it 'returns http too many requests after limit and returns to normal status after period' do
expect { request.call }.to change { throttle_count }.by(0)
expect(response).to_not have_http_status(429)
end
end
end
let(:remote_ip) { '1.2.3.5' }
let(:discriminator) { remote_ip }
describe 'throttle excessive sign-up requests by IP address' do
context 'when accessed through the website' do
@ -131,4 +163,68 @@ describe Rack::Attack, type: :request do
it_behaves_like 'throttled endpoint'
end
end
describe 'throttle excessive oauth application registration requests by IP address' do
let(:throttle) { 'throttle_oauth_application_registrations/ip' }
let(:limit) { 5 }
let(:period) { 10.minutes }
let(:path) { '/api/v1/apps' }
let(:params) do
{
client_name: 'Throttle Test',
redirect_uris: 'urn:ietf:wg:oauth:2.0:oob',
scopes: 'read',
}
end
let(:request) { -> { post path, params: params, headers: { 'REMOTE_ADDR' => remote_ip } } }
it_behaves_like 'throttled endpoint'
end
describe 'throttle excessive password change requests by account' do
let(:user) { Fabricate(:user, email: 'user@host.example') }
let(:throttle) { 'throttle_password_change/account' }
let(:limit) { 10 }
let(:period) { 10.minutes }
let(:request) { -> { put path, headers: { 'REMOTE_ADDR' => remote_ip } } }
let(:path) { '/auth' }
let(:discriminator) { user.id }
before do
sign_in user, scope: :user
# Unfortunately, devise's `sign_in` helper causes the `session` to be
# loaded in the next request regardless of whether it's actually accessed
# by the client code.
#
# So, we make an extra query to clear issue a session cookie instead.
#
# A less resource-intensive way to deal with that would be to generate the
# session cookie manually, but this seems pretty involved.
get '/'
end
it_behaves_like 'throttled endpoint'
end
describe 'throttle excessive emoji reaction requests by account' do
let(:user) { Fabricate(:user, email: 'user@host.example') }
let(:throttle) { 'throttle_password_change/account' }
let(:limit) { 10 }
let(:period) { 10.minutes }
let(:request) { -> { put path, headers: { 'REMOTE_ADDR' => remote_ip } } }
let(:status) { Fabricate(:status) }
let(:emoji) { Fabricate(:custom_emoji) }
let(:path) { "/api/v1/statuses/#{status.id}/emoji_reactions/#{emoji.shortcode}" }
let(:discriminator) { user.id }
before do
sign_in user, scope: :user
get '/'
end
it_behaves_like 'does not throttle endpoint'
end
end

View file

@ -44,6 +44,11 @@ describe Admin::StatusesController do
describe 'GET #show' do
before do
status.media_attachments << Fabricate(:media_attachment, type: :image, account: status.account)
status.save!
status.snapshot!(at_time: status.created_at, rate_limit: false)
status.update!(text: 'Hello, this is an edited post')
status.snapshot!(rate_limit: false)
get :show, params: { account_id: account.id, id: status.id }
end

View file

@ -50,9 +50,11 @@ describe Oauth::AuthorizedApplicationsController do
let!(:application) { Fabricate(:application) }
let!(:access_token) { Fabricate(:accessible_access_token, application: application, resource_owner_id: user.id) }
let!(:web_push_subscription) { Fabricate(:web_push_subscription, user: user, access_token: access_token) }
let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) }
before do
sign_in user, scope: :user
allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub)
post :destroy, params: { id: application.id }
end
@ -67,5 +69,9 @@ describe Oauth::AuthorizedApplicationsController do
it 'removes the web_push_subscription' do
expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'sends a session kill payload to the streaming server' do
expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", '{"event":"kill"}')
end
end
end

View file

@ -147,14 +147,22 @@ describe Settings::ApplicationsController do
end
describe 'destroy' do
let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) }
let!(:access_token) { Fabricate(:accessible_access_token, application: app) }
before do
allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub)
post :destroy, params: { id: app.id }
end
it 'redirects back to applications page and removes the app' do
it 'redirects back to applications page removes the app' do
expect(response).to redirect_to(settings_applications_path)
expect(Doorkeeper::Application.find_by(id: app.id)).to be_nil
end
it 'sends a session kill payload to the streaming server' do
expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", '{"event":"kill"}')
end
end
describe 'regenerate' do

View file

@ -0,0 +1,17 @@
HTTP/1.1 200 OK
server: nginx
date: Thu, 13 Jun 2024 14:33:13 GMT
content-type: text/html; charset=ISO-8859-1
content-length: 158
accept-ranges: bytes
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tofu á l'orange</title>
</head>
<body>
<h2>Tofu á l'orange</h2>
</body>
</html>

View file

@ -0,0 +1,50 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe ActivityPub::Parser::StatusParser do
subject { described_class.new(json) }
let(:sender) { Fabricate(:account, followers_url: 'http://example.com/followers', domain: 'example.com', uri: 'https://example.com/actor') }
let(:follower) { Fabricate(:account, username: 'bob') }
let(:json) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: [ActivityPub::TagManager.instance.uri_for(sender), '#foo'].join,
type: 'Create',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: object_json,
}.with_indifferent_access
end
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), 'post1'].join('/'),
type: 'Note',
to: [
'https://www.w3.org/ns/activitystreams#Public',
ActivityPub::TagManager.instance.uri_for(follower),
],
content: '@bob lorem ipsum',
contentMap: {
EN: '@bob lorem ipsum',
},
published: 1.hour.ago.utc.iso8601,
updated: 1.hour.ago.utc.iso8601,
tag: {
type: 'Mention',
href: ActivityPub::TagManager.instance.uri_for(follower),
},
}
end
it 'correctly parses status' do
expect(subject).to have_attributes(
text: '@bob lorem ipsum',
uri: [ActivityPub::TagManager.instance.uri_for(sender), 'post1'].join('/'),
reply: false,
language: :en
)
end
end

View file

@ -127,7 +127,7 @@ describe Admin::SystemCheck::ElasticsearchCheck do
end
def stub_elasticsearch_error
client = instance_double(Elasticsearch::Transport::Client)
client = instance_double(Elasticsearch::Client)
allow(client).to receive(:info).and_raise(Elasticsearch::Transport::Transport::Error)
allow(Chewy).to receive(:client).and_return(client)
end

View file

@ -1,48 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Vacuum::ApplicationsVacuum do
subject { described_class.new }
describe '#perform' do
let!(:app_with_token) { Fabricate(:application, created_at: 1.month.ago) }
let!(:app_with_grant) { Fabricate(:application, created_at: 1.month.ago) }
let!(:app_with_signup) { Fabricate(:application, created_at: 1.month.ago) }
let!(:app_with_owner) { Fabricate(:application, created_at: 1.month.ago, owner: Fabricate(:user)) }
let!(:unused_app) { Fabricate(:application, created_at: 1.month.ago) }
let!(:recent_app) { Fabricate(:application, created_at: 1.hour.ago) }
before do
Fabricate(:access_token, application: app_with_token)
Fabricate(:access_grant, application: app_with_grant)
Fabricate(:user, created_by_application: app_with_signup)
subject.perform
end
it 'does not delete applications with valid access tokens' do
expect { app_with_token.reload }.to_not raise_error
end
it 'does not delete applications with valid access grants' do
expect { app_with_grant.reload }.to_not raise_error
end
it 'does not delete applications that were used to create users' do
expect { app_with_signup.reload }.to_not raise_error
end
it 'does not delete owned applications' do
expect { app_with_owner.reload }.to_not raise_error
end
it 'does not delete applications registered less than a day ago' do
expect { recent_app.reload }.to_not raise_error
end
it 'deletes unused applications' do
expect { unused_app.reload }.to raise_error ActiveRecord::RecordNotFound
end
end
end

View file

@ -25,6 +25,17 @@ describe 'Scheduled Statuses' do
it_behaves_like 'forbidden for wrong scope', 'write write:statuses'
end
context 'with an application token' do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: 'read:statuses') }
it 'returns http unprocessable entity' do
get api_v1_scheduled_statuses_path, headers: headers
expect(response)
.to have_http_status(422)
end
end
context 'with correct scope' do
let(:scopes) { 'read:statuses' }

View file

@ -8,6 +8,22 @@ describe 'API V1 Statuses Translations' do
let(:scopes) { 'read:statuses' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
context 'with an application token' do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: scopes) }
describe 'POST /api/v1/statuses/:status_id/translate' do
let(:status) { Fabricate(:status, account: user.account, text: 'Hola', language: 'es') }
before do
post "/api/v1/statuses/#{status.id}/translate", headers: headers
end
it 'returns http unprocessable entity' do
expect(response).to have_http_status(422)
end
end
end
context 'with an oauth token' do
describe 'POST /api/v1/statuses/:status_id/translate' do
let(:status) { Fabricate(:status, account: user.account, text: 'Hola', language: 'es') }

View file

@ -266,6 +266,32 @@ describe '/api/v1/statuses' do
expect(response).to have_http_status(404)
end
end
context 'when scheduling a status' do
let(:params) { { status: 'Hello world', scheduled_at: 10.minutes.from_now } }
let(:account) { user.account }
it 'returns HTTP 200' do
subject
expect(response).to have_http_status(200)
end
it 'creates a scheduled status' do
expect { subject }.to change { account.scheduled_statuses.count }.from(0).to(1)
end
context 'when the scheduling time is less than 5 minutes' do
let(:params) { { status: 'Hello world', scheduled_at: 4.minutes.from_now } }
it 'does not create a scheduled status', :aggregate_failures do
subject
expect(response).to have_http_status(422)
expect(account.scheduled_statuses).to be_empty
end
end
end
end
describe 'DELETE /api/v1/statuses/:id' do

View file

@ -37,6 +37,8 @@ describe 'Public' do
context 'when the instance allows public preview' do
let(:expected_statuses) { [local_status, remote_status, media_status] }
it_behaves_like 'forbidden for wrong scope', 'profile'
context 'with an authorized user' do
it_behaves_like 'a successful request to the public timeline'
end
@ -122,13 +124,9 @@ describe 'Public' do
Form::AdminSettings.new(timeline_preview: false).save
end
context 'with an authenticated user' do
let(:expected_statuses) { [local_status, remote_status, media_status] }
it_behaves_like 'forbidden for wrong scope', 'profile'
it_behaves_like 'a successful request to the public timeline'
end
context 'with an unauthenticated user' do
context 'without an authentication token' do
let(:headers) { {} }
it 'returns http unprocessable entity' do
@ -137,6 +135,22 @@ describe 'Public' do
expect(response).to have_http_status(422)
end
end
context 'with an application access token, not bound to a user' do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: scopes) }
it 'returns http unprocessable entity' do
subject
expect(response).to have_http_status(422)
end
end
context 'with an authenticated user' do
let(:expected_statuses) { [local_status, remote_status, media_status] }
it_behaves_like 'a successful request to the public timeline'
end
end
context 'when user is setting filters' do

View file

@ -30,6 +30,8 @@ RSpec.describe 'Tag' do
let(:params) { {} }
let(:hashtag) { 'life' }
it_behaves_like 'forbidden for wrong scope', 'profile'
context 'when given only one hashtag' do
let(:expected_statuses) { [life_status] }
@ -93,13 +95,15 @@ RSpec.describe 'Tag' do
Form::AdminSettings.new(timeline_preview: false).save
end
context 'when the user is not authenticated' do
it_behaves_like 'forbidden for wrong scope', 'profile'
context 'without an authentication token' do
let(:headers) { {} }
it 'returns http unauthorized' do
it 'returns http unprocessable entity' do
subject
expect(response).to have_http_status(401)
expect(response).to have_http_status(422)
end
end

View file

@ -57,9 +57,11 @@ RSpec.describe BackupService do
end
def expect_outbox_export
json = export_json(:outbox)
body = export_json_raw(:outbox)
json = Oj.load(body)
aggregate_failures do
expect(body.scan('@context').count).to eq 1
expect(json['@context']).to_not be_nil
expect(json['type']).to eq 'OrderedCollection'
expect(json['totalItems']).to eq 4
@ -89,8 +91,12 @@ RSpec.describe BackupService do
end
end
def export_json_raw(type)
read_zip_file(backup, "#{type}.json")
end
def export_json(type)
Oj.load(read_zip_file(backup, "#{type}.json"))
Oj.load(export_json_raw(type))
end
def include_create_item(status)

View file

@ -28,6 +28,7 @@ RSpec.describe FetchLinkCardService do
stub_request(:get, 'http://example.com/sjis_with_wrong_charset').to_return(request_fixture('sjis_with_wrong_charset.txt'))
stub_request(:get, 'http://example.com/koi8-r').to_return(request_fixture('koi8-r.txt'))
stub_request(:get, 'http://example.com/windows-1251').to_return(request_fixture('windows-1251.txt'))
stub_request(:get, 'http://example.com/low_confidence_latin1').to_return(request_fixture('low_confidence_latin1.txt'))
Rails.cache.write('oembed_endpoint:example.com', oembed_cache) if oembed_cache
@ -150,6 +151,14 @@ RSpec.describe FetchLinkCardService do
end
end
context 'with a URL of a page in ISO-8859-1 encoding, that charlock_holmes cannot detect' do
let(:status) { Fabricate(:status, text: 'Check out http://example.com/low_confidence_latin1') }
it 'decodes the HTML' do
expect(status.preview_card.title).to eq("Tofu á l'orange")
end
end
context 'with a Japanese path URL' do
let(:status) { Fabricate(:status, text: 'テストhttp://example.com/日本語') }
@ -186,6 +195,19 @@ RSpec.describe FetchLinkCardService do
end
end
context 'with an URL too long for PostgreSQL unique indexes' do
let(:url) { "http://example.com/#{'a' * 2674}" }
let(:status) { Fabricate(:status, text: url) }
it 'does not fetch the URL' do
expect(a_request(:get, url)).to_not have_been_made
end
it 'does not create a preview card' do
expect(status.preview_card).to be_nil
end
end
context 'with a URL of a page with oEmbed support' do
let(:html) { '<!doctype html><title>Hello world</title><link rel="alternate" type="application/json+oembed" href="http://example.com/oembed?url=http://example.com/html">' }
let(:status) { Fabricate(:status, text: 'http://example.com/html') }

View file

@ -309,6 +309,19 @@ RSpec.describe NotifyService do
expect(subject.filter?).to be false
end
end
context 'when the sender is mentioned in an unrelated message chain' do
before do
original_status = Fabricate(:status, visibility: :direct)
intermediary_status = Fabricate(:status, visibility: :direct, thread: original_status)
notification.target_status.update(thread: intermediary_status)
Fabricate(:mention, status: original_status, account: notification.from_account)
end
it 'returns true' do
expect(subject.filter?).to be true
end
end
end
end
end

View file

@ -61,6 +61,16 @@ RSpec.describe PostStatusService do
status2 = subject.call(account, text: 'test', idempotency: 'meepmeep', scheduled_at: future)
expect(status2.id).to eq status1.id
end
context 'when scheduled_at is less than min offset' do
let(:invalid_scheduled_time) { 4.minutes.from_now }
it 'raises invalid record error' do
expect do
subject.call(account, text: 'Hi future!', scheduled_at: invalid_scheduled_time)
end.to raise_error(ActiveRecord::RecordInvalid)
end
end
end
it 'creates response to the original status of boost' do

View file

@ -163,7 +163,7 @@ describe Scheduler::AccountsStatusesCleanupScheduler do
def cleanable_statuses_count
Status
.where(account_id: [account_alice, account_chris, account_erin]) # Accounts with enabled policies
.where('created_at < ?', 2.weeks.ago) # Policy defaults is 2.weeks
.where(created_at: ...2.weeks.ago) # Policy defaults is 2.weeks
.count
end
end