legacy.js 8.4 KB

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