mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Session view and k8s dashboard fixes (#154982)
## Summary - fixes some issues in session_view wrt to logs-cloud_defend.process* data. - added a 'collapse all' children feature. with sticky scroll session leader! - k8s dashboard session table: user.name -> user.id (id is more likely to be set for both endpoint and cloud-defend) - Fixed a major bug when 'searching within terminal'. If a process is highlighted it would cause kibana to blow up. - session view handling of session leader user info improved. - codeowners updated. awp-viz -> sec-cloudnative-integrations - a badge will be added to the selector header when it's not in used by a response flow ### Screenshots  Sticky session leader demo: https://www.loom.com/share/b039e48fdfd647b291f293d643339660 ### Checklist Delete any items that are not applicable to this PR. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [x] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
28b1f96d47
commit
0a5b4233d1
35 changed files with 162 additions and 90 deletions
8
.github/CODEOWNERS
vendored
8
.github/CODEOWNERS
vendored
|
@ -431,7 +431,7 @@ src/plugins/kibana_overview @elastic/appex-sharedux
|
|||
src/plugins/kibana_react @elastic/appex-sharedux
|
||||
src/plugins/kibana_usage_collection @elastic/kibana-core
|
||||
src/plugins/kibana_utils @elastic/kibana-app-services
|
||||
x-pack/plugins/kubernetes_security @elastic/awp-viz
|
||||
x-pack/plugins/kubernetes_security @elastic/sec-cloudnative-integrations
|
||||
packages/kbn-language-documentation-popover @elastic/kibana-visualizations
|
||||
x-pack/plugins/lens @elastic/kibana-visualizations
|
||||
x-pack/plugins/license_api_guard @elastic/platform-deployment-management
|
||||
|
@ -567,7 +567,7 @@ packages/kbn-securitysolution-utils @elastic/security-solution-platform
|
|||
packages/kbn-server-http-tools @elastic/kibana-core
|
||||
packages/kbn-server-route-repository @elastic/apm-ui
|
||||
test/plugin_functional/plugins/session_notifications @elastic/kibana-core
|
||||
x-pack/plugins/session_view @elastic/awp-viz
|
||||
x-pack/plugins/session_view @elastic/sec-cloudnative-integrations
|
||||
packages/kbn-set-map @elastic/kibana-operations
|
||||
examples/share_examples @elastic/kibana-app-services
|
||||
src/plugins/share @elastic/appex-sharedux
|
||||
|
@ -1176,8 +1176,8 @@ x-pack/plugins/security_solution/cypress/README.md @elastic/security-engineering
|
|||
x-pack/test/security_solution_cypress @elastic/security-engineering-productivity
|
||||
|
||||
## Security Solution sub teams - adaptive-workload-protection
|
||||
x-pack/plugins/security_solution/public/common/components/sessions_viewer @elastic/awp-viz
|
||||
x-pack/plugins/security_solution/public/kubernetes @elastic/awp-viz
|
||||
x-pack/plugins/security_solution/public/common/components/sessions_viewer @elastic/sec-cloudnative-integrations
|
||||
x-pack/plugins/security_solution/public/kubernetes @elastic/sec-cloudnative-integrations
|
||||
|
||||
## Security Solution sub teams - Protections Experience
|
||||
x-pack/plugins/security_solution/public/threat_intelligence @elastic/protections-experience
|
||||
|
|
|
@ -15,7 +15,7 @@ import kubernetesSecurityObj from './kubernetes_security.devdocs.json';
|
|||
|
||||
|
||||
|
||||
Contact [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) for questions regarding this plugin.
|
||||
Contact [@elastic/sec-cloudnative-integrations](https://github.com/orgs/elastic/teams/sec-cloudnative-integrations) for questions regarding this plugin.
|
||||
|
||||
**Code health stats**
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana']
|
|||
| <DocLink id="kibKibanaReactPluginApi" text="kibanaReact"/> | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 185 | 1 | 153 | 5 |
|
||||
| kibanaUsageCollection | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 |
|
||||
| <DocLink id="kibKibanaUtilsPluginApi" text="kibanaUtils"/> | [@elastic/kibana-app-services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 609 | 3 | 416 | 9 |
|
||||
| <DocLink id="kibKubernetesSecurityPluginApi" text="kubernetesSecurity"/> | [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) | - | 3 | 0 | 3 | 1 |
|
||||
| <DocLink id="kibKubernetesSecurityPluginApi" text="kubernetesSecurity"/> | [@elastic/sec-cloudnative-integrations](https://github.com/orgs/elastic/teams/sec-cloudnative-integrations) | - | 3 | 0 | 3 | 1 |
|
||||
| <DocLink id="kibLensPluginApi" text="lens"/> | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 608 | 0 | 513 | 53 |
|
||||
| <DocLink id="kibLicenseApiGuardPluginApi" text="licenseApiGuard"/> | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 8 | 0 | 8 | 0 |
|
||||
| <DocLink id="kibLicenseManagementPluginApi" text="licenseManagement"/> | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 1 |
|
||||
|
@ -151,7 +151,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana']
|
|||
| searchprofiler | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 |
|
||||
| <DocLink id="kibSecurityPluginApi" text="security"/> | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides authentication and authorization features, and exposes functionality to understand the capabilities of the currently authenticated user. | 280 | 0 | 94 | 0 |
|
||||
| <DocLink id="kibSecuritySolutionPluginApi" text="securitySolution"/> | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 117 | 0 | 76 | 27 |
|
||||
| <DocLink id="kibSessionViewPluginApi" text="sessionView"/> | [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) | - | 7 | 0 | 7 | 1 |
|
||||
| <DocLink id="kibSessionViewPluginApi" text="sessionView"/> | [@elastic/sec-cloudnative-integrations](https://github.com/orgs/elastic/teams/sec-cloudnative-integrations) | - | 7 | 0 | 7 | 1 |
|
||||
| <DocLink id="kibSharePluginApi" text="share"/> | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds URL Service and sharing capabilities to Kibana | 118 | 0 | 59 | 10 |
|
||||
| <DocLink id="kibSnapshotRestorePluginApi" text="snapshotRestore"/> | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 22 | 1 | 22 | 1 |
|
||||
| <DocLink id="kibSpacesPluginApi" text="spaces"/> | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 253 | 0 | 65 | 0 |
|
||||
|
|
|
@ -15,7 +15,7 @@ import sessionViewObj from './session_view.devdocs.json';
|
|||
|
||||
|
||||
|
||||
Contact [@elastic/awp-viz](https://github.com/orgs/elastic/teams/awp-viz) for questions regarding this plugin.
|
||||
Contact [@elastic/sec-cloudnative-integrations](https://github.com/orgs/elastic/teams/sec-cloudnative-integrations) for questions regarding this plugin.
|
||||
|
||||
**Code health stats**
|
||||
|
||||
|
|
|
@ -270,7 +270,7 @@
|
|||
{
|
||||
"groupName": "TTY Output",
|
||||
"matchPackageNames": ["xterm", "byte-size", "@types/byte-size"],
|
||||
"reviewers": ["team:awp-viz"],
|
||||
"reviewers": ["team:sec-cloudnative-integrations"],
|
||||
"matchBaseBranches": ["main"],
|
||||
"labels": ["Team: AWP: Visualization", "release_note:skip", "backport:skip"],
|
||||
"enabled": true,
|
||||
|
|
|
@ -54,7 +54,7 @@ describe('getSelectorConditions', () => {
|
|||
|
||||
// check that process specific conditions are not included
|
||||
expect(options.includes('processExecutable')).toBeFalsy();
|
||||
expect(options.includes('processUserId')).toBeFalsy();
|
||||
expect(options.includes('sessionLeaderInteractive')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('grabs process conditions for process selectors', () => {
|
||||
|
@ -70,7 +70,6 @@ describe('getSelectorConditions', () => {
|
|||
|
||||
// check that process specific conditions are not included
|
||||
expect(options.includes('processExecutable')).toBeTruthy();
|
||||
expect(options.includes('processUserId')).toBeTruthy();
|
||||
expect(options.includes('sessionLeaderInteractive')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -338,6 +338,10 @@ export const ControlGeneralView = ({ policy, onChange, show }: ViewDeps) => {
|
|||
</EuiFlexItem>
|
||||
|
||||
{selectors.map((selector, i) => {
|
||||
const usedByResponse = !!responses.find((response) =>
|
||||
response.match.includes(selector.name)
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiFlexItem key={i}>
|
||||
<ControlGeneralViewSelector
|
||||
|
@ -345,6 +349,7 @@ export const ControlGeneralView = ({ policy, onChange, show }: ViewDeps) => {
|
|||
index={i}
|
||||
selector={selector}
|
||||
selectors={selectors}
|
||||
usedByResponse={usedByResponse}
|
||||
onDuplicate={onDuplicateSelector}
|
||||
onRemove={onRemoveSelector}
|
||||
onChange={onSelectorChange}
|
||||
|
|
|
@ -116,6 +116,14 @@ export const name = i18n.translate('xpack.cloudDefend.name', {
|
|||
defaultMessage: 'Name',
|
||||
});
|
||||
|
||||
export const unusedSelector = i18n.translate('xpack.cloudDefend.unusedSelector', {
|
||||
defaultMessage: 'Not in use',
|
||||
});
|
||||
|
||||
export const unusedSelectorHelp = i18n.translate('xpack.cloudDefend.unusedSelectorHelp', {
|
||||
defaultMessage: 'This selector is not in use by any response.',
|
||||
});
|
||||
|
||||
export const errorInvalidResourceLabel = i18n.translate(
|
||||
'xpack.cloudDefend.errorInvalidResourceLabel',
|
||||
{
|
||||
|
|
|
@ -50,6 +50,7 @@ describe('<ControlGeneralViewSelector />', () => {
|
|||
onChange={onChange}
|
||||
onRemove={onRemove}
|
||||
onDuplicate={onDuplicate}
|
||||
usedByResponse={false}
|
||||
/>
|
||||
</TestProvider>
|
||||
);
|
||||
|
@ -68,6 +69,12 @@ describe('<ControlGeneralViewSelector />', () => {
|
|||
expect(getByTestId('cloud-defend-selectorcondition-operation')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders a badge to show that the selector is unused', () => {
|
||||
const { getByText } = render(<WrappedComponent />);
|
||||
|
||||
expect(getByText(i18n.unusedSelector)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('allows the user to add a limited set of operations', () => {
|
||||
const { getByTestId, rerender } = render(<WrappedComponent />);
|
||||
|
||||
|
|
|
@ -184,6 +184,7 @@ const StringArrayCondition = ({
|
|||
export const ControlGeneralViewSelector = ({
|
||||
selector,
|
||||
selectors,
|
||||
usedByResponse,
|
||||
index,
|
||||
onRemove,
|
||||
onDuplicate,
|
||||
|
@ -393,17 +394,24 @@ export const ControlGeneralViewSelector = ({
|
|||
css={styles.accordion}
|
||||
extraAction={
|
||||
<EuiFlexGroup alignItems="center" gutterSize="none">
|
||||
{accordionState === 'closed' && (
|
||||
<div>
|
||||
<EuiText css={styles.conditionsBadge} size="xs">
|
||||
<b>{i18n.conditions}</b>
|
||||
</EuiText>
|
||||
<EuiBadge title={conditionsAdded.join(',')} color="hollow">
|
||||
{conditionsAdded.length}
|
||||
<div>
|
||||
{accordionState === 'closed' && (
|
||||
<>
|
||||
<EuiText css={styles.conditionsBadge} size="xs">
|
||||
<b>{i18n.conditions}</b>
|
||||
</EuiText>
|
||||
<EuiBadge title={conditionsAdded.join(',')} color="hollow">
|
||||
{conditionsAdded.length}
|
||||
</EuiBadge>
|
||||
</>
|
||||
)}
|
||||
{!usedByResponse && (
|
||||
<EuiBadge title={i18n.unusedSelectorHelp} color="warning">
|
||||
{i18n.unusedSelector}
|
||||
</EuiBadge>
|
||||
<div css={styles.verticalDivider} />
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
<div css={styles.verticalDivider} />
|
||||
</div>
|
||||
<EuiFlexItem>
|
||||
<EuiPopover
|
||||
id={selector.name}
|
||||
|
|
|
@ -240,14 +240,8 @@
|
|||
{
|
||||
"required": ["processName"]
|
||||
},
|
||||
{
|
||||
"required": ["processUserId"]
|
||||
},
|
||||
{
|
||||
"required": ["sessionLeaderInteractive"]
|
||||
},
|
||||
{
|
||||
"required": ["sessionLeaderName"]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
|
@ -335,22 +329,8 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"processUserId": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"sessionLeaderInteractive": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sessionLeaderName": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -72,6 +72,6 @@ describe('<ControlYamlView />', () => {
|
|||
);
|
||||
|
||||
expect(getByTestId('cloudDefendAdditionalErrors')).toBeTruthy();
|
||||
expect(getByText('"sessionLeaderName" values cannot exceed 16 bytes')).toBeTruthy();
|
||||
expect(getByText('"targetFilePath" values cannot exceed 255 bytes')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -55,8 +55,8 @@ export const MOCK_YAML_INVALID_STRING_ARRAY_CONDITION = `file:
|
|||
operation:
|
||||
- createExecutable
|
||||
- modifyExecutable
|
||||
sessionLeaderName:
|
||||
- reallylongsessionleadernamethatshouldnotbeallowed
|
||||
targetFilePath:
|
||||
- /bin/${new Array(256).fill('a').join()}
|
||||
responses:
|
||||
- match:
|
||||
- default
|
||||
|
|
|
@ -78,9 +78,7 @@ export type SelectorCondition =
|
|||
| 'operation'
|
||||
| 'processExecutable'
|
||||
| 'processName'
|
||||
| 'processUserId'
|
||||
| 'sessionLeaderInteractive'
|
||||
| 'sessionLeaderName';
|
||||
| 'sessionLeaderInteractive';
|
||||
|
||||
export interface SelectorConditionOptions {
|
||||
type: SelectorConditionType;
|
||||
|
@ -141,9 +139,7 @@ export const SelectorConditionsMap: SelectorConditionsMapProps = {
|
|||
ignoreVolumeMounts: { selectorType: 'file', type: 'flag', not: ['ignoreVolumeFiles'] },
|
||||
processExecutable: { selectorType: 'process', type: 'stringArray', not: ['processName'] },
|
||||
processName: { selectorType: 'process', type: 'stringArray', not: ['processExecutable'] },
|
||||
processUserId: { selectorType: 'process', type: 'stringArray' },
|
||||
sessionLeaderInteractive: { selectorType: 'process', type: 'boolean' },
|
||||
sessionLeaderName: { selectorType: 'process', type: 'stringArray', maxValueBytes: 16 },
|
||||
};
|
||||
|
||||
export type ResponseAction = 'log' | 'alert' | 'block';
|
||||
|
@ -168,9 +164,7 @@ export interface Selector {
|
|||
// process selector properties
|
||||
processExecutable?: string[];
|
||||
processName?: string[];
|
||||
processUserId?: string[];
|
||||
sessionLeaderInteractive?: string[];
|
||||
sessionLeaderName?: string[];
|
||||
|
||||
// non yaml fields
|
||||
type: SelectorType;
|
||||
|
@ -230,6 +224,7 @@ export interface ViewDeps extends SettingsDeps {
|
|||
export interface ControlGeneralViewSelectorDeps {
|
||||
selector: Selector;
|
||||
selectors: Selector[];
|
||||
usedByResponse: boolean;
|
||||
index: number;
|
||||
onChange(selector: Selector, index: number): void;
|
||||
onRemove(index: number): void;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"type": "plugin",
|
||||
"id": "@kbn/kubernetes-security-plugin",
|
||||
"owner": "@elastic/awp-viz",
|
||||
"owner": "@elastic/sec-cloudnative-integrations",
|
||||
"plugin": {
|
||||
"id": "kubernetesSecurity",
|
||||
"server": true,
|
||||
|
|
|
@ -33,7 +33,7 @@ exports[`SessionsView renders correctly against snapshot 1`] = `
|
|||
Executable
|
||||
</div>
|
||||
<div>
|
||||
User
|
||||
User ID
|
||||
</div>
|
||||
<div>
|
||||
Interactive
|
||||
|
|
|
@ -13,7 +13,7 @@ import { DEFAULT_DATE_COLUMN_MIN_WIDTH } from '../../../timelines/components/tim
|
|||
import {
|
||||
COLUMN_SESSION_START,
|
||||
COLUMN_EXECUTABLE,
|
||||
COLUMN_ENTRY_USER,
|
||||
COLUMN_ENTRY_USER_ID,
|
||||
COLUMN_INTERACTIVE,
|
||||
COLUMN_HOST_NAME,
|
||||
COLUMN_ENTRY_TYPE,
|
||||
|
@ -34,8 +34,8 @@ export const sessionsHeaders: ColumnHeaderOptions[] = [
|
|||
},
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
id: 'process.entry_leader.user.name',
|
||||
display: COLUMN_ENTRY_USER,
|
||||
id: 'process.entry_leader.user.id',
|
||||
display: COLUMN_ENTRY_USER_ID,
|
||||
},
|
||||
{
|
||||
columnHeaderType: defaultColumnHeaderType,
|
||||
|
|
|
@ -39,10 +39,10 @@ export const COLUMN_EXECUTABLE = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const COLUMN_ENTRY_USER = i18n.translate(
|
||||
'xpack.securitySolution.sessionsView.columnEntryUser',
|
||||
export const COLUMN_ENTRY_USER_ID = i18n.translate(
|
||||
'xpack.securitySolution.sessionsView.columnEntryUserID',
|
||||
{
|
||||
defaultMessage: 'User',
|
||||
defaultMessage: 'User ID',
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -191,7 +191,7 @@ export interface ProcessEvent {
|
|||
'@timestamp'?: string;
|
||||
event?: {
|
||||
kind?: EventKind;
|
||||
category?: string[];
|
||||
category?: string | string[];
|
||||
action?: EventAction | EventAction[];
|
||||
id?: string;
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"type": "plugin",
|
||||
"id": "@kbn/session-view-plugin",
|
||||
"owner": "@elastic/awp-viz",
|
||||
"owner": "@elastic/sec-cloudnative-integrations",
|
||||
"plugin": {
|
||||
"id": "sessionView",
|
||||
"server": true,
|
||||
|
|
|
@ -64,7 +64,9 @@ export const DetailPanelAlertListItem = ({
|
|||
const { args, name: processName } = event.process ?? {};
|
||||
const { event: processEvent } = event;
|
||||
const forceState = !isInvestigated ? 'open' : undefined;
|
||||
const category = processEvent?.category?.[0];
|
||||
const category = Array.isArray(processEvent?.category)
|
||||
? processEvent?.category?.[0]
|
||||
: processEvent?.category;
|
||||
const processEventAlertCategory = category ?? ProcessEventAlertCategory.process;
|
||||
const alertCategoryDetailDisplayText =
|
||||
category !== ProcessEventAlertCategory.process
|
||||
|
|
|
@ -266,6 +266,20 @@ export const autoExpandProcessTree = (processMap: ProcessMap, jumpToEntityId?: s
|
|||
return processMap;
|
||||
};
|
||||
|
||||
// recusively collapses all children below provided node
|
||||
export const collapseProcessTree = (node: Process) => {
|
||||
if (!node.autoExpand) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.children) {
|
||||
node.children.forEach((child) => {
|
||||
child.autoExpand = false;
|
||||
collapseProcessTree(child);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const processNewEvents = (
|
||||
eventsProcessMap: ProcessMap,
|
||||
events: ProcessEvent[] | undefined,
|
||||
|
|
|
@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { ProcessTreeNode } from '../process_tree_node';
|
||||
import { BackToInvestigatedAlert } from '../back_to_investigated_alert';
|
||||
import { useProcessTree } from './hooks';
|
||||
import { collapseProcessTree } from './helpers';
|
||||
import { ProcessTreeLoadMoreButton } from '../process_tree_load_more_button';
|
||||
import {
|
||||
AlertStatusEventEntityIdMap,
|
||||
|
@ -97,6 +98,7 @@ export const ProcessTree = ({
|
|||
verboseMode,
|
||||
jumpToEntityId,
|
||||
});
|
||||
const [forceRerender, setForceRerender] = useState(0);
|
||||
|
||||
const eventsRemaining = useMemo(() => {
|
||||
const total = data?.[0]?.total || 0;
|
||||
|
@ -126,6 +128,14 @@ export const ProcessTree = ({
|
|||
setIsInvestigatedEventVisible(true);
|
||||
}, [onProcessSelected]);
|
||||
|
||||
const handleCollapseProcessTree = useCallback(() => {
|
||||
collapseProcessTree(sessionLeader);
|
||||
if (scrollerRef.current) {
|
||||
scrollerRef.current.scrollTop = 0;
|
||||
}
|
||||
setForceRerender(Math.random());
|
||||
}, [sessionLeader]);
|
||||
|
||||
useEffect(() => {
|
||||
if (setSearchResults) {
|
||||
setSearchResults(searchResults);
|
||||
|
@ -160,6 +170,7 @@ export const ProcessTree = ({
|
|||
ref={scrollerRef}
|
||||
css={styles.sessionViewProcessTree}
|
||||
data-test-subj="sessionView:sessionViewProcessTree"
|
||||
key={forceRerender}
|
||||
>
|
||||
{sessionLeader && (
|
||||
<ProcessTreeNode
|
||||
|
@ -176,6 +187,7 @@ export const ProcessTree = ({
|
|||
showTimestamp={showTimestamp}
|
||||
verboseMode={verboseMode}
|
||||
searchResults={searchResults}
|
||||
handleCollapseProcessTree={handleCollapseProcessTree}
|
||||
loadPreviousButton={
|
||||
hasPreviousPage ? (
|
||||
<ProcessTreeLoadMoreButton
|
||||
|
|
|
@ -23,8 +23,6 @@ export const useStyles = () => {
|
|||
overflow: 'auto',
|
||||
height: '100%',
|
||||
backgroundColor: euiVars.euiColorLightestShade,
|
||||
paddingTop: size.base,
|
||||
paddingLeft: size.s,
|
||||
};
|
||||
|
||||
const selectionArea: CSSObject = {
|
||||
|
|
|
@ -54,6 +54,7 @@ Object {
|
|||
>
|
||||
cmd test alert
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
@ -148,6 +149,7 @@ Object {
|
|||
>
|
||||
cmd test alert
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
@ -64,7 +64,7 @@ describe('ProcessTreeNode component', () => {
|
|||
renderResult = mockedContext.render(<ProcessTreeNode {...props} isSessionLeader />);
|
||||
|
||||
expect(renderResult.container.textContent?.replace(/\s+/g, ' ')).toEqual(
|
||||
' bash started by vagrant'
|
||||
' bash started by vagrant '
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import React, {
|
|||
RefObject,
|
||||
ReactElement,
|
||||
} from 'react';
|
||||
import { EuiButton, EuiIcon, EuiToolTip, formatDate } from '@elastic/eui';
|
||||
import { EuiButton, EuiIcon, EuiToolTip, formatDate, EuiButtonIcon } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { chain } from 'lodash';
|
||||
|
@ -61,6 +61,7 @@ export interface ProcessDeps {
|
|||
onJumpToOutput: (entityId: string) => void;
|
||||
loadNextButton?: ReactElement | null;
|
||||
loadPreviousButton?: ReactElement | null;
|
||||
handleCollapseProcessTree?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,6 +84,7 @@ export function ProcessTreeNode({
|
|||
onJumpToOutput,
|
||||
loadPreviousButton,
|
||||
loadNextButton,
|
||||
handleCollapseProcessTree,
|
||||
}: ProcessDeps) {
|
||||
const [childrenExpanded, setChildrenExpanded] = useState(isSessionLeader || process.autoExpand);
|
||||
const [alertsExpanded, setAlertsExpanded] = useState(false);
|
||||
|
@ -133,7 +135,15 @@ export function ProcessTreeNode({
|
|||
|
||||
const alertTypeCounts = useMemo(() => {
|
||||
const alertCounts: AlertTypeCount[] = chain(alerts)
|
||||
.groupBy((alert) => alert.event?.category?.[0])
|
||||
.groupBy((alert) => {
|
||||
const category = alert.event?.category;
|
||||
|
||||
if (Array.isArray(category)) {
|
||||
return category?.[0];
|
||||
}
|
||||
|
||||
return category;
|
||||
})
|
||||
.map((processAlerts, alertCategory) => ({
|
||||
category: alertCategory as ProcessEventAlertCategory,
|
||||
count: processAlerts.length,
|
||||
|
@ -176,8 +186,12 @@ export function ProcessTreeNode({
|
|||
}
|
||||
|
||||
onProcessSelected?.(process);
|
||||
|
||||
if (isSessionLeader && scrollerRef.current) {
|
||||
scrollerRef.current.scrollTop = 0;
|
||||
}
|
||||
},
|
||||
[onProcessSelected, process]
|
||||
[isSessionLeader, onProcessSelected, process, scrollerRef]
|
||||
);
|
||||
|
||||
const processDetails = process.getDetails();
|
||||
|
@ -219,6 +233,19 @@ export function ProcessTreeNode({
|
|||
|
||||
const children = process.getChildren(verboseMode);
|
||||
|
||||
const user = processDetails?.process?.user;
|
||||
const userName = useMemo(() => {
|
||||
if (user?.name) {
|
||||
return user.name;
|
||||
} else if (user?.id === '0') {
|
||||
return 'root';
|
||||
} else if (user?.id) {
|
||||
return `uid: ${user?.id}`;
|
||||
}
|
||||
|
||||
return '-';
|
||||
}, [user?.id, user?.name]);
|
||||
|
||||
if (!processDetails?.process) {
|
||||
return null;
|
||||
}
|
||||
|
@ -231,7 +258,6 @@ export function ProcessTreeNode({
|
|||
parent,
|
||||
working_directory: workingDirectory,
|
||||
start,
|
||||
user,
|
||||
} = processDetails.process;
|
||||
|
||||
const shouldRenderChildren = isSessionLeader || (childrenExpanded && children?.length > 0);
|
||||
|
@ -275,7 +301,14 @@ export function ProcessTreeNode({
|
|||
<Nbsp />
|
||||
<EuiIcon type="user" />
|
||||
<Nbsp />
|
||||
<b css={styles.darkText}>{user?.name || 'ID: ' + user?.id}</b>
|
||||
<b css={styles.darkText}>{userName}</b>
|
||||
<Nbsp />
|
||||
<EuiButtonIcon
|
||||
size="xs"
|
||||
iconType="fold"
|
||||
onClick={handleCollapseProcessTree}
|
||||
css={styles.jumpToTop}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
|
|
|
@ -95,7 +95,6 @@ export const useStyles = ({
|
|||
display: 'block',
|
||||
cursor: 'pointer',
|
||||
position: 'relative',
|
||||
marginBottom: isSessionLeader ? size.s : '0px',
|
||||
'&:hover:before': {
|
||||
backgroundColor: hoverColor,
|
||||
},
|
||||
|
@ -114,6 +113,10 @@ export const useStyles = ({
|
|||
},
|
||||
};
|
||||
|
||||
const jumpToTop: CSSObject = {
|
||||
float: 'right',
|
||||
};
|
||||
|
||||
const textSection: CSSObject = {
|
||||
marginLeft: size.s,
|
||||
span: {
|
||||
|
@ -131,13 +134,23 @@ export const useStyles = ({
|
|||
display: 'inline-block',
|
||||
verticalAlign: 'middle',
|
||||
},
|
||||
paddingLeft: PROCESS_TREE_LEFT_PADDING,
|
||||
};
|
||||
|
||||
const searchHighlight = `
|
||||
color: ${colors.fullShade};
|
||||
border-radius: '0px';
|
||||
background-color: ${searchResColor};
|
||||
`;
|
||||
if (isSessionLeader) {
|
||||
processNode.position = 'sticky';
|
||||
processNode.top = '-' + size.base;
|
||||
processNode.zIndex = 1;
|
||||
processNode.borderTop = `${size.base} solid transparent`;
|
||||
processNode.backgroundColor = euiVars.euiColorLightestShade;
|
||||
processNode.borderBottom = border.editable;
|
||||
}
|
||||
|
||||
const searchHighlight: CSSObject = {
|
||||
color: colors.fullShade,
|
||||
borderRadius: '0px',
|
||||
backgroundColor: searchResColor,
|
||||
};
|
||||
|
||||
const wrapper: CSSObject = {
|
||||
paddingLeft: size.s,
|
||||
|
@ -188,6 +201,7 @@ export const useStyles = ({
|
|||
icon,
|
||||
textSection,
|
||||
sessionLeader,
|
||||
jumpToTop,
|
||||
};
|
||||
}, [depth, euiTheme, hasAlerts, hasInvestigatedAlert, isSelected, euiVars, isSessionLeader]);
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ const css: CSSObject = {
|
|||
display: 'inline',
|
||||
fontSize: 0,
|
||||
lineHeight: 0,
|
||||
verticalAlign: 'middle',
|
||||
},
|
||||
};
|
||||
// Component that takes an array of matching indices in a text and pass down a highlight
|
||||
|
|
|
@ -201,6 +201,10 @@ export const SessionView = ({
|
|||
[onProcessSelected, searchResults]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
onSearchIndexChange(0);
|
||||
}, [onSearchIndexChange, searchResults]);
|
||||
|
||||
const handleOnAlertDetailsClosed = useCallback((alertUuid: string) => {
|
||||
setFetchAlertStatus([alertUuid]);
|
||||
}, []);
|
||||
|
|
|
@ -21,24 +21,22 @@ describe('getAlertCategoryDisplayText(alert, category)', () => {
|
|||
|
||||
it('should display rule name when alert category is process', () => {
|
||||
expect(getAlertCategoryDisplayText(mockAlerts[0], ProcessEventAlertCategory.process)).toEqual(
|
||||
undefined
|
||||
''
|
||||
);
|
||||
});
|
||||
|
||||
it('should display rule name when alert category is undefined', () => {
|
||||
expect(getAlertCategoryDisplayText(mockAlerts[0], undefined)).toEqual(undefined);
|
||||
expect(getAlertCategoryDisplayText(mockAlerts[0], undefined)).toEqual('');
|
||||
});
|
||||
|
||||
it('should display rule name when file path is undefined', () => {
|
||||
const fileAlert = { ...mockFileAlert, file: {} };
|
||||
expect(getAlertCategoryDisplayText(fileAlert, ProcessEventAlertCategory.file)).toEqual(
|
||||
undefined
|
||||
);
|
||||
expect(getAlertCategoryDisplayText(fileAlert, ProcessEventAlertCategory.file)).toEqual('');
|
||||
});
|
||||
it('should display rule name when destination address is undefined and alert category is network', () => {
|
||||
const networkAlert = { ...mockNetworkAlert, destination: undefined };
|
||||
expect(getAlertCategoryDisplayText(networkAlert, ProcessEventAlertCategory.network)).toEqual(
|
||||
undefined
|
||||
''
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,7 +19,7 @@ export const getAlertCategoryDisplayText = (alert: ProcessEvent, category: strin
|
|||
if (filePath && category === ProcessEventAlertCategory.file) return dataOrDash(filePath);
|
||||
if (destination?.address && category === ProcessEventAlertCategory.network)
|
||||
return dataOrDash(getAlertNetworkDisplay(destination));
|
||||
return;
|
||||
return '';
|
||||
};
|
||||
|
||||
export const getAlertNetworkDisplay = (destination: ProcessEventIPAddress) => {
|
||||
|
|
|
@ -31372,7 +31372,6 @@
|
|||
"xpack.securitySolution.kpiUsers.totalUsers.title": "Utilisateurs",
|
||||
"xpack.securitySolution.kubernetes.columnContainer": "Conteneur",
|
||||
"xpack.securitySolution.kubernetes.columnEntryType": "Type d’entrée",
|
||||
"xpack.securitySolution.kubernetes.columnEntryUser": "Utilisateur d’entrée de session",
|
||||
"xpack.securitySolution.kubernetes.columnExecutable": "Leader de session",
|
||||
"xpack.securitySolution.kubernetes.columnInteractive": "Interactivité",
|
||||
"xpack.securitySolution.kubernetes.columnNode": "Nœud",
|
||||
|
@ -32041,7 +32040,6 @@
|
|||
"xpack.securitySolution.selector.summaryView.options.summaryView.description": "Afficher un rendu du flux d'événements pour chaque alerte",
|
||||
"xpack.securitySolution.sessionsView.columnEntrySourceIp": "IP source",
|
||||
"xpack.securitySolution.sessionsView.columnEntryType": "Type",
|
||||
"xpack.securitySolution.sessionsView.columnEntryUser": "Utilisateur",
|
||||
"xpack.securitySolution.sessionsView.columnExecutable": "Exécutable",
|
||||
"xpack.securitySolution.sessionsView.columnHostName": "Nom d'hôte",
|
||||
"xpack.securitySolution.sessionsView.columnInteractive": "Interactif",
|
||||
|
|
|
@ -31351,7 +31351,6 @@
|
|||
"xpack.securitySolution.kpiUsers.totalUsers.title": "ユーザー",
|
||||
"xpack.securitySolution.kubernetes.columnContainer": "コンテナー",
|
||||
"xpack.securitySolution.kubernetes.columnEntryType": "エントリタイプ",
|
||||
"xpack.securitySolution.kubernetes.columnEntryUser": "セッションエントリユーザー",
|
||||
"xpack.securitySolution.kubernetes.columnExecutable": "セッションリーダー",
|
||||
"xpack.securitySolution.kubernetes.columnInteractive": "インタラクティブ",
|
||||
"xpack.securitySolution.kubernetes.columnNode": "ノード",
|
||||
|
@ -32020,7 +32019,6 @@
|
|||
"xpack.securitySolution.selector.summaryView.options.summaryView.description": "各アラートのイベントフローのレンダリングを表示",
|
||||
"xpack.securitySolution.sessionsView.columnEntrySourceIp": "ソース IP",
|
||||
"xpack.securitySolution.sessionsView.columnEntryType": "型",
|
||||
"xpack.securitySolution.sessionsView.columnEntryUser": "ユーザー",
|
||||
"xpack.securitySolution.sessionsView.columnExecutable": "実行ファイル",
|
||||
"xpack.securitySolution.sessionsView.columnHostName": "ホスト名",
|
||||
"xpack.securitySolution.sessionsView.columnInteractive": "インタラクティブ",
|
||||
|
|
|
@ -31367,7 +31367,6 @@
|
|||
"xpack.securitySolution.kpiUsers.totalUsers.title": "用户",
|
||||
"xpack.securitySolution.kubernetes.columnContainer": "容器",
|
||||
"xpack.securitySolution.kubernetes.columnEntryType": "条目类型",
|
||||
"xpack.securitySolution.kubernetes.columnEntryUser": "会话条目用户",
|
||||
"xpack.securitySolution.kubernetes.columnExecutable": "会话 Leader",
|
||||
"xpack.securitySolution.kubernetes.columnInteractive": "交互性",
|
||||
"xpack.securitySolution.kubernetes.columnNode": "节点",
|
||||
|
@ -32036,7 +32035,6 @@
|
|||
"xpack.securitySolution.selector.summaryView.options.summaryView.description": "查看每个告警的事件渲染",
|
||||
"xpack.securitySolution.sessionsView.columnEntrySourceIp": "源 IP",
|
||||
"xpack.securitySolution.sessionsView.columnEntryType": "类型",
|
||||
"xpack.securitySolution.sessionsView.columnEntryUser": "用户",
|
||||
"xpack.securitySolution.sessionsView.columnExecutable": "可执行",
|
||||
"xpack.securitySolution.sessionsView.columnHostName": "主机名",
|
||||
"xpack.securitySolution.sessionsView.columnInteractive": "交互",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue