ES /docs

Cupix::Errors::PermissionDenied: Permission denied

RCA: Cupix::Errors::PermissionDenied: Permission denied

Overview#

What Happened#

cupixvista-api (tesla, ECE QA deployment) 에서 Cupix::Errors::PermissionDenied (code PERM10000) 가 Error Tracking 이슈로 그룹핑되어 12건 집계되었다. 최근 발생 로그를 보면 GET /api/v1/reviews/:id/membersPUT /api/v1/me 두 엔드포인트에서 권한이 없는 사용자가 요청을 보낼 때 정상적으로 403 Forbidden 을 반환하며 발생하는 authorization 실패다. 애플리케이션은 이 예외를 rescue_from 으로 처리해 403 JSON 을 렌더하고, 로그는 error 가 아닌 info 레벨로 남긴다.

Quick Facts#

Field Value
exception.class Cupix::Errors::PermissionDenied
exception.message Permission denied
exception.code PERM10000
top_frame app/repositories/user_repository.rb:116 / app/repositories/review_repository.rb:170
runtime Rails (tesla monolith)
env ECE QA (cupixvista, us-west-2)

Affected Teams#

Team / Domain Error Count Impact
cupixvista-api (tesla) 11 (14d 로그) 사용자 없음 — 권한 없는 요청에 정상적으로 403 반환

Timeline#

  1. 2025-08-29 18:57 KST — Error Tracking 이슈 first_seen (representative sample)
  2. 2026-07-23 12:58~13:57 KSTGET /reviews/xb9bb8/members, GET /reviews/x452mc/members 403 다수 발생
  3. 2026-07-29 14:48 KST — 클러스터 last_seen (GET /reviews/x452mc/members 403)
  4. 2026-07-31 18:49 KSTPUT /api/v1/me 403
  5. 2026-08-03 15:19 KST — 가장 최근 GET /reviews/iw8g86/members 403

Error Log#

Datadog Logs

text
Permission denied

최근 실제 발생 로그 (Datadog, service:cupixvista-api "Permission denied", now-14d):

json
{
  "timestamp": "2026-08-03 15:19:06",
  "status": "info",
  "message": "[403] GET /api/v1/reviews/iw8g86/members (Api::V1::ReviewsController#members)",
  "error": {
    "reason": "Permission denied",
    "code": "PERM10000",
    "message": "Permission denied",
    "class": "Cupix::Errors::PermissionDenied"
  }
}
json
{
  "timestamp": "2026-07-31 09:49:24",
  "status": "info",
  "message": "[403] PUT /api/v1/me (Api::V1::MeController#update)",
  "error": {
    "reason": "Permission denied",
    "code": "PERM10000",
    "message": "Permission denied",
    "class": "Cupix::Errors::PermissionDenied"
  }
}

Impact#

  • Service: cupixvista-api (실제 app = tesla, ECE QA)
  • 발생 횟수: 12 (Error Tracking), 최근 14일 로그 11건
  • 최초 발생: 2025-08-29 18:57 KST
  • 최근 발생: 2026-07-29 14:48 KST (클러스터 기준); Datadog 로그 기준 최근 발생 2026-08-03 15:19 KST

Root Cause Summary#

이 이슈는 코드 결함이 아니라 정상적인 authorization 거부다. Cupix::Errors::PermissionDenied 는 tesla 전반의 권한 검증 지점에서 의도적으로 raise 하는 예외이며, client_error_controller.rb:26rescue_from 이 이를 잡아 403 Forbidden JSON 으로 응답한다. 최근 발생 로그 두 종류는 모두 권한 없는 클라이언트 요청이다. (1) PUT /api/v1/meUserRepository 가 요청자의 team_id 와 대상 user 의 team_id 가 다를 때 (user_repository.rb:116) 거부한다. (2) GET /api/v1/reviews/:id/members 는 요청자가 해당 review 에 대한 열람 권한이 없을 때 (review_repository.rb:170, self.model.updatable_by? 실패) 거부한다. Error Tracking 은 APM span 에서 raise 된 예외를 캡처해 이슈로 묶었을 뿐이며, 애플리케이션 로그 자체는 error/warn 이 아닌 info 레벨([403] ...)이다. 즉 사용자 영향이나 수정할 defect 가 없는, expected business/permission error 다.

Technical Analysis#

Code Path#

  • Entry point: Api::V1::MeController#update (app/controllers/api/v1/me_controller.rb:12) 및 Api::V1::ReviewsController#members (app/controllers/concerns/share_controller.rb:7)
  • Failure point (permission guard): app/repositories/user_repository.rb:116, app/repositories/review_repository.rb:170
  • Rescue/handler: app/controllers/concerns/client_error_controller.rb:26,69-71app/controllers/concerns/error_base_controller.rb:5

PUT /api/v1/me 는 repository 로 위임되고, 요청자와 대상 user 의 팀이 다르면 권한 거부된다:

app/repositories/user_repository.rb:113-118ruby
    if current_user.present?
      if current_user.team_id != model.team_id
        raise Cupix::Errors::PermissionDenied.new(code: 'PERM10000', reason: 'Permission denied')
      end
    end

GET /api/v1/reviews/:id/members 는 review 에 대한 권한이 없으면 거부된다:

