Files
Gnuboard7/plugins/_bundled/sirsoft-tosspayments/dist/js/plugin.iife.js
T
HeuJung 1e89335258 fix(core,extensions): 동적 스크립트 주입 경로 전부에 출처 게이트 적용
레이아웃 scripts[] 에만 있던 원격 스크립트 차단 게이트가 다른 주입 경로에는
없어, 저장측(SafeLayoutExpressions·NoExternalUrls)이 외부 URL 저장을 422 로
막아도 런타임 디스패치 한 번으로 임의 원격 코드가 로드됐다. 저장 검증이
통째로 무의미해지는 상태였고 오류도 경고도 남지 않았다.

판정식을 support/scriptSrcPolicy.ts 로 분리해 런타임 SSoT 를 하나로 두고,
loadScript 액션 · 확장 핸들러 재로드 · 편집기 프리뷰 · 편집기 자산 매니페스트 ·
G7Core.asset.loadScript · PG 플러그인 SDK 주입이 모두 그것을 경유하게 했다.
사본을 두지 않은 이유는 차집합이 그대로 우회로가 되기 때문이다.

- 게이트는 캐시 검사보다 앞에 둔다 (뒤에 두면 이미 로드된 미신뢰 스크립트가
 캐시 히트로 통과한다)
- NoExternalUrls 순회를 9키로 확대 — 번들 레이아웃 601개 사전 스캔으로
 정당한 외부 URL 0건을 확인한 뒤 넓혔다 (저장 회귀 없음)
- callExternal 은 생성자를 참조 동일성으로 거부하고(별칭 전역 포함),
 프로토타입 경로 세그먼트를 읽기·매핑 키 양쪽에서 차단한다
- PG SDK 는 서비스 SDK 라 자체 호스팅이 불가능하므로 manifest 선언 +
 주입 직전 호스트 확인이 게이트다. tosspayments SDK URL 은 확장자가 없어
 정적 검사에 걸리지 않아 그 런타임 확인이 유일한 게이트다
- 함께 드러난 결함: 동시 loadScript 가 로드 전에 완료 처리되던 문제,
 확장 재로드에서 script 기존재가 CSS 까지 건너뛰던 문제, loadCSS 의
 in-flight 미공유
2026-09-02 21:43:13 +09:00

