<?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: ai-coding-radar</title>
    <description>The latest articles on DEV Community by ai-coding-radar (@aicodingradar).</description>
    <link>https://dev.to/aicodingradar</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%2F4075946%2F8c6208d4-b528-4223-b18a-b02ce490c0e9.png</url>
      <title>DEV Community: ai-coding-radar</title>
      <link>https://dev.to/aicodingradar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aicodingradar"/>
    <language>en</language>
    <item>
      <title>Fail-closed npm and PyPI vulnerability checks in n8n</title>
      <dc:creator>ai-coding-radar</dc:creator>
      <pubDate>Fri, 14 Aug 2026 19:19:24 +0000</pubDate>
      <link>https://dev.to/aicodingradar/fail-closed-npm-and-pypi-vulnerability-checks-in-n8n-1hcf</link>
      <guid>https://dev.to/aicodingradar/fail-closed-npm-and-pypi-vulnerability-checks-in-n8n-1hcf</guid>
      <description>&lt;h1&gt;
  
  
  Fail-closed npm and PyPI vulnerability checks in n8n
&lt;/h1&gt;

&lt;p&gt;A dependency scan is only useful when it answers two different questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Did the public vulnerability sources report a finding for the exact version
I run?&lt;/li&gt;
&lt;li&gt;Did every required source answer successfully?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Treating a timeout as "zero vulnerabilities" makes a dashboard look clean at&lt;br&gt;
the exact moment it has stopped checking. This workflow keeps those states&lt;br&gt;
separate and can run on a daily n8n schedule without maintaining a scanner&lt;br&gt;
server.&lt;/p&gt;
&lt;h2&gt;
  
  
  Use exact installed versions
&lt;/h2&gt;

&lt;p&gt;Start with the versions from your lockfile or deployment inventory, not a&lt;br&gt;
range and not the registry's current &lt;code&gt;latest&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"packages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lodash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ecosystem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.17.20"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"requests"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ecosystem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PyPI"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.31.0"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"includeDownloads"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"includeVulnerabilities"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"includeRepository"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"includeCisaKev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An exact version lets OSV answer for what is actually deployed. The result also&lt;br&gt;
contains the registry's latest version, but it never silently replaces the&lt;br&gt;
version you requested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Import the workflow
&lt;/h2&gt;

&lt;p&gt;Download the tested five-node n8n workflow:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://raw.githubusercontent.com/Jarvis-Dong/oss-package-health-monitor/main/examples/n8n-oss-exact-version-scan.json" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/Jarvis-Dong/oss-package-health-monitor/main/examples/n8n-oss-exact-version-scan.json&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After import:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an n8n &lt;strong&gt;Header Auth&lt;/strong&gt; credential named &lt;code&gt;Authorization&lt;/code&gt; with value
&lt;code&gt;Bearer YOUR_APIFY_API_TOKEN&lt;/code&gt; and attach it to the HTTP Request node.&lt;/li&gt;
&lt;li&gt;Replace the two sample packages with your exact installed versions.&lt;/li&gt;
&lt;li&gt;Replace the final no-op node with Slack, email, Teams, Notion, a database,
or a private webhook.&lt;/li&gt;
&lt;li&gt;Test once, then activate the daily schedule.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The public workflow export deliberately contains no token, cookie, credential&lt;br&gt;
ID, or destination URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route findings without hiding failures
&lt;/h2&gt;

&lt;p&gt;The filter forwards a row when any of these conditions is true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;vulnerabilities&lt;/code&gt; contains an OSV record;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cisaKevMatches&lt;/code&gt; contains a CVE listed in CISA KEV;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt; is &lt;code&gt;partial&lt;/code&gt; or &lt;code&gt;error&lt;/code&gt; because a required source failed;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isLatest&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;, so the installed version can be reviewed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An OSV error leaves &lt;code&gt;vulnerabilities&lt;/code&gt; and &lt;code&gt;vulnerabilityCount&lt;/code&gt; as &lt;code&gt;null&lt;/code&gt;. It is&lt;br&gt;
not converted into an empty list. That distinction is what makes the workflow&lt;br&gt;
fail closed instead of producing a false clean result.&lt;/p&gt;

&lt;p&gt;The Actor queries public npm and PyPI registry metadata, the&lt;br&gt;
&lt;a href="https://osv.dev/" rel="noopener noreferrer"&gt;OSV API&lt;/a&gt;, and the&lt;br&gt;
&lt;a href="https://www.cisa.gov/known-exploited-vulnerabilities-catalog" rel="noopener noreferrer"&gt;CISA Known Exploited Vulnerabilities catalog&lt;/a&gt;.&lt;br&gt;
It does not access private packages or automatically change dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the API with visible pricing
&lt;/h2&gt;

&lt;p&gt;The public Actor and exact-version example are available here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actor: &lt;a href="https://apify.com/ai-coding-radar/oss-package-health-monitor" rel="noopener noreferrer"&gt;https://apify.com/ai-coding-radar/oss-package-health-monitor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Example: &lt;a href="https://apify.com/ai-coding-radar/oss-package-health-monitor/examples/scan-installed-npm-and-pypi-versions-for-cves" rel="noopener noreferrer"&gt;https://apify.com/ai-coding-radar/oss-package-health-monitor/examples/scan-installed-npm-and-pypi-versions-for-cves&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source and Make recipe: &lt;a href="https://github.com/Jarvis-Dong/oss-package-health-monitor" rel="noopener noreferrer"&gt;https://github.com/Jarvis-Dong/oss-package-health-monitor&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The published price is &lt;code&gt;$0.0015&lt;/code&gt; per returned package record plus the small&lt;br&gt;
Actor-start event shown on the Store page. Start with a short dependency list,&lt;br&gt;
keep the source-status fields in downstream alerts, and treat the output as&lt;br&gt;
point-in-time evidence rather than a security certification.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>security</category>
      <category>software</category>
    </item>
  </channel>
</rss>
