Commit Graph
150 Commits
Author SHA1 Message Date
CVE-BOT 5494c367e8 Trending CVEs update 2026-09-04 19:09 🤖 2026-09-04 19:09:37 +00:00
CVE-BOT 4c3ac69169 Trending CVEs update 2026-09-04 17:09 🤖 2026-09-04 17:09:28 +00:00
CVE-BOT 90a6742893 Trending CVEs update 2026-09-04 14:10 🤖 2026-09-04 14:10:04 +00:00
CVE-BOT bba0a1ac34 Trending CVEs update 2026-09-04 09:09 🤖 2026-09-04 09:09:35 +00:00
CVE-BOT 0f31ce9052 Drop PoC links to deleted repositories 2026-09-04 2026-09-04 08:29:00 +00:00
CVE-BOT b05a5acb33 Sync CVE metadata 2026-09-04 2026-09-04 07:56:23 +00:00
CVE-BOT ee2b6eb533 Update CVE PoCs 2026-09-04 2026-09-04 07:21:28 +00:00
CVE-BOT 6991dd330b Trending CVEs update 2026-09-04 07:09 🤖 2026-09-04 07:09:12 +00:00
CVE-BOT d3f7816a8b Trending CVEs update 2026-09-04 03:09 🤖 2026-09-04 03:09:05 +00:00
CVE-BOT 7bfde05f77 Trending CVEs update 2026-09-04 01:49 🤖 2026-09-04 01:49:53 +00:00
0xMarcio aae0c789a3 Meet the query where it is written: slang, names, typos
Security slang and NVD prose rarely share a word. "sqli" found 843 CVEs
where "sql injection" found 12,719, "lpe" 80 against 1,311 for "privilege
escalation". A term now expands to its phrases and an entry matches on any
of them, with the best-scoring form counted.

A vulnerability searched by the name it is known by leads with the record
it belongs to, whatever the prose and the repository names say: ghostcat
put a different record first, moveit a newer one. Forty names are mapped.

A term with no word in the index at all is nearly always a typo. The
closest word one edit away stands in, weighed by how many records use it,
and the status line says which: citrx finds citrix, fortinte fortinet,
l0g4j log4j.

CVE pages carry BreadcrumbList markup for the home, year and block trail,
and the site publishes an OpenSearch descriptor so a browser can add it
as a search engine from the address bar.
2026-09-04 02:59:24 +02:00
0xMarcio 08d697bd12 Carry each record's title, vendor and product, and search them first
A CVE List V5 record names what it is about: a title such as "PAN-OS:
Arbitrary File Creation Leads to OS Command Injection Vulnerability in
GlobalProtect", a vendor and its products. None of it was kept, so a query
for pan-os or prisma access only found records whose prose happened to say
so. Every record now opens with the title in bold and a Vendor badge per
affected vendor, refreshed from a local CVE List checkout in one pass and
kept current by the daily sync for the records it fetches. Only the lines
above the description move; descriptions and PoC sections are untouched.
The pass also settled the heading link on cve.org and trimmed the stray
space some version badges carried.

The site index carries title, vendor and product for the 40 percent of
indexed CVEs whose records have them. Search scores a hit in those above
any position in the description, results show the title as a headline,
and each page puts it under the CVE id, in the browser title, the meta
description and an Affected row, with the block hubs listing it in place
of the description's opening.

The 35 MB metadata file the homepage needed before anything could be
filtered is split in two for the page: the CVSS rows load with the rest,
the advisories follow the index instead of competing with it for the
connection. cve_metadata.json stays whole for anyone reading the API.
2026-09-04 02:32:22 +02:00
0xMarcio 7e20cbfa32 Give every CVE page a way in: year and block hubs
A sitemap gets a page discovered; a link from a page already indexed gets
it crawled and ranked. 62,000 of the CVE pages had no internal link at
all, only a sitemap entry, since the Related block only exists where two
CVEs share a PoC author. The tree is now home, year, block, CVE: one page
per year listing its thousand-id blocks, one page per block listing every
CVE in it with its PoC count and known-exploited mark, crumbs above every
CVE title pointing back up, and a year row on the homepage. No page
carries more than a thousand links. The hubs get their own sitemap shard,
dated from the newest page they contain.
2026-09-04 02:20:00 +02:00
0xMarcio dfa41dfbe1 Index the words once and rank ties by evidence
Every keystroke scanned 28 MB of description text, and for terms of four
characters or more the loose regex then ran against every entry that did
not match, about 80,000 regex tests a keystroke: 100 to 230 ms on a
desktop, several times that on a phone. The index now maps each word to
the entries carrying it, built in slices after the data lands so typing
never waits on it, and a term is a prefix lookup plus a scan of the
candidates alone: 7 to 40 ms measured on the same queries. The loose form
of a term, "log-4j" for log4j, is only found by the full pass, which now
runs when the indexed pass comes back nearly empty and not otherwise. The
severity and source of an entry are cached as well, so a filter pass over
the whole index takes 20 ms instead of half a second.

Within a score tier the order fell straight through to the year, which put
five 2026 records above regreSSHion for "openssh" and kept Log4Shell off
the first page of "log4j". Ties now go to the evidence first: CISA's
listing, how many PoCs exist, how likely exploitation is, and only then
the year. A switch on the results panel offers newest and most PoCs
instead, and rides in the URL like the query and the filters. The title,
vendor and product of a record score above its prose once the records
carry them.
2026-09-04 02:16:18 +02:00
0xMarcio b09f83f840 Put every filter on the page and give CVE pages the homepage search
The two filter menus hid their state behind ALL and took two clicks per
value. Each severity and source is now its own chip in the row, always
visible: the first click narrows to that value, further clicks widen the
set, and switching the last lit chip off clears. Active chips use the
colours the result rows already use for the same words, and the
selection rides in the URL as sev= and src=, so a narrowed view can be
linked like a query.

Standalone CVE pages carried a smaller search box beside the wordmark
that read as another site's. They now open with the homepage bar itself,
same height, type and placeholder, under the wordmark with the index
count on the right. Enter still submits to /?q= because the pages are
static, and the form has a real submit button for screen readers.
2026-09-04 01:49:32 +02:00
0xMarcio 46b5acf052 Tighten the writers: timeouts, a shorter audit, fewer bytes in git
Every writer now has a timeout, so a hung download cannot hold the
repository-updates lock for six hours and cancel the runs queued behind
it. The link audit moves from one 12,000-and-12,000 batch a week, which
measured an hour under that lock, to a seventh of it every day.

The social card PNG is drawn at deploy time and no longer committed: it
changed about four times a day at 68 KB a copy. nuclei.json stops
mirroring EPSS scores; every indexed CVE already has one in epss.json,
the site never fell back to the copy, and the daily recomputation
rewrote 3,400 lines of it each night.

The metadata sync retries GitHub advisory calls on 403, 429 and 5xx
instead of failing on the first one, and refuses to publish a year
shard that an NVD feed would shrink by a tenth or delete outright.
2026-09-04 00:36:16 +02:00
CVE-BOT fc1595d0af Trending CVEs update 2026-09-03 18:34 🤖 2026-09-03 18:34:03 +00:00
CVE-BOT 32b6d4c714 Trending CVEs update 2026-09-03 09:40 🤖 2026-09-03 09:40:06 +00:00
CVE-BOT 14388d3ffa Sync exploit archives 2026-09-03 🤖 2026-09-03 09:16:13 +00:00
CVE-BOT 968d7e94f0 Sync CVE metadata 2026-09-03 2026-09-03 08:00:09 +00:00
CVE-BOT 7ace47c9fc Update CVE PoCs 2026-09-03 2026-09-03 07:19:02 +00:00
0xMarcio 0e5e8d7e8c Fix correctness, security and consistency bugs across the generated site
The per-CVE pages read as a different site and disagreed with search about
their own contents. Both traced to the page generator having grown its own
copy of rules the browser already had.

