Skip to content

Commit

Permalink
First iteration of poc
Browse files Browse the repository at this point in the history
  • Loading branch information
chantal-kelm committed Jul 8, 2024
1 parent dc08c3d commit 8f13638
Show file tree
Hide file tree
Showing 18 changed files with 909 additions and 2 deletions.
24 changes: 22 additions & 2 deletions plugins/main/public/components/common/modules/modules-defaults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import { ComplianceTable } from '../../overview/compliance-table';
import { ButtonModuleGenerateReport } from '../modules/buttons';
import { OfficePanel } from '../../overview/office/panel';
import { GitHubPanel } from '../../overview/github/panel';
import { DashboardVuls, InventoryVuls } from '../../overview/vulnerabilities';
// import { InventoryVuls } from '../../overview/vulnerabilities';
import {
DashboardVuls,
InventoryVuls,
} from '../../overview/poc-dashboards-by-reference/dashboards';
import { DashboardMITRE } from '../../overview/mitre/dashboard';
import { withModuleNotForAgent } from '../hocs';
import {
Expand Down Expand Up @@ -86,7 +90,6 @@ const renderDiscoverTab = (props: WazuhDiscoverProps) => {
component: () => <WazuhDiscover {...props} />,
};
};

export const ModulesDefaults = {
general: {
init: 'events',
Expand Down Expand Up @@ -245,6 +248,23 @@ export const ModulesDefaults = {
vuls: {
init: 'dashboard',
tabs: [
// {
// id: 'dashboard',
// name: 'Dashboard',
// component: DashboardSavedObject,
// /* For ButtonExploreAgent to insert correctly according to the module's index pattern, the moduleIndexPatternTitle parameter is added. By default it applies the index patternt wazuh-alerts-* */
// buttons: [
// (...props) => {
// console.log('ButtonExploreAgent Props in vuls:', props);
// return (
// <ButtonExploreAgent
// {...props}
// moduleIndexPatternTitle={WAZUH_VULNERABILITIES_PATTERN}
// />
// );
// },
// ],
// },
{
id: 'dashboard',
name: 'Dashboard',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React, { useEffect, useState } from 'react';
import {
clusterReq,
clusterNodes,
} from '../../../../../controllers/management/components/management/configuration/utils/wz-fetch';
import { WzRequest } from '../../../../../react-services';
import { webDocumentationLink } from '../../../../../../common/services/web_documentation';
import { EuiCallOut, EuiLink } from '@elastic/eui';
import { UI_LOGGER_LEVELS } from '../../../../../../common/constants';
import { UI_ERROR_SEVERITIES } from '../../../../../react-services/error-orchestrator/types';
import { getErrorOrchestrator } from '../../../../../react-services/common-services';
import { useUserPermissionsRequirements } from '../../../../common/hooks';

async function checkVDIsEnabledCluster() {
// Get nodes
const responseNodes = await clusterNodes();

const nodes = responseNodes.data.data.affected_items.map(({ name }) => name);

// Check if at least some of the nodes has the module enabled
for (const node of nodes) {
const responseNodeWmodules = await WzRequest.apiReq(
'GET',
`/cluster/${node}/configuration/wmodules/wmodules`,
{},
);
const vdConfiguration =
responseNodeWmodules.data.data?.affected_items?.[0]?.wmodules?.find(
({ ['vulnerability-detection']: wmodule }) => wmodule,
);
if (vdConfiguration?.['vulnerability-detection']?.enabled === 'yes') {
return true;
}
}
return false;
}

async function checkVDIsEnabledManager() {
const responseWmodules = await WzRequest.apiReq(
'GET',
`/manager/configuration/wmodules/wmodules`,
{},
);

const vdConfiguration =
responseWmodules.data.data?.affected_items?.[0]?.wmodules?.find(
({ ['vulnerability-detection']: wmodule }) => wmodule,
);
return vdConfiguration?.['vulnerability-detection']?.enabled === 'yes';
}

export const ModuleEnabledCheck = () => {
const [data, setData] = useState<{ enabled: boolean } | null>(null);
const [userPermissionRequirements] = useUserPermissionsRequirements([
{ action: 'cluster:status', resource: '*:*:*' },
{ action: 'cluster:read', resource: 'node:id:*' },
{ action: 'manager:read', resource: '*:*:*' },
]);

const checkVDIsEnabled = async () => {
try {
// Check cluster status
setData(null);
const clusterStatus = await clusterReq();

// Check if the module is enabled
const enabled =
clusterStatus.data.data.enabled === 'yes' &&
clusterStatus.data.data.running === 'yes'
? await checkVDIsEnabledCluster()
: await checkVDIsEnabledManager();
setData({ enabled });
} catch (error) {
const options = {
context: `${ModuleEnabledCheck.name}.useEffect`,
level: UI_LOGGER_LEVELS.ERROR,
severity: UI_ERROR_SEVERITIES.BUSINESS,
error: {
error: error,
message: error.message || error,
title: 'Error checking if the module is enabled',
},
};
getErrorOrchestrator().handleError(options);
}
};

useEffect(() => {
/* Only check if the module is enabled if the user has the expected permissions to
do the API requests */
if (!userPermissionRequirements) {
checkVDIsEnabled();
}
}, [userPermissionRequirements]);

return data?.enabled === false ? (
<EuiCallOut title='Warning' color='warning' iconType='alert'>
<p>
Vulnerabilies detection module is not enabled. You can learn to how to
configure following the{' '}
<EuiLink
href={webDocumentationLink(
'user-manual/capabilities/vulnerability-detection/configuring-scans.html#configuring-vulnerability-detection',
)}
external
target='_blank'
rel='noopener noreferrer'
>
documentation
</EuiLink>
.
</p>
</EuiCallOut>
) : null;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { EuiFlexItem, EuiCodeBlock, EuiTabbedContent } from '@elastic/eui';
import { IndexPattern } from '../../../../../../../../src/plugins/data/common';
import DocViewer from '../../../../common/doc-viewer/doc-viewer';
import { useDocViewer } from '../../../../common/doc-viewer';

export const DocumentViewTableAndJson = ({ document, indexPattern }) => {
const docViewerProps = useDocViewer({
doc: document,
indexPattern: indexPattern as IndexPattern,
});

return (
<EuiFlexItem>
<EuiTabbedContent
tabs={[
{
id: 'table',
name: 'Table',
content: <DocViewer {...docViewerProps} />,
},
{
id: 'json',
name: 'JSON',
content: (
<EuiCodeBlock
aria-label={'Document details'}
language='json'
isCopyable
paddingSize='s'
>
{JSON.stringify(document, null, 2)}
</EuiCodeBlock>
),
},
]}
/>
</EuiFlexItem>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.discoverNoResults {
display: flex;
align-items: center;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import './loading_spinner.scss';
import React from 'react';
import { EuiTitle, EuiPanel, EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui';
import { FormattedMessage } from '@osd/i18n/react';

export function LoadingSpinner() {
return (
<EuiPanel hasBorder={false} hasShadow={false} color="transparent" className="discoverNoResults">
<EuiEmptyPrompt
icon={<EuiLoadingSpinner data-test-subj="loadingSpinner" size="xl" />}
title={
<EuiTitle size="s" data-test-subj="loadingSpinnerText">
<h2>
<FormattedMessage id="discover.searchingTitle" defaultMessage="Searching" />
</h2>
</EuiTitle>
}
/>
</EuiPanel>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Any modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';
import { FormattedMessage, I18nProvider } from '@osd/i18n/react';

import { EuiCallOut, EuiPanel } from '@elastic/eui';

interface Props {
message?: string;
}

export const DiscoverNoResults = ({ message }: Props) => {
return (
<I18nProvider>
<EuiPanel hasBorder={false} hasShadow={false} color='transparent'>
<EuiCallOut
title={
message ?? (
<FormattedMessage
id='discover.noResults.searchExamples.noResultsMatchSearchCriteriaTitle'
defaultMessage='No results match your search criteria'
/>
)
}
color='warning'
iconType='help'
data-test-subj='discoverNoResults'
/>
</EuiPanel>
</I18nProvider>
);
};
Loading

0 comments on commit 8f13638

Please sign in to comment.