diff --git a/js/terminal-ext.js b/js/terminal-ext.js index 3ecac058..136b9b97 100644 --- a/js/terminal-ext.js +++ b/js/terminal-ext.js @@ -415,7 +415,13 @@ const extend = (term) => { // directly rather than executeCommandLine for exactly this reason. term.runDeepLink = ({ replay = false } = {}) => { if (term.deepLink != "") { - term.executeCommandLine(term.deepLink, { + const parsed = term.parseCommandLine(term.deepLink); + + if (parsed.cmd === "eval" && window.confirm(parsed.line) !== true) { + return; + } + + term.executeCommandLine(parsed.line, { addToHistory: false, promptAfter: false, showLeadingNewline: false, diff --git a/tests/terminal-ext.test.js b/tests/terminal-ext.test.js index 3162b37f..ffe4d263 100644 --- a/tests/terminal-ext.test.js +++ b/tests/terminal-ext.test.js @@ -145,6 +145,44 @@ describe("terminal-ext", () => { ); }); + it("requires exact confirmation before dispatching an eval deep link", () => { + const confirm = vi + .fn() + .mockReturnValueOnce(false) + .mockReturnValueOnce(undefined) + .mockReturnValueOnce(true) + .mockReturnValueOnce(true); + const { extend } = loadTerminalExt({ confirm }); + env.window.location.hash = "#eval-alert(1)"; + const term = createTerm(); + + extend(term); + term.executeCommandLine = vi.fn(() => Promise.resolve()); + + term.runDeepLink(); + term.runDeepLink({ replay: true }); + term.runDeepLink(); + term.runDeepLink({ replay: true }); + + expect(confirm).toHaveBeenNthCalledWith(1, "eval alert(1)"); + expect(confirm).toHaveBeenNthCalledWith(2, "eval alert(1)"); + expect(confirm).toHaveBeenNthCalledWith(3, "eval alert(1)"); + expect(confirm).toHaveBeenNthCalledWith(4, "eval alert(1)"); + expect(term.executeCommandLine).toHaveBeenCalledTimes(2); + expect(term.executeCommandLine).toHaveBeenNthCalledWith(1, "eval alert(1)", { + addToHistory: false, + promptAfter: false, + showLeadingNewline: false, + trackAnalytics: true, + }); + expect(term.executeCommandLine).toHaveBeenNthCalledWith(2, "eval alert(1)", { + addToHistory: false, + promptAfter: false, + showLeadingNewline: false, + trackAnalytics: false, + }); + }); + it.each([ ["#jobs", "jobs"], ["#whois-root", "whois root"],