<?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>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>
    <item>
      <title>TLS Certificate Automation: Certbot and ACME at Scale</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Sat, 19 Sep 2026 14:15:03 +0000</pubDate>
      <link>https://dev.to/hugovalters/tls-certificate-automation-certbot-and-acme-at-scale-4nbp</link>
      <guid>https://dev.to/hugovalters/tls-certificate-automation-certbot-and-acme-at-scale-4nbp</guid>
      <description>&lt;p&gt;03:14. &lt;code&gt;payments-api&lt;/code&gt; starts returning 502s. Four hundred and forty-seven seconds later someone notices the dashboard is red. Forty-seven minutes later, someone finds the postmortem bullet nobody wants to write: the "automation" was a cron job on a host that had been decommissioned eight months earlier. The cron job didn't fail. It was never going to run again. Nobody owned the cert, nobody monitored it, and the first signal was customer-facing downtime.&lt;/p&gt;

&lt;p&gt;Certificate management is the cheapest layer of your stack to automate and the most expensive one to get wrong. It is also the layer where "we renew manually" stopped being a defensible statement somewhere around 2019 and became, in 2026, a statement about your on-call rotation rather than your security posture. If a leaf certificate expires and your service degrades, that is not bad luck — it is an unowned dependency.&lt;/p&gt;

&lt;p&gt;This guide is for engineers running real traffic: sysadmins, SREs, and platform teams who already have Certbot installed and want to know why it keeps biting them anyway. You'll get ACME internals, Certbot patterns that survive contact with production, challenge-selection tradeoffs, renewal hook discipline, hardening, and the rate limits that will ruin your Thursday. What you won't get is a "five clicks in a vendor dashboard" walkthrough — that content exists, it's just not useful to anyone operating at scale.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Automating issuance is easy; automating &lt;strong&gt;deployment&lt;/strong&gt; is where outages live. If your reload path isn't idempotent and monitored, you haven't automated anything.&lt;/li&gt;
&lt;li&gt;The CAB Forum baseline keeps shrinking certificate lifetimes — plan for 200 days now, 100 days in 2027, and short-lived certificates sooner than you think.&lt;/li&gt;
&lt;li&gt;HTTP-01 is the default for a reason; DNS-01 is the only path to wildcards and it turns your DNS API key into a certificate-issuance credential. Scope it accordingly.&lt;/li&gt;
&lt;li&gt;Trust the packaged &lt;code&gt;certbot.timer&lt;/code&gt;, write a real deploy hook, and monitor the certificate &lt;strong&gt;as served&lt;/strong&gt;, not the file on disk.
:::&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Certbot 2.x (or newer) installed from your distro, snap, or the official container image — check the docs for the current release line&lt;/li&gt;
&lt;li&gt;Root or sudo on the host that terminates TLS&lt;/li&gt;
&lt;li&gt;Port 80 reachable from the public internet if you're using HTTP-01 (including over IPv6)&lt;/li&gt;
&lt;li&gt;A DNS provider with a supported API plugin if you need wildcards&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;openssl&lt;/code&gt; and &lt;code&gt;jq&lt;/code&gt; for the inventory and monitoring snippets below&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Certificate automation is the layer you actually skipped
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The 90-day treadmill is over
&lt;/h3&gt;

&lt;p&gt;The CAB Forum ballot SC-081 phases down the maximum lifetime of publicly-trusted certificates: 200 days from March 2026, 100 days from March 2027, and 47 days from March 2029. Let's Encrypt has been piloting short-lived (roughly six-day) certificates for years and is pushing the ecosystem toward them. Check the current Baseline Requirements before you bake a number into an SLA — the schedule has been revised before and will be again.&lt;/p&gt;

