-
Notifications
You must be signed in to change notification settings - Fork 1
Feat(client&extension): 온보딩<->익스텐션 데이터 통신 작업 #99
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,9 +61,18 @@ export const usePostSignUp = () => { | |
| mutationFn: (data: postSignUpRequest) => postSignUp(data), | ||
| onSuccess: (data) => { | ||
| const newToken = data?.data?.token || data?.token; | ||
|
|
||
| const sendTokenToExtension = (token: string) => { | ||
| window.postMessage( | ||
| { | ||
| type: 'SET_TOKEN', | ||
| token, | ||
| }, | ||
| window.location.origin | ||
| ); | ||
| }; | ||
| if (newToken) { | ||
| localStorage.setItem('token', newToken); | ||
| sendTokenToExtension(newToken); | ||
| } | ||
|
Comment on lines
73
to
76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 토큰을 localStorage에 저장하는 것은 XSS에 취약 가능하다면 서버가 HttpOnly Secure 쿠키로 토큰(또는 세션)을 설정하도록 전환하세요. 프론트에서는 localStorage 보관을 제거합니다. 대안:
🧰 Tools🪛 ast-grep (0.38.6)[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. (browser-storage-sensitive-data) |
||
|
|
||
| console.log('회원가입 성공:', data); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -24,7 +24,7 @@ const App = () => { | |||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| const handleDuplicateRightClick = () => { | ||||||||||||
| window.location.href = "/dashboard"; | ||||||||||||
| window.location.href = "https://www.pinback.today/"; | ||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 외부 페이지 이동은 chrome.tabs.create를 사용하세요 popup 내부에서 window.location.href 변경은 UX가 어색합니다. 새 탭을 여는 쪽이 일반적이며, 환경별 베이스 URL로 관리하세요. - const handleDuplicateRightClick = () => {
- window.location.href = "https://www.pinback.today/";
- };
+ const handleDuplicateRightClick = () => {
+ const url = import.meta.env.VITE_WEB_BASE_URL ?? "https://www.pinback.today/";
+ chrome.tabs.create({ url });
+ };📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| return ( | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,7 @@ | |
| ); | ||
| const newToken = response.data.data.token; | ||
| chrome.storage.local.set({ token: newToken }, () => { | ||
| console.log('Token re-saved to chrome storage'); | ||
| }); | ||
| return newToken; | ||
| }; | ||
|
|
@@ -28,14 +28,12 @@ | |
| if (isNoAuth) return config; | ||
|
|
||
| const email = await new Promise<string | undefined>((resolve) => { | ||
| chrome.storage.local.get('email', (result) => { | ||
| resolve(result.email); | ||
| }); | ||
| chrome.storage.local.get('userEmail', (result) => resolve(result.userEmail)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. storage이거 key email로 되어있지 않나요?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넹네! 이거 익스텐션 크롬스토리지랑 그냥 웹 로컬스토리지 각각의 키 구분할라고 (제가 헷갈려서..ㅎ)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그렇다면 익스텐션 axios interceptor 확인해보셔야 할 것 같아요! 이전에 인터셉터 설정할때 email로 통일한 기억이..! |
||
| }); | ||
|
|
||
| let token = await new Promise<string | undefined>((resolve) => { | ||
| chrome.storage.local.get('token', (result) => { | ||
| resolve(result.token); | ||
| resolve(result.token); | ||
| }); | ||
| }); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,39 +1,24 @@ | ||||||||||||||||||||||||||||||||||
| console.log('백그라운드 기능'); | ||||||||||||||||||||||||||||||||||
| chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { | ||||||||||||||||||||||||||||||||||
| if (message.type === 'FETCH_OG_META') { | ||||||||||||||||||||||||||||||||||
| fetch(message.url) | ||||||||||||||||||||||||||||||||||
| .then((res) => res.text()) | ||||||||||||||||||||||||||||||||||
| .then((html) => { | ||||||||||||||||||||||||||||||||||
| const parser = new DOMParser(); | ||||||||||||||||||||||||||||||||||
| const doc = parser.parseFromString(html, 'text/html'); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const getMeta = (prop) => | ||||||||||||||||||||||||||||||||||
| doc | ||||||||||||||||||||||||||||||||||
| .querySelector(`meta[property="${prop}"]`) | ||||||||||||||||||||||||||||||||||
| ?.getAttribute('content') || ''; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const makeAbsoluteUrl = (base, img) => { | ||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||
| return img ? new URL(img, base).href : ''; | ||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||
| return img; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| const image = getMeta('og:image'); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| sendResponse({ | ||||||||||||||||||||||||||||||||||
| title: getMeta('og:title'), | ||||||||||||||||||||||||||||||||||
| description: getMeta('og:description'), | ||||||||||||||||||||||||||||||||||
| siteName: getMeta('og:site_name'), | ||||||||||||||||||||||||||||||||||
| image: makeAbsoluteUrl(message.url, image), | ||||||||||||||||||||||||||||||||||
| url: getMeta('og:url') || message.url, | ||||||||||||||||||||||||||||||||||
| chrome.runtime.onInstalled.addListener((details) => { | ||||||||||||||||||||||||||||||||||
| if (details.reason === 'install') { | ||||||||||||||||||||||||||||||||||
| chrome.identity.getProfileUserInfo(function (info) { | ||||||||||||||||||||||||||||||||||
| chrome.storage.local.set({ 'userEmail': info.email }, () => { | ||||||||||||||||||||||||||||||||||
| console.log(info.email); | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+4
to
7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 이메일 수집 시 PII 로그 제거 + 빈 이메일/에러 처리 필요
아래처럼 수정해 주세요. - chrome.identity.getProfileUserInfo(function (info) {
- chrome.storage.local.set({ 'userEmail': info.email }, () => {
- console.log(info.email);
- });
+ chrome.identity.getProfileUserInfo(function (info) {
+ const email = info?.email?.trim();
+ if (email) {
+ chrome.storage.local.set({ userEmail: email }, () => {
+ if (chrome.runtime.lastError) {
+ console.warn('userEmail 저장 실패:', chrome.runtime.lastError.message);
+ }
+ });
+ } else {
+ console.warn('Chrome 프로필 이메일을 사용할 수 없습니다(비로그인/권한 미부여).');
+ }📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Check: lint[warning] 6-6: 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||
| .catch((err) => { | ||||||||||||||||||||||||||||||||||
| console.error('OG fetch 실패:', err); | ||||||||||||||||||||||||||||||||||
| sendResponse(null); | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
| return true; // async 응답 | ||||||||||||||||||||||||||||||||||
| setTimeout(() => { | ||||||||||||||||||||||||||||||||||
| chrome.tabs.create({ | ||||||||||||||||||||||||||||||||||
| url: `http://localhost:5173/onboarding?email=${info.email}`, | ||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 개발용 localhost URL 하드코딩 제거 배포 시 깨집니다. 환경변수/설정으로 분리하세요. 적용 예(파일 상단 등 외부 추가 코드): // 외부 추가 코드
const ONBOARDING_ORIGIN = import.meta.env.VITE_CLIENT_ORIGIN ?? 'https://app.pinback.io';
const ONBOARDING_URL = new URL('/onboarding', ONBOARDING_ORIGIN).toString();해당 라인 교체: - url: `http://localhost:5173/onboarding?email=${info.email}`,
+ url: ONBOARDING_URL,🤖 Prompt for AI Agents
constantly-dev marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
| }, 1000); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 이메일을 URL 쿼리로 전달하지 마세요(PII 유출 위험) + 불필요한 setTimeout 제거
- setTimeout(() => {
- chrome.tabs.create({
- url: `http://localhost:5173/onboarding?email=${info.email}`,
- });
- }, 1000);
+ chrome.tabs.create({ url: 'http://localhost:5173/onboarding' }, (tab) => {
+ const email = info?.email?.trim();
+ if (!email || !tab?.id) return;
+ // 컨텐츠 스크립트가 페이지로 window.postMessage('SET_EMAIL') 포워딩
+ chrome.tabs.sendMessage(
+ tab.id,
+ { type: 'SET_EMAIL', email },
+ () => void chrome.runtime.lastError // 컨텐츠 스크립트 로드 전 호출시 에러 무시
+ );
+ });컨텐츠 스크립트 측에서는 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| chrome.runtime.onMessage.addListener((message) => { | ||||||||||||||||||||||||||||||||||
| if (message.type === 'SET_TOKEN') { | ||||||||||||||||||||||||||||||||||
| chrome.storage.local.set({ 'token': message.token }, () => { | ||||||||||||||||||||||||||||||||||
| console.log('Token saved!', message.token); | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+18
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 토큰 콘솔 출력 제거 + 검증/응답 처리 + 발신자 검증 추가
-chrome.runtime.onMessage.addListener((message) => {
- if (message.type === 'SET_TOKEN') {
- chrome.storage.local.set({ 'token': message.token }, () => {
- console.log('Token saved!', message.token);
- });
- }
-});
+chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
+ if (message?.type !== 'SET_TOKEN') return;
+ if (sender?.id && sender.id !== chrome.runtime.id) {
+ sendResponse({ ok: false, error: 'unauthorized_sender' });
+ return;
+ }
+ const token = typeof message.token === 'string' ? message.token.trim() : '';
+ if (!token) {
+ sendResponse({ ok: false, error: 'invalid_token' });
+ return;
+ }
+ chrome.storage.local.set({ token }, () => {
+ if (chrome.runtime.lastError) {
+ sendResponse({ ok: false, error: chrome.runtime.lastError.message });
+ } else {
+ sendResponse({ ok: true });
+ }
+ });
+ return true; // 비동기 응답 유지
+});참고: 이슈 #85 요구사항에 따라 🧰 Tools🪛 GitHub Check: lint[warning] 21-21: 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1 +1,10 @@ | ||||||||||||||||||||||||||||||||||||||||||
| console.log('컨텐츠 스크립트 로드됨'); | ||||||||||||||||||||||||||||||||||||||||||
| 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, | ||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+2
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion postMessage 출처 검증 없음 → 임의 사이트가 토큰 주입 가능 콘텐츠 스크립트가 모든 사이트(<all_urls>)에서 메시지를 수신합니다. origin 화이트리스트 및 페이로드 검증이 필요합니다. -window.addEventListener('message', (event) => {
+const ALLOWED_ORIGINS = ['https://www.pinback.today', 'http://localhost:5173'];
+window.addEventListener('message', (event) => {
if (event.source !== window) return;
- if (event.data.type === 'SET_TOKEN') {
+ if (!ALLOWED_ORIGINS.includes(event.origin)) return;
+ if (event.data?.type === 'SET_TOKEN' && typeof event.data.token === 'string') {
chrome.runtime.sendMessage({
type: 'SET_TOKEN',
token: event.data.token,
});
}
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
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.
🛠️ Refactor suggestion
이메일 누락 시 가입 요청 차단
userEmail이 비어있으면 요청을 막고 사용자 안내하세요.
📝 Committable suggestion
🤖 Prompt for AI Agents