[Fleet] Use a scopped SO client in agent policies handler to avoid spaces divulgation (#211506)

This commit is contained in:
Nicolas Chaulet 2025-02-19 12:24:37 -05:00 committed by GitHub
parent 5908bf4195
commit 0ae316f39c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 60 additions and 14 deletions

View file

@ -61,6 +61,7 @@ import { AgentPolicyNotFoundError, FleetUnauthorizedError, FleetError } from '..
import { createAgentPolicyWithPackages } from '../../services/agent_policy_create';
import { updateAgentPolicySpaces } from '../../services/spaces/agent_policy';
import { packagePolicyToSimplifiedPackagePolicy } from '../../../common/services/simplified_package_policy_helper';
import { FLEET_API_PRIVILEGES } from '../../constants/api_privileges';
import { getAutoUpgradeAgentsStatus } from '../../services/agents';
export async function populateAssignedAgentsCount(
@ -133,8 +134,17 @@ export const getAgentPoliciesHandler: FleetRequestHandler<
TypeOf<typeof GetAgentPoliciesRequestSchema.query>
> = async (context, request, response) => {
const [coreContext, fleetContext] = await Promise.all([context.core, context.fleet]);
const soClient = fleetContext.internalSoClient;
const authzFleetReadAgentPolicies =
request.authzResult?.[FLEET_API_PRIVILEGES.AGENT_POLICIES.READ] === true;
const authzFleetAgentRead = request.authzResult?.[FLEET_API_PRIVILEGES.AGENTS.READ] === true;
const soClient =
authzFleetReadAgentPolicies || authzFleetAgentRead
? coreContext.savedObjects.client
: fleetContext.internalSoClient;
const esClient = coreContext.elasticsearch.client.asInternalUser;
const {
full: withPackagePolicies = false,
noAgentCount,
@ -142,7 +152,7 @@ export const getAgentPoliciesHandler: FleetRequestHandler<
format,
...restOfQuery
} = request.query;
if (!fleetContext.authz.fleet.readAgentPolicies && withPackagePolicies) {
if (!authzFleetReadAgentPolicies && withPackagePolicies) {
throw new FleetUnauthorizedError(
'full query parameter require agent policies read permissions'
);
@ -155,11 +165,11 @@ export const getAgentPoliciesHandler: FleetRequestHandler<
let { items } = agentPoliciesResponse;
const { total, page, perPage } = agentPoliciesResponse;
if (fleetContext.authz.fleet.readAgents && (noAgentCount === false || withAgentCount)) {
if (authzFleetAgentRead && (noAgentCount === false || withAgentCount)) {
await populateAssignedAgentsCount(fleetContext.agentClient.asCurrentUser, items);
}
if (!fleetContext.authz.fleet.readAgentPolicies) {
if (!authzFleetReadAgentPolicies) {
items = items.map(sanitizeItemForReadAgentOnly);
} else if (withPackagePolicies && format === inputsFormat.Simplified) {
items.map((item) => {
@ -190,10 +200,18 @@ export const bulkGetAgentPoliciesHandler: FleetRequestHandler<
TypeOf<typeof BulkGetAgentPoliciesRequestSchema.body>
> = async (context, request, response) => {
try {
const fleetContext = await context.fleet;
const soClient = fleetContext.internalSoClient;
const [coreContext, fleetContext] = await Promise.all([context.core, context.fleet]);
const authzFleetReadAgentPolicies =
request.authzResult?.[FLEET_API_PRIVILEGES.AGENT_POLICIES.READ] === true;
const authzFleetAgentRead = request.authzResult?.[FLEET_API_PRIVILEGES.AGENTS.READ] === true;
const soClient =
authzFleetReadAgentPolicies || authzFleetAgentRead
? coreContext.savedObjects.client
: fleetContext.internalSoClient;
const { full: withPackagePolicies = false, ignoreMissing = false, ids } = request.body;
if (!fleetContext.authz.fleet.readAgentPolicies && withPackagePolicies) {
if (!authzFleetReadAgentPolicies && withPackagePolicies) {
throw new FleetUnauthorizedError(
'full query parameter require agent policies read permissions'
);
@ -202,7 +220,7 @@ export const bulkGetAgentPoliciesHandler: FleetRequestHandler<
withPackagePolicies,
ignoreMissing,
});
if (!fleetContext.authz.fleet.readAgentPolicies) {
if (!authzFleetReadAgentPolicies) {
items = items.map(sanitizeItemForReadAgentOnly);
} else if (withPackagePolicies && request.query.format === inputsFormat.Simplified) {
items.map((item) => {
@ -221,7 +239,7 @@ export const bulkGetAgentPoliciesHandler: FleetRequestHandler<
const body: BulkGetAgentPoliciesResponse = {
items,
};
if (fleetContext.authz.fleet.readAgents) {
if (authzFleetAgentRead) {
await populateAssignedAgentsCount(fleetContext.agentClient.asCurrentUser, items);
}

View file

@ -39,6 +39,7 @@ export default function (providerContext: FtrProviderContext) {
let defaultSpacePolicy1: CreateAgentPolicyResponse;
let spaceTest1Policy1: CreateAgentPolicyResponse;
let spaceTest1Policy2: CreateAgentPolicyResponse;
let defaultAndTestSpacePolicy: CreateAgentPolicyResponse;
before(async () => {
await setupTestUsers(getService('security'), true);
@ -51,16 +52,24 @@ export default function (providerContext: FtrProviderContext) {
await apiClient.postEnableSpaceAwareness();
const [_defaultSpacePolicy1, _spaceTest1Policy1, _spaceTest1Policy2] = await Promise.all([
await spaces.createTestSpace(TEST_SPACE_1);
const [
_defaultSpacePolicy1,
_spaceTest1Policy1,
_spaceTest1Policy2,
_defaultAndTestSpacePolicy,
] = await Promise.all([
apiClient.createAgentPolicy(),
apiClient.createAgentPolicy(TEST_SPACE_1),
apiClient.createAgentPolicy(TEST_SPACE_1),
apiClient.createAgentPolicy(undefined, {
space_ids: ['default', TEST_SPACE_1],
}),
]);
defaultSpacePolicy1 = _defaultSpacePolicy1;
spaceTest1Policy1 = _spaceTest1Policy1;
spaceTest1Policy2 = _spaceTest1Policy2;
await spaces.createTestSpace(TEST_SPACE_1);
defaultAndTestSpacePolicy = _defaultAndTestSpacePolicy;
});
after(async () => {
@ -74,20 +83,31 @@ export default function (providerContext: FtrProviderContext) {
describe('GET /agent_policies', () => {
it('should return policies in a specific space', async () => {
const agentPolicies = await apiClient.getAgentPolicies(TEST_SPACE_1);
expect(agentPolicies.total).to.eql(2);
expect(agentPolicies.total).to.eql(3);
const policyIds = agentPolicies.items?.map((item) => item.id);
expect(policyIds).to.contain(spaceTest1Policy1.item.id);
expect(policyIds).to.contain(spaceTest1Policy2.item.id);
expect(policyIds).to.contain(defaultAndTestSpacePolicy.item.id);
expect(policyIds).not.to.contain(defaultSpacePolicy1.item.id);
});
it('should return policies in default space', async () => {
const agentPolicies = await apiClient.getAgentPolicies();
expect(agentPolicies.total).to.eql(1);
expect(agentPolicies.total).to.eql(2);
const policyIds = agentPolicies.items?.map((item) => item.id);
expect(policyIds).not.to.contain(spaceTest1Policy1.item.id);
expect(policyIds).not.contain(spaceTest1Policy2.item.id);
expect(policyIds).to.contain(defaultSpacePolicy1.item.id);
expect(policyIds).to.contain(defaultAndTestSpacePolicy.item.id);
});
it('should return only spaces user can access', async () => {
const agentPolicies = await apiClientDefaultSpaceOnly.getAgentPolicies();
expect(
agentPolicies.items.find((item) => item.id === defaultAndTestSpacePolicy.item.id)
?.space_ids
).to.eql(['default', '?']);
});
});
@ -104,6 +124,14 @@ export default function (providerContext: FtrProviderContext) {
apiClient.getAgentPolicy(defaultSpacePolicy1.item.id, TEST_SPACE_1)
);
});
it('should return only spaces user can access', async () => {
const policyRes = await apiClientDefaultSpaceOnly.getAgentPolicy(
defaultAndTestSpacePolicy.item.id
);
expect(policyRes.item.space_ids).to.eql(['default', '?']);
});
});
describe('POST /agent_policies', () => {