Skip to content

Conversation

@jllee000
Copy link
Collaborator

@jllee000 jllee000 commented Jan 8, 2026

📌 Related Issues

관련된 Issue를 태그해주세요. (e.g. - close #25)

📄 Tasks

  1. 온보딩 구글 로그인 완료 시, 윈도우 메시징 기법으로 전달
  2. 익스텐션 상에서는 메시징으로 받은 토큰 크롬스토리지로 저장
  3. 토큰에 따른 ui 분기 및 아이콘 수정

⭐ PR Point (To Reviewer)

📷 Screenshot

Summary by CodeRabbit

릴리스 노트

  • Refactor

    • 토큰 처리 메커니즘 개선
    • 오류 처리 로직 간소화
  • Chores

    • 디버그 로깅 제거로 성능 최적화
    • 내부 저장소 키 업데이트
  • UI 개선

    • 로그아웃 버튼 아이콘을 SVG로 업데이트

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Jan 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
pinback-client-client Ready Ready Preview, Comment Jan 8, 2026 4:02pm
pinback-client-landing Ready Ready Preview, Comment Jan 8, 2026 4:02pm

@jllee000 jllee000 added the feat 기능 개발하라 개발 달려라 달려 label Jan 8, 2026
@jllee000 jllee000 self-assigned this Jan 8, 2026
@github-actions github-actions bot added the fix 버그 수정하라 러브버그 label Jan 8, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

Walkthrough

토큰 저장소 메커니즘을 Chrome 로컬 스토리지에서 window.postMessage 기반 통신으로 전환하고, 인증 오류 시 온보딩 리다이렉트 로직을 제거하며, 디버그 로그 문구를 정리한 변경 사항입니다.

Changes

Cohort / File(s) 변경 요약
토큰 저장소 메커니즘 전환
apps/client/src/pages/onBoarding/GoogleCallback.tsx, apps/client/src/shared/apis/queries.ts, apps/extension/src/content.ts, apps/extension/src/background.ts
토큰 저장을 직접 Chrome 로컬 스토리지 호출에서 window.postMessage SET_TOKEN 메시지 기반으로 변경. GoogleCallback과 queries는 window.postMessage로 토큰 전송, content와 background에서 메시지 수신 후 Chrome 스토리지에 저장
인증 에러 처리 단순화
apps/extension/src/apis/axiosInstance.ts
401/403 응답 시 온보딩 URL로의 리다이렉트 로직 완전 제거. 기존 재시도 메커니즘과 스토리지 초기화 코드 삭제
확장프로그램 상태 관리 개선
apps/extension/src/App.tsx
isToken 상태 타입을 boolean | null에서 boolean으로 변경, 초기값을 null에서 false로 변경
로그아웃 UI 개선
apps/extension/src/pages/LogOutPop.tsx
Icon 컴포넌트를 logout_chippi.svg 이미지 요소로 교체
배경 메시지 처리 개선
apps/extension/src/background.ts
Chrome 스토리지 저장 키 형식 변경 및 로그 메시지 일반화
디버그 로깅 제거
apps/client/public/firebase-messaging-sw.js, packages/design-system/src/components/gtag/utils/gtag.ts
Firebase FCM 및 Google Analytics 관련 console.log 디버그 문구 제거

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PR #224: GoogleCallback.tsx 및 queries.ts의 토큰 저장소 로직 수정 — 이 PR에서는 window.postMessage 기반으로 변경하는 반면 #224는 Chrome 스토리지 직접 호출을 추가하는 상충 변경
  • PR #226: axiosInstance.ts의 인증 에러 처리 로직 수정 — 이 PR은 리다이렉트 로직을 제거하는 반면 #226은 재시도 가드 로직을 추가하는 상충 변경
  • PR #213: 확장프로그램 UI 및 토큰 처리 로직 수정 — App.tsx, LogOutPop.tsx, content/background 메시지 흐름 동시 변경

Suggested labels

frontend

Suggested reviewers

  • constantly-dev
  • jjangminii

Poem

🐰 토큰 메시지로 살금살금,
크롬 저장소 대신 윈도우 속 춤,
로그는 깨끗이 정리하고,
에러 리다이렉트는 안녕하고,
확장과 클라이언트 소통 더 매끄럽게! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning firebase-messaging-sw.js와 gtag.ts의 console.log 제거, LogOutPop.tsx의 아이콘 변경은 #223의 범위를 벗어나 있습니다. firebase-messaging-sw.js, gtag.ts의 로그 제거와 LogOutPop.tsx의 아이콘 변경을 별도 PR로 분리하고, 이 PR에서는 제거하시기 바랍니다.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 익스텐션 로그인 상태 분기 및 토큰 저장 로직 수정으로, 코드 변경의 핵심 내용(토큰 저장 방식 변경 및 UI 분기)을 정확히 반영하고 있습니다.
Description check ✅ Passed PR 설명은 필수 템플릿의 핵심 섹션(Related Issues, Tasks)을 포함하고 있으며, 세 가지 작업 항목을 명확히 나열하고 있습니다.
Linked Issues check ✅ Passed 코드 변경사항이 #223의 요구사항(온보딩 완료 시 토큰을 로컬스토리지 및 익스텐션 저장소에 저장)을 충분히 충족합니다. #25는 별개의 기능(Progress 컴포넌트)이며 이 PR과 무관합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ffa7b5 and 04b7407.

📒 Files selected for processing (1)
  • packages/design-system/src/components/gtag/utils/gtag.ts
💤 Files with no reviewable changes (1)
  • packages/design-system/src/components/gtag/utils/gtag.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: storybook

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jllee000 jllee000 changed the title Fix(extension): 익스텐션 로그인 분기 및 토큰 저장 로직 수정 Fix(extension): 익스텐션 로그인 상태 분기 및 토큰 저장 로직 수정 Jan 8, 2026
@github-actions
Copy link

github-actions bot commented Jan 8, 2026

✅ Storybook chromatic 배포 확인:
🐿️ storybook

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/extension/src/content.ts (1)

2-4: event.origin 검증 추가 필수 - 심각한 보안 취약점

Content script가 모든 웹사이트에서 실행되기 때문에("matches": ["<all_urls>"]), 현재 event.source !== window 검증만으로는 불충분합니다. 악의적인 웹사이트의 JavaScript가 동일한 window 컨텍스트에서 window.postMessage()로 임의의 토큰을 설정할 수 있습니다.

event.origin 검증을 추가하여 신뢰할 수 있는 도메인(https://www.pinback.today)에서만 메시지를 수락하도록 수정하세요:

제안된 수정
 window.addEventListener('message', (event) => {
+  if (event.origin !== 'https://www.pinback.today') return;
   if (event.source !== window) return;
   if (event.data.type === 'SET_TOKEN') {

개발 환경을 포함해야 한다면:

const ALLOWED_ORIGINS = [
  'https://www.pinback.today',
  'http://localhost:3000',
];

window.addEventListener('message', (event) => {
  if (!ALLOWED_ORIGINS.includes(event.origin)) return;
  if (event.source !== window) return;
  // ...
});
🤖 Fix all issues with AI agents
In @apps/client/src/shared/apis/queries.ts:
- Line 74: Replace the direct client-side persistence of the JWT by removing the
localStorage.setItem('token', newToken) call and instead either send the
newToken to the backend to set an HttpOnly, Secure cookie (preferred) or store
it in ephemeral in-memory state (e.g., an auth store) combined with a
short-lived access token + refresh token flow; update the code path handling
newToken (reference: localStorage.setItem and variable newToken) to call the
backend endpoint that issues HttpOnly cookies or to populate your in-memory auth
store and implement refresh-token handling so tokens are not stored in
localStorage.

In @apps/extension/src/App.tsx:
- Line 18: Remove the debug console.log in App.tsx: delete the line
console.log(!!result.token, result); (or wrap it behind a development-only guard
like if (process.env.NODE_ENV === 'development') { ... } or use a proper
logger), ensuring no debug console output remains in the production build.

In @apps/extension/src/background.ts:
- Around line 5-7: Remove the debug console.log that prints sensitive info: find
the chrome.storage.local.set call that uses info.email (and the similar
console.log usage around lines 19-21) and delete the console.log(info.email)
statements (or replace with environment-guarded logging, e.g., only log when a
NODE_ENV or build flag indicates development); ensure no direct logging of
info.email or tokens remains in background.ts and keep only non-sensitive or
conditional logs.

In @apps/extension/src/content.ts:
- Line 10: Remove the debug console.log that prints the token
(console.log('Token saved!', event.data.token)); do not output event.data.token
or other sensitive values to the console—either delete the statement entirely or
replace it with a non-sensitive confirmation message (e.g., "Token saved"
without the token) or a secure/log- redacted call that omits event.data.token.
- Around line 9-11: Remove the duplicate local storage write in content.ts:
delete the chrome.storage.local.set(...) call that writes event.data.token and
keep only the chrome.runtime.sendMessage(...) that sends the token; ensure the
background message handler for "SET_TOKEN" (the handler that currently calls
chrome.storage.local.set in background.ts) remains responsible for persisting
event.data.token so the token is stored only once in the SET_TOKEN handling
code.
🧹 Nitpick comments (4)
apps/extension/src/App.tsx (1)

14-14: 로딩 상태 처리 개선 권장

isToken 상태를 boolean | null에서 boolean로 변경하면서 초기값을 false로 설정했습니다. 이 경우 토큰 확인이 완료되기 전에 LogOutPop이 먼저 렌더링되어 사용자에게 깜빡임이 보일 수 있습니다.

로딩 상태를 구분하기 위해 세 가지 상태를 고려해보세요:

  • null: 아직 확인 중
  • true: 인증됨
  • false: 미인증
♻️ 로딩 상태를 포함한 개선안
-  const [isToken, setIsToken] = useState(false);
+  const [isToken, setIsToken] = useState<boolean | null>(null);

그리고 렌더링 로직에서:

return (
  <>
    {isToken === null ? (
      <div>로딩 중...</div> // 또는 로딩 스피너
    ) : isToken ? (
      isDuplicatePop ? (
        <DuplicatePop
          onLeftClick={handleDuplicateLeftClick}
          onRightClick={handleDuplicateRightClick}
        />
      ) : (
        <MainPop type={mainPopType} />
      )
    ) : (
      <LogOutPop />
    )}
  </>
);
apps/client/src/shared/apis/queries.ts (1)

75-84: 함수 추출 권장

sendTokenToExtension 함수가 onSuccess 콜백 내부에 정의되어 있습니다. 이 함수는 재사용 가능한 유틸리티이므로, 모듈 최상위 레벨로 추출하여 테스트 가능성과 재사용성을 높이는 것을 권장합니다.

♻️ 함수 추출 제안

파일 상단에 유틸리티 함수로 추출:

// 파일 상단 또는 별도 utils 파일
const sendTokenToExtension = (token: string) => {
  window.postMessage(
    {
      type: 'SET_TOKEN',
      token,
    },
    window.location.origin
  );
};

그리고 onSuccess에서 직접 호출:

  onSuccess: (data) => {
    const newToken = data?.data?.token || data?.token;

    if (newToken) {
      localStorage.setItem('token', newToken);
-      const sendTokenToExtension = (token: string) => {
-        window.postMessage(
-          {
-            type: 'SET_TOKEN',
-            token,
-          },
-          window.location.origin
-        );
-      };
      sendTokenToExtension(newToken);
    }
    // ...
  },
apps/client/src/pages/onBoarding/GoogleCallback.tsx (2)

10-20: useEffect 의존성 배열에 필요한 값이 누락되어 있습니다.

navigatesearchParams가 effect 내부에서 사용되지만 의존성 배열이 비어 있습니다. 이로 인해 ESLint react-hooks/exhaustive-deps 경고가 발생할 수 있으며, 향후 리팩토링 시 stale closure 문제가 발생할 수 있습니다.

♻️ 의존성 배열 수정 제안
  useEffect(() => {
    const code = searchParams.get('code');

    if (!code) {
      alert('로그인 실패. 다시 시도해주세요.');
      navigate('/onboarding?step=SOCIAL_LOGIN');
      return;
    }

    loginWithCode(code);
-  }, []);
+  }, [searchParams, navigate]);

참고: loginWithCode도 의존성에 포함하거나, useCallback으로 감싸는 것이 이상적이지만, 현재 구조에서는 무한 루프를 피하기 위해 위 수정만으로도 충분합니다.


30-39: sendTokenToExtension 함수를 조건문 밖으로 추출하는 것을 권장합니다.

현재 함수가 if (accessToken) 블록 내부에 정의되어 있어 코드 가독성이 떨어지고, 재사용이 어렵습니다. 컴포넌트 외부 또는 최소한 handleUserLogin 함수 상단으로 이동하면 코드 구조가 더 명확해집니다.

♻️ 함수 추출 제안
+const sendTokenToExtension = (token: string) => {
+  window.postMessage(
+    {
+      type: 'SET_TOKEN',
+      token,
+    },
+    window.location.origin
+  );
+};
+
 const GoogleCallback = () => {
   const navigate = useNavigate();
   const [searchParams] = useSearchParams();
   
   // ... 생략 ...
   
   const handleUserLogin = (
     isUser: boolean,
     accessToken: string | undefined
   ) => {
     if (isUser) {
       if (accessToken) {
         localStorage.setItem('token', accessToken);
-
-        const sendTokenToExtension = (token: string) => {
-          window.postMessage(
-            {
-              type: 'SET_TOKEN',
-              token,
-            },
-            window.location.origin
-          );
-        };
         sendTokenToExtension(accessToken);
       }
       navigate('/');
     } else {
       navigate('/onboarding?step=ALARM');
     }
   };
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9682f51 and a804963.

⛔ Files ignored due to path filters (2)
  • apps/extension/public/logout_chippi.svg is excluded by !**/*.svg
  • packages/design-system/src/icons/source/logout_chippi.2512.svg is excluded by !**/*.svg
📒 Files selected for processing (7)
  • apps/client/src/pages/onBoarding/GoogleCallback.tsx
  • apps/client/src/shared/apis/queries.ts
  • apps/extension/src/App.tsx
  • apps/extension/src/apis/axiosInstance.ts
  • apps/extension/src/background.ts
  • apps/extension/src/content.ts
  • apps/extension/src/pages/LogOutPop.tsx
💤 Files with no reviewable changes (1)
  • apps/extension/src/apis/axiosInstance.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-08T11:47:10.642Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 30
File: apps/extension/src/App.tsx:10-21
Timestamp: 2025-07-08T11:47:10.642Z
Learning: In apps/extension/src/App.tsx, the InfoBox component currently uses a hardcoded external URL for the icon prop as a temporary static placeholder. The plan is to replace this with dynamic favicon extraction from bookmarked websites in future iterations.

Applied to files:

  • apps/extension/src/pages/LogOutPop.tsx
📚 Learning: 2025-09-11T11:48:10.615Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 75
File: apps/extension/src/apis/axiosInstance.ts:30-34
Timestamp: 2025-09-11T11:48:10.615Z
Learning: Pinback 프로젝트에서는 사용자 이메일 저장 시 'email' 키를 사용하도록 통일했습니다 (localStorage 및 chrome.storage.local 모두).

Applied to files:

  • apps/extension/src/background.ts
🪛 ast-grep (0.40.4)
apps/client/src/shared/apis/queries.ts

[warning] 73-73: Detected potential storage of sensitive information in browser localStorage. Sensitive data like email addresses, personal information, or authentication tokens should not be stored in localStorage as it's accessible to any script.
Context: localStorage.setItem('token', newToken)
Note: [CWE-312] Cleartext Storage of Sensitive Information [REFERENCES]
- https://owasp.org/www-community/vulnerabilities/HTML5_Security_Cheat_Sheet
- https://cwe.mitre.org/data/definitions/312.html

(browser-storage-sensitive-data)

🪛 GitHub Check: lint
apps/extension/src/App.tsx

[warning] 18-18:
Unexpected console statement

apps/extension/src/background.ts

[warning] 6-6:
Unexpected console statement


[warning] 20-20:
Unexpected console statement

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: storybook
🔇 Additional comments (2)
apps/extension/src/pages/LogOutPop.tsx (1)

2-2: 아이콘 렌더링 개선 확인

Icon 컴포넌트를 SVG 이미지 파일로 교체한 변경사항이 적절합니다. 접근성을 위한 alt 텍스트도 포함되어 있고, 스타일링도 올바르게 적용되었습니다.

Also applies to: 10-14

apps/client/src/pages/onBoarding/GoogleCallback.tsx (1)

22-45: 토큰 전달 로직이 PR 목표에 맞게 잘 구현되었습니다.

window.postMessage를 사용하여 익스텐션으로 토큰을 전달하는 방식으로 변경된 것을 확인했습니다. targetOrigin으로 window.location.origin을 사용하여 보안 측면에서도 적절합니다.

익스텐션의 content script에서 SET_TOKEN 메시지를 올바르게 수신하고 있으며, Chrome storage에 토큰이 정상적으로 저장되고 있습니다. 추가로 background script에서도 메시지를 수신하여 별도로 token을 저장하므로 안정성이 더욱 높습니다.

};

if (newToken) {
localStorage.setItem('token', newToken);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

보안: localStorage에 토큰 저장 검토 필요

정적 분석 도구가 올바르게 지적한 것처럼, localStorage에 인증 토큰을 평문으로 저장하는 것은 XSS 공격에 취약합니다(CWE-312). 모든 스크립트가 접근 가능하므로, 악성 스크립트가 토큰을 탈취할 수 있습니다.

다음 대안을 고려해주세요:

  1. HttpOnly 쿠키 사용 (서버측 설정 필요)
  2. 짧은 만료 시간의 토큰 + 리프레시 토큰 전략
  3. 메모리 내 저장 (새로고침 시 재로그인 필요)
localStorage vs cookies for JWT token storage security best practices 2026
🤖 Prompt for AI Agents
In @apps/client/src/shared/apis/queries.ts at line 74, Replace the direct
client-side persistence of the JWT by removing the localStorage.setItem('token',
newToken) call and instead either send the newToken to the backend to set an
HttpOnly, Secure cookie (preferred) or store it in ephemeral in-memory state
(e.g., an auth store) combined with a short-lived access token + refresh token
flow; update the code path handling newToken (reference: localStorage.setItem
and variable newToken) to call the backend endpoint that issues HttpOnly cookies
or to populate your in-memory auth store and implement refresh-token handling so
tokens are not stored in localStorage.

Comment on lines +9 to +11
chrome.storage.local.set({ token: event.data.token }, () => {
console.log('Token saved!', event.data.token);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "background.ts" -o -name "content.ts" | head -20

Repository: Pinback-Team/pinback-client

Length of output: 137


🏁 Script executed:

cat -n apps/extension/src/content.ts

Repository: Pinback-Team/pinback-client

Length of output: 550


🏁 Script executed:

cat -n apps/extension/src/background.ts

Repository: Pinback-Team/pinback-client

Length of output: 903


중복 저장 로직 제거 필요

content.ts에서 chrome.storage.local.set으로 토큰을 저장한 후, 동일한 토큰을 chrome.runtime.sendMessage로 background 스크립트에 전달하고 있습니다. background.ts에서는 SET_TOKEN 메시지를 수신하여 동일한 저장소에 다시 저장합니다(9-11줄과 background.ts 19-21줄).

이는 불필요한 중복입니다. 토큰 저장은 background에서만 담당하도록 통일하세요:

  • content.ts에서 9-11줄의 chrome.storage.local.set 호출 제거
  • message 전달만 유지하여 background에서 저장 처리
적용 예시
 window.addEventListener('message', (event) => {
   if (event.source !== window) return;
   if (event.data.type === 'SET_TOKEN') {
     chrome.runtime.sendMessage({
       type: 'SET_TOKEN',
       token: event.data.token,
     });
-    chrome.storage.local.set({ token: event.data.token }, () => {
-      console.log('Token saved!', event.data.token);
-    });
   }
 });
🤖 Prompt for AI Agents
In @apps/extension/src/content.ts around lines 9 - 11, Remove the duplicate
local storage write in content.ts: delete the chrome.storage.local.set(...) call
that writes event.data.token and keep only the chrome.runtime.sendMessage(...)
that sends the token; ensure the background message handler for "SET_TOKEN" (the
handler that currently calls chrome.storage.local.set in background.ts) remains
responsible for persisting event.data.token so the token is stored only once in
the SET_TOKEN handling code.

token: event.data.token,
});
chrome.storage.local.set({ token: event.data.token }, () => {
console.log('Token saved!', event.data.token);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

디버그용 console.log 제거 필요

토큰과 같은 민감한 정보를 콘솔에 출력하지 않도록 해주세요.

🧹 제거 제안
-    chrome.storage.local.set({ token: event.data.token }, () => {
-      console.log('Token saved!', event.data.token);
-    });
+    chrome.storage.local.set({ token: event.data.token });

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @apps/extension/src/content.ts at line 10, Remove the debug console.log that
prints the token (console.log('Token saved!', event.data.token)); do not output
event.data.token or other sensitive values to the console—either delete the
statement entirely or replace it with a non-sensitive confirmation message
(e.g., "Token saved" without the token) or a secure/log- redacted call that
omits event.data.token.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/extension/src/background.ts (1)

18-20: 토큰 로깅 제거 - 보안 개선!

민감한 토큰 정보를 콘솔 로그에서 제거한 것은 보안 측면에서 매우 좋은 개선입니다.

다만, 선택적으로 아래 사항들을 고려해보시면 더 좋을 것 같습니다:

  • message.token의 유효성 검증 (undefined, null, 빈 문자열 체크)
  • 스토리지 저장 실패 시 에러 핸들링
♻️ 선택적 개선 제안
 chrome.runtime.onMessage.addListener((message) => {
   if (message.type === 'SET_TOKEN') {
+    if (!message.token) {
+      console.error('Invalid token received');
+      return;
+    }
     chrome.storage.local.set({ token: message.token }, () => {
+      if (chrome.runtime.lastError) {
+        console.error('Failed to save token:', chrome.runtime.lastError);
+        return;
+      }
       console.log('Token saved!');
     });
   }
 });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a804963 and 8ffa7b5.

📒 Files selected for processing (7)
  • apps/client/public/firebase-messaging-sw.js
  • apps/client/src/shared/apis/queries.ts
  • apps/extension/src/App.tsx
  • apps/extension/src/background.ts
  • apps/extension/src/content.ts
  • apps/extension/src/hooks/useSaveBookmarks.ts
  • packages/design-system/src/components/gtag/utils/gtag.ts
💤 Files with no reviewable changes (2)
  • apps/client/public/firebase-messaging-sw.js
  • apps/extension/src/hooks/useSaveBookmarks.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/extension/src/content.ts
  • apps/extension/src/App.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-11T11:48:10.615Z
Learnt from: constantly-dev
Repo: Pinback-Team/pinback-client PR: 75
File: apps/extension/src/apis/axiosInstance.ts:30-34
Timestamp: 2025-09-11T11:48:10.615Z
Learning: Pinback 프로젝트에서는 사용자 이메일 저장 시 'email' 키를 사용하도록 통일했습니다 (localStorage 및 chrome.storage.local 모두).

Applied to files:

  • apps/extension/src/background.ts
🪛 ast-grep (0.40.4)
apps/client/src/shared/apis/queries.ts

[warning] 73-73: Detected potential storage of sensitive information in browser localStorage. Sensitive data like email addresses, personal information, or authentication tokens should not be stored in localStorage as it's accessible to any script.
Context: localStorage.setItem('token', newToken)
Note: [CWE-312] Cleartext Storage of Sensitive Information [REFERENCES]
- https://owasp.org/www-community/vulnerabilities/HTML5_Security_Cheat_Sheet
- https://cwe.mitre.org/data/definitions/312.html

(browser-storage-sensitive-data)

🔇 Additional comments (3)
apps/extension/src/background.ts (1)

4-6: 개인정보 로깅 제거 - 좋은 개선사항입니다!

사용자 이메일을 콘솔 로그에서 제거한 것은 개인정보보호 측면에서 올바른 개선입니다. PII(Personally Identifiable Information) 데이터를 로그에 남기지 않는 것이 GDPR, CCPA 등 개인정보 보호 규정 준수에 도움이 됩니다.

packages/design-system/src/components/gtag/utils/gtag.ts (1)

43-43: 프로덕션 로그 정리 작업 승인

GA 이벤트 전송 디버깅 로그를 제거한 변경사항이 적절합니다. 콘솔 출력을 깔끔하게 유지하면서 핵심 추적 기능은 그대로 유지됩니다.

apps/client/src/shared/apis/queries.ts (1)

75-84: window.postMessage 패턴 올바르게 구현됨

window.postMessage를 통한 토큰 전달 방식과 origin 검증(window.location.origin)이 올바르게 적용되었으며, 익스텐션 콘텐츠 스크립트에서도 메시지를 올바르게 수신하고 검증하고 있습니다:

  • ✅ 메시지 출처 검증: event.source !== window로 동일 윈도우 출처 확인
  • ✅ 타입 검증: event.data.type === 'SET_TOKEN' 명시적 확인
  • ✅ 토큰 저장: chrome.storage.local.set()으로 안전하게 저장
  • ✅ 배경 스크립트 연동: chrome.runtime.sendMessage()로 배경 스크립트에 전달, 추가 검증 및 저장

보안 측면에서 메시지 검증이 콘텐츠 스크립트와 배경 스크립트 두 단계에서 이루어지고 있어 적절합니다.

Comment on lines 73 to 74
if (newToken) {
localStorage.setItem('token', newToken);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n apps/client/src/shared/apis/queries.ts | sed -n '65,90p'

Repository: Pinback-Team/pinback-client

Length of output: 899


🏁 Script executed:

# Search for httpOnly cookie usage or alternative token storage patterns
rg -i "httponly|http-only|cookie.*secure|document\.cookie" --type ts --type js

Repository: Pinback-Team/pinback-client

Length of output: 53


🏁 Script executed:

# Check for XSS protection mechanisms like CSP, sanitization, or security headers
rg -i "content-security-policy|csp|sanitize|xss" --type ts --type js --type json

Repository: Pinback-Team/pinback-client

Length of output: 50385


🏁 Script executed:

# Search for token storage patterns across the codebase
rg "localStorage.*token|sessionStorage.*token|setItem.*token" --type ts --type js -A 2 -B 2

Repository: Pinback-Team/pinback-client

Length of output: 2568


🏁 Script executed:

# Check if there's any security configuration file or documentation
fd -e "json" -e "ts" -e "js" | xargs grep -l "security\|csrf\|xss" 2>/dev/null | head -20

Repository: Pinback-Team/pinback-client

Length of output: 113


localStorage 토큰 저장 방식의 보안 위험성 확인

localStorage에 인증 토큰을 저장하는 방식이 정적 분석 결과와 일치합니다. 이는 XSS 공격에 취약한 구조입니다 (CWE-312). JavaScript에 접근 가능한 저장소에 민감한 인증 정보가 저장되므로, 악의적인 스크립트가 토큰을 탈취할 수 있습니다.

현재 아키텍처에서는:

  • axiosInstance의 요청 인터셉터가 localStorage에서 토큰을 읽음
  • httpOnly 쿠키 사용 없음
  • XSS 방어 메커니즘(CSP 등) 미적용 상태

토큰 저장 방식을 보안 기준에 맞게 개선하거나, XSS 방어 메커니즘을 적용하여 이 위험을 완화해야 합니다.

🧰 Tools
🪛 ast-grep (0.40.4)

[warning] 73-73: Detected potential storage of sensitive information in browser localStorage. Sensitive data like email addresses, personal information, or authentication tokens should not be stored in localStorage as it's accessible to any script.
Context: localStorage.setItem('token', newToken)
Note: [CWE-312] Cleartext Storage of Sensitive Information [REFERENCES]
- https://owasp.org/www-community/vulnerabilities/HTML5_Security_Cheat_Sheet
- https://cwe.mitre.org/data/definitions/312.html

(browser-storage-sensitive-data)

Copy link
Collaborator

@jjangminii jjangminii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인했습니다-!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이미지 사라졌을까요?? 😢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat 기능 개발하라 개발 달려라 달려 fix 버그 수정하라 러브버그

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Fix] 온보딩 페이지 - 크롬스토리지 저장 추가

4 participants