2 lines
11 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function(){"use strict";const F="sirsoft-tosspayments",y="g7:sirsoft-tosspayments:pendingClose",C="/api/plugins/sirsoft-tosspayments/payment/close-report";function f(){try{return window.sessionStorage??null}catch{return null}}function G(e){const t=f();if(t)try{t.setItem(y,JSON.stringify(e))}catch{}}function E(){const e=f();if(e)try{e.removeItem(y)}catch{}}function q(){const e=f();if(!e)return null;try{const t=e.getItem(y);if(!t)return null;const s=JSON.parse(t);return s&&typeof s.orderId=="string"&&s.orderId!==""?s:null}catch{return null}}async function j(e,t,s){const n={orderId:e.orderId,amount:Number(e.amount),buyer_email:e.buyer_email??"",buyer_phone:e.buyer_phone??"",code:t.slice(0,60),reason:s.trim().slice(0,160)},o=window.G7Core?.api;if(typeof o?.post=="function"){await o.post(C,n);return}await fetch(C,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(n),keepalive:!0})}async function B(){const e=q();if(!e)return;let t;try{t=new URLSearchParams(window.location.search)}catch{return}const s=t.get("orderId")??"";if(s!==""&&s!==e.orderId)return;if(/\/(complete|success)(\/|$|\?)/.test(window.location.pathname)){E();return}const n=t.get("error")??"",o=t.get("message")??"";if(!(n===""&&s==="")){E();try{await j(e,n,o!==""?o:n)}catch(r){console.warn(`[${F}] failed to report payment failure`,r)}}}const H=["js.tosspayments.com"];function Y(e,t){const s=window?.G7Core?.t;if(typeof s!="function")return t;const n=`sirsoft-tosspayments.${e}`,o=s(n);return typeof o=="string"&&o!==n?o:t}function W(e){let t=null;try{t=new URL(e)}catch{t=null}if(t===null||t.protocol!=="https:"||!H.includes(t.hostname.toLowerCase()))throw new Error(Y("payment.error.sdk_url_untrusted","결제 모듈 주소가 올바르지 않아 결제를 진행할 수 없습니다."))}async function J(e){if(W(e),window.TossPayments)return;document.querySelectorAll(`script[src="${CSS.escape(e)}"]`).forEach(s=>s.remove());const t=window?.G7Core?.asset?.loadScript;if(typeof t=="function"){await t(e,{},{label:"tosspayments SDK"});return}await new Promise((s,n)=>{const o=document.createElement("script");o.src=e,o.async=!0,o.onload=()=>s(),o.onerror=()=>n(new Error(`Failed to load script: ${e}`)),document.head.appendChild(o)})}function V(e,t){if(!e.order_sheet_mode||!t)return{method:"CARD",easyPay:null};const s=(e.enabled_methods??[]).find(n=>n.id===t);return s?{method:s.method,easyPay:s.easy_pay_provider}:{method:"CARD",easyPay:null}}function I(e){if(e==="on")return!0;if(e==="off")return!1}function z(e,t){const s={validHours:e.vbank?.valid_hours??24},n=e.vbank?.cash_receipt_type??"";n&&(s.cashReceipt={type:n});const o=I(e.use_escrow);return o!==void 0&&(s.useEscrow=o),s}function v(e,t,s){if(t.use_escrow==="off")return;const n=s.escrow_products??[];n.length>0&&(e.escrowProducts=n)}async function Q(e,t){const s=e.params||{},{pgPaymentData:n}=s;if(!n){console.error("[sirsoft-tosspayments] pgPaymentData is required");return}const o=window.G7Core,r=s.paymentMethod??o?.state?.getLocal?.()?.paymentMethod;try{const a=await o.api.get("/modules/sirsoft-ecommerce/payments/client-config/tosspayments");if(!a.data){console.error("[sirsoft-tosspayments] Failed to fetch client config",a);return}const i=a.data;if(window.TossPayments||await J(i.sdk_url),window.TossPayments||await new Promise(m=>setTimeout(m,100)),!window.TossPayments){console.error("[sirsoft-tosspayments] TossPayments SDK not available");return}const ye=window.TossPayments(i.client_key).payment({customerKey:n.customer_key??window.TossPayments.ANONYMOUS}),T=n.currency??"KRW",{method:d,easyPay:S}=V(i,r);if(T!=="KRW"&&(d!=="CARD"||S!==null)){console.warn("[sirsoft-tosspayments] non-KRW currency supports card only",{currency:T,method:d}),o?.state?.setLocal?.({paymentErrorMessage:o?.t?.("sirsoft-tosspayments.errors.non_krw_method")??"This payment method is available for KRW only.",isSubmittingOrder:!1}),o?.modal?.open?.("tosspayments_payment_error_modal");return}const K=window.location.origin,c={method:d,amount:{currency:T,value:n.amount},orderId:n.order_number,orderName:n.order_name,successUrl:K+i.callback_urls.success,failUrl:K+i.callback_urls.fail,customerEmail:n.customer_email??void 0,customerName:n.customer_name??void 0,customerMobilePhone:n.customer_phone??void 0};if(d==="CARD")c.card={flowMode:"DEFAULT",useCardPoint:!1,useAppCardOnly:!1},S&&(c.card.flowMode="DIRECT",c.card.easyPay=S);else if(d==="VIRTUAL_ACCOUNT")c.virtualAccount=z(i,n),v(c,i,n);else if(d==="TRANSFER"){const m=I(i.use_escrow);m!==void 0&&(c.transfer={useEscrow:m}),v(c,i,n)}G({orderId:n.order_number,amount:n.amount,buyer_email:n.customer_email??"",buyer_phone:n.customer_phone??""}),await ye.requestPayment(c)}catch(a){if(console.error("[sirsoft-tosspayments] requestPayment error",a),a?.code==="USER_CANCEL"){console.info("[sirsoft-tosspayments] Payment cancelled by user");try{await o.api.post(`/modules/sirsoft-ecommerce/orders/${n.order_number}/cancel-payment`,{cancel_code:a.code,cancel_message:a.message})}catch(U){console.warn("[sirsoft-tosspayments] Failed to record cancellation",U)}o?.state?.setLocal?.({isSubmittingOrder:!1}),o?.modal?.open?.("tosspayments_payment_cancel_modal");return}const i=a?.message??"Unknown error";o?.state?.setLocal?.({paymentErrorMessage:i,isSubmittingOrder:!1}),o?.modal?.open?.("tosspayments_payment_error_modal")}}const A={requestPayment:Q},X="sirsoft-tosspayments",L="__tossAdminPaymentMethodBrandInjectorInstalled",N="__tossAdminPaymentMethodBrandSyncListenerAttached",Z=/^\/admin\/ecommerce\/settings\/?$/,R='[data-toss-admin-payment-brand-mark="true"]',ee=200,te=120,ne=[{id:"toss_tosspay",labels:["토스페이 (토스페이먼츠)","토스페이 간편결제 — 토스페이먼츠를 통해 처리","TossPay (TossPayments)","TossPay easy payment — processed via TossPayments"],shortLabels:[],markLines:["T"],markClassName:"bg-blue-500 text-white"},{id:"toss_kakaopay",labels:["카카오페이 (토스페이먼츠)","카카오페이 간편결제 — 토스페이먼츠를 통해 처리","KakaoPay (TossPayments)","KakaoPay easy payment — processed via TossPayments"],shortLabels:[],markLines:["K"],markClassName:"bg-yellow-400 text-gray-950"},{id:"toss_naverpay",labels:["네이버페이 (토스페이먼츠)","네이버페이 간편결제 — 토스페이먼츠를 통해 처리","NaverPay (TossPayments)","NaverPay easy payment — processed via TossPayments"],shortLabels:[],markLines:["N"],markClassName:"bg-green-500 text-white"},{id:"toss_payco",labels:["페이코 (토스페이먼츠)","페이코 간편결제 — 토스페이먼츠를 통해 처리","PAYCO (TossPayments)","PAYCO easy payment — processed via TossPayments"],shortLabels:[],markLines:["P"],markClassName:"bg-red-500 text-white"},{id:"toss_samsungpay",labels:["삼성페이 (토스페이먼츠)","삼성페이 간편결제 — 토스페이먼츠를 통해 처리","Samsung Pay (TossPayments)","Samsung Pay easy payment — processed via TossPayments"],shortLabels:[],markLines:["S"],markClassName:"bg-blue-600 text-white"}];let M=null,u=null,p=!1;function se(){return window}function w(){if(!Z.test(window.location.pathname))return!1;const e=new URLSearchParams(window.location.search).get("tab");return e===null||e===""||e==="order_settings"}function oe(e){return e instanceof HTMLElement?e.className:""}function O(e){return(e??"").replace(/​/g,"").replace(/\s+/g," ").trim()}function re(e){const t=oe(e);return t.includes("excel-card")||t.includes("flex-center")&&t.includes("border")&&t.includes("gap-4")}function k(e){return e.querySelector(".font-medium")}function ae(e){const t=O(k(e)?.textContent),s=O(e.textContent);return ne.find(n=>n.shortLabels.some(o=>t===o)||n.labels.some(o=>s.includes(o)))??null}function ie(e){return Array.from(e.querySelectorAll("div")).filter(re)}function ce(e){return Array.from(e.querySelectorAll("svg, [data-icon], i")).find(s=>!s.closest("[data-drag-handle]")&&!s.closest(R)&&!s.closest('.row-stack, .excel-card-body, button, select, [role="switch"]'))??null}function D(e,t){e.dataset.tossAdminPaymentBrandMark="true",e.dataset.tossAdminPaymentMethod=t.id,e.setAttribute("aria-hidden","true"),e.className=`inline-flex items-center justify-center rounded-lg font-bold ${t.markClassName}`,e.style.width="32px",e.style.height="32px",e.style.flex="0 0 32px",e.style.lineHeight="1",e.style.fontSize=t.markLines.join("").length>2?"9px":"12px";const s=t.markLines.join("");e.textContent!==s&&(e.textContent=t.markLines[0]??"")}function le(e){const t=document.createElement("span");return D(t,e),t}function de(e,t){const s=e.querySelector(R);if(s instanceof HTMLSpanElement)return e.dataset.tossAdminPaymentMethod=t.id,D(s,t),!0;const n=le(t),o=ce(e);if(o&&o.parentElement)return e.dataset.tossAdminPaymentMethod=t.id,o.replaceWith(n),!0;const r=k(e);return r&&r.parentElement?(e.dataset.tossAdminPaymentMethod=t.id,r.parentElement.insertBefore(n,r),!0):!1}function h(e=document){if(typeof window>"u"||typeof document>"u"||!w())return!1;let t=!1;return ie(e).forEach(s=>{const n=ae(s);n&&de(s,n)&&(t=!0)}),t}function ue(){p||(p=!0,window.setTimeout(()=>{p=!1,h()},0))}function g(){u!==null&&(window.clearInterval(u),u=null)}function _(){if(!w())return;g(),h();let e=0;u=window.setInterval(()=>{e+=1,h(),e>=te&&g()},ee);const t=document.body;t[N]||(t[N]=!0,M=new MutationObserver(()=>ue()),M.observe(document.body,{childList:!0,subtree:!0,characterData:!0}))}function P(){if(w()){_();return}g()}function me(){if(typeof window>"u"||typeof document>"u")return;const e=se();if(e[L])return;e[L]=!0,console.info(`[${X}] admin payment method brand sync installed`),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>_()):_();const t=history.pushState.bind(history);history.pushState=(...n)=>{t(...n),window.setTimeout(()=>P(),200)};const s=history.replaceState.bind(history);history.replaceState=(...n)=>{s(...n),window.setTimeout(()=>P(),200)},window.addEventListener("popstate",()=>window.setTimeout(()=>P(),200))}const l="sirsoft-tosspayments",b={info:(...e)=>console.info(`[${l}]`,...e),warn:(...e)=>console.warn(`[${l}]`,...e),error:(...e)=>console.error(`[${l}]`,...e)};function x(){const e=window.G7Core;if(!e)return 0;const t=e.getActionDispatcher;if(typeof t!="function")return 0;const s=t();if(!s||typeof s.registerHandler!="function")return 0;let n=0;for(const[o,r]of Object.entries(A)){const a=`${l}.${o}`;s.registerHandler(a,r,{category:"plugin",source:l}),n++}return n}function $(){me(),B();const e=()=>{const t=x();if(t>0){b.info(`${t} handler(s) registered`);return}let s=0;const n=50,o=setInterval(()=>{s++;const r=x();if(r>0){clearInterval(o),b.info(`${r} handler(s) registered (after ${s} retries)`);return}s>=n&&(clearInterval(o),b.warn("ActionDispatcher not available after timeout"))},100)};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):e()}$(),window.__SirsoftTosspayments={identifier:l,handlers:Object.keys(A),initPlugin:$}})();