PunyCode Protection

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

当前为 2017-04-22 提交的版本,查看 最新版本

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