<?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[Shubham's Dev Journal]]></title><description><![CDATA[Practical, easy-to-read posts on frontend, backend, cloud, security and the wider world of tech, written by a fullstack engineer who builds things and shares what he learns.]]></description><link>https://shubham-works.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6ae831c5fa2e89bfc5ee3061/d4b0246f-ffbe-4e7f-8e49-70fafc9854e0.png</url><title>Shubham&apos;s Dev Journal</title><link>https://shubham-works.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 07 Nov 2026 11:35:19 GMT</lastBuildDate><atom:link href="https://shubham-works.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[I Built a Free AWS Cloud Practitioner (CLF-C02) Exam Simulator: 2,000 Questions, Timed Mocks and an AI Proctor]]></title><description><![CDATA[Practise for AWS CLF-C02 with 2,000 original questions, every answer explained, timed 65-question mock exams and an optional on-device AI proctor.]]></description><link>https://shubham-works.hashnode.dev/i-built-a-free-aws-cloud-practitioner-clf-c02-exam-simulator-2-000-questions-timed-mocks-and-an-ai-proctor</link><guid isPermaLink="true">https://shubham-works.hashnode.dev/i-built-a-free-aws-cloud-practitioner-clf-c02-exam-simulator-2-000-questions-timed-mocks-and-an-ai-proctor</guid><category><![CDATA[AWS]]></category><category><![CDATA[Cloud]]></category><category><![CDATA[AWS certification]]></category><category><![CDATA[AWS Cloud Practitioner]]></category><category><![CDATA[React]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[supabase]]></category><dc:creator><![CDATA[Shubham Chaudhary]]></dc:creator><pubDate>Wed, 04 Nov 2026 14:03:40 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://raw.githubusercontent.com/chaudharyShub/CloudPrep/master/docs/images/landing.png" alt="CloudPrep landing page: " /></p>
<blockquote>
<p><strong>TL;DR:</strong> <strong><a href="https://aws-cloud-prepare.vercel.app/">CloudPrep</a></strong> is a free practice simulator for the <strong>AWS Certified Cloud Practitioner (CLF-C02)</strong> exam. It has 2,000 original questions, an explanation for every option, real-format 65-question / 90-minute mock exams, and an optional on-device AI proctor. No ads, no paywall.</p>
<p>👉 <strong>Try it:</strong> <a href="https://aws-cloud-prepare.vercel.app/">aws-cloud-prepare.vercel.app</a></p>
</blockquote>
<hr />
<h2>🤔 Why I built it</h2>
<p>CloudPrep started as my own preparation tool for the CLF-C02 exam. I wanted practice that felt like the <strong>real exam</strong>: the same length, the same timing, the same pressure. I also wanted every answer explained and backed by an AWS source.</p>
<p>So I built it, and then made it a free, public product so anyone preparing for the exam can use it too.</p>
<hr />
<h2>📊 CloudPrep at a glance</h2>
<table>
<thead>
<tr>
<th align="left">📚 2,000</th>
<th align="left">⏱️ 65 / 90</th>
<th align="left">🧩 4</th>
<th align="left">💸 Free</th>
</tr>
</thead>
<tbody><tr>
<td align="left">original questions, each option explained</td>
<td align="left">questions and minutes, like the real exam</td>
<td align="left">exam domains, weighted like the official guide</td>
<td align="left">no ads, no paywall</td>
</tr>
</tbody></table>
<p>The questions are split across the four CLF-C02 domains:</p>
<table>
<thead>
<tr>
<th align="left">Domain</th>
<th align="center">Exam weight</th>
<th align="center">Questions in CloudPrep</th>
</tr>
</thead>
<tbody><tr>
<td align="left">1. Cloud Concepts</td>
<td align="center">24%</td>
<td align="center">400</td>
</tr>
<tr>
<td align="left">2. Security and Compliance</td>
<td align="center">30%</td>
<td align="center">600</td>
</tr>
<tr>
<td align="left">3. Cloud Technology and Services</td>
<td align="center">34%</td>
<td align="center">753</td>
</tr>
<tr>
<td align="left">4. Billing, Pricing, and Support</td>
<td align="center">12%</td>
<td align="center">247</td>
</tr>
</tbody></table>
<hr />
<h2>✨ What you can do with it</h2>
<h3>1. 📖 Learning mode: practise at your own pace</h3>
<p><img src="https://raw.githubusercontent.com/chaudharyShub/CloudPrep/master/docs/images/learning-mode.png" alt="Learning mode showing a checked answer with its explanation" /></p>
<ul>
<li><strong>Untimed practice</strong> by domain or topic</li>
<li><strong>Instant feedback</strong> with an explanation for <em>every</em> option, not just the correct one</li>
<li><strong>Links to the AWS documentation</strong> each question is based on</li>
<li><strong>Bookmarks</strong> for questions you want to revisit</li>
<li><strong>Review due:</strong> missed questions come back on a schedule so you see them again before you forget them</li>
</ul>
<pre><code class="language-text">Review schedule:  1 day → 3 days → 7 days → 21 days → 60 days
</code></pre>
<blockquote>
<p>💡 Learning mode works <strong>without an account</strong>. Just open the site and start practising.</p>
</blockquote>
<h3>2. ⏱️ Timed mock exams: the real thing, before the real thing</h3>
<p><img src="https://raw.githubusercontent.com/chaudharyShub/CloudPrep/master/docs/images/mock-exam-dark.png" alt="A mock exam question with the timer and answer sheet in dark mode" /></p>
<ul>
<li><strong>65 questions in 90 minutes</strong>, following the CLF-C02 domain weightings</li>
<li><strong>Flag questions</strong>, jump around with a <strong>question grid</strong>, and do a <strong>final review</strong></li>
<li><strong>Auto-submit</strong> when the timer runs out</li>
<li>An <strong>exam-paper style answer sheet</strong></li>
<li>The timer counts down to a fixed end time, so it <strong>survives reloads</strong></li>
</ul>
<h3>3. 📈 Results that tell you what to fix</h3>
<ul>
<li>Practice score with a <strong>per-domain breakdown</strong></li>
<li><strong>Answer review</strong> with explanations</li>
<li><strong>Exam history</strong> and progress guidance across attempts</li>
<li>The dashboard <strong>flags your weakest domain</strong> and shows the time used in recent exams</li>
</ul>
<h3>4. 🤖 AI-proctored exams (opt-in)</h3>
<p>Want to train under real exam conditions? Turn on the AI proctor.</p>
<ul>
<li>A <strong>check-in</strong>, like an online proctored exam</li>
<li><strong>MediaPipe face and object models</strong> plus microphone loudness, all running <strong>on your device</strong></li>
<li>It watches your face, head, eyes and mouth, looks for <strong>phones, other people, books and screens</strong>, and blocks shortcuts</li>
<li><strong>Two warnings, then the exam ends.</strong> A phone or another person ends it at once.</li>
</ul>
<blockquote>
<p>🔒 <strong>Nothing is recorded or uploaded.</strong> It all runs in your browser.</p>
</blockquote>
<p>There's also a lighter <strong>strict mode</strong> that asks for fullscreen and logs tab and focus changes, without judging them.</p>
<h3>5. ☁️ Sync across devices</h3>
<ul>
<li><strong>Sign in with Google</strong> to save your progress, bookmarks, exam history, the exam in progress and settings to your account</li>
<li>Everything <strong>follows you to every device</strong></li>
<li>A copy is kept in the browser, so it <strong>stays fast and works offline</strong>. Changes sync when you're back online.</li>
</ul>
<h3>6. 🎨 Designed for long study sessions</h3>
<ul>
<li>Light theme and a <strong>true-black dark theme</strong></li>
<li>Serif titles, rounded cards and an exam-paper answer sheet</li>
<li><strong>Add to Home Screen</strong> support with its own icons</li>
<li>Automated <strong>axe accessibility checks (WCAG 2.2 AA rules)</strong> pass on every page, in both themes</li>
</ul>
<hr />
<h2>👥 Guest vs signed in</h2>
<table>
<thead>
<tr>
<th align="left">Feature</th>
<th align="center">Guest</th>
<th align="center">Signed in (Google)</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Learning mode</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="left">Bookmarks and settings</td>
<td align="center">✅ (this browser only)</td>
<td align="center">✅ (synced)</td>
</tr>
<tr>
<td align="left">Timed mock exams</td>
<td align="center">❌</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="left">Dashboard, history and progress</td>
<td align="center">❌</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="left">Sync across devices</td>
<td align="center">❌</td>
<td align="center">✅</td>
</tr>
</tbody></table>
<hr />
<h2>🛠️ Tech stack</h2>
<p>For the developers reading this, here's what's under the hood:</p>
<table>
<thead>
<tr>
<th align="left">Layer</th>
<th align="left">Tech</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>Frontend</strong></td>
<td align="left">React 19, TypeScript (strict), Vite 8, Tailwind CSS 4, React Router</td>
</tr>
<tr>
<td align="left"><strong>Backend</strong></td>
<td align="left">Supabase only: Postgres with Row Level Security + Supabase Auth (Google). No separate API server.</td>
</tr>
<tr>
<td align="left"><strong>Data</strong></td>
<td align="left">Questions loaded at runtime and cached in <strong>IndexedDB</strong>; user data in a versioned <code>localStorage</code> layer, synced to Supabase when signed in</td>
</tr>
<tr>
<td align="left"><strong>AI proctor</strong></td>
<td align="left">MediaPipe face and object models, running on-device</td>
</tr>
<tr>
<td align="left"><strong>Testing</strong></td>
<td align="left">Vitest + Testing Library (database schema and security rules run in <strong>PGlite</strong>), Playwright + axe for E2E and accessibility</td>
</tr>
<tr>
<td align="left"><strong>Hosting</strong></td>
<td align="left">Vercel (static SPA)</td>
</tr>
</tbody></table>
<hr />
<h2>🧾 Honest notes</h2>
<p>I'd rather you trust it for the right reasons, so:</p>
<ul>
<li>⚠️ CloudPrep is <strong>not affiliated with or endorsed by AWS</strong>. A practice score doesn't predict or guarantee an official result.</li>
<li>📊 It shows the <strong>percentage you got right</strong>. It does not estimate AWS's scaled 100–1,000 score.</li>
<li>✍️ The questions are <strong>original</strong>, written with AI assistance from public AWS documentation and the official exam guide, then checked against the pages they cite. They are <strong>not</strong> copied from the real exam or from dumps.</li>
<li>✅ 1,995 of the 2,000 questions are checked against their AWS sources; 5 are still marked unverified.</li>
</ul>
<hr />
<h2>🚀 Try it and tell me what you think</h2>
<p>If you're preparing for the AWS Cloud Practitioner exam, give it a go:</p>
<h3>👉 <a href="https://aws-cloud-prepare.vercel.app/">aws-cloud-prepare.vercel.app</a></h3>
<p>I'd love your feedback:</p>
<ul>
<li>💬 Found a question that's wrong or unclear? Tell me in the comments.</li>
<li>🐛 Something broken on your browser or device? Let me know.</li>
<li>⭐ If it helps you, share it with someone who's preparing for the exam.</li>
</ul>
<p>Good luck with your exam! ☁️</p>
<hr />
<p>*Preparing for frontend interviews too? Check out <strong><a href="https://frontend-interview-prepare.vercel.app/">Frontend Interview Prep</a></strong>, with React and JavaScript interview questions and answers.*</p>
<p><em>Made by Shubham Chaudhary · <a href="https://shubhamworks.vercel.app/">Portfolio</a></em></p>
]]></content:encoded></item><item><title><![CDATA[🛡️Your node_modules Is an Attack Surface: A Practical npm Security Checklist for 2026]]></title><description><![CDATA[Axios, TanStack, Shai-Hulud: the 2026 npm supply chain attacks explained, plus a practical checklist to secure your installs, CI/CD and dependencies.]]></description><link>https://shubham-works.hashnode.dev/your-node-modules-is-an-attack-surface-a-practical-npm-security-checklist-for-2026</link><guid isPermaLink="true">https://shubham-works.hashnode.dev/your-node-modules-is-an-attack-surface-a-practical-npm-security-checklist-for-2026</guid><category><![CDATA[npm]]></category><category><![CDATA[Security]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[webdev]]></category><category><![CDATA[pnpm]]></category><category><![CDATA[best practices]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Shubham Chaudhary]]></dc:creator><pubDate>Tue, 03 Nov 2026 07:30:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae831c5fa2e89bfc5ee3061/fc85cfb5-0b70-4589-bf98-fb232d112a63.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>On March 31, 2026, someone hijacked the npm account of axios's lead maintainer. Axios is an HTTP client with over 100 million weekly downloads. The attacker skipped the project's CI pipeline and published two malicious versions directly from the npm CLI.</p>
<p>Those versions didn't touch axios's own code. They added one new dependency, <code>plain-crypto-js</code>, which existed only to run a <code>postinstall</code> script. That script contacted a command-and-control server and dropped a remote access trojan on the machine.</p>
<p>All of this happened during <code>npm install</code>, before any application code ran.</p>
<p>The malicious versions were live for about four hours. Any fresh install or auto-update in that window could have pulled them.</p>
<blockquote>
<p>💀 <strong>You didn't install malware. You ran <code>npm install</code>.</strong></p>
</blockquote>
<p>This post covers what happened in 2026, how these attacks work, and a checklist you can apply to your project in about 30 minutes. 👇</p>
<hr />
<h2>📅 2026 So Far: A Quick Recap</h2>
<p>Axios wasn't a one-off. Here are some of the year's major incidents:</p>
<table>
<thead>
<tr>
<th>Date</th>
<th>Target</th>
<th>How</th>
</tr>
</thead>
<tbody><tr>
<td>Mar 31</td>
<td><code>axios</code></td>
<td>Maintainer account hijacked; malicious dependency with a postinstall script</td>
</tr>
<tr>
<td>Apr 22</td>
<td><code>@bitwarden/cli</code></td>
<td>Preinstall script downloaded the Bun runtime and ran a credential stealer</td>
</tr>
<tr>
<td>Apr 29</td>
<td>SAP CAP packages (<code>@cap-js/*</code>, <code>mbt</code>)</td>
<td>"Mini Shai-Hulud" worm, preinstall hook</td>
</tr>
<tr>
<td>May 11</td>
<td><code>@tanstack/*</code> and others</td>
<td>Abused a <code>pull_request_target</code> workflow and poisoned the GitHub Actions cache to steal an OIDC token; published with valid provenance</td>
</tr>
<tr>
<td>May 19</td>
<td><code>@antv/*</code></td>
<td>Compromised maintainer account; ~639 malicious versions across 323 packages in about an hour</td>
</tr>
<tr>
<td>Jun 1</td>
<td><code>@redhat-cloud-services/*</code></td>
<td>Compromised employee GitHub account pushed commits that bypassed code review</td>
</tr>
<tr>
<td>Jun 3</td>
<td>57+ packages</td>
<td><code>binding.gyp</code> campaign; 286+ malicious versions in under two hours</td>
</tr>
<tr>
<td>Jul 14</td>
<td>AsyncAPI packages</td>
<td>Commits pushed to unprotected pre-release branches; CI exposed <code>NPM_TOKEN</code></td>
</tr>
</tbody></table>
<p>📈 The pattern is clear: attacks are faster, automated, and increasingly target the CI pipeline instead of just individual developers.</p>
<hr />
<h2>🧠 How These Attacks Actually Work</h2>
<p>Most incidents combine a few of these techniques.</p>
<h3>1. 🔓 Account Takeover</h3>
<p>A maintainer gets phished or has a token leaked, and the attacker publishes a "new version" of a trusted package.</p>
<p>Your <code>^1.2.0</code> range happily accepts it.</p>
<h3>2. 📜 Install Scripts</h3>
<p>npm runs <code>preinstall</code>, <code>install</code>, and <code>postinstall</code> scripts automatically.</p>
<p>That means arbitrary code on your machine or CI runner, with access to your environment variables, SSH keys, and cloud credentials.</p>
<h3>3. 🪱 Self-Replication (Worms)</h3>
<p>The Shai-Hulud family steals npm tokens and GitHub tokens, then uses them to publish infected versions of other packages the victim maintains.</p>
<p>One compromised developer becomes dozens of compromised packages.</p>
<h3>4. 🏭 CI/CD Compromise</h3>
<p>The TanStack attack needed no stolen password.</p>
<p>The attacker abused a GitHub Actions workflow to extract a publishing token from the runner. Because the package was built by the real pipeline, it carried valid provenance.</p>
<h3>5. 🎭 Typosquatting and Dependency Confusion</h3>
<p>Look-alike package names (<code>reacct-dom</code>), or a public package with the same name as your private one.</p>
<h3>🕵️ What Do Attackers Steal?</h3>
<ul>
<li>🔑 npm and GitHub tokens</li>
<li>☁️ Cloud credentials (AWS, GCP, Azure)</li>
<li>☸️ Kubernetes tokens</li>
<li>🗝️ SSH keys</li>
<li>⚙️ CI/CD secrets</li>
<li>📄 <code>.env</code> contents</li>
<li>🔐 Password manager data</li>
<li>🤖 AI tool configuration files</li>
</ul>
<p>Some of the 2026 attack waves targeted credentials and configuration data beyond traditional development secrets.</p>
<hr />
<h2>📋 The Checklist</h2>
<p>Here are practical steps you can take to reduce your exposure.</p>
<h2>1. 💻 Local Development</h2>
<h3>🔒 Commit Your Lockfile, and Review It</h3>
<p><code>package-lock.json</code> (or <code>pnpm-lock.yaml</code> / <code>yarn.lock</code>) pins exact versions of your whole dependency tree.</p>
<p>Treat lockfile changes in a PR as code changes.</p>
<p>A new, unfamiliar transitive dependency (like <code>plain-crypto-js</code>) is a red flag. 🚩</p>
<h3>🚫 Turn Off Install Scripts by Default</h3>
<p>Most attacks this year relied on <code>preinstall</code> or <code>postinstall</code>.</p>
<p>Disable them:</p>
<p><strong><code>.npmrc</code></strong></p>
<pre><code class="language-ini">ignore-scripts=true
</code></pre>
<p>Some packages genuinely need build scripts (native modules like <code>sharp</code> or <code>bcrypt</code>).</p>
<p>Run their scripts explicitly after install:</p>
<pre><code class="language-bash">npm rebuild sharp --ignore-scripts=false
</code></pre>
<p>If you use pnpm, it already doesn't run dependency build scripts by default (since v10). Approve only the ones you trust with:</p>
<pre><code class="language-bash">pnpm approve-builds
</code></pre>
<p>Bun also skips lifecycle scripts unless a package is in <code>trustedDependencies</code>.</p>
<h3>⏳ Add a Release Cooldown</h3>
<p>Malicious versions are usually caught within hours.</p>
<p>The axios versions were flagged about six minutes after publishing and contained within roughly four hours.</p>
<p>If your package manager refuses versions newer than a day or two, you skip most of that window.</p>
<p>All major package managers now support this, but each uses a different unit:</p>
<p><strong>📦 npm 11.10+ (<code>.npmrc</code>) — value in DAYS</strong></p>
<pre><code class="language-ini">min-release-age=2
</code></pre>
<p><strong>📦 pnpm 10.16+ (<code>pnpm-workspace.yaml</code>) — value in MINUTES</strong></p>
<pre><code class="language-yaml"># pnpm 11 defaults to 1440 (24 hours)
minimumReleaseAge: 2880
</code></pre>
<p><strong>📦 Yarn 4.10+ (<code>.yarnrc.yml</code>) — value in MINUTES</strong></p>
<pre><code class="language-yaml">npmMinimalAgeGate: 2880
</code></pre>
<p><strong>📦 Bun (<code>bunfig.toml</code>) — value in SECONDS</strong></p>
<pre><code class="language-toml">[install]
minimumReleaseAge = 172800
</code></pre>
<p>Need an urgent hotfix? 🔥</p>
<p>Override for one install, e.g.:</p>
<pre><code class="language-bash">npm i --min-release-age 0 package@version
</code></pre>
<h3>🎯 Be Deliberate With Version Ranges</h3>
<p><code>^</code> and <code>~</code> let new versions in automatically.</p>
<p>For critical or high-privilege dependencies, consider exact versions and upgrade on purpose.</p>
<hr />
<h2>2. 🔍 Auditing</h2>
<h3>🩺 Run <code>npm audit</code> Regularly</h3>
<p>It catches known vulnerabilities, not fresh malware, but it's still a baseline.</p>
<h3>✍️ Verify Signatures and Provenance</h3>
<pre><code class="language-bash">npm audit signatures
</code></pre>
<p>This checks registry signatures and provenance attestations for your installed packages.</p>
<p>The axios malicious releases were missing the GitHub Actions provenance that legitimate releases had, which was a key signal.</p>
<blockquote>
<p><strong>🚨 But don't treat provenance as proof of safety.</strong></p>
</blockquote>
<p>The TanStack and Red Hat packages had valid provenance because the attackers compromised the real build pipeline.</p>
<h3>📡 Add a Dependency Scanner</h3>
<p>Tools like Socket, Snyk, or GitHub's Dependabot alerts watch for suspicious behavior (new install scripts, network calls, obfuscated code) and known-malicious packages.</p>
<p>Socket flagged the axios release within minutes.</p>
<h3>🤖 Make Your Update Bots Wait Too</h3>
<p>Dependabot and Renovate can open PRs, and trigger CI installs, before a human looks.</p>
<p>Give them a cooldown:</p>
<p><strong><code>.github/dependabot.yml</code></strong></p>
<pre><code class="language-yaml">version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    cooldown:
      default-days: 2
</code></pre>
<p><strong><code>renovate.json</code></strong></p>
<pre><code class="language-json">{
  "minimumReleaseAge": "2 days"
}
</code></pre>
<p>Both still let security updates through without the delay.</p>
<hr />
<h2>3. 🏗️ CI/CD Security</h2>
<h3>🧊 Use Deterministic Installs</h3>
<p>Use the appropriate install command for your package manager:</p>
<pre><code class="language-bash">npm ci                              # npm
pnpm install --frozen-lockfile      # pnpm
yarn install --immutable            # yarn
</code></pre>
<p>These fail if the lockfile and <code>package.json</code> disagree, instead of silently resolving new versions.</p>
<h3>🙈 Keep Secrets Away From the Install Step</h3>
<p>Don't expose deploy keys or cloud credentials as environment variables during <code>npm ci</code>.</p>
<p>Inject them only into the steps that need them.</p>
<h3>⏱️ Use Short-Lived, Least-Privilege Tokens</h3>
<p>Avoid long-lived npm tokens in CI.</p>
<p>If you publish packages, use npm Trusted Publishing (OIDC) and enable 2FA on your npm account.</p>
<h3>🧱 Harden GitHub Actions</h3>
<ul>
<li>⚡ Be very careful with <code>pull_request_target</code>. It runs with your repo's secrets, and checking out PR code inside it is how the TanStack attack started.</li>
<li>📌 Pin third-party actions to a full commit SHA, not a tag.</li>
<li>🌿 Protect all branches that can trigger publishing, including <code>next</code> or pre-release branches (the AsyncAPI attack used one).</li>
</ul>
<h3>🌐 Restrict Outbound Network Access</h3>
<p>Restrict outbound network access from CI runners where you can, so a payload can't reach its command-and-control server.</p>
<hr />
<h2>4. 🚑 Have a Response Plan</h2>
<p>If a package you use is compromised:</p>
<ol>
<li><p>🔎 <strong>Identify exposure.</strong> Check whether any lockfile in your projects resolved to an affected version, and when your machines or CI ran an install.</p>
</li>
<li><p>🧯 <strong>Isolate before rotating.</strong> Some 2026 payloads (the TanStack wave) installed a background service that deletes your home directory if it detects the stolen GitHub token was revoked. Clean or reimage affected machines first, then rotate.</p>
</li>
<li><p>🔄 <strong>Rotate everything the install could see:</strong> npm tokens, GitHub tokens, cloud keys, SSH keys, and CI/CD secrets.</p>
</li>
<li><p>🧹 <strong>Rebuild from a clean lockfile</strong> pinned to a known-good version.</p>
</li>
<li><p>🎣 <strong>Hunt for unexpected repos, workflows, or package versions</strong> published from your accounts.</p>
</li>
</ol>
<hr />
<h2>🧰 Putting It Together: A Hardened Starter Setup</h2>
<p>Here's a minimal setup you can drop into a project today.</p>
<h3>⚙️ <code>.npmrc</code></h3>
<pre><code class="language-ini">ignore-scripts=true
min-release-age=2
save-exact=true
</code></pre>
<h3>🔁 <code>.github/workflows/ci.yml</code> (Relevant Parts)</h3>
<pre><code class="language-yaml">name: CI

on:
  pull_request:        # not pull_request_target
  push:
    branches: [main]

permissions:
  contents: read       # least privilege by default

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@&lt;full-commit-sha&gt;
      - uses: actions/setup-node@&lt;full-commit-sha&gt;
        with:
          node-version: 22
          cache: npm

      - name: Install (no secrets in this step)
        run: npm ci

      - name: Verify signatures
        run: npm audit signatures

      - name: Rebuild trusted native deps
        run: npm rebuild sharp --ignore-scripts=false

      - name: Build &amp; test
        run: npm run build &amp;&amp; npm test
</code></pre>
<p>It isn't bulletproof. Nothing is.</p>
<p>But it closes the paths that most 2026 attacks actually used: install scripts, brand-new malicious versions, silent version drift, and over-privileged CI. 💪</p>
<hr />
<h2>🏁 Key Takeaways</h2>
<ul>
<li>🚪 <strong>Install scripts are the #1 entry point.</strong> Disable them by default and allowlist the few you need.</li>
<li>⌛ <strong>Time is your cheapest defense.</strong> A 1–2 day cooldown would have skipped most of this year's malicious releases.</li>
<li>🔐 <strong>Lockfiles plus <code>npm ci</code> stop silent changes</strong> to your dependency tree.</li>
<li>🧾 <strong>Provenance helps, but it isn't proof.</strong> Compromised pipelines can produce validly signed malware.</li>
<li>🎯 <strong>Your CI is a target.</strong> Least-privilege tokens, careful workflow triggers, and SHA-pinned actions matter.</li>
</ul>
<hr />
<p>The next time you run <code>npm install</code>, remember: you're not just installing code. You're trusting every package in your dependency tree to execute safely.</p>
<p>🛡️ <strong>Secure your dependencies before attackers turn them into your weakest link.</strong></p>
]]></content:encoded></item><item><title><![CDATA[JavaScript: The Language That Keeps Developers Humble 😂]]></title><description><![CDATA[Explore JavaScript's strangest behaviours, from [] + [] to NaN !== NaN. Discover why type coercion and unexpected quirks keep developers humble!]]></description><link>https://shubham-works.hashnode.dev/javascript-the-language-that-keeps-developers-humble</link><guid isPermaLink="true">https://shubham-works.hashnode.dev/javascript-the-language-that-keeps-developers-humble</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[programming languages]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[frontend]]></category><dc:creator><![CDATA[Shubham Chaudhary]]></dc:creator><pubDate>Mon, 02 Nov 2026 11:34:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae831c5fa2e89bfc5ee3061/fe7bac09-aa2c-40db-8c9f-551e5a999327.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you've ever worked with JavaScript, you probably know one thing:</p>
<p><strong>JavaScript doesn't always do what you expect. And sometimes, it does exactly what you asked, but still manages to surprise you.</strong></p>
<p>I've been working with JavaScript for quite some time, and even today, there are moments when I look at the output, stare at my screen for a few seconds, and question my entire understanding of programming.</p>
<p>Let's look at some examples.</p>
<h2>1. The famous <code>[] + []</code></h2>
<pre><code class="language-javascript">console.log([] + []); // ""
</code></pre>
<p>Wait, what?</p>
<p>Two empty arrays added together give us an empty string?</p>
<p>That's because JavaScript converts arrays into primitive values during the addition operation. An empty array converts to an empty string.</p>
<p>JavaScript: "You asked me to add two arrays. I did something. Don't ask questions."</p>
<h2>2. Adding an array to an object</h2>
<pre><code class="language-javascript">console.log([] + {}); // "[object Object]"
console.log({} + []); // 0 (in some contexts)
</code></pre>
<p>Yes, the order matters!</p>
<p>The second example can be interpreted as an empty block followed by a unary plus expression, depending on the execution context.</p>
<p>At this point, we're not writing JavaScript. We're negotiating with it.</p>
<h2>3. <code>typeof null</code> is "object"</h2>
<pre><code class="language-javascript">console.log(typeof null); // "object"
</code></pre>
<p>Null represents the intentional absence of a value.</p>
<p>So why does JavaScript tell us it's an object?</p>
<p>Well, it's a historical bug from JavaScript's early implementation that was kept for backward compatibility.</p>
<p>And here we are, decades later, still explaining it in interviews.</p>
<p>Some bugs never get fired. They just become features.</p>
<h2>4. NaN is not equal to itself</h2>
<pre><code class="language-javascript">console.log(NaN === NaN); // false
</code></pre>
<p>Imagine introducing two identical things and being told they aren't equal.</p>
<p>That's exactly what happens here.</p>
<p><code>NaN</code> stands for Not-a-Number, and according to the IEEE 754 floating-point standard, NaN is not equal to anything, including itself.</p>
<p>Thankfully, JavaScript gives us <code>Number.isNaN()</code>.</p>
<pre><code class="language-javascript">console.log(Number.isNaN(NaN)); // true
</code></pre>
<p>At least someone understands NaN.</p>
<h2>5. Empty arrays are truthy</h2>
<pre><code class="language-javascript">if ([]) {
  console.log("JavaScript says YES!");
}
</code></pre>
<p>Output:</p>
<pre><code class="language-text">JavaScript says YES!
</code></pre>
<p>An empty array is truthy. So is an empty object.</p>
<pre><code class="language-javascript">Boolean([]); // true
Boolean({}); // true
Boolean(""); // false
Boolean(0);  // false
</code></pre>
<p>Coming from other programming languages, this can be a little confusing.</p>
<p>An empty array doesn't mean false in JavaScript. It means, "I'm empty, but I'm still participating."</p>
<h2>6. <code>0.1 + 0.2 !== 0.3</code></h2>
<pre><code class="language-javascript">console.log(0.1 + 0.2);         // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
</code></pre>
<p>This one has probably frustrated almost every JavaScript developer at some point.</p>
<p>The reason is floating-point precision. Computers represent many decimal numbers using binary floating-point, and some values cannot be represented exactly.</p>
<p>So, JavaScript isn't necessarily wrong here. It's dealing with how floating-point numbers work.</p>
<p>Still, imagine explaining this to a client:</p>
<p>"Your total is ₹0.30000000000000004. It's a feature of mathematics."</p>
<h2>7. <code>[] == ![]</code></h2>
<pre><code class="language-javascript">console.log([] == ![]); // true
</code></pre>
<p>Read that again.</p>
<p>An empty array is loosely equal to its own negation.</p>
<p>Why?</p>
<ul>
<li><code>[]</code> is a truthy value.</li>
<li><code>![]</code> becomes <code>false</code>.</li>
<li>During loose equality comparison, JavaScript performs type coercion.</li>
<li>The empty array eventually converts to an empty string, and then to <code>0</code>.</li>
<li><code>false</code> also converts to <code>0</code>.</li>
</ul>
<p>And that's how we get <code>true</code>.</p>
<p>If this doesn't make you appreciate strict equality (<code>===</code>), I don't know what will.</p>
<hr />
<h2>But is JavaScript actually bad?</h2>
<p>Not really.</p>
<p>Most of these behaviours have explanations. Some come from historical decisions, some from type coercion, and others from the underlying rules of computer science.</p>
<p>JavaScript is incredibly powerful. Its flexibility is one of the reasons it became so popular.</p>
<p>But that flexibility comes with responsibility.</p>
<p>Here are a few things I personally try to follow:</p>
<ul>
<li>Prefer <code>===</code> over <code>==</code>.</li>
<li>Use <code>Number.isNaN()</code> when checking for NaN.</li>
<li>Understand truthy and falsy values.</li>
<li>Be careful with implicit type coercion.</li>
<li>Use TypeScript when stronger type safety is needed.</li>
<li>Never assume JavaScript will behave like another programming language.</li>
</ul>
<h2>Final thoughts</h2>
<p>The funny thing about JavaScript is that the more you learn, the more you realise how much there is still to learn.</p>
<p>Sometimes it makes development easier. Sometimes it makes debugging unnecessarily interesting.</p>
<p>And sometimes, it just makes you question why you chose frontend development in the first place. 😂</p>
<p>But that's also what makes learning JavaScript fun.</p>
<p><strong>What's your favourite JavaScript WTF moment?</strong></p>
<p>Drop it in the comments. I'd love to add a few more to this list!</p>
<hr />
<p><em>P.S. This is my first post here on Hashnode. I thought there couldn't be a better way to start than talking about the language that has given me both a career and countless debugging headaches.</em> 🚀</p>
]]></content:encoded></item></channel></rss>