<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[tercmd]]></title><description><![CDATA[tercmd]]></description><link>https://www.tercmd.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1705251268267/jyjM3FLoi.png</url><title>tercmd</title><link>https://www.tercmd.com</link></image><generator>RSS for Node</generator><lastBuildDate>Tue, 07 Apr 2026 09:57:27 GMT</lastBuildDate><atom:link href="https://www.tercmd.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Creating a Community Board with HTMX & Express]]></title><description><![CDATA[Step 1. Basic app
We create a folder for our application. Then we cd into the folder and run npm init -y.
Then we install express and hbs using npm or any other Node package manager.
npm init -y
npm install express hbs --save

Create index.js with th...]]></description><link>https://www.tercmd.com/community-board-using-htmx-express</link><guid isPermaLink="true">https://www.tercmd.com/community-board-using-htmx-express</guid><category><![CDATA[htmx]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Express]]></category><dc:creator><![CDATA[tercmd]]></dc:creator><pubDate>Sun, 21 Jan 2024 09:48:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705830387071/57495401-ffd7-45ac-a979-4ca3bb1cc05f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-step-1-basic-app">Step 1. Basic app</h2>
<p>We create a folder for our application. Then we <code>cd</code> into the folder and run <code>npm init -y</code>.</p>
<p>Then we install <code>express</code> and <code>hbs</code> using npm or any other Node package manager.</p>
<pre><code class="lang-bash">npm init -y
npm install express hbs --save
</code></pre>
<p>Create <code>index.js</code> with the following contents:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> hbs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'hbs'</span>);
<span class="hljs-keyword">const</span> port = <span class="hljs-number">3000</span>;

app.set(<span class="hljs-string">'view engine'</span>, <span class="hljs-string">'hbs'</span>);
app.use(express.static(<span class="hljs-string">'static'</span>));
app.use(express.urlencoded({ <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span> })); <span class="hljs-comment">// support encoded bodies</span>
app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    res.render(<span class="hljs-string">"index"</span>);
})

