mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Permissions Policy Reporting (#186892)
## Summary
1. Added top-level `permissionsPolicy` configuration setting.
2. Added support for `report_to` directive.
3. Added support for `Permissions-Policy-Report-Only` header to enable
reporting mode.
4. The [spec](https://www.w3.org/TR/permissions-policy/#reporting)
mentions `featureId` in the reporting body, however the field is
`policyId` in Chromium.
## How to test
- Add in your `kibana.dev.yml`.
```
server.customResponseHeaders.Reporting-Endpoints: violations-endpoint="https://localhost:5601/kibana/internal/security/analytics/_record_violations"
server.securityResponseHeaders.permissionsPolicy: 'microphone=()'
server.securityResponseHeaders.permissionsPolicyReportOnly: 'camera=()'
```
- Make sure you have [dev tools configured for Reporting
API](https://developer.chrome.com/docs/capabilities/web-apis/reporting-api#use_devtools).
- In the browser console invoke `navigator.mediaDevices.getUserMedia({
audio: true, video: true }).catch((e) => {});`
- Open Dev Tools -> Application -> Reporting API.
You should see 2 reports for permissions violation, one with `report`
disposition and another with `enforce` disposition.
<img width="1285" alt="Screenshot 2024-06-27 at 13 36 12"
src="3f3da7f6
-f6b0-4f33-9a81-dff3db0ac2b8">
### Checklist
- [x]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [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
- [x] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
### Related Issue(s)
https://github.com/elastic/kibana/issues/175113,
https://github.com/elastic/kibana/issues/184939
### Release Note
Added support for Permissions Policy reporting.
This commit is contained in:
parent
07a74304fe
commit
cc50c8dc94
19 changed files with 191 additions and 28 deletions
|
@ -85,6 +85,9 @@ enforce even rudimentary CSP rules, though {kib} is still accessible. This
|
|||
configuration is effectively ignored when <<csp-strict, `csp.strict`>> is enabled.
|
||||
*Default: `true`*
|
||||
|
||||
`permissionsPolicy.report_to:`::
|
||||
Add sources for the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy[Permissions Policy `report-to` directive].
|
||||
|
||||
[[elasticsearch-maxSockets]] `elasticsearch.maxSockets`::
|
||||
The maximum number of sockets that can be used for communications with {es}.
|
||||
*Default: `Infinity`*
|
||||
|
@ -424,6 +427,12 @@ Refer to the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissio
|
|||
directives, values, and text format. To disable, set to `null`.
|
||||
*Default:* `camera=(), display-capture=(), fullscreen=(self), geolocation=(), microphone=(), web-share=()`
|
||||
|
||||
[[server-securityResponseHeaders-permissionsPolicyReportOnly]] `server.securityResponseHeaders.permissionsPolicyReportOnly`::
|
||||
experimental[] Controls whether the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy[`Permissions-Policy-Report-Only`] header
|
||||
is used in all responses to the client from the {kib} server, and specifies what value is used. Allowed values are any text value or `null`.
|
||||
Refer to the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy[`Permissions-Policy` documentation] for defined
|
||||
directives, values, and text format.
|
||||
|
||||
[[server-securityResponseHeaders-disableEmbedding]]`server.securityResponseHeaders.disableEmbedding`::
|
||||
Controls whether the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy[`Content-Security-Policy`] and
|
||||
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options[`X-Frame-Options`] headers are configured to disable embedding
|
||||
|
|
|
@ -27,4 +27,8 @@ export {
|
|||
type ExternalUrlConfigType,
|
||||
} from './src/external_url';
|
||||
|
||||
export type { PermissionsPolicyConfigType } from './src/permissions_policy';
|
||||
|
||||
export { permissionsPolicyConfig } from './src/permissions_policy';
|
||||
|
||||
export { createCookieSessionStorageFactory } from './src/cookie_session_storage';
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { config, HttpConfig } from './http_config';
|
||||
import { cspConfig } from './csp';
|
||||
import { permissionsPolicyConfig } from './permissions_policy';
|
||||
import { ExternalUrlConfig } from './external_url';
|
||||
|
||||
const validHostnames = ['www.example.com', '8.8.8.8', '::1', 'localhost', '0.0.0.0'];
|
||||
|
@ -654,7 +655,13 @@ describe('HttpConfig', () => {
|
|||
},
|
||||
});
|
||||
const rawCspConfig = cspConfig.schema.validate({});
|
||||
const httpConfig = new HttpConfig(rawConfig, rawCspConfig, ExternalUrlConfig.DEFAULT);
|
||||
const rawPermissionsPolicyConfig = permissionsPolicyConfig.schema.validate({});
|
||||
const httpConfig = new HttpConfig(
|
||||
rawConfig,
|
||||
rawCspConfig,
|
||||
ExternalUrlConfig.DEFAULT,
|
||||
rawPermissionsPolicyConfig
|
||||
);
|
||||
|
||||
expect(httpConfig.customResponseHeaders).toEqual({
|
||||
string: 'string',
|
||||
|
@ -668,7 +675,13 @@ describe('HttpConfig', () => {
|
|||
it('defaults restrictInternalApis to false', () => {
|
||||
const rawConfig = config.schema.validate({}, {});
|
||||
const rawCspConfig = cspConfig.schema.validate({});
|
||||
const httpConfig = new HttpConfig(rawConfig, rawCspConfig, ExternalUrlConfig.DEFAULT);
|
||||
const rawPermissionsPolicyConfig = permissionsPolicyConfig.schema.validate({});
|
||||
const httpConfig = new HttpConfig(
|
||||
rawConfig,
|
||||
rawCspConfig,
|
||||
ExternalUrlConfig.DEFAULT,
|
||||
rawPermissionsPolicyConfig
|
||||
);
|
||||
expect(httpConfig.restrictInternalApis).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
securityResponseHeadersSchema,
|
||||
} from './security_response_headers_config';
|
||||
import { CdnConfig } from './cdn_config';
|
||||
import { PermissionsPolicyConfigType } from './permissions_policy';
|
||||
|
||||
const SECOND = 1000;
|
||||
|
||||
|
@ -343,14 +344,16 @@ export class HttpConfig implements IHttpConfig {
|
|||
constructor(
|
||||
rawHttpConfig: HttpConfigType,
|
||||
rawCspConfig: CspConfigType,
|
||||
rawExternalUrlConfig: ExternalUrlConfig
|
||||
rawExternalUrlConfig: ExternalUrlConfig,
|
||||
rawPermissionsPolicyConfig: PermissionsPolicyConfigType
|
||||
) {
|
||||
this.autoListen = rawHttpConfig.autoListen;
|
||||
this.host = rawHttpConfig.host;
|
||||
this.port = rawHttpConfig.port;
|
||||
this.cors = rawHttpConfig.cors;
|
||||
const { securityResponseHeaders, disableEmbedding } = parseRawSecurityResponseHeadersConfig(
|
||||
rawHttpConfig.securityResponseHeaders
|
||||
rawHttpConfig.securityResponseHeaders,
|
||||
rawPermissionsPolicyConfig
|
||||
);
|
||||
this.securityResponseHeaders = securityResponseHeaders;
|
||||
this.customResponseHeaders = Object.entries(rawHttpConfig.customResponseHeaders ?? {}).reduce(
|
||||
|
|
|
@ -23,6 +23,7 @@ import { HttpService } from './http_service';
|
|||
import { HttpConfigType, config } from './http_config';
|
||||
import { cspConfig } from './csp';
|
||||
import { externalUrlConfig, ExternalUrlConfig } from './external_url';
|
||||
import { permissionsPolicyConfig } from './permissions_policy';
|
||||
|
||||
const logger = loggingSystemMock.create();
|
||||
const env = Env.createDefault(REPO_ROOT, getEnvOptions());
|
||||
|
@ -42,6 +43,7 @@ const createConfigService = (value: Partial<HttpConfigType> = {}) => {
|
|||
configService.setSchema(config.path, config.schema);
|
||||
configService.setSchema(cspConfig.path, cspConfig.schema);
|
||||
configService.setSchema(externalUrlConfig.path, externalUrlConfig.schema);
|
||||
configService.setSchema(permissionsPolicyConfig.path, permissionsPolicyConfig.schema);
|
||||
return configService;
|
||||
};
|
||||
const contextPreboot = contextServiceMock.createPrebootContract();
|
||||
|
|
|
@ -29,6 +29,7 @@ import type {
|
|||
import { Router, RouterOptions } from '@kbn/core-http-router-server-internal';
|
||||
|
||||
import { CspConfigType, cspConfig } from './csp';
|
||||
import { PermissionsPolicyConfigType, permissionsPolicyConfig } from './permissions_policy';
|
||||
import { HttpConfig, HttpConfigType, config as httpConfig } from './http_config';
|
||||
import { HttpServer } from './http_server';
|
||||
import { HttpsRedirectServer } from './https_redirect_server';
|
||||
|
@ -76,7 +77,13 @@ export class HttpService
|
|||
configService.atPath<HttpConfigType>(httpConfig.path, { ignoreUnchanged: false }),
|
||||
configService.atPath<CspConfigType>(cspConfig.path),
|
||||
configService.atPath<ExternalUrlConfigType>(externalUrlConfig.path),
|
||||
]).pipe(map(([http, csp, externalUrl]) => new HttpConfig(http, csp, externalUrl)));
|
||||
configService.atPath<PermissionsPolicyConfigType>(permissionsPolicyConfig.path),
|
||||
]).pipe(
|
||||
map(
|
||||
([http, csp, externalUrl, permissionsPolicy]) =>
|
||||
new HttpConfig(http, csp, externalUrl, permissionsPolicy)
|
||||
)
|
||||
);
|
||||
const shutdownTimeout$ = this.config$.pipe(map(({ shutdownTimeout }) => shutdownTimeout));
|
||||
this.prebootServer = new HttpServer(coreContext, 'Preboot', shutdownTimeout$);
|
||||
this.httpServer = new HttpServer(coreContext, 'Kibana', shutdownTimeout$);
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 { TypeOf, schema } from '@kbn/config-schema';
|
||||
import type { ServiceConfigDescriptor } from '@kbn/core-base-server-internal';
|
||||
|
||||
const configSchema = schema.object({
|
||||
report_to: schema.arrayOf(schema.string(), {
|
||||
defaultValue: [],
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type PermissionsPolicyConfigType = TypeOf<typeof configSchema>;
|
||||
|
||||
export const permissionsPolicyConfig: ServiceConfigDescriptor<PermissionsPolicyConfigType> = {
|
||||
path: 'permissionsPolicy',
|
||||
schema: configSchema,
|
||||
};
|
|
@ -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 { permissionsPolicyConfig } from './config';
|
||||
export type { PermissionsPolicyConfigType } from './config';
|
|
@ -14,7 +14,7 @@ import {
|
|||
describe('parseRawSecurityResponseHeadersConfig', () => {
|
||||
it('returns default values', () => {
|
||||
const config = schema.validate({});
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.disableEmbedding).toBe(false);
|
||||
expect(result.securityResponseHeaders).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
@ -30,7 +30,7 @@ describe('parseRawSecurityResponseHeadersConfig', () => {
|
|||
it('a custom value results in the expected Strict-Transport-Security header', () => {
|
||||
const strictTransportSecurity = 'max-age=31536000; includeSubDomains';
|
||||
const config = schema.validate({ strictTransportSecurity });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['Strict-Transport-Security']).toEqual(
|
||||
strictTransportSecurity
|
||||
);
|
||||
|
@ -38,7 +38,7 @@ describe('parseRawSecurityResponseHeadersConfig', () => {
|
|||
|
||||
it('a null value removes the Strict-Transport-Security header', () => {
|
||||
const config = schema.validate({ strictTransportSecurity: null });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['Strict-Transport-Security']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
@ -47,13 +47,13 @@ describe('parseRawSecurityResponseHeadersConfig', () => {
|
|||
it('a custom value results in the expected X-Content-Type-Options header', () => {
|
||||
const xContentTypeOptions = 'nosniff'; // there is no other valid value to test with
|
||||
const config = schema.validate({ xContentTypeOptions });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['X-Content-Type-Options']).toEqual(xContentTypeOptions);
|
||||
});
|
||||
|
||||
it('a null value removes the X-Content-Type-Options header', () => {
|
||||
const config = schema.validate({ xContentTypeOptions: null });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['X-Content-Type-Options']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
@ -62,13 +62,13 @@ describe('parseRawSecurityResponseHeadersConfig', () => {
|
|||
it('a custom value results in the expected Referrer-Policy header', () => {
|
||||
const referrerPolicy = 'strict-origin-when-cross-origin';
|
||||
const config = schema.validate({ referrerPolicy });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['Referrer-Policy']).toEqual(referrerPolicy);
|
||||
});
|
||||
|
||||
it('a null value removes the Referrer-Policy header', () => {
|
||||
const config = schema.validate({ referrerPolicy: null });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['Referrer-Policy']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
@ -77,21 +77,45 @@ describe('parseRawSecurityResponseHeadersConfig', () => {
|
|||
it('a custom value results in the expected Permissions-Policy header', () => {
|
||||
const permissionsPolicy = 'display-capture=(self)';
|
||||
const config = schema.validate({ permissionsPolicy });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['Permissions-Policy']).toEqual(permissionsPolicy);
|
||||
});
|
||||
|
||||
it('a null value removes the Permissions-Policy header', () => {
|
||||
const config = schema.validate({ permissionsPolicy: null });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['Permissions-Policy']).toBeUndefined();
|
||||
});
|
||||
|
||||
it('includes report-to directive if it is provided', () => {
|
||||
const config = schema.validate({ permissionsPolicy: 'display-capture=(self)' });
|
||||
const result = parse(config, { report_to: ['violations-endpoint'] });
|
||||
expect(result.securityResponseHeaders['Permissions-Policy']).toEqual(
|
||||
'display-capture=(self);report-to=violations-endpoint'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('permissionsPolicyReportOnly', () => {
|
||||
it('a custom value results in the expected Permissions-Policy-Report-Only header', () => {
|
||||
const config = schema.validate({ permissionsPolicyReportOnly: 'display-capture=(self)' });
|
||||
const result = parse(config, { report_to: ['violations-endpoint'] });
|
||||
expect(result.securityResponseHeaders['Permissions-Policy-Report-Only']).toEqual(
|
||||
'display-capture=(self);report-to=violations-endpoint'
|
||||
);
|
||||
});
|
||||
|
||||
it('includes Permissions-Policy-Report-Only only if report-to directive is set', () => {
|
||||
const config = schema.validate({ permissionsPolicy: 'display-capture=(self)' });
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['Permissions-Policy-Report-Only']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('disableEmbedding', () => {
|
||||
it('a true value results in the expected X-Frame-Options header and expected disableEmbedding result value', () => {
|
||||
const config = schema.validate({ disableEmbedding: true });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['X-Frame-Options']).toMatchInlineSnapshot(
|
||||
`"SAMEORIGIN"`
|
||||
);
|
||||
|
@ -103,7 +127,7 @@ describe('parseRawSecurityResponseHeadersConfig', () => {
|
|||
it('a custom value results in the expected Cross-Origin-Opener-Policy header', () => {
|
||||
const crossOriginOpenerPolicy = 'same-origin-allow-popups';
|
||||
const config = schema.validate({ crossOriginOpenerPolicy });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['Cross-Origin-Opener-Policy']).toEqual(
|
||||
crossOriginOpenerPolicy
|
||||
);
|
||||
|
@ -111,7 +135,7 @@ describe('parseRawSecurityResponseHeadersConfig', () => {
|
|||
|
||||
it('a null value removes the Cross-Origin-Opener-Policy header', () => {
|
||||
const config = schema.validate({ crossOriginOpenerPolicy: null });
|
||||
const result = parse(config);
|
||||
const result = parse(config, { report_to: [] });
|
||||
expect(result.securityResponseHeaders['Cross-Origin-Opener-Policy']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import { schema, TypeOf } from '@kbn/config-schema';
|
||||
import { PermissionsPolicyConfigType } from './permissions_policy';
|
||||
|
||||
export const securityResponseHeadersSchema = schema.object({
|
||||
strictTransportSecurity: schema.oneOf([schema.string(), schema.literal(null)], {
|
||||
|
@ -38,6 +39,7 @@ export const securityResponseHeadersSchema = schema.object({
|
|||
defaultValue:
|
||||
'camera=(), display-capture=(), fullscreen=(self), geolocation=(), microphone=(), web-share=()',
|
||||
}),
|
||||
permissionsPolicyReportOnly: schema.maybe(schema.oneOf([schema.string(), schema.literal(null)])),
|
||||
disableEmbedding: schema.boolean({ defaultValue: false }), // is used to control X-Frame-Options and CSP headers
|
||||
crossOriginOpenerPolicy: schema.oneOf(
|
||||
// See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy
|
||||
|
@ -58,7 +60,8 @@ export const securityResponseHeadersSchema = schema.object({
|
|||
* @internal
|
||||
*/
|
||||
export function parseRawSecurityResponseHeadersConfig(
|
||||
raw: TypeOf<typeof securityResponseHeadersSchema>
|
||||
raw: TypeOf<typeof securityResponseHeadersSchema>,
|
||||
rawPermissionsPolicyConfig: PermissionsPolicyConfigType
|
||||
) {
|
||||
const securityResponseHeaders: Record<string, string | string[]> = {};
|
||||
const { disableEmbedding } = raw;
|
||||
|
@ -72,9 +75,21 @@ export function parseRawSecurityResponseHeadersConfig(
|
|||
if (raw.referrerPolicy) {
|
||||
securityResponseHeaders['Referrer-Policy'] = raw.referrerPolicy;
|
||||
}
|
||||
|
||||
const reportTo = rawPermissionsPolicyConfig.report_to.length
|
||||
? `;report-to=${rawPermissionsPolicyConfig.report_to}`
|
||||
: '';
|
||||
|
||||
if (raw.permissionsPolicy) {
|
||||
securityResponseHeaders['Permissions-Policy'] = raw.permissionsPolicy;
|
||||
securityResponseHeaders['Permissions-Policy'] = `${raw.permissionsPolicy}${reportTo}`;
|
||||
}
|
||||
|
||||
if (raw.permissionsPolicyReportOnly && reportTo) {
|
||||
securityResponseHeaders[
|
||||
'Permissions-Policy-Report-Only'
|
||||
] = `${raw.permissionsPolicyReportOnly}${reportTo}`;
|
||||
}
|
||||
|
||||
if (raw.crossOriginOpenerPolicy) {
|
||||
securityResponseHeaders['Cross-Origin-Opener-Policy'] = raw.crossOriginOpenerPolicy;
|
||||
}
|
||||
|
|
|
@ -91,6 +91,11 @@ export const createConfigService = ({
|
|||
...csp,
|
||||
});
|
||||
}
|
||||
if (path === 'permissionsPolicy') {
|
||||
return new BehaviorSubject({
|
||||
report_to: [],
|
||||
});
|
||||
}
|
||||
throw new Error(`Unexpected config path: ${path}`);
|
||||
});
|
||||
return configService;
|
||||
|
|
|
@ -14,7 +14,12 @@ import { coreDeprecationProvider } from '@kbn/core-config-server-internal';
|
|||
import { nodeConfig } from '@kbn/core-node-server-internal';
|
||||
import { pidConfig } from '@kbn/core-environment-server-internal';
|
||||
import { executionContextConfig } from '@kbn/core-execution-context-server-internal';
|
||||
import { config as httpConfig, cspConfig, externalUrlConfig } from '@kbn/core-http-server-internal';
|
||||
import {
|
||||
config as httpConfig,
|
||||
cspConfig,
|
||||
externalUrlConfig,
|
||||
permissionsPolicyConfig,
|
||||
} from '@kbn/core-http-server-internal';
|
||||
import { config as elasticsearchConfig } from '@kbn/core-elasticsearch-server-internal';
|
||||
import { config as coreAppConfig } from '@kbn/core-apps-server-internal';
|
||||
import { opsConfig } from '@kbn/core-metrics-server-internal';
|
||||
|
@ -56,6 +61,7 @@ export function registerServiceConfig(configService: ConfigService) {
|
|||
serverlessConfig,
|
||||
statusConfig,
|
||||
uiSettingsConfig,
|
||||
permissionsPolicyConfig,
|
||||
];
|
||||
|
||||
configService.addDeprecationProvider(rootConfigPath, coreDeprecationProvider);
|
||||
|
|
|
@ -17,12 +17,14 @@ import {
|
|||
config as httpConfig,
|
||||
cspConfig,
|
||||
externalUrlConfig,
|
||||
permissionsPolicyConfig,
|
||||
} from '@kbn/core-http-server-internal';
|
||||
import { mockCoreContext } from '@kbn/core-base-server-mocks';
|
||||
import type { Logger } from '@kbn/logging';
|
||||
|
||||
const CSP_CONFIG = cspConfig.schema.validate({});
|
||||
const EXTERNAL_URL_CONFIG = externalUrlConfig.schema.validate({});
|
||||
const PERMISSIONS_POLICY_CONFIG = permissionsPolicyConfig.schema.validate({});
|
||||
|
||||
describe('Http2 - Smoke tests', () => {
|
||||
let server: HttpServer;
|
||||
|
@ -56,7 +58,7 @@ describe('Http2 - Smoke tests', () => {
|
|||
},
|
||||
shutdownTimeout: '5s',
|
||||
});
|
||||
config = new HttpConfig(rawConfig, CSP_CONFIG, EXTERNAL_URL_CONFIG);
|
||||
config = new HttpConfig(rawConfig, CSP_CONFIG, EXTERNAL_URL_CONFIG, PERMISSIONS_POLICY_CONFIG);
|
||||
server = new HttpServer(coreContext, 'tests', of(config.shutdownTimeout));
|
||||
});
|
||||
|
||||
|
|
|
@ -14,12 +14,14 @@ import {
|
|||
config as httpConfig,
|
||||
cspConfig,
|
||||
externalUrlConfig,
|
||||
permissionsPolicyConfig,
|
||||
} from '@kbn/core-http-server-internal';
|
||||
import { flattenCertificateChain, fetchPeerCertificate, isServerTLS } from './tls_utils';
|
||||
|
||||
describe('setTlsConfig', () => {
|
||||
const CSP_CONFIG = cspConfig.schema.validate({});
|
||||
const EXTERNAL_URL_CONFIG = externalUrlConfig.schema.validate({});
|
||||
const PERMISSIONS_POLICY_CONFIG = permissionsPolicyConfig.schema.validate({});
|
||||
|
||||
beforeAll(() => {
|
||||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
||||
|
@ -39,7 +41,12 @@ describe('setTlsConfig', () => {
|
|||
},
|
||||
shutdownTimeout: '1s',
|
||||
});
|
||||
const firstConfig = new HttpConfig(rawHttpConfig, CSP_CONFIG, EXTERNAL_URL_CONFIG);
|
||||
const firstConfig = new HttpConfig(
|
||||
rawHttpConfig,
|
||||
CSP_CONFIG,
|
||||
EXTERNAL_URL_CONFIG,
|
||||
PERMISSIONS_POLICY_CONFIG
|
||||
);
|
||||
|
||||
const serverOptions = getServerOptions(firstConfig);
|
||||
const server = createServer(serverOptions);
|
||||
|
@ -85,7 +92,12 @@ describe('setTlsConfig', () => {
|
|||
shutdownTimeout: '1s',
|
||||
});
|
||||
|
||||
const secondConfig = new HttpConfig(secondRawConfig, CSP_CONFIG, EXTERNAL_URL_CONFIG);
|
||||
const secondConfig = new HttpConfig(
|
||||
secondRawConfig,
|
||||
CSP_CONFIG,
|
||||
EXTERNAL_URL_CONFIG,
|
||||
PERMISSIONS_POLICY_CONFIG
|
||||
);
|
||||
|
||||
setTlsConfig(server, secondConfig.ssl);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
config as httpConfig,
|
||||
cspConfig,
|
||||
externalUrlConfig,
|
||||
permissionsPolicyConfig,
|
||||
} from '@kbn/core-http-server-internal';
|
||||
import { isServerTLS, flattenCertificateChain, fetchPeerCertificate } from './tls_utils';
|
||||
import { mockCoreContext } from '@kbn/core-base-server-mocks';
|
||||
|
@ -24,6 +25,7 @@ import type { Logger } from '@kbn/logging';
|
|||
|
||||
const CSP_CONFIG = cspConfig.schema.validate({});
|
||||
const EXTERNAL_URL_CONFIG = externalUrlConfig.schema.validate({});
|
||||
const PERMISSIONS_POLICY_CONFIG = permissionsPolicyConfig.schema.validate({});
|
||||
const enhanceWithContext = (fn: (...args: any[]) => any) => fn.bind(null, {});
|
||||
|
||||
describe('HttpServer - TLS config', () => {
|
||||
|
@ -54,7 +56,12 @@ describe('HttpServer - TLS config', () => {
|
|||
},
|
||||
shutdownTimeout: '1s',
|
||||
});
|
||||
const firstConfig = new HttpConfig(rawHttpConfig, CSP_CONFIG, EXTERNAL_URL_CONFIG);
|
||||
const firstConfig = new HttpConfig(
|
||||
rawHttpConfig,
|
||||
CSP_CONFIG,
|
||||
EXTERNAL_URL_CONFIG,
|
||||
PERMISSIONS_POLICY_CONFIG
|
||||
);
|
||||
|
||||
const config$ = new BehaviorSubject(firstConfig);
|
||||
|
||||
|
@ -109,7 +116,12 @@ describe('HttpServer - TLS config', () => {
|
|||
shutdownTimeout: '1s',
|
||||
});
|
||||
|
||||
const secondConfig = new HttpConfig(secondRawConfig, CSP_CONFIG, EXTERNAL_URL_CONFIG);
|
||||
const secondConfig = new HttpConfig(
|
||||
secondRawConfig,
|
||||
CSP_CONFIG,
|
||||
EXTERNAL_URL_CONFIG,
|
||||
PERMISSIONS_POLICY_CONFIG
|
||||
);
|
||||
config$.next(secondConfig);
|
||||
|
||||
const secondCertificate = await fetchPeerCertificate(firstConfig.host, firstConfig.port);
|
||||
|
|
|
@ -40,6 +40,7 @@ kibana_vars=(
|
|||
csp.report_uri
|
||||
csp.report_to
|
||||
csp.report_only.form_action
|
||||
permissionsPolicy.report_to
|
||||
data.autocomplete.valueSuggestions.terminateAfter
|
||||
data.autocomplete.valueSuggestions.timeout
|
||||
data.search.asyncSearch.waitForCompletion
|
||||
|
|
|
@ -235,7 +235,14 @@ const permissionsPolicyViolation: EventTypeOpts<PermissionsPolicyViolationEvent>
|
|||
type: 'text',
|
||||
_meta: {
|
||||
description: '"featureId" field of Reporting API permissions policy violation report.',
|
||||
optional: false,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
policyId: {
|
||||
type: 'text',
|
||||
_meta: {
|
||||
description: '"policyId" field of Reporting API permissions policy violation report.',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
sourceFile: {
|
||||
|
|
|
@ -95,7 +95,7 @@ describe('POST /internal/security/analytics/_record_violations', () => {
|
|||
user_agent: 'jest',
|
||||
body: {
|
||||
disposition: 'report',
|
||||
featureId: 'camera',
|
||||
policyId: 'camera',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -90,8 +90,13 @@ export const permissionsPolicyViolationReportSchema = schema.object(
|
|||
{
|
||||
/**
|
||||
* The string identifying the policy-controlled feature whose policy has been violated. This string can be used for grouping and counting related reports.
|
||||
* Spec mentions featureId, however the report that is sent from Chrome has policyId. This is to handle both cases.
|
||||
*/
|
||||
featureId: schema.string(),
|
||||
policyId: schema.maybe(schema.string()),
|
||||
/**
|
||||
* The string identifying the policy-controlled feature whose policy has been violated. This string can be used for grouping and counting related reports.
|
||||
*/
|
||||
featureId: schema.maybe(schema.string()),
|
||||
/**
|
||||
* If known, the file where the violation occured, or null otherwise.
|
||||
*/
|
||||
|
@ -140,6 +145,7 @@ export function defineRecordViolations({ router, analyticsService }: RouteDefini
|
|||
schema.oneOf([cspViolationReportSchema, permissionsPolicyViolationReportSchema])
|
||||
),
|
||||
cspViolationReportSchema,
|
||||
permissionsPolicyViolationReportSchema,
|
||||
]),
|
||||
},
|
||||
options: {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue