Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Readme

CISA KEV To RSS

This val.town HTTP endpoint reads the JSON from CISA's Known Exploited Vulnerabilities Catalog and converts it to an RSS feed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
const CISA_JSON_URL = "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json";
const RSS_FEED_URL = "https://hrbrmstr-cisakevtorss.web.val.run"; // Update this to your actual RSS feed URL
function escapeXML(str: string): string {
return str.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&apos;");
}
function removeInvalidXMLChars(str: string): string {
return str
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g, "") // Control characters
.replace(/[\uD800-\uDFFF]/g, "") // Surrogate pairs
.replace(/[\uFFFE\uFFFF]/g, "") // Non-characters
.replace(/\uFFFD/g, ""); // Remove Unicode Replacement Character
}
function generateRSS(data: any): string {
const { title, catalogVersion, dateReleased, vulnerabilities } = data;
const sortedVulnerabilities = vulnerabilities.sort((a: any, b: any) => {
const dateA = new Date(a.dateAdded);
const dateB = new Date(b.dateAdded);
return dateB.getTime() - dateA.getTime();
});
let rss = `<?xml version="1.0" encoding="UTF-8" ?>\n`;
rss += `<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">\n`;
rss += ` <channel>\n`;
rss += ` <title>${escapeXML(title)}</title>\n`;
rss += ` <description>RSS feed for ${escapeXML(title)}</description>\n`;
rss += ` <link>https://www.cisa.gov</link>\n`; // Adjust the link as necessary
rss += ` <atom:link href="${RSS_FEED_URL}" rel="self" type="application/rss+xml" />\n`;
rss += ` <lastBuildDate>${new Date(dateReleased).toUTCString()}</lastBuildDate>\n`;
rss += ` <pubDate>${new Date(dateReleased).toUTCString()}</pubDate>\n`;
rss += ` <generator>Deno RSS Generator</generator>\n`;
for (const vuln of sortedVulnerabilities) {
const link = `https://nvd.nist.gov/vuln/detail/${escapeXML(vuln.cveID)}`;
const guid = vuln.cveID;
let description = vuln.shortDescription || "";
description = removeInvalidXMLChars(description);
description = escapeXML(description);
description = description.replace(/\?>/g, "&#63;&gt;");
description = description.replace(/<\?/g, "&amp;lt;&#63;");
description = description.replace(/\?/g, "&#63;");
description = `<![CDATA[${description}]]>`;
rss += ` <item>\n`;
rss += ` <title>${escapeXML(vuln.vulnerabilityName)} (${escapeXML(vuln.cveID)})</title>\n`;
rss += ` <description>${description}</description>\n`;
rss += ` <link>${escapeXML(link)}</link>\n`;
rss += ` <guid isPermaLink="false">${escapeXML(guid)}</guid>\n`;
rss += ` <pubDate>${new Date(vuln.dateAdded).toUTCString()}</pubDate>\n`;
rss += ` <category>${escapeXML(vuln.vendorProject)}</category>\n`;
rss += ` </item>\n`;
}
rss += ` </channel>\n`;
rss += `</rss>`;
return rss;
}
export async function handler(req: Request): Promise<Response> {
try {
const response = await fetch(CISA_JSON_URL);
if (!response.ok) {
throw new Error(`Failed to fetch data: ${response.status} ${response.statusText}`);
}
const data = await response.json();
const rssFeed = generateRSS(data);
return new Response(rssFeed, {
headers: {
"Content-Type": "application/rss+xml; charset=UTF-8",
"Access-Control-Allow-Origin": "*", // Be generous with CORS
"Cache-Control": "public, max-age=3600", // Cache for 1 hour (change this if you think you need faster resolution)
},
});
} catch (error) {
console.error("Error generating RSS feed:", error);
return new Response("Internal Server Error", { status: 500 });
}
}
hrbrmstr-cisakevtorss.web.val.run
September 15, 2024