kibana/x-pack/plugins/security_solution/common/detection_engine/utils.ts
Kibana Machine d2075f63d2
[Security solutions] Adds linter rule to forbid usage of no-non-null-assertion (TypeScript ! bang operator) (#114375) (#115126)
## Summary

Fixes: https://github.com/elastic/kibana/issues/114535

**What this linter rule does:**
* Sets the [@typescript-eslint/no-non-null-assertion](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-non-null-assertion.md) linter rule to become an error if seen.

If you try to use the `!` operator you get an error and nice helper message that tries to encourage better practices such as this one:

<img width="1635" alt="Screen Shot 2021-10-07 at 11 26 14 AM" src="https://user-images.githubusercontent.com/1151048/136474207-f38d3461-0af9-4cdc-885b-632cb49d8a24.png">

**Why are we deciding to set this linter rule?**
* Recommended from Kibana [styleguide](https://github.com/elastic/kibana/blob/master/STYLEGUIDE.mdx#avoid-non-null-assertions) for ~2 years now and still recommended.
* A lot of TypeScript has evolved and has operators such as `?` which can replace the `!` in most cases. Other cases can use a throw explicitly or other ways to manage this.
* Some types can change instead of using this operator and we should just change the types.
* TypeScript flows have improved and when we upgrade the linter will cause errors where we can remove the `!` operator which is 👍 better than leaving them when they're not needed anymore.
* Newer programmers and team members sometimes mistake it for the `?` when it is not the same thing.
* We have had past bugs and bugs recently because of these.
* It's easier to use the linter to find bugs than to rely on manual tests. 

**How did Frank fix all the 403 areas in which the linter goes off?**
* Anywhere I could remove the `!` operator without side effects or type script errors I just removed the `!` operator.
* Anywhere in test code where I could replace the code with a `?` or a `throw` I went through that route.
* Within the code areas (non test code) where I saw what looks like a simple bug that I could fix using a `?? []` or `?` operator or `String(value)` or fixing a simple type I would choose that route first. These areas I marked in the code review with the words `NOTE:` for anyone to look at.
* Within all other areas of the code and tests where anything looked more involved I just disabled the linter for that particular line. When in doubt I chose this route.

### Checklist

- [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios

Co-authored-by: Frank Hassanabad <frank.hassanabad@elastic.co>
2021-10-15 01:01:23 -04:00

75 lines
2.5 KiB
TypeScript

/*
* 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 { isEmpty } from 'lodash';
import type {
EntriesArray,
CreateExceptionListItemSchema,
ExceptionListItemSchema,
} from '@kbn/securitysolution-io-ts-list-types';
import { Type } from '@kbn/securitysolution-io-ts-alerting-types';
import { hasLargeValueList } from '@kbn/securitysolution-list-utils';
import { RuleExecutionStatus, Threshold, ThresholdNormalized } from './schemas/common/schemas';
export const hasLargeValueItem = (
exceptionItems: Array<ExceptionListItemSchema | CreateExceptionListItemSchema>
) => {
return exceptionItems.some((exceptionItem) => hasLargeValueList(exceptionItem.entries));
};
export const hasNestedEntry = (entries: EntriesArray): boolean => {
const found = entries.filter(({ type }) => type === 'nested');
return found.length > 0;
};
export const hasEqlSequenceQuery = (ruleQuery: string | undefined): boolean => {
if (ruleQuery != null) {
const parsedQuery = ruleQuery.trim().split(/[ \t\r\n]+/);
return parsedQuery[0] === 'sequence' && parsedQuery[1] !== 'where';
}
return false;
};
export const isEqlRule = (ruleType: Type | undefined): boolean => ruleType === 'eql';
export const isThresholdRule = (ruleType: Type | undefined): boolean => ruleType === 'threshold';
export const isQueryRule = (ruleType: Type | undefined): boolean =>
ruleType === 'query' || ruleType === 'saved_query';
export const isThreatMatchRule = (ruleType: Type | undefined): boolean =>
ruleType === 'threat_match';
export const normalizeThresholdField = (
thresholdField: string | string[] | null | undefined
): string[] => {
return Array.isArray(thresholdField)
? thresholdField
: isEmpty(thresholdField)
? []
: // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
[thresholdField!];
};
export const normalizeThresholdObject = (threshold: Threshold): ThresholdNormalized => {
return {
...threshold,
field: normalizeThresholdField(threshold.field),
};
};
export const normalizeMachineLearningJobIds = (value: string | string[]): string[] =>
Array.isArray(value) ? value : [value];
export const getRuleStatusText = (
value: RuleExecutionStatus | null | undefined
): RuleExecutionStatus | null =>
value === RuleExecutionStatus['partial failure']
? RuleExecutionStatus.warning
: value != null
? value
: null;