app/repositories/review_repository.rb:170ruby
    raise Cupix::Errors::PermissionDenied.new(code: 'PERM10000', reason: 'Permission denied') unless self.model.updatable_by?(self.current_user)

예외는 controller 의 rescue_from 에서 잡혀 403 으로 렌더된다:

app/controllers/concerns/client_error_controller.rb:26,69-71ruby
    rescue_from Cupix::Errors::PermissionDenied, with: :permission_denied_403_error
    # ...
    def permission_denied_403_error(exception)
      raise_error(403, exception)
    end

raise_error 는 JSON 을 렌더할 뿐 Cupix::Logger.error 를 호출하지 않는다 — 즉 이 경로는 error-level 로그를 남기지 않는다:

app/controllers/concerns/error_base_controller.rb:5-33ruby
    def raise_error(status_code, exception, code: nil, type: nil, reason: nil, message: nil)
      # ...
      render status: status_code, json: {
        result: { code: code, type: type, reason: reason, message: message }
      }
    end

기대 동작: 권한 없는 사용자의 요청은 403 으로 차단되어야 한다. 실제 동작: 정확히 그렇게 동작하며, 이는 defect 가 아니다. Error Tracking 이 APM 에서 raise 된 예외를 이슈로 그룹핑했을 뿐이다.

Log Evidence#

Datadog 쿼리 (재현 가능):

text
service:cupixvista-api "Permission denied"
text
service:cupixvista-api status:error

status:error 검색은 14일 범위에서 0건 — 이 예외는 error 로그를 남기지 않는다. "Permission denied" 키워드 검색은 11건 모두 status: info[403] 응답으로 나타난다. 발생 엔드포인트 분포:

text
2026-08-03 15:19:06  info  [403] GET  /api/v1/reviews/iw8g86/members
2026-08-03 15:16:58  info  [403] GET  /api/v1/reviews/iw8g86/members
2026-07-31 09:49:24  info  [403] PUT  /api/v1/me
2026-07-30 18:58:24  info  [403] PUT  /api/v1/me
2026-07-29 14:48:03  info  [403] GET  /api/v1/reviews/x452mc/members
2026-07-28 02:14:38  info  [403] PUT  /api/v1/me
2026-07-26 16:38:00  info  [403] PUT  /api/v1/me
2026-07-23 14:57:46  info  [403] GET  /api/v1/reviews/x452mc/members
2026-07-23 04:15:49  info  [403] GET  /api/v1/reviews/xb9bb8/members
2026-07-23 04:15:39  info  [403] GET  /api/v1/reviews/xb9bb8/members
2026-07-23 03:58:00  info  [403] GET  /api/v1/reviews/xb9bb8/members

Representative Error (Permission denied) 와 최근 발생 메시지가 일치하므로 대표 샘플은 이번 케이스에서 stale 하지 않다 (message 관점). 단 first_seen (2025-08-29) 은 오래됐고, 실제 활동은 2026-07 이후에 집중된다.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 권한 없는 클라이언트 요청에 대한 정상적인 403 authorization 거부 (noise) 로그 11건 모두 [403] status: info, PERM10000; 코드상 user_repository.rb:116(팀 불일치), review_repository.rb:170(권한 없음) 의 의도적 guard; client_error_controller.rb:26 가 403 으로 처리 Confirmed
H2 잘못된 권한 로직으로 정당한 사용자가 차단되는 버그 두 guard 모두 명확한 권한 조건(팀 소속, review 권한)에 기반; error-level 로그 0건; 저빈도(14mo 12건)로 광범위 오작동 징후 없음 Rejected
H3 예외 처리 누락으로 500 이 발생하는 문제 rescue_from ... permission_denied_403_error 존재; 응답이 500 이 아닌 403; status:error 0건 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

없음. 코드 변경 불필요. 정상적인 authorization 거부이며 사용자 영향이나 defect 가 없다.

단기 개선 (1주 이내)#

  • Error Tracking 에서 이 이슈를 Ignore 처리해 status-board noise 를 줄인다. PermissionDenied 는 client 4xx 이므로 Error Tracking 대상에서 제외하는 것이 적절하다.
  • 필요 시 APM 설정에서 Cupix::Errors::PermissionDenied (및 다른 4xx client error 클래스) 를 span error 로 표시하지 않도록 제외하면, 근본적으로 Error Tracking 그룹핑을 방지할 수 있다.

장기 개선 (재발 방지)#

  • 4xx client-error 계열 예외(PermissionDenied, NotFound, Parameter 등)를 APM/Error Tracking 에서 일괄 제외하는 정책을 datadog initializer 수준에서 정립하면 유사한 authorization/validation noise 이슈의 반복 생성을 막을 수 있다.

Monitoring#

권한 거부 추세를 관찰하려면 아래 쿼리로 timeseries 를 구성한다 (급증 시 권한 로직/UI 변경 여부 점검):

text
service:cupixvista-api "Permission denied"

정상 상태에서는 error-level 로그가 없어야 함을 확인하는 쿼리 (0 을 유지해야 함):

text
service:cupixvista-api status:error @error.class:Cupix::Errors::PermissionDenied

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial (코드 변경 없음, Ignore 또는 APM 제외 설정만)

Noise Verdict#

noise — 권한 없는 클라이언트 요청에 대한 정상적인 403 authorization 거부로, error 로그도 남기지 않고 수정할 코드 결함이 없다.