Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active December 1, 2020 14:14
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WebReflection/d03d73c5934cff66036a22d678085f54 to your computer and use it in GitHub Desktop.
Save WebReflection/d03d73c5934cff66036a22d678085f54 to your computer and use it in GitHub Desktop.
A privileged answer to a well known issue.

This site throws in users and, most importantly, developers face, the fact publishing websites with hundreds of JS Kilobytes just to see some content, content that might also break due JS itself or browsers that haven't been tested or targeted, is very bad.

The same site is also great to remind everyone that a11y (accessibility) matters, and if you got upset by its disruptive technique, and you are a Web developer, now you know how it feels for people incapable of surfing the "modern Web" with its overly-bloated frameworks and practices that way too often don't take a11y into account.

However, JS is not to blame here, while developers abusing JS without following graceful enhancement practices, or without testing their sites offer some meaningful content even for users that might have disabled JS for whatever reason, are to blame so ... please "don't be that kind of developer".

That being said, as an exercise to see if I could surf it via JS, I've created this tiny snippet you can copy and paste in the browser console ... and after all, if it breaks, or make the browsing less natural or broken, you got the point JS should rarely be the only way to present Web content 👍

(async function IamPrivileged(event) {
  const {href} = event.currentTarget;
  if (href.indexOf(location.protocol + '//' + location.hostname))
    return;
  if (event.isTrusted)
    event.preventDefault();
  const html = await (await fetch(href)).text();
  const doc = (new DOMParser).parseFromString(html, 'text/html');
  const [head, body, noscript] = doc.querySelectorAll('head,body,noscript');
  const {documentElement} = document;
  documentElement.replaceChild(head, document.head);
  documentElement.replaceChild(body, document.body);
  while (noscript.hasChildNodes())
    body.insertBefore(noscript.firstChild, noscript);
  for (const a of document.querySelectorAll('a'))
    a.addEventListener('click', IamPrivileged);
  const {textContent} = head.querySelector('title');
  const method = event.isTrusted ? 'pushState' : 'replaceState';
  history[method](href, textContent, href);
  addEventListener(
    'popstate',
    IamPrivileged.pop || (IamPrivileged.pop = ({state}) => {
      IamPrivileged({target: {href: state}});
    })
  );
}({currentTarget: location}));
@myfonj
Copy link

myfonj commented Nov 30, 2020

There used to be distribution and usage model for this kind of functionality even before Developer Tools Consoles became integral part of browsers:

Bookmarklet

Yes, bookmarked javavascript: URLs still (mostly) works even today.

This is "installation page" of the original console snippet slightly altered and crudely crammed into javascript: URL in href of link in datauri document, so it can be easily "installed". Copy and paste this code into your URL bar or navigate to this data URI any other way and follow instructions displayed there.

data:text/html,<title>"I am Privileged" bookmarklet installation serverless single page application</title><a title="I am Privileged and Want to See NOSCRIPT content" href="javascript:(function(){if(confirm('I am not offended by this site and want to see it\'s content for educative purposes.'))(async function IamPrivileged(event) {const {href} = event.target;/* if (href.indexOf(location.protocol + '//' + location.hostname))return;*/if (event.isTrusted)event.preventDefault();try{const html = await (await fetch(href)).text();const doc = (new DOMParser).parseFromString(html, 'text/html');const [head, body, noscript] = doc.querySelectorAll('head,body,noscript');const {documentElement} = document;documentElement.replaceChild(head, document.head);documentElement.replaceChild(body, document.body);while (noscript.hasChildNodes())body.insertBefore(noscript.firstChild, noscript);for (const a of document.querySelectorAll('a'))a.addEventListener('click', IamPrivileged);const {textContent} = head.querySelector('title');const method = event.isTrusted ? 'pushState' : 'replaceState';history[method](href, textContent, href);addEventListener('popstate',IamPrivileged.pop || (IamPrivileged.pop = ({state}) => {IamPrivileged({target: {href: state}});}));}catch(err){console.warn(err)}}({target: {href: location.href}}));})();">I am Privileged and want to see noscript content</a> bookmarklet<br>Drag and drop this link into your Bookmarks Toolbar or bookmark it other way.<br>Then click the Bookmarks Toolbar item or invoke resulting bookmark other way on site of interest<a href="https://heydonworks.com/" style="color:iherit;text-decoration:none;font-weight:bold">.</a><br><br><noscript>It <del style="text-decoration:none;font-size:.6rem;opacity:.4">Heydon</del><ins>works</ins>!</noscript>

@WebReflection
Copy link
Author

WebReflection commented Nov 30, 2020

javascript: in the url bar doesn't work here (Firefox) but it does in Chrome so that this:

