All files cors-killer.ts

100% Statements 31/31
95.23% Branches 20/21
100% Functions 7/7
100% Lines 31/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 531x   1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x         1x 18x 18x 18x 18x   2x 2x     16x           18x     18x       18x       2x 2x 2x 2x 2x 18x 18x  
import { randArray } from "./util";
 
const CORS_FREE_HOSTS = [
	(hostname: string) => hostname === "i.imgur.com", // imgur画像はCORS許可あり
	(hostname: string) => hostname.endsWith("github.io"), // GitHub PagesはCORS対応
	(hostname: string) => hostname.endsWith("raw.githubusercontent.com"), // GitHub rawもCORSあり
	(hostname: string) => hostname === "cdn.jsdelivr.net", // JSDelivrはCORS対応CDN
	(hostname: string) => hostname === "upload.wikimedia.org", // Wikipedia画像CDN
	(hostname: string) => hostname === "images.unsplash.com", // Unsplash画像はCORS許可
	(hostname: string) => hostname.endsWith("googleusercontent.com"), // Google系画像CDN(Blogger等)
	(hostname: string) => hostname.endsWith("picsum.photos"), // Lorem Picsum(CORSあり)
	(hostname: string) => hostname === "placehold.co", // プレースホルダー画像(CORS対応)
	(hostname: string) => hostname === "dummyimage.com", // プレースホルダー画像(古典的)
];
 
/**
 * 指定された画像URLを、安全かつCanvasなどで使用できる形に整形する
 */
export const corsKiller = (url: string): string => {
	let u: URL;
	try {
		u = new URL(url);
	} catch (_err) {
		// URLオブジェクト化できなければ無効なURLとみなして空文字を返す
		return "";
	}
 
	// URL オブジェクトのインスタンスを経由して href を取得する方が安全
	const { href, protocol, hostname } = u;
 
	// プロキシを通す必要がないスキーマはそのまま返す
	// - data:  → Base64 埋め込み形式の画像など
	// - blob:  → JavaScript 内で生成した Blob URL
	// - file:  → ローカルファイル(ブラウザ制限あり)
	if (["data:", "blob:", "file:"].includes(protocol)) return href;
 
	// CORS制限のないホワイトリストに含まれていればプロキシ不要
	if (CORS_FREE_HOSTS.some((fn) => fn(hostname))) return href;
 
	// URL文字長があまりにも長い場合は空文字を返す
	// (ブラウザやプロキシが処理できない可能性が高い)
	if (href.length > 2000) return "";
 
	// 上記以外はCORS制限がある可能性が高いため、プロキシをランダムに使用して回避
	// プロキシは全て公開・無認証サービス(障害時に備えて複数用意)
	const proxied = randArray([
		`https://api.allorigins.win/raw?url=${href}`, // オープンな汎用プロキシ(やや不安定)
		`https://corsproxy.io/?${href}`, // AllOrigins互換の軽量プロキシ
		`https://api.codetabs.com/v1/proxy?quest=${href}`, // 古くからあるCORS対応プロキシ
	]);
	return proxied ? proxied : "";
};