Source Code
The entire project, readable in your browser. No hidden JavaScript, no sneaky beacons. If we claimed we don't track you, this is where you check.
|
Pages/
Feeds/
Backend/
Includes/
Stylesheet/
JavaScript/
data/counter.txt and data/guestbook.txt hold visitor-supplied content
(a number and signed messages), so they are not shown here.
|
1 /* ============================================================================ 2 terminal.js -- Interactive fake shell 3 ---------------------------------------------------------------------------- 4 A retro command line with real, computed output. Commands: 5 help scan browser network hardware privacy fingerprint 6 clear ascii neofetch fortune about source whoami export ver echo 7 8 Attaches to a container that already contains: 9 <div id="termOutput"></div> 10 <div class="term-inputline"> 11 <span class="ps1">guest@whatcanilearn:~$</span> 12 <input class="term-input" id="termInput"> 13 </div> 14 15 Reads the report from window.EYES.report if a scan has run; otherwise builds 16 one on demand. Stores nothing. 17 ========================================================================== */ 18 (function () { 19 "use strict"; 20 21 var EYES = window.EYES = window.EYES || {}; 22 23 function esc(s) { 24 return String(s == null ? '' : s) 25 .replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); 26 } 27 28 var PS1 = 'guest@whatcanilearn:~$'; 29 30 function Terminal(outEl, inEl) { 31 this.out = outEl; 32 this.in = inEl; 33 this.history = []; 34 this.hpos = -1; 35 var self = this; 36 37 inEl.addEventListener('keydown', function (ev) { 38 if (ev.key === 'Enter') { 39 var cmd = inEl.value; 40 inEl.value = ''; 41 self.run(cmd); 42 } else if (ev.key === 'ArrowUp') { 43 ev.preventDefault(); 44 if (self.history.length) { 45 if (self.hpos < 0) self.hpos = self.history.length; 46 self.hpos = Math.max(0, self.hpos - 1); 47 inEl.value = self.history[self.hpos] || ''; 48 } 49 } else if (ev.key === 'ArrowDown') { 50 ev.preventDefault(); 51 if (self.history.length && self.hpos >= 0) { 52 self.hpos = Math.min(self.history.length, self.hpos + 1); 53 inEl.value = self.history[self.hpos] || ''; 54 } 55 } else if (ev.key === 'l' && ev.ctrlKey) { 56 ev.preventDefault(); 57 self.clear(); 58 } 59 }); 60 61 // Focus the input when the terminal area is clicked. 62 if (outEl.parentNode) { 63 outEl.parentNode.addEventListener('click', function () { inEl.focus(); }); 64 } 65 } 66 67 Terminal.prototype.print = function (html) { 68 var div = document.createElement('div'); 69 div.innerHTML = html; 70 this.out.appendChild(div); 71 this.out.scrollTop = this.out.scrollHeight; 72 }; 73 Terminal.prototype.echoCmd = function (cmd) { 74 this.print('<span class="prompt">' + esc(PS1) + '</span> ' + esc(cmd)); 75 }; 76 Terminal.prototype.clear = function () { this.out.innerHTML = ''; }; 77 78 Terminal.prototype.run = function (raw) { 79 var cmd = (raw || '').trim(); 80 this.echoCmd(cmd); 81 if (cmd === '') return; 82 this.history.push(cmd); 83 this.hpos = -1; 84 85 var parts = cmd.split(/\s+/); 86 var name = parts[0].toLowerCase(); 87 var args = parts.slice(1); 88 var fn = this.commands[name]; 89 if (fn) { 90 try { fn.call(this, args); } 91 catch (e) { this.print('<span class="err">error: ' + esc(e.message) + '</span>'); } 92 } else { 93 this.print('<span class="err">' + esc(name) + ': command not found</span>. Type <span class="cyan">help</span>.'); 94 } 95 }; 96 97 /* ---- helpers to get data ---------------------------------------------- */ 98 function withReport(cb, term) { 99 if (EYES.report) { cb(EYES.report); return; } 100 term.print('<span class="dim">no scan in memory -- collecting now...</span>'); 101 if (EYES.buildReport) { 102 EYES.buildReport().then(cb).catch(function () { 103 term.print('<span class="err">collection failed.</span>'); 104 }); 105 } else { 106 term.print('<span class="err">scan engine not loaded on this page.</span>'); 107 } 108 } 109 110 /* ---- command implementations ------------------------------------------ */ 111 Terminal.prototype.commands = { 112 help: function () { 113 this.print( 114 '<span class="cyan">Available commands:</span>\n' + 115 ' help this message\n' + 116 ' scan run a full scan and summarize\n' + 117 ' browser browser / OS / engine details\n' + 118 ' network IP, geo, ISP, VPN status\n' + 119 ' hardware CPU, memory, GPU, screen\n' + 120 ' privacy compute your privacy score\n' + 121 ' fingerprint show your local fingerprint ID\n' + 122 ' whoami one-line identity summary\n' + 123 ' neofetch system info, ASCII-art style\n' + 124 ' ascii show the site logo\n' + 125 ' fortune a privacy-themed fortune\n' + 126 ' export download your report (json|html|md|txt)\n' + 127 ' ver version info\n' + 128 ' echo [text] print text\n' + 129 ' about what is this site\n' + 130 ' source where to read the code\n' + 131 ' clear clear the screen' 132 ); 133 }, 134 135 scan: function () { 136 var t = this; 137 withReport(function (r) { 138 var b = r.browser || {}, s = r.server || {}, fp = r.fingerprint || {}; 139 var geo = s.geo || {}; 140 t.print( 141 '<span class="ok">== SCAN COMPLETE ==</span>\n' + 142 'IP ............ ' + esc(s.ip || '?') + '\n' + 143 'Location ...... ' + esc([geo.city, geo.country].filter(Boolean).join(', ') || 'unknown') + '\n' + 144 'ISP ........... ' + esc(geo.isp || 'unknown') + '\n' + 145 'Browser ....... ' + esc((b.ua ? b.ua.browser + ' ' + b.ua.version : '?')) + '\n' + 146 'OS ............ ' + esc((b.ua ? b.ua.os + ' ' + b.ua.osVersion : '?')) + '\n' + 147 'CPU cores ..... ' + esc((b.platform ? b.platform.cores : '?')) + '\n' + 148 'GPU ........... ' + esc((b.webgl ? b.webgl.renderer : '?')) + '\n' + 149 'Fingerprint ... ' + esc(fp.id ? fp.id.slice(0, 32) + '...' : '?') + '\n' + 150 'Tracking risk . ' + esc(fp.trackingRisk || '?') + '\n' + 151 '<span class="dim">(nothing stored)</span>' 152 ); 153 }, t); 154 }, 155 156 browser: function () { 157 var t = this; 158 withReport(function (r) { 159 var b = r.browser || {}; 160 if (!b.ua) { t.print('<span class="err">no browser data</span>'); return; } 161 t.print( 162 'Browser ....... ' + esc(b.ua.browser + ' ' + b.ua.version) + '\n' + 163 'Engine ........ ' + esc(b.ua.engine) + '\n' + 164 'OS ............ ' + esc(b.ua.os + ' ' + b.ua.osVersion) + '\n' + 165 'Platform ...... ' + esc(b.platform.platform) + '\n' + 166 'Arch .......... ' + esc(b.platform.architecture) + '\n' + 167 'Languages ..... ' + esc((b.platform.languages || []).join(', ')) + '\n' + 168 'Cookies ....... ' + esc(b.apis.cookies ? 'enabled' : 'disabled') + '\n' + 169 'DNT ........... ' + esc(b.platform.doNotTrack || 'not set') + '\n' + 170 'UA ............ ' + esc(b.ua.raw) 171 ); 172 }, t); 173 }, 174 175 network: function () { 176 var t = this; 177 withReport(function (r) { 178 var s = r.server || {}, geo = (s.geo || {}), net = s.network || {}; 179 t.print( 180 'IP ............ ' + esc(s.ip || '?') + '\n' + 181 'Country ....... ' + esc(geo.country || 'unknown') + '\n' + 182 'City .......... ' + esc(geo.city || 'unknown') + '\n' + 183 'Timezone ...... ' + esc(geo.timezone || 'unknown') + '\n' + 184 'Coords ........ ' + esc(geo.lat != null ? geo.lat + ', ' + geo.lon : 'unknown') + '\n' + 185 'ISP ........... ' + esc(geo.isp || 'unknown') + '\n' + 186 'ASN ........... ' + esc(geo.asn || 'unknown') + '\n' + 187 'VPN/Proxy ..... ' + esc((geo.proxy || geo.vpn) ? 'YES' : 'no') + '\n' + 188 'Hosting ....... ' + esc(geo.hosting ? 'YES' : 'no') + '\n' + 189 'Cloudflare .... ' + esc(net.behind_cloudflare ? 'YES' : 'no') + '\n' + 190 'HTTPS ......... ' + esc((s.request && s.request.is_https) ? 'yes' : 'NO') 191 ); 192 }, t); 193 }, 194 195 hardware: function () { 196 var t = this; 197 withReport(function (r) { 198 var b = r.browser || {}; 199 t.print( 200 'CPU cores ..... ' + esc(b.platform ? b.platform.cores : '?') + '\n' + 201 'Memory ........ ' + esc(b.platform && b.platform.memory ? b.platform.memory + ' GB' : 'unknown') + '\n' + 202 'GPU vendor .... ' + esc(b.webgl ? b.webgl.vendor : '?') + '\n' + 203 'GPU renderer .. ' + esc(b.webgl ? b.webgl.renderer : '?') + '\n' + 204 'Screen ........ ' + esc(b.screen ? b.screen.width + 'x' + b.screen.height : '?') + '\n' + 205 'Pixel ratio ... ' + esc(b.screen ? b.screen.pixelRatio : '?') + '\n' + 206 'Color depth ... ' + esc(b.screen ? b.screen.colorDepth + '-bit' : '?') + '\n' + 207 'Touch points .. ' + esc(b.platform ? b.platform.maxTouchPoints : '?') + 208 (b.battery ? '\nBattery ....... ' + esc(b.battery.level + '% ' + (b.battery.charging ? '(charging)' : '')) : '') 209 ); 210 }, t); 211 }, 212 213 privacy: function () { 214 var t = this; 215 withReport(function (r) { 216 var p = EYES.computePrivacy(r); 217 var lines = ['<span class="cyan">PRIVACY SCORE: ' + p.score + ' / 100</span>']; 218 p.categories.forEach(function (c) { 219 var total = 20, filled = Math.round((c.score / 100) * total); 220 var bar = ''; 221 for (var i = 0; i < total; i++) bar += (i < filled ? '#' : '-'); 222 var pad = (c.name + ' ').slice(0, 16); 223 var cls = c.score >= 66 ? 'ok' : c.score >= 33 ? 'warn' : 'err'; 224 lines.push(pad + ' [<span class="' + cls + '">' + bar + '</span>] ' + c.score); 225 }); 226 t.print(lines.join('\n')); 227 }, t); 228 }, 229 230 fingerprint: function () { 231 var t = this; 232 withReport(function (r) { 233 var fp = r.fingerprint || {}; 234 if (!fp.id) { t.print('<span class="err">no fingerprint available</span>'); return; } 235 var lines = ['<span class="cyan">FINGERPRINT ID (SHA-256):</span>', esc(fp.id), '', 236 'Entropy ....... ~' + esc(fp.entropyBits) + ' bits', 237 'Uniqueness .... ' + esc(fp.uniqueness) + '%', 238 'You are ~1 in . ' + esc(fp.oneIn.toLocaleString()), 239 'Tracking risk . ' + esc(fp.trackingRisk), '', '<span class="dim">per-signal:</span>']; 240 Object.keys(fp.signals).forEach(function (k) { 241 lines.push(' ' + (k + ' ').slice(0, 10) + ' ' + esc(fp.signals[k].short) + '...'); 242 }); 243 t.print(lines.join('\n')); 244 }, t); 245 }, 246 247 whoami: function () { 248 var t = this; 249 withReport(function (r) { 250 var b = r.browser || {}, s = r.server || {}, geo = s.geo || {}; 251 var who = (b.ua ? b.ua.browser : 'a browser') + ' on ' + (b.ua ? b.ua.os : 'an OS') + 252 ', ~' + (geo.city || geo.country || 'somewhere') + ', via ' + (geo.isp || 'your ISP') + 253 ', ' + (b.platform ? b.platform.cores + ' cores' : '') + 254 '. You are 1 in ' + ((r.fingerprint && r.fingerprint.oneIn) ? r.fingerprint.oneIn.toLocaleString() : 'many') + '.'; 255 t.print('<span class="ok">' + esc(who) + '</span>'); 256 }, t); 257 }, 258 259 neofetch: function () { 260 var t = this; 261 withReport(function (r) { 262 var b = r.browser || {}, s = r.server || {}, geo = s.geo || {}, fp = r.fingerprint || {}; 263 var art = [ 264 " .--. ", " |o_o | ", " |:_/ | ", 265 " // \\ \\ ", " (| | ) ", " /'\\_ _/`\\ ", 266 " \\___)=(___/ " 267 ]; 268 var info = [ 269 'guest@whatcanilearn', 270 '-------------------', 271 'OS: ' + (b.ua ? b.ua.os + ' ' + b.ua.osVersion : '?'), 272 'Browser: ' + (b.ua ? b.ua.browser + ' ' + b.ua.version : '?'), 273 'Engine: ' + (b.ua ? b.ua.engine : '?'), 274 'CPU: ' + (b.platform ? b.platform.cores + ' cores' : '?'), 275 'Memory: ' + (b.platform && b.platform.memory ? b.platform.memory + ' GB' : '?'), 276 'GPU: ' + (b.webgl ? b.webgl.renderer : '?'), 277 'Screen: ' + (b.screen ? b.screen.width + 'x' + b.screen.height : '?'), 278 'IP: ' + (s.ip || '?'), 279 'Location: ' + ([geo.city, geo.country].filter(Boolean).join(', ') || '?'), 280 'ISP: ' + (geo.isp || '?'), 281 'FP: ' + (fp.id ? fp.id.slice(0, 12) + '...' : '?'), 282 'Risk: ' + (fp.trackingRisk || '?') 283 ]; 284 var lines = []; 285 var n = Math.max(art.length, info.length); 286 for (var i = 0; i < n; i++) { 287 var a = art[i] || ' '; 288 var b2 = info[i] || ''; 289 lines.push('<span class="green">' + esc(a) + '</span> <span class="cyan">' + esc(b2) + '</span>'); 290 } 291 t.print(lines.join('\n')); 292 }, t); 293 }, 294 295 ascii: function () { 296 this.print('<span class="green">' + esc( 297 " __ __ _\n" + 298 " \\ \\ / /__ _ __ ___| |\n" + 299 " \\ \\ /\\ / / _ \\ '__/ _ \\ |\n" + 300 " \\ V V / __/ | | __/_|\n" + 301 " \\_/\\_/ \\___|_| \\___(_)\n" + 302 " What Can I Learn About You?" 303 ) + '</span>'); 304 }, 305 306 fortune: function () { 307 var fortunes = [ 308 'The internet never forgets. But this website does -- instantly.', 309 'You are not paranoid if they really are fingerprinting you. (They are.)', 310 'A cookie a day keeps the privacy away.', 311 'Privacy is not about having something to hide. It is about having something to protect.', 312 'Every "Accept All" button is a tiny surrender.', 313 'Your GPU is basically a name tag you can never take off.', 314 'The best time to install a tracker blocker was 20 years ago. The second best time is now.', 315 'On the internet, nobody knows you are a dog -- but they know your screen resolution.', 316 'VPN: because your ISP does not need a diary of your life.', 317 'They cannot sell what they never collected.' 318 ]; 319 var idx = Math.floor(Math.random() * fortunes.length); 320 this.print('<span class="yellow">' + esc(fortunes[idx]) + '</span>'); 321 }, 322 323 export: function (args) { 324 var fmt = (args[0] || 'json').toLowerCase(); 325 if (!EYES.exportReport) { this.print('<span class="err">export module not loaded</span>'); return; } 326 if (['json','html','md','markdown','txt'].indexOf(fmt) < 0) { 327 this.print('usage: export [json|html|md|txt]'); return; 328 } 329 var t = this; 330 withReport(function (r) { 331 EYES.exportReport(fmt === 'markdown' ? 'md' : fmt, r); 332 t.print('<span class="ok">downloading report as ' + esc(fmt) + '...</span>'); 333 }, t); 334 }, 335 336 ver: function () { 337 this.print('WhatCanILearn shell v1.0 -- built like it is 2003. No warranty. No cookies.'); 338 }, 339 340 echo: function (args) { this.print(esc(args.join(' '))); }, 341 342 about: function () { 343 this.print('This site shows what any website can learn about you, then throws it away.\n' + 344 'No database. No tracking. No cookies. Type source to read the code.'); 345 }, 346 347 source: function () { 348 this.print('The full source is on the <span class="cyan">Source</span> page: ' + 349 '<a href="' + (EYES.BASE || '') + '/source.php">' + esc((EYES.BASE || '') + '/source.php') + '</a>'); 350 }, 351 352 clear: function () { this.clear(); } 353 }; 354 355 /* ---- boot the terminal if the page has one ---------------------------- */ 356 EYES.initTerminal = function () { 357 var outEl = document.getElementById('termOutput'); 358 var inEl = document.getElementById('termInput'); 359 if (!outEl || !inEl) return null; 360 var term = new Terminal(outEl, inEl); 361 term.print('<span class="dim">WhatCanILearn interactive shell. Type </span><span class="cyan">help</span><span class="dim"> to begin.</span>'); 362 EYES.terminal = term; 363 inEl.focus(); 364 return term; 365 }; 366 367 })(); 368 You are reading the real file being executed on the server. What you see is what runs. |