no-link-referrer.ts 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
  1. /*
  2. Due to Owncast's goal of being private by default, we don't want any external
  3. links to leak the instance of Owncast as a referrer.
  4. This observer attempts to catch any anchor tags and automatically add the
  5. noopener and noreferrer attributes to them so the instance of Owncast isn't
  6. passed along in the headers.
  7. This should should be fired somewhere relatively high level in the DOM and live
  8. for the entirety of the page.
  9. */
  10. /* eslint-disable no-restricted-syntax */
  11. export default function setupNoLinkReferrer(observationRoot: HTMLElement): void {
  12. // Options for the observer (which mutations to observe)
  13. const config = { attributes: false, childList: true, subtree: true };
  14. const addNoReferrer = (node: Element): void => {
  15. const existingAttributes = node.getAttribute('rel');
  16. const attributes = `${existingAttributes} noopener noreferrer`;
  17. node.setAttribute('rel', attributes);
  18. };
  19. // Callback function to execute when mutations are observed
  20. // eslint-disable-next-line func-names
  21. const callback = function (mutationList) {
  22. for (const mutation of mutationList) {
  23. for (const node of mutation.addedNodes) {
  24. // we track only elements, skip other nodes (e.g. text nodes)
  25. // eslint-disable-next-line no-continue
  26. if (!(node instanceof HTMLElement)) continue;
  27. if (node.tagName.toLowerCase() === 'a') {
  28. addNoReferrer(node);
  29. }
  30. }
  31. }
  32. };
  33. observationRoot.querySelectorAll('a').forEach(anchor => addNoReferrer(anchor));
  34. // Create an observer instance linked to the callback function
  35. const observer = new MutationObserver(callback);
  36. // Start observing the target node for configured mutations
  37. observer.observe(observationRoot, config);
  38. }