const assert = require("assert"); const fs = require("fs"); const path = require("path"); const { issueConfirmation, consumeConfirmation, isDestructivePath } = require("../src/services/destructive-confirm"); const root = path.join(__dirname, ".."); const destructivePath = /(?:^|\/)(?:delete|remove|clear|reset|renew|uninstall|cleanup|archive|revoke|unlink|unset|revert)(?:\/|$)/i; function filesBelow(directory, extension, output = []) { for (const entry of fs.readdirSync(directory, { withFileTypes: true })) { const target = path.join(directory, entry.name); if (entry.isDirectory()) filesBelow(target, extension, output); else if (target.endsWith(extension)) output.push(target); } return output; } function maskEjs(source) { return source.replace(/<%[\s\S]*?%>/g, "EJS"); } function attribute(tag, name) { return tag.match(new RegExp(`\\b${name}\\s*=\\s*["']([^"']*)["']`, "i"))?.[1] || ""; } function requireConfirmationCopy(tag, file) { for (const attributeName of ["data-confirm-mode", "data-confirm-title", "data-confirm-text", "data-confirm-label"]) { assert(new RegExp(`\\b${attributeName}(?:\\s*=|\\s|>)`, "i").test(tag), `${path.relative(root, file)} has an unconnected destructive control missing ${attributeName}: ${tag}`); } assert.equal(attribute(tag, "data-confirm-mode"), "modal", `${path.relative(root, file)} must use the shared timed modal`); } function verifyViews() { const files = [ ...filesBelow(path.join(root, "src", "web", "views"), ".ejs"), ...filesBelow(path.join(root, "plugins"), ".ejs") ]; let controls = 0; for (const file of files) { const source = maskEjs(fs.readFileSync(file, "utf8")); assert(!/name\s*=\s*["']action["'][^>]*value\s*=\s*["']delete["']/i.test(source), `${path.relative(root, file)} hides a delete behind a generic POST route; use an explicit /delete endpoint`); for (const tag of source.match(/<(?:form|button)\b[^>]*>/gi) || []) { const action = attribute(tag, "action") || attribute(tag, "formaction"); const confirmAction = attribute(tag, "data-confirm-action"); if (!destructivePath.test(action) && !destructivePath.test(confirmAction)) continue; requireConfirmationCopy(tag, file); controls += 1; } } assert(controls >= 30, `Expected broad destructive-control coverage, found only ${controls}`); return controls; } function verifySharedInfrastructure() { for (const action of [ "/admin/commands/1/delete", "/admin/navigation/reset", "/admin/updates/core/revert", "/plugins/auto-vc/settings/remove" ]) { assert.equal(isDestructivePath(action), true, `Server does not protect ${action}`); } const req = { session: { user: { id: "admin-user", isAdmin: true } }, body: {}, get() { return null; } }; const issued = issueConfirmation(req, "/admin/updates/core/revert"); req.session.destructive_confirmations[issued.token].not_before = Date.now() - 1; assert.equal(consumeConfirmation(req, "/admin/updates/core/revert", issued.token).valid, true); const appScript = fs.readFileSync(path.join(root, "src", "web", "public", "app.js"), "utf8"); assert(appScript.includes("submitter?.dataset?.confirmMode"), "Submit-button confirmation metadata is ignored"); assert(appScript.includes("submitter?.dataset?.confirmLabel || form.dataset.confirmLabel"), "The clicked submit button cannot override generic confirmation copy"); assert(appScript.includes('form.querySelector(\'input[name="confirmation_token"]\')'), "Timed form confirmation tokens are not attached to submitted forms"); assert(appScript.includes("const response = await fetch(action, requestOptions)"), "Async update requests do not submit through the shared confirmed form payload"); assert(!appScript.includes("window.LumiConfirm.destructiveFetch(form.action"), "Timed update forms request a second confirmation instead of reusing the form token"); assert(appScript.includes('state.confirmButton.removeEventListener("click", state.onConfirm)'), "Cancelled or expired confirmations leave stale modal submissions behind"); assert(appScript.includes("state.onConfirm = onConfirm"), "Timed form confirmations do not retain their listener for cleanup"); const toolManagerScript = fs.readFileSync(path.join(root, "plugins", "lumi_ai", "public", "tool-manager.js"), "utf8"); for (const field of ["confirmMode", "confirmTitle", "confirmText", "confirmLabel"]) { assert(toolManagerScript.includes(`form.dataset.${field}`), `Dynamic AI tool deletion is missing ${field}`); } const autoVcView = fs.readFileSync(path.join(root, "plugins", "auto-vc", "views", "auto-vc.ejs"), "utf8"); assert(autoVcView.includes('settingsForm.action = "/plugins/auto-vc/settings/remove"'), "Auto VC lobby deletion does not switch to its protected route"); const improvementView = fs.readFileSync(path.join(root, "plugins", "lumi_ai", "views", "improvement-center.ejs"), "utf8"); assert(improvementView.includes("/reviews/<%= review.id %>/delete")); assert(improvementView.includes("/corrections/<%= entry.id %>/delete")); const improvementScript = fs.readFileSync(path.join(root, "plugins", "lumi_ai", "public", "improvement-center.js"), "utf8"); assert.equal(improvementScript.includes("window.confirm"), false, "Improvement Center still uses the legacy browser confirmation"); } const controls = verifyViews(); verifySharedInfrastructure(); console.log(`Destructive action verification passed (${controls} controls).`);