<?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: Hugo | DevOps | Cybersecurity</title>
    <description>The latest articles on DEV Community by Hugo | DevOps | Cybersecurity (@hugovalters).</description>
    <link>https://dev.to/hugovalters</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%2F1175774%2F8a57b46a-da15-4ac2-ab41-fbe56c6a99a5.jpg</url>
      <title>DEV Community: Hugo | DevOps | Cybersecurity</title>
      <link>https://dev.to/hugovalters</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hugovalters"/>
    <language>en</language>
    <item>
      <title>Why One Source Is Never Enough: How We Decide a Vulnerability Is Fixed</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Tue, 20 Oct 2026 14:15:04 +0000</pubDate>
      <link>https://dev.to/hugovalters/why-one-source-is-never-enough-how-we-decide-a-vulnerability-is-fixed-45h7</link>
      <guid>https://dev.to/hugovalters/why-one-source-is-never-enough-how-we-decide-a-vulnerability-is-fixed-45h7</guid>
      <description>&lt;p&gt;Every vulnerability record on this platform answers one practical question: &lt;strong&gt;is there a fix, or not?&lt;/strong&gt; The honest answer is often harder to establish than it looks — because the first entity to say "this is fixed" is frequently the one with the least independent standing to confirm it.&lt;/p&gt;

