legacy.js 8.9 KB

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