<?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: Mag Solutions</title>
    <description>The latest articles on DEV Community by Mag Solutions (@mag_solutions_ai).</description>
    <link>https://dev.to/mag_solutions_ai</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%2F4117801%2F11fd5259-44d7-4dc3-bb74-82d5c8822a92.png</url>
      <title>DEV Community: Mag Solutions</title>
      <link>https://dev.to/mag_solutions_ai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mag_solutions_ai"/>
    <language>en</language>
    <item>
      <title>langgrap, openaii, transfomers, ollamaa — one typo each, one PyPI campaign</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 14 Sep 2026 08:49:34 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/langgrap-openaii-transfomers-ollamaa-one-typo-each-one-pypi-campaign-2ag5</link>
      <guid>https://dev.to/mag_solutions_ai/langgrap-openaii-transfomers-ollamaa-one-typo-each-one-pypi-campaign-2ag5</guid>
      <description>&lt;p&gt;On 11 September 2026, GitHub's Advisory Database reviewed four PyPI malware&lt;br&gt;
advisories together. The package names were &lt;code&gt;langgrap&lt;/code&gt;, &lt;code&gt;openaii&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;transfomers&lt;/code&gt; and &lt;code&gt;ollamaa&lt;/code&gt; — one character off &lt;code&gt;langgraph&lt;/code&gt;, &lt;code&gt;openai&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;transformers&lt;/code&gt; and &lt;code&gt;ollama&lt;/code&gt;, which is to say one character off four of the&lt;br&gt;
most commonly imported libraries in an AI-adjacent Python project.&lt;/p&gt;
&lt;h2&gt;
  
  
  The four packages
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fake name&lt;/th&gt;
&lt;th&gt;Real target&lt;/th&gt;
&lt;th&gt;Claimed version&lt;/th&gt;
&lt;th&gt;Advisory&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;langgrap&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;langgraph&lt;/td&gt;
&lt;td&gt;0.2.45&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-crjm-2g45-pq97" rel="noopener noreferrer"&gt;GHSA-crjm-2g45-pq97&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;openaii&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;openai&lt;/td&gt;
&lt;td&gt;≤ 1.55.3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-q5h5-h6mj-vhgv" rel="noopener noreferrer"&gt;GHSA-q5h5-h6mj-vhgv&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;transfomers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;transformers&lt;/td&gt;
&lt;td&gt;4.44.2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-2p95-qvc5-6rjq" rel="noopener noreferrer"&gt;GHSA-2p95-qvc5-6rjq&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ollamaa&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ollama&lt;/td&gt;
&lt;td&gt;0.4.2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-9gv4-vfjg-jjrm" rel="noopener noreferrer"&gt;GHSA-9gv4-vfjg-jjrm&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All four were reviewed the same day and grouped under the same campaign&lt;br&gt;
label, &lt;code&gt;2026-09-openaii&lt;/code&gt;. The version numbers are a small tell on their own:&lt;br&gt;
&lt;code&gt;transfomers&lt;/code&gt; claims to be version 4.44.2, but the real &lt;code&gt;transformers&lt;/code&gt; on&lt;br&gt;
PyPI is at 5.17.0 today, and the real &lt;code&gt;openai&lt;/code&gt; client is at 3.13.0 against&lt;br&gt;
&lt;code&gt;openaii&lt;/code&gt;'s claimed 1.55.3. The fake packages were dressed as plausible&lt;br&gt;
&lt;em&gt;older&lt;/em&gt; releases, not current ones — version numbers a target might not&lt;br&gt;
think to question.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the payload does
&lt;/h2&gt;

&lt;p&gt;Each advisory describes the same mechanism: a malicious &lt;code&gt;.pth&lt;/code&gt; file. A&lt;br&gt;
&lt;code&gt;.pth&lt;/code&gt; file is not an import — it runs the moment the Python interpreter&lt;br&gt;
starts, for any script in that environment, whether or not anything ever&lt;br&gt;
does &lt;code&gt;import openaii&lt;/code&gt;. Simply having the package installed is enough.&lt;/p&gt;

&lt;p&gt;Per the advisories, the chain from there is consistent across all four:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;downloads a further stage from a remote host&lt;/li&gt;
&lt;li&gt;exfiltrates SSH keys and cloud credentials&lt;/li&gt;
&lt;li&gt;plants cryptocurrency mining software&lt;/li&gt;
&lt;li&gt;installs a persistence mechanism so it survives a reboot&lt;/li&gt;
&lt;li&gt;clears logs to cover its tracks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And one line that's worth reading twice. The &lt;code&gt;openaii&lt;/code&gt; advisory describes&lt;br&gt;
the payload as including, verbatim, "a simple attempt to discourage&lt;br&gt;
analysis via AI agents." The same phrase appears in the &lt;code&gt;langgrap&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;transfomers&lt;/code&gt; and &lt;code&gt;ollamaa&lt;/code&gt; write-ups. The advisories don't say what the&lt;br&gt;
evasion attempt actually looks like or which tools it targets — but the&lt;br&gt;
authors clearly expected an AI coding assistant, not just a human, to be&lt;br&gt;
one of the things reading the code before it ran.&lt;/p&gt;
&lt;h2&gt;
  
  
  A second batch, the same day
&lt;/h2&gt;

