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

Compliance dashboard UI and API #171312

Merged

Conversation

Omolola-Akinleye
Copy link
Contributor

@Omolola-Akinleye Omolola-Akinleye commented Nov 15, 2023

Summary

Summarize your PR. If it involves visual changes include a screenshot or gif.

  • Add benchmarks to Compliance Dashboard API.
  • Add score_by_benchmark_id to the Benchmark Scores Index this will show posture stats for each benchmark id
  • Add benchmark aggregation query using benchmark id and version
  • Add BWC API versioning
  • STATS API V1 should show clusters
  • STATS API V2 should show benchmarks
  • Add unit tests
  • Added integration tests with API versioning test cases.

To test PR with API versioning, in Kibana client - x-pack/plugins/cloud_security_posture/public/common/api/use_stats_api.ts

  • Change version value to 1 to see clusters
http.get<ComplianceDashboardData>(getStatsRoute(CSPM_POLICY_TEMPLATE), { version: '1' }),
  • Change version value to 2 to see versions
http.get<ComplianceDashboardData>(getStatsRoute(CSPM_POLICY_TEMPLATE), { version: '2' })
image

@Omolola-Akinleye Omolola-Akinleye self-assigned this Nov 22, 2023
@Omolola-Akinleye Omolola-Akinleye added Team:Cloud Security Cloud Security team related v8.12.0 release_note:skip Skip the PR/issue when compiling release notes labels Nov 22, 2023
@Omolola-Akinleye Omolola-Akinleye marked this pull request as ready for review November 22, 2023 01:46
@Omolola-Akinleye Omolola-Akinleye requested a review from a team as a code owner November 22, 2023 01:46
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-cloud-security-posture (Team:Cloud Security)

@maxcold
Copy link
Contributor

maxcold commented Nov 24, 2023

@Omolola-Akinleye Is the PR ready for review? the build seems to be failing and for some reason I can't access the results of the build due to the type check failures

@Omolola-Akinleye Omolola-Akinleye force-pushed the compliance-dashboard-data-api branch from c3934a9 to a2d884d Compare November 27, 2023 14:52
@Omolola-Akinleye
Copy link
Contributor Author

@Omolola-Akinleye Is the PR ready for review? the build seems to be failing and for some reason I can't access the results of the build due to the type check failures

@maxcold PR is now ready for review sorry for the hold up.

Copy link
Contributor

@maxcold maxcold left a comment

Choose a reason for hiding this comment

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

I started with the CR, but couldn't finish it in one go, will continue tomorrow

@@ -0,0 +1,18 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

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

let's add some basic unit tests for these utils

Copy link
Contributor

Choose a reason for hiding this comment

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

also maybe some comments on why we need these mapping utils, it's was a bit hard to figure it out just from looking at the code

export const MAPPING_VERSION_DELIMITER = '_';