Security:
- JSON-LD was serialised with json.dumps alone, which leaves < > and & raw. A
  description containing "</script>" ended the element and turned the rest into
  live markup. 78 descriptions in the corpus already carry that sequence; only
  the 155-character meta trim kept them out of the payload. Escape to \u00XX.

Correctness:
- The PoC count summed only the repository column, so 19,940 pages announced
  "0 public PoC exploits" directly above the ExploitDB and Metasploit entries
  they were listing, in the title and meta description too. Count every source
  the page renders, matching POC_FIELDS in logic.js.
- The severity chip took the highest score while search took the first, so
  18,434 CVEs showed one severity in the results and another on the page. Both
  now take the primary assessment, with the same Nuclei fallback search uses.
- Identical CVSS rows printed two and three times on 13,340 pages. Dedupe on
  (version, score, vector) and label each survivor with its assessor from
  row[4]; row[5] is the assessment type and left 129 pages showing two rows
  both marked "Secondary".
- repo_from_url accepted any host containing "github" and kept .git suffixes,
  so a clone URL and its plain form counted as two sources on the page and one
  in search. 273 CVEs disagreed. Match repoFromUrl in logic.js exactly.
- 41 CVEs nobody has scored were unreachable by search, including by exact ID:
  the severity filter tested membership of a list naming only the four scored
  bands, so ALL excluded them. ALL now means all.
- EPSS percentiles printed a hard-coded "th": 51th, 62th, 93th on 21,100 pages.
- Search reported "1 PoCs", and the empty-results branch returned before
  updating the live region, leaving it reading "loading index..." forever.

Deploy safety:
- A failed EPSS fetch returned {} and published it, silently erasing every
  score from 82,000 pages. It now raises, as does a feed covering under 90%
  of the index.

Presentation:
- CVE pages never loaded the webfonts the homepage loads, so all 82,468 fell
  back to system fonts. This is why they looked unlike the rest of the site.
- 404.html referenced four CSS classes that no longer existed and rendered
  unstyled, and its meta refresh threw a reader out of a search after two
  seconds. It is now generated from the same constants and header as every
  other page, and offers the search box instead.
- The homepage and the generated footer credited different sources. Both now
  come from brand.SOURCES_LINE, and the build refuses to publish a homepage
  that has drifted from it.
- Three aria-labels sat on div elements with no role, where they are ignored.

SEO:
- 61,936 pages filled "Related" with numerically adjacent CVEs, which are not
  related to anything. Keep only shared-author relations; 20,532 pages have one.
- Sitemap lastmod described the CVE record, so a CVE that gained forty PoCs
  still advertised a date from 2021. It now reflects the page, and each shard
  carries its newest page's date rather than today's.
- TechArticle is not a type Google draws a rich result from, and its dates
  claimed the CVE record's publication as the page's. Use WebPage with the
  vulnerability as mainEntity.
- Curated sources and the CVE and NVD records no longer carry nofollow; the
  crowd-sourced repository list still does.
2026-09-02 17:17:28 +02:00
CVE-BOT d65df632cf Trending CVEs update 2026-09-02 09:30 🤖 2026-09-02 09:30:44 +00:00
CVE-BOT 0f46f3180a Sync nuclei templates 2026-09-02 🤖 2026-09-02 08:52:55 +00:00
CVE-BOT a124d64067 Update CVE PoCs 2026-09-02 2026-09-02 07:14:07 +00:00
0xMarcio 8e0a85a050 Cut the trailing feature lists out of every description
The About box, the meta description, the og and twitter tags and the JSON-LD all
ended in a tacked-on list of features: "Updated hourly", "Free JSON API, no
signup", "Plain JSON, no key". None of it describes what the thing is, and the
meta description ran past where a result gets truncated anyway.

