From 0c4994c3f37f155a422d92d386f3ce6b97d8ae43 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 5 Dec 2024 01:00:34 +1100 Subject: [PATCH] [8.17] [Console] Only `copy as curl` should be allowed for kbn requests (#201915) (#202677) # Backport This will backport the following commits from `main` to `8.17`: - [[Console] Only `copy as curl` should be allowed for kbn requests (#201915)](https://github.com/elastic/kibana/pull/201915) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Ignacio Rivas Co-authored-by: Elastic Machine --- .../components/context_menu/context_menu.tsx | 21 +++++++- src/plugins/console/public/services/api.ts | 4 +- .../convert_request_to_language/index.ts | 6 ++- test/functional/apps/console/_context_menu.ts | 49 +++++++++++++++++++ test/functional/page_objects/console_page.ts | 7 +++ 5 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/plugins/console/public/application/containers/editor/components/context_menu/context_menu.tsx b/src/plugins/console/public/application/containers/editor/components/context_menu/context_menu.tsx index 3860f0b7bc704..daa1515d90e7c 100644 --- a/src/plugins/console/public/application/containers/editor/components/context_menu/context_menu.tsx +++ b/src/plugins/console/public/application/containers/editor/components/context_menu/context_menu.tsx @@ -28,7 +28,11 @@ import type { EditorRequest } from '../../types'; import { useServicesContext } from '../../../../contexts'; import { StorageKeys } from '../../../../../services'; -import { DEFAULT_LANGUAGE, AVAILABLE_LANGUAGES } from '../../../../../../common/constants'; +import { + DEFAULT_LANGUAGE, + AVAILABLE_LANGUAGES, + KIBANA_API_PREFIX, +} from '../../../../../../common/constants'; interface Props { getRequests: () => Promise; @@ -90,9 +94,24 @@ export const ContextMenu = ({ // Get all the selected requests const requests = await getRequests(); + // If we have any kbn requests, we should not allow the user to copy as + // anything other than curl + const hasKbnRequests = requests.some((req) => req.url.startsWith(KIBANA_API_PREFIX)); + + if (hasKbnRequests && withLanguage !== 'curl') { + notifications.toasts.addDanger({ + title: i18n.translate('console.consoleMenu.copyAsMixedRequestsMessage', { + defaultMessage: 'Kibana requests can only be copied as curl', + }), + }); + + return; + } + const { data: requestsAsCode, error: requestError } = await convertRequestToLanguage({ language: withLanguage, esHost: esHostService.getHost(), + kibanaHost: window.location.origin, requests, }); diff --git a/src/plugins/console/public/services/api.ts b/src/plugins/console/public/services/api.ts index c15e90dfd1bd4..c104a5fc16d09 100644 --- a/src/plugins/console/public/services/api.ts +++ b/src/plugins/console/public/services/api.ts @@ -14,15 +14,17 @@ export async function convertRequestToLanguage({ requests, language, esHost, + kibanaHost, }: { language: string; esHost: string; + kibanaHost: string; requests: EditorRequest[]; }) { return sendRequest({ path: `/api/console/convert_request_to_language`, method: 'post', - query: { language, esHost }, + query: { language, esHost, kibanaHost }, body: requests, }); } diff --git a/src/plugins/console/server/routes/api/console/convert_request_to_language/index.ts b/src/plugins/console/server/routes/api/console/convert_request_to_language/index.ts index 23577d7b200d9..115ece83859fa 100644 --- a/src/plugins/console/server/routes/api/console/convert_request_to_language/index.ts +++ b/src/plugins/console/server/routes/api/console/convert_request_to_language/index.ts @@ -18,6 +18,7 @@ const routeValidationConfig = { query: schema.object({ language: schema.string(), esHost: schema.string(), + kibanaHost: schema.string(), }), body: schema.maybe( schema.arrayOf( @@ -39,7 +40,7 @@ export const registerConvertRequestRoute = ({ }: RouteDependencies) => { const handler: RequestHandler = async (ctx, req, response) => { const { body, query } = req; - const { language, esHost } = query; + const { language, esHost, kibanaHost } = query; try { // Iterate over each request and build all the requests into a single string @@ -60,6 +61,9 @@ export const registerConvertRequestRoute = ({ printResponse: true, complete: true, elasticsearchUrl: esHost, + otherUrls: { + kbn: kibanaHost, + }, }); return response.ok({ diff --git a/test/functional/apps/console/_context_menu.ts b/test/functional/apps/console/_context_menu.ts index 4ee3c2cca40a7..0e126467c04c2 100644 --- a/test/functional/apps/console/_context_menu.ts +++ b/test/functional/apps/console/_context_menu.ts @@ -70,6 +70,55 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { } }); + it('doesnt allow to copy kbn requests as anything other than curl', async () => { + const canReadClipboard = await browser.checkBrowserPermission('clipboard-read'); + + await PageObjects.console.clearEditorText(); + await PageObjects.console.enterText('GET _search\n'); + + // Add a kbn request + // pressEnter + await PageObjects.console.enterText('GET kbn:/api/spaces/space'); + // Make sure to select the es and kbn request + await PageObjects.console.selectAllRequests(); + + await PageObjects.console.clickContextMenu(); + await PageObjects.console.clickCopyAsButton(); + + let resultToast = await toasts.getElementByIndex(1); + let toastText = await resultToast.getVisibleText(); + + expect(toastText).to.be('Requests copied to clipboard as curl'); + + // Check if the clipboard has the curl request + if (canReadClipboard) { + const clipboardText = await browser.getClipboardValue(); + expect(clipboardText).to.contain('curl -X GET'); + } + + // Wait until async operation is done + await PageObjects.common.sleep(1000); + + // Focus editor once again + await PageObjects.console.focusInputEditor(); + + // Try to copy as javascript + await PageObjects.console.clickContextMenu(); + await PageObjects.console.changeLanguageAndCopy('javascript'); + + resultToast = await toasts.getElementByIndex(2); + toastText = await resultToast.getVisibleText(); + + expect(toastText).to.be('Kibana requests can only be copied as curl'); + + // Since we tried to copy as javascript, the clipboard should still have + // the curl request + if (canReadClipboard) { + const clipboardText = await browser.getClipboardValue(); + expect(clipboardText).to.contain('curl -X GET'); + } + }); + it.skip('allows to change default language', async () => { await PageObjects.console.clickContextMenu(); diff --git a/test/functional/page_objects/console_page.ts b/test/functional/page_objects/console_page.ts index a80f3426e256e..30a4b27c3f037 100644 --- a/test/functional/page_objects/console_page.ts +++ b/test/functional/page_objects/console_page.ts @@ -52,6 +52,13 @@ export class ConsolePageObject extends FtrService { await textArea.clearValueWithKeyboard(); } + public async focusInputEditor() { + const outputEditor = await this.testSubjects.find('consoleMonacoEditor'); + // Simply clicking on the editor doesn't focus it, so we need to click + // on the margin view overlays + await (await outputEditor.findByClassName('margin-view-overlays')).click(); + } + public async focusOutputEditor() { const outputEditor = await this.testSubjects.find('consoleMonacoOutput'); // Simply clicking on the output editor doesn't focus it, so we need to click