Files
Gnuboard7/plugins/_bundled/sirsoft-pay_kginicis/resources/js/index.ts
T
HeuJung 21fc114f37 fix(core,extensions): 보안 결함 3건과 이중저장소·레이아웃 중복키 결함군 폐쇄
KISA 제보 3건(KVE-2026-2010/2011/2018)과 그 동일 계열 형제 결함을 전수 조치하고,
그 과정에서 드러난 두 결함군을 함께 닫는다.

- 검증 시점과 연결 시점이 host 를 다르게 읽던 SSRF 통로를 정규화 SSoT 한 곳으로 모았다
- 세션을 여는 지점(2FA 완료·토큰 재발급)이 잠금 검사를 거치지 않아 계정 잠금이 우회됐다
- 인증도 서명도 없는 브라우저 리턴 콜백이 주문 상태를 바꾸던 통로를 4 PG 전부에서 닫고,
 소유권을 대조하는 close-report 를 토스에도 신설했다. 그 결과 정리 주체를 잃는
 결제창 미완료 주문은 만료 자동취소가 거둔다
- 저장소 A(_local)에만 쓰는 경로가 B 의 값을 조용히 덮던 회귀를 정본 writer 로 닫았다
 (engine-v1.63.5). 한 방향만 보던 정적 검사에 반대 방향 축과 양방향 계약 테스트를 더했다
- 레이아웃 JSON 의 같은 객체 중복 키가 앞선 선언을 오류 없이 삼키던 결함군을 닫았다
2026-09-02 17:36:11 +09:00

113 lines
3.9 KiB
TypeScript

import { handlerMap } from './handlers';
import { installOrderResponseInterceptor } from './orderResponseInterceptor';
import { installMypageOrderShowInjector } from './mypageOrderShowInjector';
import { installAdminOrderPaymentDisplayInjector } from './adminOrderPaymentDisplayInjector';
import { installOrderCompleteReceiptInjector } from './orderCompleteReceiptInjector';
import { installVbankInfoInjector } from './vbankInfoInjector';
import {
installPaymentCloseMessageListener,
reportStandardPaymentFailureOnReturn,
} from './paymentCloseMessageListener';
import { installCheckoutJpyPaymentMethodRestrictor } from './checkoutJpyPaymentMethodRestrictor';
import { installAdminPaymentMethodBrandInjector } from './adminPaymentMethodBrandInjector';
const PLUGIN_IDENTIFIER = 'sirsoft-pay_kginicis';
const logger = {
info: (...args: unknown[]) => console.info(`[${PLUGIN_IDENTIFIER}]`, ...args),
warn: (...args: unknown[]) => console.warn(`[${PLUGIN_IDENTIFIER}]`, ...args),
error: (...args: unknown[]) => console.error(`[${PLUGIN_IDENTIFIER}]`, ...args),
};
function registerHandlers(): number {
const g7Core = (window as Record<string, unknown>).G7Core as Record<string, unknown> | undefined;
if (!g7Core) {
return 0;
}
const getDispatcher = g7Core.getActionDispatcher as (() => Record<string, unknown>) | undefined;
if (typeof getDispatcher !== 'function') {
return 0;
}
const dispatcher = getDispatcher() as Record<string, unknown> | undefined;
if (!dispatcher || typeof dispatcher.registerHandler !== 'function') {
return 0;
}
let count = 0;
for (const [name, handler] of Object.entries(handlerMap)) {
const fullName = `${PLUGIN_IDENTIFIER}.${name}`;
dispatcher.registerHandler(fullName, handler, {
category: 'plugin',
source: PLUGIN_IDENTIFIER,
});
count++;
}
return count;
}
function initPlugin(): void {
const doInit = () => {
const count = registerHandlers();
if (count > 0) {
logger.info(`${count} handler(s) registered`);
return;
}
let retries = 0;
const maxRetries = 50;
const interval = setInterval(() => {
retries++;
const result = registerHandlers();
if (result > 0) {
clearInterval(interval);
logger.info(`${result} handler(s) registered (after ${retries} retries)`);
return;
}
if (retries >= maxRetries) {
clearInterval(interval);
logger.warn('ActionDispatcher not available after timeout');
}
}, 100);
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', doInit);
} else {
doInit();
}
}
// fetch 인터셉터: 체크아웃 페이지에서 kginicis 주문 응답을 가로채 결제창 호출
// (체크아웃 템플릿이 코어 영역이라 수정 불가하므로 클라이언트 사이드 우회)
installOrderResponseInterceptor();
installCheckoutJpyPaymentMethodRestrictor();
installMypageOrderShowInjector();
installAdminOrderPaymentDisplayInjector();
installAdminPaymentMethodBrandInjector();
installOrderCompleteReceiptInjector();
installVbankInfoInjector();
installPaymentCloseMessageListener();
// 결제 실패로 돌아온 화면이면 서버에 보고한다. 브라우저 리턴 콜백(PC·모바일·해외결제)은
// PG 서명도 IP 증명도 없어 주문 상태를 바꾸지 않으므로, 소유권을 대조하는 close-report 가
// 정당한 결제 실패를 기록하는 유일한 경로다. 저장해 둔 정보가 없으면 아무 일도 하지 않는다.
void reportStandardPaymentFailureOnReturn();
initPlugin();
(window as Record<string, unknown>).__SirsoftKginicis = {
identifier: PLUGIN_IDENTIFIER,
handlers: Object.keys(handlerMap),
initPlugin,
};