Commit Graph
36 Commits
Author SHA1 Message Date
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 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 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 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
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 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
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
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
0xMarcio 81017c9871 Expand CVE PoC coverage and metadata 2026-09-01 19:01:07 +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
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
0xMarcio 8c739f44e5 Reskin to GitHub Primer dark
Every colour is now a Primer dark_default token. The header band, its four
capability pills and the heading above the trending table are gone, so the
search field is the first thing on the page and the switcher labels its own
table. Rows tighten to 10px, descriptions clamp to two lines, and the PoC
list loses its box for a hairline rule.

Blue is the only thing you can click. Amber means 500 stars or more and red
means known-exploited, and nothing else carries a hue: the star glyph is
dimmed at every value so the column reads as numbers rather than symbols.

The handoff's placeholder wanted an em dash. It reads "Search CVE, vendor,
product or keyword, exclude with -windows" with the middot the count line
already uses, and the six em dashes I had left in code comments are gone
too.
2026-08-31 04:00:54 +02:00
0xMarcio 066ddf4400 Rebuild the frontend to the refit design
Space Grotesk for prose, IBM Plex Mono for every identifier, and the flat
hairline-bordered surfaces from the handoff. Green marks what you can act
on and amber only ever means a star count; nothing else is coloured.

Three changes to how the same data reads. Descriptions clamp to three lines
behind an expander, so a kernel CVE no longer pushes the links people came
for below the fold. Raw URLs become owner / repo rows carrying stars and
last-commit age, ranked best first, five at a time. And an empty search box
now keeps the trending PoCs on screen with a TRENDING / RECENT toggle
rather than showing an empty page.

The search grammar, ranking and negation are unchanged.
2026-08-30 21:53:31 +02:00
0xMarcio db97e50d1e Remove redundant search description from hero section 2025-12-18 09:54:37 +01:00
0xMarcio 4ccfcddef0 Update hero section titles and enhance styling for improved visibility 2025-12-18 07:36:05 +01:00
0xMarcio 5c78e585a1 Simplify site to search + trending 2025-12-18 07:13:08 +01:00
0xMarcio 5cb3a28aeb Restore README trending tables and auto-build site 2025-12-17 21:39:16 +01:00
0xMarcio 0afef5597e Restore multi-year trending PoCs with 4-day filter 2025-12-17 21:14:24 +01:00
0xMarcio 722d7261ba Filter trending PoCs to current-year updates 2025-12-17 21:06:33 +01:00
0xMarcio 23be2e0751 Fix trending PoCs rendering and simplify frontend 2025-12-17 20:54:34 +01:00
0xMarcio 0bd6c7ceda Align trending PoCs and filter to recent data 2025-12-17 20:24:46 +01:00
0xMarcio da14307c37 Filter to PoC-backed CVEs and fix trending PoCs 2025-12-17 19:39:59 +01:00
0xMarcio 64e895579d Highlight PoC-heavy CVEs and filter KEV lists 2025-12-17 19:12:21 +01:00
0xMarcio 0f18fc5736 Refresh KEV and EPSS pages for cleaner signal 2025-12-17 18:24:55 +01:00
0xMarcio 1ae3d7e711 Fix search layout and add dynamic CVE pages 2025-12-17 17:38:30 +01:00
0xMarcio 224d673b96 Redesign CVE search UI and fix data path 2025-12-17 17:09:25 +01:00
0xMarcio 62777e27b6 Polish EPSS wording, no GPT-y labels 2025-12-17 16:21:43 +01:00
0xMarcio 213673f0db Fix PoC search data path and clean naming 2025-12-17 16:19:35 +01:00
0xMarcio 6a8a659c51 Fix PoC search and unify site theme 2025-12-17 16:08:35 +01:00
0xMarcio b1085c10f5 Add legacy PoC search page and nav link 2025-12-17 15:04:14 +01:00
0xMarcio aac323de9b Add KEV/EPSS static site generator 2025-12-17 13:59:41 +01:00
0xMarcio 2da222ca15 Updated CVEs JSON 2025-09-29 21:33:15 +02:00
0xMarcio 1f3d35410a Update Sun May 26 23:17:22 CEST 2024 2024-05-26 23:17:22 +02:00
0xMarcio e43b5e8e12 Update Sun May 26 14:06:40 CEST 2024 2024-05-26 14:06:40 +02:00
0xMarcio 27447d5da4 Update Sun May 26 01:24:15 CEST 2024 2024-05-26 01:24:15 +02:00