&lt;p&gt;Our position is simple and it does not change: &lt;strong&gt;no single source is treated as final.&lt;/strong&gt; A vendor, a CNA, or an automated advisory feed stating that a version is fixed is a &lt;em&gt;claim&lt;/em&gt;. We record that claim, we show it to you plainly — and we mark the record &lt;strong&gt;PATCH UNDER REVIEW&lt;/strong&gt; until an &lt;strong&gt;independent, separate-origin source&lt;/strong&gt; (a Linux distribution's own security team, an enterprise errata feed, or an ecosystem database that did its own analysis) confirms the fix. Only then does a record turn green.&lt;/p&gt;

&lt;p&gt;This is not caution for its own sake. The history of security is full of cases where the first "it's fixed" was wrong — sometimes by accident, sometimes on purpose.&lt;/p&gt;




&lt;h2&gt;
  
  
  A trusted source can be wrong — or hostile: xz-utils (CVE-2024-3094)
&lt;/h2&gt;

&lt;p&gt;In March 2024, a backdoor was discovered in xz-utils, a compression library present in nearly every Linux distribution. It carried the maximum severity score of 10.0. What makes this case essential is &lt;em&gt;how&lt;/em&gt; it got there.&lt;/p&gt;

&lt;p&gt;The backdoor was not the work of an outside attacker breaking in. It was introduced by a project maintainer — an identity operating as "Jia Tan" that had spent roughly two years building trust, contributing legitimately, and applying pressure through sock-puppet accounts until it was handed maintainer rights to the project. The malicious code was then shipped &lt;strong&gt;in the released source tarballs, while being absent from the project's public Git repository.&lt;/strong&gt; An observer trusting the maintainer's published releases would have seen a normal, trusted, actively-maintained project.&lt;/p&gt;

&lt;p&gt;It was caught not by trusting the source, but by independent scrutiny: a Microsoft engineer, Andres Freund, investigating an unrelated performance oddity in a Debian development build. Linux distributions — Debian, Red Hat, SUSE — then independently reverted the affected packages, and the hosting platform temporarily disabled the project's repository.&lt;/p&gt;

&lt;p&gt;The lesson: &lt;strong&gt;the entity publishing a release is not, by itself, proof that the release is safe or that its claims are true.&lt;/strong&gt; Confirmation has to come from somewhere that did its own work.&lt;/p&gt;




&lt;h2&gt;
  
  
  The first "fixed" is often not the real fix: Log4Shell (CVE-2021-44228)
&lt;/h2&gt;

&lt;p&gt;In December 2021, Log4Shell became one of the most severe vulnerabilities ever disclosed, affecting an enormous share of Java applications worldwide. A fix was published quickly — version 2.15.0.&lt;/p&gt;

&lt;p&gt;Except 2.15.0 was not the end of it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;New CVE&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2.15.0&lt;/td&gt;
&lt;td&gt;Incomplete fix in certain configurations&lt;/td&gt;
&lt;td&gt;CVE-2021-45046&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.16.0&lt;/td&gt;
&lt;td&gt;Denial-of-service issue&lt;/td&gt;
&lt;td&gt;CVE-2021-45105&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.17.0&lt;/td&gt;
&lt;td&gt;Further remote-code-execution issue&lt;/td&gt;
&lt;td&gt;CVE-2021-44832&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2.17.1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Real, stable fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If a platform had simply recorded "fixed in 2.15.0" the moment that version shipped — because the upstream project said so — it would have told its users they were safe when they were not, three times over. The real, stable fix was established only through independent testing and disclosure over the following weeks.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/security/why-one-source-is-never-enough-how-we-decide-a-vulnerability-is-fixed/" rel="noopener noreferrer"&gt;valtersit.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🛠️ Partner Tools for Developers
&lt;/h3&gt;

&lt;p&gt;ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.&lt;/p&gt;

&lt;p&gt;➜ &lt;a href="https://www.valtersit.com/deals/" rel="noopener noreferrer"&gt;valtersit.com/deals/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vulnerabilitymanagement</category>
      <category>patchverification</category>
      <category>dataintegrity</category>
      <category>securityresearch</category>
    </item>
    <item>
      <title>Wazuh SIEM: Open Source Threat Detection Deployment</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Mon, 19 Oct 2026 14:15:05 +0000</pubDate>
      <link>https://dev.to/hugovalters/wazuh-siem-open-source-threat-detection-deployment-385l</link>
      <guid>https://dev.to/hugovalters/wazuh-siem-open-source-threat-detection-deployment-385l</guid>
      <description>&lt;p&gt;The PagerDuty pager went off 300 times between 02:00 and 06:00 on a Saturday. The on-call sysadmin — a good one, two months into the job — muted the channel at 03:40 and went back to sleep. Nobody blamed him. The root cause was a default Wazuh install forwarding every level-7 alert to PagerDuty, with no suppression, no tuning, and no ISM policy. That deployment was ripped out six weeks later and the org bought a commercial SIEM they couldn't afford. The tool wasn't the problem. The deployment was.&lt;/p&gt;

&lt;p&gt;This guide is for infrastructure and security engineers who are deploying Wazuh for real — not labbing it. By the end you'll know how to size it honestly, deploy it with TLS from day one, tune rules so you don't drown, wire up the integrations that pay for themselves, and keep it running past the 18-month mark when most open source SIEM installs quietly rot.&lt;/p&gt;

&lt;p&gt;:::note[TL;DR]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wazuh is a real SIEM + HIDS + compliance stack, but only if you separate the indexer from the manager past ~50 agents — the all-in-one installer is a lab toy.&lt;/li&gt;
&lt;li&gt;Set ISM retention on the indexer &lt;strong&gt;before&lt;/strong&gt; you ingest a single alert. Default is forever; you will fill a disk.&lt;/li&gt;
&lt;li&gt;Custom rules go in the &lt;code&gt;100000+&lt;/code&gt; ID range, always. Never edit stock rules in place.&lt;/li&gt;
&lt;li&gt;Don't forward everything to PagerDuty. Level ≥ 10 only, and only after a week of tuning.&lt;/li&gt;
&lt;li&gt;The tuning tax is real: budget 2-4 hours a week for the first quarter, then ~1 hour a week ongoing.
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Two or more Linux hosts (Ubuntu 22.04 LTS or RHEL/Rocky 9 are safe bets) with root or sudo&lt;/li&gt;
&lt;li&gt;Minimum 8 vCPU / 16 GB RAM / 200 GB SSD for a lab; see sizing section for prod&lt;/li&gt;
&lt;li&gt;A DNS name or IP plan for manager, indexer, dashboard, and agents&lt;/li&gt;
&lt;li&gt;Open ports per the table below (firewall both sides)&lt;/li&gt;
&lt;li&gt;Basic familiarity with &lt;code&gt;systemctl&lt;/code&gt;, &lt;code&gt;openssl&lt;/code&gt;, and reading XML without crying&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Wazuh Exists (And Why You Should Care)
&lt;/h2&gt;

&lt;p&gt;Most orgs can't afford Splunk at scale. Elastic's move to SSPL in 2021 burned a lot of people who'd built on the ELK stack. Wazuh is the FOSS answer that actually ships, and it bundles three things you'd otherwise buy separately: host-based intrusion detection (file integrity, rootkit, log analysis), SIEM correlation, and compliance evidence collection. The common failure mode is people install it, forget to tune it, and drown in 40k alerts a day.&lt;/p&gt;

&lt;h3&gt;
  
  
  The OSSIM Problem and the Agent-Based Advantage
&lt;/h3&gt;

&lt;p&gt;OSSIM (AlienVault's open source SIEM) died a slow death after AT&amp;amp;T acquired AlienVault, and its agentless, network-scanning model was already losing to agent-based telemetry for endpoint detection. Agents see file changes, process executions, and registry modifications that a network tap never will. Wazuh's architecture reflects this: an &lt;strong&gt;agent&lt;/strong&gt; on each endpoint forwards events to a &lt;strong&gt;manager&lt;/strong&gt; that evaluates rules and correlates, which writes to an &lt;strong&gt;indexer&lt;/strong&gt; (OpenSearch fork) that the &lt;strong&gt;dashboard&lt;/strong&gt; queries.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/security/wazuh-siem-open-source-threat-detection-deployment/" rel="noopener noreferrer"&gt;valtersit.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🛠️ Partner Tools for Developers
&lt;/h3&gt;

&lt;p&gt;ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.&lt;/p&gt;

&lt;p&gt;➜ &lt;a href="https://www.valtersit.com/deals/" rel="noopener noreferrer"&gt;valtersit.com/deals/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>wazuh</category>
      <category>siem</category>
      <category>security</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>Ceph Standalone: RADOS, RBD, CephFS Without Proxmox</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Sun, 18 Oct 2026 14:15:03 +0000</pubDate>
      <link>https://dev.to/hugovalters/ceph-standalone-rados-rbd-cephfs-without-proxmox-mcd</link>
      <guid>https://dev.to/hugovalters/ceph-standalone-rados-rbd-cephfs-without-proxmox-mcd</guid>
      <description>&lt;p&gt;A monitor got wedged at 03:14 on a Tuesday. The Proxmox cluster was fine — quorum held, VMs were running — but &lt;code&gt;ceph -s&lt;/code&gt; from the host returned a &lt;code&gt;HEALTH_WARN&lt;/code&gt; nobody on the team could interpret, because nobody on the team had ever run &lt;code&gt;ceph -s&lt;/code&gt; outside the GUI. The &lt;code&gt;pveceph&lt;/code&gt; wrapper had hidden the monmap, the wrapper had baked opinionated defaults into a config file that was actually a symlink into &lt;code&gt;/etc/pve&lt;/code&gt;, and the recovery procedure in the vendor KB started with "edit ceph.conf" on a file that was being regenerated by a daemon every thirty seconds. We lost four hours to a problem that a standalone cluster would have surfaced in a single &lt;code&gt;ceph mon dump&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This guide is for the admin who has outgrown the hypervisor-managed storage layer — or is about to build storage from scratch and wants to do it properly. By the end you'll have bootstrapped a standalone Ceph cluster with cephadm, built RADOS pools with sane placement, served RBD images to clients via systemd units, mounted CephFS with a standby MDS, and wired up the monitoring and backup paths that actually matter. No Proxmox, no wrappers, no config-file roulette.&lt;/p&gt;

&lt;p&gt;:::note[TL;DR]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proxmox's &lt;code&gt;pveceph&lt;/code&gt; integration hides the RADOS layer you need when things break; standalone cephadm gives you the real control surface.&lt;/li&gt;
&lt;li&gt;Three nodes is the floor, not the target — plan for at least four with a dedicated cluster network.&lt;/li&gt;
&lt;li&gt;RADOS is the foundation: get pools, PGs, and CRUSH right before you touch RBD or CephFS.&lt;/li&gt;
&lt;li&gt;RBD and CephFS are clients — tune them like clients, back them up like clients.&lt;/li&gt;
&lt;li&gt;Replication is not backup. Write the &lt;code&gt;rbd export&lt;/code&gt; script before you need it.
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Three or more physical or VM nodes running a modern Debian/Ubuntu or RHEL-family release, all on the same L2 network segment for the public and cluster networks.&lt;/li&gt;
&lt;li&gt;Root or passwordless sudo on every node.&lt;/li&gt;
&lt;li&gt;At least one dedicated block device per node for OSDs (not a partition on the OS disk).&lt;/li&gt;
&lt;li&gt;Two network interfaces per node (public + cluster), or a single interface with VLAN separation if you're brave and documented.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chrony&lt;/code&gt; installed and pointed at a reliable time source.&lt;/li&gt;
&lt;li&gt;SSH key-based access from the bootstrap node to every other node.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why You Should Stop Letting Proxmox Drive Your Ceph Cluster
&lt;/h2&gt;

&lt;p&gt;Proxmox's Ceph integration is genuinely good at one thing: making a hyperconverged cluster boot in an afternoon. It wraps &lt;code&gt;ceph&lt;/code&gt; commands in &lt;code&gt;pveceph&lt;/code&gt;, generates a &lt;code&gt;ceph.conf&lt;/code&gt; that lives as a symlink under &lt;code&gt;/etc/pve/&lt;/code&gt;, and applies defaults that assume you want a small cluster with minimal tuning. That's fine until the defaults aren't fine, and then you're debugging through a layer that was never designed to be debugged.&lt;/p&gt;

&lt;p&gt;Here's the telltale fingerprint of a Proxmox-generated config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# /etc/pve/ceph.conf — note this is a symlink, not a regular file
&lt;/span&gt;&lt;span class="nn"&gt;[global]&lt;/span&gt;
     &lt;span class="py"&gt;auth_client_required&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;cephx&lt;/span&gt;
     &lt;span class="py"&gt;auth_cluster_required&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;cephx&lt;/span&gt;
     &lt;span class="py"&gt;auth_service_required&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;cephx&lt;/span&gt;
     &lt;span class="py"&gt;cluster_network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.15.0.0/24&lt;/span&gt;
     &lt;span class="py"&gt;fsid&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;4f0d2a8e-...&lt;/span&gt;
     &lt;span class="py"&gt;mon_allow_pool_delete&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
     &lt;span class="py"&gt;mon_host&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.15.0.11 10.15.0.12 10.15.0.13&lt;/span&gt;
     &lt;span class="py"&gt;ms_bind_ipv4&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
     &lt;span class="py"&gt;osd_pool_default_min_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2&lt;/span&gt;
     &lt;span class="py"&gt;osd_pool_default_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;3&lt;/span&gt;
     &lt;span class="py"&gt;public_network&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;10.15.0.0/24&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;mon_allow_pool_delete = true&lt;/code&gt; is a Proxmox default you probably don't want on a production cluster. The symlink means any hand-edit gets clobbered. And the comparison between the wrapper and the real thing is stark:&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/storage/ceph-standalone-rados-rbd-cephfs-without-proxmox/" rel="noopener noreferrer"&gt;valtersit.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🛠️ Partner Tools for Developers
&lt;/h3&gt;

&lt;p&gt;ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.&lt;/p&gt;

&lt;p&gt;➜ &lt;a href="https://www.valtersit.com/deals/" rel="noopener noreferrer"&gt;valtersit.com/deals/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ceph</category>
      <category>storage</category>
      <category>rados</category>
      <category>cephadm</category>
    </item>
    <item>
      <title>Linkerd vs Istio: Choosing the Right Service Mesh</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Mon, 12 Oct 2026 14:15:04 +0000</pubDate>
      <link>https://dev.to/hugovalters/linkerd-vs-istio-choosing-the-right-service-mesh-4db1</link>
      <guid>https://dev.to/hugovalters/linkerd-vs-istio-choosing-the-right-service-mesh-4db1</guid>
      <description>&lt;p&gt;A platform team I worked with burned six weeks rolling Istio across an 800-pod cluster, then discovered their on-call rotation couldn't debug a broken request path without three &lt;code&gt;istioctl proxy-config&lt;/code&gt; incantations and a Kiali dashboard nobody had learned to read. The mesh worked. The humans didn't. They ripped it out and installed Linkerd in an afternoon.&lt;/p&gt;

&lt;p&gt;That story isn't an argument that Istio is bad. It's an argument that the mesh decision is a &lt;em&gt;team&lt;/em&gt; decision, not a &lt;em&gt;feature&lt;/em&gt; decision. The wrong mesh at the wrong scale is technical debt you pay for in 3 AM pages.&lt;/p&gt;

&lt;p&gt;This guide is for platform engineers, SREs, and DevOps leads running Kubernetes somewhere between 10 and 5,000 pods. After reading it you'll be able to size the real cost of each mesh, benchmark the latency tax honestly, and make a defensible call — including the option of running neither.&lt;/p&gt;

&lt;p&gt;:::note[TL;DR]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Both meshes deliver mTLS and L7 observability. Istio additionally delivers method/path-level authorization, egress control, and mature multi-cluster — at a real operational cost.&lt;/li&gt;
&lt;li&gt;Per-sidecar memory is the hidden budget killer: Linkerd's Rust proxy is typically far lighter than Envoy, and it compounds across every pod you run.&lt;/li&gt;
&lt;li&gt;If you can't articulate a concrete need for L7 authz or multi-cluster, Linkerd is the correct default. Istio is a commitment, not a checkbox.&lt;/li&gt;
&lt;li&gt;Cilium Service Mesh deserves evaluation if you already run Cilium as your CNI — sometimes the answer is neither of the two big names.
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A working Kubernetes cluster (1.27+ recommended; tested against 1.29 as of this writing) with &lt;code&gt;kubectl&lt;/code&gt; and Helm 3 configured.&lt;/li&gt;
&lt;li&gt;Cluster-admin rights to install CRDs and control-plane components.&lt;/li&gt;
&lt;li&gt;A non-production namespace you're willing to break. Do not run your first mesh install against prod.&lt;/li&gt;
&lt;li&gt;Basic familiarity with mTLS, SPIFFE identities, and how sidecar injection works.&lt;/li&gt;
&lt;li&gt;Optional but recommended: &lt;code&gt;fortio&lt;/code&gt; or &lt;code&gt;wrk2&lt;/code&gt; for load generation, and a Prometheus/Grafana stack already scraping your cluster.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Service Mesh Lie You've Been Sold
&lt;/h2&gt;

&lt;p&gt;A service mesh does exactly three things well: it gives you mTLS everywhere without app changes, it gives you L7 observability (request rates, error codes, latency percentiles) at the network layer, and it gives you traffic policy — retries, timeouts, circuit breaking, shifting, mirroring. That's it.&lt;/p&gt;

&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; do authorization policy for your application. It does not do business logic. It does not fix your broken service discovery, and it will not make a badly designed service topology resilient. If you install a mesh hoping it will "secure the cluster," you've outsourced a problem you haven't defined.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Real Cost Model Nobody Talks About
&lt;/h3&gt;

&lt;p&gt;The vendor blog posts show you a latency chart with a p50 delta of "under a millisecond" and call it negligible. p50 is a lie. What matters is p99 under load, because that's the number your users feel and your SLOs measure.&lt;/p&gt;

&lt;p&gt;Then there's the resource tax. Every sidecar consumes CPU and memory on every node. At 50 pods this is background noise. At 500 pods it's a line item. At 5,000 pods it's a capacity planning exercise that will surprise your finance team.&lt;/p&gt;

&lt;p&gt;A war story worth internalizing: on an 800-pod cluster running Istio's sidecar mode, the Envoy proxies consumed roughly 20-25% of total node memory budget. Nobody had modeled that. The cluster was over-provisioned to compensate, which meant paying for nodes that existed solely to host proxies.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/service_mesh/linkerd-vs-istio-choosing-the-right-service-mesh/" rel="noopener noreferrer"&gt;valtersit.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🛠️ Partner Tools for Developers
&lt;/h3&gt;

&lt;p&gt;ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.&lt;/p&gt;

&lt;p&gt;➜ &lt;a href="https://www.valtersit.com/deals/" rel="noopener noreferrer"&gt;valtersit.com/deals/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>servicemesh</category>
      <category>kubernetes</category>
      <category>linkerd</category>
      <category>istio</category>
    </item>
    <item>
      <title>Longhorn vs Rook-Ceph: Kubernetes Storage Compared</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Sun, 11 Oct 2026 14:15:03 +0000</pubDate>
      <link>https://dev.to/hugovalters/longhorn-vs-rook-ceph-kubernetes-storage-compared-44oh</link>
      <guid>https://dev.to/hugovalters/longhorn-vs-rook-ceph-kubernetes-storage-compared-44oh</guid>
      <description>&lt;p&gt;Every platform team eventually gets the ticket. It reads something like: "The Postgres pod won't schedule after the node rebooted, and now the whole staging environment is down." You dig in and find the database was pinned to a &lt;code&gt;hostPath&lt;/code&gt; volume, the node got cordoned for a kernel patch, and nobody wrote down that the PVC was effectively single-node. That's the moment persistent storage stops being an app team's problem and becomes a platform decision.&lt;/p&gt;

&lt;p&gt;This guide is for the engineer who has to make that decision and live with it for the next five years. By the end you'll understand how Longhorn and Rook-Ceph actually move bytes to disk, where each one breaks under pressure, and how to pick the one your team can operate at 3 AM — not the one that wins a synthetic benchmark on someone's blog.&lt;/p&gt;

&lt;p&gt;It assumes you already run Kubernetes, you have opinions about &lt;code&gt;StorageClass&lt;/code&gt;, and you're tired of the "just use NFS" crowd.&lt;/p&gt;

&lt;p&gt;:::note[TL;DR]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Longhorn&lt;/strong&gt; is an opinionated, iSCSI-backed, replica-per-volume system built for operational simplicity. Great under ~50 nodes, painful above it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rook-Ceph&lt;/strong&gt; is the full RADOS/Ceph stack under a Kubernetes operator. More performance and more features, but you're adopting an ecosystem, not a CSI driver.&lt;/li&gt;
&lt;li&gt;Neither is "install and forget." Both fail in interesting ways, and both need off-cluster backups.&lt;/li&gt;
&lt;li&gt;Pick based on the team you have, not the benchmark you envy.
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You should have a working Kubernetes cluster (v1.27+ is a reasonable floor as of this writing), &lt;code&gt;kubectl&lt;/code&gt; with cluster-admin, &lt;code&gt;helm&lt;/code&gt; v3, and &lt;code&gt;fio&lt;/code&gt; available for testing. For the S3 backup examples you'll need credentials to an object store. For Ceph, expect to provision raw block devices — loopback files will teach you nothing useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Storage Problem Nobody Wants to Own
&lt;/h2&gt;

&lt;p&gt;Stateful workloads on Kubernetes are still painful because the abstraction leaks. A &lt;code&gt;PersistentVolumeClaim&lt;/code&gt; looks like a simple API object, but underneath it's a promise about durability, latency, and failure semantics that the API can't express. When that promise breaks, it breaks loudly and at scale.&lt;/p&gt;

&lt;p&gt;There are three tiers of production storage, and they fit different problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cloud provider CSI&lt;/strong&gt; — managed, expensive, and vendor-locked. Fine if you're all-in on one cloud and the bill is acceptable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network filesystems (NFS, CIFS)&lt;/strong&gt; — fine for shared config and read-mostly data, catastrophic for databases. If you're running Postgres on NFS, stop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed storage you run yourself&lt;/strong&gt; — Longhorn, Rook-Ceph, OpenEBS, Portworx. This is the tier that gives you control and takes your weekends.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;"Distributed storage" means data is replicated across failure domains so a single disk or node loss doesn't lose data. What vendors pretend it means is "we put a CSI driver on top of a single server and called it distributed." Check where the replicas actually live before you trust the word.&lt;/p&gt;

&lt;p&gt;A war story worth internalizing: a team I worked with ran their primary Postgres on a &lt;code&gt;hostPath&lt;/code&gt; volume with a &lt;code&gt;nodeAffinity&lt;/code&gt; rule. It worked for eleven months. Then a NIC failed on that node, the pod couldn't reschedule because the data was on a disk attached to a dead machine, and the recovery window was four hours of restoring from a nightly &lt;code&gt;pg_dump&lt;/code&gt;. The lesson isn't "don't use hostPath" — it's that storage topology is a platform concern, and if you don't own it, you're borrowing someone else's outage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Longhorn — The Opinionated Upstart
&lt;/h2&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/storage/longhorn-vs-rook-ceph-kubernetes-storage-compared/" rel="noopener noreferrer"&gt;valtersit.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🛠️ Partner Tools for Developers
&lt;/h3&gt;

&lt;p&gt;ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.&lt;/p&gt;

&lt;p&gt;➜ &lt;a href="https://www.valtersit.com/deals/" rel="noopener noreferrer"&gt;valtersit.com/deals/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>storage</category>
      <category>longhorn</category>
      <category>rookceph</category>
    </item>
    <item>
      <title>ZimaBlade PCIe Expansion: Custom NAS and Router Combo</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Wed, 07 Oct 2026 14:15:03 +0000</pubDate>
      <link>https://dev.to/hugovalters/zimablade-pcie-expansion-custom-nas-and-router-combo-3ae4</link>
      <guid>https://dev.to/hugovalters/zimablade-pcie-expansion-custom-nas-and-router-combo-3ae4</guid>
      <description>&lt;p&gt;The 2am phone call was a &lt;code&gt;zpool scrub&lt;/code&gt;. The BGP session had flapped four minutes earlier, the failover to the backup ISP did not happen because the backup circuit was on the same box, and the on-call rotation had my number because I was the only one who claimed to understand the hardware. That is the story of every "one box to rule them all" build that survives past month three.&lt;/p&gt;

&lt;p&gt;The ZimaBlade is a $70-ish single-board computer with a real PCIe slot, and that slot is the entire reason anyone cares. This guide is for homelabbers, sysadmins, and network engineers who want to know whether you can genuinely run a NAS and a router on the same silicon — and more importantly, whether you &lt;em&gt;should&lt;/em&gt;. By the end you will know which PCIe cards are worth buying, how to size ZFS on 8GB of soldered DDR3L, which router OS actually fits an N3350, and where the failure domains will bite you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A ZimaBlade 7700 (or 3760 — verify the PCIe electrical lanes before buying, they differ)&lt;/li&gt;
&lt;li&gt;8GB RAM variant preferred; 4GB is workable for a router-only role&lt;/li&gt;
&lt;li&gt;A PCIe 2.0 x4 card that physically fits the half-height slot&lt;/li&gt;
&lt;li&gt;Boot media: SATA SSD or USB, not the onboard eMMC for anything you care about&lt;/li&gt;
&lt;li&gt;A serial console adapter (USB-TTL) for headless recovery&lt;/li&gt;
&lt;li&gt;Working knowledge of ZFS, Linux networking, and at least one firewall OS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;:::note[TL;DR]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ZimaBlade's PCIe 2.0 x4 slot (~2 GB/s theoretical) is fine for 2.5GbE and a 4-bay SATA HBA. It is &lt;em&gt;not&lt;/em&gt; a 10GbE or NVMe RAID platform.&lt;/li&gt;
&lt;li&gt;Two build paths: single-purpose (NAS &lt;em&gt;or&lt;/em&gt; router) or split-purpose (both, virtualized or bare metal with cgroups).&lt;/li&gt;
&lt;li&gt;Running NAS + router on one box is a bad idea for most people and a great idea for a narrow set of lab/cabin/RV scenarios.&lt;/li&gt;
&lt;li&gt;Verify NIC chipset &lt;em&gt;before&lt;/em&gt; purchase — Intel i211 vs. Realtek changes your entire driver story.
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the ZimaBlade Is a Weird, Wonderful Little Box
&lt;/h2&gt;

&lt;p&gt;Most SBCs give you GPIO pins and a dream. The ZimaBlade gives you a physical PCIe slot, dual NICs, and SATA — which is a completely different product category. That slot is why this thing has a cult following despite shipping with a 2016-era Celeron.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Actually Get for ~$70
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Intel Celeron N3350 (dual-core, 1.1–2.4 GHz) or N3450 (quad-core) depending on SKU&lt;/li&gt;
&lt;li&gt;8GB DDR3L soldered (some SKUs 4GB — check before buying)&lt;/li&gt;
&lt;li&gt;PCIe 2.0 x4 physical slot, &lt;strong&gt;electrically x1 on some board revisions&lt;/strong&gt; — this is the single most important spec to verify&lt;/li&gt;
&lt;li&gt;Dual gigabit NICs (Intel i211 on some revisions, Realtek RTL8111 on others — this matters enormously for FreeBSD)&lt;/li&gt;
&lt;li&gt;SATA via ASM1061 controller, USB 3.0, eMMC boot&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The PCIe Slot Is the Whole Point
&lt;/h3&gt;

&lt;p&gt;PCIe 2.0 x4 gives you roughly 2 GB/s of theoretical bandwidth. That sounds thin until you realize 2.5GbE is ~312 MB/s and a 4-disk SATA HBA will never saturate it. Where it falls apart: 10GbE needs ~1.25 GB/s sustained, and NVMe RAID wants multiples of that. If you saw a YouTube thumbnail promising 10GbE on a ZimaBlade, that thumbnail was lying to you.&lt;/p&gt;

&lt;p&gt;Verify the link negotiation before you trust any of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Confirm the actual negotiated link width and speed, not the marketing spec&lt;/span&gt;
lspci &lt;span class="nt"&gt;-vv&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A2&lt;/span&gt; &lt;span class="s2"&gt;"LnkCap&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;LnkSta"&lt;/span&gt;
&lt;span class="c"&gt;# Expected on a good board: LnkSta: Speed 5GT/s, Width x4&lt;/span&gt;
&lt;span class="c"&gt;# If you see Width x1, you bought the wrong revision&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;LnkSta&lt;/code&gt; reports &lt;code&gt;Width x1&lt;/code&gt;, your 2.5GbE card will still work but you have less headroom for an HBA on the same bus. Plan accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  What This Box Is Not
&lt;/h3&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/zima/zimablade-pcie-expansion-custom-nas-and-router-combo/" rel="noopener noreferrer"&gt;valtersit.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🛠️ Partner Tools for Developers
&lt;/h3&gt;

&lt;p&gt;ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.&lt;/p&gt;

&lt;p&gt;➜ &lt;a href="https://www.valtersit.com/deals/" rel="noopener noreferrer"&gt;valtersit.com/deals/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>zimablade</category>
      <category>homelab</category>
      <category>zfs</category>
      <category>opnsense</category>
    </item>
    <item>
      <title>iSCSI Storage for Hypervisors: TrueNAS, LIO, Multipath</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Sat, 03 Oct 2026 14:15:03 +0000</pubDate>
      <link>https://dev.to/hugovalters/iscsi-storage-for-hypervisors-truenas-lio-multipath-4cf</link>
      <guid>https://dev.to/hugovalters/iscsi-storage-for-hypervisors-truenas-lio-multipath-4cf</guid>
      <description>&lt;p&gt;Two paths, one dead for three weeks, nobody noticed. That's the story I keep running into. A cluster of a dozen VMs ran fine on a single surviving iSCSI path until a switch firmware reboot took the second one down at 02:00. Every VM froze for five minutes, then errored out. The monitoring was green the entire time because nobody was watching &lt;em&gt;path state&lt;/em&gt; — only "is the LUN up." This guide is about making sure that doesn't happen to you.&lt;/p&gt;

&lt;p&gt;This is for sysadmins and platform engineers running KVM-based hypervisors (Proxmox VE, plain libvirt, XCP-ng) who need block storage that behaves under failure. After reading, you'll be able to design an iSCSI fabric, build a target on TrueNAS SCALE or raw LIO, write a &lt;code&gt;multipath.conf&lt;/code&gt; that isn't cargo-culted, and verify failover before you trust it with production VMs.&lt;/p&gt;

&lt;p&gt;:::note[TL;DR]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Block storage wins for VM workloads because there's no double-caching and no file-locking pathologies — but only if you build it right.&lt;/li&gt;
&lt;li&gt;TrueNAS SCALE wraps the same kernel LIO target you'd get with &lt;code&gt;targetcli&lt;/code&gt;, just with a GUI and guardrails. Pick based on who's on call at 3am.&lt;/li&gt;
&lt;li&gt;A single path is a SPOF with a nice name. Multipath is not optional.&lt;/li&gt;
&lt;li&gt;Tune &lt;code&gt;no_path_retry&lt;/code&gt; and &lt;code&gt;dev_loss_tmo&lt;/code&gt;, or your VMs will hang for minutes on path failure.&lt;/li&gt;
&lt;li&gt;Monitor path state, not LUN state. Save your &lt;code&gt;targetcli&lt;/code&gt; config every time.
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Two servers: one target (TrueNAS SCALE or Debian/Ubuntu/RHEL with &lt;code&gt;targetcli&lt;/code&gt;), one or more initiators.&lt;/li&gt;
&lt;li&gt;Dedicated NICs or VLANs for iSCSI traffic. Do not share with VM traffic.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;open-iscsi&lt;/code&gt; and &lt;code&gt;multipath-tools&lt;/code&gt; installed on every initiator.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;targetcli-fb&lt;/code&gt; (or &lt;code&gt;targetcli&lt;/code&gt;) on raw LIO targets.&lt;/li&gt;
&lt;li&gt;Root/sudo on both sides and a maintenance window for the first failover test.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Block Storage Still Wins (And When It Doesn't)
&lt;/h2&gt;

&lt;p&gt;The reflex is "just use NFS, it's simpler." Sometimes that's correct — shared filesystems, template stores, and anything where multiple hosts need the same file are NFS territory. But for VM disk images, block wins, and it's not close.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Case For iSCSI Over NFS for Hypervisors
&lt;/h3&gt;

&lt;p&gt;With iSCSI, the client is stateless. The hypervisor sees a LUN, puts XFS or LVM-thin or ZFS on it, and the target has no idea what filesystem lives inside. That means no client-side cache coherency to reason about, no NFSv4.1 session trunking to debug, and no file-locking pathologies when a VM does something stupid with &lt;code&gt;flock()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;NFSv4.1 sessions are &lt;em&gt;not&lt;/em&gt; multipathing. They give you trunking across multiple connections to the &lt;em&gt;same&lt;/em&gt; server, but if that server dies, everything dies. Real multipath gives you two independent paths to two independent controllers, and the kernel picks one.&lt;/p&gt;

&lt;p&gt;I moved a cluster off NFS after a coherency incident: two KVM hosts mounted the same NFS export, one host's page cache went stale after a network blip, and a VM wrote a corrupted qcow2 over a file the other host had just modified. iSCSI with a properly sized LUN per VM eliminated the class of problem entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  When You Should Absolutely Not Do This
&lt;/h3&gt;

&lt;p&gt;If you have one initiator, one target, one switch, and one cable — you've built a SPOF with extra steps. iSCSI without multipath is worse than NFS because at least NFS will give you a clean I/O error. A dead iSCSI path with default timeouts will hang your VM for minutes before anything surfaces.&lt;/p&gt;

&lt;p&gt;The "I'll add multipath later" crowd always adds it after the first outage. Do it now.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/storage/iscsi-storage-for-hypervisors-truenas-lio-multipath/" rel="noopener noreferrer"&gt;valtersit.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🛠️ Partner Tools for Developers
&lt;/h3&gt;

&lt;p&gt;ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.&lt;/p&gt;

&lt;p&gt;➜ &lt;a href="https://www.valtersit.com/deals/" rel="noopener noreferrer"&gt;valtersit.com/deals/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>iscsi</category>
      <category>truenas</category>
      <category>lio</category>
      <category>multipath</category>
    </item>
    <item>
      <title>Dark Web Reality Check: What I Found After 3 Weeks</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Tue, 29 Sep 2026 14:15:04 +0000</pubDate>
      <link>https://dev.to/hugovalters/dark-web-reality-check-what-i-found-after-3-weeks-13pe</link>
      <guid>https://dev.to/hugovalters/dark-web-reality-check-what-i-found-after-3-weeks-13pe</guid>
      <description>&lt;p&gt;The first thing I found wasn't a drug market. It wasn't a carding forum. It was a Jenkins instance.&lt;/p&gt;

&lt;p&gt;I was three days into a four-week enumeration project, running a Python scraper against a list of .onion addresses pulled from Ahmia, when a response came back that shouldn't have existed on Tor: a Jenkins login page, running version 2.289.1, exposed via a hidden service with no authentication on the &lt;code&gt;/script&lt;/code&gt; endpoint. The hostname in the page title belonged to a Fortune 500 subsidiary. Someone in their dev org had spun up a Tor hidden service "for remote access" during a 2020 lockdown and never told security. That CI pipeline had production AWS credentials in its environment variables.&lt;/p&gt;

&lt;p&gt;If you're a defender, that's the dark web. Not the monster under the bed — the mirror showing you what you already leaked.&lt;/p&gt;

&lt;p&gt;This guide is for sysadmins, security engineers, and threat intel analysts who want to understand what's actually out there, how to enumerate it without getting owned, and — most importantly — how to find your own shadow assets before someone else does. I'm not going to teach you to buy drugs. I'm going to teach you to do OSINT on Tor like an adult with a change-management process.&lt;/p&gt;

&lt;p&gt;:::note[TL;DR]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The "dark web" is 90% dead links, scams, and mirrors of clearnet services. The interesting 10% is mostly accidental exposure and leak sites.&lt;/li&gt;
&lt;li&gt;Never browse Tor from your daily driver. Whonix or Tails, full stop.&lt;/li&gt;
&lt;li&gt;Blocking Tor at the firewall is theater. Detect and log it instead.&lt;/li&gt;
&lt;li&gt;The real risk to your org isn't hackers on Tor — it's the sysadmin who spun up a hidden service and forgot.&lt;/li&gt;
&lt;li&gt;Treat leak sites as a data source, not a destination. Automate, don't interact.
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before you touch any of this, you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A dedicated research VM or physical host (not your work laptop — I'll explain why in the opsec section)&lt;/li&gt;
&lt;li&gt;Whonix Gateway + Workstation, or Tails on a USB stick&lt;/li&gt;
&lt;li&gt;Python 3.9+ with &lt;code&gt;stem&lt;/code&gt;, &lt;code&gt;requests[socks]&lt;/code&gt;, and &lt;code&gt;beautifulsoup4&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Basic familiarity with &lt;code&gt;iptables&lt;/code&gt;/&lt;code&gt;nftables&lt;/code&gt; and Linux namespaces&lt;/li&gt;
&lt;li&gt;A legal review from your org's counsel if you're doing this on company time (seriously)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What "The Dark Web" Actually Is (And What It Isn't)
&lt;/h2&gt;

&lt;p&gt;Words matter here, because people conflate three different things constantly. The &lt;strong&gt;deep web&lt;/strong&gt; is anything not indexed by search engines — your intranet, your bank portal, your O365 tenant. The &lt;strong&gt;dark web&lt;/strong&gt; is content hosted on overlay networks like Tor, I2P, and Freenet that requires specific software to reach. &lt;strong&gt;Tor&lt;/strong&gt; is the transport; the dark web is an application layer on top of it.&lt;/p&gt;

&lt;p&gt;Tor uses onion routing — traffic is wrapped in three layers of encryption and bounced through three relays (guard, middle, exit). I2P uses garlic routing, which bundles multiple messages together and is optimized for internal services (eepsites) rather than exit traffic. Freenet is a distributed datastore, mostly academic at this point. ZeroNet is effectively dead. If someone tells you they're "on the dark web" and they mean ZeroNet, they're either confused or lying.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why "Just Block Tor at the Firewall" Is a Losing Strategy
&lt;/h3&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/security/is-the-dark-web-just-for-hackers/" rel="noopener noreferrer"&gt;valtersit.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🛠️ Partner Tools for Developers
&lt;/h3&gt;

&lt;p&gt;ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.&lt;/p&gt;

&lt;p&gt;➜ &lt;a href="https://www.valtersit.com/deals/" rel="noopener noreferrer"&gt;valtersit.com/deals/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tor</category>
      <category>osint</category>
      <category>threatintel</category>
      <category>opsec</category>
    </item>
    <item>
      <title>OSSEC vs Wazuh: FIM and Active Response Compared</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Mon, 28 Sep 2026 14:15:03 +0000</pubDate>
      <link>https://dev.to/hugovalters/ossec-vs-wazuh-fim-and-active-response-compared-308f</link>
      <guid>https://dev.to/hugovalters/ossec-vs-wazuh-fim-and-active-response-compared-308f</guid>
      <description>&lt;h1&gt;
  
  
  OSSEC vs Wazuh: File Integrity Monitoring and Active Response
&lt;/h1&gt;

&lt;p&gt;OSSEC and Wazuh share roughly 80% of their codebase and 100% of their spelling of &lt;code&gt;ossec.conf&lt;/code&gt;. The 20% divergence is where your architecture decision lives — and it is not a small 20%. It is the difference between a scanner that writes flat files and a platform that expects an OpenSearch cluster with a JVM heap you have to size.&lt;/p&gt;

&lt;p&gt;Here is the thesis up front: &lt;strong&gt;OSSEC is a fragile, brilliant, near-frozen engine. Wazuh is the same engine with a database, an API, and a compliance story bolted on — and a resource bill to match.&lt;/strong&gt; Both will page you at 03:00. Only one of them will still have a CVE-patched release next quarter.&lt;/p&gt;

&lt;p&gt;This guide covers FIM and Active Response specifically, because those are the two modules people actually migrate for. Everything else — log decoders, rootcheck, SCA — is either identical or not worth the migration on its own.&lt;/p&gt;

&lt;p&gt;:::note[TL;DR]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Syscheck's engine is the same code in both projects. The divergence is in change attribution (whodata), storage, and the API.&lt;/li&gt;
&lt;li&gt;Real-time FIM on Linux is inotify-based in both. Wazuh adds auditd-backed &lt;code&gt;whodata&lt;/code&gt; for attribution; OSSEC's implementation is older and thinner.&lt;/li&gt;
&lt;li&gt;Active Response is the single most dangerous module in either product. &lt;code&gt;firewall-drop&lt;/code&gt; at &lt;code&gt;[location]server[/location]&lt;/code&gt; will block your own agents.&lt;/li&gt;
&lt;li&gt;OSSEC's floor is a 1–2 GB VM. A useful Wazuh deployment is realistically 8–16 GB across manager plus indexer.&lt;/li&gt;
&lt;li&gt;Migrating is a config porting exercise for syscheck and a near-rewrite for anything that touched the OSSEC database output.
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A working OSSEC 2.9.x/3.x or Wazuh 3.x/4.x manager, and at least one agent registered.&lt;/li&gt;
&lt;li&gt;Root (or equivalent sudo) on the manager and agents — syscheck reads &lt;code&gt;/etc&lt;/code&gt;, and Active Response scripts run as root by design.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;auditd&lt;/code&gt; installed on Linux hosts if you want &lt;code&gt;whodata&lt;/code&gt; attribution.&lt;/li&gt;
&lt;li&gt;For Wazuh 4.x: the indexer and dashboard up, since that is where FIM events land.&lt;/li&gt;
&lt;li&gt;A staging host you are willing to lock out of the network. Seriously.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Two Codebases, One Ancestor
&lt;/h2&gt;

&lt;p&gt;Wazuh started as a fork of OSSEC and had its first standalone release in 2015. Since then, upstream OSSEC's cadence has been measured in years, not months; the 2.9.x line went effectively quiet and the 3.x line is carried by Atomicorp as a vendor-maintained branch. Wazuh ships on a much faster cycle and, critically, publishes CVEs and fixes for the whole stack.&lt;/p&gt;

&lt;p&gt;The uncomfortable truth is that the fork is the &lt;em&gt;product&lt;/em&gt;, not a bug. The original OSSEC codebase was never designed for a manager that stores every FIM event, an API with RBAC, or a multi-node cluster. Wazuh didn't just add features — it rearchitected the data plane, and that rearchitecture is exactly what costs you RAM.&lt;/p&gt;

&lt;p&gt;First thing to do on any inherited box: find out which engine you are actually running, because "OSSEC" appears in paths regardless of which project you deployed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Wazuh still installs to /var/ossec — the path lies. Check the version.&lt;/span&gt;
/var/ossec/bin/wazuh-control info 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; /var/ossec/bin/ossec-control status

&lt;span class="c"&gt;# Both projects write VERSION into ossec-init.conf on install&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^VERSION'&lt;/span&gt; /var/ossec/etc/ossec-init.conf

&lt;span class="c"&gt;# Confirm which binaries exist — this is the fastest fork fingerprint&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; /var/ossec/bin/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'wazuh|ossec'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/security/ossec-vs-wazuh-fim-and-active-response-compared/" rel="noopener noreferrer"&gt;valtersit.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🛠️ Partner Tools for Developers
&lt;/h3&gt;

&lt;p&gt;ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.&lt;/p&gt;

&lt;p&gt;➜ &lt;a href="https://www.valtersit.com/deals/" rel="noopener noreferrer"&gt;valtersit.com/deals/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ossec</category>
      <category>wazuh</category>
      <category>fileintegritymonitoring</category>
      <category>activeresponse</category>
    </item>
    <item>
      <title>ZimaBoard as a Home Server: N3450 Pushed to Its Limits</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Fri, 25 Sep 2026 14:15:03 +0000</pubDate>
      <link>https://dev.to/hugovalters/zimaboard-as-a-home-server-n3450-pushed-to-its-limits-2pg2</link>
      <guid>https://dev.to/hugovalters/zimaboard-as-a-home-server-n3450-pushed-to-its-limits-2pg2</guid>
      <description>&lt;p&gt;Every few months someone posts a ZimaBoard in a 3D-printed rack, running Pi-hole, and the comments fill up with people asking where to buy one. Eleven months later that same board is sitting in a drawer because &lt;code&gt;/dev/mmcblk0&lt;/code&gt; hit end-of-life, the 32GB eMMC silently corrupted the Proxmox root filesystem, and the only backup was a snapshot on a USB drive that hadn't been mounted in six weeks. The hardware didn't fail. The person running it treated a 6W single-board computer like a datacenter node and skipped every flash-endurance and thermal decision that makes the difference.&lt;/p&gt;

&lt;p&gt;This guide is for homelab operators who already own a ZimaBoard 432 or 832 — or are about to buy one — and want to run real workloads on it: Proxmox, a media stack, a reverse proxy, a WireGuard gateway, and a handful of containers. By the end you will have a measured baseline for the N3450, a storage architecture that does not eat itself, a PCIe expansion path you actually committed to, VPN throughput numbers you can reproduce, and a written list of triggers that tell you when to migrate off the platform.&lt;/p&gt;

&lt;p&gt;The thesis, and everything below follows from it: &lt;strong&gt;the Celeron N3450 is a fine low-power server for I/O-bound workloads and a terrible one for crypto-bound and CPU-bound workloads.&lt;/strong&gt; Miss that distinction and you will spend a weekend tuning a box that was never going to do what you asked.&lt;/p&gt;

&lt;p&gt;:::note[TL;DR]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The N3450 has &lt;strong&gt;no AES-NI, no AVX, no TSX, and single-channel soldered non-ECC DDR3L&lt;/strong&gt;. That single fact dictates WireGuard over OpenVPN, rules out ZFS root, and caps TLS termination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boot from eMMC, store on SATA/NVMe, and move &lt;code&gt;/var/log&lt;/code&gt; to tmpfs immediately.&lt;/strong&gt; Default journald on eMMC is a documented way to kill the board in under a year.&lt;/li&gt;
&lt;li&gt;The open &lt;strong&gt;PCIe 2.0 x4 slot is the only real differentiator&lt;/strong&gt; against an N100 mini-PC. Pick &lt;em&gt;one&lt;/em&gt; expansion path — SATA, NIC, or NVMe — and commit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buy the 832 (8GB).&lt;/strong&gt; The 216 ships 2GB of soldered RAM; that is not a virtualization host, it is a paperweight with two NICs.
:::&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need a ZimaBoard 432 or 832 with a 12V/2A (minimum) power supply, a Debian 12 or Proxmox VE 8 install target, physical access to the PCIe slot, and a second machine to run &lt;code&gt;iperf3&lt;/code&gt; against. Install &lt;code&gt;sysbench&lt;/code&gt;, &lt;code&gt;mmc-utils&lt;/code&gt;, &lt;code&gt;smartmontools&lt;/code&gt;, &lt;code&gt;ethtool&lt;/code&gt;, &lt;code&gt;lm-sensors&lt;/code&gt;, &lt;code&gt;cpupowerutils&lt;/code&gt;, and &lt;code&gt;zram-tools&lt;/code&gt; before you start. Everything below was written against Proxmox VE 8.x on Debian 12; package names shift over time, so check your distro if something is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Celeron N3450 Actually Is (and Isn't)
&lt;/h2&gt;

&lt;p&gt;Before you tune anything, you need to know what physics allows. The N3450 is &lt;strong&gt;Apollo Lake / Goldmont&lt;/strong&gt;: 4 cores, 4 threads, 1.1 GHz base, 2.2 GHz burst, 6W TDP, single-channel DDR3L-1866 soldered to the board with no ECC option. No SMT, so four cores is four threads, full stop.&lt;/p&gt;

&lt;p&gt;The headline is what is &lt;em&gt;missing&lt;/em&gt;: &lt;strong&gt;no AES-NI, no AVX, no TSX, no AVX2.&lt;/strong&gt; On the Celeron SKUs Intel fused off the AES extensions that the Goldmont core design otherwise carries. This is not a footnote you skim past. It determines your VPN protocol, your disk-encryption throughput, your TLS handshake capacity, and whether WireGuard's 400 Mbps is a benchmark result or a fantasy. Every "ZimaBoard is great for X" post that doesn't mention the missing AES-NI was written by someone who never ran a benchmark.&lt;/p&gt;

&lt;p&gt;Measure your own board before you trust anyone's numbers — including mine:&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
This is an abbreviated cross-post. Full article (all config files, architecture diagrams, images): &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/zima/zimaboard-as-a-home-server-n3450-pushed-to-its-limits/" rel="noopener noreferrer"&gt;valtersit.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🛠️ Partner Tools for Developers
&lt;/h3&gt;

&lt;p&gt;ValtersIT curates partner deals for developers and sysadmins — VPS hosting, security tools, monitoring platforms and dev productivity gear. No filler, no affiliate spam.&lt;/p&gt;

&lt;p&gt;➜ &lt;a href="https://www.valtersit.com/deals/" rel="noopener noreferrer"&gt;valtersit.com/deals/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>homelab</category>
      <category>zimaboard</category>
      <category>proxmox</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>ValtersIT CVE + Threat-IP API: Integration Guide for Firewalls, SIEM and CI/CD</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Tue, 22 Sep 2026 11:39:17 +0000</pubDate>
      <link>https://dev.to/hugovalters/valtersit-cve-threat-ip-api-integration-guide-for-firewalls-siem-and-cicd-2n16</link>
      <guid>https://dev.to/hugovalters/valtersit-cve-threat-ip-api-integration-guide-for-firewalls-siem-and-cicd-2n16</guid>
      <description>&lt;p&gt;Your firewall vendor publishes a critical advisory. Days later NVD assigns a score, a PoC lands on GitHub, and CISA adds it to KEV. Correlating those sources by hand is a tooling problem, not a knowledge problem.&lt;/p&gt;

&lt;p&gt;The ValtersIT API puts CVE records (150,000+) and a threat-IP dataset (290,000+ addresses) behind one REST interface with one authentication scheme. This guide shows five integrations end to end. &lt;strong&gt;Every API request and response shape below was checked against the live API&lt;/strong&gt;, including the failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you get
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CVE records&lt;/td&gt;
&lt;td&gt;150,000+. &lt;code&gt;cvss&lt;/code&gt;, &lt;code&gt;cvss_vector&lt;/code&gt;, &lt;code&gt;epss&lt;/code&gt;, &lt;code&gt;severity&lt;/code&gt;, &lt;code&gt;cisa_kev&lt;/code&gt; (boolean), &lt;code&gt;has_exploit&lt;/code&gt;, &lt;code&gt;has_patch&lt;/code&gt;, &lt;code&gt;poc_status&lt;/code&gt;, &lt;code&gt;poc_url&lt;/code&gt;, &lt;code&gt;cwe_id&lt;/code&gt;, &lt;code&gt;mitre_tactic&lt;/code&gt;, &lt;code&gt;shodan_dorks&lt;/code&gt;, &lt;code&gt;affected_version&lt;/code&gt;, &lt;code&gt;patch_details&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detection content&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;yara_rule&lt;/code&gt;, &lt;code&gt;wazuh_rule&lt;/code&gt;, &lt;code&gt;elastic_rule&lt;/code&gt; on paid plans where a rule exists. &lt;code&gt;sigma_rule&lt;/code&gt; and &lt;code&gt;poc_code&lt;/code&gt; are Pro only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threat IPs&lt;/td&gt;
&lt;td&gt;290,000+ addresses. &lt;code&gt;ip&lt;/code&gt;, &lt;code&gt;threat_type&lt;/code&gt;, &lt;code&gt;confidence&lt;/code&gt;, &lt;code&gt;sources&lt;/code&gt;, &lt;code&gt;cve_ids&lt;/code&gt;, &lt;code&gt;malware_families&lt;/code&gt;, &lt;code&gt;country_code&lt;/code&gt;, &lt;code&gt;asn&lt;/code&gt;, &lt;code&gt;org&lt;/code&gt;, &lt;code&gt;ports&lt;/code&gt;, &lt;code&gt;is_tor&lt;/code&gt;, &lt;code&gt;is_vpn&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data honesty&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pending_fields&lt;/code&gt; lists what NVD has not filled in yet, &lt;code&gt;backfilled_fields&lt;/code&gt; lists what we filled in after the fact&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Base URL:&lt;/strong&gt; &lt;code&gt;https://api.valtersit.com/api/v1&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; &lt;code&gt;Authorization: Bearer &amp;lt;your key&amp;gt;&lt;/code&gt; on every request. Keys look like &lt;code&gt;vit_v1_...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Threat IP types you can filter on: &lt;code&gt;scanner&lt;/code&gt;, &lt;code&gt;mixed&lt;/code&gt;, &lt;code&gt;malware_dist&lt;/code&gt;, &lt;code&gt;botnet_c2&lt;/code&gt;, &lt;code&gt;phishing&lt;/code&gt;, &lt;code&gt;tor&lt;/code&gt;, &lt;code&gt;exploit_host&lt;/code&gt;, &lt;code&gt;c2_server&lt;/code&gt;, &lt;code&gt;spam&lt;/code&gt;, &lt;code&gt;brute_force&lt;/code&gt;. Confidence scores are conservative: only a small number of IPs score 75 or above, so start with &lt;code&gt;min_confidence=50&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it in 30 seconds
&lt;/h2&gt;

&lt;p&gt;The public sandbox key needs no signup and costs nothing. It serves ten &lt;strong&gt;synthetic&lt;/strong&gt; CVE records (&lt;code&gt;CVE-2026-90001&lt;/code&gt; to &lt;code&gt;CVE-2026-90010&lt;/code&gt;) so you can test your parsing before you pay for anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all ten sandbox records&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.valtersit.com/api/v1/cve &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer vit_sandbox_free_demo_key_2026"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'.data[] | {cve_id, vendor, cvss, severity, cisa_kev, poc_status}'&lt;/span&gt;

&lt;span class="c"&gt;# One record&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.valtersit.com/api/v1/cve/CVE-2026-90001 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer vit_sandbox_free_demo_key_2026"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'{cve_id, vendor, cvss, severity, cisa_kev, poc_status, pending_fields}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"cve_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CVE-2026-90001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vendor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SandboxSoft Firewall Pro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cvss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;9.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cisa_kev"&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;"poc_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Public"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pending_fields"&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="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sandbox has no IP feed. For real data, register for a free key at &lt;a href="https://api.valtersit.com" rel="noopener noreferrer"&gt;api.valtersit.com&lt;/a&gt; and top up, or subscribe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ground rules used in every example
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Put the key in an environment variable: &lt;code&gt;export VIT_API_KEY=vit_v1_...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /cve&lt;/code&gt; needs at least one filter (&lt;code&gt;vendor&lt;/code&gt;, &lt;code&gt;severity&lt;/code&gt;, &lt;code&gt;cvss_min&lt;/code&gt;, &lt;code&gt;cvss_max&lt;/code&gt;, &lt;code&gt;epss_min&lt;/code&gt;, &lt;code&gt;has_exploit&lt;/code&gt;, &lt;code&gt;has_patch&lt;/code&gt;, &lt;code&gt;changed_since&lt;/code&gt;, &lt;code&gt;published_since&lt;/code&gt;). Pages hold at most 100 records. (The sandbox key is exempt from the filter rule.)&lt;/li&gt;
&lt;li&gt;Single lookups such as &lt;code&gt;GET /cve/CVE-2026-26084&lt;/code&gt; return the record itself, not a &lt;code&gt;data&lt;/code&gt; wrapper. List endpoints return &lt;code&gt;{"data": [...], ...}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Errors: &lt;code&gt;401&lt;/code&gt; bad or missing key, &lt;code&gt;402&lt;/code&gt; out of credits, &lt;code&gt;403&lt;/code&gt; plan does not allow it, &lt;code&gt;404&lt;/code&gt; unknown CVE, &lt;code&gt;422&lt;/code&gt; bad parameter, &lt;code&gt;429&lt;/code&gt; rate limit. Every metered response carries &lt;code&gt;X-RateLimit-Limit&lt;/code&gt; and &lt;code&gt;X-RateLimit-Remaining&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pay-as-you-go spends one credit per CVE record returned. Standard and Pro track every CVE you look up, and re-checking one you have already fetched is free.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Use case 1: Firewall blocklist from the threat-IP feed
&lt;/h2&gt;

&lt;p&gt;Browsing the feed by criteria needs Standard or Pro (Standard returns up to 500 rows per request, Pro up to 1,000). Pay-as-you-go can look up specific addresses with &lt;code&gt;?ips=1.2.3.4,5.6.7.8&lt;/code&gt; (up to 10 per request) but cannot pull the list.&lt;/p&gt;

&lt;p&gt;Pick your filters from the data, not from hope. At the time of writing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Filter&lt;/th&gt;
&lt;th&gt;IPs returned&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type=botnet_c2&amp;amp;min_confidence=50&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;about 10,800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type=malware_dist&amp;amp;min_confidence=50&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;about 31,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type=scanner&amp;amp;min_confidence=50&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;about 125,000 (usually too broad to block outright)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Pull botnet C2 IPs and write an nftables batch file.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ipaddress&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.valtersit.com/api/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;VIT_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;PAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;  &lt;span class="c1"&gt;# Standard cap. Pro allows 1000. A larger limit is clamped, not rejected.
&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threat_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;ips&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/ip/feed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;threat_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min_confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;min_confidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PAGE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;offset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;ips&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
        &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;next_offset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# None on the last page
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ips&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ips&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;v4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ips&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ipaddress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;v6&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ips&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ipaddress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ip_address&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;lines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;add table inet filter&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;family&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;members&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vit_block4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ipv4_addr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vit_block6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ipv6_addr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v6&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;add set inet filter &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; {{ type &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;family&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;; }}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;flush set inet filter &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;add element inet filter &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; {{ &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; }}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ips&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;botnet_c2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/var/lib/vit/blocklist.nft&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makedirs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;build_batch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ips&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nft&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# one atomic transaction
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loaded &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ips&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; addresses&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the drop rules once, and run the script from cron (&lt;code&gt;*/30 * * * *&lt;/code&gt;). Because it rebuilds the sets on every run, addresses that leave the feed age out on their own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nft add rule inet filter input ip saddr @vit_block4 drop
nft add rule inet filter input ip6 saddr @vit_block6 drop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Roll this out carefully.&lt;/strong&gt; Threat feeds contain false positives: shared hosting, CGNAT pools and reused addresses. Start with a &lt;code&gt;log&lt;/code&gt; rule instead of &lt;code&gt;drop&lt;/code&gt;, keep an allowlist for your own ranges and partners, and review what would have been blocked before you enforce anything. The API never returns private, loopback or reserved ranges.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;pfSense / OPNsense:&lt;/strong&gt; the API requires an authentication header, so a firewall cannot fetch the feed directly as a URL table alias. Run the fetch on any Linux host, write one address per line to a file you serve on your internal network, and point the alias at that internal URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case 2: Enrich SIEM alerts with CVE context
&lt;/h2&gt;

&lt;p&gt;An IDS alert carries a CVE ID and nothing else. This helper turns it into a priority decision using fields that exist on every record.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.valtersit.com/api/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;VIT_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;enrich&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cve_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/cve/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cve_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;               &lt;span class="c1"&gt;# rate limited: wait and retry
&lt;/span&gt;            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;               &lt;span class="c1"&gt;# not in the database
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                   &lt;span class="c1"&gt;# 401 / 402 / 5xx should be loud
&lt;/span&gt;        &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                           &lt;span class="c1"&gt;# the record itself, no "data" wrapper
&lt;/span&gt;        &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;P1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cisa_kev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;has_exploit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cvss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; \
                   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;P2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;has_exploit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cvss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;P3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cve_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cve_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cvss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cvss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;epss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;epss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cisa_kev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cisa_kev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;has_exploit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;has_exploit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;has_patch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;has_patch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pending_fields&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pending_fields&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;priority&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate limited after 3 attempts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Print the result as JSON and this works as a scripted lookup, an n8n or Shuffle step, or a Splunk adaptive response action. For Elastic, the Logstash &lt;code&gt;http&lt;/code&gt; filter does the same inline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cve_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"https://api.valtersit.com/api/v1/cve/%{[cve_id]}"&lt;/span&gt;
      &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"Authorization"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"Bearer ${VIT_API_KEY}"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;target_body&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"vit"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;vit&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;cvss&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;mutate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;add_field&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"[threat][cvss]"&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"%{[vit][cvss]}"&lt;/span&gt;
          &lt;span class="s2"&gt;"[threat][epss]"&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"%{[vit][epss]}"&lt;/span&gt;
          &lt;span class="s2"&gt;"[threat][kev]"&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"%{[vit][cisa_kev]}"&lt;/span&gt;
          &lt;span class="s2"&gt;"[threat][exploit]"&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"%{[vit][has_exploit]}"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put a cache in front of the lookup (Logstash memcached filter, Redis, or a &lt;code&gt;translate&lt;/code&gt; table). Alerts repeat the same CVE constantly, and on pay-as-you-go every fresh record costs a credit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case 3: CI/CD prioritization gate
&lt;/h2&gt;

&lt;p&gt;The API is not a scanner and it does not match package versions. Let your scanner (Trivy, Grype, OSV-Scanner) find the CVE IDs, then use the API to decide which ones should actually stop a build: known-exploited, exploit-available, or unpatched and severe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Fail the build on CVEs that are actively dangerous. Input: trivy JSON on stdin.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.valtersit.com/api/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HEADERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;VIT_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;MAX_LOOKUPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;  &lt;span class="c1"&gt;# each new CVE costs a credit on pay-as-you-go
&lt;/span&gt;
&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VulnerabilityID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
              &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
              &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vulnerabilities&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
              &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VulnerabilityID&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CVE-&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)})[:&lt;/span&gt;&lt;span class="n"&gt;MAX_LOOKUPS&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;blocked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cve_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/cve/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cve_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HEADERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;                                &lt;span class="c1"&gt;# unknown to us: leave it to the scanner
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;402&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;::error::out of API credits, gate could not run&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cisa_kev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;has_exploit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;has_patch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cvss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;blocked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cve_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; cvss=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cvss&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; kev=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cisa_kev&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; exploit=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;has_exploit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; patch=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;has_patch&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;blocked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;::error::blocking CVEs found:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;blocked&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gate passed (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; CVEs checked)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CVE gate&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;VIT_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VIT_API_KEY }}&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;trivy fs --format json --quiet . | python3 scripts/cve_gate.py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also ask the API directly for the worst offenders of a vendor you depend on:&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; &lt;span class="s2"&gt;"https://api.valtersit.com/api/v1/cve?vendor=Fortinet&amp;amp;cvss_min=9&amp;amp;has_exploit=true&amp;amp;has_patch=false&amp;amp;limit=5"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$VIT_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="s1"&gt;'.total_count, (.data[] | {cve_id, cvss})'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use case 4: Change tracking
&lt;/h2&gt;

&lt;p&gt;CVE data matures over days: NVD scores arrive late, patches appear, a PoC goes public. Three tools cover it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Poll for changes.&lt;/strong&gt; &lt;code&gt;changed_since&lt;/code&gt; takes an ISO-8601 date or datetime. Store the time of your last run and ask for everything newer:&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; &lt;span class="s2"&gt;"https://api.valtersit.com/api/v1/cve?vendor=Microsoft&amp;amp;changed_since=2026-09-18T00:00:00Z&amp;amp;limit=50"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$VIT_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'.total_count, (.data[] | {cve_id, cvss, has_exploit, updated_at})'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;Z&lt;/code&gt; for UTC. A &lt;code&gt;+02:00&lt;/code&gt; offset must be URL-encoded as &lt;code&gt;%2B02:00&lt;/code&gt;, because a bare &lt;code&gt;+&lt;/code&gt; in a query string turns into a space and the API answers &lt;code&gt;422&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read &lt;code&gt;pending_fields&lt;/code&gt;.&lt;/strong&gt; Every record says which values are still missing, so you know when a result is provisional:&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="nl"&gt;"pending_fields"&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="s2"&gt;"license_model"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"origin_country"&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;Re-check a record later and the list shrinks. &lt;code&gt;backfilled_fields&lt;/code&gt; reports values we filled in after you paid for the record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Push instead of poll (Pro).&lt;/strong&gt; A watchlist entry sends a webhook when a matching CVE is published or updated:&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; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.valtersit.com/api/v1/watchlist &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$VIT_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"vendors": ["Fortinet"], "min_severity": "high", "webhook_url": "https://example.com/hooks/cve"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your endpoint receives a &lt;code&gt;POST&lt;/code&gt; with &lt;code&gt;cve_id&lt;/code&gt;, &lt;code&gt;vendor&lt;/code&gt;, &lt;code&gt;cvss&lt;/code&gt;, &lt;code&gt;summary&lt;/code&gt; and &lt;code&gt;url&lt;/code&gt;. Webhook URLs must be public &lt;code&gt;http(s)&lt;/code&gt; endpoints (private and internal addresses are refused), up to 50 entries per account. Delivery is best-effort: a failed POST is not retried, so treat the webhook as a nudge and use &lt;code&gt;changed_since&lt;/code&gt; as your source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case 5: Detection rules and PoC status in one call
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.valtersit.com/api/v1/cve/CVE-2026-26084&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;VIT_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cve_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vendor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cvss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cvss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;| PoC:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;poc_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;poc_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ext&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yara_rule&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yar&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wazuh_rule&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;elastic_rule&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cve_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wrote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cve_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ext&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sigma_rule_locked&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a Sigma rule exists for this CVE but needs the Pro plan&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not every CVE has every rule, so always check for &lt;code&gt;null&lt;/code&gt;. Treat every rule as a starting point: read it, test it against known-good traffic in a lab, and only then deploy. &lt;code&gt;poc_status&lt;/code&gt; and &lt;code&gt;poc_url&lt;/code&gt; are on every plan. The &lt;code&gt;poc_code&lt;/code&gt; field (sanitized exploit code where publicly available) and &lt;code&gt;sigma_rule&lt;/code&gt; are Pro only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /cve&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Search CVEs (at least one filter, up to 100 per page)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /cve/{id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One CVE record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /ip/feed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Threat IPs: &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;min_confidence&lt;/code&gt;, &lt;code&gt;country&lt;/code&gt;, &lt;code&gt;cve&lt;/code&gt;, &lt;code&gt;changed_since&lt;/code&gt;, &lt;code&gt;ips&lt;/code&gt;, &lt;code&gt;limit&lt;/code&gt;, &lt;code&gt;offset&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /ip/{ip}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One IP (&lt;code&gt;404&lt;/code&gt; if unknown, &lt;code&gt;400&lt;/code&gt; for private ranges)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;POST /watchlist&lt;/code&gt;, &lt;code&gt;GET /watchlist&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Webhook alerts (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /history&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CVEs you have looked up, with change flags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /usage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Credits and rate-limit status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /status&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Public service status&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Credits / month&lt;/th&gt;
&lt;th&gt;Rate limit&lt;/th&gt;
&lt;th&gt;API keys&lt;/th&gt;
&lt;th&gt;IP feed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pay-as-you-go&lt;/td&gt;
&lt;td&gt;top-up, €0.08 per credit&lt;/td&gt;
&lt;td&gt;10 / min&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;look up specific IPs (&lt;code&gt;ips=&lt;/code&gt;, up to 10 per request)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;td&gt;5,000&lt;/td&gt;
&lt;td&gt;30 / min&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;full browsing, 500 rows per request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;20,000&lt;/td&gt;
&lt;td&gt;100 / min&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;full browsing, 1,000 rows per request, plus proprietary sources&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Founding Member pricing — ends 31 Dec 2026.&lt;/strong&gt; Standard &lt;strong&gt;€19/mo&lt;/strong&gt; (list €29) and Pro &lt;strong&gt;€65/mo&lt;/strong&gt; (list €99), kept for as long as your subscription stays active. &lt;a href="https://www.valtersit.com/cve/pricing/" rel="noopener noreferrer"&gt;See pricing →&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>cybersecurity</category>
      <category>api</category>
      <category>python</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why Security Teams Are Replacing Manual CVE Tracking With the ValtersIT API</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Mon, 21 Sep 2026 21:00:00 +0000</pubDate>
      <link>https://dev.to/hugovalters/why-security-teams-are-replacing-manual-cve-tracking-with-the-valtersit-api-2p53</link>
      <guid>https://dev.to/hugovalters/why-security-teams-are-replacing-manual-cve-tracking-with-the-valtersit-api-2p53</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;How one API call replaces four fragmented sources, closes the NVD-to-action gap in under 5 minutes, and costs less than one hour of analyst time per month.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every security team has a CVE problem. Not a shortage of CVE data — a surplus of it, scattered across sources that disagree, update at different rates, and give you no way to prioritize.&lt;/p&gt;

&lt;p&gt;NVD publishes 200–400 CVEs per day. CISA adds entries to KEV on its own schedule. GitHub shows PoC exploit code hours before any official feed picks it up. Your SIEM fires on a CVE ID. You need CVSS, the exploit status, whether a patch exists, and a detection rule — immediately, not after two browser tabs and a grep through three CSVs.&lt;/p&gt;

&lt;p&gt;The ValtersIT API is the single endpoint that gives you all of that, per CVE, on demand.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the free sources actually cost you
&lt;/h2&gt;

&lt;p&gt;The common response to "we need CVE enrichment" is to stitch together free sources: NVD API, CISA KEV CSV, OSV.dev, ExploitDB. This works. It also means maintaining four integrations, handling rate limits on all four, reconciling data that contradicts itself (NVD says CVSS 7.5, an advisory says 9.8), and re-running the chain every time you need fresh data.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;What it gives you&lt;/th&gt;
&lt;th&gt;What it doesn't give you&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NVD API&lt;/td&gt;
&lt;td&gt;CVSS, CWE, CPE&lt;/td&gt;
&lt;td&gt;EPSS, real exploit status, patch confirmation, detection rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CISA KEV&lt;/td&gt;
&lt;td&gt;Active exploitation flag&lt;/td&gt;
&lt;td&gt;CVSS, EPSS, technical detail, detection content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OSV.dev&lt;/td&gt;
&lt;td&gt;Package-level vuln records&lt;/td&gt;
&lt;td&gt;Normalized severity, MITRE tactic, PoC code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ExploitDB&lt;/td&gt;
&lt;td&gt;PoC code&lt;/td&gt;
&lt;td&gt;Machine-readable CVE linkage, CVSS, KEV status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub CVEProject&lt;/td&gt;
&lt;td&gt;Raw JSON from CNAs&lt;/td&gt;
&lt;td&gt;No enrichment, no structured severity, no detection content&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Correlating these manually takes 15–30 minutes per CVE. Automating it takes weeks of engineering time and ongoing maintenance.&lt;/p&gt;

&lt;p&gt;A typical security engineer costs €60–100/hr fully loaded. One hour of manual CVE research per week is €3,000–5,000/year. The ValtersIT Standard plan is &lt;strong&gt;€19/month&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What one API call returns
&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://api.valtersit.com/api/v1/cve/CVE-2024-3400 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer vit_v1_YOUR_KEY"&lt;/span&gt; | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"cve_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CVE-2024-3400"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vendor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Palo Alto Networks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cvss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cvss_vector"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"epss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.9744&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"has_exploit"&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;"has_patch"&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;"cisa_kev"&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;"poc_status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Public"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cwe_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CWE-77"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mitre_tactic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"T1190"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shodan_dorks"&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="s2"&gt;"product:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;GlobalProtect&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http.title:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;GlobalProtect&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&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;"affected_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;"PAN-OS 10.2, 11.0, 11.1 below specific versions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"patch_details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fixed in PAN-OS 10.2.9-h1, 11.0.4-h1, 11.1.2-h3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"yara_rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rule CVE_2024_3400 { ... }"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"wazuh_rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;group name=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;vulnerability,...&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt; ... &amp;lt;/group&amp;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;"elastic_rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"process where process.args : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;*/...AAAA*&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&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;"pending_fields"&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;"credits_used"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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;That is one HTTP request. Everything needed to triage, alert, and respond — no joins, no secondary lookups, no format reconciliation.&lt;/p&gt;




&lt;h2&gt;
  
  
  The EPSS number changes everything
&lt;/h2&gt;

&lt;p&gt;CVSS scores are assigned once and rarely change. A CVE with CVSS 7.5 from 2023 sits in the same bucket as a CVE with CVSS 7.5 from last week, even if one has active exploits in the wild and the other has never been touched.&lt;/p&gt;

&lt;p&gt;EPSS (Exploit Prediction Scoring System) is updated daily and measures the probability of exploitation in the next 30 days based on real-world data. The ValtersIT API returns it as a decimal float (0.0–1.0) on every CVE record, normalized — no string parsing needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practical prioritization:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.valtersit.com/api/v1/cve&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;severity&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;critical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;has_exploit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cve&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="c1"&gt;# EPSS &amp;gt;0.5 = 50%+ chance of exploitation in next 30 days
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;epss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;cve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;epss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cve_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | CVSS &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cvss&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | EPSS &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;epss&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cve&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;vendor&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query runs in under 300ms. The same result from NVD + EPSS API requires two separate authenticated calls, rate limit awareness, and field normalization.&lt;/p&gt;




&lt;h2&gt;
  
  
  Detection content included — not add-on priced
&lt;/h2&gt;

&lt;p&gt;Every CVE that has had detection content generated ships it in the API response with no extra call. YARA rules, Wazuh rules, and Elastic EQL/KQL queries are included on every paid plan. Sigma rules and raw PoC code are Pro only.&lt;/p&gt;

&lt;p&gt;Getting detection content from free sources requires a Sigma rule repository (often months behind), manual YARA authoring, or a paid subscription to a different vendor. The ValtersIT API includes it in the same response you're already making.&lt;/p&gt;




&lt;h2&gt;
  
  
  The data honesty contract
&lt;/h2&gt;

&lt;p&gt;Free CVE feeds hide their gaps behind null values that look identical to "this field genuinely doesn't apply." The ValtersIT API separates the two cases explicitly:&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;"cve_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CVE-2026-99999"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cvss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pending_fields"&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="s2"&gt;"cvss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"epss"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mitre_tactic"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;pending_fields&lt;/code&gt; means NVD hasn't assigned these values yet (CVSS typically arrives 2–14 days after publication). When they do, the field moves out of &lt;code&gt;pending_fields&lt;/code&gt;. On Pay-As-You-Go, the backfill is &lt;strong&gt;free&lt;/strong&gt; — you're not charged twice because NVD caught up.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pricing: less than one analyst-hour per month
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Founding Member pricing — ends 31 Dec 2026&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Standard €19/mo (list €29) and Pro €65/mo (list €99), locked permanently for subscriptions started before 1 Jan 2027.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Credits/mo&lt;/th&gt;
&lt;th&gt;Sigma + PoC code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Sandbox&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;— (10 synthetic records)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;td&gt;€19/mo&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;€65/mo&lt;/td&gt;
&lt;td&gt;50,000&lt;/td&gt;
&lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pay-as-you-go&lt;/td&gt;
&lt;td&gt;€5/1,000 credits&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One credit per CVE record returned. Already-fetched CVEs on Standard/Pro are always free to re-check.&lt;/p&gt;




&lt;h2&gt;
  
  
  Start in 30 seconds — no credit card
&lt;/h2&gt;

&lt;p&gt;The sandbox key is public. Ten synthetic CVE records, full response shape, immediate testing:&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; &lt;span class="s2"&gt;"https://api.valtersit.com/api/v1/cve"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer vit_sandbox_free_demo_key_2026"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | jq &lt;span class="s1"&gt;'.data[0] | {cve_id, vendor, cvss, epss, cisa_kev, has_exploit, pending_fields}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When ready for real data: &lt;a href="https://api.valtersit.com" rel="noopener noreferrer"&gt;api.valtersit.com&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The gap the ValtersIT API closes
&lt;/h2&gt;

&lt;p&gt;Between "CVE is published" and "your team acts on it" there are four steps: find it, enrich it with CVSS/EPSS/KEV/exploit status, write or find detection content, distribute to whoever needs it. Free tools require a human to do all four manually. The ValtersIT API collapses steps two and three into a single HTTP call.&lt;/p&gt;

&lt;p&gt;That's not a small efficiency gain. It's the difference between a CVE being actionable in 5 minutes and actionable in 5 days.&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>cybersecurity</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