&lt;p&gt;What that fixes: revocation. Revocation has been broken since OCSP was invented. A six-day certificate doesn't need to be revoked reliably because it expires before anyone can care. What it breaks: every assumption built on a 60-day renewal window, every cron job with a monthly cadence, and every monitoring rule that alerts at 30 days remaining.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ DECLASSIFIED / TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
You are reading a truncated version of this technical guide. &lt;br&gt;
To read the full, unedited deep-dive (including all configuration files, architecture diagrams, and high-res images), &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/security/tls-certificate-automation-certbot-and-acme-at-scale/" rel="noopener noreferrer"&gt;visit the original post on Valters IT Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>tls</category>
      <category>certbot</category>
      <category>letsencrypt</category>
      <category>devsecops</category>
    </item>
    <item>
      <title>Docker on ZimaBoard: Memory Constraints &amp; Workarounds</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Fri, 18 Sep 2026 14:15:03 +0000</pubDate>
      <link>https://dev.to/hugovalters/docker-on-zimaboard-memory-constraints-workarounds-2672</link>
      <guid>https://dev.to/hugovalters/docker-on-zimaboard-memory-constraints-workarounds-2672</guid>
      <description>&lt;p&gt;The ZimaBoard is genuinely good hardware. It's also a 6-watt passively cooled x86 box with soldered RAM and an eMMC that will die before your warranty does. Docker will run on it fine. Docker will also cheerfully OOM-kill your reverse proxy at 3 AM because you let a Jellyfin container have 1.2 GB of headroom it never asked for.&lt;/p&gt;

&lt;p&gt;This guide is for homelab operators, self-hosters, and junior infra folks who bought a fanless x86 board expecting a Raspberry Pi with better I/O — and got a memory ceiling they can't upgrade. By the end you'll understand why three specific failure modes happen (&lt;strong&gt;OOM-kill roulette&lt;/strong&gt;, &lt;strong&gt;eMMC death by swap churn&lt;/strong&gt;, and &lt;strong&gt;build-time OOM on the host&lt;/strong&gt;), and you'll have hard limits, zram tuned correctly, and a workload-sizing strategy that keeps a 2 GB box serving useful work instead of thrashing itself to death.&lt;/p&gt;

&lt;p&gt;Every recommendation below is a tradeoff. I'll name the tradeoff. There's no getting-started fluff — I assume Debian/Ubuntu/CasaOS is installed and &lt;code&gt;docker&lt;/code&gt; runs.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;ZimaBoard RAM is soldered. You lose &lt;strong&gt;300–400 MB&lt;/strong&gt; to kernel + dockerd + containerd + journald before your first container even starts.&lt;/li&gt;
&lt;li&gt;Never put a swapfile on eMMC. Use &lt;strong&gt;zram&lt;/strong&gt; with a hard cap, and understand zram buys latency, not free memory.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deploy.resources.limits.memory&lt;/code&gt; is often silently ignored in Compose. Use &lt;code&gt;mem_limit&lt;/code&gt; on non-Swarm hosts.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;memory.high&lt;/code&gt; slightly below &lt;code&gt;memory.max&lt;/code&gt; so you get throttle + PSI pressure, not a silent kill.&lt;/li&gt;
&lt;li&gt;Build images off the board. BuildKit's default parallelism will OOM-kill a 2 GB Apollo Lake host.
:::&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;ZimaBoard 216, 432, or 832 running Debian 12+, Ubuntu 22.04+, or a CasaOS base.&lt;/li&gt;
&lt;li&gt;Docker Engine 24+ with cgroup v2 enabled (verify in Section 3).&lt;/li&gt;
&lt;li&gt;Root or &lt;code&gt;sudo&lt;/code&gt; access for sysctl, systemd unit overrides, and cgroup inspection.&lt;/li&gt;
&lt;li&gt;A SATA SSD or NVMe (via USB) if you plan to run anything stateful. This is not optional for anything except throwaway tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Section 1 — Know What You Actually Bought
&lt;/h2&gt;

&lt;p&gt;The ZimaBoard is not a Raspberry Pi. It's an Apollo Lake x86 SoC — Goldmont cores, no SMT, no ECC, DDR4 soldered to the board. You cannot add RAM. Ever. The memory you ordered is the memory you get for the life of the device.&lt;/p&gt;

&lt;h3&gt;
  
  
  The SKU table matters
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;CPU&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;eMMC&lt;/th&gt;
&lt;th&gt;Realistic container count&lt;/th&gt;
&lt;th&gt;What will actually fit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;216&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Celeron N3350 (2c)&lt;/td&gt;
&lt;td&gt;2 GB&lt;/td&gt;
&lt;td&gt;16 GB&lt;/td&gt;
&lt;td&gt;3–5 tiny&lt;/td&gt;
&lt;td&gt;AdGuard, Caddy, a wiki. No DB.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;432&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Celeron N3350 (2c)&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;td&gt;32 GB&lt;/td&gt;
&lt;td&gt;6–10 small&lt;/td&gt;
&lt;td&gt;Reverse proxy, small Postgres + PgBouncer, monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;832&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Celeron N3450 (4c)&lt;/td&gt;
&lt;td&gt;8 GB&lt;/td&gt;
&lt;td&gt;32 GB&lt;/td&gt;
&lt;td&gt;12–20 medium&lt;/td&gt;
&lt;td&gt;Media server (with QuickSync), Home Assistant, light JVM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Baseline memory accounting before you install anything
&lt;/h3&gt;

