<?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: Visagan S</title>
    <description>The latest articles on DEV Community by Visagan S (@visagans).</description>
    <link>https://dev.to/visagans</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3844961%2Ff8184b07-347b-48c3-a48e-72638a4406a0.png</url>
      <title>DEV Community: Visagan S</title>
      <link>https://dev.to/visagans</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/visagans"/>
    <language>en</language>
    <item>
      <title>I Built a GlassWorm Detector — Here's How Invisible Unicode Attacks Actually Work</title>
      <dc:creator>Visagan S</dc:creator>
      <pubDate>Thu, 26 Mar 2026 16:08:57 +0000</pubDate>
      <link>https://dev.to/visagans/i-built-a-glassworm-detector-heres-how-invisible-unicode-attacks-actually-work-151n</link>
      <guid>https://dev.to/visagans/i-built-a-glassworm-detector-heres-how-invisible-unicode-attacks-actually-work-151n</guid>
      <description>&lt;p&gt;Last week, I opened a VS Code extension file that looked perfectly normal. Five lines of clean JavaScript. A standard &lt;code&gt;import&lt;/code&gt;, an &lt;code&gt;activate&lt;/code&gt; function, a &lt;code&gt;console.log&lt;/code&gt;. Nothing suspicious.&lt;/p&gt;

&lt;p&gt;Except line 2 — an empty line — was carrying 246 bytes of hidden malicious code.&lt;/p&gt;

&lt;p&gt;Not obfuscated. Not minified. Not buried in a dependency. &lt;strong&gt;Literally invisible.&lt;/strong&gt; The characters were in the file, taking up space on disk, but my editor rendered them as nothing. A blank line. Empty air.&lt;/p&gt;

&lt;p&gt;That's GlassWorm — the first self-propagating worm to use invisible Unicode characters to hide malware in VS Code extensions. It has infected 35,800+ machines across 5 waves since October 2025, compromised 151+ GitHub repositories, and as of March 2026, it's still spreading.&lt;/p&gt;

&lt;p&gt;I spent the past week reverse-engineering the encoding technique, building detection tools, and creating an interactive educational demo. Everything is open-sourced. This article walks through what I found.&lt;/p&gt;




&lt;h2&gt;
  
  
  The trick in 60 seconds
&lt;/h2&gt;

&lt;p&gt;Every character you type has a number — a Unicode code point. The letter &lt;code&gt;A&lt;/code&gt; is &lt;code&gt;U+0041&lt;/code&gt;. A space is &lt;code&gt;U+0020&lt;/code&gt;. Your editor reads these numbers and draws something on screen.&lt;/p&gt;

&lt;p&gt;But Unicode also has characters that are assigned numbers &lt;strong&gt;but draw nothing&lt;/strong&gt;. They exist in the file. They take up bytes on disk. But when your editor tries to render them, it produces zero pixels. No dot, no space, no whitespace indicator — absolutely nothing.&lt;/p&gt;

&lt;p&gt;GlassWorm abuses 16 of these characters, called &lt;strong&gt;Variation Selectors&lt;/strong&gt; (&lt;code&gt;U+FE00&lt;/code&gt; through &lt;code&gt;U+FE0F&lt;/code&gt;), to encode entire JavaScript payloads that pass every code review, every diff tool, and every linter in existence.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the encoding actually works
&lt;/h2&gt;

&lt;p&gt;I'm going to walk through this with a real example. Our payload is a harmless &lt;code&gt;console.log&lt;/code&gt;, but the technique is identical to what GlassWorm uses to steal your GitHub tokens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Every character is a byte
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'c' = 0x63    'o' = 0x6F    'n' = 0x6E    's' = 0x73
'o' = 0x6F    'l' = 0x6C    'e' = 0x65    '.' = 0x2E
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing surprising. Each character maps to a byte value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Split each byte into two nibbles
&lt;/h3&gt;

&lt;p&gt;A nibble is 4 bits — half a byte. It can hold values 0 through 15 (0x0 to 0xF). Why does this matter? Because the next step only gives us 16 invisible characters to work with — exactly the range a nibble covers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'c' = 0x63 = 01100011
                │         │
        ┌───────┘         └───────┐
   High nibble: 6            Low nibble: 3
   (byte &amp;gt;&amp;gt; 4)               (byte &amp;amp; 0x0F)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two simple bitwise operations: shift right 4 for the high half, AND with &lt;code&gt;0x0F&lt;/code&gt; for the low half.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Add 0xFE00 to each nibble
&lt;/h3&gt;

&lt;p&gt;This is the core trick. Nibble &lt;code&gt;6&lt;/code&gt; becomes &lt;code&gt;6 + 0xFE00 = U+FE06&lt;/code&gt;. Nibble &lt;code&gt;3&lt;/code&gt; becomes &lt;code&gt;3 + 0xFE00 = U+FE03&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;U+FE06&lt;/code&gt; and &lt;code&gt;U+FE03&lt;/code&gt; are Variation Selector characters. They are &lt;strong&gt;real Unicode characters&lt;/strong&gt; — they exist in the file, they consume 3 bytes each on disk (UTF-8 encoding: &lt;code&gt;EF B8 86&lt;/code&gt; and &lt;code&gt;EF B8 83&lt;/code&gt;) — but every mainstream editor, terminal, and diff tool renders them as &lt;strong&gt;absolutely nothing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So the letter &lt;code&gt;'c'&lt;/code&gt; has become two invisible characters. Do this for every byte in your payload, and the entire thing vanishes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Inject into the file
&lt;/h3&gt;

&lt;p&gt;The invisible payload gets placed on what appears to be a blank line inside a normal-looking VS Code extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What VS Code shows you:          What's actually in the file:
─────────────────────────         ────────────────────────────
1 │ import * as vscode ...        1 │ import * as vscode ...
2 │                               2 │ ⚠ 82 INVISIBLE CHARACTERS
3 │ export function activate()    3 │ export function activate()
4 │   console.log('activated')    4 │   console.log('activated')
5 │ }                             5 │ }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line 2 looks empty. It's not. It's carrying your entire malicious payload — 82 invisible characters encoding 41 bytes of executable JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: The decoder brings it back
&lt;/h3&gt;

&lt;p&gt;A small JavaScript function — visible in the source but innocuous-looking — reads each pair of invisible characters and reverses the encoding:&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;function&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&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;high&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;codePointAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mh"&gt;0xFE00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// invisible → nibble&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;low&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;codePointAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mh"&gt;0xFE00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;high&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;low&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                   &lt;span class="c1"&gt;// two nibbles → byte&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromCharCode&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// In real GlassWorm:&lt;/span&gt;
&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;invisibleString&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;  &lt;span class="c1"&gt;// 💥 the hidden code executes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five lines. That's all it takes. Subtract &lt;code&gt;0xFE00&lt;/code&gt; to get the nibble back, shift-and-OR to reassemble the byte, convert to character, join, &lt;code&gt;eval()&lt;/code&gt;. Game over.&lt;/p&gt;




&lt;h2&gt;
  
  
  What GlassWorm actually does with this
&lt;/h2&gt;

&lt;p&gt;The invisible payload isn't a &lt;code&gt;console.log&lt;/code&gt;. It's a multi-stage RAT called the &lt;strong&gt;ZOMBI module&lt;/strong&gt; that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Steals credentials&lt;/strong&gt; — NPM tokens, GitHub PATs, OpenVSX tokens, Git credentials, SSH keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drains crypto wallets&lt;/strong&gt; — targets 49 different wallet extensions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Installs SOCKS proxies&lt;/strong&gt; — turns your machine into a relay for criminal traffic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploys hidden VNC servers&lt;/strong&gt; — full remote access to your workstation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-propagates&lt;/strong&gt; — uses stolen tokens to compromise more extensions and packages, creating exponential growth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The C2 infrastructure is equally sophisticated: Solana blockchain transaction memos (can't be taken down), Google Calendar as a backup channel (bypasses network monitoring), and BitTorrent DHT for decentralized server discovery.&lt;/p&gt;

&lt;p&gt;And it avoids infecting machines with Russian locale. Make of that what you will.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;I wanted to understand this deeply enough to detect it, so I built a complete toolkit:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Detection scanner
&lt;/h3&gt;

&lt;p&gt;A zero-dependency Python scanner that runs 8 detection rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scan your VS Code extensions right now&lt;/span&gt;
python3 glassworm_scanner.py ~/.vscode/extensions/ &lt;span class="nt"&gt;--verbose&lt;/span&gt;

&lt;span class="c"&gt;# JSON output for CI/CD&lt;/span&gt;
python3 glassworm_scanner.py ./src &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It catches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variation selector clusters (3+ = suspicious, 10+ in JS = critical)&lt;/li&gt;
&lt;li&gt;Decoder signatures near invisible characters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eval()&lt;/code&gt; + string construction patterns&lt;/li&gt;
&lt;li&gt;Solana/blockchain C2 indicators in non-blockchain code&lt;/li&gt;
&lt;li&gt;Suspicious &lt;code&gt;postinstall&lt;/code&gt; lifecycle scripts&lt;/li&gt;
&lt;li&gt;Mid-file BOM injection markers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exit code 0 = clean, 1 = compromised. Drop it into any pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Git pre-commit hook
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp &lt;/span&gt;pre-commit-hook.sh .git/hooks/pre-commit
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x .git/hooks/pre-commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now any commit containing invisible Unicode payloads gets blocked before it hits your repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Interactive web explainer
&lt;/h3&gt;

&lt;p&gt;This is the part I'm most proud of. A full interactive walkthrough where you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type any string and watch it encode into invisible characters in real time&lt;/li&gt;
&lt;li&gt;See the raw hex bytes on disk&lt;/li&gt;
&lt;li&gt;Decode it back to prove the round-trip&lt;/li&gt;
&lt;li&gt;Step through the nibble math character by character&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Try it live:&lt;/strong&gt; &lt;a href="https://visagansp.github.io/glassworm-toolkit/" rel="noopener noreferrer"&gt;visagansp.github.io/glassworm-toolkit&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Educational CLI demo
&lt;/h3&gt;

&lt;p&gt;A 10-step terminal walkthrough that generates a safe "infected" file and scans it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 glassworm_educational_demo.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It creates a real file with invisible characters embedded in it, then runs the scanner to prove detection works. All payloads are harmless &lt;code&gt;console.log&lt;/code&gt; statements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything is on GitHub:&lt;/strong&gt; &lt;a href="https://github.com/visagansp/glassworm-toolkit" rel="noopener noreferrer"&gt;github.com/visagansp/glassworm-toolkit&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How to check if you're already infected
&lt;/h2&gt;

&lt;p&gt;This takes 30 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone the toolkit&lt;/span&gt;
git clone https://github.com/visagansp/glassworm-toolkit.git
&lt;span class="nb"&gt;cd &lt;/span&gt;glassworm-toolkit

&lt;span class="c"&gt;# Scan your VS Code extensions&lt;/span&gt;
python3 glassworm_scanner.py ~/.vscode/extensions/ &lt;span class="nt"&gt;--extended&lt;/span&gt;

&lt;span class="c"&gt;# Scan your node_modules&lt;/span&gt;
python3 glassworm_scanner.py /path/to/project/node_modules/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see CRITICAL findings, &lt;strong&gt;assume compromise&lt;/strong&gt;. Rotate all tokens — NPM, GitHub, OpenVSX, SSH keys, cloud API keys — immediately. Check for rogue processes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Look for SOCKS proxies and VNC servers GlassWorm installs&lt;/span&gt;
netstat &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;':(1080|5900|5901|4444|8888)'&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'node.*socks|vnc|proxy'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Prevention checklist
&lt;/h2&gt;

&lt;p&gt;If you take away one thing from this article, do these:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Disable VS Code extension auto-update.&lt;/strong&gt; GlassWorm's Wave 1 got onto 35,800 machines because extensions auto-updated to malicious versions. Go to Settings → &lt;code&gt;extensions.autoUpdate&lt;/code&gt; → set to &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Install the pre-commit hook.&lt;/strong&gt; Three commands. Protects every repo you work in. Zero false positives on clean code in my testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Scan before you install.&lt;/strong&gt; Before adding any extension or npm package, run the scanner on it. It takes seconds and needs zero dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Maintain an extension allowlist.&lt;/strong&gt; If your team has more than a few developers, centralize which extensions are approved. Shadow extensions are how GlassWorm gets in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Integrate into CI/CD.&lt;/strong&gt; The scanner outputs JSON and uses exit codes. Plug it into GitHub Actions or GitLab CI in 5 lines of YAML.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;GlassWorm is just one technique. The supply chain attack landscape is evolving fast. I'm working on a follow-up article covering the broader 2025–2026 attack timeline — from s1ngularity stealing AI assistant credentials, to Shai-Hulud's self-destructing npm worm, to the Trivy extension attack that weaponized AI coding assistants themselves through prompt injection. The common thread: developer environments are the new front line, and the attacks are getting creative in ways traditional security tooling wasn't built to handle.&lt;/p&gt;

&lt;p&gt;If you found this useful, star the repo and share it with your team. The toolkit is MIT licensed — use it, fork it, improve it.&lt;/p&gt;

&lt;p&gt;And go scan your extensions folder. Right now. You might be surprised what's hiding in the blank lines.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Toolkit:&lt;/strong&gt; &lt;a href="https://github.com/visagansp/glassworm-toolkit" rel="noopener noreferrer"&gt;github.com/visagansp/glassworm-toolkit&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://visagansp.github.io/glassworm-toolkit/" rel="noopener noreferrer"&gt;visagansp.github.io/glassworm-toolkit&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Visagan S — security engineer, pentester, and AI engineer. I build tools that help developers understand and defend against the attacks targeting their workflows. Connect with me on &lt;a href="https://github.com/visagansp" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>javascript</category>
      <category>vscode</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
