Skip to content

Commit f6eec11

Browse files
[8.19] [EDR Workflows][Fleet] Disable ATP only on endpoint package updates (elastic#219224) (elastic#219372)
# Backport This will backport the following commits from `main` to `8.19`: - [[EDR Workflows][Fleet] Disable ATP only on endpoint package updates (elastic#219224)](elastic#219224) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Konrad Szwarc","email":"[email protected]"},"sourceCommit":{"committedDate":"2025-04-28T10:19:51Z","message":"[EDR Workflows][Fleet] Disable ATP only on endpoint package updates (elastic#219224)\n\nThis PR fixes an issue where Agent Tamper Protection could be disabled\nwhen updating integration packages other than Endpoint.\n\nExtended test coverage.\n\n`8.17` backport PR https://github.com/elastic/kibana/pull/219225","sha":"2ff21aa7aab74e88801c60df89ff67007b454a2b","branchLabelMapping":{"^v9.1.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:Fleet","Team:Defend Workflows","ci:all-cypress-suites","backport:version","v8.17.0","v8.18.0","v9.1.0","v8.19.0","v9.0.1"],"title":"[EDR Workflows][Fleet] Disable ATP only on endpoint package updates","number":219224,"url":"https://github.com/elastic/kibana/pull/219224","mergeCommit":{"message":"[EDR Workflows][Fleet] Disable ATP only on endpoint package updates (elastic#219224)\n\nThis PR fixes an issue where Agent Tamper Protection could be disabled\nwhen updating integration packages other than Endpoint.\n\nExtended test coverage.\n\n`8.17` backport PR https://github.com/elastic/kibana/pull/219225","sha":"2ff21aa7aab74e88801c60df89ff67007b454a2b"}},"sourceBranch":"main","suggestedTargetBranches":["8.17","8.18","8.19"],"targetPullRequestStates":[{"branch":"8.17","label":"v8.17.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.18","label":"v8.18.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/219224","number":219224,"mergeCommit":{"message":"[EDR Workflows][Fleet] Disable ATP only on endpoint package updates (elastic#219224)\n\nThis PR fixes an issue where Agent Tamper Protection could be disabled\nwhen updating integration packages other than Endpoint.\n\nExtended test coverage.\n\n`8.17` backport PR https://github.com/elastic/kibana/pull/219225","sha":"2ff21aa7aab74e88801c60df89ff67007b454a2b"}},{"branch":"8.19","label":"v8.19.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"9.0","label":"v9.0.1","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/219368","number":219368,"state":"OPEN"}]}] BACKPORT--> --------- Co-authored-by: kibanamachine <[email protected]>
1 parent fad7c73 commit f6eec11

File tree

2 files changed

+130
-13
lines changed

2 files changed

+130
-13
lines changed

x-pack/platform/plugins/shared/fleet/server/services/package_policy.test.ts

Lines changed: 126 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,7 +1826,7 @@ describe('Package policy service', () => {
18261826
mockAgentPolicyService.bumpRevision.mockReset();
18271827
});
18281828

1829-
const generateAttributes = (overrides: Record<string, string | string[]> = {}) => ({
1829+
const generateAttributes = (overrides: Record<string, unknown> = {}) => ({
18301830
name: 'endpoint-12',
18311831
description: '',
18321832
namespace: 'default',
@@ -1841,7 +1841,7 @@ describe('Package policy service', () => {
18411841
...overrides,
18421842
});
18431843

1844-
const generateSO = (overrides: Record<string, string | string[]> = {}) => ({
1844+
const generateSO = (overrides: Record<string, unknown> = {}) => ({
18451845
id: 'existing-package-policy',
18461846
type: 'ingest-package-policies',
18471847
references: [],
@@ -1854,18 +1854,17 @@ describe('Package policy service', () => {
18541854
const setupSOClientMocks = (
18551855
savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>,
18561856
initialPolicies: string[],
1857-
updatesPolicies: string[]
1857+
updatesPolicies: string[],
1858+
overrides: Record<string, unknown> = {}
18581859
) => {
18591860
savedObjectsClient.get.mockResolvedValueOnce(
1860-
generateSO({ name: 'test-package-policy', policy_ids: initialPolicies })
1861+
generateSO({ name: 'test-package-policy', policy_ids: initialPolicies, ...overrides })
18611862
);
1862-
18631863
savedObjectsClient.get.mockResolvedValueOnce(
1864-
generateSO({ name: 'test-package-policy-1', policy_ids: updatesPolicies })
1864+
generateSO({ name: 'test-package-policy-1', policy_ids: updatesPolicies, ...overrides })
18651865
);
1866-
18671866
savedObjectsClient.get.mockResolvedValueOnce(
1868-
generateSO({ name: 'test-package-policy-1', policy_ids: updatesPolicies })
1867+
generateSO({ name: 'test-package-policy-1', policy_ids: updatesPolicies, ...overrides })
18691868
);
18701869
};
18711870

@@ -2000,6 +1999,42 @@ describe('Package policy service', () => {
20001999
);
20012000
});
20022001
});
2002+
2003+
it('should never remove protections for non-endpoint packages, regardless of policy_ids change', async () => {
2004+
const savedObjectsClient = savedObjectsClientMock.create();
2005+
const elasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
2006+
const testPolicyIds = ['test-agent-policy-1', 'test-agent-policy-2'];
2007+
2008+
// Ensure both old and new policies are NOT endpoint
2009+
setupSOClientMocks(
2010+
savedObjectsClient,
2011+
testPolicyIds,
2012+
[],
2013+
// Add package override for both old and new policies
2014+
{ package: { name: 'not-endpoint', title: 'Other', version: '1.0.0' } }
2015+
);
2016+
2017+
await packagePolicyService.update(
2018+
savedObjectsClient,
2019+
elasticsearchClient,
2020+
generateSO({
2021+
package: { name: 'not-endpoint', title: 'Other', version: '1.0.0' },
2022+
}).id,
2023+
generateAttributes({
2024+
policy_ids: [],
2025+
name: 'test-package-policy-1',
2026+
package: { name: 'not-endpoint', title: 'Other', version: '1.0.0' },
2027+
})
2028+
);
2029+
2030+
const calls = mockAgentPolicyService.bumpRevision.mock.calls;
2031+
expect(calls).toHaveLength(testPolicyIds.length);
2032+
2033+
calls.forEach((call, idx) => {
2034+
expect(call[2]).toContain(`test-agent-policy-${idx + 1}`);
2035+
expect(call[3]).toMatchObject({ removeProtection: false });
2036+
});
2037+
});
20032038
});
20042039
});
20052040

@@ -2812,7 +2847,7 @@ describe('Package policy service', () => {
28122847
beforeEach(() => {
28132848
mockAgentPolicyService.bumpRevision.mockReset();
28142849
});
2815-
const generateAttributes = (overrides: Record<string, string | string[]> = {}) => ({
2850+
const generateAttributes = (overrides: Record<string, unknown> = {}) => ({
28162851
name: 'endpoint-12',
28172852
description: '',
28182853
namespace: 'default',
@@ -2827,7 +2862,7 @@ describe('Package policy service', () => {
28272862
...overrides,
28282863
});
28292864

2830-
const generateSO = (overrides: Record<string, string | string[]> = {}) => ({
2865+
const generateSO = (overrides: Record<string, unknown> = {}) => ({
28312866
id: 'existing-package-policy',
28322867
type: 'ingest-package-policies',
28332868
references: [],
@@ -3025,6 +3060,87 @@ describe('Package policy service', () => {
30253060
);
30263061
});
30273062
});
3063+
it('should never remove protections for non-endpoint packages, regardless of policy_ids change', async () => {
3064+
const savedObjectsClient = savedObjectsClientMock.create();
3065+
3066+
// All non-endpoint policies
3067+
const nonEndpointPoliciesSO = [
3068+
generateSO({
3069+
name: 'not-endpoint-policy',
3070+
policy_ids: ['test-agent-policy-1'],
3071+
id: 'not-endpoint-1',
3072+
package: { name: 'not-endpoint', title: 'Other', version: '1.0.0' },
3073+
}),
3074+
generateSO({
3075+
name: 'not-endpoint-policy-2',
3076+
policy_ids: ['test-agent-policy-2'],
3077+
id: 'not-endpoint-2',
3078+
package: { name: 'not-endpoint', title: 'Other', version: '1.0.0' },
3079+
}),
3080+
];
3081+
3082+
const nonEndpointTestedPolicies = nonEndpointPoliciesSO.map((so) => so.attributes);
3083+
3084+
setupSOClientMocks(savedObjectsClient, nonEndpointPoliciesSO);
3085+
3086+
const elasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
3087+
3088+
await callPackagePolicyServiceBulkUpdate(
3089+
savedObjectsClient,
3090+
elasticsearchClient,
3091+
nonEndpointTestedPolicies
3092+
);
3093+
3094+
const calls = mockAgentPolicyService.bumpRevision.mock.calls;
3095+
expect(calls).toHaveLength(2);
3096+
calls.forEach((call, idx) => {
3097+
expect(call[2]).toContain(`test-agent-policy-${idx + 1}`);
3098+
expect(call[3]).toMatchObject({ removeProtection: false });
3099+
});
3100+
});
3101+
3102+
it('should only set removeProtection for endpoint package in a mixed bulkUpdate', async () => {
3103+
const savedObjectsClient = savedObjectsClientMock.create();
3104+
3105+
const mixedPoliciesSO = [
3106+
generateSO({
3107+
name: 'endpoint-policy',
3108+
policy_ids: ['test-agent-policy-1'],
3109+
id: 'endpoint-1',
3110+
package: { name: 'endpoint', title: 'Elastic Endpoint', version: '0.9.0' },
3111+
}),
3112+
generateSO({
3113+
name: 'not-endpoint-policy',
3114+
policy_ids: ['test-agent-policy-2'],
3115+
id: 'not-endpoint-1',
3116+
package: { name: 'not-endpoint', title: 'Other', version: '1.0.0' },
3117+
}),
3118+
];
3119+
const mixedTestedPolicies = [
3120+
{ ...mixedPoliciesSO[0].attributes, policy_ids: [] }, // endpoint policy IDs removed
3121+
{ ...mixedPoliciesSO[1].attributes, policy_ids: ['test-agent-policy-2'] }, // not-endpoint unchanged
3122+
];
3123+
3124+
setupSOClientMocks(savedObjectsClient, mixedPoliciesSO);
3125+
3126+
const elasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser;
3127+
3128+
await callPackagePolicyServiceBulkUpdate(
3129+
savedObjectsClient,
3130+
elasticsearchClient,
3131+
mixedTestedPolicies
3132+
);
3133+
3134+
const calls = mockAgentPolicyService.bumpRevision.mock.calls;
3135+
expect(calls).toHaveLength(2);
3136+
3137+
// Find by id, not by order
3138+
const endpointCall = calls.find((call) => call[2] === 'test-agent-policy-1');
3139+
const nonEndpointCall = calls.find((call) => call[2] === 'test-agent-policy-2');
3140+
3141+
expect(endpointCall?.[3]).toMatchObject({ removeProtection: true });
3142+
expect(nonEndpointCall?.[3]).toMatchObject({ removeProtection: false });
3143+
});
30283144
});
30293145
});
30303146

x-pack/platform/plugins/shared/fleet/server/services/package_policy.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,14 +1116,15 @@ class PackagePolicyClientImpl implements PackagePolicyClient {
11161116
logger.debug(`Bumping revision of associated agent policies ${associatedPolicyIds}`);
11171117
const bumpPromises = [];
11181118
for (const policyId of associatedPolicyIds) {
1119-
// Check if the agent policy is in both old and updated package policies
1119+
const isEndpointPolicy = newPolicy.package?.name === 'endpoint'; // Check if the agent policy is in both old and updated package policies
11201120
const assignedInOldPolicy = oldPackagePolicy.policy_ids.includes(policyId);
11211121
const assignedInNewPolicy = newPolicy.policy_ids.includes(policyId);
11221122

11231123
// Remove protection if policy is unassigned (in old but not in updated) or policy is assigned (in updated but not in old)
11241124
const removeProtection =
1125-
(assignedInOldPolicy && !assignedInNewPolicy) ||
1126-
(!assignedInOldPolicy && assignedInNewPolicy);
1125+
isEndpointPolicy &&
1126+
((assignedInOldPolicy && !assignedInNewPolicy) ||
1127+
(!assignedInOldPolicy && assignedInNewPolicy));
11271128

11281129
bumpPromises.push(
11291130
agentPolicyService.bumpRevision(soClient, esClient, policyId, {

0 commit comments

Comments
 (0)