Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cloud Security] Adding Auth check on API #186937

Merged
merged 13 commits into from
Jul 12, 2024
2 changes: 2 additions & 0 deletions x-pack/plugins/cloud_security_posture/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ export const DATA_VIEW_INDEX_PATTERN = 'logs-*';

export const SECURITY_DEFAULT_DATA_VIEW_ID = 'security-solution-default';

export const ALERTS_INDEX_PATTERN = '.alerts-security.alerts-*';

export const CSP_INGEST_TIMESTAMP_PIPELINE = 'cloud_security_posture_add_ingest_timestamp_pipeline';
export const CSP_LATEST_FINDINGS_INGEST_TIMESTAMP_PIPELINE =
'cloud_security_posture_latest_index_add_ingest_timestamp_pipeline';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ export const defineBulkActionCspBenchmarkRulesRoute = (router: CspRouter) =>
.post({
access: 'internal',
path: CSP_BENCHMARK_RULES_BULK_ACTION_ROUTE_PATH,
options: {
tags: ['access:cloud-security-posture-read'],
},
})
.addVersion(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export const defineFindCspBenchmarkRuleRoute = (router: CspRouter) =>
.get({
access: 'internal',
path: FIND_CSP_BENCHMARK_RULE_ROUTE_PATH,
options: {
tags: ['access:cloud-security-posture-read'],
},
})
.addVersion(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export const defineGetCspBenchmarkRulesStatesRoute = (router: CspRouter) =>
.get({
access: 'internal',
path: CSP_GET_BENCHMARK_RULES_STATE_ROUTE_PATH,
options: {
tags: ['access:cloud-security-posture-read'],
},
})
.addVersion(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export const defineGetComplianceDashboardRoute = (router: CspRouter) =>
.get({
access: 'internal',
path: STATS_ROUTE_PATH,
options: {
tags: ['access:cloud-security-posture-read'],
},
})
.addVersion(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ export const defineGetDetectionEngineAlertsStatus = (router: CspRouter) =>
.get({
access: 'internal',
path: GET_DETECTION_RULE_ALERTS_STATUS_PATH,
options: {
tags: ['access:cloud-security-posture-read'],
},
})
.addVersion(
{
Expand Down
11 changes: 2 additions & 9 deletions x-pack/test/cloud_security_posture_api/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { resolve } from 'path';
import type { FtrConfigProviderContext } from '@kbn/test';
import { CLOUD_SECURITY_PLUGIN_VERSION } from '@kbn/cloud-security-posture-plugin/common/constants';

Expand All @@ -15,14 +15,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {

return {
...xpackFunctionalConfig.getAll(),
testFiles: [
require.resolve('./telemetry/telemetry.ts'),
require.resolve('./routes/vulnerabilities_dashboard.ts'),
require.resolve('./routes/stats.ts'),
require.resolve('./routes/csp_benchmark_rules_bulk_update.ts'),
require.resolve('./routes/csp_benchmark_rules_get_states.ts'),
require.resolve('./routes/benchmarks.ts'),
],
testFiles: [resolve(__dirname, './routes')],
junit: {
reportName: 'X-Pack Cloud Security Posture API Tests',
},
Expand Down
56 changes: 55 additions & 1 deletion x-pack/test/cloud_security_posture_api/routes/benchmarks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ import expect from '@kbn/expect';
import Chance from 'chance';
import { CspBenchmarkRule } from '@kbn/cloud-security-posture-plugin/common/types/latest';
import { FtrProviderContext } from '../ftr_provider_context';
import { CspSecurityCommonProvider } from './helper/user_roles_utilites';

const chance = new Chance();

// eslint-disable-next-line import/no-default-export
export default function (providerContext: FtrProviderContext) {
const { getService } = providerContext;

const retry = getService('retry');
const es = getService('es');
const kibanaServer = getService('kibanaServer');
const supertest = getService('supertest');
const log = getService('log');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const cspSecurity = CspSecurityCommonProvider(providerContext);

const getCspBenchmarkRules = async (benchmarkId: string): Promise<CspBenchmarkRule[]> => {
let cspBenchmarkRules: CspBenchmarkRule[] = [];
Expand Down Expand Up @@ -263,5 +265,57 @@ export default function (providerContext: FtrProviderContext) {
expect(scoreAfterMute.score.postureScore).to.equal(0);
});
});

describe('Get Benchmark API', async () => {
beforeEach(async () => {
await index.removeFindings();
await kibanaServer.savedObjects.clean({
types: ['cloud-security-posture-settings'],
});
await waitForPluginInitialized();
});

it('Calling Benchmark API as User with no read access to Security', async () => {
const benchmark = 'cis_aws';
const benchmarkRules = await getCspBenchmarkRules(benchmark);

const cspmFinding1 = getMockFinding(benchmarkRules[0], 'passed');

await index.addFindings([cspmFinding1]);

const { body: benchmarksResult } = await supertestWithoutAuth
.get('/internal/cloud_security_posture/benchmarks')
.set(ELASTIC_HTTP_VERSION_HEADER, '2')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.set('kbn-xsrf', 'xxxx')
.auth(
'role_security_no_read_user',
cspSecurity.getPasswordForUser('role_security_no_read_user')
);

expect(benchmarksResult.statusCode).to.equal(403);
});

// Blocked by https://github.com/elastic/kibana/issues/188059
it.skip('Calling Benchmark API as User with read access to Security', async () => {
const benchmark = 'cis_aws';
const benchmarkRules = await getCspBenchmarkRules(benchmark);

const cspmFinding1 = getMockFinding(benchmarkRules[0], 'passed');

await index.addFindings([cspmFinding1]);

const { status } = await supertestWithoutAuth
.get('/internal/cloud_security_posture/benchmarks')
.set(ELASTIC_HTTP_VERSION_HEADER, '2')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.set('kbn-xsrf', 'xxxx')
.auth(
'role_security_read_user',
cspSecurity.getPasswordForUser('role_security_read_user')
);
expect(status).to.equal(200);
});
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ import type { CspBenchmarkRule } from '@kbn/cloud-security-posture-plugin/common
// eslint-disable @kbn/imports/no_boundary_crossing
import { generateBenchmarkRuleTags } from '@kbn/cloud-security-posture-plugin/common/utils/detection_rules';
import type { FtrProviderContext } from '../ftr_provider_context';
import { CspSecurityCommonProvider } from './helper/user_roles_utilites';

// eslint-disable-next-line import/no-default-export
export default function ({ getService }: FtrProviderContext) {
export default function (providerContext: FtrProviderContext) {
const { getService } = providerContext;
const retry = getService('retry');
const supertest = getService('supertest');
const log = getService('log');
const kibanaServer = getService('kibanaServer');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const cspSecurity = CspSecurityCommonProvider(providerContext);

const generateRuleKey = (rule: CspBenchmarkRule): string => {
return `${rule.metadata.benchmark.id};${rule.metadata.benchmark.version};${rule.metadata.benchmark.rule_number}`;
Expand Down Expand Up @@ -447,5 +451,68 @@ export default function ({ getService }: FtrProviderContext) {
expect(body.error).to.eql('Bad Request');
expect(body.statusCode).to.eql(400);
});

it('users without read privileges on cloud security should not be able to mute', async () => {
const rule1 = await getRandomCspBenchmarkRule();
const rule2 = await getRandomCspBenchmarkRule();

const { status } = await supertestWithoutAuth
.post(`/internal/cloud_security_posture/rules/_bulk_action`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.set('kbn-xsrf', 'xxxx')
.auth(
'role_security_no_read_user',
cspSecurity.getPasswordForUser('role_security_no_read_user')
)
.send({
action: 'mute',
rules: [
{
benchmark_id: rule1.metadata.benchmark.id,
benchmark_version: rule1.metadata.benchmark.version,
rule_number: rule1.metadata.benchmark.rule_number || '',
rule_id: rule1.metadata.id,
},
{
benchmark_id: rule2.metadata.benchmark.id,
benchmark_version: rule2.metadata.benchmark.version,
rule_number: rule2.metadata.benchmark.rule_number || '',
rule_id: rule2.metadata.id,
},
],
});
expect(status).to.be(403);
});
// Blocked by https://github.com/elastic/kibana/issues/188059
it.skip('users with read privileges on cloud security should be able to mute', async () => {
const rule1 = await getRandomCspBenchmarkRule();
const rule2 = await getRandomCspBenchmarkRule();

const { status } = await supertestWithoutAuth
.post(`/internal/cloud_security_posture/rules/_bulk_action`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.set('kbn-xsrf', 'xxxx')
.auth('role_security_read_user', cspSecurity.getPasswordForUser('role_security_read_user'))
.send({
action: 'mute',
rules: [
{
benchmark_id: rule1.metadata.benchmark.id,
benchmark_version: rule1.metadata.benchmark.version,
rule_number: rule1.metadata.benchmark.rule_number || '',
rule_id: rule1.metadata.id,
},
{
benchmark_id: rule2.metadata.benchmark.id,
benchmark_version: rule2.metadata.benchmark.version,
rule_number: rule2.metadata.benchmark.rule_number || '',
rule_id: rule2.metadata.id,
},
],
});
expect(status).to.be(200);
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ import {
import { CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE } from '@kbn/cloud-security-posture-plugin/common/constants';
import type { CspBenchmarkRule } from '@kbn/cloud-security-posture-plugin/common/types/latest';
import type { FtrProviderContext } from '../ftr_provider_context';
import { CspSecurityCommonProvider } from './helper/user_roles_utilites';

// eslint-disable-next-line import/no-default-export
export default function ({ getService }: FtrProviderContext) {
export default function (providerContext: FtrProviderContext) {
const { getService } = providerContext;
const retry = getService('retry');
const supertest = getService('supertest');
const log = getService('log');
const kibanaServer = getService('kibanaServer');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const cspSecurity = CspSecurityCommonProvider(providerContext);

const generateRuleKey = (rule: CspBenchmarkRule): string => {
return `${rule.metadata.benchmark.id};${rule.metadata.benchmark.version};${rule.metadata.benchmark.rule_number}`;
Expand Down Expand Up @@ -160,5 +164,28 @@ export default function ({ getService }: FtrProviderContext) {

expectExpect(body).toEqual({});
});
// Blocked by https://github.com/elastic/kibana/issues/188059
it.skip('GET rules states API with user with read access', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch

const { status } = await supertestWithoutAuth
.get(`/internal/cloud_security_posture/rules/_get_states?tags=CIS`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.set('kbn-xsrf', 'xxxx')
.auth('role_security_read_user', cspSecurity.getPasswordForUser('role_security_read_use'));
expect(status).to.be(200);
});

it('GET rules states API API with user without read access', async () => {
const { status } = await supertestWithoutAuth
.get(`/internal/cloud_security_posture/rules/_get_states`)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana')
.set('kbn-xsrf', 'xxxx')
.auth(
'role_security_no_read_user',
cspSecurity.getPasswordForUser('role_security_no_read_user')
);
expect(status).to.be(403);
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* 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 { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
import expect from '@kbn/expect';
import { FtrProviderContext } from '../ftr_provider_context';
import { CspSecurityCommonProvider } from './helper/user_roles_utilites';

// eslint-disable-next-line import/no-default-export
export default function (providerContext: FtrProviderContext) {
const { getService } = providerContext;

const retry = getService('retry');
const supertest = getService('supertest');
const log = getService('log');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const cspSecurity = CspSecurityCommonProvider(providerContext);

/**
* required before indexing findings
*/
const waitForPluginInitialized = (): Promise<void> =>
retry.try(async () => {
log.debug('Check CSP plugin is initialized');
const response = await supertest
.get('/internal/cloud_security_posture/status?check=init')
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.expect(200);
expect(response.body).to.eql({ isPluginInitialized: true });
log.debug('CSP plugin is initialized');
});

describe('/internal/cloud_security_posture/detection_engine_rules/alerts/_status', () => {
describe('GET detection_engine_rules API with user that has specific access', async () => {
before(async () => {
await waitForPluginInitialized();
});
it('GET detection_engine_rules API with user with read access', async () => {
const { status } = await supertestWithoutAuth
.get(
'/internal/cloud_security_posture/detection_engine_rules/alerts/_status?tags=["CIS"]'
)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set('kbn-xsrf', 'xxxx')
.auth(
'role_security_read_user_alerts',
cspSecurity.getPasswordForUser('role_security_read_user_alerts')
);
expect(status).to.be(200);
});

it('GET detection_engine_rules API with user without read access', async () => {
const { status } = await supertestWithoutAuth
.get(
'/internal/cloud_security_posture/detection_engine_rules/alerts/_status?tags=["CIS"]'
)
.set(ELASTIC_HTTP_VERSION_HEADER, '1')
.set('kbn-xsrf', 'xxxx')
.auth(
'role_security_no_read_user_alerts',
cspSecurity.getPasswordForUser('role_security_no_read_user_alerts')
);
expect(status).to.be(403);
});
});
});
}
15 changes: 15 additions & 0 deletions x-pack/test/cloud_security_posture_api/routes/helper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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 { CspSecurityCommonProvider } from './user_roles_utilites';

export const cloudSecurityPosturePageObjects = {
cspSecurity: CspSecurityCommonProvider,
};
export const pageObjects = {
...cloudSecurityPosturePageObjects,
};
Loading