Files
cve/tests/test_search.js
T

208 lines
9.6 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const vm = require('node:vm');
const { MessageChannel } = require('node:worker_threads');
const script = fs.readFileSync(path.join(__dirname, '../docs/logic.js'), 'utf8');
function startPage(t, { search = '', trending, kev = {}, indexError = false }) {
const elements = new Map();
const calls = [];
const document = {
addEventListener() {},
querySelectorAll: () => [],
querySelector(selector) {
if (!elements.has(selector)) elements.set(selector, {
value: '',
innerHTML: selector === '[data-trend-rows]' ? '<a href="/CVE-2026-1234">Prerendered PoC</a>' : '',
addEventListener() {}, setAttribute() {}, querySelectorAll: () => []
});
return elements.get(selector);
}
};
const context = vm.createContext({
console: { warn() {} }, document, performance, MessageChannel, URLSearchParams, URL,
location: { pathname: '/', search },
window: { matchMedia: () => ({ matches: false }) },
setTimeout() {}, clearTimeout() {},
async fetch(url) {
calls.push(url);
if (url === '/CVE_list.json' && indexError) throw new Error('Index unavailable');
const data = url === '/trending_poc.json' ? await trending
: url === '/kev.json' ? await kev
: url === '/CVE_list.json' ? [{ cve: 'CVE-2026-1234', desc: 'widget', poc: ['https://github.com/owner/poc'] }]
: {};
return { ok: true, json: async () => data };
}
});
vm.runInContext(script, context);
t.after(() => vm.runInContext('yieldPort.port1.close(); yieldPort.port2.close();', context));
return { elements, calls, context };
}
test('repository security advisories have GHSA filtering without repository stars or dates', async t => {
const { context } = startPage(t, { trending: new Promise(() => {}) });
await new Promise(setImmediate);
const url = 'https://github.com/vendor/project/security/advisories/GHSA-1234';
vm.runInContext('repoMeta = { "vendor/project": [5000, "2026-10-06"] };', context);
const row = vm.runInContext(`pocRow(${JSON.stringify(url)})`, context);
assert.match(row, /GHSA-1234/);
assert.doesNotMatch(row, /★|5\.0k/);
assert.equal(vm.runInContext(`entrySources({poc: [${JSON.stringify(url)}]}).has('GHSA')`, context), true);
assert.equal(vm.runInContext(`repoFromUrl(${JSON.stringify(url)})`, context), null);
});
test('curated artifact paths survive search rendering without duplicate repository roots', async t => {
const { context } = startPage(t, { trending: new Promise(() => {}) });
await new Promise(setImmediate);
const base = 'https://github.com/google/security-research';
const variants = ['lts', 'mitigation'].map(kind => `${base}/tree/master/pocs/linux/kernelctf/CVE-2024-26642_${kind}`);
const template = 'https://github.com/projectdiscovery/nuclei-templates/blob/main/http/Test.yaml?version=One#Request';
const entry = { cve: 'CVE-2024-26642', poc: [base, 'https://github.com/example/poc', 'https://github.com/EXAMPLE/POC/blob/main/exploit.py'],
collections: [...variants, variants[0].replace('google/security-research', 'GOOGLE/Security-Research')],
nuclei: [template, template.replace('Test.yaml', 'test.yaml')] };
context.variantEntry = entry;
vm.runInContext(`metadata = { 'CVE-2024-26642': { advisories: [[${JSON.stringify(base)}, ['Exploit']]] } };`, context);
for (const fn of ['entryLinks', 'rankedLinks']) {
const links = Array.from(vm.runInContext(`${fn}(variantEntry)`, context));
assert.equal(links.length, 5);
for (const url of [...variants, template, template.replace('Test.yaml', 'test.yaml')]) assert.ok(links.includes(url), url);
assert.ok(!links.includes(base));
}
const html = vm.runInContext('resultRow(variantEntry)', context);
for (const url of variants) assert.ok(html.includes(`href="${url}"`));
assert.doesNotMatch(html, /CREDIBLE ADVISORIES/);
assert.equal(vm.runInContext(`normalizedLink(${JSON.stringify(variants[0])}) === normalizedLink(${JSON.stringify(base)})`, context), true);
});
test('a stalled trending feed does not block a bookmarked search', async t => {
const { elements, calls } = startPage(t, { search: '?q=widget', trending: new Promise(() => {}) });
await new Promise(setImmediate);
assert.ok(calls.includes('/CVE_list.json'));
assert.equal(elements.get('[data-search]').value, 'widget');
assert.match(elements.get('[data-results]').innerHTML, /href="\/CVE-2026-1234"/);
});
test('a bookmarked typo is corrected when indexing finishes without changing filters', async t => {
const { elements, context } = startPage(t, {
search: '?q=wdiget&src=github&kev=1&sort=newest',
trending: new Promise(() => {}),
kev: { 'CVE-2026-1234': ['2026-10-06', false] }
});
await new Promise(setImmediate);
assert.equal(vm.runInContext('wordIndex.ready', context), true);
assert.match(elements.get('[data-results]').innerHTML, /href="\/CVE-2026-1234"/);
assert.match(elements.get('[data-status]').textContent, /widget for wdiget/);
assert.equal(elements.get('[data-search]').value, 'wdiget');
assert.equal(vm.runInContext('state.query', context), 'wdiget');
assert.equal(vm.runInContext('state.sort', context), 'NEWEST');
assert.equal(vm.runInContext('state.kevOnly', context), true);
assert.equal(vm.runInContext("[...state.filters.source].join(',')", context), 'GITHUB');
});
test('a failed trending feed preserves the published homepage rows', async t => {
const { elements } = startPage(t, { trending: Promise.reject(new Error('Feed unavailable')) });
await new Promise(setImmediate);
assert.match(elements.get('[data-trend-rows]').innerHTML, /Prerendered PoC/);
});
test('an empty successful feed replaces stale homepage rows', async t => {
const { elements } = startPage(t, { trending: { items: [], total_cves: 1, with_pocs: 1, generated: '2026-10-06' } });
await new Promise(setImmediate);
assert.match(elements.get('[data-trend-rows]').innerHTML, /No recent PoCs/);
});
test('an index failure stays visible when the optional trending feed arrives later', async t => {
let finishTrending;
const trending = new Promise(resolve => { finishTrending = resolve; });
const { elements } = startPage(t, { search: '?q=widget', trending, indexError: true });
await new Promise(setImmediate);
assert.match(elements.get('[data-results]').innerHTML, /could not be loaded/);
assert.equal(elements.get('[data-results]').hidden, false);
finishTrending({ items: [], total_cves: 1, with_pocs: 1, generated: '2026-10-06' });
await new Promise(setImmediate);
assert.match(elements.get('[data-results]').innerHTML, /could not be loaded/);
assert.equal(elements.get('[data-results]').hidden, false);
assert.equal(elements.get('[data-status]').textContent, 'index unavailable');
});
test('late KEV data cannot hide a bookmarked search failure', async t => {
let finishKev;
const kev = new Promise(resolve => { finishKev = resolve; });
const { elements } = startPage(t, {
search: '?q=widget', trending: new Promise(() => {}), kev, indexError: true
});
await new Promise(setImmediate);
finishKev({ 'CVE-2026-1234': ['2026-10-06', false] });
await new Promise(setImmediate);
assert.match(elements.get('[data-results]').innerHTML, /could not be loaded/);
assert.equal(elements.get('[data-results]').hidden, false);
assert.equal(elements.get('[data-trending]').hidden, true);
});
for (const sort of ['relevance', 'newest']) {
test(`late EPSS data refreshes results while respecting ${sort} sort`, async t => {
const elements = new Map();
const document = {
addEventListener() {},
querySelectorAll: () => [],
querySelector(selector) {
if (!elements.has(selector)) {
elements.set(selector, {
value: '',
innerHTML: '',
addEventListener() {},
setAttribute() {},
querySelectorAll: () => []
});
}
return elements.get(selector);
}
};
let finishEpss;
const pendingEpss = new Promise(resolve => { finishEpss = resolve; });
const records = ['CVE-2025-1234', 'CVE-2024-1234'].map(cve => ({
cve,
desc: 'widget remote execution',
poc: [`https://github.com/owner/${cve}`]
}));
const context = vm.createContext({
console,
document,
performance,
MessageChannel,
URLSearchParams,
location: { pathname: '/', search: `?q=widget&sort=${sort}` },
window: { matchMedia: () => ({ matches: false }) },
setTimeout() {},
clearTimeout() {},
async fetch(url) {
const data = url === '/epss.json' ? await pendingEpss
: url === '/CVE_list.json' ? records
: url === '/trending_poc.json'
? { items: [], generated: '2026-10-06', total_cves: 2, with_pocs: 2 }
: {};
return { ok: true, json: async () => data };
}
});
vm.runInContext(script, context);
t.after(() => vm.runInContext('yieldPort.port1.close(); yieldPort.port2.close();', context));
const resultIds = () => [...elements.get('[data-results]').innerHTML
.matchAll(/class="result-id" href="\/([^"]+)"/g)].map(match => match[1]);
await new Promise(setImmediate);
assert.deepEqual(resultIds(), ['CVE-2025-1234', 'CVE-2024-1234']);
finishEpss({ 'CVE-2025-1234': [0.01, 0.1], 'CVE-2024-1234': [0.99, 0.999] });
await new Promise(setImmediate);
assert.deepEqual(resultIds(), sort === 'relevance'
? ['CVE-2024-1234', 'CVE-2025-1234']
: ['CVE-2025-1234', 'CVE-2024-1234']);
assert.match(elements.get('[data-results]').innerHTML, /EPSS 99%/);
});
}