kibana/src/plugins/custom_integrations/server/custom_integration_registry.ts
Larry Gregory 74d88580a5
Migrate codebase to use Object.hasOwn instead of Object.hasOwnProperty (#186829)
## Summary

This PR has breadth, but not depth. This adds 3 new `eslint` rules. The
first two protect against the use of code generated from strings (`eval`
and friends), which will not work client-side due to our CSP, and is not
something we wish to support server-side. The last rule aims to prevent
a subtle class of bugs, and to defend against a subset of prototype
pollution exploits:

- `no-new-func` to be compliant with our CSP, and to prevent code
execution from strings server-side:
https://eslint.org/docs/latest/rules/no-new-func
- `no-implied-eval` to be compliant with our CSP, and to prevent code
execution from strings server-side:
https://eslint.org/docs/latest/rules/no-implied-eval. Note that this
function implies that it prevents no-new-func, but I don't see [test
cases](https://github.com/eslint/eslint/blob/main/tests/lib/rules/no-implied-eval.js)
covering this behavior, so I think we should play it safe and enable
both rules.
- `no-prototype-builtins` to prevent accessing shadowed properties:
https://eslint.org/docs/latest/rules/no-prototype-builtins


In order to be compliant with `no-prototype-builtins`, I've migrated all
usages and variants of `Object.hasOwnProperty` to use the newer
[`Object.hasOwn`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn).
2024-08-13 10:30:19 -05:00

62 lines
2 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 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { Logger } from '@kbn/core/server';
import { IntegrationCategory, INTEGRATION_CATEGORY_DISPLAY, CustomIntegration } from '../common';
function isAddable(integration: CustomIntegration): boolean {
return !!integration.categories.length && !integration.eprOverlap;
}
function isReplacement(integration: CustomIntegration): boolean {
return !!integration.categories.length && !!integration.eprOverlap;
}
export class CustomIntegrationRegistry {
private readonly _integrations: CustomIntegration[];
private readonly _logger: Logger;
private readonly _isDev: boolean;
constructor(logger: Logger, isDev: boolean) {
this._integrations = [];
this._logger = logger;
this._isDev = isDev;
}
registerCustomIntegration(customIntegration: CustomIntegration) {
if (
this._integrations.some((integration: CustomIntegration) => {
return integration.id === customIntegration.id;
})
) {
const message = `Integration with id=${customIntegration.id} already exists.`;
if (this._isDev) {
this._logger.error(message);
} else {
this._logger.debug(message);
}
return;
}
const allowedCategories: IntegrationCategory[] = (customIntegration.categories ?? []).filter(
(category) => {
return Object.hasOwn(INTEGRATION_CATEGORY_DISPLAY, category);
}
) as IntegrationCategory[];
this._integrations.push({ ...customIntegration, categories: allowedCategories });
}
getAppendCustomIntegrations(): CustomIntegration[] {
return this._integrations.filter(isAddable);
}
getReplacementCustomIntegrations(): CustomIntegration[] {
return this._integrations.filter(isReplacement);
}
}