Four strings collapse to two so they cannot drift apart again. SUBTITLE is the
line under the wordmark and the social description; DESCRIPTION is the meta
description, the JSON-LD description and the GitHub About box. Both say what the
site is and stop.
2026-09-02 04:28:23 +02:00
0xMarcio 9b9957ea7f Give every CVE its own page at the site root
82,000 CVEs sat behind one URL. Each of them is a query somebody types, and the
pages answering those queries belonged to other people: searching
"CVE-2026-72898 poc" returns SentinelOne, Horizon3, Wiz and BishopFox, all of
them serving one page per CVE.

Pages are flat files at /CVE-2026-72898. No /cve/ prefix, no extension, no
trailing slash. GitHub Pages resolves an extensionless request to the .html
beside it, checked against the live site rather than assumed.

Each page carries what the index already knows and nothing invented: every CVSS
assessment with its vector, EPSS with percentile, KEV date and ransomware flag,
Nuclei severity and CWE, and the PoC links grouped by source with stars and last
push. 94% of them carry three or more of those signals.

Discovery works two ways. sitemap.xml is now an index over one shard per year,
which fits the 50,000 URL cap on the split the data already has and lets a
crawler refetch only the year that moved. Search results link to the CVE page
instead of straight out to NVD, which moves to a chip beside CVE.ORG, and each
page links six related CVEs picked by shared PoC author.

Rendering all of them takes 20s and adds nothing to the repository: the payloads
they read are already gitignored and built in CI, so the pages go into the Pages
artifact and git never sees them.
2026-09-02 04:00:28 +02:00
0xMarcio 3e9e0a40e0 Follow the repository rename and drop two bits of padding
The slug moved to 0xMarcio/pocindex, so the raw.githubusercontent URLs in the
README banner were riding a redirect. SLUG now lives in brand.py with the rest
of the identity and getTrending imports it.

The About description lost "with CVSS, KEV and EPSS. Free JSON API, no signup."
GitHub truncates listings near 150 characters, so that tail rarely rendered and
read as a feature list rather than a description. 142 characters now.

The MITRE disclaimer comes out of the footer. The brand carries no MITRE mark,
which was the reason for picking it, and referring to CVE identifiers by name is
ordinary descriptive use. NVD, Tenable and Rapid7 all do it without a notice.

_urltest.html is temporary and comes out in the next commit.
2026-09-02 03:54:25 +02:00
CVE-BOT 3c420d723b Trending CVEs update 2026-09-02 01:41 🤖 2026-09-02 01:41:49 +00:00
0xMarcio 31da22381c Rebrand to PoC Index and move to pocindex.io
The site called itself three different things: CVE PoC Hub in the title, CVE
Radar in the h1 and the social metadata, and cve on GitHub. Google had no
consistent site name to settle on, so none of the mentions compounded. CVE
Radar was taken anyway, by SOCRadar's free tool and by cveradar.com.

Every user-facing name and URL now comes from scripts/brand.py, so the next
move is one edit rather than a sweep through the tree.

Also repairs the parts of the site a crawler could not use:

- robots.txt and sitemap.xml were both 404. scripts/build_seo.py writes them,
  wired into the Pages job. trending_poc.json, kev.json and stats.json stay
  crawlable because the first paint needs them; the five bulk payloads are
  disallowed, which is 13.2 MB per crawl spent on data no crawler queries.
- Search state lives in ?q= now. Results were unlinkable, so nobody could cite
  one and there was no route for a SearchAction to declare. replaceState
  rather than pushState, so typing does not bury the back button.
