legacy.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  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. 'UptimeRobot',
  102. 'BetterStackBot',
  103. 'cron-job.org',
  104. // Security Scanners
  105. 'CensysInspect',
  106. 'Shodan',
  107. 'BitSightBot',
  108. // Other Common Crawlers
  109. '80legs',
  110. 'ia_archiver',
  111. 'Teoma',
  112. 'Linguee Bot',
  113. 'AddThis.com robot',
  114. 'Speedy Spider'
  115. ];
  116. /**
  117. * whitelist of top level domains to consider a secure context,
  118. * regardless of protocol
  119. *
  120. * @private
  121. * @enum {Array}
  122. * @readonly
  123. */
  124. var tld = [
  125. '.onion',
  126. '.i2p'
  127. ];
  128. /**
  129. * whitelist of hostnames to consider a secure context,
  130. * regardless of protocol
  131. *
  132. * @private
  133. * @enum {Array}
  134. * @readonly
  135. */
  136. // whitelists of TLDs & local hostnames
  137. var hostname = [
  138. 'localhost',
  139. '127.0.0.1',
  140. '[::1]'
  141. ];
  142. /**
  143. * check if the context is secure
  144. *
  145. * @private
  146. * @name Check.isSecureContext
  147. * @function
  148. * @return {bool}
  149. */
  150. function isSecureContext()
  151. {
  152. // use .isSecureContext if available
  153. if (window.isSecureContext === true || window.isSecureContext === false) {
  154. return window.isSecureContext;
  155. }
  156. // HTTPS is considered secure
  157. if (window.location.protocol === 'https:') {
  158. return true;
  159. }
  160. // filter out actually secure connections over HTTP
  161. for (var i = 0; i < tld.length; i++) {
  162. if (
  163. window.location.hostname.indexOf(
  164. tld[i],
  165. window.location.hostname.length - tld[i].length
  166. ) !== -1
  167. ) {
  168. return true;
  169. }
  170. }
  171. // whitelist localhost for development
  172. for (var j = 0; j < hostname.length; j++) {
  173. if (window.location.hostname === hostname[j]) {
  174. return true;
  175. }
  176. }
  177. // totally INSECURE http protocol!
  178. return false;
  179. }
  180. /**
  181. * checks whether this is a bot we dislike
  182. *
  183. * @private
  184. * @name Check.isBadBot
  185. * @function
  186. * @return {bool}
  187. */
  188. function isBadBot() {
  189. // check whether a bot user agent part can be found in the current
  190. // user agent
  191. for (var i = 0; i < badBotUA.length; i++) {
  192. if (navigator.userAgent.indexOf(badBotUA[i]) !== -1) {
  193. return true;
  194. }
  195. }
  196. return false;
  197. }
  198. /**
  199. * checks whether this is an unsupported browser, via feature detection
  200. *
  201. * @private
  202. * @name Check.isOldBrowser
  203. * @function
  204. * @return {bool}
  205. */
  206. function isOldBrowser() {
  207. // webcrypto support
  208. if (!(
  209. 'crypto' in window &&
  210. 'getRandomValues' in window.crypto &&
  211. 'subtle' in window.crypto &&
  212. 'encrypt' in window.crypto.subtle &&
  213. 'decrypt' in window.crypto.subtle &&
  214. 'Uint8Array' in window &&
  215. 'Uint32Array' in window
  216. )) {
  217. return true;
  218. }
  219. return false;
  220. }
  221. /**
  222. * shows an error message
  223. *
  224. * @private
  225. * @name Check.showError
  226. * @param {string} message
  227. * @function
  228. */
  229. function showError(message)
  230. {
  231. var element = document.getElementById('errormessage');
  232. if (message.indexOf('<a') === -1) {
  233. element.appendChild(
  234. document.createTextNode(message)
  235. );
  236. } else {
  237. element.innerHTML = message;
  238. }
  239. removeHiddenFromId('errormessage');
  240. }
  241. /**
  242. * removes "hidden" CSS class from element with given ID
  243. *
  244. * @private
  245. * @name Check.removeHiddenFromId
  246. * @param {string} id
  247. * @function
  248. */
  249. function removeHiddenFromId(id)
  250. {
  251. var element = document.getElementById(id);
  252. if (element) {
  253. element.className = element.className.replace(/\bhidden\b/g, '');
  254. }
  255. }
  256. /**
  257. * returns if the check has concluded
  258. *
  259. * @name Check.getInit
  260. * @function
  261. * @return {bool}
  262. */
  263. me.getInit = function()
  264. {
  265. return init;
  266. };
  267. /**
  268. * returns the current status of the check
  269. *
  270. * @name Check.getStatus
  271. * @function
  272. * @return {bool}
  273. */
  274. me.getStatus = function()
  275. {
  276. return status;
  277. };
  278. /**
  279. * init on application start, returns an all-clear signal
  280. *
  281. * @name Check.init
  282. * @function
  283. */
  284. me.init = function()
  285. {
  286. // prevent early init
  287. if (typeof document === 'undefined' || typeof navigator === 'undefined' || typeof window === 'undefined') {
  288. return;
  289. }
  290. // prevent bots from viewing a document and potentially deleting data
  291. // when burn-after-reading is set
  292. if (isBadBot()) {
  293. showError('I love you too, bot…');
  294. init = true;
  295. return;
  296. }
  297. if (isOldBrowser()) {
  298. // some browsers (Chrome based ones) would have webcrypto support if using HTTPS
  299. if (!isSecureContext()) {
  300. removeHiddenFromId('insecurecontextnotice');
  301. }
  302. removeHiddenFromId('oldnotice');
  303. init = true;
  304. return;
  305. }
  306. if (!isSecureContext()) {
  307. removeHiddenFromId('httpnotice');
  308. }
  309. init = true;
  310. // only if everything passed, we set the status to true
  311. status = true;
  312. };
  313. return me;
  314. })();
  315. // main application start, called when DOM is fully loaded
  316. if (document.readyState === 'complete' || (!document.attachEvent && document.readyState === 'interactive')) {
  317. Check.init();
  318. } else {
  319. if (document.addEventListener) {
  320. // first choice is DOMContentLoaded event
  321. document.addEventListener('DOMContentLoaded', Check.init, false);
  322. // backup is window load event
  323. window.addEventListener('load', Check.init, false);
  324. } else {
  325. // must be IE
  326. document.attachEvent('onreadystatechange', Check.init);
  327. window.attachEvent('onload', Check.init);
  328. }
  329. }
  330. this.Legacy = {
  331. Check: Check
  332. };
  333. }).call(this);