&lt;p&gt;People consistently under-estimate the tax the plumbing applies. In testing on a fresh Debian 12 install with Docker installed and zero containers running, I typically see:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Typical RSS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kernel + minimal userspace&lt;/td&gt;
&lt;td&gt;130–170 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dockerd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;60–90 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;containerd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;25–40 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;docker-proxy&lt;/code&gt; / shims (idle)&lt;/td&gt;
&lt;td&gt;20–50 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;systemd-journald&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30–60 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~300–400 MB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's your 2 GB board down to ~1.6 GB of &lt;em&gt;actual&lt;/em&gt; headroom before workloads. Now install CasaOS, Portainer, and Watchtower and you're at 640 MB consumed at idle — before a single service runs. The 216 is not a "small home server." It's an appliance box for three well-behaved containers.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ DECLASSIFIED / TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
You are reading a truncated version of this technical guide. &lt;br&gt;
To read the full, unedited deep-dive (including all configuration files, architecture diagrams, and high-res images), &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/zima/docker-on-zimaboard-memory-constraints-workarounds/" rel="noopener noreferrer"&gt;visit the original post on Valters IT Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>zimaboard</category>
      <category>docker</category>
      <category>memory</category>
      <category>selfhosting</category>
    </item>
    <item>
      <title>Proxmox Resource Pools: Multi-Tenant Isolation Guide</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Sun, 13 Sep 2026 14:15:03 +0000</pubDate>
      <link>https://dev.to/hugovalters/proxmox-resource-pools-multi-tenant-isolation-guide-1155</link>
      <guid>https://dev.to/hugovalters/proxmox-resource-pools-multi-tenant-isolation-guide-1155</guid>
      <description>&lt;p&gt;A managed service provider migrated 40 customers onto a single six-node Proxmox VE cluster. Every VM lived in the default "no pool" state. Every tenant admin had been handed &lt;code&gt;PVEVMAdmin&lt;/code&gt; on &lt;code&gt;/vms&lt;/code&gt; during onboarding because "it just made things easier." One Tuesday afternoon, a new hire at tenant #12 ran &lt;code&gt;qm list&lt;/code&gt; from a jump box and printed the entire customer base — VMIDs, hostnames, resource usage, node placement — to a shared Slack channel. Nothing was breached. Nothing was exploited. The permission model did exactly what it was told to do.&lt;/p&gt;