- A WebSite JSON-LD block carries the name Google prints as the site name.
- The footer carries the MITRE trademark notice.
2026-09-02 03:36:22 +02:00
CVE-BOT e58a5a26cc Trending CVEs update 2026-09-01 23:17 🤖 2026-09-01 23:17:01 +00:00
0xMarcio 8dec6181e8 Refine README search strip 2026-09-02 01:07:28 +02:00
0xMarcio 0748d77eee Add README search call to action 2026-09-02 01:05:49 +02:00
0xMarcio aad1789a71 Keep published counts synchronized 2026-09-02 00:54:09 +02:00
0xMarcio 7a33d182ac Prefer positive CVSS assessments 2026-09-01 23:56:04 +02:00
0xMarcio 95b818930a Remove unscored severity filter 2026-09-01 23:26:48 +02:00
0xMarcio 8fe2bf2d04 Index verified advisory PoCs and expand CVSS sources 2026-09-01 23:17:04 +02:00
0xMarcio dcf4068b7a Deduplicate published sources and improve filters 2026-09-01 22:08:20 +02:00
CVE-BOT ec0fae9788 Trending CVEs update 2026-09-01 17:43 🤖 2026-09-01 17:43:44 +00:00
0xMarcio 81017c9871 Expand CVE PoC coverage and metadata 2026-09-01 19:01:07 +02:00
CVE-BOT 3f4626884b Trending CVEs update 2026-09-01 13:52 🤖 2026-09-01 13:52:06 +00:00
0xMarcio f140e13160 Repair the README pipeline after the move
Moving the data files into index/ left three references behind in .github,
and the hourly job has been failing to stage its own output since. The
README has been advertising 104,016 CVEs with PoCs while the site served
89,125, and every KEV marker had dropped out of its table because the
catalogue was being read from a path that no longer exists.

Figures come from the same count build_site publishes, so the badges, the
hero card and the site cannot disagree again.
2026-09-01 15:50:08 +02:00
CVE-BOT d874588866 Trending CVEs update 2026-09-01 08:34 🤖 2026-09-01 08:34:22 +00:00
CVE-BOT 188f0646c5 Trending CVEs update 2026-09-01 03:42 🤖 2026-09-01 03:42:54 +00:00
0xMarcio 84da5a5fc6 Index ExploitDB and Metasploit, score everything with EPSS, and stop hiding new PoCs
Four gaps, measured against the index before any of this was written.

ExploitDB and Metasploit are folded in the way nuclei templates were. ExploitDB
covers 25,066 CVEs and adds 30,327 links across 17,237 entries; Metasploit
covers 3,178 and adds 3,520 across 2,550. Together with the templates the index
goes from 101,809 CVEs carrying a PoC to 103,945. Neither archive skews recent,
which is why nuclei went first, but both are the difference between an old CVE
having nothing and having something that runs. Each keeps its own section and
its own field, and each leads the list ahead of the repositories: a module, an
entry and a template all run as they stand, without reading somebody's code.

EPSS now covers the index rather than the 4,274 CVEs a template happened to
rate. FIRST's daily feed scores 103,783 of 103,945, so a result can say how
likely a flaw is to be exploited in the next thirty days, not only that code
exists for it. It is fetched during the site build and never tracked here: a
daily rescore of 366,000 rows would be nothing but churn.

The trending tables also stop hiding what they exist to show. The star floor
excluded 1,059 of the 1,312 2026 PoC repositories pushed this quarter, and a
repository published this morning has no stars, so the newest row on the front
page was six days old. A separate lane drops the floor entirely and pays for it
with a ten day window, a cap of ten rows, and the same paperwork check the rest
of the table uses. The newest row is now hours old.
2026-09-01 05:24:39 +02:00
0xMarcio 35cb1e6962 Index nuclei templates as PoCs and take their ratings with them
A nuclei template is a runnable check for one CVE, which makes it a proof of
concept whether or not anyone published a repository for it. Measured against
the index: 3,952 CVEs gain a template link and 858 of those had no PoC at all,
584 of the additions falling in 2024 or later. That last figure is the point.
ExploitDB carries more CVEs overall and Metasploit is better weaponised, but
both skew historical; nuclei is where coverage of a recent CVE actually shows
up first.

