// ide-extension-data.mjs — Loads top-extensions + blocklist from knowledge files. // Zero dependencies (Node.js builtins only). // Used by ide-extension-scanner.mjs for typosquat + blocklist checks. import { readFile } from 'node:fs/promises'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const KNOWLEDGE_DIR = join(__dirname, '..', '..', 'knowledge'); let _vscode = null; let _jetbrains = null; async function loadJson(path) { try { const raw = await readFile(path, 'utf8'); return JSON.parse(raw); } catch { return null; } } /** * Load top VS Code extension IDs. * @returns {Promise} Lowercased "publisher.name" strings. */ export async function loadTopVSCode() { if (_vscode !== null) return _vscode.vscode || []; _vscode = await loadJson(join(KNOWLEDGE_DIR, 'top-vscode-extensions.json')) || { vscode: [], blocklist: [] }; return (_vscode.vscode || []).map(normalizeId); } /** * Load VS Code extension blocklist entries. * @returns {Promise} Entries of form "publisher.name@version" or "publisher.name@*". */ export async function loadVSCodeBlocklist() { if (_vscode !== null) return _vscode.blocklist || []; _vscode = await loadJson(join(KNOWLEDGE_DIR, 'top-vscode-extensions.json')) || { vscode: [], blocklist: [] }; return _vscode.blocklist || []; } /** * Load top JetBrains plugin xmlIds (canonical corpus for typosquat detection). * @returns {Promise} Lowercased xmlIds. */ export async function loadTopJetBrains() { if (_jetbrains !== null) return (_jetbrains.jetbrains || []).map(normalizeId); _jetbrains = await loadJson(join(KNOWLEDGE_DIR, 'top-jetbrains-plugins.json')) || { jetbrains: [], blocklist: [] }; return (_jetbrains.jetbrains || []).map(normalizeId); } /** * Load JetBrains plugin blocklist entries. * Empty by design — no public confirmed-malicious JetBrains Marketplace plugins * as of 2026-04-17. Enterprise policy.json can seed private entries. * @returns {Promise} Entries of form "xmlId@version" or "xmlId@*". */ export async function loadJetBrainsBlocklist() { if (_jetbrains !== null) return _jetbrains.blocklist || []; _jetbrains = await loadJson(join(KNOWLEDGE_DIR, 'top-jetbrains-plugins.json')) || { jetbrains: [], blocklist: [] }; return _jetbrains.blocklist || []; } /** * Normalize extension ID for comparison. * @param {string} id * @returns {string} */ export function normalizeId(id) { return String(id || '').toLowerCase().trim(); } /** * Reset cache (for tests). */ export function _resetCache() { _vscode = null; _jetbrains = null; }