[9.0] [Security Solution][Risk Score]Code changes for limiting the transformID length to 36 characters (#213405) (#215267)

# Backport

This will backport the following commits from `main` to `9.0`:
- [[Security Solution][Risk Score]Code changes for limiting the
transformID length to 36 characters
(#213405)](https://github.com/elastic/kibana/pull/213405)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Abhishek
Bhatia","email":"117628830+abhishekbhatia1710@users.noreply.github.com"},"sourceCommit":{"committedDate":"2025-03-20T05:53:55Z","message":"[Security
Solution][Risk Score]Code changes for limiting the transformID length to
36 characters (#213405)\n\n## Summary\n\nThe code changes in this PR
ensure that the transform ID is limited to\n36 characters when creating
or updating the transform for risk-score.\n\nThis adjustment aligns with
ES constraint on transform ID length.\n\n\n## Test Steps\n\n1. Create a
new namespace with a very long name. Ex
:\n`namespace_that_stretches_farther_than_the_universe_and_beyond_like_buzz`\n🚀\n2.
Enable the Risk Score in the new namespace. It should successfully\nget
enabled.\n3. Check the transform that was created (using dev
tools)\n\n```\nGET
_transform/risk_score_latest_transform_*?filter_path=transforms.id,transforms._meta.space_id\n```\n\nOutput
\n\n\n![image](https://github.com/user-attachments/assets/3b5d5e67-cddf-4c6a-b8ff-675517c123b2)\n\n###
Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers
should verify this PR satisfies this list as well.\n\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] If a plugin
configuration key changed, check if it needs to be\nallowlisted in the
cloud and added to the
[docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n-
[x] The PR description includes the appropriate Release Notes
section,\nand the correct `release_note:*` label is applied per
the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n\n---------\n\nCo-authored-by:
Mark Hopkin
<mark.hopkin@elastic.co>","sha":"a3f89ec2c25b1ca6a75a7bf41ac0360a3a887806","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","v9.0.0","Team:Entity
Analytics","backport:version","v9.1.0"],"title":"[Security
Solution][Risk Score]Code changes for limiting the transformID length to
36
characters","number":213405,"url":"https://github.com/elastic/kibana/pull/213405","mergeCommit":{"message":"[Security
Solution][Risk Score]Code changes for limiting the transformID length to
36 characters (#213405)\n\n## Summary\n\nThe code changes in this PR
ensure that the transform ID is limited to\n36 characters when creating
or updating the transform for risk-score.\n\nThis adjustment aligns with
ES constraint on transform ID length.\n\n\n## Test Steps\n\n1. Create a
new namespace with a very long name. Ex
:\n`namespace_that_stretches_farther_than_the_universe_and_beyond_like_buzz`\n🚀\n2.
Enable the Risk Score in the new namespace. It should successfully\nget
enabled.\n3. Check the transform that was created (using dev
tools)\n\n```\nGET
_transform/risk_score_latest_transform_*?filter_path=transforms.id,transforms._meta.space_id\n```\n\nOutput
\n\n\n![image](https://github.com/user-attachments/assets/3b5d5e67-cddf-4c6a-b8ff-675517c123b2)\n\n###
Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers
should verify this PR satisfies this list as well.\n\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] If a plugin
configuration key changed, check if it needs to be\nallowlisted in the
cloud and added to the
[docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n-
[x] The PR description includes the appropriate Release Notes
section,\nand the correct `release_note:*` label is applied per
the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n\n---------\n\nCo-authored-by:
Mark Hopkin
<mark.hopkin@elastic.co>","sha":"a3f89ec2c25b1ca6a75a7bf41ac0360a3a887806"}},"sourceBranch":"main","suggestedTargetBranches":["9.0"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/213405","number":213405,"mergeCommit":{"message":"[Security
Solution][Risk Score]Code changes for limiting the transformID length to
36 characters (#213405)\n\n## Summary\n\nThe code changes in this PR
ensure that the transform ID is limited to\n36 characters when creating
or updating the transform for risk-score.\n\nThis adjustment aligns with
ES constraint on transform ID length.\n\n\n## Test Steps\n\n1. Create a
new namespace with a very long name. Ex
:\n`namespace_that_stretches_farther_than_the_universe_and_beyond_like_buzz`\n🚀\n2.
Enable the Risk Score in the new namespace. It should successfully\nget
enabled.\n3. Check the transform that was created (using dev
tools)\n\n```\nGET
_transform/risk_score_latest_transform_*?filter_path=transforms.id,transforms._meta.space_id\n```\n\nOutput
\n\n\n![image](https://github.com/user-attachments/assets/3b5d5e67-cddf-4c6a-b8ff-675517c123b2)\n\n###
Checklist\n\nCheck the PR satisfies following conditions. \n\nReviewers
should verify this PR satisfies this list as well.\n\n- [x] [Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common scenarios\n- [x] If a plugin
configuration key changed, check if it needs to be\nallowlisted in the
cloud and added to the
[docker\nlist](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)\n-
[x] The PR description includes the appropriate Release Notes
section,\nand the correct `release_note:*` label is applied per
the\n[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)\n\n---------\n\nCo-authored-by:
Mark Hopkin
<mark.hopkin@elastic.co>","sha":"a3f89ec2c25b1ca6a75a7bf41ac0360a3a887806"}}]}]
BACKPORT-->

Co-authored-by: Abhishek Bhatia <117628830+abhishekbhatia1710@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2025-03-20 09:08:02 +01:00 committed by GitHub
parent 12a8f3062c
commit a043700f59
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 40 additions and 5 deletions

View file

@ -12,6 +12,7 @@ describe('getTransformOptions', () => {
const options = getTransformOptions({
dest: 'dest',
source: ['source'],
namespace: 'tests',
});
expect(options).toMatchInlineSnapshot(`
@ -19,6 +20,7 @@ describe('getTransformOptions', () => {
"_meta": Object {
"managed": true,
"managed_by": "security-entity-analytics",
"space_id": "tests",
"version": 3,
},
"dest": Object {

View file

@ -164,9 +164,11 @@ export type TransformOptions = Omit<TransformPutTransformRequest, 'transform_id'
export const getTransformOptions = ({
dest,
source,
namespace,
}: {
dest: string;
source: string[];
namespace: string;
}): Omit<TransformPutTransformRequest, 'transform_id'> => ({
dest: {
index: dest,
@ -206,5 +208,6 @@ export const getTransformOptions = ({
version: 3, // When this field is updated we automatically update the transform
managed: true, // Metadata that identifies the transform. It has no functionality
managed_by: 'security-entity-analytics', // Metadata that identifies the transform. It has no functionality
space_id: namespace, // Metadata that identifies the space where the transform is running. Helps in debugging as the original transformid could be hashed if longer than 64 characters
},
});

View file

@ -45,6 +45,7 @@ const totalFieldsLimit = 1000;
describe('RiskScoreDataClient', () => {
let riskScoreDataClient: RiskScoreDataClient;
let riskScoreDataClientWithNameSpace: RiskScoreDataClient;
let riskScoreDataClientWithLongNameSpace: RiskScoreDataClient;
let mockSavedObjectClient: ReturnType<typeof savedObjectsClientMock.create>;
beforeEach(() => {
@ -60,6 +61,8 @@ describe('RiskScoreDataClient', () => {
riskScoreDataClient = new RiskScoreDataClient(options);
const optionsWithNamespace = { ...options, namespace: 'space-1' };
riskScoreDataClientWithNameSpace = new RiskScoreDataClient(optionsWithNamespace);
const optionsWithLongNamespace = { ...options, namespace: 'a_a-'.repeat(200) };
riskScoreDataClientWithLongNameSpace = new RiskScoreDataClient(optionsWithLongNamespace);
});
afterEach(() => {
@ -103,6 +106,10 @@ describe('RiskScoreDataClient', () => {
assertIndex('space-1');
assertTransform('space-1');
// Space with more than 36 characters
await riskScoreDataClientWithLongNameSpace.init();
assertTransform('a_a-'.repeat(200));
expect(
(createOrUpdateComponentTemplate as jest.Mock).mock.lastCall[0].template.template
).toMatchSnapshot();
@ -445,7 +452,7 @@ const assertTransform = (namespace: string) => {
field: '@timestamp',
},
},
transform_id: `risk_score_latest_transform_${namespace}`,
transform_id: transforms.getLatestTransformId(namespace),
settings: {
unattended: true,
},
@ -453,6 +460,7 @@ const assertTransform = (namespace: string) => {
version: 3,
managed: true,
managed_by: 'security-entity-analytics',
space_id: namespace,
},
},
});

View file

@ -201,6 +201,7 @@ export class RiskScoreDataClient {
...getTransformOptions({
dest: getRiskScoreLatestIndex(namespace),
source: [indexPatterns.alias],
namespace: this.options.namespace,
}),
},
});
@ -367,6 +368,7 @@ export class RiskScoreDataClient {
...getTransformOptions({
dest: getRiskScoreLatestIndex(namespace),
source: [indexPatterns.alias],
namespace: this.options.namespace,
}),
},
});

View file

@ -19,6 +19,7 @@ import {
scheduleLatestTransformNow,
scheduleTransformNow,
upgradeLatestTransformIfNeeded,
getLatestTransformId,
} from './transforms';
const transformId = 'test_transform_id';
@ -48,6 +49,7 @@ const timeSeriesIndex = getRiskScoreTimeSeriesIndex('tests');
const transformConfig = getTransformOptions({
dest: latestIndex,
source: [timeSeriesIndex],
namespace: 'tests',
});
const updatedTransformsMock = {
@ -206,4 +208,12 @@ describe('transforms utils', () => {
expect(esClient.transform.putTransform).toHaveBeenCalled();
});
});
describe('checkTransformNameLength', () => {
it('should limit the length of tranformId to less than or equal 64 characters', async () => {
const longTransformId = 'a_a-'.repeat(1000);
const response = await getLatestTransformId(longTransformId);
expect(response.length).toBeLessThanOrEqual(36);
});
});
});

View file

@ -14,6 +14,7 @@ import type {
TransformGetTransformStatsTransformStats,
AcknowledgedResponseBase,
} from '@elastic/elasticsearch/lib/api/types';
import murmurhash from 'murmurhash';
import {
getRiskScoreLatestIndex,
getRiskScoreTimeSeriesIndex,
@ -116,8 +117,15 @@ export const reinstallTransform = async ({
});
};
export const getLatestTransformId = (namespace: string): string =>
`risk_score_latest_transform_${namespace}`;
export const getLatestTransformId = (namespace: string): string => {
const maxTransformId = 64;
const prefix = `risk_score_latest_transform_`;
const fullName = `${prefix}${namespace}`;
const processedNamespace =
fullName.length > maxTransformId ? murmurhash.v3(namespace).toString(16) : namespace;
return `${prefix}${processedNamespace}`;
};
const hasTransformStarted = (transformStats: TransformGetTransformStatsTransformStats): boolean => {
return transformStats.state === 'indexing' || transformStats.state === 'started';
@ -174,6 +182,7 @@ export const upgradeLatestTransformIfNeeded = async ({
const newConfig = getTransformOptions({
dest: latestIndex,
source: [timeSeriesIndex],
namespace,
});
if (isTransformOutdated(response.transforms[0], newConfig)) {

View file

@ -8,6 +8,7 @@
import expect from '@kbn/expect';
import { riskEngineConfigurationTypeName } from '@kbn/security-solution-plugin/server/lib/entity_analytics/risk_engine/saved_object';
import { getLatestTransformId } from '@kbn/security-solution-plugin/server/lib/entity_analytics/utils/transforms';
import { riskEngineRouteHelpersFactory } from '../../utils';
import { FtrProviderContext } from '../../../../ftr_provider_context';
@ -70,7 +71,7 @@ export default ({ getService }: FtrProviderContext) => {
const indexTemplateName = '.risk-score.risk-score-default-index-template';
const dataStreamName = 'risk-score.risk-score-default';
const latestIndexName = 'risk-score.risk-score-latest-default';
const transformId = 'risk_score_latest_transform_default';
const transformId = getLatestTransformId('default');
const defaultPipeline =
'entity_analytics_create_eventIngest_from_timestamp-pipeline-default';
@ -350,7 +351,7 @@ export default ({ getService }: FtrProviderContext) => {
const indexTemplateName = `.risk-score.risk-score-${customSpaceName}-index-template`;
const dataStreamName = `risk-score.risk-score-${customSpaceName}`;
const latestIndexName = `risk-score.risk-score-latest-${customSpaceName}`;
const transformId = `risk_score_latest_transform_${customSpaceName}`;
const transformId = getLatestTransformId(customSpaceName);
const defaultPipeline = `entity_analytics_create_eventIngest_from_timestamp-pipeline-${customSpaceName}`;
await riskEngineRoutesWithNamespace.init();