PluginProbe ʕ •ᴥ•ʔ
VK Blocks / trunk
VK Blocks vtrunk
1.122.0 1.121.0 1.120.0 1.119.2 1.119.1 1.119.0 1.118.7 1.82.0.0 1.82.0.1 1.83.0.0 1.83.0.1 1.84.0.0 1.84.0.1 1.85.0.2 1.85.0.3 1.85.1.0 1.85.1.1 1.86.0.0 1.86.0.1 1.86.1.0 1.87.0.0 1.87.0.1 1.88.0.1 1.88.0.2 1.89.0.0 1.9.2 1.90.0.0 1.90.0.1 1.90.1.1 1.91.0.0 1.91.1.1 1.92.0.0 1.92.0.1 1.92.1.0 1.92.1.1 1.93.0.0 trunk 1.93.0.1 0.1.1 1.93.1.0 0.10.3 1.93.1.1 0.11.0 1.94.0.0 0.12.6 1.94.0.1 0.15.1 1.94.1.0 0.16.2 1.94.2.1 0.17.6 1.94.2.2 0.2.2 1.95.0.3 0.22.4 1.95.0.4 0.26.7 1.96.2.0 0.3.2 1.96.2.1 0.31.0 1.97.0.1 0.35.5 1.97.0.2 0.37.5 1.99.0.0 0.38.8 1.99.0.1 0.39.4 0.4.7 0.41.0 0.42.0 0.44.13 0.45.2 0.46.1 0.47.0 0.48.0 0.49.8 0.5.2 0.50.3 0.51.0 0.52.2 0.53.2 0.54.0 0.55.0 0.56.2 0.56.3 0.57.6 0.58.10 0.59.0 0.6.0 0.60.1 0.61.2 0.7.1 0.8.2 1.0.16 1.10.0 1.100.0.0 1.100.0.1 1.102.0.0 1.102.0.1 1.103.0.0 1.104.0.0 1.104.0.1 1.105.1.0 1.105.1.1 1.106.0.0 1.106.0.1 1.107.0.0 1.107.0.1 1.107.0.2 1.108.0.0 1.108.0.1 1.109.0.0 1.109.0.1 1.11.4 1.110.0.0 1.110.0.1 1.111.0.0 1.111.0.1 1.111.0.2 1.112.0.0 1.112.0.1 1.113.0.0 1.113.0.1 1.114.0.0 1.114.0.1 1.114.2.0 1.114.2.1 1.115.0.0 1.115.0.1 1.115.1.0 1.115.1.1 1.115.2.0 1.115.2.1 1.116.0.0 1.116.0.1 1.116.1.0 1.116.1.1 1.116.2.0 1.117.0.0 1.117.0.1 1.117.1.0 1.118.2 1.118.5 1.12.0 1.13.2 1.14.1 1.15.1 1.16.11 1.17.0 1.18.6 1.19.0 1.19.1 1.2.3 1.20.7 1.21.0 1.22.4 1.23.0 1.24.5 1.25.1 1.26.2 1.27.7.2 1.28.0.1 1.29.2.0 1.3.9 1.30.0.1 1.31.0.1 1.32.0.2 1.33.2.1 1.36.1.5 1.37.0.0 1.39.2.1 1.4.6 1.40.0.1 1.40.1.0 1.40.1.1 1.41.0.0 1.41.0.1 1.41.2.2 1.41.2.3 1.43.0.0 1.43.0.1 1.43.0.2 1.44.0.0 1.44.0.1 1.45.0.0 1.45.0.1 1.46.0.0 1.46.0.1 1.47.0.0 1.47.0.1 1.47.1.0 1.48.0.0 1.48.0.1 1.48.0.2 1.48.1.0 1.48.1.1 1.5.0 1.50.0.0 1.50.0.1 1.51.0.0 1.51.0.1 1.52.0.0 1.52.0.1 1.53.0.0 1.53.0.1 1.54.0.0 1.54.0.1 1.55.0.0 1.55.0.1 1.56.0.0 1.56.0.1 1.57.0.0 1.57.0.2 1.57.0.3 1.57.0.4 1.57.0.5 1.57.1.0 1.57.1.1 1.57.1.2 1.58.0.0 1.58.0.1 1.59.0.0 1.59.0.1 1.6.0 1.60.0.0 1.60.0.1 1.63.0.0 1.63.0.1 1.64.0.0 1.64.0.1 1.64.1.0 1.64.1.2 1.67.0.0 1.67.0.1 1.68.0.0 1.68.0.1 1.69.0.0 1.69.0.1 1.69.1.1 1.69.1.2 1.7.1 1.70.0.0 1.70.0.1 1.71.0.0 1.71.0.1 1.72.0.0 1.72.1.0 1.72.1.1 1.73.0.0 1.73.0.1 1.74.0.0 1.74.0.1 1.75.0.0 1.75.1.0 1.75.1.1 1.76.0.0 1.76.0.1 1.76.1.0 1.76.1.1 1.76.2.0 1.76.2.1 1.77.0.0 1.77.0.1 1.78.0.0 1.78.0.1 1.79.0.0 1.79.0.1 1.79.0.2 1.79.0.3 1.79.1.0 1.79.1.1 1.8.2 1.80.1.0 1.80.1.1 1.80.1.2 1.81.0.1 1.81.0.2
vk-blocks / src / blocks / visual-embed / utils / match-url-pattern.js
vk-blocks / src / blocks / visual-embed / utils Last commit date
get-css-length.js 1 week ago match-url-pattern.js 1 month ago youtube-preview.js 1 week ago
match-url-pattern.js
156 lines
1 /**
2 * 許可 URL パターンと src URL のドメイン境界一致を判定するユーティリティ。
3 *
4 * `https://*.example.com/path/*` のようなパターン表記をサポートし、
5 * - ホスト名はドメイン境界(ドット区切り)で判定して
6 * `https://attacker.com/.example.com/...` のような偽�
7 を弾く
8 * - パス側は `*` を正規表現の `.*` に展開して柔軟一致
9 * を行う。
10 */
11
12 // ワイルドカードを URL として解釈するためのプレースホルダ。
13 // ユーザーが�
14 �力する許可 URL パターンにこの文字列が含まれる現実性はほぼ
15 // ゼロだが、衝突可能性をさらに下げるため目立つ識別子にしている。
16 const WILDCARD_PLACEHOLDER = 'vkwildcardplaceholder';
17
18 // 正規表現特殊文字をエスケープ。
19 const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
20
21 /**
22 * ホスト名のドメイン境界一致判定。
23 *
24 * patternHost が "*.example.com" のとき、 "example.com" 自体と
25 * 任意のサブドメインを許可する(パス側の偽�
26 サブドメインは弾く)。
27 *
28 * @param {string} hostname 検証対象 URL のホスト名
29 * @param {string} patternHost 許可パターンのホスト名(`*.` プレフィックス可)
30 * @return {boolean} 一致すれば true
31 */
32 export const isHostMatch = (hostname, patternHost) => {
33 if (!patternHost.startsWith('*.')) {
34 return hostname === patternHost;
35 }
36 const suffix = patternHost.slice(2);
37 return hostname === suffix || hostname.endsWith('.' + suffix);
38 };
39
40 // 生 URL 文字列から明示ポート(`:1234`)を抽出する。
41 // new URL() はデフォルトポート(http:80 / https:443)を空文字に正規化してしまうため、
42 // 「パターンがデフォルトポートを明示しているか」を保持できない。
43 // → 明示ポートが書かれていたら fail-close で一致を要求するために、文字列段階で抽出する。
44 const extractExplicitPort = (str) => {
45 const m = str.match(/^[a-z][a-z0-9+.-]*:\/\/[^/?#]*?:(\d+)(?=[/?#]|$)/i);
46 return m ? m[1] : '';
47 };
48
49 /**
50 * 許可パターンと src URL のドメイン境界マッチ。
51 *
52 * @param {string} urlStr 検証対象 URL
53 * @param {string} patternStr 許可パターン(例: `https://*.google.com/*`)
54 * @return {boolean} 一致すれば true
55 */
56 export const matchUrlPattern = (urlStr, patternStr) => {
57 // 型ガード。`.replace` を持つが `.match` を持たないオブジェクト等が
58 // 紛れた場合に extractExplicitPort の中で例外が出るのを防ぎ、
59 // fail-close に統一する。
60 if (typeof urlStr !== 'string' || typeof patternStr !== 'string') {
61 return false;
62 }
63
64 let url;
65 let patternUrl;
66 try {
67 url = new URL(urlStr);
68 patternUrl = new URL(patternStr.replace(/\*/g, WILDCARD_PLACEHOLDER));
69 } catch (e) {
70 return false;
71 }
72
73 // プロトコル一致
74 if (url.protocol !== patternUrl.protocol) {
75 return false;
76 }
77
78 // ホスト名はドメイン境界で判定(スラッシュを跨ぐ偽�
79 を防ぐ)
80 const patternHost = patternUrl.hostname.replace(
81 new RegExp(WILDCARD_PLACEHOLDER, 'g'),
82 '*'
83 );
84 if (!isHostMatch(url.hostname, patternHost)) {
85 return false;
86 }
87
88 // 明示ポート一致(パターンが :443 / :80 のようなデフォルトポートを明示している場合も含めて、
89 // 対象 URL 側にも同じ明示ポートが�
90 要。fail-close で別ポートのバイパスを防ぐ)
91 const patternExplicitPort = extractExplicitPort(patternStr);
92 if (patternExplicitPort) {
93 const urlExplicitPort = extractExplicitPort(urlStr);
94 if (urlExplicitPort !== patternExplicitPort) {
95 return false;
96 }
97 }
98
99 // パスはワイルドカード対応の正規表現で判定
100 const pathRegex = patternUrl.pathname
101 .split(WILDCARD_PLACEHOLDER)
102 .map(escapeRegExp)
103 .join('.*');
104 if (!new RegExp('^' + pathRegex + '$').test(url.pathname)) {
105 return false;
106 }
107
108 // クエリが指定されていれば同じワイルドカード規則で判定
109 // (pathname のみ比較すると、フル URL で絞っていた既存パターンが今までより
110 // 広く許可されてしまうため、search も同じルールで比較する)
111 if (patternUrl.search) {
112 const searchRegex = patternUrl.search
113 .slice(1)
114 .split(WILDCARD_PLACEHOLDER)
115 .map(escapeRegExp)
116 .join('.*');
117 if (!new RegExp('^' + searchRegex + '$').test(url.search.slice(1))) {
118 return false;
119 }
120 }
121
122 // フラグメント(#hash)が指定されていれば同じワイルドカード規則で判定
123 // (search と同じく fail-close。パターン側に hash が無ければ不問)
124 if (patternUrl.hash) {
125 const hashRegex = patternUrl.hash
126 .slice(1)
127 .split(WILDCARD_PLACEHOLDER)
128 .map(escapeRegExp)
129 .join('.*');
130 if (!new RegExp('^' + hashRegex + '$').test(url.hash.slice(1))) {
131 return false;
132 }
133 }
134
135 return true;
136 };
137
138 /**
139 * src URL が許可パターン�
140 �列のいずれかにマッチするか判定する。
141 *
142 * @param {string} src 検証対象 URL
143 * @param {string[]} patterns 許可パターン�
144 �列
145 * @return {boolean} いずれかに一致すれば true
146 */
147 export const isAllowedSrc = (src, patterns) => {
148 if (!src) {
149 return false;
150 }
151 if (!Array.isArray(patterns) || patterns.length === 0) {
152 return false;
153 }
154 return patterns.some((pattern) => matchUrlPattern(src, pattern));
155 };
156