Templates also carry the ratings the entries never had. Severity, CVSS with
its vector, EPSS with its percentile and CWE now sit in nuclei.json for the
site to read: 4,193 of 4,418 templates state a CVSS score and 4,274 an EPSS
score. A result can finally say how bad a flaw is and how likely it is to be
used, rather than only that somebody wrote code for it.

Templates are kept in their own section and their own field. They are not
somebody's repository and should not be ranked, starred or credited as one,
but they do lead the PoC list, being the one link that runs as it stands.

The index files are also revalidated rather than reused from cache. They are
rebuilt hourly and a returning visitor was holding whatever it downloaded
first, which cost a stale index here until the copy was thrown away by hand.
2026-09-01 05:11:31 +02:00
0xMarcio fca6754b6f Send stray keystrokes to the search field
Landing on the page and typing did nothing, so the first thing anyone tries
was lost and the field had to be clicked first. It is focused on load on
viewports wide enough to imply a keyboard, and typing anywhere else moves
focus there.

Focus moves during keydown rather than on the character itself, so the
keystroke that triggered it still lands in the field: typing gitea from
anywhere on the page searches for gitea, not itea. A lone slash focuses
without typing itself, and escape clears the field and steps back out.
2026-09-01 04:54:53 +02:00
0xMarcio de770d6c6f Bring the site up to the README and fix the unreadable text
The page and the README had drifted into looking like two projects. The site
now opens on the same card the README banner draws, built as markup rather
than a second copy of the image, so it fills itself from the index totals the
page already loads and cannot go stale.

Contrast was the real problem. --neutral sat at 4.12:1 against the canvas,
under the 4.5 floor, and it carried the search placeholder, the star glyph and
every separator; descriptions were rendered in the muted token at 6.15:1 while
doing the work of body copy. Descriptions move to a subtle token at 12.26:1,
muted and neutral both come up, and links go to the brighter accent at 7.49:1.
The column headers were 9.5px mono under a tenth of an em of tracking, which
no contrast ratio was going to rescue.

Known-exploited CVEs are marked in the trending table too, not only inside a
result, since which row to read first is the one thing the table could not
say. The README swaps the fire emoji for the same KEV pill, served from the
repository so every flagged row points at one cached file.

The search field no longer repeats the totals now that the hero carries them,
and keeps its slot for the match timing.
2026-09-01 04:46:30 +02:00
CVE-BOT cf3ea1e7b7 Trending CVEs update 2026-09-01 02:29 🤖 2026-09-01 02:29:52 +00:00
0xMarcio 2ed7386028 Give the README a banner and cut it back to the useful parts
The README opened on a bare heading and five year tables, said nothing about
what the project is, and never linked the site. Anyone landing on the repo had
to guess. It now leads with a generated banner carrying the live index totals,
a badge row, and tables trimmed to what fits a table cell.

hero.py draws the banner and the social card as self-contained SVG, since
GitHub proxies README images and drops anything the file has to fetch for
itself. It counts the corpus through build_site's own parser rather than a
second guess at what counts as a PoC, so the banner cannot drift from the site
header, and leaves the figures in docs/stats.json for the README build to
quote. If the count fails it falls back to the deployed site and, failing
that, writes nothing at all: a banner reading zero is worse than yesterday's.

Badges describing the index are baked in at build time so they always match
the banner and cannot degrade to "resource not found"; the two that describe
live GitHub state carry a stamp that defeats the image proxy cache.

The sweep now runs hourly instead of every six hours. It takes under two
minutes and the repository shows up in "recently updated" far more often.

Also adds the OpenGraph tags the site never had, so a shared link renders a
card instead of a bare URL.
2026-09-01 04:28:33 +02:00