[Synthtrace] Fix wrong url build in the Kibana client (#217678)

Relates to #217529 
#216653
#216844

## Summary

The issue was introduced in the [PR
here](https://github.com/elastic/kibana/pull/212120/files#diff-34f8e7299930135fd708d98018fc6f4141d6e7c25df7e5fdb90f3472ad0e2948R36):
basically, the URL will look like: `
http:/user:pass@localhost:5620/api/fleet/epm/packages/apm?prerelease=false`
because `Path.join` will strip the `/` which is needed in this case -
this URL is also passed to `getFetchAgent`. This PR will fix this issue.
This commit is contained in:
jennypavlova 2025-04-10 17:49:50 +02:00 committed by GitHub
parent 23cbaa6d55
commit 7f0a625d66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 91 additions and 5 deletions

View file

@ -11,9 +11,9 @@
import fetch from 'node-fetch';
import { RequestInit } from 'node-fetch';
import Path from 'path';
import { kibanaHeaders } from './client_headers';
import { getFetchAgent } from '../../cli/utils/ssl';
import { normalizeUrl } from '../utils/normalize_url';
type KibanaClientFetchOptions = RequestInit;
@ -33,18 +33,20 @@ export class KibanaClient {
}
fetch<T>(pathname: string, options: KibanaClientFetchOptions): Promise<T> {
const url = Path.join(this.target, pathname);
return fetch(url, {
const pathnameWithLeadingSlash = pathname.startsWith('/') ? pathname : `/${pathname}`;
const url = new URL(`${this.target}${pathnameWithLeadingSlash}`);
const normalizedUrl = normalizeUrl(url.toString());
return fetch(normalizedUrl, {
...options,
headers: {
...this.headers,
...options.headers,
},
agent: getFetchAgent(url),
agent: getFetchAgent(normalizedUrl),
}).then(async (response) => {
if (response.status >= 400) {
throw new KibanaClientHttpError(
`Response error for ${options.method?.toUpperCase() ?? 'GET'} ${url}`,
`Response error for ${options.method?.toUpperCase() ?? 'GET'} ${normalizedUrl}`,
response.status,
await response.json().catch((error) => {
return undefined;

View file

@ -0,0 +1,12 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
export function normalizeUrl(url: string): string {
return url.replace(/([^:]\/)\/+/g, '$1');
}

View file

@ -0,0 +1,72 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { normalizeUrl } from './normalize_url';
describe('normalizeUrl', () => {
it('should remove unnecessary double slashes from the URL', () => {
const url = 'http://example.com//some//path///to/resource';
const result = normalizeUrl(url);
expect(result).toBe('http://example.com/some/path/to/resource');
});
it('should preserve the protocol slashes', () => {
const url = 'http:///example.com';
const result = normalizeUrl(url);
expect(result).toBe('http://example.com');
});
it('should handle URLs with query parameters', () => {
const url = 'http://example.com//some/path?key=value&key2=value2';
const result = normalizeUrl(url);
expect(result).toBe('http://example.com/some/path?key=value&key2=value2');
});
it('should handle URLs with port', () => {
const url = 'http://example:3000//some/path?key=value&key2=value2';
const result = normalizeUrl(url);
expect(result).toBe('http://example:3000/some/path?key=value&key2=value2');
});
it('should handle URLs with localhost port and extra path', () => {
const url = 'http://localhost:3000//some/path//more/path';
const result = normalizeUrl(url);
expect(result).toBe('http://localhost:3000/some/path/more/path');
});
it('should handle URLs with trailing slashes', () => {
const url = 'http://example.com/some/path///';
const result = normalizeUrl(url);
expect(result).toBe('http://example.com/some/path/');
});
it('should handle URLs without double slashes', () => {
const url = 'http://example.com/some/path';
const result = normalizeUrl(url);
expect(result).toBe('http://example.com/some/path');
});
it('should handle URLs with no path', () => {
const url = 'http://example.com';
const result = normalizeUrl(url);
expect(result).toBe('http://example.com');
});
it('should handle empty strings', () => {
const url = '';
const result = normalizeUrl(url);
expect(result).toBe('');
});
it('should handle URLs with differnt protocol', () => {
const url = 'ftp://example.com//some/path';
const result = normalizeUrl(url);
expect(result).toBe('ftp://example.com/some/path');
});
});