legacy.js 9.6 KB

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