채팅 웹훅 관련 버그 - 위임되었는데 웹훅이 nil입니다

재현을 시도하는 것이 꺼려지긴 하지만, 버그를 발견하거나 만들었다고 생각합니다: curl을 사용하여 채팅 웹훅을 수동으로 빌드하고 테스트한 후, 키를 삭제하고 기존 키로 추가 요청을 보냈습니다. 이전에는 스레딩이 비활성화되어 있던 기존 채팅 채널에서 문제가 없었으나, 이후 데이터가 손상되었습니다.

취한 대략적인 단계:

  1. 봇 계정이 게시하는 웹훅을 추가했습니다.

  2. 공개된 curl 유틸리티를 사용하여 API를 테스트했는데 잘 작동했습니다.
    2.1 웹훅을 백엔드 소프트웨어에 배포했는데, 작동했습니다.
    2.2 시크릿 키를 curl 유틸리티에 대한 신뢰할 수 없어 웹훅을 삭제했습니다.
    2.3 백엔드를 새 키를 사용하도록 업데이트했거나, 기존 키로 curl 요청을 보냈을 수 있습니다.
    2.4 모든 것이 엉망이 되었습니다.

  3. 삭제된 웹훅을 사용했을 수 있습니다.

  4. 채팅 채널이 완전히 깨졌습니다. 데이터베이스의 어떤 문제가 있다고 의심하지만, 기술적 지식이 부족하여 무엇인지, 어떻게 추측해야 할지조차 알지 못합니다. 렌더링이 멈추고 프론트엔드에 오류가 다수 발생합니다. ‘읽지 않음’ 상태가 사라지지 않습니다. 채널에서 메시지를 보내는 것은 작동하는 것 같습니다(로컬 캐시에 저장되는 것으로 추정), 하지만 채널을 나가 다른 곳에 게시한 후 다시 돌아오면 메시지가 있습니다. 페이지를 새로고침하면 모든 메시지가 다시 사라집니다. 4.1 웹훅이 500 내부 서버 오류를 반환하기 시작했습니다.

  5. 유사한 매개변수로 다양한 채널에 게시하는 새 웹훅을 빌드했습니다. 다른 채널은 작동했지만, 깨진 채널은 여전히 고장 난 상태였습니다.

  6. 고장 난 채널을 완전히 삭제했습니다.
    6.1 동일한 설정/이름으로 채널을 다시 구축했습니다.

  7. 새 채널을 작동하는(5.) 웹훅을 가리키도록 설정했습니다 - 성공.

관찰 - 의도적으로 잘못된 키를 보내면 404가 생성됩니다. 이는 정상입니다. 오류가 기록되지 않습니다.

이 인스턴스에서 38개의 복사본 - 채팅 관련 항목에 액세스할 때마다 증가합니다.

에러 로그/트레이스
### Message (38 copies reported)

