forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Security Solution] Fix non-responsive rule details page (elastic#187953
) **Resolves:** elastic#177734 ## Summary This PR fixes a non-responsive rule details page under non default space. ## Details **[Security Solution] Rule details page is not responsive and leads to page crash for rule in non-default spaces [elastic#177734](elastic#177734 resurfaced back. Investigation has show that **[Security Solution] Remove usage of deprecated React rendering utilities [elastic#181099](elastic#181099 is the cause. The problem is quite subtle to comprehend it just by looking at the code. In fact it boils down to an unstable `useAsync()` hook dependency. Every re-render `useAsync()` resolves a promise causing an additional re-render to show updated results and the cycle repeats. Such hook is used in `x-pack/plugins/security_solution/public/common/components/visualization_actions/actions.tsx` ```ts const panels = useAsync( () => buildContextMenuForActions({ actions: contextMenuActions.map((action) => ({ action, context: {}, trigger: VISUALIZATION_CONTEXT_MENU_TRIGGER, })), }), [contextMenuActions] ); ``` where `contextMenuActions` is an unstable dependency. This is the case due to refactoring to `useSaveToLibrary()` hook by **[Security Solution] Remove usage of deprecated React rendering utilities [elastic#181099](elastic#181099 which started retuning a new object every render. The dependency chain is `contextMenuActions` -> `useActions()` -> `useSaveToLibrary()`. The actual fix is to replace ```ts const { lens, ...startServices } = useKibana().services; ``` with ```ts const startServices = useKibana().services; ``` Since `startServices` is used as a hook dependency it must be stable. A rest property in object destruction expression is always a new object and can't be used as a dependency as is. Using stable `useKibana().services` fixes the problem. (cherry picked from commit 8a539a8)
- Loading branch information
Showing
7 changed files
with
192 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
84 changes: 84 additions & 0 deletions
84
...press/cypress/e2e/detection_response/rule_management/rule_details/non_default_space.cy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* | ||
* 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 { createRule } from '../../../../tasks/api_calls/rules'; | ||
import { ruleFields } from '../../../../data/detection_engine'; | ||
import { getExistingRule, getNewRule } from '../../../../objects/rule'; | ||
|
||
import { RULE_NAME_HEADER, RULE_SWITCH } from '../../../../screens/rule_details'; | ||
|
||
import { createTimeline } from '../../../../tasks/api_calls/timelines'; | ||
import { deleteAlertsAndRules, deleteConnectors } from '../../../../tasks/api_calls/common'; | ||
import { login } from '../../../../tasks/login'; | ||
import { activateSpace, getSpaceUrl } from '../../../../tasks/space'; | ||
import { visit } from '../../../../tasks/navigation'; | ||
import { ruleDetailsUrl } from '../../../../urls/rule_details'; | ||
|
||
describe('Non-default space rule detail page', { tags: ['@ess'] }, function () { | ||
const SPACE_ID = 'test'; | ||
|
||
beforeEach(() => { | ||
login(); | ||
activateSpace(SPACE_ID); | ||
deleteAlertsAndRules(); | ||
deleteConnectors(); | ||
createTimeline().then((response) => { | ||
createRule({ | ||
...getNewRule({ | ||
rule_id: 'rulez', | ||
description: ruleFields.ruleDescription, | ||
name: ruleFields.ruleName, | ||
severity: ruleFields.ruleSeverity, | ||
risk_score: ruleFields.riskScore, | ||
tags: ruleFields.ruleTags, | ||
false_positives: ruleFields.falsePositives, | ||
note: ruleFields.investigationGuide, | ||
timeline_id: response.body.data.persistTimeline.timeline.savedObjectId, | ||
timeline_title: response.body.data.persistTimeline.timeline.title ?? '', | ||
interval: ruleFields.ruleInterval, | ||
from: `now-1h`, | ||
query: ruleFields.ruleQuery, | ||
enabled: false, | ||
max_signals: 500, | ||
threat: [ | ||
{ | ||
...ruleFields.threat, | ||
technique: [ | ||
{ | ||
...ruleFields.threatTechnique, | ||
subtechnique: [ruleFields.threatSubtechnique], | ||
}, | ||
], | ||
}, | ||
], | ||
}), | ||
}).then((rule) => { | ||
cy.wrap(rule.body.id).as('ruleId'); | ||
}); | ||
}); | ||
}); | ||
|
||
it('Check responsiveness by enabling/disabling the rule', function () { | ||
visit(getSpaceUrl(SPACE_ID, ruleDetailsUrl(this.ruleId))); | ||
cy.get(RULE_NAME_HEADER).should('contain', ruleFields.ruleName); | ||
|
||
cy.intercept( | ||
'POST', | ||
getSpaceUrl(SPACE_ID, '/api/detection_engine/rules/_bulk_action?dry_run=false') | ||
).as('bulk_action'); | ||
cy.get(RULE_SWITCH).should('be.visible'); | ||
cy.get(RULE_SWITCH).click(); | ||
cy.wait('@bulk_action').then(({ response }) => { | ||
cy.wrap(response?.statusCode).should('eql', 200); | ||
cy.wrap(response?.body.attributes.results.updated[0].max_signals).should( | ||
'eql', | ||
getExistingRule().max_signals | ||
); | ||
cy.wrap(response?.body.attributes.results.updated[0].enabled).should('eql', true); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
x-pack/test/security_solution_cypress/cypress/tasks/space.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/* | ||
* 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 { API_HEADERS } from './api_calls/common'; | ||
|
||
/** | ||
* Creates a space and sets it as the current one | ||
*/ | ||
export function activateSpace(spaceId: string): void { | ||
const baseUrl = Cypress.config().baseUrl; | ||
if (!baseUrl) { | ||
throw Error(`Cypress config baseUrl not set!`); | ||
} | ||
|
||
cy.request({ | ||
url: `${baseUrl}/api/spaces/space`, | ||
method: 'POST', | ||
body: { | ||
id: spaceId, | ||
name: spaceId, | ||
}, | ||
headers: API_HEADERS, | ||
// For the majority cases the specified space already exists and | ||
// this request would fail. To avoid condition logic and an extra | ||
// request to check for space existence it fails silently. | ||
// | ||
// While it will make errors less transparent when a user doesn't | ||
// have credentials to create spaces. But it's a trade off for now | ||
// choosing simplicity over errors transparency. | ||
failOnStatusCode: false, | ||
}); | ||
|
||
cy.setCurrentSpace(spaceId); | ||
} | ||
|
||
/** | ||
* Constructs a space aware url | ||
*/ | ||
export function getSpaceUrl(spaceId: string, url: string): string { | ||
return spaceId ? `/s/${spaceId}${url}` : url; | ||
} |