Skip to content
Merged

5.8.0 #4160

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
275608d
docs: 5.7.0リリース済みに更新、5.8.0にセキュリティレビュー指摘事項を追加
pooza Mar 13, 2026
5b02c2a
fix: config_form_testのアサーション修正(visibility label順序、minutes null)
pooza Mar 13, 2026
410a004
fix: OAuthコールバックのパラメータ欠落時にSentryへ通知しない
pooza Mar 14, 2026
e4b9ce0
docs: 5.8.0マイルストーンに#4159を追加
pooza Mar 14, 2026
10362aa
fix: 不正トークンによる401エラー時にSentryへ通知しない
pooza Mar 15, 2026
77df388
docs: セッション開始時の同期手順をCLAUDE.mdに追加
pooza Mar 15, 2026
792922d
feat: about APIでブースト/リノートのカスタムラベルを返す (#4161)
pooza Mar 15, 2026
a2c6cc5
docs: セッション開始時の同期手順にgit fetch・リモート乖離確認を追加
pooza Mar 15, 2026
e2d0250
docs: 5.8.0〜5.10.0のマイルストーンを再編成
pooza Mar 15, 2026
69bc8a6
feat: bundler-auditの導入とrake lint統合 (#4158)
pooza Mar 15, 2026
1e2727d
feat: Sentry before_sendフィルタによる秘匿情報スクラビング (#4157)
pooza Mar 15, 2026
402c2d4
chore: バージョンを5.8.0にバンプ
pooza Mar 15, 2026
0a3faf5
docs: about APIレスポンス例にreblog_labelを追加
pooza Mar 15, 2026
9be2e46
docs: 5.8.0をリリース待ち状態に更新
pooza Mar 15, 2026
82ec6ba
feat: PUT /api/v1/statuses/:id パススルーを追加
pooza Mar 15, 2026
2348ed2
feat: PUT /api/v1/statuses/:id パススルーを追加 (#4162)
pooza Mar 15, 2026
ed57563
docs: 5.8.0に#4162を追加しステージング再検証が必要な状態に更新
pooza Mar 15, 2026
bc0c683
refactor: PUT /api/v1/statuses/:id を media_attributes のみ許可に変更
pooza Mar 15, 2026
8b1a2c2
refactor: PUT /api/v1/statuses/:id に X-Mulukhiya-Purpose ベースのフィルタリングを導入
pooza Mar 15, 2026
231a4e9
Merge branch 'feature/put-statuses-passthrough' into develop
pooza Mar 15, 2026
7109404
docs: 5.8.0のステータスを更新(#4162 ステージング検証中)
pooza Mar 15, 2026
b3199a4
feat: X-Mulukhiya-Purpose: media_update をサポート
pooza Mar 15, 2026
02dd9df
Merge branch 'feature/put-statuses-passthrough' into develop
pooza Mar 15, 2026
9ed2467
refactor: nginx サンプルを X-Mulukhiya-Purpose 対応に更新
pooza Mar 15, 2026
ced00a7
fix: media_update 時に元の投稿本文を自動補完
pooza Mar 15, 2026
94cc815
Merge branch 'feature/put-statuses-passthrough' into develop
pooza Mar 15, 2026
223dcbc
fix: media_update 時に /source エンドポイントでソーステキストを取得
pooza Mar 15, 2026
8aeda04
chore: nginx サンプルを簡素化し 5.8.0 ステータスを更新
pooza Mar 15, 2026
f038be5
bundle update
pooza Mar 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .bundler-audit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
ignore:
# Sinatra ReDoS (ETag header) - sinatra 4.2.0で修正だが、
# rack 3.2 + Sinatra 4.2.0でトークン汚染インシデント発生のため
# sinatra 4.1.1に固定中。docs/postmortem-2025-10-rack32.md 参照
- GHSA-mr3q-g2mv-mr4q
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ gem 'sentry-sidekiq'
gem 'sidekiq', '~>8.1'
gem 'sidekiq-scheduler', '~>6.0.1'
group :development do
gem 'bundler-audit'
gem 'ostruct' # gli < 2.22の未宣言依存
gem 'rack-test'
gem 'rails-erb-lint'
Expand Down
13 changes: 9 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ GIT

GIT
remote: https://github.com/pooza/ginseng-fediverse.git
revision: d780863b197b054aa91e15dfe9acacd7c022e41b
revision: 3a30745ecc8661a22fdf5cb5c4cc0bc1fe756cb5
branch: main
specs:
ginseng-fediverse (1.8.21)
Expand Down Expand Up @@ -125,6 +125,9 @@ GEM
base64 (0.3.0)
bigdecimal (4.0.1)
builder (3.3.0)
bundler-audit (0.9.3)
bundler (>= 1.2.0)
thor (~> 1.0)
cgi (0.5.1)
concurrent-ruby (1.3.6)
connection_pool (3.0.2)
Expand Down Expand Up @@ -205,7 +208,7 @@ GEM
multi_xml (>= 0.5.2)
i18n (1.14.8)
concurrent-ruby (~> 1.0)
json (2.19.0)
json (2.19.1)
json-schema (6.2.0)
addressable (~> 2.8)
bigdecimal (>= 3.1, < 5)
Expand Down Expand Up @@ -346,7 +349,7 @@ GEM
rubocop-ast (>= 1.49.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.49.0)
rubocop-ast (1.49.1)
parser (>= 3.3.7.2)
prism (~> 1.7)
rubocop-minitest (0.39.1)
Expand Down Expand Up @@ -417,11 +420,12 @@ GEM
temple (0.10.4)
test-unit (3.7.7)
power_assert
thor (1.5.0)
tilt (2.1.0)
time (0.4.2)
date
timecop (0.9.10)
timeout (0.6.0)
timeout (0.6.1)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (3.2.0)
Expand Down Expand Up @@ -449,6 +453,7 @@ PLATFORMS
x86_64-linux

DEPENDENCIES
bundler-audit
concurrent-ruby
dry-validation
faye-websocket!
Expand Down
12 changes: 12 additions & 0 deletions app/lib/mulukhiya.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,23 @@ def self.setup_sentry
config.release = Package.version
config.environment = Environment.type
config.traces_sample_rate = Config.instance['/sentry/traces_sample_rate'] || 0
config.before_send = method(:scrub_sentry_event)
end
rescue => e
warn "Sentry initialization skipped: #{e.message}"
end

def self.scrub_sentry_event(event, _hint)
patterns = (Config.instance['/sentry/scrub_patterns'] || []).map {|p| Regexp.new(p)}
return event if patterns.empty?
event.exception&.values&.each do |ex| # rubocop:disable Style/HashEachMethods
patterns.each do |pattern|
ex.value = ex.value&.gsub(pattern, '[FILTERED]')
end
end
return event
end

def self.setup_debug
Ricecream.disable
return unless Environment.development?
Expand Down
1 change: 1 addition & 0 deletions app/lib/mulukhiya/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def about
controller: self['/controller'],
status: Environment.status_class.default.merge(
label: controller.status_label,
reblog_label: controller.reblog_label,
max_length: controller.max_length,
),
capabilities: sub_hash("/#{name}/capabilities"),
Expand Down
40 changes: 34 additions & 6 deletions app/lib/mulukhiya/controller/mastodon_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class MastodonController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand All @@ -33,7 +33,7 @@ class MastodonController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand All @@ -48,7 +48,35 @@ class MastodonController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
end

put '/api/:version/statuses/:id' do
verify_token_integrity!
purpose = request.env['HTTP_X_MULUKHIYA_PURPOSE']
body = case purpose
when nil, '', 'media_update'
source = sns.fetch_status_source(params[:id], {headers: @headers})
{status: source['text'], media_attributes: params[:media_attributes]}.compact
when 'tag'
{status: params[:status], media_attributes: params[:media_attributes]}.compact
else
raise Ginseng::ValidateError, "unknown purpose: #{purpose}"
end
raise Ginseng::ValidateError, 'media_attributes is required' if body.empty?
reporter.response = sns.update_status(params[:id], body, {headers: @headers})
@renderer.message = reporter.response.parsed_response
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::ValidateError => e
@renderer.message = {error: e.message}
@renderer.status = 422
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand All @@ -63,7 +91,7 @@ class MastodonController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand All @@ -78,7 +106,7 @@ class MastodonController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand All @@ -93,7 +121,7 @@ class MastodonController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand Down
12 changes: 6 additions & 6 deletions app/lib/mulukhiya/controller/misskey_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class MisskeyController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand All @@ -32,7 +32,7 @@ class MisskeyController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand All @@ -56,7 +56,7 @@ class MisskeyController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand All @@ -75,7 +75,7 @@ class MisskeyController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand All @@ -89,7 +89,7 @@ class MisskeyController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand All @@ -103,7 +103,7 @@ class MisskeyController < Controller
@renderer.status = reporter.response.code
return @renderer.to_s
rescue Ginseng::GatewayError => e
e.alert
e.alert unless e.source_status == 401
@renderer.message = {error: e.message}
@renderer.status = e.source_status
return @renderer.to_s
Expand Down
9 changes: 7 additions & 2 deletions app/lib/mulukhiya/controller/ui_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ class UIController < Controller
end

get '/oauth/callback' do
raise Ginseng::AuthError, 'Missing state' unless params[:state]
raise Ginseng::AuthError, 'Missing code' unless params[:code]
unless params[:state] && params[:code]
@renderer = SlimRenderer.new
@renderer.template = 'token_error'
@renderer[:error] = 'Missing required OAuth parameters'
@renderer.status = 400
return @renderer.to_s
end
result = sns.auth_with_pkce(params[:code], params[:state])
raise Ginseng::AuthError, 'Token exchange failed' unless result
parsed = result.parsed_response
Expand Down
4 changes: 4 additions & 0 deletions app/lib/mulukhiya/controller_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ def status_label
return config["/#{name}/status/label"]
end

def reblog_label
return config["/#{name}/status/reblog_label"]
end

def status_delete_limit
return config["/#{name}/status/delete/limit"] rescue nil
end
Expand Down
11 changes: 11 additions & 0 deletions app/task/bundler_audit.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Mulukhiya
extend Rake::DSL

namespace :bundler_audit do
desc 'bundler-audit'
task :check do
Dir.chdir(Environment.dir)
sh 'bundler-audit check --update'
end
end
end
2 changes: 1 addition & 1 deletion app/task/lint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ module Mulukhiya
extend Rake::DSL

desc 'lint all'
task lint: ['erb:lint', 'slim:lint', 'rubocop:lint', 'yaml:lint']
task lint: ['erb:lint', 'slim:lint', 'rubocop:lint', 'yaml:lint', 'bundler_audit:check']
end
8 changes: 7 additions & 1 deletion config/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ mastodon:
limit: 30
key: id
label: 投稿
reblog_label: ブースト
parser: toot
spoiler_text: null
streaming:
Expand Down Expand Up @@ -377,6 +378,7 @@ misskey:
default_max_length: 3000
key: noteId
label: ノート
reblog_label: リノート
parser: note
spoiler_text: null
streaming:
Expand Down Expand Up @@ -404,7 +406,7 @@ package:
- tkoishi@b-shock.co.jp
license: MIT
url: https://github.com/pooza/mulukhiya-toot-proxy
version: 5.7.0
version: 5.8.0
parser:
note:
fields:
Expand Down Expand Up @@ -449,6 +451,10 @@ redis:
sentry:
dsn: null
traces_sample_rate: 0
scrub_patterns:
- '[a-zA-Z0-9_\-]{20,}'
- '/home/[^\s]+'
- '/Users/[^\s]+'
ruby:
bundler:
install: false
Expand Down
8 changes: 1 addition & 7 deletions config/sample/mastodon/mulukhiya.nginx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ map "${request_method}:${http_x_mulukhiya}" $media_put_backend {
default http://localhost:3000;
}
map "${request_method}:${http_x_mulukhiya}" $status_put_backend {
"~^PUT:$" reject;
"~^PUT:$" http://localhost:3008;
default http://localhost:3000;
}

Expand All @@ -28,12 +28,6 @@ location ~ ^/api/v[0-9]+/media/[0-9]+$ {
}
location ~ ^/api/v[0-9]+/statuses/[0-9]+$ {
include /path/to/mulukhiya_proxy.conf;
if ($http_x_mulukhiya_purpose != '') {
proxy_pass http://localhost:3008;
}
if ($status_put_backend = reject) {
return 405;
}
proxy_pass $status_put_backend;
}
location = /api/v1/timelines/public {
Expand Down
Loading
Loading