I recently changed my at home monitoring of the CISA Known Exploited Vulnerabilities feed (More information on KEV here) to make the alerting more accessible. While I monitor some products we use at work this is not a business critical service.

This quick and dirty script uses Mojolicious to hit this CISA KEV API, read a local file to match against target vendors, products or both, logging matches to a local cache so we don't keep reporting on the same thing and sending a notification to my local gotify instance (clients for web, Android etc.).

#!/usr/bin/env perl use strict; use warnings; use feature 'say'; use Mojo::Log; use Mojo::File; use Mojo::JSON qw(decode_json encode_json); use Mojo::UserAgent; # cisa-kev-mon # monitor the CISA KEV feed # * Send notifications to gotify # * Log them locally so we don't keep reporting on the same thing. # For further info on CISA Known Exploited Vulnerablities visit: # https://www.cisa.gov/known-exploited-vulnerabilities # logging my $log = Mojo::Log->new( path => 'cve_mon.log', level => 'debug' ); # read list software we're interested in unless ( -e 'targets.json' ){ $log->fatal( 'no targets.json' ); die 'No targets.json'; } # get targets my @targets = @{ decode_json( Mojo::File->new( 'targets.json' )->slurp + ) }; # gotify notification server config unless ( $ENV{GOTIFY_URL} && $ENV{GOTIFY_TOKEN} ){ $log->fatal( 'Check Gotify env vars, GOTIFY_URL & GOTIFY_TOKEN' ); die 'Fail: Check Gotify env vars, GOTIFY_URL & GOTIFY_TOKEN'; } my $gotify_url = $ENV{GOTIFY_URL}; my $gotify_token = $ENV{GOTIFY_TOKEN}; # cve.org base url my $cve_url = 'https://www.cve.org/CVERecord?id='; # cisa recent json feed my $cisa_url = 'https://www.cisa.gov/sites/default/files/feeds/known +_exploited_vulnerabilities.json'; # local cache so we don't report on already seen CVEs my $cve_cache = Mojo::File->new( 'seen_cves.json' ); # Fetch JSON my $ua = Mojo::UserAgent->new; my $res = $ua->get( $cisa_url )->result; # die if we can't get the JSON feed unless ( $res->is_success ){ $log->error( 'Failed to fetch CISA feed: ' . $res->message ); die 'Failed to fetch CISA feed: ' . $res->message; } my $data = decode_json( $res->body ); my @vulns = @{ $data->{vulnerabilities} }; # Load existing CVEs from local cache my %seen_cves; if ( -e $cve_cache ){ %seen_cves = %{ decode_json( $cve_cache->slurp ) }; } # Filter existing & collect new CVEs my @new_cves; foreach my $vuln ( @vulns ) { my $vendor = $vuln->{vendorProject}; my $product = $vuln->{product}; my $cve_id = $vuln->{cveID}; # skip if the CVE has been logged before next if $seen_cves{$cve_id}; # for each target for my $target( @targets ){ my $matches = 0; # Match both vendor and product if (defined $target->{vendor} && defined $target->{product}) { + $matches = ($vendor =~ /\Q$target->{vendor}\E/i && $produc +t =~ /\Q$target->{product}\E/i); } # match vendor elsif (defined $target->{vendor}) { $matches = ($vendor =~ /\Q$target->{vendor}\E/i); } # match product elsif (defined $target->{product}) { $matches = ($product =~ /\Q$target->{product}\E/i); } if ($matches) { # post to gotify my $res = $ua->post( $gotify_url => { 'X-Gotify-Key' => $gotify_token } => form => { title => 'cisa KEV CVE alert', message => "New CVE: $vendor - $product $cve_url$c +ve_id", priority => 5, } )->result; unless ( $res->is_success ){ $log->fatal( 'Failed to post to gotify: ' . $res->code + . ' - ' . $res->message ); die 'Failed to post to gotify: ' . $res->code . ' - ' +. $res->message; } # add to local cache push @new_cves, $vuln; $seen_cves{$cve_id} = 1; last; } } } # Output if ( @new_cves ) { say 'New vulnerabilities found:'; foreach my $cve ( @new_cves ) { say "[$cve->{cveID}] $cve->{vendorProject} $cve->{product}: $c +ve->{vulnerabilityName} (Added: $cve->{dateAdded})"; } } else { say 'No new vulnerabilities for your monitored vendors/products.'; } # Save updated seen CVEs to local file Mojo::File->new( 'seen_cves.json' )->spew( encode_json( \%seen_cves ) +);

targets.json example:

[ { "vendor": "Microsoft", "product": "SharePoint" }, { "vendor": "Microsoft", "product": "Windows 2012" }, { "vendor": "Microsoft", "product": "Windows 10" }, { "vendor": "Microsoft", "product": "CoPilot" }, { "vendor": "Microsoft", "product": "Teams" }, { "vendor": "Microsoft", "product": "Edge" }, { "vendor": "Oracle", "product": "Solaris" } { "vendor": "Example Vendor"}, { "product": "Example Product"} ]

Example output if you bother to leave that in:

New vulnerabilities found: [CVE-2025-49704] Microsoft SharePoint: Microsoft SharePoint Code Injec +tion Vulnerability (Added: 2025-07-22) [CVE-2025-49706] Microsoft SharePoint: Microsoft SharePoint Improper A +uthentication Vulnerability (Added: 2025-07-22) [CVE-2025-53770] Microsoft SharePoint: Microsoft SharePoint Deserializ +ation of Untrusted Data Vulnerability (Added: 2025-07-20)

Working in large multi vendor organisations, many of whom are outsourced, we don't always hear about things promptly, if at all. Forewarned is forearmed as the adage goes. Screenshot of the Gotify Android app output.


In reply to CISA Known Exploited Vulnerabilities monitoring & notifications by marto

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.