Skip to content

Commit

Permalink
[Cloud Security] use only available agent versions for Cloudformation…
Browse files Browse the repository at this point in the history
… and Cloud Shell parameters (#166198)

## Summary

fixes 
- elastic/security-team#7557

instead of using Kibana version for Cloudformation and Cloud Shell
params in CNVM and CSPM integrations, check if the version of an agent
that matches the current Kibana version actually available as an
artifact. Relevant for serverless, where Kibana version points to
not-yet released versions of Elastic Agent.

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
  • Loading branch information
maxcold committed Sep 26, 2023
1 parent 3072343 commit 62f9b56
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 6 deletions.
125 changes: 125 additions & 0 deletions x-pack/plugins/fleet/public/hooks/use_agent_version.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { renderHook } from '@testing-library/react-hooks';

import { useAgentVersion } from './use_agent_version';
import { useKibanaVersion } from './use_kibana_version';
import { sendGetAgentsAvailableVersions } from './use_request';

jest.mock('./use_kibana_version');
jest.mock('./use_request');

describe('useAgentVersion', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should return agent version that matches Kibana version if released', async () => {
const mockKibanaVersion = '8.8.1';
const mockAvailableVersions = ['8.9.0', '8.8.1', '8.8.0', '8.7.0'];

(useKibanaVersion as jest.Mock).mockReturnValue(mockKibanaVersion);
(sendGetAgentsAvailableVersions as jest.Mock).mockResolvedValue({
data: { items: mockAvailableVersions },
});

const { result, waitForNextUpdate } = renderHook(() => useAgentVersion());

expect(sendGetAgentsAvailableVersions).toHaveBeenCalled();

await waitForNextUpdate();

expect(result.current).toEqual(mockKibanaVersion);
});

it('should return the latest availeble agent version if a version that matches Kibana version is not released', async () => {
const mockKibanaVersion = '8.11.0';
const mockAvailableVersions = ['8.8.0', '8.7.0', '8.9.2', '7.16.0'];

(useKibanaVersion as jest.Mock).mockReturnValue(mockKibanaVersion);
(sendGetAgentsAvailableVersions as jest.Mock).mockResolvedValue({
data: { items: mockAvailableVersions },
});

const { result, waitForNextUpdate } = renderHook(() => useAgentVersion());

expect(sendGetAgentsAvailableVersions).toHaveBeenCalled();

await waitForNextUpdate();

expect(result.current).toEqual('8.9.2');
});

it('should return the agent version that is <= Kibana version if an agent version that matches Kibana version is not released', async () => {
const mockKibanaVersion = '8.8.3';
const mockAvailableVersions = ['8.8.0', '8.8.1', '8.8.2', '8.7.0', '8.9.2', '7.16.0'];

(useKibanaVersion as jest.Mock).mockReturnValue(mockKibanaVersion);
(sendGetAgentsAvailableVersions as jest.Mock).mockResolvedValue({
data: { items: mockAvailableVersions },
});

const { result, waitForNextUpdate } = renderHook(() => useAgentVersion());

expect(sendGetAgentsAvailableVersions).toHaveBeenCalled();

await waitForNextUpdate();

expect(result.current).toEqual('8.8.2');
});

it('should return the latest availeble agent version if a snapshot version', async () => {
const mockKibanaVersion = '8.10.0-SNAPSHOT';
const mockAvailableVersions = ['8.8.0', '8.7.0', '8.9.2', '7.16.0'];

(useKibanaVersion as jest.Mock).mockReturnValue(mockKibanaVersion);
(sendGetAgentsAvailableVersions as jest.Mock).mockResolvedValue({
data: { items: mockAvailableVersions },
});

const { result, waitForNextUpdate } = renderHook(() => useAgentVersion());

expect(sendGetAgentsAvailableVersions).toHaveBeenCalled();

await waitForNextUpdate();

expect(result.current).toEqual('8.9.2');
});

it('should return kibana version if no agent versions available', async () => {
const mockKibanaVersion = '8.11.0';
const mockAvailableVersions: string[] = [];

(useKibanaVersion as jest.Mock).mockReturnValue(mockKibanaVersion);
(sendGetAgentsAvailableVersions as jest.Mock).mockResolvedValue({
data: { items: mockAvailableVersions },
});

const { result, waitForNextUpdate } = renderHook(() => useAgentVersion());

expect(sendGetAgentsAvailableVersions).toHaveBeenCalled();

await waitForNextUpdate();

expect(result.current).toEqual('8.11.0');
});

it('should return kibana version if the list of available agent versions is not available', async () => {
const mockKibanaVersion = '8.11.0';

(useKibanaVersion as jest.Mock).mockReturnValue(mockKibanaVersion);
(sendGetAgentsAvailableVersions as jest.Mock).mockRejectedValue(new Error('Fetching error'));

const { result, waitForNextUpdate } = renderHook(() => useAgentVersion());

expect(sendGetAgentsAvailableVersions).toHaveBeenCalled();
await waitForNextUpdate();

expect(result.current).toEqual(mockKibanaVersion);
});
});
27 changes: 21 additions & 6 deletions x-pack/plugins/fleet/public/hooks/use_agent_version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useEffect, useState } from 'react';
import semverRcompare from 'semver/functions/rcompare';
import semverLt from 'semver/functions/lt';

import { useKibanaVersion } from './use_kibana_version';
import { sendGetAgentsAvailableVersions } from './use_request';

/**
* @returns The most recent agent version available to install or upgrade to.
* @returns The most compatible agent version available to install or upgrade to.
*/
export const useAgentVersion = (): string | undefined => {
const kibanaVersion = useKibanaVersion();
Expand All @@ -21,12 +22,26 @@ export const useAgentVersion = (): string | undefined => {
const getVersions = async () => {
try {
const res = await sendGetAgentsAvailableVersions();
// if the endpoint returns an error, use the fallback versions
const versionsList = res?.data?.items ? res.data.items : [kibanaVersion];
const availableVersions = res?.data?.items;
let agentVersionToUse;

if (
availableVersions &&
availableVersions.length > 0 &&
availableVersions.indexOf(kibanaVersion) === -1
) {
availableVersions.sort(semverRcompare);
agentVersionToUse =
availableVersions.find((version) => {
return semverLt(version, kibanaVersion);
}) || availableVersions[0];
} else {
agentVersionToUse = kibanaVersion;
}

setAgentVersion(versionsList[0]);
setAgentVersion(agentVersionToUse);
} catch (err) {
return;
setAgentVersion(kibanaVersion);
}
};

Expand Down

0 comments on commit 62f9b56

Please sign in to comment.