Skip to content

Commit bcdf6ee

Browse files
[security-defend-workflows] Roll forward PIT id in osquery paging (#252535)
## Summary - Thread the latest PIT id through osquery agent group paging and close with the final refreshed id. ## Test plan - `yarn test:jest x-pack/platform/plugins/shared/osquery/server/lib/parse_agent_groups.test.ts` Refs: ralph issue-236; #252458 Made with [Cursor](https://cursor.com) Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent cf216d8 commit bcdf6ee

2 files changed

Lines changed: 37 additions & 18 deletions

File tree

x-pack/platform/plugins/shared/osquery/server/lib/parse_agent_groups.test.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ function createPaginatedMockResponse(totalAgents: number, chunkSize = 9000) {
1919
return jest.fn(
2020
({
2121
searchAfter,
22+
pitId,
2223
}: {
2324
searchAfter?: SortResults;
2425
kuery?: string;
2526
showInactive?: boolean;
2627
pitId?: string;
2728
perPage?: number;
2829
page?: number;
29-
}): Promise<{ agents: Agent[]; total: number }> => {
30+
}): Promise<{ agents: Agent[]; total: number; pit?: string }> => {
3031
const start = searchAfter ? (searchAfter[0] as number) + 1 : 0;
3132
const end = Math.min(start + chunkSize, agentIds.length);
3233
const chunk = agentIds.slice(start, end);
@@ -37,6 +38,7 @@ function createPaginatedMockResponse(totalAgents: number, chunkSize = 9000) {
3738
sort: [start + index],
3839
})) as Agent[],
3940
total: agentIds.length,
41+
...(pitId ? { pit: `${pitId}-next` } : {}),
4042
});
4143
}
4244
);
@@ -137,31 +139,39 @@ describe('aggregateResults', () => {
137139
results: generateResults(),
138140
total: 18001,
139141
searchAfter: ['firstSort'],
142+
pitId: 'refreshedPit-1',
140143
})
141144
.mockResolvedValueOnce({
142145
results: generateResults(2),
143146
total: 18001,
144147
searchAfter: ['secondSort'],
148+
pitId: 'refreshedPit-2',
145149
})
146150
.mockResolvedValueOnce({
147151
results: ['result_18001'],
148152
total: 18001,
149153
searchAfter: ['thirdSort'],
154+
pitId: 'refreshedPit-3',
150155
});
151156

152157
const result = await aggregateResults(generatorMock, mockElasticsearchClient, mockContext);
153158
expect(generatorMock).toHaveBeenCalledWith(1, expect.any(Number));
154159
expect(generatorMock).toHaveBeenCalledWith(1, expect.any(Number), undefined, 'mockedPitId');
155-
expect(generatorMock).toHaveBeenCalledWith(2, expect.any(Number), ['firstSort'], 'mockedPitId');
160+
expect(generatorMock).toHaveBeenCalledWith(
161+
2,
162+
expect.any(Number),
163+
['firstSort'],
164+
'refreshedPit-1'
165+
);
156166
expect(generatorMock).toHaveBeenCalledWith(
157167
3,
158168
expect.any(Number),
159169
['secondSort'],
160-
'mockedPitId'
170+
'refreshedPit-2'
161171
);
162172
expect(mockOpenPointInTime).toHaveBeenCalledTimes(1);
163173
expect(mockClosePointInTime).toHaveBeenCalledTimes(1);
164-
expect(mockClosePointInTime).toHaveBeenCalledWith({ id: 'mockedPitId' });
174+
expect(mockClosePointInTime).toHaveBeenCalledWith({ id: 'refreshedPit-3' });
165175
expect(result.length).toEqual(18001);
166176
});
167177
});
@@ -249,11 +259,11 @@ describe('parseAgentSelection', () => {
249259
index: '.fleet-agents',
250260
keep_alive: '10m',
251261
});
252-
expect(mockClosePointInTime).toHaveBeenCalledWith({ id: 'mockedPitId' });
262+
expect(mockClosePointInTime).toHaveBeenCalledWith({ id: 'mockedPitId-next-next' });
253263

254264
const thirdCall = mockAgentService.listAgents.mock.calls[2][0];
255265
expect(thirdCall.searchAfter).toBeDefined();
256-
expect(thirdCall.pitId).toBe('mockedPitId');
266+
expect(thirdCall.pitId).toBe('mockedPitId-next');
257267
});
258268

259269
it('should continue even if PIT close fails', async () => {

x-pack/platform/plugins/shared/osquery/server/lib/parse_agent_groups.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const aggregateResults = async (
2828
perPage: number,
2929
searchAfter?: SortResults,
3030
pitId?: string
31-
) => Promise<{ results: string[]; total: number; searchAfter?: SortResults }>,
31+
) => Promise<{ results: string[]; total: number; searchAfter?: SortResults; pitId?: string }>,
3232
esClient: ElasticsearchClient,
3333
context: OsqueryAppContext
3434
) => {
@@ -39,30 +39,37 @@ export const aggregateResults = async (
3939
// One page only, no need for PIT
4040
results = initialResults;
4141
} else {
42-
const { id: pitId } = await esClient.openPointInTime({
43-
index: AGENTS_INDEX,
44-
keep_alive: '10m',
45-
});
42+
let pitId = (
43+
await esClient.openPointInTime({
44+
index: AGENTS_INDEX,
45+
keep_alive: '10m',
46+
})
47+
).id;
4648
let currentSort: SortResults | undefined;
4749
// Refetch first page with PIT
48-
const { results: pitInitialResults, searchAfter } = await generator(
50+
const {
51+
results: pitInitialResults,
52+
searchAfter,
53+
pitId: returnedPitId,
54+
} = await generator(
4955
1,
5056
PER_PAGE,
5157
currentSort, // No searchAfter for first page, its built based on first page results
5258
pitId
5359
);
5460
results = pitInitialResults;
5561
currentSort = searchAfter;
62+
pitId = returnedPitId ?? pitId;
5663
let currPage = 2;
5764
while (currPage <= totalPages) {
58-
const { results: additionalResults, searchAfter: additionalSearchAfter } = await generator(
59-
currPage++,
60-
PER_PAGE,
61-
currentSort,
62-
pitId
63-
);
65+
const {
66+
results: additionalResults,
67+
searchAfter: additionalSearchAfter,
68+
pitId: additionalPitId,
69+
} = await generator(currPage++, PER_PAGE, currentSort, pitId);
6470
results.push(...additionalResults);
6571
currentSort = additionalSearchAfter;
72+
pitId = additionalPitId ?? pitId;
6673
}
6774

6875
try {
@@ -171,6 +178,7 @@ export const parseAgentSelection = async (
171178
res.agents.length > 0 && res.agents[res.agents.length - 1].sort
172179
? res.agents[res.agents.length - 1].sort
173180
: undefined,
181+
pitId: res.pit,
174182
};
175183
},
176184
esClient,
@@ -208,6 +216,7 @@ export const parseAgentSelection = async (
208216
res.agents.length > 0 && res.agents[res.agents.length - 1].sort
209217
? res.agents[res.agents.length - 1].sort
210218
: undefined,
219+
pitId: res.pit,
211220
};
212221
},
213222
esClient,

0 commit comments

Comments
 (0)