From 506f594a4e612a50bf31d652d8d6c85a0f332355 Mon Sep 17 00:00:00 2001 From: Haewon Kam Date: Wed, 7 Oct 2026 14:47:09 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=8D=B0=EB=AA=A8=20=EB=AF=B8=EB=A6=AC?= =?UTF-8?q?=EB=B3=B4=EA=B8=B0=20=EB=B0=B0=ED=8F=AC=20=EC=A3=BC=EC=86=8C?= =?UTF-8?q?=EC=97=90=EC=84=9C=EB=8F=84=20=EC=8A=A4=ED=8A=9C=EB=94=94?= =?UTF-8?q?=EC=98=A4=20=EC=84=9C=EB=B2=84=20=ED=95=A8=EC=88=98=EB=A5=BC=20?= =?UTF-8?q?=EB=B6=80=EB=A5=BC=20=EC=88=98=20=EC=9E=88=EA=B2=8C=20=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 미리보기는 Vercel 로그인 뒤에 있어 팀원만 연다. Referer 확인도 출처 단위로 비교한다. Co-Authored-By: Claude Opus 5.5 --- api/_studio.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/api/_studio.js b/api/_studio.js index ac65548..67d5bb3 100644 --- a/api/_studio.js +++ b/api/_studio.js @@ -30,13 +30,17 @@ const ALLOWED_ORIGINS = [ 'http://127.0.0.1:3000', ]; +/** 이 프로젝트의 미리보기 배포 주소. 미리보기는 Vercel 로그인(배포 보호) 뒤에 있어 팀원만 연다. */ +const PREVIEW_ORIGIN = /^https:\/\/infinith-demo-[a-z0-9]+-o2odev-5530s-projects\.vercel\.app$/; +const isAllowedOrigin = (o) => ALLOWED_ORIGINS.includes(o) || PREVIEW_ORIGIN.test(o); + /** * CORS·메서드·출처를 확인한다. 통과하면 null, 아니면 이미 응답을 보낸 뒤 true 를 돌려준다. * @param {string[]} methods 허용 메서드 */ export function guard(req, res, methods) { const origin = req.headers.origin || ''; - const allowed = ALLOWED_ORIGINS.includes(origin); + const allowed = isAllowedOrigin(origin); if (allowed) { res.setHeader('Access-Control-Allow-Origin', origin); res.setHeader('Vary', 'Origin'); @@ -53,7 +57,8 @@ export function guard(req, res, methods) { } // 같은 출처 GET 은 브라우저가 Origin 을 붙이지 않을 수 있어 Referer 로도 확인한다. const referer = req.headers.referer || ''; - const sameSite = ALLOWED_ORIGINS.some((o) => referer.startsWith(o + '/')); + const refOrigin = (referer.match(/^https?:\/\/[^/]+/) || [''])[0]; + const sameSite = isAllowedOrigin(refOrigin); if (!allowed && !sameSite) { res.status(403).json({ ok: false, error: 'forbidden origin' }); return true;