PageSourceSearch

https://shop.sqcf.org/js/jq_load_marker.js?1780393768

js sqcf.org collected 2026-09-24 10:12:11 UTC 6,298 bytes, 140 lines download raw bytes

1/**
2 * jQuery .load() 가드 (DEMO-1291)
3 *
4 *   - 시나리오 A (자기-page 자기-load): 브라우저에서 요청을 즉시 cancel + console 사유 출력.
5 *   - 그 외 same-origin .load(): X-Imweb-JQ-Load 마커 헤더만 부착 (서버 관측용).
6 *
7 * ë°°ê²½:
8 *   DEMO-1291 발생 사고 — 코드위젯에 박힌 $("#bag").load("/shop_cart") 가 사이트 전체
9 *   무한로딩을 유발. .load() 가 응답 HTML 의 inline <script> 를 콜백 실행 전에 즉시
10 *   평가해버려 작성자가 콜백에서 .remove() 로 script 를 지워도 이미 실행됨.
11 *
12 * 시나리오 A (selector 없는 자기-page 자기-load) — 클라이언트 차단:
13 *   현재 페이지 P 의 위젯이 .load(P) 로 자기 자신을 다시 끌어오면, 응답 안의 같은 위젯이
14 *   또 .load(P) 를 호출 → 무한 재귀 + 스크립트 중복 실행. 가장 직접적인 사고 패턴.
15 *   단, .load("P #selector") 처럼 selector 접미사가 있으면 jQuery 가 응답의 <script> 를
16 *   제거 후 주입하므로 재귀 위험이 없다 → 차단하지 않고 마커만 부착(정상 섹션 갱신 패턴).
17 *   resolveLoadPath(url) 이 현재 페이지 path 와 같고 selector 가 없으면:
18 *     1. 원본 .load 를 호출하지 않는다 → 응답을 DOM 에 삽입하지 않으므로 재귀/스크립트
19 *        재실행을 원천 차단.
20 *     2. console.error 로 차단 사유 출력.
21 *     3. Network 탭 가시성을 위해 요청을 띄우자마자 abort → (canceled) 로 표시.
22 *        (이 throwaway 요청의 응답은 절대 사용/삽입하지 않는다 — 진단 흔적 용도.)
23 *
24 * 마커 (시나리오 A 가 아닌 same-origin .load()):
25 *   .load(url, ...) 의 url 첫 공백 앞 토큰을 URL 로 해석하여 same-origin 이면 pathname 을
26 *   X-Imweb-JQ-Load 헤더로 부착. 서버측 self_recursion_guard.sub 관측용.
27 *   cross-origin / URL parse 실패 / 비문자열 url 은 미부착 (사고 패턴 범위 밖).
28 *
29 * 로드 위치:
30 *   html/common/class/site_page.cls 의 header_js 배열에 jQuery 직후 등록.
31 *   head 영역에서 jQuery 직후 동기 로드 → 사이트 코드위젯 inline script 가 .load() 호출하기 전 패치 완료.
32 *
33 * 한계:
34 *   - jQuery 가 아닌 fetch/XMLHttpRequest 직접 사용한 코드위젯은 미적용.
35 *   - 코드위젯 작성자가 $.fn.load 를 다시 덮어쓰면 우회 가능 (드뭄).
36 *   - jQuery 로드 전에 실행되는 코드 (jQuery 보다 먼저 로드된 외부 SDK 등) 는 못 잡음.
37 *   - URL 생성자 미지원 환경 (구형 브라우저) 은 미적용 (사이트 동작 영향 없음).
38 *   - 시나리오 B (다른 페이지 Q 에서 .load(P)) 는 자기-page 매칭이 안 됨 — 본 가드 범위 밖.
39 */
40(function ($) {
41	if (!$ || !$.fn || typeof $.fn.load !== 'function' || typeof $.ajaxPrefilter !== 'function') {
42		return;
43	}
44
45	var origLoad = $.fn.load;
46	var IN_LOAD = '';
47
48	// path 정규화 — 앞뒤 슬래시 제거 후 단일 선행 슬래시. 서버 self_recursion_guard.sub 의
49	// `'/' . trim($path, '/')` 와 동일 규칙으로 맞춰 자기-page 판정을 일치시킨다.
50	function normPath(p) {
51		return '/' + String(p == null ? '' : p).replace(/^\/+|\/+$/g, '');
52	}
53
54	function resolveLoadPath(url) {
55		if (typeof url !== 'string' || url === '') {
56			return '';
57		}
58		// `.load("/path #selector")` 형식 지원 — 첫 공백 앞 토큰만 URL
59		var firstToken = url.split(/\s+/)[0];
60		if (!firstToken) {
61			return '';
62		}
63		try {
64			var absolute = new URL(firstToken, location.href);
65			// cross-origin .load 는 사고 패턴 범위 밖
66			if (absolute.host !== location.host) {
67				return '';
68			}
69			return absolute.pathname || '';
70		} catch (e) {
71			return '';
72		}
73	}
74
75	// selector 접미사 유무 — `.load("url selector")` 형식. selector 가 있으면 jQuery 가
76	// 응답의 <script> 를 제거 후 주입하므로(실행 안 됨) 재귀 위험이 없다 → 차단 제외 판정에 사용.
77	function hasSelectorSuffix(url) {
78		if (typeof url !== 'string') {
79			return false;
80		}
81		var parts = url.trim().split(/\s+/);
82		return parts.length > 1 && parts.slice(1).join('') !== '';
83	}
84
85	// 시나리오 A 차단 시 Network 에 (canceled) 흔적만 남기는 throwaway 요청.
86	// 응답은 절대 사용하지 않는다 (open → send → 즉시 abort).
87	function emitCanceledTrace(rawUrl) {
88		try {
89			var firstToken = String(rawUrl).split(/\s+/)[0];
90			var xhr = new XMLHttpRequest();
91			xhr.open('GET', firstToken, true);
92			try {
93				xhr.setRequestHeader('X-Imweb-JQ-Load-Blocked', '1');
94			} catch (e) {}
95			xhr.send();
96			xhr.abort();
97		} catch (e) {}
98	}
99
100	$.fn.load = function (url) {
101		// $(window).load(handler) event (jQuery 1.x 호환) — 통과
102		if (typeof url === 'function') {
103			return origLoad.apply(this, arguments);
104		}
105
106		var path = resolveLoadPath(url);
107
108		// 시나리오 A: selector 없는 자기-page 자기-load → 클라이언트 차단.
109		// selector 가 있으면 jQuery 가 script 를 제거하므로 재귀 위험 없음 → 통과(마커만).
110		if (path && !hasSelectorSuffix(url) && normPath(path) === normPath(location.pathname)) {
111			if (window.console && typeof console.error === 'function') {
112				console.error(
113					'[DEMO-1291] 자기 페이지 재귀 .load() 차단: ' + normPath(path) +
114					' — 자기 자신을 ajax 로 다시 불러오면 응답 안의 <script> 가 재실행되어 ' +
115					'무한 재귀와 중복 초기화를 유발합니다. (요청을 취소했습니다)'
116				);
117			}
118			emitCanceledTrace(url);
119			// 원본 .load 미호출 = 응답 DOM 삽입/스크립트 재실행 원천 차단. 체이닝 위해 this 반환.
120			return this;
121		}
122
123		// 그 외 same-origin .load() — 마커만 부착 (서버 관측용)
124		// 마커도 normPath 로 정규화 → 서버 self_recursion_guard.sub 의 trim 규칙과 동일 형태로 전파.
125		// path 가 '' (cross-origin / 파싱 실패) 이면 마커 미부착 유지.
126		var prev = IN_LOAD;
127		IN_LOAD = path ? normPath(path) : '';
128		try {
129			return origLoad.apply(this, arguments);
130		} finally {
131			IN_LOAD = prev;
132		}
133	};
134
135	$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
136		if (IN_LOAD) {
137			jqXHR.setRequestHeader('X-Imweb-JQ-Load', IN_LOAD);
138		}
139	});
140})(window.jQuery || window.$);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.