legacy.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /**
  2. * PrivateBin
  3. *
  4. * a zero-knowledge paste bin
  5. *
  6. * @see {@link https://github.com/PrivateBin/PrivateBin}
  7. * @copyright 2012 Sébastien SAUVAGE ({@link http://sebsauvage.net})
  8. * @license {@link https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License}
  9. * @name Legacy
  10. * @namespace
  11. */
  12. /**
  13. * IMPORTANT NOTICE FOR DEVELOPERS:
  14. * The logic in this file is intended to run in legacy browsers. Avoid any use of:
  15. * - jQuery (doesn't work in older browsers)
  16. * - ES5 or newer in general
  17. * - const/let, use the traditional var declarations instead
  18. * - async/await or Promises, use traditional callbacks
  19. * - shorthand function notation "() => output", use the full "function() {return output;}" style
  20. * - IE doesn't support:
  21. * - URL(), use the traditional window.location object
  22. * - endsWith(), use indexof()
  23. * - yes, this logic needs to support IE 6, to at least display the error message
  24. */
  25. 'use strict';
  26. (function() {
  27. /**
  28. * compatibility check
  29. *
  30. * @name Check
  31. * @class
  32. */
  33. var Check = (function () {
  34. var me = {};
  35. /**
  36. * Status of the initial check, true means it passed
  37. *
  38. * @private
  39. * @prop {bool}
  40. */
  41. var status = false;
  42. /**
  43. * Initialization check did run
  44. *
  45. * @private
  46. * @prop {bool}
  47. */
  48. var init = false;
  49. /**
  50. * blacklist of UserAgents (parts) known to belong to a bot
  51. *
  52. * @private
  53. * @type {string[]}
  54. * @readonly
  55. */
  56. var badBotUA = [
  57. // Generic bot identifiers
  58. 'bot/',
  59. 'Bot/',
  60. '-bot',
  61. '-Bot',
  62. 'crawler',
  63. 'Crawler',
  64. 'spider',
  65. 'Spider',
  66. 'scraper',
  67. 'Scraper',
  68. // Search Engines
  69. 'Googlebot',
  70. 'Mediapartners-Google',
  71. 'AdsBot-Google',
  72. 'bingbot',
  73. 'msnbot',
  74. 'BingPreview',
  75. 'Yahoo! Slurp',
  76. 'Baiduspider',
  77. 'YandexBot',
  78. 'DuckDuckBot',
  79. // SEO & Analytics
  80. 'AhrefsBot',
  81. 'SemrushBot',
  82. 'MJ12bot',
  83. 'rogerbot',
  84. 'Screaming Frog',
  85. // Social Media
  86. 'facebookexternalhit',
  87. 'Facebot',
  88. 'Twitterbot',
  89. 'LinkedInBot',
  90. 'Pinterestbot',
  91. 'Slackbot',
  92. // AI & LLM
  93. 'GPTBot',
  94. 'ChatGPT-User',
  95. 'OAI-SearchBot',
  96. 'ClaudeBot',
  97. 'anthropic-ai',
  98. 'PerplexityBot',
  99. // Monitoring & Uptime
  100. 'Pingdom',
  101. 'cron-job.org',
  102. // Security Scanners
  103. 'CensysInspect',
  104. 'Shodan',
  105. 'BitSightBot',
  106. // Other Common Crawlers
  107. '80legs',
  108. 'ia_archiver',
  109. 'Teoma',
  110. 'Linguee Bot',
  111. 'AddThis.com robot',
  112. 'Speedy Spider'
  113. ];
  114. /**
  115. * whitelist of top level domains to consider a secure context,
  116. * regardless of protocol
  117. *
  118. * @private
  119. * @enum {Array}
  120. * @readonly
  121. */
  122. var tld = [
  123. '.onion',
  124. '.i2p'
  125. ];
  126. /**
  127. * whitelist of hostnames to consider a secure context,
  128. * regardless of protocol
  129. *
  130. * @private
  131. * @enum {Array}
  132. * @readonly
  133. */
  134. // whitelists of TLDs & local hostnames
  135. var hostname = [
  136. 'localhost',
  137. '127.0.0.1',
  138. '[::1]'
  139. ];
  140. /**
  141. * check if the context is secure
  142. *
  143. * @private
  144. * @name Check.isSecureContext
  145. * @function
  146. * @return {bool}
  147. */
  148. function isSecureContext()
  149. {
  150. // use .isSecureContext if available
  151. if (window.isSecureContext === true || window.isSecureContext === false) {
  152. return window.isSecureContext;
  153. }
  154. // HTTPS is considered secure
  155. if (window.location.protocol === 'https:') {
  156. return true;
  157. }
  158. // filter out actually secure connections over HTTP
  159. for (var i = 0; i < tld.length; i++) {
  160. if (
  161. window.location.hostname.indexOf(
  162. tld[i],
  163. window.location.hostname.length - tld[i].length
  164. ) !== -1
  165. ) {
  166. return true;
  167. }
  168. }
  169. // whitelist localhost for development
  170. for (var j = 0; j < hostname.length; j++) {
  171. if (window.location.hostname === hostname[j]) {
  172. return true;
  173. }
  174. }
  175. // totally INSECURE http protocol!
  176. return false;
  177. }
  178. /**
  179. * checks whether this is a bot we dislike
  180. *
  181. * @private
  182. * @name Check.isBadBot
  183. * @function
  184. * @return {bool}
  185. */
  186. function isBadBot() {
  187. // check whether a bot user agent part can be found in the current
  188. // user agent
  189. for (var i = 0; i < badBotUA.length; i++) {
  190. if (navigator.userAgent.indexOf(badBotUA[i]) !== -1) {
  191. return true;
  192. }
  193. }
  194. return false;
  195. }
  196. /**
  197. * checks whether this is an unsupported browser, via feature detection
  198. *
  199. * @private
  200. * @name Check.isOldBrowser
  201. * @function
  202. * @return {bool}
  203. */
  204. function isOldBrowser() {
  205. // webcrypto support
  206. if (!(
  207. 'crypto' in window &&
  208. 'getRandomValues' in window.crypto &&
  209. 'subtle' in window.crypto &&
  210. 'encrypt' in window.crypto.subtle &&
  211. 'decrypt' in window.crypto.subtle &&
  212. 'Uint8Array' in window &&
  213. 'Uint32Array' in window
  214. )) {
  215. return true;
  216. }
  217. return false;
  218. }
  219. /**
  220. * shows an error message
  221. *
  222. * @private
  223. * @name Check.showError
  224. * @param {string} message
  225. * @function
  226. */
  227. function showError(message)
  228. {
  229. var element = document.getElementById('errormessage');
  230. if (message.indexOf('<a') === -1) {
  231. element.appendChild(
  232. document.createTextNode(message)
  233. );
  234. } else {
  235. element.innerHTML = message;
  236. }
  237. removeHiddenFromId('errormessage');
  238. }
  239. /**
  240. * removes "hidden" CSS class from element with given ID
  241. *
  242. * @private
  243. * @name Check.removeHiddenFromId
  244. * @param {string} id
  245. * @function
  246. */
  247. function removeHiddenFromId(id)
  248. {
  249. var element = document.getElementById(id);
  250. if (element) {
  251. element.className = element.className.replace(/\bhidden\b/g, '');
  252. }
  253. }
  254. /**
  255. * returns if the check has concluded
  256. *
  257. * @name Check.getInit
  258. * @function
  259. * @return {bool}
  260. */
  261. me.getInit = function()
  262. {
  263. return init;
  264. };
  265. /**
  266. * returns the current status of the check
  267. *
  268. * @name Check.getStatus
  269. * @function
  270. * @return {bool}
  271. */
  272. me.getStatus = function()
  273. {
  274. return status;
  275. };
  276. /**
  277. * init on application start, returns an all-clear signal
  278. *
  279. * @name Check.init
  280. * @function
  281. */
  282. me.init = function()
  283. {
  284. // prevent early init
  285. if (typeof document === 'undefined' || typeof navigator === 'undefined' || typeof window === 'undefined') {
  286. return;
  287. }
  288. // prevent bots from viewing a document and potentially deleting data
  289. // when burn-after-reading is set
  290. if (isBadBot()) {
  291. showError('I love you too, bot…');
  292. init = true;
  293. return;
  294. }
  295. if (isOldBrowser()) {
  296. // some browsers (Chrome based ones) would have webcrypto support if using HTTPS
  297. if (!isSecureContext()) {
  298. removeHiddenFromId('insecurecontextnotice');
  299. }
  300. removeHiddenFromId('oldnotice');
  301. init = true;
  302. return;
  303. }
  304. if (!isSecureContext()) {
  305. removeHiddenFromId('httpnotice');
  306. }
  307. init = true;
  308. // only if everything passed, we set the status to true
  309. status = true;
  310. };
  311. return me;
  312. })();
  313. // main application start, called when DOM is fully loaded
  314. if (document.readyState === 'complete' || (!document.attachEvent && document.readyState === 'interactive')) {
  315. Check.init();
  316. } else {
  317. if (document.addEventListener) {
  318. // first choice is DOMContentLoaded event
  319. document.addEventListener('DOMContentLoaded', Check.init, false);
  320. // backup is window load event
  321. window.addEventListener('load', Check.init, false);
  322. } else {
  323. // must be IE
  324. document.attachEvent('onreadystatechange', Check.init);
  325. window.attachEvent('onload', Check.init);
  326. }
  327. }
  328. this.Legacy = {
  329. Check: Check
  330. };
  331. }).call(this);