export const toBenchmarkDocFieldKey = (benchmarkId: string, benchmarkVersion: string) => {
if (benchmarkVersion.includes(MAPPING_VERSION_DELIMITER))
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: seems like the linter is ok with it but I'm personally not a big fan of if without {}, it's confusing to read and prone to errors. I'd either have it like

if (benchmarkVersion.includes(MAPPING_VERSION_DELIMITER)) {
  return `${benchmarkId};${benchmarkVersion.replaceAll('_', '.')}`;
}

return `${benchmarkId};${benchmarkVersion}`;

or have a ternary in the return

@Omolola-Akinleye Omolola-Akinleye changed the title Compliance dashboard data api Compliance dashboard data UI and API Nov 29, 2023
@Omolola-Akinleye Omolola-Akinleye changed the title Compliance dashboard data UI and API Compliance dashboard UI and API Nov 29, 2023
getStats(logger, esClient, query, pitId, runtimeMappings),
getGroupedFindingsEvaluation(logger, esClient, query, pitId, runtimeMappings),
getClusters(logger, esClient, query, pitId, runtimeMappings),
getTrends(logger, esClient, policyTemplate),
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: logger usually passes as the last argument in our plugin.

Comment on lines +96 to +101
if (!Array.isArray(version.aggs_by_benchmark_name.buckets))
throw new Error('missing aggs by benchmark name');

if (version.aggs_by_benchmark_name && version.aggs_by_benchmark_name.buckets.length > 0) {
benchmarkName = version.aggs_by_benchmark_name.buckets[0].key;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

let's check if there are missing value at once

Suggested change
if (!Array.isArray(version.aggs_by_benchmark_name.buckets))
throw new Error('missing aggs by benchmark name');
if (version.aggs_by_benchmark_name && version.aggs_by_benchmark_name.buckets.length > 0) {
benchmarkName = version.aggs_by_benchmark_name.buckets[0].key;
}
if (!Array.isArray(version.aggs_by_benchmark_name.buckets))
throw new Error('missing aggs by benchmark name');
if (version.aggs_by_benchmark_name && version.aggs_by_benchmark_name.buckets.length > 0) {
benchmarkName = version.aggs_by_benchmark_name.buckets[0].key;
}
if (!Array.isArray(version.aggs_by_benchmark_name.buckets))
throw new Error('missing aggs by benchmark name');
if (version.aggs_by_benchmark_name && version.aggs_by_benchmark_name.buckets.length > 0) {
benchmarkName = version.aggs_by_benchmark_name.buckets[0].key;
}
if (!Array.isArray(resourcesTypesAggs))
throw new Error('missing aggs by resource type per benchmark');

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@CohenIdo I'm not sure what you are asking here. Do you mind elaborating?

const resourcesTypesAggs = version.aggs_by_resource_type.buckets;
if (!Array.isArray(resourcesTypesAggs))
throw new Error('missing aggs by resource type per benchmark');
const groupedFindingsEvaluation = getFailedFindingsFromAggs(resourcesTypesAggs);
Copy link
Contributor

Choose a reason for hiding this comment

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

getFailedFindingsFromAggs is confusing name for a function that returns

{
      name: bucket.key,
      totalFindings: bucket.doc_count,
      totalFailed,
      totalPassed,
      postureScore: calculatePostureScore(totalPassed, totalFailed),
    }

I know it's not part of the PR, but is a good chance to rename it for more meaningful name.

};
aggs_by_benchmark_name: Aggregation<KeyDocCount>;
}
export interface FailedFindingsBucket extends KeyDocCount {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please rename the interface name? The bucket not only includes failed findings.

Comment on lines 27 to 37
score_by_benchmark_id: Record<
string,
Record<
string,
{
total_findings: number;
passed_findings: number;
failed_findings: number;
}
>
>;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm suggesting to refactor the interface in order to avoid code duplication and using ovject instead of Record:

interface FindingsDetails {
  total_findings: number;
  passed_findings: number;
  failed_findings: number;
}

interface ScoreByClusterId {
  [clusterId: string]: FindingsDetails;
}

interface ScoreByBenchmarkId {
  [benchmarkId: string]: {
    [key: string]: FindingsDetails;
  };
}

export interface ScoreTrendDoc {
  '@timestamp': string;
  total_findings: number;
  passed_findings: number;
  failed_findings: number;
  score_by_cluster_id: ScoreByClusterId;
  score_by_benchmark_id: ScoreByBenchmarkId;
}

// large number that should be sufficient for 24 hours considering we write to the score index every 5 minutes
size: 999,
// Amount of samples of the last 24 hours (accounting that we take a sample every 5 minutes)
size: (24 * 60) / 5,
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's use please the identical parameter currently utilized in the task manager configuration to stay synchronized with any future modifications. It might necessitate confirming that the calculation result is an integer by rounding the outcome.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey @CohenIdo, this is a score trends aggregation query which will return the size of the number of trend documents. I don't see how I can apply the 5m interval here.

@Omolola-Akinleye Omolola-Akinleye enabled auto-merge (squash) November 30, 2023 13:58
@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
cloudSecurityPosture 432.1KB 434.5KB +2.3KB

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @Omolola-Akinleye

@Omolola-Akinleye
Copy link
Contributor Author

Addressed PR changes @CohenIdo @maxcold

Copy link
Contributor

@opauloh opauloh left a comment

Choose a reason for hiding this comment

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

LGTM, just some nits that can be addressed in the future

// large number that should be sufficient for 24 hours considering we write to the score index every 5 minutes
size: 999,
// Amount of samples of the last 24 hours (accounting that we take a sample every 5 minutes)
size: (24 * 60) / CSPM_FINDINGS_STATS_INTERVAL,
Copy link
Contributor

Choose a reason for hiding this comment

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

nice improvement 🚀


const CSPM_FINDINGS_STATS_TASK_ID = 'cloud_security_posture-findings_stats';
const CSPM_FINDINGS_STATS_TASK_TYPE = 'cloud_security_posture-stats_task';
const CSPM_FINDINGS_STATS_INTERVAL = '5m';
export const CSPM_FINDINGS_STATS_INTERVAL = 5;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: move that to the common/constants.ts file

</>
);

const NonCompactPercentageLabels = ({
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this could be called PercentageLabels instead of NonCompactPercentageLabels

@Omolola-Akinleye Omolola-Akinleye merged commit af28af8 into elastic:main Nov 30, 2023
30 checks passed
@kibanamachine kibanamachine added the backport:skip This commit does not require backporting label Nov 30, 2023
Omolola-Akinleye added a commit that referenced this pull request Dec 13, 2023
## Summary

Summarize your PR. If it involves visual changes include a screenshot or
gif.
Address nit comments from [Compliance Dashboard UI/API
PR](#171312)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting release_note:skip Skip the PR/issue when compiling release notes Team:Cloud Security Cloud Security team related v8.12.0
Projects
None yet
7 participants