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/members 와 PUT /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#
- 2025-08-29 18:57 KST — Error Tracking 이슈 first_seen (representative sample)
- 2026-07-23 12:58~13:57 KST —
GET /reviews/xb9bb8/members,GET /reviews/x452mc/members403 다수 발생 - 2026-07-29 14:48 KST — 클러스터 last_seen (
GET /reviews/x452mc/members403) - 2026-07-31 18:49 KST —
PUT /api/v1/me403 - 2026-08-03 15:19 KST — 가장 최근
GET /reviews/iw8g86/members403
Error Log#
Permission denied
최근 실제 발생 로그 (Datadog, service:cupixvista-api "Permission denied", now-14d):
{
"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"
}
}
{
"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:26 의 rescue_from 이 이를 잡아 403 Forbidden JSON 으로 응답한다. 최근 발생 로그 두 종류는 모두 권한 없는 클라이언트 요청이다. (1) PUT /api/v1/me 는 UserRepository 가 요청자의 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-71→app/controllers/concerns/error_base_controller.rb:5
PUT /api/v1/me 는 repository 로 위임되고, 요청자와 대상 user 의 팀이 다르면 권한 거부된다:
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 에 대한 권한이 없으면 거부된다:
raise Cupix::Errors::PermissionDenied.new(code: 'PERM10000', reason: 'Permission denied') unless self.model.updatable_by?(self.current_user)
예외는 controller 의 rescue_from 에서 잡혀 403 으로 렌더된다:
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 로그를 남기지 않는다:
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 쿼리 (재현 가능):
service:cupixvista-api "Permission denied"
service:cupixvista-api status:error
status:error 검색은 14일 범위에서 0건 — 이 예외는 error 로그를 남기지 않는다. "Permission denied" 키워드 검색은 11건 모두 status: info 의 [403] 응답으로 나타난다. 발생 엔드포인트 분포:
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 변경 여부 점검):
service:cupixvista-api "Permission denied"
정상 상태에서는 error-level 로그가 없어야 함을 확인하는 쿼리 (0 을 유지해야 함):
service:cupixvista-api status:error @error.class:Cupix::Errors::PermissionDenied
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (코드 변경 없음, Ignore 또는 APM 제외 설정만)
Noise Verdict#
noise — 권한 없는 클라이언트 요청에 대한 정상적인 403 authorization 거부로, error 로그도 남기지 않고 수정할 코드 결함이 없다.