PageSourceSearch

https://distribb.io/static/js/posthog-init.js?v=2

js distribb.io collected 2026-10-10 00:50:08 UTC 4,188 bytes, 96 lines download raw bytes

1/* PostHog bootstrap.
2 *
3 * Loaded with `defer` immediately after array.js, so window.posthog is already
4 * the real library when this runs -- array.js finishes with `window.posthog =
5 * <instance>` whether or not the queue stub is present, which is why
6 * templates/_posthog.html can skip that stub entirely.
7 *
8 * Config and identity arrive in the #posthog-config JSON tag rather than as
9 * inline JS, so this file stays static-cacheable and carries no per-request
10 * values. */
11(function () {
12  if (window.__posthogInitialised) return;
13
14  var node = document.getElementById('posthog-config');
15  if (!node || !window.posthog || typeof window.posthog.init !== 'function') return;
16
17  var cfg;
18  try {
19    cfg = JSON.parse(node.textContent || '{}');
20  } catch (e) {
21    return;
22  }
23  if (!cfg.apiKey) return;
24
25  window.__posthogInitialised = true;
26
27  /* ---- session replay masking -------------------------------------------
28     rrweb offers maskTextSelector but NOT unmaskTextSelector, so "mask
29     everything then unmask the safe parts" is not available. A selector list
30     on its own would be an allowlist, and an allowlist fails OPEN: the day
31     someone adds a page that prints an API key, it records in the clear and
32     nothing complains. That is the same failure mode that left datafa.st
33     blocked and Stripe attribution empty for months.
34
35     So the primary defence keys on what text LOOKS like, not where it sits.
36     A new page printing a token is masked on day one without anyone updating
37     a list. The selector list below is only a cheap second layer.
38
39     Deliberately still readable: button labels, headings, nav, and error
40     messages under 240 chars, because those are the whole point of watching a
41     replay to work out why somebody churned. */
42  var SENSITIVE = new RegExp(
43    '[\\w.+-]+@[\\w-]+\\.[\\w.]{2,}'          + '|' +  // email addresses
44    '(?:sk|pk|phc|phx|rk|whsec)_[A-Za-z0-9_]{8,}' + '|' +  // stripe / posthog style keys (sk_live_… keeps the underscore)
45    'Bearer\\s+[A-Za-z0-9._-]{12,}'           + '|' +  // auth headers
46    'eyJ[A-Za-z0-9._-]{20,}'                  + '|' +  // JWTs
47    'gh[pousr]_[A-Za-z0-9]{20,}'              + '|' +  // github tokens
48    'xox[baprs]-[A-Za-z0-9-]{10,}'            + '|' +  // slack tokens
49    'AKIA[0-9A-Z]{12,}'                       + '|' +  // aws access keys
50    '[A-Za-z0-9+/]{32,}={0,2}'                          // long opaque blobs
51  );
52  var LONG_PROSE = 240;   // article drafts and generated copy
53
54  function stars(text) {
55    // Keep whitespace so the replay still shows real layout, not one blob.
56    return text.replace(/\S/g, '*');
57  }
58
59  function maskText(text) {
60    if (!text || text.length < 4) return text;
61    if (text.length > LONG_PROSE) return stars(text);
62    if (SENSITIVE.test(text)) return stars(text);
63    return text;
64  }
65  window.__phMaskText = maskText;   // exported so it can be tested directly
66
67  window.posthog.init(cfg.apiKey, {
68    api_host: cfg.apiHost,
69    defaults: '2026-05-30',
70    /* Anonymous visitors still send events -- web analytics needs them -- they
71       just don't each get a person profile, which is what burns the quota. */
72    person_profiles: 'identified_only',
73    session_recording: {
74      maskAllInputs: true,
75      maskTextFn: maskText,
76      maskTextSelector: [
77        '[class*="api-key"]', '[class*="apikey"]', '[class*="apiKey"]',
78        '[id*="api-key"]', '[id*="apikey"]', '[id*="apiKey"]',
79        '[class*="token"]', '[id*="token"]',
80        '[class*="secret"]', '[id*="secret"]',
81        '[class*="password"]', '[id*="password"]',
82        '.ph-mask'
83      ].join(', ')
84    }
85  });
86
87  if (cfg.person && cfg.person.distinctId) {
88    window.posthog.identify(cfg.person.distinctId, cfg.person.properties || {});
89  } else if (typeof window.posthog._isIdentified === 'function' && window.posthog._isIdentified()) {
90    /* The session ended (logout or expiry). Stop attributing this browser to
91       the previous user -- but only when it was actually identified, since
92       resetting on every anonymous pageview would hand each one a fresh id and
93       break anonymous funnels. */
94    window.posthog.reset();
95  }
96})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.