🏠 Home 

Greasy Fork is available in English.

PunyCode Protection

Warns on clicking links and arriving into sites which uses PunyCode.


安装此脚本?
  1. // ==UserScript==
  2. // @name PunyCode Protection
  3. // @namespace PunyCode Protection
  4. // @version 1.0.4
  5. // @description Warns on clicking links and arriving into sites which uses PunyCode.
  6. // @author jcunews
  7. // @match *://*/*
  8. // @grant none
  9. // @run-at document-start
  10. // ==/UserScript==
  11. //URL to redirect when user rejected the prompt upon arriving to a possibly fake site.
  12. var redirectURL = "about:blank";
  13. //---------------------------punycode.js start
  14. //source: https://github.com/bestiejs/punycode.js/blob/master/punycode.js
  15. //modified to be compatible with ES5.1 and client-side scripting, and to remove comments.
  16. var punycode = (function() {
  17. var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
  18. var base = 36;
  19. var tMin = 1;
  20. var tMax = 26;
  21. var skew = 38;
  22. var damp = 700;
  23. var initialBias = 72;
  24. var initialN = 128; // 0x80
  25. var delimiter = '-'; // '\x2D'
  26. var regexPunycode = /^xn--/;
  27. var regexNonASCII = /[^\0-\x7E]/; // non-ASCII chars
  28. var regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g; // RFC 3490 separators
  29. var errors = {
  30. 'overflow': 'Overflow: input needs wider integers to process',
  31. 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
  32. 'invalid-input': 'Invalid input'
  33. };
  34. var baseMinusTMin = base - tMin;
  35. var floor = Math.floor;
  36. var stringFromCharCode = String.fromCharCode;
  37. function error(type) {
  38. throw new RangeError(errors[type]);
  39. }
  40. function map(array, fn) {
  41. var r###lt = [];
  42. var length = array.length;
  43. while (length--) {
  44. r###lt[length] = fn(array[length]);
  45. }
  46. return r###lt;
  47. }
  48. function mapDomain(string, fn) {
  49. var parts = string.split('@');
  50. var r###lt = '';
  51. if (parts.length > 1) {
  52. r###lt = parts[0] + '@';
  53. string = parts[1];
  54. }
  55. string = string.replace(regexSeparators, '\x2E');
  56. var labels = string.split('.');
  57. var encoded = map(labels, fn).join('.');
  58. return r###lt + encoded;
  59. }
  60. function ucs2decode(string) {
  61. var output = [];
  62. var counter = 0;
  63. var length = string.length;
  64. while (counter < length) {
  65. var value = string.charCodeAt(counter++);
  66. if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
  67. var extra = string.charCodeAt(counter++);
  68. if ((extra & 0xFC00) == 0xDC00) { // Low surrogate.
  69. output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
  70. } else {
  71. output.push(value);
  72. counter--;
  73. }
  74. } else {
  75. output.push(value);
  76. }
  77. }
  78. return output;
  79. }
  80. //var ucs2encode = array => String.fromCodePoint(...array);
  81. var ucs2encode = function(array) {
  82. return String.fromCodePoint.apply(String, array);
  83. };
  84. var basicToDigit = function(codePoint) {
  85. if (codePoint - 0x30 < 0x0A) {
  86. return codePoint - 0x16;
  87. }
  88. if (codePoint - 0x41 < 0x1A) {
  89. return codePoint - 0x41;
  90. }
  91. if (codePoint - 0x61 < 0x1A) {
  92. return codePoint - 0x61;
  93. }
  94. return base;
  95. };
  96. var digitToBasic = function(digit, flag) {
  97. return digit + 22 + 75 * (digit < 26) - ((flag !== 0) << 5);
  98. };
  99. var adapt = function(delta, numPoints, firstTime) {
  100. var k = 0;
  101. delta = firstTime ? floor(delta / damp) : delta >> 1;
  102. delta += floor(delta / numPoints);
  103. for (; delta > baseMinusTMin * tMax >> 1; k += base) {
  104. delta = floor(delta / baseMinusTMin);
  105. }
  106. return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
  107. };
  108. var decode = function(input) {
  109. var output = [];
  110. var inputLength = input.length;
  111. var i = 0;
  112. var n = initialN;
  113. var bias = initialBias;
  114. var basic = input.lastIndexOf(delimiter);
  115. if (basic < 0) {
  116. basic = 0;
  117. }
  118. for (var j = 0; j < basic; ++j) {
  119. if (input.charCodeAt(j) >= 0x80) {
  120. error('not-basic');
  121. }
  122. output.push(input.charCodeAt(j));
  123. }
  124. for (var index = basic > 0 ? basic + 1 : 0; index < inputLength;) {
  125. var oldi = i;
  126. for (var w = 1, k = base; ; k += base) {
  127. if (index >= inputLength) {
  128. error('invalid-input');
  129. }
  130. var digit = basicToDigit(input.charCodeAt(index++));
  131. if (digit >= base || digit > floor((maxInt - i) / w)) {
  132. error('overflow');
  133. }
  134. i += digit * w;
  135. var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
  136. if (digit < t) {
  137. break;
  138. }
  139. var baseMinusT = base - t;
  140. if (w > floor(maxInt / baseMinusT)) {
  141. error('overflow');
  142. }
  143. w *= baseMinusT;
  144. }
  145. var out = output.length + 1;
  146. bias = adapt(i - oldi, out, oldi === 0);
  147. if (floor(i / out) > maxInt - n) {
  148. error('overflow');
  149. }
  150. n += floor(i / out);
  151. i %= out;
  152. output.splice(i++, 0, n);
  153. }
  154. // return String.fromCodePoint(...output);
  155. return String.fromCodePoint.apply(String, output);
  156. };
  157. var encode = function(input) {
  158. var output = [];
  159. input = ucs2decode(input);
  160. var inputLength = input.length;
  161. var n = initialN;
  162. var delta = 0;
  163. var bias = initialBias;
  164. input.forEach(function(currentValue) {
  165. if (currentValue < 0x80) {
  166. output.push(stringFromCharCode(currentValue));
  167. }
  168. });
  169. var basicLength = output.length;
  170. var handled###ount = basicLength;
  171. if (basicLength) {
  172. output.push(delimiter);
  173. }
  174. while (handled###ount < inputLength) {
  175. var m = maxInt;
  176. input.forEach(function(currentValue) {
  177. if (currentValue >= n && currentValue < m) {
  178. m = currentValue;
  179. }
  180. });
  181. var handled###ountPlusOne = handled###ount + 1;
  182. if (m - n > floor((maxInt - delta) / handled###ountPlusOne)) {
  183. error('overflow');
  184. }
  185. delta += (m - n) * handled###ountPlusOne;
  186. n = m;
  187. input.forEach(function(currentValue) {
  188. if (currentValue < n && ++delta > maxInt) {
  189. error('overflow');
  190. }
  191. if (currentValue == n) {
  192. var q = delta;
  193. for (var k = base; ; k += base) {
  194. var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
  195. if (q < t) {
  196. break;
  197. }
  198. var qMinusT = q - t;
  199. var baseMinusT = base - t;
  200. output.push(
  201. stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
  202. );
  203. q = floor(qMinusT / baseMinusT);
  204. }
  205. output.push(stringFromCharCode(digitToBasic(q, 0)));
  206. bias = adapt(delta, handled###ountPlusOne, handled###ount == basicLength);
  207. delta = 0;
  208. ++handled###ount;
  209. }
  210. });
  211. ++delta;
  212. ++n;
  213. }
  214. return output.join('');
  215. };
  216. var toUnicode = function(input) {
  217. return mapDomain(input, function(string) {
  218. return regexPunycode.test(string) ? decode(string.slice(4).toLowerCase()) : string;
  219. });
  220. };
  221. var toASCII = function(input) {
  222. return mapDomain(input, function(string) {
  223. return regexNonASCII.test(string) ? 'xn--' + encode(string) : string;
  224. });
  225. };
  226. return {
  227. 'version': '2.1.0',
  228. 'ucs2': {
  229. 'decode': ucs2decode,
  230. 'encode': ucs2encode
  231. },
  232. 'decode': decode,
  233. 'encode': encode,
  234. 'toASCII': toASCII,
  235. 'toUnicode': toUnicode
  236. };
  237. })();
  238. //---------------------------punycode.js end
  239. var hostname;
  240. window.pc=punycode;
  241. function latinLike(s) {
  242. return punycode.ucs2.decode(s).some(function(code){
  243. return (
  244. //C1 Controls and Latin-1 Supplement; up to Cyrillic Supplement
  245. (code >= 0x0080) && (code <= 0x052f) ||
  246. //Cherokee
  247. (code >= 0x13a0) && (code <= 0x13ff) ||
  248. //Cyrillic Extended-C
  249. (code >= 0x1c80) && (code <= 0x1c8f) ||
  250. //Latin-2 supplement, Greek Extended
  251. (code >= 0x1d00) && (code <= 0x1eff) ||
  252. //Superscripts and Subscripts
  253. (code >= 0x2070) && (code <= 0x209f) ||
  254. //Letterlike Symbols
  255. (code >= 0x2100) && (code <= 0x214f) ||
  256. //Latin Extended-C, Coptic, Tifinagh
  257. (code >= 0x2c60) && (code <= 0x2d7f) ||
  258. //Cyrillic Extended-A
  259. (code >= 0x2de0) && (code <= 0x2dff) ||
  260. //Lisu
  261. (code >= 0xa4d0) && (code <= 0xa4ff) ||
  262. //Cyrillic Extended-B
  263. (code >= 0xa640) && (code <= 0xa69f) ||
  264. //Latin Extended-D
  265. (code >= 0xa720) && (code <= 0xa7ff) ||
  266. //Latin Extended-E, Cherokee Supplement
  267. (code >= 0xab30) && (code <= 0xabbf) ||
  268. //Halfwidth and Fullwidth Forms (letters only)
  269. (code >= 0xff00) && (code <= 0xff5a)
  270. );
  271. })
  272. }
  273. function checkHostName(ahostname, hn, suspicious) {
  274. suspicious = latinLike(ahostname);
  275. if (suspicious) {
  276. hn = punycode.toUnicode(ahostname);
  277. if (hn === ahostname) {
  278. hn = punycode.toASCII(ahostname);
  279. suspicious = hn !== ahostname;
  280. } else {
  281. hn = ahostname;
  282. suspicious = true;
  283. }
  284. hostname = hn;
  285. } else if (suspicious = ((hn = punycode.toUnicode(ahostname)) !== ahostname) && latinLike(hn)) hostname = ahostname;
  286. return suspicious;
  287. }
  288. //warn upon arriving to a possibly fake site
  289. if (checkHostName(location.hostname) &&
  290. !confirm("Warning! This website real domain name is:\n\n" + hostname + "\n\nDo you want to proceed?")) {
  291. location.href = redirectURL;
  292. }
  293. //warn on clicking a link pointing to a possibly fake site
  294. addEventListener("click", function(ev) {
  295. if (!ev.button && ev.target && (ev.target.tagName === "A") &&
  296. ev.target.hostname && (ev.target.hostname !== location.hostname)) {
  297. if (checkHostName(ev.target.hostname) &&
  298. !confirm("Warning! About to go to a web page whose real domain name is:\n\n" + hostname + "\n\nDo you want to proceed?")) {
  299. ev.preventDefault();
  300. if (ev.stopPropagation) ev.stopPropagation();
  301. if (ev.stopImmediatePropagation) ev.stopImmediatePropagation();
  302. return false;
  303. }
  304. }
  305. return true;
  306. }, true);
  307. //hook window.open()
  308. var _open = window.open;
  309. window._open = window.open;
  310. window.open = function(url) {
  311. if (checkHostName(url) &&
  312. !confirm("Warning! A script is about to open a web page whose real domain name is:\n\n" + hostname + "\n\nDo you want to proceed?")) {
  313. return null;
  314. }
  315. return _open.apply(window, arguments);
  316. };