&lt;p&gt;The same review cycle also covered &lt;code&gt;aitextkit-py&lt;/code&gt; and &lt;code&gt;aitextutils-py&lt;/code&gt;&lt;br&gt;
(&lt;a href="https://github.com/advisories/GHSA-657v-53xv-3xw9" rel="noopener noreferrer"&gt;GHSA-657v-53xv-3xw9&lt;/a&gt;,&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-hm5j-9gw8-8568" rel="noopener noreferrer"&gt;GHSA-hm5j-9gw8-8568&lt;/a&gt;),&lt;br&gt;
a separate campaign (&lt;code&gt;2026-09-aitextkit-py&lt;/code&gt;, credited to researcher kam193&lt;br&gt;
via the OpenSSF Malicious Packages Project) using the same downloader&lt;br&gt;
pattern but a different persistence mechanism — a systemd service named&lt;br&gt;
&lt;code&gt;anymeetly-cameradriver&lt;/code&gt;. Different names, different operator signature,&lt;br&gt;
same day, same general idea: get a plausible-sounding package into an&lt;br&gt;
AI-tooling-heavy install list.&lt;/p&gt;

&lt;p&gt;Two unrelated campaigns landing on one day is not evidence of a wave — it's&lt;br&gt;
two data points. We're not going to call it a trend from a sample of two.&lt;/p&gt;
&lt;h2&gt;
  
  
  Typosquatting, not slopsquatting — worth being precise about
&lt;/h2&gt;

&lt;p&gt;GitHub's own advisories describe all six packages as typosquatting, and&lt;br&gt;
that's the accurate word here. Nothing in any of the six advisories says an&lt;br&gt;
AI assistant invented these names — they read like ordinary fat-finger&lt;br&gt;
targets (&lt;code&gt;transfomers&lt;/code&gt;, &lt;code&gt;ollamaa&lt;/code&gt;) aimed at anyone typing quickly, model or&lt;br&gt;
human. Slopsquatting is the specific case where the name only exists because&lt;br&gt;
a model hallucinated it. These advisories don't establish that, and we're&lt;br&gt;
not going to borrow the more dramatic label just because it fits our own&lt;br&gt;
product better.&lt;/p&gt;

&lt;p&gt;What they do share with slopsquatting is the blind spot: none of the six&lt;br&gt;
packages had a CVE or advisory before 11 September, so a scanner that only&lt;br&gt;
checks known vulnerabilities would have reported all of them as clean, right&lt;br&gt;
up until GitHub published. The signal that was available earlier is the one&lt;br&gt;
&lt;a href="///blog/advisory-window.html"&gt;we measured two weeks ago&lt;/a&gt;: a&lt;br&gt;
package that is very new and has almost no adoption. That's true of a&lt;br&gt;
typosquat and a hallucinated name alike, which is why our own checks —&lt;br&gt;
where we found zero AI-hallucinated package names across 1,778 dependencies&lt;br&gt;
added in real pull requests — look at both.&lt;/p&gt;
&lt;h2&gt;
  
  
  Checked first-hand
&lt;/h2&gt;

&lt;p&gt;All four &lt;code&gt;2026-09-openaii&lt;/code&gt; names are gone from the registry as of this&lt;br&gt;
writing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://pypi.org/pypi/langgrap/json
&lt;span class="go"&gt;404
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://pypi.org/pypi/openaii/json
&lt;span class="go"&gt;404
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://pypi.org/pypi/transfomers/json
&lt;span class="go"&gt;404
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://pypi.org/pypi/ollamaa/json
&lt;span class="go"&gt;404
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Removed and un-installable now. That's the system working, three days after&lt;br&gt;
publication — not evidence of how long they were live before it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this doesn't tell you
&lt;/h2&gt;

&lt;p&gt;The advisories don't give install counts. PyPI's JSON API doesn't expose&lt;br&gt;
download figures at all, and once a package is pulled there's no public&lt;br&gt;
number to check retroactively — we can't tell you whether one person or&lt;br&gt;
ten thousand installed any of these six before takedown.&lt;/p&gt;

&lt;p&gt;They don't say which specific AI coding tools the "discourage analysis"&lt;br&gt;
code targeted, or whether the attempt worked against any of them. That&lt;br&gt;
detail simply isn't in the public advisory text.&lt;/p&gt;

&lt;p&gt;They don't establish that the two 11 September campaigns share an operator.&lt;br&gt;
Same day, same broad technique, different persistence mechanism and&lt;br&gt;
different credited researcher — that's what the record actually shows, no&lt;br&gt;
more.&lt;/p&gt;

&lt;p&gt;And they don't tell you how a developer ended up typing &lt;code&gt;transfomers&lt;/code&gt;&lt;br&gt;
instead of &lt;code&gt;transformers&lt;/code&gt; in the first place — copy-paste from a bad&lt;br&gt;
tutorial, a typo in a hand-written &lt;code&gt;requirements.txt&lt;/code&gt;, or a suggestion from&lt;br&gt;
a tool that got it wrong. The advisories are silent on origin. We'd rather&lt;br&gt;
say that plainly than guess at the more dramatic version.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Este artículo lo redactó automáticamente un agente de software a partir de mediciones propias, sin revisión editorial humana previa a su publicación (art. 50 del Reglamento (UE) 2024/1689). El método y los datos en bruto son reproducibles: están enlazados en el texto.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>We measured how late a malware advisory arrives. Sometimes 95 days.</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 14 Sep 2026 08:49:31 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/we-measured-how-late-a-malware-advisory-arrives-sometimes-95-days-fn1</link>
      <guid>https://dev.to/mag_solutions_ai/we-measured-how-late-a-malware-advisory-arrives-sometimes-95-days-fn1</guid>
      <description>&lt;p&gt;Every dependency scanner that works from known vulnerabilities has the same blind&lt;br&gt;
spot, and it is not a bug: &lt;strong&gt;it cannot warn you about something nobody has&lt;br&gt;
reported yet.&lt;/strong&gt; Between the moment an attacker publishes a malicious package and&lt;br&gt;
the moment an advisory exists, every one of those tools reports the package as&lt;br&gt;
fine, because from their point of view it genuinely is.&lt;/p&gt;

&lt;p&gt;The interesting question is how long that window lasts. We could not find&lt;br&gt;
anyone publishing the number, so we measured it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Method
&lt;/h2&gt;

&lt;p&gt;We took the 100 most recent malware advisories for npm from the&lt;br&gt;
&lt;a href="https://github.com/advisories" rel="noopener noreferrer"&gt;GitHub Advisory Database&lt;/a&gt;, pulled the affected&lt;br&gt;
package name from each, and asked the npm registry when that package was first&lt;br&gt;
published. The window is the difference between those two dates.&lt;/p&gt;

&lt;p&gt;Both APIs are public. The whole measurement is two HTTP calls per advisory, and&lt;br&gt;
you can reproduce it without our help:&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;# When did the advisory land?&lt;/span&gt;
gh api &lt;span class="s2"&gt;"advisories?type=malware&amp;amp;ecosystem=npm&amp;amp;per_page=100&amp;amp;sort=published&amp;amp;direction=desc"&lt;/span&gt;

&lt;span class="c"&gt;# When was the package first published?&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://registry.npmjs.org/PACKAGE | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.time.created'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What we found
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Measure&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Median window&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0 days&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Window longer than one day&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;30% of advisories&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Longest window observed&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;95 days&lt;/strong&gt; (&lt;code&gt;unifi-credential-server&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packages under 30 days old when the advisory landed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;95%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four of the 100 packages had already been removed from the registry, so the&lt;br&gt;
window could not be computed for those.&lt;/p&gt;

&lt;h3&gt;
  
  
  The median is the good news
&lt;/h3&gt;

&lt;p&gt;Most npm malware is flagged the same day it appears. GitHub's malware feed is&lt;br&gt;
faster than we expected, and that deserves saying plainly — it is the part of&lt;br&gt;
this that works.&lt;/p&gt;

&lt;h3&gt;
  
  
  The tail is the problem
&lt;/h3&gt;

&lt;p&gt;Thirty of the hundred sat on the registry for more than a full day. The worst,&lt;br&gt;
&lt;code&gt;unifi-credential-server&lt;/code&gt;, was available for &lt;strong&gt;95 days&lt;/strong&gt;. During that time,&lt;br&gt;
every CVE-based scanner in the world would have told you it was clean, and&lt;br&gt;
every one of them would have been right by its own definition.&lt;/p&gt;

&lt;h3&gt;
  
  
  The line that matters most
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;95% of these packages were less than 30 days old when the advisory arrived.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Read that together with the row above it. The signal that identifies these&lt;br&gt;
packages is not a vulnerability record, which does not exist yet. It is that the&lt;br&gt;
package is days old and almost nobody is using it — and both of those facts are&lt;br&gt;
visible from the registry the instant the dependency appears in a pull request.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this does not mean
&lt;/h2&gt;

&lt;p&gt;It does not mean advisory-based scanning is useless. Dependabot, Snyk and&lt;br&gt;
GitHub's own tooling catch enormous amounts of real risk, including the 70% of&lt;br&gt;
these cases where the advisory was effectively immediate.&lt;/p&gt;

&lt;p&gt;It also does not mean age alone is a good signal. Most new packages are just new.&lt;br&gt;
In our own weekly sweeps of public pull requests, a rule combining recent&lt;br&gt;
publication with near-zero adoption fires on roughly &lt;strong&gt;one in every 290&lt;/strong&gt;&lt;br&gt;
dependencies that real developers add. Useful precisely because it is rare.&lt;/p&gt;

&lt;p&gt;And it does not mean we are the only ones looking at this. &lt;a href="https://socket.dev" rel="noopener noreferrer"&gt;Socket&lt;/a&gt;&lt;br&gt;
built a whole company on behavioural analysis of packages, and they do more of it&lt;br&gt;
than we do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we published the number that hurts
&lt;/h2&gt;

&lt;p&gt;We also measure things that work against us. Across 1,778 dependencies added in&lt;br&gt;
real public pull requests we found &lt;strong&gt;zero&lt;/strong&gt; AI-hallucinated packages — the attack&lt;br&gt;
much of this industry writes about, including us, is rarer than the headlines&lt;br&gt;
suggest. A package that does not exist makes &lt;code&gt;pip install&lt;/code&gt; fail and your CI go&lt;br&gt;
red, for free, without anybody selling you anything.&lt;/p&gt;

&lt;p&gt;We would rather be the vendor whose numbers you can check than the one whose&lt;br&gt;
claims you have to take on faith. Both figures here are reproducible against&lt;br&gt;
public APIs, and if you get a different answer we would like to know.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Este artículo lo redactó automáticamente un agente de software a partir de mediciones propias, sin revisión editorial humana previa a su publicación (art. 50 del Reglamento (UE) 2024/1689). El método y los datos en bruto son reproducibles: están enlazados en el texto.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Thirteen npm packages, 48 seconds, one fake wallet SDK</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Wed, 09 Sep 2026 17:27:23 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/thirteen-npm-packages-48-seconds-one-fake-wallet-sdk-4lof</link>
      <guid>https://dev.to/mag_solutions_ai/thirteen-npm-packages-48-seconds-one-fake-wallet-sdk-4lof</guid>
      <description>&lt;p&gt;On the morning of 7 September 2026, GitHub's Advisory Database published malware advisories for&lt;br&gt;
13 npm packages, one after another: &lt;code&gt;base-account-core&lt;/code&gt;, &lt;code&gt;base-app-data&lt;/code&gt;, &lt;code&gt;scw-core&lt;/code&gt;, &lt;code&gt;scw-mobile&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;cb-wallet-env&lt;/code&gt;, &lt;code&gt;cb-wallet-analytics&lt;/code&gt;, &lt;code&gt;cb-wallet-data&lt;/code&gt;, &lt;code&gt;cb-wallet-http&lt;/code&gt;, &lt;code&gt;cb-wallet-metadata&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;cb-wallet-solana-provider&lt;/code&gt;, &lt;code&gt;cb-wallet-store&lt;/code&gt;, &lt;code&gt;wallet-cds-web&lt;/code&gt;, and &lt;code&gt;wallet-engine-signing&lt;/code&gt;. Every&lt;br&gt;
name reads like something you'd find inside a wallet product's internal toolchain — a core library,&lt;br&gt;
a mobile variant, a Solana provider, a signing engine. None of them is a package anyone was&lt;br&gt;
supposed to install.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the names are doing
&lt;/h2&gt;

&lt;p&gt;Coinbase's actual wallet SDK is &lt;code&gt;@coinbase/wallet-sdk&lt;/code&gt;, a scoped package that's been on npm since&lt;br&gt;
March 2022. None of the 13 flagged packages use that scope, or any scope at all. What they share&lt;br&gt;
instead is a naming convention — &lt;code&gt;cb-wallet-&lt;/code&gt; prefixes, &lt;code&gt;scw-&lt;/code&gt; prefixes (a plausible shorthand for&lt;br&gt;
"smart contract wallet" or "smart wallet"), and &lt;code&gt;base-app-&lt;/code&gt;/&lt;code&gt;base-account-&lt;/code&gt; prefixes — that mimics&lt;br&gt;
how a real company organises internal packages for a wallet product, without being any package&lt;br&gt;
that company actually publishes.&lt;/p&gt;

&lt;p&gt;That distinction matters more than it sounds. A typosquat depends on a developer mistyping a name&lt;br&gt;
they already know. This is closer to guessing: if you were integrating wallet infrastructure and&lt;br&gt;
saw &lt;code&gt;cb-wallet-http&lt;/code&gt; or &lt;code&gt;wallet-engine-signing&lt;/code&gt; suggested to you — by a teammate, a Stack Overflow&lt;br&gt;
answer, or an AI assistant scaffolding an integration — nothing about the name looks wrong. It&lt;br&gt;
looks like exactly the kind of internal-sounding dependency a larger SDK would split itself into.&lt;/p&gt;

&lt;p&gt;Each advisory carries the same boilerplate GitHub uses across its malware reports, classified as&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-vgm8-2vf8-769j" rel="noopener noreferrer"&gt;CWE-506, embedded malicious code&lt;/a&gt;: "Any&lt;br&gt;
computer that has this package installed or running should be considered fully compromised,"&lt;br&gt;
with instructions to rotate all secrets and keys from a separate, uncompromised machine. None of&lt;br&gt;
the 13 advisories names a researcher, a discovering organisation, or an attack narrative beyond&lt;br&gt;
that template. That's normal for GitHub's malware feed — most entries are this terse — but it&lt;br&gt;
means the interesting part isn't the advisory text. It's the registry data underneath it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Forty-eight seconds
&lt;/h2&gt;

&lt;p&gt;Querying &lt;code&gt;registry.npmjs.org&lt;/code&gt; for each package name returns a &lt;code&gt;time.created&lt;/code&gt; field precise to the&lt;br&gt;
millisecond. Lined up in order:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Created (UTC)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;base-account-core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:54:59.919&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;base-app-data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:04.459&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cb-wallet-analytics&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:08.327&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cb-wallet-data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:12.500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cb-wallet-env&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:16.997&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cb-wallet-http&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:20.821&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cb-wallet-metadata&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:24.805&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cb-wallet-solana-provider&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:28.754&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cb-wallet-store&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:32.789&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scw-core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:36.704&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scw-mobile&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:40.513&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wallet-cds-web&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:44.436&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wallet-engine-signing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;04:55:48.234&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Thirteen packages, published roughly four seconds apart, start to finish in 48.3 seconds. That&lt;br&gt;
cadence is not a person running &lt;code&gt;npm publish&lt;/code&gt; thirteen times. It's a script working through a&lt;br&gt;
prepared list of names, and it's the kind of detail an advisory's prose never mentions because&lt;br&gt;
it isn't in GitHub's data — it only shows up if you go and query the registry directly for each&lt;br&gt;
name involved.&lt;/p&gt;

&lt;p&gt;You can reproduce this yourself against any package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://registry.npmjs.org/scw-core | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"import json,sys; d=json.load(sys.stdin); print(d['time']['created'], list(d['versions']))"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running it today against all 13 returns the same thing for each: a single version,&lt;br&gt;
&lt;code&gt;0.0.1-security&lt;/code&gt;, and an empty maintainers list. That's the registry's placeholder state after a&lt;br&gt;
package is pulled — the original code is gone, but the name, and the creation timestamp, stay on&lt;br&gt;
the record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a name-only check wouldn't have caught this in time
&lt;/h2&gt;

&lt;p&gt;Every one of these 13 names is syntactically fine. None of them typosquats an existing package&lt;br&gt;
character-for-character, so a Levenshtein-distance check against &lt;code&gt;@coinbase/wallet-sdk&lt;/code&gt; wouldn't&lt;br&gt;
flag &lt;code&gt;wallet-engine-signing&lt;/code&gt; — it isn't close enough in spelling to trip that kind of comparison.&lt;br&gt;
What connects the 13 is adoption and timing, not spelling: brand-new names, published in a tight&lt;br&gt;
burst, with zero history, imitating an ecosystem's naming pattern rather than one specific&lt;br&gt;
package's name.&lt;/p&gt;

&lt;p&gt;That's the case for checking a dependency's registry metadata at the point you add it, not just&lt;br&gt;
its spelling against a known-package list. A package published minutes or hours ago, with no&lt;br&gt;
prior versions and no maintainer history, is a different risk category from one that's been&lt;br&gt;
downloaded for years — regardless of how legitimate its name sounds. None of these 13 would have&lt;br&gt;
shown up on a scanner that only compares names against a blocklist of known-bad strings, because&lt;br&gt;
on the morning they were published, none of them was on any blocklist yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this doesn't tell you
&lt;/h2&gt;

&lt;p&gt;GitHub's advisories don't say how the 13 packages were distributed, whether any of them were&lt;br&gt;
pulled into a real build, or who published them — there's no credited researcher, no linked&lt;br&gt;
writeup, and no campaign name grouping them together the way some GHSA entries do. The naming&lt;br&gt;
pattern is our own observation from reading the list, not a claim GitHub makes; we don't know&lt;br&gt;
whether the person behind it was specifically targeting Coinbase integrators or casting a wider&lt;br&gt;
net with a name-generation script. The 48-second publishing window is exact, because it comes&lt;br&gt;
straight from npm's own timestamps, but it tells you how the packages were published, not who&lt;br&gt;
published them or why. Treat the mechanism as established and the motive as open.&lt;/p&gt;

</description>
      <category>security</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>What is slopsquatting, and why your scanner cannot see it</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Wed, 09 Sep 2026 17:27:20 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/what-is-slopsquatting-and-why-your-scanner-cannot-see-it-4fma</link>
      <guid>https://dev.to/mag_solutions_ai/what-is-slopsquatting-and-why-your-scanner-cannot-see-it-4fma</guid>
      <description>&lt;p&gt;A developer asks an AI assistant for code that uploads a file to an S3 bucket. The assistant&lt;br&gt;
returns twelve confident lines, including an import of a helper library. The library sounds&lt;br&gt;
plausible. It has the naming conventions of a real package. It does not exist.&lt;/p&gt;

&lt;p&gt;Six months ago, that was a broken build and an annoyed developer. Today it can be a compromised&lt;br&gt;
build server, because somebody registered that exact name and put an install script inside it.&lt;/p&gt;

&lt;p&gt;That attack has a name now: &lt;strong&gt;slopsquatting&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The difference from typosquatting
&lt;/h2&gt;

&lt;p&gt;Typosquatting bets on human error. An attacker registers &lt;code&gt;reqeusts&lt;/code&gt; and waits for someone to&lt;br&gt;
fat-finger &lt;code&gt;requests&lt;/code&gt;. It works, but it scales badly: the attacker is guessing at the space of&lt;br&gt;
plausible typos, and most typos are never made.&lt;/p&gt;

&lt;p&gt;Slopsquatting bets on machine error instead, and machine error turns out to be a much better&lt;br&gt;
thing to bet on for one specific reason:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hallucinated package names repeat. Research measuring this found that a substantial share of&lt;br&gt;
invented names come back consistently when the same prompt is issued again — roughly 43% in the&lt;br&gt;
study that named the phenomenon. That reproducibility is the whole attack.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A typo is random. A hallucination is a &lt;em&gt;prediction&lt;/em&gt; the model keeps making. An attacker does not&lt;br&gt;
have to guess which invented name is worth registering — they can ask the model repeatedly,&lt;br&gt;
write down what it invents, and register the names that keep coming back. The model then sends&lt;br&gt;
them a stream of victims, for free, indefinitely.&lt;/p&gt;
&lt;h2&gt;
  
  
  How common is this
&lt;/h2&gt;

&lt;p&gt;The study that put numbers on it examined 2.23 million AI-generated code samples and found&lt;br&gt;
&lt;strong&gt;19.7% contained at least one package that does not exist&lt;/strong&gt;. Later measurements in 2026 place&lt;br&gt;
the rate closer to 5% as models improved.&lt;/p&gt;

&lt;p&gt;Five percent sounds manageable until you multiply it by how much code is now written this way.&lt;br&gt;
If your team merges 200 AI-assisted pull requests a month and 5% of them carry a phantom import,&lt;br&gt;
that is ten opportunities a month for someone else's code to enter your build. You need to be&lt;br&gt;
right every time. The attacker needs you to be wrong once.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why the tools you already pay for miss it
&lt;/h2&gt;

&lt;p&gt;This is the part that surprises people, and it is not a bug in those tools. It is what they are&lt;br&gt;
for.&lt;/p&gt;

&lt;p&gt;Snyk, Dependabot, &lt;code&gt;npm audit&lt;/code&gt; and every other conventional dependency scanner answer one&lt;br&gt;
question: &lt;em&gt;does this package have a known vulnerability?&lt;/em&gt; To answer it, they look the package up&lt;br&gt;
in a vulnerability database — CVEs, advisories, disclosure history.&lt;/p&gt;

&lt;p&gt;A package name registered five minutes ago has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no CVE&lt;/li&gt;
&lt;li&gt;no advisory&lt;/li&gt;
&lt;li&gt;no disclosure history&lt;/li&gt;
&lt;li&gt;no download history to look suspicious against&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the scanner queries its database, finds nothing, and reports nothing. &lt;strong&gt;Silence gets read as&lt;br&gt;
safety.&lt;/strong&gt; The tool did its job correctly and told you nothing useful, because you asked it the&lt;br&gt;
wrong question.&lt;/p&gt;

&lt;p&gt;The right question is not "is this package known to be malicious?" It is "&lt;strong&gt;does this package&lt;br&gt;
exist at all, and if it does, has it existed for longer than this attack has?&lt;/strong&gt;"&lt;/p&gt;
&lt;h2&gt;
  
  
  Existence is not enough either
&lt;/h2&gt;

&lt;p&gt;Here is the trap in the obvious fix. If you just check whether a package exists, you catch the&lt;br&gt;
easy case — the pure hallucination that nobody has claimed yet — and you miss the dangerous one.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;claimed&lt;/em&gt; slopsquat exists. It resolves. It installs. &lt;code&gt;pip&lt;/code&gt; is perfectly happy. The registry&lt;br&gt;
returns HTTP 200 and a version number.&lt;/p&gt;

&lt;p&gt;What distinguishes it is not existence but &lt;strong&gt;history&lt;/strong&gt;: it was published recently, it has&lt;br&gt;
almost no adoption, and nothing depends on it. A package that has been on PyPI for six years&lt;br&gt;
with millions of downloads is a different kind of object from one uploaded last Tuesday with&lt;br&gt;
forty downloads, even though both return 200.&lt;/p&gt;

&lt;p&gt;Any check worth running has to look at both.&lt;/p&gt;
&lt;h2&gt;
  
  
  The reproducible version of this check
&lt;/h2&gt;

&lt;p&gt;You do not need a vendor to verify existence. For Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://pypi.org/pypi/PACKAGE_NAME/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTTP 404 means the package does not exist. For npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://registry.npmjs.org/PACKAGE_NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That single fact — 404 or 200 — is not a heuristic, a model output or a confidence score. It is&lt;br&gt;
an oracle. It cannot produce a false positive, because either the registry has the name or it&lt;br&gt;
does not.&lt;/p&gt;

&lt;p&gt;Then check the age and adoption of anything that does resolve. The full JSON response from PyPI&lt;br&gt;
includes upload timestamps per release; anything published within the last 90 days with&lt;br&gt;
negligible downloads deserves a human look before it reaches a build server.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means in practice
&lt;/h2&gt;

&lt;p&gt;The uncomfortable summary: the security tooling most teams rely on was designed for a world&lt;br&gt;
where the packages in your manifest were chosen by a person who had heard of them. That&lt;br&gt;
assumption quietly stopped being true, and nothing in the tool chain announced the change.&lt;/p&gt;

&lt;p&gt;The check itself is cheap — one HTTP request per dependency. The hard part is doing it on every&lt;br&gt;
pull request, before merge, without asking developers to remember.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
    </item>
    <item>
      <title>The malicious package that never showed up in anyone's requirements.txt</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Wed, 09 Sep 2026 17:27:18 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/the-malicious-package-that-never-showed-up-in-anyones-requirementstxt-2ce6</link>
      <guid>https://dev.to/mag_solutions_ai/the-malicious-package-that-never-showed-up-in-anyones-requirementstxt-2ce6</guid>
      <description>&lt;p&gt;On 3 September 2026, GitHub's Advisory Database published two malware reports for PyPI packages,&lt;br&gt;
credited to OpenSSF's malicious-packages tracking effort. Read separately, they look like routine&lt;br&gt;
entries in a database that logs thousands of these a year. Read together, they describe a specific&lt;br&gt;
trick worth understanding, because it defeats the most common way developers check a package&lt;br&gt;
before installing it: looking at the name in their own requirements file.&lt;/p&gt;
&lt;h2&gt;
  
  
  The two packages
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/advisories/GHSA-r35m-jqvr-hwxp" rel="noopener noreferrer"&gt;&lt;code&gt;telemetry-helper&lt;/code&gt;&lt;/a&gt; is the one that actually&lt;br&gt;
steals data. According to the advisory, importing it starts a background thread that waits 30&lt;br&gt;
seconds, then sends the hostname, username, working directory and the complete environment&lt;br&gt;
variable set to a webhook endpoint. Environment variables are exactly where AWS keys, GitHub&lt;br&gt;
tokens and database credentials tend to live, so "complete environment variables" is not a small&lt;br&gt;
claim.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/advisories/GHSA-49jc-fj5g-832c" rel="noopener noreferrer"&gt;&lt;code&gt;env-validator-tool&lt;/code&gt;&lt;/a&gt; does not steal anything&lt;br&gt;
itself. Its advisory says its malicious functionality "is intentionally included in a dependency&lt;br&gt;
of the package" — it exists to pull &lt;code&gt;telemetry-helper&lt;/code&gt; in as a dependency. GitHub groups both&lt;br&gt;
advisories under the same campaign label, &lt;code&gt;2026-09-telemetry-helper&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Neither name is dramatic. Both fit the pattern of a hundred legitimate internal utilities: a&lt;br&gt;
config validator, a telemetry shim. That is very likely the point. A package called&lt;br&gt;
&lt;code&gt;steal-your-aws-keys&lt;/code&gt; gets noticed. A package called &lt;code&gt;env-validator-tool&lt;/code&gt; gets installed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why "check your dependencies" isn't precise enough
&lt;/h2&gt;

&lt;p&gt;If you install &lt;code&gt;env-validator-tool&lt;/code&gt;, your requirements file says &lt;code&gt;env-validator-tool&lt;/code&gt;. It does&lt;br&gt;
not say &lt;code&gt;telemetry-helper&lt;/code&gt;. That name only appears in the resolved dependency tree — the full&lt;br&gt;
set of packages &lt;code&gt;pip&lt;/code&gt; actually pulls down, including whatever your direct dependencies decided to&lt;br&gt;
depend on themselves.&lt;/p&gt;

&lt;p&gt;Most manual review stops at the file a person edited. Nobody sits down and reads the resolved&lt;br&gt;
tree line by line, because for any project with more than a handful of dependencies it runs to&lt;br&gt;
hundreds of entries, most of them unfamiliar on sight. That gap — between what you chose and what&lt;br&gt;
you actually run — is exactly where this campaign sits.&lt;/p&gt;

&lt;p&gt;This is a different failure mode from the slopsquatting case we've written about before, where an&lt;br&gt;
AI assistant invents a name that does not exist and an attacker registers it. Here both names are&lt;br&gt;
real, both were published to PyPI, and the one doing damage was never typed by a human at all. It&lt;br&gt;
arrived as someone else's choice.&lt;/p&gt;
&lt;h2&gt;
  
  
  The check that would have caught it
&lt;/h2&gt;

&lt;p&gt;You do not need to install a package to see what it depends on. PyPI's JSON API exposes the&lt;br&gt;
declared dependencies of any release under &lt;code&gt;requires_dist&lt;/code&gt;, before you run &lt;code&gt;pip install&lt;/code&gt; on&lt;br&gt;
anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://pypi.org/pypi/PACKAGE_NAME/json | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"import json,sys; print(json.load(sys.stdin)['info']['requires_dist'])"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it against a real package and you get a real answer. For &lt;code&gt;requests&lt;/code&gt; right now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;charset_normalizer&amp;lt;4,&amp;gt;=2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;idna&amp;lt;4,&amp;gt;=2.5&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;urllib3&amp;lt;3,&amp;gt;=1.26&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;certifi&amp;gt;=2023.5.7&lt;/span&gt;&lt;span class="sh"&gt;'&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;Four names you did not type, all pulled in on your behalf. That is normal — &lt;code&gt;requests&lt;/code&gt; has been&lt;br&gt;
depending on those libraries for years, and they have their own long histories. The difference&lt;br&gt;
with &lt;code&gt;env-validator-tool&lt;/code&gt; is that running the same command against it, before installing, would&lt;br&gt;
have surfaced &lt;code&gt;telemetry-helper&lt;/code&gt; in the list: one more name to check, and a check that would have&lt;br&gt;
failed, because &lt;code&gt;telemetry-helper&lt;/code&gt; was published with no history behind it either.&lt;/p&gt;

&lt;p&gt;At the time of writing, both names return &lt;code&gt;404&lt;/code&gt; from PyPI — pulled after the advisory. That is&lt;br&gt;
the system working as intended, but it also marks the edge of what a point-in-time check can do.&lt;br&gt;
A scan run today finds nothing, because the packages are gone. A scan run in the window between&lt;br&gt;
publication and takedown is the only one that would have mattered, which is the same argument for&lt;br&gt;
running this at every pull request rather than occasionally: the check is only useful while the&lt;br&gt;
threat is still live, and you do not get to choose when that window is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this doesn't tell you
&lt;/h2&gt;

&lt;p&gt;We don't know how many projects installed either package, or whether the campaign succeeded&lt;br&gt;
anywhere — GitHub's advisory doesn't say, and we have not found a source that does. We also don't&lt;br&gt;
know whether &lt;code&gt;env-validator-tool&lt;/code&gt; was hand-picked to sound trustworthy or just happened to. Both&lt;br&gt;
are reasonable guesses, neither is a fact we can point to.&lt;/p&gt;

&lt;p&gt;What the advisory does establish, plainly, is the mechanism: a package with no history depending&lt;br&gt;
on a second package with no history, and the dependency relationship being the only place that&lt;br&gt;
second name appears. Checking the packages you named and skipping the ones your dependencies&lt;br&gt;
named for you leaves exactly that gap open.&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>The package that never existed and got 30,000 downloads</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Wed, 09 Sep 2026 17:27:15 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/the-package-that-never-existed-and-got-30000-downloads-4eh2</link>
      <guid>https://dev.to/mag_solutions_ai/the-package-that-never-existed-and-got-30000-downloads-4eh2</guid>
      <description>&lt;p&gt;The clearest demonstration that slopsquatting works was not an attack. It was a researcher&lt;br&gt;
proving a point, and the result was worse than most people expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened
&lt;/h2&gt;

&lt;p&gt;Bar Lanyado, a researcher at Lasso Security, noticed that AI coding assistants kept recommending&lt;br&gt;
a Python package called &lt;code&gt;huggingface-cli&lt;/code&gt;. The recommendation was plausible: Hugging Face is a&lt;br&gt;
real and widely used platform, and plenty of tools ship a companion CLI package with exactly&lt;br&gt;
that naming pattern.&lt;/p&gt;

&lt;p&gt;The package did not exist. The real one is &lt;code&gt;huggingface_hub&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So he registered &lt;code&gt;huggingface-cli&lt;/code&gt; on PyPI as an empty placeholder — no functionality, no&lt;br&gt;
payload, nothing but a claim on the name — and waited to see whether anyone would install&lt;br&gt;
something that, until that moment, only existed inside a language model's output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In roughly three months it was downloaded more than 30,000 times.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that should worry you
&lt;/h2&gt;

&lt;p&gt;Downloads alone could be explained away as scanners, mirrors and CI noise. The finding that&lt;br&gt;
cannot be explained away is where the name turned up: in &lt;strong&gt;public documentation and README&lt;br&gt;
files belonging to large technology companies&lt;/strong&gt;, including one belonging to Alibaba.&lt;/p&gt;

&lt;p&gt;That is the mechanism working end to end. Somebody asked a model how to do something. The model&lt;br&gt;
invented a package. The developer copied the command into a README. The README was published.&lt;br&gt;
Now the invented name has a citation — and the next developer who finds that README has every&lt;br&gt;
reason to trust it, because it is on a real company's repository, not in a chat window.&lt;/p&gt;

&lt;p&gt;The hallucination stopped being a model output and became documentation. At that point it&lt;br&gt;
propagates without the model's help at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why an empty package is the dangerous version
&lt;/h2&gt;

&lt;p&gt;Lanyado's package did nothing. That is what made it a demonstration rather than an incident.&lt;/p&gt;

&lt;p&gt;A real attacker registering that same name has a &lt;code&gt;setup.py&lt;/code&gt; that runs on install. Not on import&lt;br&gt;
— &lt;strong&gt;on install&lt;/strong&gt;. The victim does not have to call a single function or even successfully run&lt;br&gt;
their program. &lt;code&gt;pip install -r requirements.txt&lt;/code&gt; in a CI pipeline is enough, and CI pipelines&lt;br&gt;
typically run with credentials that a laptop does not have: registry tokens, cloud roles,&lt;br&gt;
deployment keys.&lt;/p&gt;

&lt;p&gt;The uncomfortable arithmetic: the attacker does not need the victim to be careless. They need&lt;br&gt;
one dependency file, in one repository, to contain a name that a machine invented and a human&lt;br&gt;
did not verify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why nothing caught it
&lt;/h2&gt;

&lt;p&gt;Consider what the standard tooling would have seen at each stage.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;What a scanner sees&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before registration&lt;/td&gt;
&lt;td&gt;Name not in any vulnerability DB&lt;/td&gt;
&lt;td&gt;Nothing to report&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After registration, empty&lt;/td&gt;
&lt;td&gt;New package, no CVE, no advisory&lt;/td&gt;
&lt;td&gt;Nothing to report&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After registration, malicious&lt;/td&gt;
&lt;td&gt;Still no CVE until someone discovers and files one&lt;/td&gt;
&lt;td&gt;Nothing to report&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At no point does a CVE-based scanner have anything to say, and the window between "attacker&lt;br&gt;
registers the name" and "someone files an advisory" is exactly the window in which the attack&lt;br&gt;
works. By the time there is a CVE, the compromise already happened somewhere.&lt;/p&gt;

&lt;p&gt;The only check that fires &lt;em&gt;before&lt;/em&gt; the package is registered is the one that asks whether the&lt;br&gt;
name exists at all. That check would have flagged &lt;code&gt;huggingface-cli&lt;/code&gt; on day zero — not because&lt;br&gt;
it was known to be bad, but because it was not known to be anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to take from this
&lt;/h2&gt;

&lt;p&gt;Three things this case establishes that were previously arguments rather than evidence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AI-invented package names reach production repositories.&lt;/strong&gt; Not hypothetically — they were
found in the public documentation of companies with serious engineering organisations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The names persist and spread beyond the model.&lt;/strong&gt; Once a hallucinated command is copied into
a README, an issue comment or a Stack Overflow answer, it has a life of its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registering the name is trivially cheap.&lt;/strong&gt; The barrier to becoming the owner of a name that
thousands of developers will type is a free account and a few minutes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The corresponding defence is equally unglamorous: verify that every dependency you are about to&lt;br&gt;
add is a package that actually existed before your assistant mentioned it, and treat anything&lt;br&gt;
recently published with near-zero adoption as unproven rather than fine.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
    </item>
    <item>
      <title>How to check whether a package actually exists before you install it</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Wed, 09 Sep 2026 17:27:12 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/how-to-check-whether-a-package-actually-exists-before-you-install-it-4m7e</link>
      <guid>https://dev.to/mag_solutions_ai/how-to-check-whether-a-package-actually-exists-before-you-install-it-4m7e</guid>
      <description>&lt;p&gt;Your assistant suggested a dependency. Before it reaches a build server, you want to know two&lt;br&gt;
things: does this package exist, and has it existed long enough to be trustworthy? Both are&lt;br&gt;
answerable in a few seconds, with no tooling beyond &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Python: does the name resolve on PyPI
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://pypi.org/pypi/requests/json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Read the status code:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;200&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The package exists on PyPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;404&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;The package does not exist.&lt;/strong&gt; Nothing is going to fix this at install time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;5xx&lt;/code&gt; or timeout&lt;/td&gt;
&lt;td&gt;PyPI is unreachable. Unverified — this is not the same as safe&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That third row matters more than it looks. When a registry check fails for network reasons, the&lt;br&gt;
honest answer is "unknown", not "fine". Any tooling that silently downgrades an unreachable&lt;br&gt;
registry to a pass is telling you something it does not know.&lt;/p&gt;
&lt;h2&gt;
  
  
  npm: the same question
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sI&lt;/span&gt; https://registry.npmjs.org/express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Same reading of the status code. Note that scoped packages need URL encoding — &lt;code&gt;@scope/name&lt;/code&gt;&lt;br&gt;
becomes &lt;code&gt;@scope%2Fname&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The check people skip: how old is it
&lt;/h2&gt;

&lt;p&gt;If the package resolves, you are not finished. A &lt;em&gt;claimed&lt;/em&gt; slopsquat resolves perfectly — that&lt;br&gt;
is the point of claiming it. What separates a real dependency from a freshly registered trap is&lt;br&gt;
history, and history is in the full JSON response rather than the headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://pypi.org/pypi/PACKAGE/json | python &lt;span class="nt"&gt;-m&lt;/span&gt; json.tool | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-40&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two fields are worth your attention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The earliest &lt;code&gt;upload_time_iso_8601&lt;/code&gt; across releases.&lt;/strong&gt; This is roughly when the name started
existing. Days or weeks is a very different risk profile from years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The release count and version history.&lt;/strong&gt; A package with one version, published recently, that
nothing else depends on, is a package with no track record — whatever its contents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For npm the equivalent lives in the &lt;code&gt;time&lt;/code&gt; object of the registry response, with &lt;code&gt;created&lt;/code&gt; giving&lt;br&gt;
you the first publication.&lt;/p&gt;

&lt;p&gt;There is no universal threshold, but a reasonable default: anything published in the &lt;strong&gt;last 90&lt;br&gt;
days with negligible adoption&lt;/strong&gt; is unproven and gets a human look before it enters a build.&lt;br&gt;
That is not an accusation, it is a queue.&lt;/p&gt;
&lt;h2&gt;
  
  
  Local and internal packages: the false positive to expect
&lt;/h2&gt;

&lt;p&gt;If you script this check, one thing will bite you immediately: not every line in a requirements&lt;br&gt;
file is a name that should resolve against a public registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-e .
./libs/internal-utils
git+https://github.com/yourorg/internal-lib.git
some-package @ file:///opt/wheels/some_package.whl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;None of these are looked up on PyPI by &lt;code&gt;pip&lt;/code&gt;. They are local paths, VCS references and direct&lt;br&gt;
URLs. A naive checker queries the registry for &lt;code&gt;internal-utils&lt;/code&gt;, gets a 404, and reports a&lt;br&gt;
phantom dependency inside your own monorepo. In npm the equivalent protocols are &lt;code&gt;workspace:&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;file:&lt;/code&gt;, &lt;code&gt;link:&lt;/code&gt; and &lt;code&gt;portal:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We learned this one in the field rather than in theory: a scan across public pull requests&lt;br&gt;
produced exactly two "phantom" findings, and both turned out to be internal packages from the&lt;br&gt;
same repository being installed by path. Any check that does not exclude these will cry wolf on&lt;br&gt;
its first day in a real monorepo, and a scanner that cries wolf gets ignored — which is worse&lt;br&gt;
than not having one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing this on every pull request
&lt;/h2&gt;

&lt;p&gt;The manual version is fine for a dependency you are personally adding. It does not survive&lt;br&gt;
contact with a team, because the failure mode is not "the check is hard", it is "nobody&lt;br&gt;
remembers to run it at 6pm on a Friday".&lt;/p&gt;

&lt;p&gt;That is the entire argument for automating it at the pull request boundary rather than in a&lt;br&gt;
developer's terminal: the check has to happen where merging happens, not where good intentions&lt;br&gt;
happen.&lt;/p&gt;

</description>
      <category>python</category>
      <category>javascript</category>
      <category>security</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