&lt;p&gt;This guide is for sysadmins, MSPs, homelab-to-production operators, and platform engineers who need to split one PVE cluster across teams, departments, or paying customers. After reading it you'll be able to design pools that enforce visibility, roles that actually constrain what a tenant can touch, storage and network boundaries that survive a hostile tenant, and an audit trail that lets you prove all of it after the fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;Resource pools are an organizational construct. ACLs are the security boundary. Confuse the two and you get a nice UI folder with zero enforcement.&lt;/li&gt;
&lt;li&gt;PVE ACLs are &lt;strong&gt;purely additive&lt;/strong&gt; — there is no deny rule anywhere in the model. Isolation comes from &lt;em&gt;not granting&lt;/em&gt;, not from revoking.&lt;/li&gt;
&lt;li&gt;Tenant isolation has three tiers. Know which one you're actually buying or you'll oversell Tier 1 as Tier 3 and get burned.&lt;/li&gt;
&lt;li&gt;API tokens with &lt;code&gt;--privsep 1&lt;/code&gt; and pool-scoped ACLs, plus SDN VNets and dedicated storage, are the minimum viable Tier 3 setup.
:::&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Proxmox VE 8.x on at least one node, cluster recommended&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;root@pam&lt;/code&gt; access (or an account with &lt;code&gt;Permissions.Modify&lt;/code&gt; at &lt;code&gt;/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Working knowledge of &lt;code&gt;pveum&lt;/code&gt; and &lt;code&gt;pvesh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A ZFS, LVM, or Ceph storage backend you can carve per tenant&lt;/li&gt;
&lt;li&gt;A managed switch or router you control for VLAN tagging&lt;/li&gt;
&lt;li&gt;A firewall you own — PVE's host firewall is not a substitute for network segmentation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proxmox Is Not a Multi-Tenant Hypervisor, and That's Fine
&lt;/h2&gt;

&lt;p&gt;PVE was designed as a trusted-admin cluster management plane. The permission system exists to keep junior admins out of the storage backend, not to defend against a determined adversary sharing hardware with you. KVM is a mature hypervisor, but PVE's tenant story is a &lt;em&gt;management&lt;/em&gt; control wrapped around it, and management controls fail in predictable ways when you assume they're security controls.&lt;/p&gt;

&lt;p&gt;The threat model that should keep you up at night is mundane. It's the contractor with a &lt;code&gt;PVEAuditor&lt;/code&gt; token enumerating every VMID in the cluster. It's the CI runner whose API token leaked into a public build log because somebody ran with &lt;code&gt;--privsep 0&lt;/code&gt; and never rotated. It's the tenant admin who can see your storage inventory and starts asking why your Ceph cluster shows 40TB free when their contract says 500GB.&lt;/p&gt;

&lt;p&gt;What PVE gives you out of the box: ACLs, roles, groups, users, API tokens with privilege separation, resource pools, SDN VNets, and per-VM firewalls. What you must build yourself: pool hygiene (things live where they're supposed to), storage separation, network segmentation, token lifecycle management, and audit that actually answers "who did what."&lt;/p&gt;

&lt;h3&gt;
  
  
  The Permission Model in 90 Seconds
&lt;/h3&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ DECLASSIFIED / TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
You are reading a truncated version of this technical guide. &lt;br&gt;
To read the full, unedited deep-dive (including all configuration files, architecture diagrams, and high-res images), &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/proxmox/proxmox-resource-pools-multi-tenant-isolation-guide/" rel="noopener noreferrer"&gt;visit the original post on Valters IT Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>proxmox</category>
      <category>multitenant</category>
      <category>accesscontrol</category>
      <category>infrastructuresecurity</category>
    </item>
    <item>
      <title>Network Segmentation: VLANs and Firewall Rules That Actually Work</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Wed, 05 Aug 2026 14:15:03 +0000</pubDate>
      <link>https://dev.to/hugovalters/network-segmentation-vlans-and-firewall-rules-that-actually-work-3g6b</link>
      <guid>https://dev.to/hugovalters/network-segmentation-vlans-and-firewall-rules-that-actually-work-3g6b</guid>
      <description>&lt;p&gt;I walked into a client site last year to find a payment processing server sitting on the same /24 as the guest WiFi. The "temporary" flat network had been running for four years. A WordPress plugin vulnerability on a marketing site gave an attacker lateral movement to cardholder data in under 40 minutes. The breach cost them their PCI compliance status, two major contracts, and the CISO's job.&lt;/p&gt;

&lt;p&gt;If you can ping your database server from the guest WiFi, you don't have a security architecture — you have a prayer.&lt;/p&gt;

&lt;p&gt;This guide is for engineers who know their flat network is a problem but haven't been given the ammunition to fix it. After reading, you'll understand VLANs for Layer 2 isolation, firewall rules for Layer 3/4 enforcement, and the operational discipline to keep both from rotting into chaos.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;VLANs contain broadcast domains — they are not security boundaries&lt;/li&gt;
&lt;li&gt;Default deny at zone boundaries is non-negotiable for production&lt;/li&gt;
&lt;li&gt;Your IP scheme should encode the VLAN ID — if it doesn't, fix it now&lt;/li&gt;
&lt;li&gt;Every firewall rule needs an owner, a ticket number, and an expiration date&lt;/li&gt;
&lt;li&gt;Disable DTP on every port today — VLAN hopping is trivial when it's on
:::&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Access to your network gear (Cisco IOS, pfSense, or Linux with nftables)&lt;/li&gt;
&lt;li&gt;A maintenance window for config changes&lt;/li&gt;
&lt;li&gt;An accurate inventory of what's actually on your network (you'll need it)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction — Stop Treating Your Network Like a Flattened Pancake
&lt;/h2&gt;

&lt;p&gt;The "one big /24" mentality is a professional liability. The 2013 Target breach should have ended this debate permanently — attackers pivoted from an HVAC vendor's credentials to the POS network because there was no real segmentation between the corporate and payment environments. PCI DSS 4.0 Requirement 1, NIST 800-125, and CIS Controls v8 Control 12 all mandate segmentation. They exist because people keep running flat networks and getting burned.&lt;/p&gt;

&lt;p&gt;Here's what we're covering: VLANs for Layer 2 isolation, firewall rules for Layer 3/4 enforcement, and the operational discipline required to maintain both. This isn't theory — this is the architecture I've deployed in environments processing billions in transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  VLAN Fundamentals — The Layer 2 Illusion of Separation
&lt;/h2&gt;

&lt;p&gt;VLANs segment broadcast domains. They make it look like devices on different VLANs can't talk to each other, but that's a convenience of default configuration, not a security property. Any router or L3 switch with interfaces on both VLANs can route between them — and if you haven't configured firewall rules, that routing is unrestricted.&lt;/p&gt;

&lt;p&gt;802.1Q tagging works by inserting a 4-byte tag into the Ethernet frame header. This tag carries a 12-bit VLAN ID, which allows switches to forward frames only to ports in the same VLAN. The critical security point: this tag is not encrypted, not authenticated, and trivially spoofable if an attacker controls a trunk port.&lt;/p&gt;

&lt;p&gt;Here's the Cisco config pattern I use for every environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vlan 10
 name WEB
vlan 20
 name APP
vlan 30
 name DB
!
interface GigabitEthernet0/1
 switchport mode access
 switchport access vlan 10
!
interface GigabitEthernet0/24
 switchport mode trunk
 switchport trunk allowed vlan 10,20,30
 switchport trunk native vlan 999
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates three VLANs, assigns one access port, and configures a trunk with an explicit allowed VLAN list. The native VLAN is changed from the default of 1 to 999 — an unused VLAN that reduces the attack surface for VLAN hopping.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ DECLASSIFIED / TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
You are reading a truncated version of this technical guide. &lt;br&gt;
To read the full, unedited deep-dive (including all configuration files, architecture diagrams, and high-res images), &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/security/network-segmentation-vlans-and-firewall-rules-that-actually-work/" rel="noopener noreferrer"&gt;visit the original post on Valters IT Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>networksegmentation</category>
      <category>vlansecurity</category>
      <category>firewallrules</category>
      <category>zerotrust</category>
    </item>
    <item>
      <title>AppArmor and seccomp Profiles: Restricting Container System Calls</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Tue, 04 Aug 2026 14:15:04 +0000</pubDate>
      <link>https://dev.to/hugovalters/apparmor-and-seccomp-profiles-restricting-container-system-calls-2316</link>
      <guid>https://dev.to/hugovalters/apparmor-and-seccomp-profiles-restricting-container-system-calls-2316</guid>
      <description>&lt;h1&gt;
  
  
  AppArmor and seccomp Profiles: Restricting Container System Calls
&lt;/h1&gt;

&lt;h2&gt;
  
  
  A Senior DevSecOps Guide to Kernel-Level Container Hardening
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Target Audience:&lt;/strong&gt; Platform engineers, security engineers, Kubernetes operators&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Tone:&lt;/strong&gt; Direct, opinionated, battle-hardened&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Prerequisites:&lt;/strong&gt; Basic Linux kernel concepts, container runtime familiarity&lt;/p&gt;


&lt;h2&gt;
  
  
  Introduction — Why Your Containers Have Too Much Kernel Access
&lt;/h2&gt;

&lt;p&gt;I've spent 15 years watching teams treat container security as a checkbox exercise. The worst part? Docker's default seccomp profile isn't a security boundary — it's a courtesy. It's designed to not break your applications, not to stop attackers.&lt;/p&gt;

&lt;p&gt;The uncomfortable truth is that most container escapes (CVE-2022-0492, CVE-2023-25173) exploit syscalls that neither AppArmor nor seccomp block by default. Every &lt;code&gt;unshare()&lt;/code&gt;, &lt;code&gt;mount()&lt;/code&gt;, or &lt;code&gt;keyctl()&lt;/code&gt; call is a potential privilege escalation vector. And most teams are running with the kernel equivalent of a screen door.&lt;/p&gt;

&lt;p&gt;Here's a quick reality check:&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;# Show default seccomp profile in Docker&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; alpine sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'cat /proc/$$/status | grep Seccomp'&lt;/span&gt;
&lt;span class="c"&gt;# Returns: Seccomp: 2 (filtered, but you'd be surprised what's allowed)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I once audited a production cluster where 60% of containers ran with &lt;code&gt;--privileged&lt;/code&gt; because "it was faster than debugging permission issues." The CISO had a stroke. The CTO didn't care. We fixed it with profiles — but it took three months of convincing.&lt;/p&gt;

&lt;p&gt;Here's the actionable takeaway: If you're not running custom AppArmor and seccomp profiles, you're running with training wheels on a motorcycle. One wrong turn and you're eating pavement.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Default container profiles allow 300+ syscalls — most workloads need &amp;lt;50&lt;/li&gt;
&lt;li&gt;AppArmor blocks file paths, seccomp blocks syscalls — you need both&lt;/li&gt;
&lt;li&gt;Capabilities don't replace profiles; they complement them&lt;/li&gt;
&lt;li&gt;Kubernetes doesn't enforce profile content, only existence — use admission controllers&lt;/li&gt;
&lt;li&gt;Test profiles in complain/log mode for 24+ hours before enforcing
:::&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Before we dive in, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Linux system with AppArmor enabled (check with &lt;code&gt;sudo aa-status&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Docker or containerd runtime (v20.10+ for full seccomp support)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;strace&lt;/code&gt;, &lt;code&gt;auditd&lt;/code&gt;, and &lt;code&gt;apparmor-utils&lt;/code&gt; installed&lt;/li&gt;
&lt;li&gt;Kubernetes cluster (v1.25+) if deploying profiles to K8s&lt;/li&gt;
&lt;li&gt;Basic familiarity with kernel syscalls and security contexts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding the Linux Syscall Attack Surface in Containers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The 300+ Syscalls Your Container Doesn't Need
&lt;/h3&gt;

&lt;p&gt;The problem is straightforward: default profiles allow 300+ syscalls. Most containers need fewer than 50. Every extra syscall is a potential weapon for an attacker who gets code execution inside your container.&lt;/p&gt;

&lt;p&gt;Consider &lt;code&gt;unshare(CLONE_NEWNS)&lt;/code&gt; — a single syscall that can break out of mount namespaces. A good seccomp profile blocks it. The default Docker profile? It allows it.&lt;/p&gt;

&lt;p&gt;Here's how to see what your container actually calls:&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;# Capture syscalls from a running container&lt;/span&gt;
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--security-opt&lt;/span&gt; &lt;span class="nv"&gt;seccomp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unconfined alpine sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"strace -c -S name ls / 2&amp;gt;&amp;amp;1 | tail -20"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs &lt;code&gt;ls&lt;/code&gt; with strace and shows a syscall count. You'll see maybe 30 syscalls for a simple command. Now imagine what a web server or database needs — probably 40-60, not 300.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comparison: Default Docker profile vs. minimal web server profile&lt;/strong&gt;&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ DECLASSIFIED / TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
You are reading a truncated version of this technical guide. &lt;br&gt;
To read the full, unedited deep-dive (including all configuration files, architecture diagrams, and high-res images), &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/security/apparmor-and-seccomp-profiles-restricting-container-system-calls/" rel="noopener noreferrer"&gt;visit the original post on Valters IT Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>apparmor</category>
      <category>seccomp</category>
      <category>containersecurity</category>
      <category>kuberneteshardening</category>
    </item>
    <item>
      <title>Network Segmentation: VLANs and Firewall Rules for Service Isolation</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Fri, 24 Jul 2026 14:15:04 +0000</pubDate>
      <link>https://dev.to/hugovalters/network-segmentation-vlans-and-firewall-rules-for-service-isolation-5b47</link>
      <guid>https://dev.to/hugovalters/network-segmentation-vlans-and-firewall-rules-for-service-isolation-5b47</guid>
      <description>&lt;p&gt;I've spent 15 years cleaning up the aftermath of flat networks. The worst was a 2020 incident where a Redis instance, left exposed on the default VLAN, became an attacker's highway. Within four hours, they'd pivoted through 200+ VMs, exfiltrated customer data, and planted cryptominers across three availability zones. The CISO's words still echo: "We thought the firewall at the edge was enough."&lt;/p&gt;

&lt;p&gt;This guide is for platform engineers and DevOps teams who've outgrown the "just add a security group" mindset. You'll learn how VLANs and firewall rules actually work together—not just theory, but production-hardened configs that prevent lateral movement. By the end, you'll be able to design a segmented network that contains breaches instead of amplifying them.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Flat networks are indefensible; one compromised service becomes your entire blast radius.&lt;/li&gt;
&lt;li&gt;VLANs provide Layer 2 isolation, but firewall rules enforce actual access control between segments.&lt;/li&gt;
&lt;li&gt;Group services by trust level, not by count—"one VLAN per service" creates management hell.&lt;/li&gt;
&lt;li&gt;Egress filtering catches 90% of breaches; allow-all-outbound is a sieve.&lt;/li&gt;
&lt;li&gt;Automate everything with IaC; hand-jammed rules are technical debt with interest.&lt;/li&gt;
&lt;li&gt;Test segmentation monthly with port scans—trusting your config is how breaches happen.
:::&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Access to a network device (Cisco IOS, Linux bridge, or cloud VPC) for VLAN configuration&lt;/li&gt;
&lt;li&gt;Basic understanding of OSI model layers 2-4&lt;/li&gt;
&lt;li&gt;A firewall platform (iptables/nftables on Linux, or cloud-native like AWS Security Groups)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nmap&lt;/code&gt; and &lt;code&gt;tcpdump&lt;/code&gt; installed on test machines for verification&lt;/li&gt;
&lt;li&gt;Terraform or similar IaC tool if automating (recommended)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Segmentation Matters — The "Flat Network is a Crime" Argument
&lt;/h2&gt;

&lt;p&gt;A flat network is the architectural equivalent of putting all your servers in one room with the door unlocked. It's convenient for the first deployment, but the moment an attacker finds one vulnerability—an unpatched web app, a Redis without auth, a Jenkins with default creds—they own everything. Lateral movement becomes trivial because there are no boundaries to cross.&lt;/p&gt;

&lt;p&gt;The principle is simple: least privilege applied to the network layer. Every service should only talk to exactly what it needs, on exactly the ports required, and nothing else. This isn't paranoia—it's physics. You cannot defend what you cannot isolate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: "But my startup has 5 services—do I really need VLANs?"&lt;/strong&gt;&lt;br&gt;
Yes. You won't stay a startup forever, and retrofitting segmentation into a production network is a nightmare that involves downtime, broken dependencies, and late-night rollbacks. Start segmented from day one, even if it's just three VLANs. Your future self will thank you.&lt;/p&gt;

&lt;h2&gt;
  
  
  VLAN Fundamentals — Not Just for Network Engineers Anymore
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What VLANs Actually Do (and Don't Do)
&lt;/h3&gt;

&lt;p&gt;VLANs isolate broadcast domains at Layer 2. That's it. They prevent ARP storms from crossing segments and force traffic through a router for inter-VLAN communication. But they are not encryption, not authentication, and not a security boundary on their own. If you trunk VLANs together without firewall rules, you've built a flat network with extra steps.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ DECLASSIFIED / TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
You are reading a truncated version of this technical guide. &lt;br&gt;
To read the full, unedited deep-dive (including all configuration files, architecture diagrams, and high-res images), &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/security/network-segmentation-vlans-and-firewall-rules-for-service-isolation/" rel="noopener noreferrer"&gt;visit the original post on Valters IT Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>networksegmentation</category>
      <category>vlansecurity</category>
      <category>firewallrules</category>
      <category>infrastructuresecurity</category>
    </item>
    <item>
      <title>Firewall 101: The 'Drop Everything by Default' Manifesto</title>
      <dc:creator>Hugo | DevOps | Cybersecurity</dc:creator>
      <pubDate>Sun, 19 Jul 2026 14:15:04 +0000</pubDate>
      <link>https://dev.to/hugovalters/firewall-101-the-drop-everything-by-default-manifesto-2d9g</link>
      <guid>https://dev.to/hugovalters/firewall-101-the-drop-everything-by-default-manifesto-2d9g</guid>
      <description>&lt;p&gt;I once audited a "secure" government-contractor network where the lead admin proudly showed me their $25,000 Next-Gen Firewall. It had every subscription active: AI-powered threat detection, SSL inspection, sandboxing—the works. Then I looked at the bottom of the filter list. &lt;/p&gt;

&lt;p&gt;Rule #157: &lt;code&gt;chain=forward action=accept src-address=0.0.0.0/0 dst-address=0.0.0.0/0 comment="Temporary fix for printer issues"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That "temporary fix" had been there for eighteen months. Because the admin couldn't figure out which specific ports the legacy Xerox scanner needed, they just opened the entire network to the entire world. They had spent $25,000 on a gatekeeper, and then handed the keys to everyone on the street because they didn't want to spend twenty minutes reading a packet capture.&lt;/p&gt;

&lt;p&gt;If you have a "Permit All" or "Allow Any" rule at the end of your firewall, you don't have a firewall; you have a very expensive, very warm paperweight. You aren't practicing security; you are practicing hope. And in the world of 2026, hope is a remarkably poor defense against a motivated threat actor.&lt;/p&gt;

&lt;p&gt;It is time to nuke your rules, embrace the "Default Deny" manifesto, and build a configuration where every single bit of data has to justify its existence before it’s allowed to pass.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Rant: The "Troubleshooting" Lie
&lt;/h3&gt;

&lt;p&gt;We’ve all heard it: "I’ll just disable the firewall for a second to see if that’s the problem." &lt;/p&gt;

&lt;p&gt;That "second" becomes an hour. That hour becomes a weekend. That weekend becomes a year. Most "Permit All" rules are the scars of a sysadmin who got tired of fighting with a vendor’s vague documentation. Instead of doing the work—using &lt;code&gt;tcpdump&lt;/code&gt; or the MikroTik &lt;code&gt;Packet Sniffer&lt;/code&gt; to see exactly what traffic is being blocked—they take the path of least resistance. &lt;/p&gt;

&lt;p&gt;The problem is that once you open that door, you never close it. You forget. Or you're afraid that closing it will break something you don't understand. &lt;/p&gt;

&lt;p&gt;A Senior Sysadmin operates under a different philosophy: &lt;strong&gt;If I don't understand why a packet is moving, it shouldn't be moving.&lt;/strong&gt; Security is not a feature you add on top of a working network. Security &lt;em&gt;is&lt;/em&gt; the network. If your application doesn't work under a strict "Default Deny" policy, the application is broken, not the firewall.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Mechanics: The Default Deny Principle
&lt;/h3&gt;

&lt;p&gt;The "Default Deny" principle (also known as "Whitelist-only") is the only sane way to manage a network. In this model, you start with a completely empty routing table and a firewall that drops every single packet. &lt;/p&gt;

&lt;p&gt;You then add "Allow" rules for specific, documented needs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Allow&lt;/strong&gt; DNS (UDP 53) to your trusted resolvers.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Allow&lt;/strong&gt; HTTPS (TCP 443) to your web servers.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Allow&lt;/strong&gt; SSH (TCP 22) only from your Management Jump Box.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Everything else—every random scan, every misconfigured IoT device phoning home, every lateral movement attempt from a compromised laptop—hits the "Implicit Drop" at the bottom of the list and vanishes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why "Blacklisting" Fails
&lt;/h4&gt;

&lt;p&gt;Most consumer routers and amateur setups use "Default Allow." They allow everything and try to block "bad" things. This is a losing game. There are billions of "bad" IPs and thousands of "bad" ports. You cannot possibly maintain a list of everything you &lt;em&gt;don't&lt;/em&gt; want. You must maintain a list of exactly what you &lt;em&gt;do&lt;/em&gt; want.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Mechanics: Stateful Packet Inspection (SPI)
&lt;/h3&gt;

&lt;p&gt;To run a "Default Deny" firewall without losing your mind, you must use &lt;strong&gt;Stateful Packet Inspection&lt;/strong&gt;. &lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ DECLASSIFIED / TRUNCATED VERSION&lt;/strong&gt;&lt;br&gt;
You are reading a truncated version of this technical guide. &lt;br&gt;
To read the full, unedited deep-dive (including all configuration files, architecture diagrams, and high-res images), &lt;strong&gt;&lt;a href="https://www.valtersit.com/guides/networking/firewall_101-the_drop_everything_by_default_manifesto/" rel="noopener noreferrer"&gt;visit the original post on Valters IT Docs&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>networking</category>
      <category>firewall</category>
      <category>security</category>
      <category>sysadmin</category>
    </item>
  </channel>
</rss>
