mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Harden console functions (#171367)
## Summary This PR overrides console functions only in production, in order to sanitize input parameters for any potential calls made to the global console from Kibana's dependencies. This initial implementation overrides the `debug`, `error`, `info`, `log`, `trace`, and `warn` functions, and only sanitizes string inputs. Future updates may expand this to handle other types, or strings nested in objects. The unmodified console methods are now exposed internally in Kibana as `unsafeConsole`. Where needed for formatting (log appenders, core logger), calls to the global console have been replaced by `unsafeConsole`. This PR also adds a new es linting rule to disallow calls to `unsafeConsole` unless `eslint-disable-next-line @kbn/eslint/no_unsafe_console` is used. ### Testing Not sure how we could test this. The overrides are only enabled when running in a true production environment (e.g. docker) by checking `process.env.NODE_ENV`. I was able to manually test by adding additional console output denoting when the console functions were being overriden or not. Closes https://github.com/elastic/kibana-team/issues/664 Closes #176340 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c5819dc09e
commit
2627f48d95
34 changed files with 379 additions and 47 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -674,6 +674,7 @@ packages/kbn-search-index-documents @elastic/enterprise-search-frontend
|
|||
packages/kbn-search-response-warnings @elastic/kibana-data-discovery
|
||||
x-pack/plugins/searchprofiler @elastic/platform-deployment-management
|
||||
x-pack/test/security_api_integration/packages/helpers @elastic/kibana-security
|
||||
packages/kbn-security-hardening @elastic/kibana-security
|
||||
x-pack/plugins/security @elastic/kibana-security
|
||||
x-pack/packages/security/plugin_types_common @elastic/kibana-security
|
||||
x-pack/packages/security/plugin_types_public @elastic/kibana-security
|
||||
|
|
|
@ -676,6 +676,7 @@
|
|||
"@kbn/search-index-documents": "link:packages/kbn-search-index-documents",
|
||||
"@kbn/search-response-warnings": "link:packages/kbn-search-response-warnings",
|
||||
"@kbn/searchprofiler-plugin": "link:x-pack/plugins/searchprofiler",
|
||||
"@kbn/security-hardening": "link:packages/kbn-security-hardening",
|
||||
"@kbn/security-plugin": "link:x-pack/plugins/security",
|
||||
"@kbn/security-plugin-types-common": "link:x-pack/packages/security/plugin_types_common",
|
||||
"@kbn/security-plugin-types-public": "link:x-pack/packages/security/plugin_types_public",
|
||||
|
|
|
@ -7,16 +7,17 @@
|
|||
*/
|
||||
|
||||
import type { LogRecord } from '@kbn/logging';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
import { createLogger } from './logger';
|
||||
|
||||
describe('createLogger', () => {
|
||||
// Calling `.mockImplementation` on all of them to avoid jest logging the console usage
|
||||
const logErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
||||
const logWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
||||
const logInfoSpy = jest.spyOn(console, 'info').mockImplementation();
|
||||
const logDebugSpy = jest.spyOn(console, 'debug').mockImplementation();
|
||||
const logTraceSpy = jest.spyOn(console, 'trace').mockImplementation();
|
||||
const logLogSpy = jest.spyOn(console, 'log').mockImplementation();
|
||||
const logErrorSpy = jest.spyOn(unsafeConsole, 'error').mockImplementation();
|
||||
const logWarnSpy = jest.spyOn(unsafeConsole, 'warn').mockImplementation();
|
||||
const logInfoSpy = jest.spyOn(unsafeConsole, 'info').mockImplementation();
|
||||
const logDebugSpy = jest.spyOn(unsafeConsole, 'debug').mockImplementation();
|
||||
const logTraceSpy = jest.spyOn(unsafeConsole, 'trace').mockImplementation();
|
||||
const logLogSpy = jest.spyOn(unsafeConsole, 'log').mockImplementation();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import type { Logger } from '@kbn/logging';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
|
||||
/**
|
||||
* Create custom logger until we have a proper logging solution: https://github.com/elastic/kibana/issues/33796
|
||||
|
@ -16,20 +17,20 @@ export function createLogger(isDev: boolean): Logger {
|
|||
// TODO: Replace with a core logger once we implement it in https://github.com/elastic/kibana/issues/33796
|
||||
// For now, it logs only in dev mode
|
||||
const logger: Logger = {
|
||||
// eslint-disable-next-line no-console
|
||||
fatal: (...args) => (isDev ? console.error(...args) : void 0),
|
||||
// eslint-disable-next-line no-console
|
||||
error: (...args) => (isDev ? console.error(...args) : void 0),
|
||||
// eslint-disable-next-line no-console
|
||||
warn: (...args) => (isDev ? console.warn(...args) : void 0),
|
||||
// eslint-disable-next-line no-console
|
||||
info: (...args) => (isDev ? console.info(...args) : void 0),
|
||||
// eslint-disable-next-line no-console
|
||||
debug: (...args) => (isDev ? console.debug(...args) : void 0),
|
||||
// eslint-disable-next-line no-console
|
||||
trace: (...args) => (isDev ? console.trace(...args) : void 0),
|
||||
// eslint-disable-next-line no-console
|
||||
log: (...args) => (isDev ? console.log(...args) : void 0),
|
||||
// eslint-disable-next-line @kbn/eslint/no_unsafe_console
|
||||
fatal: (...args) => (isDev ? unsafeConsole.error(...args) : void 0),
|
||||
// eslint-disable-next-line @kbn/eslint/no_unsafe_console
|
||||
error: (...args) => (isDev ? unsafeConsole.error(...args) : void 0),
|
||||
// eslint-disable-next-line @kbn/eslint/no_unsafe_console
|
||||
warn: (...args) => (isDev ? unsafeConsole.warn(...args) : void 0),
|
||||
// eslint-disable-next-line @kbn/eslint/no_unsafe_console
|
||||
info: (...args) => (isDev ? unsafeConsole.info(...args) : void 0),
|
||||
// eslint-disable-next-line @kbn/eslint/no_unsafe_console
|
||||
debug: (...args) => (isDev ? unsafeConsole.debug(...args) : void 0),
|
||||
// eslint-disable-next-line @kbn/eslint/no_unsafe_console
|
||||
trace: (...args) => (isDev ? unsafeConsole.trace(...args) : void 0),
|
||||
// eslint-disable-next-line @kbn/eslint/no_unsafe_console
|
||||
log: (...args) => (isDev ? unsafeConsole.log(...args) : void 0),
|
||||
isLevelEnabled: () => true,
|
||||
get: () => logger,
|
||||
};
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
"@kbn/core-injected-metadata-browser-internal",
|
||||
"@kbn/core-analytics-browser",
|
||||
"@kbn/core-base-browser-mocks",
|
||||
"@kbn/core-injected-metadata-browser-mocks"
|
||||
"@kbn/core-injected-metadata-browser-mocks",
|
||||
"@kbn/security-hardening"
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
*/
|
||||
|
||||
import { LogRecord, LogLevel } from '@kbn/logging';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
import { ConsoleAppender } from './console_appender';
|
||||
|
||||
test('`append()` correctly formats records and pushes them to console.', () => {
|
||||
jest.spyOn(global.console, 'log').mockImplementation(() => {
|
||||
jest.spyOn(unsafeConsole, 'log').mockImplementation(() => {
|
||||
// noop
|
||||
});
|
||||
|
||||
|
@ -47,10 +48,7 @@ test('`append()` correctly formats records and pushes them to console.', () => {
|
|||
|
||||
for (const record of records) {
|
||||
appender.append(record);
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.log).toHaveBeenCalledWith(`mock-${JSON.stringify(record)}`);
|
||||
expect(unsafeConsole.log).toHaveBeenCalledWith(`mock-${JSON.stringify(record)}`);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.log).toHaveBeenCalledTimes(records.length);
|
||||
expect(unsafeConsole.log).toHaveBeenCalledTimes(records.length);
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import type { Layout, LogRecord, DisposableAppender } from '@kbn/logging';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -25,8 +26,8 @@ export class ConsoleAppender implements DisposableAppender {
|
|||
* @param record `LogRecord` instance to be logged.
|
||||
*/
|
||||
public append(record: LogRecord) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(this.layout.format(record));
|
||||
// eslint-disable-next-line @kbn/eslint/no_unsafe_console
|
||||
unsafeConsole.log(this.layout.format(record));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
import { BrowserLoggingSystem } from './logging_system';
|
||||
|
||||
describe('', () => {
|
||||
|
@ -15,7 +16,7 @@ describe('', () => {
|
|||
let loggingSystem: BrowserLoggingSystem;
|
||||
|
||||
beforeEach(() => {
|
||||
mockConsoleLog = jest.spyOn(global.console, 'log').mockReturnValue(undefined);
|
||||
mockConsoleLog = jest.spyOn(unsafeConsole, 'log').mockReturnValue(undefined);
|
||||
jest.spyOn<any, any>(global, 'Date').mockImplementation(() => timestamp);
|
||||
loggingSystem = new BrowserLoggingSystem({ logLevel: 'warn' });
|
||||
});
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
],
|
||||
"kbn_references": [
|
||||
"@kbn/logging",
|
||||
"@kbn/core-logging-common-internal"
|
||||
"@kbn/core-logging-common-internal",
|
||||
"@kbn/security-hardening"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -19,6 +19,7 @@ jest.mock('../../layouts/layouts', () => {
|
|||
});
|
||||
|
||||
import { LogRecord, LogLevel } from '@kbn/logging';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
import { ConsoleAppender } from './console_appender';
|
||||
|
||||
test('`configSchema` creates correct schema.', () => {
|
||||
|
@ -37,7 +38,7 @@ test('`configSchema` creates correct schema.', () => {
|
|||
});
|
||||
|
||||
test('`append()` correctly formats records and pushes them to console.', () => {
|
||||
jest.spyOn(global.console, 'log').mockImplementation(() => {
|
||||
jest.spyOn(unsafeConsole, 'log').mockImplementation(() => {
|
||||
// noop
|
||||
});
|
||||
|
||||
|
@ -74,10 +75,7 @@ test('`append()` correctly formats records and pushes them to console.', () => {
|
|||
|
||||
for (const record of records) {
|
||||
appender.append(record);
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.log).toHaveBeenCalledWith(`mock-${JSON.stringify(record)}`);
|
||||
expect(unsafeConsole.log).toHaveBeenCalledWith(`mock-${JSON.stringify(record)}`);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
expect(console.log).toHaveBeenCalledTimes(records.length);
|
||||
expect(unsafeConsole.log).toHaveBeenCalledTimes(records.length);
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import type { Layout, LogRecord, DisposableAppender } from '@kbn/logging';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
import { Layouts } from '../../layouts/layouts';
|
||||
|
||||
const { literal, object } = schema;
|
||||
|
@ -34,8 +35,8 @@ export class ConsoleAppender implements DisposableAppender {
|
|||
* @param record `LogRecord` instance to be logged.
|
||||
*/
|
||||
public append(record: LogRecord) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(this.layout.format(record));
|
||||
// eslint-disable-next-line @kbn/eslint/no_unsafe_console
|
||||
unsafeConsole.log(this.layout.format(record));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,10 +18,11 @@ const mockCreateWriteStream = createWriteStream as unknown as jest.Mock<typeof c
|
|||
|
||||
import { LoggingSystem, config } from '..';
|
||||
import { EcsVersion } from '@kbn/ecs';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
|
||||
let system: LoggingSystem;
|
||||
beforeEach(() => {
|
||||
mockConsoleLog = jest.spyOn(global.console, 'log').mockReturnValue(undefined);
|
||||
mockConsoleLog = jest.spyOn(unsafeConsole, 'log').mockReturnValue(undefined);
|
||||
jest.spyOn<any, any>(global, 'Date').mockImplementation(() => timestamp);
|
||||
jest.spyOn(process, 'uptime').mockReturnValue(10);
|
||||
system = new LoggingSystem();
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
"@kbn/utility-types-jest",
|
||||
"@kbn/utility-types",
|
||||
"@kbn/ecs",
|
||||
"@kbn/security-hardening",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -273,6 +273,7 @@ module.exports = {
|
|||
'@kbn/eslint/no_trailing_import_slash': 'error',
|
||||
'@kbn/eslint/no_constructor_args_in_property_initializers': 'error',
|
||||
'@kbn/eslint/no_this_in_property_initializers': 'error',
|
||||
'@kbn/eslint/no_unsafe_console': 'error',
|
||||
'@kbn/imports/no_unresolvable_imports': 'error',
|
||||
'@kbn/imports/uniform_imports': 'error',
|
||||
'@kbn/imports/no_unused_imports': 'error',
|
||||
|
|
|
@ -103,4 +103,8 @@ module.exports = {
|
|||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
## no_unsafe_console
|
||||
|
||||
Disables the usage of kbn-security-hardening/console/unsafeConsole.
|
|
@ -17,5 +17,6 @@ module.exports = {
|
|||
no_trailing_import_slash: require('./rules/no_trailing_import_slash'),
|
||||
no_constructor_args_in_property_initializers: require('./rules/no_constructor_args_in_property_initializers'),
|
||||
no_this_in_property_initializers: require('./rules/no_this_in_property_initializers'),
|
||||
no_unsafe_console: require('./rules/no_unsafe_console'),
|
||||
},
|
||||
};
|
||||
|
|
71
packages/kbn-eslint-plugin-eslint/rules/no_unsafe_console.js
Normal file
71
packages/kbn-eslint-plugin-eslint/rules/no_unsafe_console.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const tsEstree = require('@typescript-eslint/typescript-estree');
|
||||
const esTypes = tsEstree.AST_NODE_TYPES;
|
||||
|
||||
/** @typedef {import("eslint").Rule.RuleModule} Rule */
|
||||
/** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.Node} Node */
|
||||
/** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.CallExpression} CallExpression */
|
||||
/** @typedef {import("@typescript-eslint/typescript-estree").TSESTree.CallExpression} VariableDeclarator */
|
||||
|
||||
const ERROR_MSG = 'Unexpected unsafeConsole statement.';
|
||||
|
||||
/**
|
||||
* @param {CallExpression} node
|
||||
*/
|
||||
const isUnsafeConsoleCall = (node) => {
|
||||
return (
|
||||
node.callee.type === esTypes.MemberExpression &&
|
||||
node.callee.property.type === esTypes.Identifier &&
|
||||
node.callee.object.name === 'unsafeConsole' &&
|
||||
node.callee.property.name
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {VariableDeclarator} node
|
||||
*/
|
||||
const isUnsafeConsoleObjectPatternDeclarator = (node) => {
|
||||
return (
|
||||
node.id.type === esTypes.ObjectPattern &&
|
||||
node.init &&
|
||||
node.init.type === esTypes.Identifier &&
|
||||
node.init.name === 'unsafeConsole'
|
||||
);
|
||||
};
|
||||
|
||||
/** @type {Rule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
fixable: 'code',
|
||||
schema: [],
|
||||
},
|
||||
create: (context) => ({
|
||||
CallExpression(_) {
|
||||
const node = /** @type {CallExpression} */ (_);
|
||||
|
||||
if (isUnsafeConsoleCall(node)) {
|
||||
context.report({
|
||||
message: ERROR_MSG,
|
||||
loc: node.callee.loc,
|
||||
});
|
||||
}
|
||||
},
|
||||
VariableDeclarator(_) {
|
||||
const node = /** @type {VariableDeclarator} */ (_);
|
||||
|
||||
if (isUnsafeConsoleObjectPatternDeclarator(node)) {
|
||||
context.report({
|
||||
message: ERROR_MSG,
|
||||
loc: node.init.loc,
|
||||
});
|
||||
}
|
||||
},
|
||||
}),
|
||||
};
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const { RuleTester } = require('eslint');
|
||||
const rule = require('./no_unsafe_console');
|
||||
const dedent = require('dedent');
|
||||
|
||||
const ruleTester = new RuleTester({
|
||||
parser: require.resolve('@typescript-eslint/parser'),
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2018,
|
||||
},
|
||||
});
|
||||
|
||||
ruleTester.run('@kbn/eslint/no_unsafe_console', rule, {
|
||||
valid: [
|
||||
{
|
||||
code: dedent`
|
||||
unsafeConsole
|
||||
`,
|
||||
},
|
||||
],
|
||||
|
||||
invalid: [
|
||||
{
|
||||
code: dedent`
|
||||
unsafeConsole.debug('something to debug')
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
line: 1,
|
||||
message: 'Unexpected unsafeConsole statement.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: dedent`
|
||||
unsafeConsole.error()
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
line: 1,
|
||||
message: 'Unexpected unsafeConsole statement.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: dedent`
|
||||
unsafeConsole.info('some info')
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
line: 1,
|
||||
message: 'Unexpected unsafeConsole statement.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: dedent`
|
||||
unsafeConsole.log('something to log')
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
line: 1,
|
||||
message: 'Unexpected unsafeConsole statement.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: dedent`
|
||||
unsafeConsole.trace()
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
line: 1,
|
||||
message: 'Unexpected unsafeConsole statement.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: dedent`
|
||||
unsafeConsole.warn('something to warn')
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
line: 1,
|
||||
message: 'Unexpected unsafeConsole statement.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: dedent`
|
||||
unsafeConsole.anyOtherMethodName()
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
line: 1,
|
||||
message: 'Unexpected unsafeConsole statement.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: dedent`
|
||||
const { debug } = unsafeConsole
|
||||
debug('something to debug')
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
line: 1,
|
||||
message: 'Unexpected unsafeConsole statement.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
7
packages/kbn-security-hardening/README.md
Normal file
7
packages/kbn-security-hardening/README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# @kbn/security-hardening
|
||||
|
||||
A package counterpart of `src/setup_node_env/harden` - containing overrides, utilities, and tools to reduce potential vulnerability.
|
||||
|
||||
## console
|
||||
|
||||
When running in production mode (`process.env.NODE_ENV === 'production'`), global console methods `debug`, `error`, `info`, `log`, `trace`, and `warn` are overridden to implement input sanitization. The export `unsafeConsole` provides access to the unmodified global console methods.
|
69
packages/kbn-security-hardening/console.ts
Normal file
69
packages/kbn-security-hardening/console.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
// From https://www.ascii-code.com/characters/control-characters,
|
||||
// but explicitly allowing the range \u0008-\u000F (line breaks, tabs, etc.)
|
||||
const CONTROL_CHAR_REGEXP = new RegExp('[\\u0000-\\u0007\\u0010-\\u001F]', 'g');
|
||||
|
||||
export const unsafeConsole = {
|
||||
debug: console.debug.bind(console),
|
||||
error: console.error.bind(console),
|
||||
info: console.info.bind(console),
|
||||
log: console.log.bind(console),
|
||||
trace: console.trace.bind(console),
|
||||
warn: console.warn.bind(console),
|
||||
};
|
||||
|
||||
function callWithSanitizedArgs(func: Function, ...args: any[]) {
|
||||
const cleanedArgs = args.map(function (arg) {
|
||||
if (typeof arg !== 'string') return arg;
|
||||
return escapeControlChars(arg);
|
||||
});
|
||||
func.apply(console, cleanedArgs);
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
console.log('Native global console methods have been overridden in production environment.');
|
||||
|
||||
console.debug = function (...args) {
|
||||
callWithSanitizedArgs(unsafeConsole.debug, ...args);
|
||||
};
|
||||
|
||||
console.error = function (...args) {
|
||||
callWithSanitizedArgs(unsafeConsole.error, ...args);
|
||||
};
|
||||
|
||||
console.info = function (...args) {
|
||||
callWithSanitizedArgs(unsafeConsole.info, ...args);
|
||||
};
|
||||
|
||||
console.log = function (...args) {
|
||||
callWithSanitizedArgs(unsafeConsole.log, ...args);
|
||||
};
|
||||
|
||||
console.trace = function (...args) {
|
||||
callWithSanitizedArgs(unsafeConsole.trace, ...args);
|
||||
};
|
||||
|
||||
console.warn = function (...args) {
|
||||
callWithSanitizedArgs(unsafeConsole.warn, ...args);
|
||||
};
|
||||
}
|
||||
|
||||
function escapeControlChars(input: string) {
|
||||
return input.replace(
|
||||
CONTROL_CHAR_REGEXP,
|
||||
// Escaping control chars via JSON.stringify to maintain consistency with `meta` and the JSON layout.
|
||||
// This way, post analysis of the logs is easier as we can search the same patterns.
|
||||
// Our benchmark didn't show a big difference in performance between custom-escaping vs. JSON.stringify one.
|
||||
// The slice is removing the double-quotes.
|
||||
(substr) => JSON.stringify(substr).slice(1, -1)
|
||||
);
|
||||
}
|
9
packages/kbn-security-hardening/index.ts
Normal file
9
packages/kbn-security-hardening/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { unsafeConsole } from './console';
|
5
packages/kbn-security-hardening/kibana.jsonc
Normal file
5
packages/kbn-security-hardening/kibana.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/security-hardening",
|
||||
"owner": "@elastic/kibana-security"
|
||||
}
|
6
packages/kbn-security-hardening/package.json
Normal file
6
packages/kbn-security-hardening/package.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/security-hardening",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
16
packages/kbn-security-hardening/tsconfig.json
Normal file
16
packages/kbn-security-hardening/tsconfig.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": []
|
||||
}
|
|
@ -11,6 +11,7 @@ import {
|
|||
type TestElasticsearchUtils,
|
||||
type TestKibanaUtils,
|
||||
} from '@kbn/core-test-helpers-kbn-server';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
|
||||
describe('Error logging', () => {
|
||||
describe('ES client errors', () => {
|
||||
|
@ -19,7 +20,7 @@ describe('Error logging', () => {
|
|||
let kibanaServer: TestKibanaUtils;
|
||||
|
||||
beforeAll(async () => {
|
||||
mockConsoleLog = jest.spyOn(global.console, 'log');
|
||||
mockConsoleLog = jest.spyOn(unsafeConsole, 'log');
|
||||
|
||||
const { startES, startKibana } = createTestServers({
|
||||
adjustTimeout: jest.setTimeout,
|
||||
|
|
|
@ -15,6 +15,7 @@ import { esTestConfig } from '@kbn/test';
|
|||
import { firstValueFrom, Subject } from 'rxjs';
|
||||
import { CliArgs } from '@kbn/config';
|
||||
import Semver from 'semver';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
|
||||
function nextMinor() {
|
||||
return Semver.inc(esTestConfig.getVersion(), 'minor') || '10.0.0';
|
||||
|
@ -36,7 +37,7 @@ describe('Version Compatibility', () => {
|
|||
let consoleSpy: jest.SpyInstance;
|
||||
|
||||
beforeEach(() => {
|
||||
consoleSpy = jest.spyOn(console, 'log');
|
||||
consoleSpy = jest.spyOn(unsafeConsole, 'log');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { createRoot, request } from '@kbn/core-test-helpers-kbn-server';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
|
||||
describe('request logging', () => {
|
||||
let mockConsoleLog: jest.SpyInstance;
|
||||
|
||||
beforeAll(() => {
|
||||
mockConsoleLog = jest.spyOn(global.console, 'log');
|
||||
mockConsoleLog = jest.spyOn(unsafeConsole, 'log');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
@ -10,6 +10,7 @@ import type { LoggerContextConfigInput } from '@kbn/core-logging-server';
|
|||
import { createRoot as createkbnTestServerRoot } from '@kbn/core-test-helpers-kbn-server';
|
||||
import { InternalCoreSetup } from '@kbn/core-lifecycle-server-internal';
|
||||
import { Subject } from 'rxjs';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
|
||||
function createRoot() {
|
||||
return createkbnTestServerRoot({
|
||||
|
@ -45,7 +46,7 @@ describe('logging service', () => {
|
|||
let root: ReturnType<typeof createRoot>;
|
||||
let mockConsoleLog: jest.SpyInstance;
|
||||
beforeAll(async () => {
|
||||
mockConsoleLog = jest.spyOn(global.console, 'log');
|
||||
mockConsoleLog = jest.spyOn(unsafeConsole, 'log');
|
||||
root = createRoot();
|
||||
|
||||
await root.preboot();
|
||||
|
@ -148,7 +149,7 @@ describe('logging service', () => {
|
|||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
mockConsoleLog = jest.spyOn(global.console, 'log');
|
||||
mockConsoleLog = jest.spyOn(unsafeConsole, 'log');
|
||||
root = createRoot();
|
||||
|
||||
await root.preboot();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { createRoot as createkbnTestServerRoot } from '@kbn/core-test-helpers-kbn-server';
|
||||
import { unsafeConsole } from '@kbn/security-hardening';
|
||||
|
||||
function createRootWithRoles(roles: string[]) {
|
||||
return createkbnTestServerRoot({
|
||||
|
@ -39,7 +40,7 @@ describe('node service global context', () => {
|
|||
let mockConsoleLog: jest.SpyInstance;
|
||||
|
||||
beforeAll(async () => {
|
||||
mockConsoleLog = jest.spyOn(global.console, 'log');
|
||||
mockConsoleLog = jest.spyOn(unsafeConsole, 'log');
|
||||
root = createRootWithRoles(roles);
|
||||
|
||||
await root.preboot();
|
||||
|
|
|
@ -157,6 +157,7 @@
|
|||
"@kbn/core-plugins-contracts-server",
|
||||
"@kbn/dev-utils",
|
||||
"@kbn/server-http-tools",
|
||||
"@kbn/security-hardening",
|
||||
"@kbn/core-base-server-mocks",
|
||||
],
|
||||
"exclude": [
|
||||
|
|
|
@ -14,3 +14,5 @@ require('./dns_ipv4_first');
|
|||
|
||||
require('@kbn/babel-register').install();
|
||||
require('./polyfill');
|
||||
|
||||
require('@kbn/security-hardening');
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"kbn_references": [
|
||||
{ "path": "../../tsconfig.json" },
|
||||
"@kbn/babel-register",
|
||||
"@kbn/security-hardening",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -1342,6 +1342,8 @@
|
|||
"@kbn/searchprofiler-plugin/*": ["x-pack/plugins/searchprofiler/*"],
|
||||
"@kbn/security-api-integration-helpers": ["x-pack/test/security_api_integration/packages/helpers"],
|
||||
"@kbn/security-api-integration-helpers/*": ["x-pack/test/security_api_integration/packages/helpers/*"],
|
||||
"@kbn/security-hardening": ["packages/kbn-security-hardening"],
|
||||
"@kbn/security-hardening/*": ["packages/kbn-security-hardening/*"],
|
||||
"@kbn/security-plugin": ["x-pack/plugins/security"],
|
||||
"@kbn/security-plugin/*": ["x-pack/plugins/security/*"],
|
||||
"@kbn/security-plugin-types-common": ["x-pack/packages/security/plugin_types_common"],
|
||||
|
|
|
@ -5748,6 +5748,10 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/security-hardening@link:packages/kbn-security-hardening":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/security-plugin-types-common@link:x-pack/packages/security/plugin_types_common":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue