<?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: cryptostrapon</title>
    <description>The latest articles on DEV Community by cryptostrapon (@cryptostrapon).</description>
    <link>https://dev.to/cryptostrapon</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4180447%2F90ee2f07-c8d3-45da-b916-eda35ed9240d.png</url>
      <title>DEV Community: cryptostrapon</title>
      <link>https://dev.to/cryptostrapon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cryptostrapon"/>
    <language>en</language>
    <item>
      <title>Address poisoning: the scam that never hacks you (plus a 15 line check for your own app)</title>
      <dc:creator>cryptostrapon</dc:creator>
      <pubDate>Thu, 29 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/cryptostrapon/address-poisoning-the-scam-that-never-hacks-you-plus-a-15-line-check-for-your-own-app-7o5</link>
      <guid>https://dev.to/cryptostrapon/address-poisoning-the-scam-that-never-hacks-you-plus-a-15-line-check-for-your-own-app-7o5</guid>
      <description>&lt;p&gt;In December 2025 a trader withdrew almost 50 million USDT from Binance to move it to their own wallet. They did the careful thing first: a 50 USDT test transfer to the right address. It arrived.&lt;/p&gt;

&lt;p&gt;Twenty six minutes later the full 49,999,950 USDT went to a different address. That address matched the real one in its first five and last four characters. The attacker swapped the funds to DAI, then ETH, and most of it went into Tornado Cash (&lt;a href="https://www.theblock.co/post/383423/crypto-trader-loses-50-million-in-address-poisoning-attack-offers-1-million-bounty-for-return" rel="noopener noreferrer"&gt;The Block&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Nothing was hacked. No key leaked, no contract was approved. And on 28 October Scam Sniffer flagged another user who lost about $50,000 the same way. The attack is called &lt;strong&gt;address poisoning&lt;/strong&gt;, and it works because of how our interfaces show addresses.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A bot watches the chain for transfers, including small test sends.&lt;/li&gt;
&lt;li&gt;It generates a vanity address whose start and end match the address you just paid. This is cheap: matching a few characters at each end takes minutes on a single GPU.&lt;/li&gt;
&lt;li&gt;It sends you a zero value or dust transfer from that look-alike. Now it sits in your history, right next to the real entry.&lt;/li&gt;
&lt;li&gt;You come back to finish the payment, open your history, copy what looks like the same address, and send.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The test transfer did not protect the trader. It was the signal the bot needed: it told the attacker which address to imitate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the UI makes it easy
&lt;/h2&gt;

&lt;p&gt;Wallets and explorers shorten addresses to &lt;code&gt;0x1234...abcd&lt;/code&gt;. People check the start, maybe the end, and stop. So "the start and end match" proves nothing. The only part an attacker cannot cheaply copy is the middle, and that is the part we hide.&lt;/p&gt;

&lt;h2&gt;
  
  
  A check you can add in 15 lines
&lt;/h2&gt;

&lt;p&gt;If you build a wallet, a payment form, a treasury tool or an internal payout script, you can warn before the user signs:&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="c1"&gt;// Warn when a recipient looks like a known address but is not the same one.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^0x/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;lookalikeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;knownAddresses&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;edge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&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;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;knownAddresses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// exact match: fine&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;const&lt;/span&gt; &lt;span class="nx"&gt;known&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;knownAddresses&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;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;known&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;sameStart&lt;/span&gt; &lt;span class="o"&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;slice&lt;/span&gt;&lt;span class="p"&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;edge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&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;edge&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;sameEnd&lt;/span&gt; &lt;span class="o"&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;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sameStart&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;sameEnd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;known&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="c1"&gt;// looks the same, is not&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use it with the addresses the user really trusts: their address book, saved withdrawal addresses, and addresses they sent real value to before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;twin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lookalikeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addressBook&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;twin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;showWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`This address looks like &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;twin&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; from your contacts, but it is a different address. `&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s2"&gt;`Copy the recipient from the original source, not from history.`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three cheap upgrades make it much stronger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Show the middle.&lt;/strong&gt; Before signing, display the full address and highlight the characters between the first and last four. That is where the difference is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distrust dust senders.&lt;/strong&gt; If the recipient only ever appeared in history through a zero value or tiny incoming transfer, say so. Real counterparties rarely introduce themselves that way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hide or mark zero value transfers&lt;/strong&gt; in the history view, so poisoned entries are not one click away.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The check is deliberately simple. It does not decide who is honest. It only catches the one moment the attack depends on: copying something that looks familiar.&lt;/p&gt;

&lt;h2&gt;
  
  
  What users can do today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Never copy a recipient from transaction history. Take it from the original source: the invoice, the exchange deposit page, the person.&lt;/li&gt;
&lt;li&gt;Save trusted addresses once, after checking them in full, and send only from the address book or a withdrawal whitelist.&lt;/li&gt;
&lt;li&gt;After a test transfer, copy the address again from the same original source, not from the history that now shows two similar entries.&lt;/li&gt;
&lt;li&gt;Compare the middle of the address, not just the ends.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We keep a short practical guide on this: &lt;a href="https://cryptostrapon.com/how-to-prevent-address-poisoning" rel="noopener noreferrer"&gt;how to check an address before you send&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Question for you:&lt;/strong&gt; if you build wallets or payment tools, do you hide zero value transfers in history, or show them with a warning? I am curious which one users actually understand.&lt;/p&gt;

&lt;p&gt;If this was useful, a star on &lt;a href="https://github.com/cryptostrapon/crypto-scam-glossary" rel="noopener noreferrer"&gt;crypto-scam-glossary&lt;/a&gt;, our open glossary of scam patterns, helps other developers find it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written with AI assistance and fact checked by the CryptoStrapon team.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>web3</category>
      <category>javascript</category>
      <category>cryptocurrency</category>
    </item>
    <item>
      <title>Git hooks in a zip: the attack that never touches npm install</title>
      <dc:creator>cryptostrapon</dc:creator>
      <pubDate>Mon, 26 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/cryptostrapon/your-take-home-repo-can-attack-you-without-npm-install-2495</link>
      <guid>https://dev.to/cryptostrapon/your-take-home-repo-can-attack-you-without-npm-install-2495</guid>
      <description>&lt;p&gt;Last week we covered the npm side: install hooks that run on the first &lt;code&gt;npm install&lt;/code&gt;. This post is about the part we did not cover, git itself.&lt;/p&gt;

&lt;p&gt;A write-up published last week by &lt;a href="https://medium.com/@rasyidinazhari/anatomy-of-a-backdoored-git-repo-scam-the-genetiq-fake-job-offer-60d6e5fc735f" rel="noopener noreferrer"&gt;Wisnu Rasyidin Azhari&lt;/a&gt; shows a variant that skips npm completely: a "recruitment documents" repo, sent as a zip, with 22 identical hooks in &lt;code&gt;.git/hooks&lt;/code&gt;. Each one downloaded a script for your OS and piped it into a shell, quietly, in the background.&lt;/p&gt;

&lt;p&gt;The trigger was not a build. It was &lt;code&gt;git pull&lt;/code&gt;, &lt;code&gt;git checkout&lt;/code&gt; or &lt;code&gt;git commit&lt;/code&gt;. The things you do without thinking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this works
&lt;/h2&gt;

&lt;p&gt;Git hooks are scripts in &lt;code&gt;.git/hooks&lt;/code&gt; that git runs on its own at certain moments. Two facts make them a good trap:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;They run without asking.&lt;/strong&gt; No prompt, no output if the script sends it to &lt;code&gt;/dev/null&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They never travel through &lt;code&gt;git clone&lt;/code&gt;.&lt;/strong&gt; Hooks are not part of the repository history. So the attacker cannot put them on GitHub. They have to send you the whole folder: a zip, a cloud drive link, "the project archive".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That second fact is your best signal. &lt;strong&gt;A recruiter who sends a zip that still contains the &lt;code&gt;.git&lt;/code&gt; folder, instead of a link to clone, is a red flag on its own.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same delivery trick works for a few neighbours of hooks:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;What runs&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.git/hooks/*&lt;/code&gt; (no &lt;code&gt;.sample&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;any script&lt;/td&gt;
&lt;td&gt;commit, checkout, pull, merge, push&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.git/config&lt;/code&gt; &lt;code&gt;core.hooksPath&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;hooks from another folder&lt;/td&gt;
&lt;td&gt;same as above&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.git/config&lt;/code&gt; &lt;code&gt;core.fsmonitor&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;any program&lt;/td&gt;
&lt;td&gt;even &lt;code&gt;git status&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.git/config&lt;/code&gt; aliases with &lt;code&gt;!&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;shell command&lt;/td&gt;
&lt;td&gt;when you use the alias&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.vscode/tasks.json&lt;/code&gt; with &lt;code&gt;"runOn": "folderOpen"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;any task&lt;/td&gt;
&lt;td&gt;when you open the folder in VS Code&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The last one is different: &lt;code&gt;.vscode&lt;/code&gt; can be committed, so it also arrives through a normal clone. VS Code asks whether you trust the folder first. Say no.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 second check
&lt;/h2&gt;

&lt;p&gt;Before any git command in a folder someone sent you:&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;# real hooks are anything that is not a .sample file&lt;/span&gt;
find .git/hooks &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.sample"&lt;/span&gt;

&lt;span class="c"&gt;# config entries that make git run programs&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-nE&lt;/span&gt; &lt;span class="s2"&gt;"hooksPath|fsmonitor|sshCommand|pager|editor|= *!"&lt;/span&gt; .git/config

&lt;span class="c"&gt;# VS Code tasks that start on open&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"folderOpen"&lt;/span&gt; .vscode/tasks.json 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a fresh repo the first two print nothing interesting. If you need to run git anyway, switch hooks off for that command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nt"&gt;-c&lt;/span&gt; core.hooksPath&lt;span class="o"&gt;=&lt;/span&gt;/dev/null status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  We added this to npm-install-audit
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/cryptostrapon/npm-install-audit" rel="noopener noreferrer"&gt;npm-install-audit&lt;/a&gt; is our small read-only Node script for unknown repos. Version 1.1 now checks &lt;code&gt;.git/hooks&lt;/code&gt;, risky &lt;code&gt;.git/config&lt;/code&gt; entries and VS Code &lt;code&gt;folderOpen&lt;/code&gt; tasks, next to the npm install hooks it already flagged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ node audit.js ./take-home
! .git/hooks/post-checkout: active git hook, runs automatically on git commands, downloads and runs code
! .git/config: core.hooksPath points git hooks to another folder: hooksPath = .githooks
! .vscode/tasks.json: VS Code task runs when the folder is opened

3 finding(s). Read them before you run npm install or any git command.
Tip: git -c core.hooksPath=/dev/null &amp;lt;command&amp;gt; runs git without repo hooks.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One file, no dependencies, no network. It ignores &lt;code&gt;.sample&lt;/code&gt; hooks, so a normal clone stays clean. It will flag husky, because husky sets &lt;code&gt;core.hooksPath&lt;/code&gt; too. Read the finding and decide.&lt;/p&gt;

&lt;p&gt;A clean result is not proof of safety. The payload in these attacks lives on the attacker's server and can change any time, so the throwaway VM habit from last week's post applies here too.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you already ran git in that folder
&lt;/h2&gt;

&lt;p&gt;Treat the machine as compromised. From a clean device, change passwords, revoke tokens and SSH keys, and move any crypto to a new wallet created on that clean device. Then wipe and reinstall. The full list, plus the indicators from the original case, is in &lt;a href="https://medium.com/@rasyidinazhari/anatomy-of-a-backdoored-git-repo-scam-the-genetiq-fake-job-offer-60d6e5fc735f" rel="noopener noreferrer"&gt;Wisnu's write-up&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Question for you:&lt;/strong&gt; which of these did you already know about: &lt;code&gt;core.fsmonitor&lt;/code&gt; running a program on a plain &lt;code&gt;git status&lt;/code&gt;, or VS Code tasks that start when a folder opens? And do you check &lt;code&gt;.git/hooks&lt;/code&gt; before running git in a folder you did not clone yourself?&lt;/p&gt;

&lt;p&gt;If npm-install-audit is useful, a star on GitHub helps other developers find it: &lt;a href="https://github.com/cryptostrapon/npm-install-audit" rel="noopener noreferrer"&gt;https://github.com/cryptostrapon/npm-install-audit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More scam breakdowns and free tools: &lt;a href="https://cryptostrapon.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=git_hooks" rel="noopener noreferrer"&gt;https://cryptostrapon.com?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=git_hooks&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>git</category>
      <category>javascript</category>
      <category>career</category>
    </item>
    <item>
      <title>Read what you sign: 5 function selectors that should make you stop (with a small decoder)</title>
      <dc:creator>cryptostrapon</dc:creator>
      <pubDate>Thu, 22 Oct 2026 11:12:28 +0000</pubDate>
      <link>https://dev.to/cryptostrapon/read-what-you-sign-5-function-selectors-that-should-make-you-stop-with-a-small-decoder-100i</link>
      <guid>https://dev.to/cryptostrapon/read-what-you-sign-5-function-selectors-that-should-make-you-stop-with-a-small-decoder-100i</guid>
      <description>&lt;p&gt;When a dapp asks you to confirm a transaction, the wallet shows a summary. The raw &lt;strong&gt;data&lt;/strong&gt; field is where the truth is, and its first four bytes (the function selector) say what is being called.&lt;/p&gt;

&lt;p&gt;Here are selectors worth recognising on the Ethereum virtual machine:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Selector&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x095ea7b3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;approve(address,uint256)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lets a spender move your token. "Unlimited" amounts are the risky part&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0xa22cb465&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;setApprovalForAll(address,bool)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lets an operator move &lt;strong&gt;all&lt;/strong&gt; your NFTs of a collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x39509351&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;increaseAllowance(address,uint256)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Same idea as approve, added on top of an existing allowance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x23b872dd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;transferFrom(address,address,uint256)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Moves tokens using an allowance. Normal for swaps, a red flag if you did not start a swap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0xa9059cbb&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;transfer(address,uint256)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A plain send. Check the recipient&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A small decoder (17 lines)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WATCH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x095ea7b3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;approve: gives a spender permission to move your tokens&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0xa22cb465&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setApprovalForAll: gives an operator access to ALL your NFTs in a collection&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x39509351&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;increaseAllowance: raises a spender's permission&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;WATCH&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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;MAX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;n&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;note&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;WATCH&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x095ea7b3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;74&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;138&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;note&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; (UNLIMITED amount)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads the first four bytes and, for &lt;code&gt;approve&lt;/code&gt;, checks if the amount is the maximum possible value (all bits set). Use it in a wallet UI, a browser tool or an internal review step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do as a user
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An approval is not a payment. Ask: &lt;strong&gt;why does this site need permission to move my tokens?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Prefer exact amounts over "unlimited" when the interface lets you choose.&lt;/li&gt;
&lt;li&gt;Revoke approvals you no longer use. Open source tools and block explorers can list them.&lt;/li&gt;
&lt;li&gt;If you did not start a swap, mint or claim, any &lt;code&gt;transferFrom&lt;/code&gt; or approval request is suspicious.&lt;/li&gt;
&lt;li&gt;A hardware wallet shows less detail on small screens, so read carefully and never approve something you cannot explain.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Follow for more plain-language scam breakdowns and open tools from CryptoStrapon. Everything we publish is free and open (MIT code, CC BY 4.0 content).&lt;/p&gt;

&lt;p&gt;Not sure about a message, link or token? Free checker, no sign-up: &lt;a href="https://cryptostrapon.com/scam-detector?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=selectors" rel="noopener noreferrer"&gt;https://cryptostrapon.com/scam-detector?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=selectors&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test yourself with the open quiz (50 core scenarios here, 104 on cryptostrapon.com/scam-quiz): &lt;a href="https://cryptostrapon.github.io/crypto-scam-quiz/" rel="noopener noreferrer"&gt;https://cryptostrapon.github.io/crypto-scam-quiz/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the quiz or this decoder is useful, a star on GitHub helps other people find it: &lt;a href="https://github.com/cryptostrapon/crypto-scam-quiz" rel="noopener noreferrer"&gt;https://github.com/cryptostrapon/crypto-scam-quiz&lt;/a&gt;&lt;/p&gt;

</description>
      <category>web3</category>
      <category>security</category>
      <category>javascript</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>You got a take-home test from a recruiter. You have 5 minutes before npm install</title>
      <dc:creator>cryptostrapon</dc:creator>
      <pubDate>Tue, 20 Oct 2026 07:30:00 +0000</pubDate>
      <link>https://dev.to/cryptostrapon/you-got-a-take-home-test-from-a-recruiter-you-have-5-minutes-before-npm-install-1cei</link>
      <guid>https://dev.to/cryptostrapon/you-got-a-take-home-test-from-a-recruiter-you-have-5-minutes-before-npm-install-1cei</guid>
      <description>&lt;p&gt;A recruiter likes your GitHub. The pay is 20 to 40 percent above market, the call goes well, and then comes the take-home: clone the repo, run &lt;code&gt;npm install&lt;/code&gt;, finish it in 48 hours.&lt;/p&gt;

&lt;p&gt;That last step is the whole attack. Nobody hacked you. You ran the malware yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is a real campaign
&lt;/h2&gt;

&lt;p&gt;Researchers call it &lt;strong&gt;Contagious Interview&lt;/strong&gt; and link it to North Korea. Socket counted 338 malicious npm packages and about 50,000 downloads as of October 2025. A newer wave, PolinRider, covers 108 malicious packages and extensions, and 1,951 public GitHub repositories were compromised as of April 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  What they take
&lt;/h2&gt;

&lt;p&gt;Not your project files. Your wallet data, browser sessions and saved logins, SSH keys and cloud credentials, and tokens from &lt;code&gt;.env&lt;/code&gt; files. One take-home can become a company breach.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt; runs a &lt;code&gt;postinstall&lt;/code&gt; hook with your permissions before you have read anything. A hidden payload decodes itself, searches browser profiles and config folders, and sends what it finds to the attacker. You see no error, and the project may even run.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tells, before you clone anything
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The message:&lt;/strong&gt; pay far above market, your own GitHub quoted back to you, a recruiter with few connections, a quick move to Telegram or Discord.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The call:&lt;/strong&gt; the camera is "broken", the face looks off, or you are asked to run the project live on screen share.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The task:&lt;/strong&gt; a deadline built to stop you reading, a private repo from an organization with no history, "just run npm install", a request to turn off antivirus.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One tell alone proves nothing. Several together are a pattern, so slow down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the message, then the repo
&lt;/h2&gt;

&lt;p&gt;Paste the recruiter's message into our free &lt;a href="https://cryptostrapon.com/scam-detector?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=take_home_repo" rel="noopener noreferrer"&gt;AI scam detector&lt;/a&gt; (our project, disclosed because it is ours) and it lists the red flags it finds. It reads text for patterns and cannot confirm who is behind an account.&lt;/p&gt;

&lt;p&gt;Then check the repo. This script only reads files: it lists install hooks and flags shell commands, hidden base64, long encoded strings and paths to wallet, browser and key data. It never runs the project and never touches the network. It is also in a small MIT repo with a self test: github.com/cryptostrapon/npm-install-audit.&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="cp"&gt;#!/usr/bin/env node
&lt;/span&gt;&lt;span class="c1"&gt;// npm-install-audit: look before you run.&lt;/span&gt;
&lt;span class="c1"&gt;// Usage: node audit.js ./some-repo&lt;/span&gt;
&lt;span class="c1"&gt;// Read only, no network, no dependencies. A clean result is not proof of safety.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&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;root&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isDirectory&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not a directory&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cannot read directory: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&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;HOOKS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;preinstall&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;install&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postinstall&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prepare&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SKIP_DIRS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.git&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node_modules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RISKY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/child_process|execSync|execFileSync|spawnSync|&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;spawn&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;runs shell commands&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;eval&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="sr"&gt;|new Function&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;evaluates code at runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;atob&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="sr"&gt;|Buffer&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;from&lt;/span&gt;&lt;span class="se"&gt;\([^&lt;/span&gt;&lt;span class="sr"&gt;)&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*base64/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;decodes base64 at runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;curl|wget&lt;/span&gt;&lt;span class="se"&gt;)\s[^\n]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\|\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;sh|bash&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pipes a download into a shell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/Login Data|keystore|wallet&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;dat|&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;ssh&lt;/span&gt;&lt;span class="se"&gt;[\/\\]&lt;/span&gt;&lt;span class="sr"&gt;|id_rsa|&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;aws&lt;/span&gt;&lt;span class="se"&gt;[\/\\]&lt;/span&gt;&lt;span class="sr"&gt;credentials|Local Storage&lt;/span&gt;&lt;span class="se"&gt;[\/\\]&lt;/span&gt;&lt;span class="sr"&gt;leveldb/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;touches key, wallet or browser data paths&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;findings&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;findings&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;! &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkPackage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&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;pkg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;package.json could not be parsed, read it by hand&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&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;const&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;HOOKS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scripts&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nf"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; hook runs: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;pkg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scripts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&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;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&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;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;re&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;why&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;RISKY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nf"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;why&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;min&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;js$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&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;blob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Za-z0-9+&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;=&lt;/span&gt;&lt;span class="se"&gt;]{300,}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;long encoded string (&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&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;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; chars)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&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;entries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;withFileTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&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;const&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SKIP_DIRS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&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;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isSymbolicLink&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isDirectory&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;depth&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="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isFile&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;package.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;checkPackage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;js|cjs|mjs|sh&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;size&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="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="nx"&gt;e6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;checkSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;findings&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; finding(s). Read them before you run npm install.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No obvious install hooks or risky patterns found. Still read the code.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it on the cloned folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &amp;lt;repo&amp;gt; task &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; node audit.js ./task
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a repo with a &lt;code&gt;postinstall&lt;/code&gt; that decodes base64 and reads &lt;code&gt;~/.ssh/id_rsa&lt;/code&gt;, it prints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;! package.json: postinstall hook runs: node ./scripts/setup.js
! scripts/setup.js: runs shell commands
! scripts/setup.js: decodes base64 at runtime
! scripts/setup.js: touches key, wallet or browser data paths
! scripts/setup.js: long encoded string (380 chars)

5 finding(s). Read them before you run npm install.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;It is a first filter, not a verdict.&lt;/strong&gt; It cannot see code fetched after install, obfuscation it does not recognize, or VS Code task files (&lt;code&gt;.vscode/tasks.json&lt;/code&gt;) that run code the moment you open a folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  A safer habit
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;npm install --ignore-scripts&lt;/code&gt; first and read before you enable anything.&lt;/li&gt;
&lt;li&gt;Open unknown folders in VS Code in Restricted Mode and do not click Trust.&lt;/li&gt;
&lt;li&gt;Use a throwaway VM or container, never the machine with your wallets and browser sessions.&lt;/li&gt;
&lt;li&gt;Verify the job on the company's own careers page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you already ran it, treat the machine as compromised. From a clean device, move your funds first, then rotate passwords and keys and revoke sessions.&lt;/p&gt;

&lt;p&gt;Full write up: &lt;a href="https://cryptostrapon.com/stories/malicious-test-project" rel="noopener noreferrer"&gt;fake test project investigation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question for the comments:&lt;/strong&gt; has a recruiter ever sent you a take-home that made you stop? What was the tell?&lt;/p&gt;

</description>
      <category>security</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>career</category>
    </item>
    <item>
      <title>How "approval phishing" drains wallets without stealing a key (and how to check it in 10 lines)</title>
      <dc:creator>cryptostrapon</dc:creator>
      <pubDate>Wed, 14 Oct 2026 12:10:03 +0000</pubDate>
      <link>https://dev.to/cryptostrapon/how-approval-phishing-drains-wallets-without-stealing-a-key-and-how-to-check-it-in-10-lines-4pfl</link>
      <guid>https://dev.to/cryptostrapon/how-approval-phishing-drains-wallets-without-stealing-a-key-and-how-to-check-it-in-10-lines-4pfl</guid>
      <description>&lt;p&gt;Most people picture a crypto theft as "someone got my seed phrase". A large share of wallet drains we see in scam reports works differently: the victim signs one innocent-looking message, and the attacker never touches the key.&lt;/p&gt;

&lt;p&gt;This post explains the mechanism and shows a short script that checks whether a token approval is unlimited.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an approval is
&lt;/h2&gt;

&lt;p&gt;ERC-20 tokens let you give another address (a "spender") the right to move your tokens, up to some amount. Exchanges, DEXs and games use this all the time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You call &lt;code&gt;approve(spender, amount)&lt;/code&gt; on the token contract.&lt;/li&gt;
&lt;li&gt;The spender can later call &lt;code&gt;transferFrom(you, anywhere, amount)&lt;/code&gt; without asking you again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The risk: many dapps ask for &lt;code&gt;MaxUint256&lt;/code&gt; (unlimited) so you don't have to approve again. If that spender is a scam contract, or a legitimate one that later gets compromised, it can empty that token from your wallet at any time. The approval stays valid until you revoke it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How scams use it
&lt;/h2&gt;

&lt;p&gt;The pattern is simple and repeated across fake airdrop sites, fake "claim" pages and cloned mint pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A page tells you to "verify your wallet" or "claim your reward".&lt;/li&gt;
&lt;li&gt;Your wallet pops up a signature or approval request. The text looks harmless, or is hard to read.&lt;/li&gt;
&lt;li&gt;You sign. The scammer's contract now has an allowance.&lt;/li&gt;
&lt;li&gt;Funds move later, sometimes days after, so the link to that page is easy to miss.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Off-chain signatures such as &lt;code&gt;permit&lt;/code&gt; / Permit2 can do the same job without you paying gas for an approval transaction, which is why "it was free" is not a safety signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check an allowance yourself
&lt;/h2&gt;

&lt;p&gt;With ethers v6:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ethers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ERC20&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function allowance(address owner, address spender) view returns (uint256)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RPC_URL&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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ERC20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&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;allowance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allowance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OWNER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SPENDER&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allowance&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MaxUint256&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UNLIMITED allowance&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;allowance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with your own wallet address, the token address and the spender you want to check. An unlimited allowance to an address you don't recognise is the red flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Revoke approvals you don't use. Block-explorer "token approvals" pages and revoke tools let you set the allowance back to zero.&lt;/li&gt;
&lt;li&gt;Prefer exact amounts over "unlimited" when your wallet offers the choice.&lt;/li&gt;
&lt;li&gt;Read what you sign. If the request mentions &lt;code&gt;approve&lt;/code&gt;, &lt;code&gt;setApprovalForAll&lt;/code&gt;, &lt;code&gt;permit&lt;/code&gt; or a long hex blob you can't parse, stop.&lt;/li&gt;
&lt;li&gt;Use a separate wallet with little in it for new or unfamiliar sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practice it
&lt;/h2&gt;

&lt;p&gt;We turned these patterns into a free quiz: 50 core scenarios in one HTML file, no tracking (the version on cryptostrapon.com has 104). Try a few and see which ones you would have signed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quiz: &lt;a href="https://cryptostrapon.github.io/crypto-scam-quiz/" rel="noopener noreferrer"&gt;https://cryptostrapon.github.io/crypto-scam-quiz/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Glossary of scam vectors (including approval phishing): &lt;a href="https://cryptostrapon.github.io/crypto-scam-glossary/" rel="noopener noreferrer"&gt;https://cryptostrapon.github.io/crypto-scam-glossary/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are open (code under MIT, content under CC BY 4.0), so you can reuse or translate them. If you want a quick sanity check on a suspicious site or token, our free AI scam detector (our project) is at &lt;a href="https://cryptostrapon.com/scam-detector?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=approval_phishing" rel="noopener noreferrer"&gt;https://cryptostrapon.com/scam-detector?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=approval_phishing&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>web3</category>
      <category>beginners</category>
      <category>javascript</category>
    </item>
    <item>
      <title>An open crypto-scam quiz in one HTML file: no build step, MIT code, CC BY 4.0 content</title>
      <dc:creator>cryptostrapon</dc:creator>
      <pubDate>Mon, 12 Oct 2026 21:43:21 +0000</pubDate>
      <link>https://dev.to/cryptostrapon/an-open-crypto-scam-quiz-in-one-html-file-no-build-step-mit-code-cc-by-40-content-47d9</link>
      <guid>https://dev.to/cryptostrapon/an-open-crypto-scam-quiz-in-one-html-file-no-build-step-mit-code-cc-by-40-content-47d9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Most scam-awareness content is a list of "red flags". It does not test whether you can apply them under pressure, so we built a quiz that does.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;What it is&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50 core scenarios (the version on cryptostrapon.com has 104) based on scam patterns we see in investigations: wallet drainers, approval phishing, fake airdrops, honeypot tokens, bridge exploits, AI-impersonation scams.&lt;/li&gt;
&lt;li&gt;Four answers each, one correct, plus an explanation and a severity label.&lt;/li&gt;
&lt;li&gt;A companion glossary of 11 scam vectors with mechanics, red flags and what to do if you were hit (the full glossary on cryptostrapon.com has 301 terms).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Live quiz: &lt;a href="https://cryptostrapon.github.io/crypto-scam-quiz/" rel="noopener noreferrer"&gt;https://cryptostrapon.github.io/crypto-scam-quiz/&lt;/a&gt;&lt;br&gt;
Glossary: &lt;a href="https://cryptostrapon.github.io/crypto-scam-glossary/" rel="noopener noreferrer"&gt;https://cryptostrapon.github.io/crypto-scam-glossary/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it is built&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The whole thing is one &lt;code&gt;index.html&lt;/code&gt; plus a &lt;code&gt;questions.json&lt;/code&gt;. Vanilla JS, no framework, no build step, no dependencies, no tracking. It is hosted on GitHub Pages, so a fork is live in a minute.&lt;/p&gt;

&lt;p&gt;A few small decisions that turned out to matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shuffled answer positions.&lt;/strong&gt; Our first draft had the right answer in the same slot far too often. We shuffled with a fixed seed so the distribution across the four slots is roughly even and the result is reproducible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generalised details.&lt;/strong&gt; The scenarios describe the pattern, not a named victim or exact figures, so they stay accurate and do not point at specific people.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open licensing.&lt;/strong&gt; Code is MIT, content is CC BY 4.0, with a &lt;code&gt;LICENSE-CONTENT.md&lt;/code&gt; so reuse terms are unambiguous for schools, wallets and newsrooms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Citable.&lt;/strong&gt; Each repo has a &lt;code&gt;CITATION.cff&lt;/code&gt; and a Zenodo DOI, plus schema.org &lt;code&gt;Dataset&lt;/code&gt; markup so it can appear in dataset search.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reuse it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Take the JSON, translate it, embed it in a training portal, or open a PR with a new scenario. Contributions and corrections are welcome.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/cryptostrapon/crypto-scam-quiz" rel="noopener noreferrer"&gt;https://github.com/cryptostrapon/crypto-scam-quiz&lt;/a&gt;&lt;br&gt;
Maintained by CryptoStrapon, a free AI crypto-scam detector: &lt;a href="https://cryptostrapon.com/scam-detector?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=open_quiz" rel="noopener noreferrer"&gt;https://cryptostrapon.com/scam-detector?utm_source=devto&amp;amp;utm_medium=post&amp;amp;utm_campaign=open_quiz&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Educational resource only, not financial or legal advice.&lt;/em&gt;&lt;/p&gt;

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