diff options
| author | Ivan Gabaldon <igabaldon@inetol.net> | 2025-10-24 11:28:07 +0200 |
|---|---|---|
| committer | Markus Heiser <markus.heiser@darmarIT.de> | 2025-10-24 12:32:43 +0200 |
| commit | 8dacbbbb157359447a07ea4562ab6447dacb5f06 (patch) | |
| tree | 094ae416687a1e5e6878130fe3c4f7b595c62bbf | |
| parent | b770a46e1f8f339b2bb2a53faba80e9c9a5b1611 (diff) | |
[fix] client/simple: insecure ctx clipboard copy
Uses the deprecated [`execCommand()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand)
to copy content to clipboard if accessing the instance through HTTP, this method
isn't going away soon.
Closes https://github.com/searxng/searxng/issues/5359
| -rw-r--r-- | client/simple/src/js/main/keyboard.ts | 27 | ||||
| -rw-r--r-- | client/simple/src/js/main/preferences.ts | 36 | ||||
| -rw-r--r-- | client/simple/src/js/main/results.ts | 14 | ||||
| -rw-r--r-- | searx/templates/simple/elements/search_url.html | 2 | ||||
| -rw-r--r-- | searx/templates/simple/preferences/cookies.html | 2 |
5 files changed, 59 insertions, 22 deletions
diff --git a/client/simple/src/js/main/keyboard.ts b/client/simple/src/js/main/keyboard.ts index b5e5d4edc..f165a601a 100644 --- a/client/simple/src/js/main/keyboard.ts +++ b/client/simple/src/js/main/keyboard.ts @@ -407,12 +407,31 @@ const toggleHelp = (keyBindings: typeof baseKeyBinding): void => { }; const copyURLToClipboard = async (): Promise<void> => { - const currentUrlElement = document.querySelector<HTMLAnchorElement>(".result[data-vim-selected] h3 a"); - assertElement(currentUrlElement); + const selectedResult = document.querySelector<HTMLElement>(".result[data-vim-selected]"); + if (!selectedResult) return; - const url = currentUrlElement.getAttribute("href"); + const resultAnchor = selectedResult.querySelector<HTMLAnchorElement>("a"); + assertElement(resultAnchor); + + const url = resultAnchor.getAttribute("href"); if (url) { - await navigator.clipboard.writeText(url); + if (window.isSecureContext) { + await navigator.clipboard.writeText(url); + } else { + const selection = window.getSelection(); + if (selection) { + const node = document.createElement("span"); + node.textContent = url; + resultAnchor.appendChild(node); + + const range = document.createRange(); + range.selectNodeContents(node); + selection.removeAllRanges(); + selection.addRange(range); + document.execCommand("copy"); + node.remove(); + } + } } }; diff --git a/client/simple/src/js/main/preferences.ts b/client/simple/src/js/main/preferences.ts index aad01df57..620370ee5 100644 --- a/client/simple/src/js/main/preferences.ts +++ b/client/simple/src/js/main/preferences.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: AGPL-3.0-or-later -import { http, listen, settings } from "../core/toolkit.ts"; +import { assertElement, http, listen, settings } from "../core/toolkit.ts"; let engineDescriptions: Record<string, [string, string]> | undefined; @@ -52,19 +52,25 @@ for (const engine of disableAllEngines) { listen("click", engine, () => toggleEngines(false, engineToggles)); } -const copyHashButton: HTMLElement | null = document.querySelector<HTMLElement>("#copy-hash"); -if (copyHashButton) { - listen("click", copyHashButton, async (event: Event) => { - event.preventDefault(); +listen("click", "#copy-hash", async function (this: HTMLElement) { + const target = this.parentElement?.querySelector<HTMLPreElement>("pre"); + assertElement(target); - const { copiedText, hash } = copyHashButton.dataset; - if (!(copiedText && hash)) return; - - try { - await navigator.clipboard.writeText(hash); - copyHashButton.innerText = copiedText; - } catch (error) { - console.error("Failed to copy hash:", error); + if (window.isSecureContext) { + await navigator.clipboard.writeText(target.innerText); + } else { + const selection = window.getSelection(); + if (selection) { + const range = document.createRange(); + range.selectNodeContents(target); + selection.removeAllRanges(); + selection.addRange(range); + document.execCommand("copy"); } - }); -} + } + + const copiedText = this.dataset.copiedText; + if (copiedText) { + this.innerText = copiedText; + } +}); diff --git a/client/simple/src/js/main/results.ts b/client/simple/src/js/main/results.ts index 8b2d8c9b6..42298f9f8 100644 --- a/client/simple/src/js/main/results.ts +++ b/client/simple/src/js/main/results.ts @@ -121,7 +121,19 @@ listen("click", "#copy_url", async function (this: HTMLElement) { const target = this.parentElement?.querySelector<HTMLPreElement>("pre"); assertElement(target); - await navigator.clipboard.writeText(target.innerText); + if (window.isSecureContext) { + await navigator.clipboard.writeText(target.innerText); + } else { + const selection = window.getSelection(); + if (selection) { + const range = document.createRange(); + range.selectNodeContents(target); + selection.removeAllRanges(); + selection.addRange(range); + document.execCommand("copy"); + } + } + const copiedText = this.dataset.copiedText; if (copiedText) { this.innerText = copiedText; diff --git a/searx/templates/simple/elements/search_url.html b/searx/templates/simple/elements/search_url.html index a585ff70b..8f52f2f9a 100644 --- a/searx/templates/simple/elements/search_url.html +++ b/searx/templates/simple/elements/search_url.html @@ -1,7 +1,7 @@ <div id="search_url" role="complementary" aria-labelledby="search_url-title"> <details class="sidebar-collapsible"> <summary class="title" id="search_url-title">{{ _('Search URL') }}</summary> - <button id="copy_url" type="submit" data-copied-text="{{ _('Copied') }}">{{ _('Copy') }}</button> + <button id="copy_url" type="button" class="button" data-copied-text="{{ _('Copied') }}">{{ _('Copy') }}</button> <div class="selectable_url"> <pre>{{ url_for('search', _external=True) }}?q={{ q|urlencode }}&language={{ current_language }}&time_range={{ time_range }}&safesearch={{ safesearch }} {%- if pageno > 1 -%} diff --git a/searx/templates/simple/preferences/cookies.html b/searx/templates/simple/preferences/cookies.html index 4b9d4687a..f3c74a2e9 100644 --- a/searx/templates/simple/preferences/cookies.html +++ b/searx/templates/simple/preferences/cookies.html @@ -51,7 +51,7 @@ {{- preferences_url_params|e }} </pre>{{- '' -}} </div> - <button id="copy-hash" class="button" data-hash="{{- preferences_url_params|e -}}" data-copied-text="{{- _('Copied') -}}">{{- _('Copy') -}}</button> + <button id="copy-hash" type="button" class="button" data-hash="{{- preferences_url_params|e -}}" data-copied-text="{{- _('Copied') -}}">{{- _('Copy') -}}</button> </div> <h4> {{- _('Insert copied preferences hash (without URL) to restore') -}}:{{- '' -}} |