javascript:eval(atob("IWFzeW5jIGZ1bmN0aW9uIGUodCl7Y29uc3R7aHJlZjpvfT10LmN1cnJlbnRUYXJnZXQ7aWYoby5pbmRleE9mKGxvY2F0aW9uLnByb3RvY29sKyIvLyIrbG9jYXRpb24uaG9zdG5hbWUpKXJldHVybjt0LmlzVHJ1c3RlZCYmdC5wcmV2ZW50RGVmYXVsdCgpO2NvbnN0IHI9YXdhaXQoYXdhaXQgZmV0Y2gobykpLnRleHQoKSxuPShuZXcgRE9NUGFyc2VyKS5wYXJzZUZyb21TdHJpbmcociwidGV4dC9odG1sIiksW2EsYyxpXT1uLnF1ZXJ5U2VsZWN0b3JBbGwoImhlYWQsYm9keSxub3NjcmlwdCIpLHtkb2N1bWVudEVsZW1lbnQ6bH09ZG9jdW1lbnQ7Zm9yKGwucmVwbGFjZUNoaWxkKGEsZG9jdW1lbnQuaGVhZCksbC5yZXBsYWNlQ2hpbGQoYyxkb2N1bWVudC5ib2R5KTtpLmhhc0NoaWxkTm9kZXMoKTspYy5pbnNlcnRCZWZvcmUoaS5maXJzdENoaWxkLGkpO2Zvcihjb25zdCB0IG9mIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoImEiKSl0LmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIixlKTtjb25zdHt0ZXh0Q29udGVudDpzfT1hLnF1ZXJ5U2VsZWN0b3IoInRpdGxlIiksZD10LmlzVHJ1c3RlZD8icHVzaFN0YXRlIjoicmVwbGFjZVN0YXRlIjtoaXN0b3J5W2RdKG8scyxvKSxhZGRFdmVudExpc3RlbmVyKCJwb3BzdGF0ZSIsZS5wb3B8fChlLnBvcD0oe3N0YXRlOnR9KT0+e2Uoe3RhcmdldDp7aHJlZjp0fX0pfSkpfSh7Y3VycmVudFRhcmdldDpsb2NhdGlvbn0p"))

might do the trick, but I'm not sure it works as GitHub link (tried, it doesn't)

plain version (still after terser)

javascript:!async function e(t){const{href:o}=t.currentTarget;if(o.indexOf(location.protocol+\"//\"+location.hostname))return;t.isTrusted&&t.preventDefault();const r=await(await fetch(o)).text(),n=(new DOMParser).parseFromString(r,\"text/html\"),[a,c,i]=n.querySelectorAll(\"head,body,noscript\"),{documentElement:l}=document;for(l.replaceChild(a,document.head),l.replaceChild(c,document.body);i.hasChildNodes();)c.insertBefore(i.firstChild,i);for(const t of document.querySelectorAll(\"a\"))t.addEventListener(\"click\",e);const{textContent:s}=a.querySelector(\"title\"),d=t.isTrusted?\"pushState\":\"replaceState\";history[d](o,s,o),addEventListener(\"popstate\",e.pop||(e.pop=({state:t})=>{e({target:{href:t}})}))}({currentTarget:location})

@myfonj
Copy link

myfonj commented Nov 30, 2020

Yes, running javascript: URLs directly from URL bar does not work in Firefox (as a "self-XSS precaution"), but works from bookmark items - either by clicking the bookmark item button, invoking it from Library or with bookmark keyword from URL. (Sadly, I know no convenient way to trigger it from URL bar directly without setting up a keyword, because even when you have your prefs allowing javascript: URLs in there so you can find it, it is then blocked same way as directly typed self-XSS. Sad.)

(Most browsers currently even delete the javascript: protocol on paste into url bar, including Firefox, where, considering it is blocked anyway, … strange.)

@WebReflection
Copy link
Author

So, data:text/javascript,alert(1) or data:application/javascript,alert(1) in Safari mobile shows a text page with the JS bit ... not usable, while the javascript: in the URL throws an error saying:

Cannot Run Script Safari cannot run the script because JavaScript is not allowed to be used this way.

... we need to go deeper ... 😂

@myfonj
Copy link

myfonj commented Nov 30, 2020

Sure, entering data:application/javascript,alert(1) (generally any kind of data:[known mime-type],content) should be equivalent of navigating to and displaying content of file of given mime-type just like if it was loaded from server or local filesystem. (MDN.)


Detour: Most straightforward way to run some JS from URL nowadays is naturally accompanying it with own HTML document (so sadly no operation on previously displayed page), like data:text/html,<script>alert(1)</script> or even inceptive data:text/html,<script src="data:application/javascript,alert(1)"></script> or just data:text/html,<script src="data:,alert(1)"></script>, because no mime is text/plain and text/plain seems still works for JS in this context.
And from that point on one can progress to build their own local/unhosted application, like this sandbox.

@WebReflection
Copy link
Author

Sure, I just would like to provide a way for mobile to see it ... and I have few other ideas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment