<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thangaram Senthamaraikannan</title>
    <description>The latest articles on DEV Community by Thangaram Senthamaraikannan (@thangaram611).</description>
    <link>https://dev.to/thangaram611</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4187861%2Fed1b6677-8ff7-418f-a830-af20bdc68aaa.png</url>
      <title>DEV Community: Thangaram Senthamaraikannan</title>
      <link>https://dev.to/thangaram611</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thangaram611"/>
    <language>en</language>
    <item>
      <title>An incident report nobody could open, an extension that wanted my token, and 80 lines instead</title>
      <dc:creator>Thangaram Senthamaraikannan</dc:creator>
      <pubDate>Fri, 16 Oct 2026 05:19:23 +0000</pubDate>
      <link>https://dev.to/thangaram611/an-incident-report-nobody-could-open-an-extension-that-wanted-my-token-and-80-lines-instead-373l</link>
      <guid>https://dev.to/thangaram611/an-incident-report-nobody-could-open-an-extension-that-wanted-my-token-and-80-lines-instead-373l</guid>
      <description>&lt;h2&gt;
  
  
  The report nobody could open
&lt;/h2&gt;

&lt;p&gt;We write incident reports as single HTML files: inline CSS, an inline SVG timeline, no build step, one file you can read in ten years. They live in a private GitHub repository next to the code they are about.&lt;/p&gt;

&lt;p&gt;Nobody could open them. GitHub shows an &lt;code&gt;.html&lt;/code&gt; file as source, and the "download it, then double-click it" workflow is where reading stops for most people. GitHub Pages would render them, but Pages on a private repository is an Enterprise feature. GitHub's PDF viewer exists, but these are not PDFs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The extension that wanted my token
&lt;/h2&gt;

&lt;p&gt;There is a Chrome extension for exactly this problem. I read its code before installing it. It asks for a GitHub token (a personal access token, or an OAuth grant with full &lt;code&gt;repo&lt;/code&gt; scope), sends that token to the author's server, and the server fetches your file and renders it on the author's domain.&lt;/p&gt;

&lt;p&gt;That is a lot of trust for a preview button. An incident report is the most sensitive document a team writes, and the fix was to hand the key to the whole repository to a stranger's Lambda.&lt;/p&gt;

&lt;p&gt;The other option, a userscript, keeps the token in the browser but resolves the short-lived tokenised link GitHub issues for a private file and passes it, as a URL parameter, to the author's website, which loads the file from there. The link to your private file lands in that site's address bar, history and hosting logs.&lt;/p&gt;

&lt;p&gt;Both are honest projects, and the idea of a button beside &lt;strong&gt;Raw&lt;/strong&gt; is theirs. I just did not want the file to leave the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Raw button already does the hard part
&lt;/h2&gt;

&lt;p&gt;Click &lt;strong&gt;Raw&lt;/strong&gt; on a private file and watch the network tab. GitHub answers &lt;code&gt;github.com/&amp;lt;owner&amp;gt;/&amp;lt;repo&amp;gt;/raw/&amp;lt;ref&amp;gt;/&amp;lt;path&amp;gt;&lt;/code&gt; with your own session, then redirects to &lt;code&gt;raw.githubusercontent.com&lt;/code&gt; with a token that expires in minutes. That host serves the file as &lt;code&gt;text/plain&lt;/code&gt;, which is why it is never rendered, and sends &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt;. And github.com's Content Security Policy allows connections to it.&lt;/p&gt;

&lt;p&gt;So a script running on the file page can do what Raw does, and then fix the only thing that was wrong, the content type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tab&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                    &lt;span class="c1"&gt;// open inside the click, so popup blockers allow it&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/blob/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/raw/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;same-origin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;tab&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the whole mechanism. The only network request is the one GitHub itself makes for Raw. No server, no token, no account, no site other than github.com in the path. The rendered page lives at a &lt;code&gt;blob:&lt;/code&gt; URL in your browser, with no cookies and no access to GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  80 lines instead
&lt;/h2&gt;

&lt;p&gt;The result is &lt;a href="https://github.com/thangaram611/unraw" rel="noopener noreferrer"&gt;unraw&lt;/a&gt;: a userscript that adds an &lt;strong&gt;unraw&lt;/strong&gt; button next to &lt;strong&gt;Raw&lt;/strong&gt; on every &lt;code&gt;.html&lt;/code&gt; file page, and a bookmarklet for people who do not want to install anything. The button copies Raw's classes and &lt;code&gt;data-*&lt;/code&gt; attributes so it matches GitHub's current styling, and a MutationObserver puts it back after GitHub's client-side navigation. The rest is the snippet above with error handling. Your userscript manager shows you all of it before installing; it is about 80 lines.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzsx02nx0jq015adi3a88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzsx02nx0jq015adi3a88.png" alt="The unraw button beside Raw" width="799" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two limits, both inherent to the approach. Pages that reference relative files (&lt;code&gt;./style.css&lt;/code&gt;, &lt;code&gt;img/x.png&lt;/code&gt;) render without them, because those paths do not exist at a &lt;code&gt;blob:&lt;/code&gt; URL; self-contained pages render completely, and inlining is the right habit for documents you want to read from GitHub anyway. Inline scripts run inside the &lt;code&gt;blob:&lt;/code&gt; page, isolated from GitHub, so treat a preview the way you would treat opening a downloaded copy.&lt;/p&gt;

&lt;p&gt;The rule that produced the tool is the rule that keeps it small: the file may not leave your browser. Anything that would need a server or a token will not be added.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install page, with the bookmarklet: &lt;a href="https://thangaram611.github.io/unraw/" rel="noopener noreferrer"&gt;https://thangaram611.github.io/unraw/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Userscript on Greasy Fork: &lt;a href="https://greasyfork.org/scripts/600316-unraw" rel="noopener noreferrer"&gt;https://greasyfork.org/scripts/600316-unraw&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source, MIT, with a comparison table drawn from each tool's code: &lt;a href="https://github.com/thangaram611/unraw" rel="noopener noreferrer"&gt;https://github.com/thangaram611/unraw&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>javascript</category>
      <category>privacy</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
