-
Notifications
You must be signed in to change notification settings - Fork 2
[FEAT] Sentry 알림이 대응 판단으로 이어지도록 이벤트 맥락 개선 #469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
useon
wants to merge
3
commits into
develop
Choose a base branch
from
feat/#468
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+343
−74
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| import { AxiosError, AxiosHeaders } from 'axios'; | ||
| import { | ||
| buildSentryApiErrorMetadata, | ||
| createSentryApiError, | ||
| normalizeEndpoint, | ||
| resolveApiErrorLevel, | ||
| resolveFeatureFromPathname, | ||
| shouldSkipApiError, | ||
| } from './sentry'; | ||
|
|
||
| describe('sentry 유틸', () => { | ||
| it('같은 API 장애가 하나의 알림 이슈로 묶이도록 가변 경로 값을 제거한다', () => { | ||
| expect(normalizeEndpoint('/api/tables/123/votes/456?tab=1')).toBe( | ||
| '/api/tables/:id/votes/:id', | ||
| ); | ||
| expect( | ||
| normalizeEndpoint( | ||
| 'https://example.com/api/tables/9e770d72-7a5d-4fd7-8c4e-7cf6115a1f6b', | ||
| ), | ||
| ).toBe('/api/tables/:uuid'); | ||
| }); | ||
|
|
||
| it('알림만 보고 사용자에게 영향받은 기능을 판단할 수 있도록 pathname을 기능 단위로 분류한다', () => { | ||
| expect(resolveFeatureFromPathname('/ko/home')).toBe('landing'); | ||
| expect(resolveFeatureFromPathname('/en/table/customize/1')).toBe('timer'); | ||
| expect(resolveFeatureFromPathname('/table/customize/1/end')).toBe( | ||
| 'debate-end', | ||
| ); | ||
| expect(resolveFeatureFromPathname('/table/customize/1/end/vote/2')).toBe( | ||
| 'vote', | ||
| ); | ||
| expect(resolveFeatureFromPathname('/live/1')).toBe('live-share'); | ||
| expect(resolveFeatureFromPathname('/oauth')).toBe('auth'); | ||
| }); | ||
|
|
||
| it('타이머, 투표, 실시간 공유의 5xx는 핵심 흐름 장애로 보고 즉시 대응 대상으로 분류한다', () => { | ||
| expect(resolveApiErrorLevel(500, 'timer')).toBe('fatal'); | ||
| expect(resolveApiErrorLevel(500, 'vote')).toBe('fatal'); | ||
| expect(resolveApiErrorLevel(500, 'live-share')).toBe('fatal'); | ||
| expect(resolveApiErrorLevel(500, 'landing')).toBe('error'); | ||
| }); | ||
|
|
||
| it('사용자 입력이나 상태 충돌처럼 예상 가능한 에러는 즉시 대응 알림보다 낮은 우선순위로 분류한다', () => { | ||
| expect(resolveApiErrorLevel(400, 'timer')).toBe('warning'); | ||
| expect(resolveApiErrorLevel(404, 'vote')).toBe('warning'); | ||
| expect(resolveApiErrorLevel(409, 'live-share')).toBe('warning'); | ||
| expect(resolveApiErrorLevel(422, 'table-composition')).toBe('warning'); | ||
| }); | ||
|
|
||
| it('토큰 재발급으로 복구될 수 있는 401은 즉시 대응 알림에서 제외한다', () => { | ||
| const unauthorizedError = new AxiosError( | ||
| 'unauthorized', | ||
| undefined, | ||
| undefined, | ||
| undefined, | ||
| { | ||
| status: 401, | ||
| statusText: 'Unauthorized', | ||
| headers: {}, | ||
| config: { headers: new AxiosHeaders() }, | ||
| data: null, | ||
| }, | ||
| ); | ||
| expect(shouldSkipApiError(unauthorizedError)).toBe(true); | ||
| }); | ||
|
|
||
| it('사용자 오프라인 상태의 네트워크 에러는 운영자가 바로 대응하기 어려워 수집하지 않는다', () => { | ||
| const offlineError = new AxiosError('Network Error', 'ERR_NETWORK'); | ||
|
|
||
| vi.spyOn(navigator, 'onLine', 'get').mockReturnValue(false); | ||
|
|
||
| expect(shouldSkipApiError(offlineError)).toBe(true); | ||
| }); | ||
|
|
||
| it('온라인 상태에서 발생한 네트워크 에러는 서버 연결 실패 가능성이 있어 수집한다', () => { | ||
| const onlineNetworkError = new AxiosError('Network Error', 'ERR_NETWORK'); | ||
|
|
||
| vi.spyOn(navigator, 'onLine', 'get').mockReturnValue(true); | ||
|
|
||
| expect(shouldSkipApiError(onlineNetworkError)).toBe(false); | ||
| }); | ||
|
|
||
| it('알림 제목만 보고 실패한 요청을 파악할 수 있도록 상태 코드, method, endpoint를 담는다', () => { | ||
| const error = new AxiosError('Request failed', undefined, { | ||
| method: 'post', | ||
| url: '/api/live/123', | ||
| headers: new AxiosHeaders(), | ||
| }); | ||
| const metadata = buildSentryApiErrorMetadata(error, '/ko/live/123'); | ||
|
|
||
| const sentryError = createSentryApiError(error, metadata); | ||
|
|
||
| expect(sentryError.name).toBe('[network-error] POST /api/live/:id'); | ||
| }); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
Sentry 컨텍스트에 원본 요청/쿼리 데이터를 가공 없이 전송하고 있어 개인정보 유출 위험이 있습니다.
두 위치 모두 사용자 입력이 포함될 수 있는 원본 데이터(쿼리스트링, 요청 파라미터, 응답 바디)를 그대로 Sentry(제3자 서비스)로 전송합니다. 여기에는 이메일, 초대 코드, OAuth
code/state, 서버 에러 메시지에 담긴 사용자 식별정보 등 민감정보가 포함될 수 있습니다. 두 사이트는 동일한 근본 원인(원본 데이터를 그대로 전달)을 공유하므로 공통 마스킹/allowlist 유틸을 만들어 적용하는 것을 권장합니다.src/apis/axiosInstance.ts#L71-L83:setContext('request', ...)의params와setContext('response', { data: error.response?.data })의data를 민감 필드 마스킹 또는 allowlist 기반 축약 후 전송하도록 수정.src/components/ErrorBoundary/ErrorBoundary.tsx#L49-L53:setContext('render', ...)의search: window.location.search를 알려진 민감 쿼리 파라미터(토큰, code, state 등)를 제거/마스킹한 값으로 대체.📍 Affects 2 files
src/apis/axiosInstance.ts#L71-L83(this comment)src/components/ErrorBoundary/ErrorBoundary.tsx#L49-L53🤖 Prompt for AI Agents