Module::DelegationError (Chat::WebhookEvent#username delegated to incoming_chat_webhook.username, but incoming_chat_webhook is nil: #<Chat::WebhookEvent id: 1, chat_message_id: 804, incoming_chat_webhook_id: 1, created_at: "2023-07-28 22:54:54.832175000 +0000", updated_at: "2023-07-28 22:54:54.832175000 +0000">) (eval):3:in `_fast_attributes' lib/freedom_patches/ams_include_without_root.rb:57:in `include!' app/controllers/application_controller.rb:551:in `render_json_dump' app/controllers/application_controller.rb:538:in `render_serialized' app/controllers/application_controller.rb:420:in `block in with_resolved_locale' app/controllers/application_controller.rb:420:in `with_resolved_locale' lib/middleware/omniauth_bypass_middleware.rb:74:in `call' lib/content_security_policy/middleware.rb:12:in `call' lib/middleware/anonymous_cache.rb:389:in `call' lib/middleware/gtm_script_nonce_injector.rb:10:in `call' config/initializers/100-quiet_logger.rb:20:in `call' config/initializers/100-silence_logger.rb:29:in `call' lib/middleware/enforce_hostname.rb:24:in `call' lib/middleware/request_tracker.rb:228:in `call'

### Backtrace

plugins/chat/app/models/chat/webhook_event.rb:10:in `rescue in username'
plugins/chat/app/models/chat/webhook_event.rb:10:in `username'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:99:in `block in attribute'
(eval):3:in `_fast_attributes'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:468:in `rescue in attributes'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:455:in `attributes'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:480:in `_serializable_hash'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:359:in `serializable_hash'
active_model_serializers (0.8.4) lib/active_model/serializer/associations.rb:200:in `serialize'
lib/freedom_patches/ams_include_without_root.rb:57:in `include!'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:368:in `block in include_associations!'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:367:in `each_key'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:367:in `include_associations!'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:362:in `serializable_hash'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:347:in `as_json'
activesupport (7.0.5.1) lib/active_support/json/encoding.rb:22:in `encode'
activesupport (7.0.5.1) lib/active_support/json/encoding.rb:22:in `encode'
activesupport (7.0.5.1) lib/active_support/core_ext/object/json.rb:42:in `to_json'
active_model_serializers (0.8.4) lib/active_model/serializer.rb:331:in `to_json'
multi_json (1.15.0) lib/multi_json/adapters/oj.rb:56:in `dump'
multi_json (1.15.0) lib/multi_json/adapters/oj.rb:56:in `dump'
multi_json (1.15.0) lib/multi_json/adapter.rb:25:in `dump'
multi_json (1.15.0) lib/multi_json.rb:139:in `dump'
app/controllers/application_controller.rb:551:in `render_json_dump'
app/controllers/application_controller.rb:538:in `render_serialized'
plugins/chat/app/controllers/chat/api/channel_messages_controller.rb:6:in `block (2 levels) in index'
plugins/chat/lib/service_runner.rb:144:in `instance_exec'
plugins/chat/lib/service_runner.rb:144:in `block in add_action'
plugins/chat/lib/service_runner.rb:128:in `call'
plugins/chat/lib/service_runner.rb:115:in `call'
plugins/chat/app/helpers/chat/with_service_helper.rb:18:in `with_service'
plugins/chat/app/controllers/chat/api/channel_messages_controller.rb:5:in `index'
actionpack (7.0.5.1) lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
actionpack (7.0.5.1) lib/abstract_controller/base.rb:215:in `process_action'
actionpack (7.0.5.1) lib/action_controller/metal/rendering.rb:165:in `process_action'
actionpack (7.0.5.1) lib/abstract_controller/callbacks.rb:234:in `block in process_action'
activesupport (7.0.5.1) lib/active_support/callbacks.rb:118:in `block in run_callbacks'
app/controllers/application_controller.rb:420:in `block in with_resolved_locale'
i18n (1.14.1) lib/i18n.rb:322:in `with_locale'
app/controllers/application_controller.rb:420:in `with_resolved_locale'
activesupport (7.0.5.1) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
activesupport (7.0.5.1) lib/active_support/callbacks.rb:138:in `run_callbacks'
actionpack (7.0.5.1) lib/abstract_controller/callbacks.rb:233:in `process_action'
actionpack (7.0.5.1) lib/action_controller/metal/rescue.rb:22:in `process_action'
actionpack (7.0.5.1) lib/action_controller/metal/instrumentation.rb:67:in `block in process_action'
activesupport (7.0.5.1) lib/active_support/notifications.rb:206:in `block in instrument'
activesupport (7.0.5.1) lib/active_support/notifications/instrumenter.rb:24:in `instrument'
activesupport (7.0.5.1) lib/active_support/notifications.rb:206:in `instrument'
actionpack (7.0.5.1) lib/action_controller/metal/instrumentation.rb:66:in `process_action'
actionpack (7.0.5.1) lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
activerecord (7.0.5.1) lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack (7.0.5.1) lib/abstract_controller/base.rb:151:in `process'
actionview (7.0.5.1) lib/action_view/rendering.rb:39:in `process'
rack-mini-profiler (3.1.0) lib/mini_profiler/profiling_methods.rb:85:in `block in profile_method'
actionpack (7.0.5.1) lib/action_controller/metal.rb:188:in `dispatch'
actionpack (7.0.5.1) lib/action_controller/metal.rb:251:in `dispatch'
actionpack (7.0.5.1) lib/action_dispatch/routing/route_set.rb:49:in `dispatch'
actionpack (7.0.5.1) lib/action_dispatch/routing/route_set.rb:32:in `serve'
actionpack (7.0.5.1) lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack (7.0.5.1) lib/action_dispatch/journey/router.rb:32:in `each'
actionpack (7.0.5.1) lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack (7.0.5.1) lib/action_dispatch/routing/route_set.rb:852:in `call'
railties (7.0.5.1) lib/rails/engine.rb:530:in `call'
railties (7.0.5.1) lib/rails/railtie.rb:226:in `public_send'
railties (7.0.5.1) lib/rails/railtie.rb:226:in `method_missing'
actionpack (7.0.5.1) lib/action_dispatch/routing/mapper.rb:19:in `block in <class:Constraints>'
actionpack (7.0.5.1) lib/action_dispatch/routing/mapper.rb:48:in `serve'
actionpack (7.0.5.1) lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack (7.0.5.1) lib/action_dispatch/journey/router.rb:32:in `each'
actionpack (7.0.5.1) lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack (7.0.5.1) lib/action_dispatch/routing/route_set.rb:852:in `call'
lib/middleware/omniauth_bypass_middleware.rb:74:in `call'
rack (2.2.7) lib/rack/tempfile_reaper.rb:15:in `call'
rack (2.2.7) lib/rack/conditional_get.rb:27:in `call'
rack (2.2.7) lib/rack/head.rb:12:in `call'
actionpack (7.0.5.1) lib/action_dispatch/http/permissions_policy.rb:38:in `call'
lib/content_security_policy/middleware.rb:12:in `call'
lib/middleware/anonymous_cache.rb:389:in `call'
lib/middleware/gtm_script_nonce_injector.rb:10:in `call'
rack (2.2.7) lib/rack/session/abstract/id.rb:266:in `context'
rack (2.2.7) lib/rack/session/abstract/id.rb:260:in `call'
actionpack (7.0.5.1) lib/action_dispatch/middleware/cookies.rb:704:in `call'
actionpack (7.0.5.1) lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport (7.0.5.1) lib/active_support/callbacks.rb:99:in `run_callbacks'
actionpack (7.0.5.1) lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack (7.0.5.1) lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'
actionpack (7.0.5.1) lib/action_dispatch/middleware/show_exceptions.rb:26:in `call'
logster (2.12.2) lib/logster/middleware/reporter.rb:43:in `call'
railties (7.0.5.1) lib/rails/rack/logger.rb:40:in `call_app'
railties (7.0.5.1) lib/rails/rack/logger.rb:27:in `call'
config/initializers/100-quiet_logger.rb:20:in `call'
config/initializers/100-silence_logger.rb:29:in `call'
actionpack (7.0.5.1) lib/action_dispatch/middleware/remote_ip.rb:93:in `call'
actionpack (7.0.5.1) lib/action_dispatch/middleware/request_id.rb:26:in `call'
lib/middleware/enforce_hostname.rb:24:in `call'
rack (2.2.7) lib/rack/method_override.rb:24:in `call'
actionpack (7.0.5.1) lib/action_dispatch/middleware/executor.rb:14:in `call'
rack (2.2.7) lib/rack/sendfile.rb:110:in `call'
actionpack (7.0.5.1) lib/action_dispatch/middleware/host_authorization.rb:131:in `call'
rack-mini-profiler (3.1.0) lib/mini_profiler.rb:260:in `call'
message_bus (4.3.7) lib/message_bus/rack/middleware.rb:60:in `call'
lib/middleware/request_tracker.rb:228:in `call'
railties (7.0.5.1) lib/rails/engine.rb:530:in `call'
railties (7.0.5.1) lib/rails/railtie.rb:226:in `public_send'
railties (7.0.5.1) lib/rails/railtie.rb:226:in `method_missing'
rack (2.2.7) lib/rack/urlmap.rb:74:in `block in call'
rack (2.2.7) lib/rack/urlmap.rb:58:in `each'
rack (2.2.7) lib/rack/urlmap.rb:58:in `call'
unicorn (6.1.0) lib/unicorn/http_server.rb:634:in `process_client'
unicorn (6.1.0) lib/unicorn/http_server.rb:739:in `worker_loop'
unicorn (6.1.0) lib/unicorn/http_server.rb:547:in `spawn_missing_workers'
unicorn (6.1.0) lib/unicorn/http_server.rb:143:in `start'
unicorn (6.1.0) bin/unicorn:128:in `<top (required)>'
vendor/bundle/ruby/3.2.0/bin/unicorn:25:in `load'
vendor/bundle/ruby/3.2.0/bin/unicorn:25:in `<main>'
1개의 좋아요

(웹훅 URL이 셸 히스토리 로그에 남는 것이 문제라면) read webhook_url을 사용하고(그 외에는 웹훅 URL을 사용하던 위치에 $webhook_url을 사용하세요) 웹훅 URL이 셸 히스토리 로그에 기록되지 않도록 하는 것이 좋습니다.

2개의 좋아요

좋은 조언입니다. 참고로 저는 보안이 강화된 업무용 컴퓨터에서 설정을 테스트하고 있었기 때문에, 웹 기반 curl 도구를 사용하여 명령을 전송했습니다. 해당 웹 서비스가 명령을 캐시하거나 나중에 유출하지 않을 것이라고 신뢰할 수 없었기 때문입니다.

채팅 훅에 좋은 기능이 될 것 같습니다: 훅 전체를 삭제하고 다시 만들지 않고도 키를 재사용/소거할 수 있는 기능 말입니다.

1개의 좋아요

좋은 지적 감사합니다 :+1:

다음 PR에서 수정될 예정입니다:

참고로, 이는 오직 오류가 발생하는 것을 방지할 뿐이며, 이미 해당 상태에 있는 경우 현재로서는 두 가지 해결책이 있습니다:

  • 새 채널 생성
  • chat_webhook_events의 고아 레코드(orphaned records)를 수동으로 삭제

해당 문제에 대한 보고가 단 한 건뿐이어서 이를 처리하기 위한 별도의 조치는 제공하지 않겠지만, 유사한 사례가 더 발생하면 도움을 드리겠습니다.

4개의 좋아요

이 주제는 마지막 답변 3일 후 자동으로 닫혔습니다. 더 이상 답변이 허용되지 않습니다.