app.listen(port, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Example app listening on port <span class="hljs-subst">${port}</span>`</span>);
})
</code></pre>
<p>Create two folders called <code>views</code> and <code>static</code> in the root of your application directory.</p>
<ul>
<li><p>The <code>views</code> folder contains the templates used for rendering the application.</p>
</li>
<li><p>The <code>static</code> folder contains static files like images, CSS, etc.</p>
</li>
</ul>
<p>Create a file called <code>index.hbs</code> in the <code>views</code> folder with this content:</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Community Board<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/htmx.org"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/@picocss/pico@1.5.11/css/pico.min.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span> <span class="hljs-attr">hx-boost</span>=<span class="hljs-string">"true"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/submit"</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This loads htmx and PicoCSS (a classless styling framework) from a CDN. In production, this is not recommended and should be loaded from your origin, but for this demo it should be okay.</p>
<p>Start the server using <code>node index.js</code> (this script is compatible with Bun too)</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Every time the <code>index.js</code> file is changed, you must restart the server with the above command. However, changing the templates does not require a server restart.</div>
</div>

<p>If you load this page, you should see this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705825983942/1c93f094-1b4a-4383-a8ad-7403918f6724.png" alt="A navbar with three options for &quot;Home&quot;, &quot;About&quot; and &quot;Submit&quot;" class="image--center mx-auto" /></p>
<h2 id="heading-step-2-listing-articles">Step 2. Listing articles</h2>
<p>Create a file in the <code>views</code> directory with the name <code>articles.hbs</code> with this content:</p>
<pre><code class="lang-xml">{{#each articles}}
<span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{this.name}}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{this.description}}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
{{/each}}
</code></pre>
<p>Then we get the list of articles (using a database query). For this example, I am using a simple object to store articles, however a database could be added in later.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> articles = [
    {
        <span class="hljs-string">"name"</span>: <span class="hljs-string">"Live GitHub Status (for GNOME Wayland)"</span>,
        <span class="hljs-string">"description"</span>: <span class="hljs-string">"I had this cool idea to show the currently focused application on my system as part of my GitHub status."</span>
    },
    <span class="hljs-comment">// ...</span>
];
</code></pre>
<p>We define a path to return a rendered list of articles in our Express server:</p>
<pre><code class="lang-javascript">app.get(<span class="hljs-string">'/articles/list'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    res.render(<span class="hljs-string">"articles"</span>, {<span class="hljs-attr">articles</span>: articles});
})
</code></pre>
<p>And in <code>index.hbs</code>, we add a <code>div</code> that will fetch the articles and swap the returned HTML into itself.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">hx-get</span>=<span class="hljs-string">"/articles/list"</span> <span class="hljs-attr">hx-trigger</span>=<span class="hljs-string">"load"</span> <span class="hljs-attr">hx-swap</span>=<span class="hljs-string">"outerHTML"</span> <span class="hljs-attr">aria-busy</span>=<span class="hljs-string">"true"</span>&gt;</span>Loading articles...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705827572426/b739be61-3b1e-4004-a1c6-1bae21168644.png" alt="Two articles showing on the page" class="image--center mx-auto" /></p>
<h2 id="heading-step-3-about-page">Step 3. About page</h2>
<p>On the about page, we follow similar steps as the index page.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705827766373/4501deda-2195-42cf-94df-afef4db4724f.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-step-4-submitting-posts">Step 4. Submitting posts</h2>
<p>We create a file called <code>submit.hbs</code> in the <code>views</code> directory.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Submit a Post<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://unpkg.com/htmx.org"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/@picocss/pico@1.5.11/css/pico.min.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span> <span class="hljs-attr">hx-boost</span>=<span class="hljs-string">"true"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>&gt;</span>Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/about"</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/submit"</span> <span class="hljs-attr">hx-post</span>=<span class="hljs-string">"/submit"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"title"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Title"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Description"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"Submit"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>We add this page to a <code>/submit</code> path (using a <code>GET</code> request, <code>POST</code> is used for the form action)</p>
<pre><code class="lang-javascript">app.get(<span class="hljs-string">'/submit'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    res.render(<span class="hljs-string">"submit"</span>);
})
</code></pre>
<p>Then we configure a path on our server to add to the list of articles, returning a message <code>Post submitted!</code></p>
<pre><code class="lang-javascript">app.post(<span class="hljs-string">'/submit'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    articles.unshift({<span class="hljs-attr">name</span>: req.body.title, <span class="hljs-attr">description</span>: req.body.description});
    res.send(<span class="hljs-string">'&lt;p&gt;Post submitted!&lt;/p&gt;'</span>);
})
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">As this is a simple response, I've not created a template for it. Also, this code is purely for adding to the beginning of the <code>articles</code> object. This should be replaced with database queries.</div>
</div>

<p>Then a user can submit posts.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705828652390/29809f60-8788-4b6e-ad3b-f171110fc755.png" alt="Submit form with title and description filled in" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705828552587/9f801211-cb3d-4ed5-9da0-f0102049e19c.png" alt="Homepage showing new post" class="image--center mx-auto" /></p>
<p>That's it! If you have any feedback, please leave a comment. Click the ❤️ if you liked this.</p>
]]></content:encoded></item><item><title><![CDATA[Creating file system routing in Bun]]></title><description><![CDATA[Bun has a built-in web server. However, handling requests with that web server is very rudimentary. So, I wanted to to create a simpler file-based routing system.
Initial approach
My initial approach was to require every file in the pages folder as a...]]></description><link>https://www.tercmd.com/creating-file-system-routing-in-bun</link><guid isPermaLink="true">https://www.tercmd.com/creating-file-system-routing-in-bun</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Bun]]></category><dc:creator><![CDATA[tercmd]]></dc:creator><pubDate>Mon, 15 Jan 2024 07:31:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705303673574/b4f1daec-dd4b-4dd0-a44d-8aa7e5fa2dfb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://bun.sh">Bun</a> has a built-in web server. However, handling requests with that web server is very rudimentary. So, I wanted to to create a simpler file-based routing system.</p>
<h2 id="heading-initial-approach">Initial approach</h2>
<p>My initial approach was to require every file in the <code>pages</code> folder as a module.</p>
<p>While this worked for basic cases, it did not work for URLs with nested levels like <code>/js/intro</code>.</p>
<h2 id="heading-slightly-better-approach">Slightly better approach</h2>
<p>I then thought to replace any <code>/</code> with a specific character or set of characters like <code>_____</code>. However, if a user uses <code>_____</code> in their path, this would return the equivalent but with <code>/</code>. Running a separate script to import all pages also didn't look clean.</p>
<h2 id="heading-the-solution">The solution</h2>
<p>I opened Bun's website and saw that they have an example called "File system routing". The example showed me that this was exactly what I wanted.</p>
<p>My final code looked like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> Bun.FileSystemRouter({
  <span class="hljs-attr">style</span>: <span class="hljs-string">"nextjs"</span>,
  <span class="hljs-attr">dir</span>: <span class="hljs-string">"./pages/"</span>,
});

Bun.serve({
  fetch(req) {
    <span class="hljs-keyword">let</span> match = router.match(req);
    <span class="hljs-keyword">let</span> page = <span class="hljs-built_in">require</span>(match.filePath);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> (page)(req, match.query, match.params);
  },
});
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Live GitHub Status (for GNOME Wayland)]]></title><description><![CDATA[I had this cool idea to show the currently focused application on my system (i use gnome on ubuntu btw) as part of my GitHub status.
Implementation
I first went into GitHub settings and created a PAT (NOT fine-grained) with the user scope.
Then, I fo...]]></description><link>https://www.tercmd.com/live-github-status-for-gnome-wayland</link><guid isPermaLink="true">https://www.tercmd.com/live-github-status-for-gnome-wayland</guid><category><![CDATA[GitHub]]></category><category><![CDATA[Python]]></category><category><![CDATA[APIs]]></category><dc:creator><![CDATA[tercmd]]></dc:creator><pubDate>Thu, 14 Dec 2023 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244373968/e932b561-9174-494d-96b1-b043bc9d3ad9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I had this cool idea to show the currently focused application on my system (i use gnome on ubuntu btw) as part of my GitHub status.</p>
<h2 id="heading-implementation">Implementation</h2>
<p>I first went into GitHub settings and created a PAT (NOT fine-grained) with the <code>user</code> scope.</p>
<p>Then, I found a <a target="_blank" href="https://gist.github.com/rbreaves/257c3edfa301786e66e964d7ac036269">command on GitHub</a> to get the focused window class under Wayland in GNOME (e.g. <code>firefox</code>, <code>Code</code>, <code>Org.gnome.Software</code>) and passed that into <code>os.popen()</code></p>
<p>I then used GPT to generate Python code to send the GraphQL API request (although similar code for TS/JS is available in <a target="_blank" href="https://github.com/wsmd/github-profile-status/">this GitHub repo</a>)</p>
<p>I also added some code to map an application name to an emoji with fallbacks.</p>
<p>Just wrap that in a <code>while True:</code> loop and you’re done—</p>
<p>Almost, there are a few things missing:</p>
<ul>
<li><p>adding more user friendly names for apps like <code>Org.gnome.Software</code> (Ubuntu Software), but that’s something for a future point in time.</p>
</li>
<li><p>clear status after some time</p>
</li>
</ul>
<h2 id="heading-the-code">The Code</h2>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="c0530f2a832463391af118ee4b766bae"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/terminalcommandnewsletter/c0530f2a832463391af118ee4b766bae" class="embed-card">https://gist.github.com/terminalcommandnewsletter/c0530f2a832463391af118ee4b766bae</a></div>]]></content:encoded></item><item><title><![CDATA[What's Phishing Like In 2023?]]></title><description><![CDATA[I found a phishing filter (here) which can be used in content blockers (as well as adblockers) like uBlock Origin. I wanted to analyze the phishing links — and this is the analysis.
Popular Web Hosts
Who are the web hosts (and website builders) that ...]]></description><link>https://www.tercmd.com/phishing-analysis-2023</link><guid isPermaLink="true">https://www.tercmd.com/phishing-analysis-2023</guid><category><![CDATA[phishing]]></category><dc:creator><![CDATA[tercmd]]></dc:creator><pubDate>Sun, 19 Nov 2023 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244378738/70bbed48-4d91-48cd-b5fd-e2ee290c8574.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I found a phishing filter (<a target="_blank" href="https://malware-filter.gitlab.io/phishing-filter/phishing-filter.txt">here</a>) which can be used in content blockers (as well as adblockers) like uBlock Origin. I wanted to analyze the phishing links — and this is the analysis.</p>
<h2 id="heading-popular-web-hosts">Popular Web Hosts</h2>
<p>Who are the web hosts (and website builders) that are used the most often in the phishing websites examined?</p>
<ol>
<li><p>Google for <a target="_blank" href="https://firebase.google.com/docs/hosting">Firebase Hosting</a>, with <code>2,428</code> unique websites <sup id="1"><a class="post-section-overview" href="#ft1">1</a></sup>.</p>
</li>
<li><p>Weebly (a free website builder), with <code>2,239</code> websites.  <sup id="2"><a class="post-section-overview" href="#ft2">2</a></sup></p>
</li>
<li><p>r2.dev, with <code>1,286</code> entries.<br /> This refers to the R2 file storage offered by Cloudflare®.</p>
</li>
<li><p>crazydomains.com (with their Sitebeat website builder), with <code>1,164</code> websites.<br /> Interestingly, the domains I tested seemed to redirect to <code>global_errors.sitebeat.crazydomains.com</code> with a “Coming Soon” page (which is also in the filter list)</p>
</li>
<li><p>workers.dev (Cloudflare Workers), with <code>759</code> entries<br /> Cloudflare Workers is a service which allows a user to run JavaScript on their edge nodes. I presume these could be proxies or returning hardcoded HTML. However, the code isn’t visible so that’s just speculation.</p>
</li>
</ol>
<p>Just these 5 domains cover 66% of recognized hosting providers.</p>
<h2 id="heading-dynamic-dns">Dynamic DNS</h2>
<p>Dynamic DNS is a service where a fixed hostname maps to a dynamic IP address (like a home IP address) that updates regularly.</p>
<ol>
<li><p>DuckDNS, with <code>138</code> entries.</p>
</li>
<li><p>DynDNS, with <code>20</code> entries.</p>
</li>
<li><p>ddns.net and ddnss.eu, with <code>1</code> entry each.</p>
</li>
</ol>
<h2 id="heading-documents">Documents</h2>
<p>This category consists of platforms that host documents, spreadsheets and presentations.</p>
<ol>
<li><p>Google Docs, with <code>1,296</code> entries.<br /> This includes 74 forms, 10 documents, 6 drawings, and 1,206 presentations.<br /> Almost all presentations contain “/pub?” with some data as URL parameters. I’m not too sure what this does.<br /> In addition, Google Drive has <code>9</code> entries.<br /> Many of the URLs seem to be taken down, either by the owner or by Google.</p>
</li>
<li><p>telegra.ph, with <code>3</code> entries.</p>
</li>
<li><p>notion.site, with <code>2</code> entries.</p>
</li>
</ol>
<h2 id="heading-forms">Forms</h2>
<p>This consists of tools that allow users to build forms.</p>
<ol>
<li><p>Microsoft Forms, with <code>62</code> forms.</p>
</li>
<li><p>forms.app, with <code>24</code> forms.</p>
</li>
<li><p>Google Forms (forms.gle), with <code>15</code> forms.</p>
</li>
<li><p>JotForm, with <code>11</code> forms.</p>
</li>
<li><p>formstack.com and hsforms.com, with <code>7</code> forms each.</p>
</li>
</ol>
<h2 id="heading-scripts">Scripts</h2>
<p>There are <code>102</code> entries for “script.google.com” (Google Apps Script) which link to macros and running the script, sometimes with parameters to track the user that clicks.</p>
<h2 id="heading-government">Government</h2>
<p>There are 4 government domains, none of which <em>seem</em> inherently malicious. The entire domain is blocked by the phishing filter.<br />I’m not listing these domains here because the pattern is what matters, not an individual country.</p>
<h2 id="heading-ip-addresses">IP Addresses</h2>
<p><code>98</code> unique IP(v4) addresses (with 441 entries) exist in the phishing filter list.</p>
<p>(For the below information, ipinfo.io was used as a source)</p>
<h3 id="heading-top-asns">Top ASNs</h3>
<p>ASNs (Autonomous System Numbers) are the organizations that own a block(s) of IP addresses to provide services to their customers (hosting providers, ISPs, some VPNs)</p>
<ol>
<li><p>“AS132203 Tencent Building, Kejizhongyi Avenue” with <code>46</code> IP addresses.</p>
</li>
<li><p>“AS14061 DigitalOcean, LLC” and “AS16276 OVH SAS” with <code>5</code> IP addresses each.</p>
</li>
<li><p>“AS396982 Google LLC” with <code>4</code> IP addresses.</p>
</li>
</ol>
<h3 id="heading-top-cities">Top Cities</h3>
<ol>
<li><p>Singapore with <code>40</code> IP addresses.</p>
</li>
<li><p>Santa Clara (California, US) with <code>9</code> IP addresses.</p>
</li>
<li><p>Beauharnois (Quebec, Canada) with <code>4</code> IP addresses.</p>
</li>
</ol>
<h2 id="heading-link-shorteners">Link Shorteners</h2>
<ol>
<li><p>TinyURL, with <code>176</code> entries.</p>
</li>
<li><p>Bitly, with <code>161</code> entries.</p>
</li>
<li><p>Twitter’s link shortener (t.co) with <code>160</code> entries.</p>
</li>
<li><p>s.id with <code>115</code> entries.</p>
</li>
<li><p>t.ly with <code>33</code> entries.</p>
</li>
</ol>
<h2 id="heading-ipfs">IPFS</h2>
<p>IPFS is a Web3 technology that allows decentralized hosting of files. These are not directly accessible in a browser, but through gateways like those from Cloudflare, ipfs.io and others.</p>
<p>When I saw the numbers for IPFS, I was genuinely surprised.</p>
<ol>
<li><p>From Cloudflare IPFS alone, there are <code>5,380</code> entries. <sup id="3"><a class="post-section-overview" href="#ft3">3</a></sup>
 (I also want to point out that <a target="_blank" href="https://www.bleepingcomputer.com/news/security/phishing-attacks-distributed-through-cloudflares-ipfs-gateway/">BleepingComputer has an article</a> about Cloudflare’s IPFS gateway being used for phishing attacks, showing its prominence.)</p>
</li>
<li><p>ipfs.io with <code>990</code> entries.</p>
</li>
<li><p>nftstorage.link with <code>805</code> entries.</p>
</li>
<li><p>dweb.link with <code>416</code> entries.</p>
</li>
<li><p>Infura IPFS with <code>366</code> entries.</p>
</li>
</ol>
<h2 id="heading-evasion-methods">Evasion Methods</h2>
<p>These are methods used such that the domain seems innocent but leads to another website (not including link shorteners).</p>
<ol>
<li><p>VKontakte, with <code>39</code> entries – this uses an “away” page that redirects to another website (an account on VKontakte is required).</p>
</li>
<li><p>google.com, with <code>22</code> entries<sup id="4"><a class="post-section-overview" href="#ft4">4</a></sup> with 13 URLs having the path “/url?q=[insert a url-encoded url here]” and <code>9</code> entries using AMP to have a Google domain, but with a phishing page. They’re having their cake and eating it too.</p>
</li>
<li><p>translate.goog (Google Translate for websites), with <code>15</code> entries.</p>
</li>
<li><p>accounts.google.com, with <code>8</code> URLs, all of which contain “continue=” and “followup=” with URLs as the values. This redirects the user after they’re signed in.</p>
</li>
</ol>
<h2 id="heading-uncategorized">Uncategorized</h2>
<p>There are <code>6,593</code> uncategorized domains.</p>
<ol>
<li><p><code>3058</code> domains use a “.com” TLD.</p>
</li>
<li><p><code>437</code> domains use a “.top” TLD.</p>
</li>
<li><p><code>217</code> domains use a “.br” TLD.</p>
</li>
<li><p><code>200</code> domains use a “.net” TLD.</p>
</li>
<li><p><code>186</code> domains use a “.pl” TLD.</p>
</li>
<li><p><code>176</code> domains use a “.xyz” TLD.</p>
</li>
<li><p><code>108</code> domains use a “.org” TLD.</p>
</li>
</ol>
<h2 id="heading-the-big-picture">The Big Picture</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244377160/fdaa8dd3-9f7a-4954-a2ca-36acd0403f45.png" alt="A diagram showing the different means for phishing outlined in this article. Website Hosting/Website Builders is at 11,800. IPFS is at 8,634. Documents is at 1,236. Link Shorteners is at 735. Forms is at 147. Dynamic DNS is at 160." /></p>
<ol>
<li><p><span id="ft1"></span> The filter list has more than 2,100 entries for both <code>firebaseapp.com</code> and <code>web.app</code>, due to the data sources of the filter list including both domains. Websites on both domains have been filtered down to just one, leading to the lower number. <a class="post-section-overview" href="#1">↩</a></p>
</li>
<li><p><span id="ft2"></span> Weebly has two domains which are <code>weebly.com</code> and <code>weeblysite.com</code> for hosted websites, in which none of the entries are duplicates. <a class="post-section-overview" href="#2">↩</a></p>
</li>
<li><p><span id="ft3"></span> This is across two domains (<code>cf-ipfs.com</code> and <code>cloudflare-ipfs.com</code>) with duplicate addresses removed. <a class="post-section-overview" href="#3">↩︎</a></p>
</li>
<li><p><span id="ft4"></span> There were <code>23</code> entries, but one of them was a search, so that’s been excluded. <a class="post-section-overview" href="#4">↩</a></p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[GPT-4 Isn't 95.2% Worse Now]]></title><description><![CDATA[A paper from Stanford & Berkeley University being spread around as proof that “GPT-4 is getting dumber” makes no sense.
Here’s the study: “How Is ChatGPT’s Behavior Changing over Time?”

Let me start off by saying that other users have experienced th...]]></description><link>https://www.tercmd.com/gpt-4-isnt-952-worse-now</link><guid isPermaLink="true">https://www.tercmd.com/gpt-4-isnt-952-worse-now</guid><category><![CDATA[GPT 4]]></category><category><![CDATA[chatgpt]]></category><dc:creator><![CDATA[tercmd]]></dc:creator><pubDate>Thu, 27 Jul 2023 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244383018/bbae4acf-204e-425e-b8de-3f984135c74a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A paper from Stanford &amp; Berkeley University being spread around as proof that “GPT-4 is getting dumber” makes no sense.</p>
<p>Here’s the study: <a target="_blank" href="https://arxiv.org/pdf/2307.09009.pdf">“How Is ChatGPT’s Behavior Changing over Time?”</a></p>
<blockquote>
<p>Let me start off by saying that other users have experienced that it does feel inferior and I don’t doubt that. But quoting a 95.2% drop (in that ONE example) is unfair.</p>
</blockquote>
<p>Let’s address each of the tasks tested for (solving math problems, answering sensitive/dangerous questions, generating code and visual reasoning)</p>
<h2 id="heading-solving-math-problems">Solving Math Problems</h2>
<p>The paper does state that GPT didn’t provide steps in the June version (even with Chain-of-Thought prompting), so perhaps that’s where it failed. This does indicate that it’s worse in some sense, but math is also one of the weak points for AI.</p>
<h2 id="heading-answering-sensitivedangerous-questions">Answering Sensitive/Dangerous Questions</h2>
<p>The rate of answering such questions is something that OpenAI wants to reduce (and so it has). GPT-3.5 did have an increased answer rate in June, which suggests filtering does need to be improved. But answering these (or not) doesn’t mean models are getting dumber.</p>
<h2 id="heading-generating-code">Generating Code</h2>
<p>This section showed GPT-4’s executable code rate reducing, due to the model adding three backticks before the code, thus making it not directly executable.</p>
<p>This wasn’t fair in my opinion since most users use ChatGPT’s web interface, so the model is expected to add code tags to add syntax highlighting and separate it from other model output (if any).</p>
<h2 id="heading-visual-reasoning">Visual Reasoning</h2>
<p>This section just shows both models increasing in rates.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>So, people using the findings of the paper as “GPT-4 is getting dumber” doesn’t make sense at all. (And in case you’re wondering, the “97.6% to 2.4%” prime number example is <a target="_blank" href="https://www.google.com/search?q=%2297.6%22+AND+%222.4%22+AND+%22GPT%22">very, very widespread</a>)</p>
<h2 id="heading-other-links-external">Other links (External)</h2>
<p><a target="_blank" href="https://www.aisnakeoil.com/p/is-gpt-4-getting-worse-over-time">Is GPT-4 getting worse over time? (aisnakeoil.com)</a></p>
]]></content:encoded></item><item><title><![CDATA[Monitoring a Hacker News Post]]></title><description><![CDATA[I posted a link to my repo regarding AI context lengths to Hacker News under the Show HN category and wanted to see its position in the listing every now and then.
API
Fortunately, Hacker News has an API which you can send a request to for it to resp...]]></description><link>https://www.tercmd.com/monitoring-a-hacker-news-post</link><guid isPermaLink="true">https://www.tercmd.com/monitoring-a-hacker-news-post</guid><category><![CDATA[hackernews]]></category><category><![CDATA[slack]]></category><dc:creator><![CDATA[tercmd]]></dc:creator><pubDate>Thu, 25 May 2023 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705246019230/64ad673f-5b7d-4db9-9a4e-d4af6e30f7f5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I posted a link to my <a target="_blank" href="https://l.tercmd.com/gh/ai-memory-overflow">repo regarding AI context lengths</a> to Hacker News under the Show HN category and wanted to see its position in the listing every now and then.</p>
<h2 id="heading-api">API</h2>
<p>Fortunately, Hacker News has an API which you can send a request to for it to respond back with a list of posts. This was a Show HN post, so I needed the <code>/v0/showstories.json</code> API. I implemented simple Python code to request that path and get the index of the post. After this, I calculate a page number by dividing the post index by 30.</p>
<h2 id="heading-message-design">Message Design?</h2>
<p>Slack offers something called Block Kit, which allows you to build detailed messages. I tried a few designs and settled on a regular message. Regardless, if I do need to redesign it, I can just add blocks.</p>
<p>I first went with a button “View Post in List”.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244386324/7376dc3a-4932-4460-bb18-7671f0ea66ff.png" alt="Slack message from app “HN Monitor” with message “ Post #36056445 is #36 on show page 2!” with a button on the side “ View Post in List”" /></p>
<p>This led to some issues.</p>
<p>I eventually added a link that does the same thing, which doesn’t show any errors and works well.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244387924/eac79c6d-7a19-4f45-96b0-d4939f1062eb.png" alt="Slack message from app “HN Monitor” with message “ Post #36056445 is #40 on show page 2!” with a link “View post on page”" /></p>
<h2 id="heading-view-post-in-list-link">“View Post in List” Link</h2>
<p>This is a link to the specific page on HN with an anchor tag of the post ID. This results in the page scrolling to the very post being tracked.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244389541/c9ecdadf-2658-48c7-831e-b511b2fae598.png" alt="Hacker News Post" /></p>
]]></content:encoded></item><item><title><![CDATA[Monitoring my GitHub repo]]></title><description><![CDATA[The Problem
Three weeks ago, I randomly got an issue on my latest GitHub repo. A few days later, I got another one. I felt like I wanted a way to know about issues, PRs, or any other repo activity as soon as it comes in. So, I thought to automate it ...]]></description><link>https://www.tercmd.com/monitoring-my-github-repo</link><guid isPermaLink="true">https://www.tercmd.com/monitoring-my-github-repo</guid><category><![CDATA[GitHub]]></category><dc:creator><![CDATA[tercmd]]></dc:creator><pubDate>Mon, 15 May 2023 06:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244401271/3d3c302e-5f96-4087-a88c-f7b09e8194da.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-problem">The Problem</h2>
<p>Three weeks ago, I randomly got an issue on my <a target="_blank" href="https://l.tercmd.com/gh/everything-chatgpt">latest GitHub repo</a>. A few days later, I got another one. I felt like I wanted a way to know about issues, PRs, or any other repo activity as soon as it comes in. So, I thought to automate it with Slack.</p>
<h2 id="heading-github-actions">GitHub Actions?</h2>
<p>My first thought was to use a GitHub Action. So, I spent a few hours tinkering with a GitHub Action on a test repo to check if it works. In the end, I arrived at a result that worked decently. Although, workflow execution logs are public, allowing <em>anyone</em> to see <em>who</em> and <em>what</em> triggered it.</p>
<p><strong>Why is that a problem?</strong></p>
<p>Because, if a user stars and unstars the repo, it would be visible in the logs. Same for if a user watches and unwatches the repo.</p>
<p>So, that never made it to the final repo.</p>
<h2 id="heading-zapier">Zapier?</h2>
<p>Then, I told ChatGPT (funny, considering the repo) about the issue and it suggested platforms like Zapier. So, I signed up for a Zapier account, oAuth’d into Slack and GitHub, and set up my Zap. I wouldn’t get notifications for every star, but that’s a small compromise.</p>
<p>The only problem the Free plan offers 100 — in this case, uncontrollable — Zap executions per month. That means, at any point, Slack notifications for that month will just <strong>stop</strong>.</p>
<p>So, I didn’t go the Zapier route.</p>
<h2 id="heading-github-integration-for-slack">GitHub integration for Slack</h2>
<p>I was looking for a solution and found a GitHub repo <code>integrations/slack</code>. This was a repo <strong>by GitHub</strong> about their integration for Slack.</p>
<p>Adding it is simple:</p>
<ul>
<li><p>Visit <a target="_blank" href="https://slack.github.com/">slack.github.com</a></p>
</li>
<li><p>Click <code>Add to Slack</code></p>
</li>
<li><p>Add the integration to your workspace, and then add it to a channel</p>
</li>
<li><p>Use this command: <code>/subscribe [repo] [optional additional events to subscribe to]</code></p>
</li>
</ul>
<p>So, I added the integration, subscribed to the repo (linking it to the channel).</p>
<p>When I made a commit, I get a notification like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244397039/afc2659c-9a57-4fe5-98f0-c67abbf26e27.png" alt="Slack message from “GitHub” app showing details of commit" /></p>
<p>A few days later, as I switched my phone on, I got a Slack notification.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244398284/b491c294-9436-45d1-b107-2f6e9cf01a67.png" alt="Slack message from “GitHub” app showing issue details (created by and issue body visible in the image)" /></p>
<p>So, I replied to the message using Slack itself.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1705244399859/716ddef4-809a-4e55-b9d5-40561b82fafa.png" alt="GitHub reply from user with Slack logo in bottom-right corner of the profile picture" /></p>
<p>(I later moved to Discord, with their <a target="_blank" href="https://discord.com/developers/docs/resources/webhook#execute-githubcompatible-webhook">tailor-made webhook integration for GitHub</a> which provides me more data than the Slack integration did)</p>
]]></content:encoded></item></channel></rss>