mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[8.0] Remove support for configuring csp.rules (#114379)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
6cb91c472d
commit
44c9611bd9
19 changed files with 54 additions and 603 deletions
|
@ -24,7 +24,6 @@ The constructor for this class is marked as internal. Third-party code should no
|
|||
| [DEFAULT](./kibana-plugin-core-server.cspconfig.default.md) | <code>static</code> | <code>CspConfig</code> | |
|
||||
| [disableEmbedding](./kibana-plugin-core-server.cspconfig.disableembedding.md) | | <code>boolean</code> | |
|
||||
| [header](./kibana-plugin-core-server.cspconfig.header.md) | | <code>string</code> | |
|
||||
| [rules](./kibana-plugin-core-server.cspconfig.rules.md) | | <code>string[]</code> | |
|
||||
| [strict](./kibana-plugin-core-server.cspconfig.strict.md) | | <code>boolean</code> | |
|
||||
| [warnLegacyBrowsers](./kibana-plugin-core-server.cspconfig.warnlegacybrowsers.md) | | <code>boolean</code> | |
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CspConfig](./kibana-plugin-core-server.cspconfig.md) > [rules](./kibana-plugin-core-server.cspconfig.rules.md)
|
||||
|
||||
## CspConfig.rules property
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
readonly rules: string[];
|
||||
```
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
## ICspConfig.disableEmbedding property
|
||||
|
||||
Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled \*and\* no custom rules have been defined, a restrictive 'frame-ancestors' rule will be added to the default CSP rules.
|
||||
Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled, a restrictive 'frame-ancestors' rule will be added to the default CSP rules.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
|
|
|
@ -16,9 +16,8 @@ export interface ICspConfig
|
|||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [disableEmbedding](./kibana-plugin-core-server.icspconfig.disableembedding.md) | <code>boolean</code> | Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled \*and\* no custom rules have been defined, a restrictive 'frame-ancestors' rule will be added to the default CSP rules. |
|
||||
| [disableEmbedding](./kibana-plugin-core-server.icspconfig.disableembedding.md) | <code>boolean</code> | Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled, a restrictive 'frame-ancestors' rule will be added to the default CSP rules. |
|
||||
| [header](./kibana-plugin-core-server.icspconfig.header.md) | <code>string</code> | The CSP rules in a formatted directives string for use in a <code>Content-Security-Policy</code> header. |
|
||||
| [rules](./kibana-plugin-core-server.icspconfig.rules.md) | <code>string[]</code> | The CSP rules used for Kibana. |
|
||||
| [strict](./kibana-plugin-core-server.icspconfig.strict.md) | <code>boolean</code> | Specify whether browsers that do not support CSP should be able to use Kibana. Use <code>true</code> to block and <code>false</code> to allow. |
|
||||
| [warnLegacyBrowsers](./kibana-plugin-core-server.icspconfig.warnlegacybrowsers.md) | <code>boolean</code> | Specify whether users with legacy browsers should be warned about their lack of Kibana security compliance. |
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ICspConfig](./kibana-plugin-core-server.icspconfig.md) > [rules](./kibana-plugin-core-server.icspconfig.rules.md)
|
||||
|
||||
## ICspConfig.rules property
|
||||
|
||||
The CSP rules used for Kibana.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
readonly rules: string[];
|
||||
```
|
|
@ -48,6 +48,12 @@ for example, `logstash-*`.
|
|||
|
||||
*Impact:* To allow Kibana to function for these legacy browsers, set `csp.strict: false`. Since this is about enforcing a security protocol, we *strongly discourage* disabling `csp.strict` unless it is critical that you support Internet Explorer 11.
|
||||
|
||||
[float]
|
||||
==== Configuring content security policy rules is no longer supported
|
||||
*Details:* Configuring `csp.rules` is removed in favor of per-directive specific configuration. Configuring the default `csp.script_src`, `csp.workers_src` and `csp.style_src` values is not required.
|
||||
|
||||
*Impact:* Configure per-directive sources instead. See https://github.com/elastic/kibana/pull/102059 for more details.
|
||||
|
||||
[float]
|
||||
==== Default logging timezone is now the system's timezone
|
||||
*Details:* In prior releases the timezone used in logs defaulted to UTC. We now use the host machine's timezone by default.
|
||||
|
|
|
@ -26,13 +26,6 @@ Toggling this causes the server to regenerate assets on the next startup,
|
|||
which may cause a delay before pages start being served.
|
||||
Set to `false` to disable Console. *Default: `true`*
|
||||
|
||||
| `csp.rules:`
|
||||
| deprecated:[7.14.0,"In 8.0 and later, this setting will no longer be supported."]
|
||||
A https://w3c.github.io/webappsec-csp/[Content Security Policy] template
|
||||
that disables certain unnecessary and potentially insecure capabilities in
|
||||
the browser. It is strongly recommended that you keep the default CSP rules
|
||||
that ship with {kib}.
|
||||
|
||||
| `csp.script_src:`
|
||||
| Add sources for the https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src[Content Security Policy `script-src` directive].
|
||||
|
||||
|
@ -502,8 +495,7 @@ To disable, set to `null`. *Default:* `null`
|
|||
| 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
|
||||
{kib} in other webpages using iframes. When set to `true`, secure headers are used to disable embedding, which adds the `frame-ancestors:
|
||||
'self'` directive to the `Content-Security-Policy` response header (if you are using the default CSP rules), and adds the `X-Frame-Options:
|
||||
SAMEORIGIN` response header. *Default:* `false`
|
||||
'self'` directive to the `Content-Security-Policy` response header and adds the `X-Frame-Options: SAMEORIGIN` response header. *Default:* `false`
|
||||
|
||||
| `server.customResponseHeaders:` {ess-icon}
|
||||
| Header names and values to
|
||||
|
|
|
@ -83,100 +83,4 @@ describe('core deprecations', () => {
|
|||
expect(messages).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cspRulesDeprecation', () => {
|
||||
describe('with nonce source', () => {
|
||||
it('logs a warning', () => {
|
||||
const settings = {
|
||||
csp: {
|
||||
rules: [`script-src 'self' 'nonce-{nonce}'`],
|
||||
},
|
||||
};
|
||||
const { messages } = applyCoreDeprecations(settings);
|
||||
expect(messages).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"csp.rules no longer supports the {nonce} syntax. Replacing with 'self' in script-src",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('replaces a nonce', () => {
|
||||
expect(
|
||||
applyCoreDeprecations({ csp: { rules: [`script-src 'nonce-{nonce}'`] } }).migrated.csp
|
||||
.rules
|
||||
).toEqual([`script-src 'self'`]);
|
||||
expect(
|
||||
applyCoreDeprecations({ csp: { rules: [`script-src 'unsafe-eval' 'nonce-{nonce}'`] } })
|
||||
.migrated.csp.rules
|
||||
).toEqual([`script-src 'unsafe-eval' 'self'`]);
|
||||
});
|
||||
|
||||
it('removes a quoted nonce', () => {
|
||||
expect(
|
||||
applyCoreDeprecations({ csp: { rules: [`script-src 'self' 'nonce-{nonce}'`] } }).migrated
|
||||
.csp.rules
|
||||
).toEqual([`script-src 'self'`]);
|
||||
expect(
|
||||
applyCoreDeprecations({ csp: { rules: [`script-src 'nonce-{nonce}' 'self'`] } }).migrated
|
||||
.csp.rules
|
||||
).toEqual([`script-src 'self'`]);
|
||||
});
|
||||
|
||||
it('removes a non-quoted nonce', () => {
|
||||
expect(
|
||||
applyCoreDeprecations({ csp: { rules: [`script-src 'self' nonce-{nonce}`] } }).migrated
|
||||
.csp.rules
|
||||
).toEqual([`script-src 'self'`]);
|
||||
expect(
|
||||
applyCoreDeprecations({ csp: { rules: [`script-src nonce-{nonce} 'self'`] } }).migrated
|
||||
.csp.rules
|
||||
).toEqual([`script-src 'self'`]);
|
||||
});
|
||||
|
||||
it('removes a strange nonce', () => {
|
||||
expect(
|
||||
applyCoreDeprecations({ csp: { rules: [`script-src 'self' blah-{nonce}-wow`] } }).migrated
|
||||
.csp.rules
|
||||
).toEqual([`script-src 'self'`]);
|
||||
});
|
||||
|
||||
it('removes multiple nonces', () => {
|
||||
expect(
|
||||
applyCoreDeprecations({
|
||||
csp: {
|
||||
rules: [
|
||||
`script-src 'nonce-{nonce}' 'self' blah-{nonce}-wow`,
|
||||
`style-src 'nonce-{nonce}' 'self'`,
|
||||
],
|
||||
},
|
||||
}).migrated.csp.rules
|
||||
).toEqual([`script-src 'self'`, `style-src 'self'`]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('without self source', () => {
|
||||
it('logs a warning', () => {
|
||||
const { messages } = applyCoreDeprecations({
|
||||
csp: { rules: [`script-src 'unsafe-eval'`] },
|
||||
});
|
||||
expect(messages).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"csp.rules must contain the 'self' source. Automatically adding to script-src.",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('adds self', () => {
|
||||
expect(
|
||||
applyCoreDeprecations({ csp: { rules: [`script-src 'unsafe-eval'`] } }).migrated.csp.rules
|
||||
).toEqual([`script-src 'unsafe-eval' 'self'`]);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not add self to other policies', () => {
|
||||
expect(
|
||||
applyCoreDeprecations({ csp: { rules: [`worker-src blob:`] } }).migrated.csp.rules
|
||||
).toEqual([`worker-src blob:`]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -45,64 +45,7 @@ const rewriteCorsSettings: ConfigDeprecation = (settings, fromPath, addDeprecati
|
|||
}
|
||||
};
|
||||
|
||||
const cspRulesDeprecation: ConfigDeprecation = (settings, fromPath, addDeprecation) => {
|
||||
const NONCE_STRING = `{nonce}`;
|
||||
// Policies that should include the 'self' source
|
||||
const SELF_POLICIES = Object.freeze(['script-src', 'style-src']);
|
||||
const SELF_STRING = `'self'`;
|
||||
|
||||
const rules: string[] = settings.csp?.rules;
|
||||
if (rules) {
|
||||
const parsed = new Map(
|
||||
rules.map((ruleStr) => {
|
||||
const parts = ruleStr.split(/\s+/);
|
||||
return [parts[0], parts.slice(1)];
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
set: [
|
||||
{
|
||||
path: 'csp.rules',
|
||||
value: [...parsed].map(([policy, sourceList]) => {
|
||||
if (sourceList.find((source) => source.includes(NONCE_STRING))) {
|
||||
addDeprecation({
|
||||
message: `csp.rules no longer supports the {nonce} syntax. Replacing with 'self' in ${policy}`,
|
||||
correctiveActions: {
|
||||
manualSteps: [`Replace {nonce} syntax with 'self' in ${policy}`],
|
||||
},
|
||||
});
|
||||
sourceList = sourceList.filter((source) => !source.includes(NONCE_STRING));
|
||||
|
||||
// Add 'self' if not present
|
||||
if (!sourceList.find((source) => source.includes(SELF_STRING))) {
|
||||
sourceList.push(SELF_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
SELF_POLICIES.includes(policy) &&
|
||||
!sourceList.find((source) => source.includes(SELF_STRING))
|
||||
) {
|
||||
addDeprecation({
|
||||
message: `csp.rules must contain the 'self' source. Automatically adding to ${policy}.`,
|
||||
correctiveActions: {
|
||||
manualSteps: [`Add 'self' source to ${policy}.`],
|
||||
},
|
||||
});
|
||||
sourceList.push(SELF_STRING);
|
||||
}
|
||||
|
||||
return `${policy} ${sourceList.join(' ')}`.trim();
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export const coreDeprecationProvider: ConfigDeprecationProvider = ({ rename, unusedFromRoot }) => [
|
||||
export const coreDeprecationProvider: ConfigDeprecationProvider = () => [
|
||||
rewriteCorsSettings,
|
||||
rewriteBasePathDeprecation,
|
||||
cspRulesDeprecation,
|
||||
];
|
||||
|
|
|
@ -80,21 +80,6 @@ describe('config.validate()', () => {
|
|||
).not.toThrow();
|
||||
});
|
||||
|
||||
it(`throws if 'rules' is also specified`, () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
rules: [
|
||||
`script-src 'unsafe-eval' 'self'`,
|
||||
`worker-src 'unsafe-eval' 'self'`,
|
||||
`style-src 'unsafe-eval' 'self'`,
|
||||
],
|
||||
script_src: [`'self'`],
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if using an `nonce-*` value', () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -104,6 +89,7 @@ describe('config.validate()', () => {
|
|||
`"[script_src]: using \\"nonce-*\\" is considered insecure and is not allowed"`
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if using `none` or `'none'`", () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -124,21 +110,6 @@ describe('config.validate()', () => {
|
|||
});
|
||||
|
||||
describe(`"worker_src"`, () => {
|
||||
it(`throws if 'rules' is also specified`, () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
rules: [
|
||||
`script-src 'unsafe-eval' 'self'`,
|
||||
`worker-src 'unsafe-eval' 'self'`,
|
||||
`style-src 'unsafe-eval' 'self'`,
|
||||
],
|
||||
worker_src: [`'self'`],
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if using an `nonce-*` value', () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -148,6 +119,7 @@ describe('config.validate()', () => {
|
|||
`"[worker_src]: using \\"nonce-*\\" is considered insecure and is not allowed"`
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if using `none` or `'none'`", () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -168,21 +140,6 @@ describe('config.validate()', () => {
|
|||
});
|
||||
|
||||
describe(`"style_src"`, () => {
|
||||
it(`throws if 'rules' is also specified`, () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
rules: [
|
||||
`script-src 'unsafe-eval' 'self'`,
|
||||
`worker-src 'unsafe-eval' 'self'`,
|
||||
`style-src 'unsafe-eval' 'self'`,
|
||||
],
|
||||
style_src: [`'self'`],
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if using an `nonce-*` value', () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -192,6 +149,7 @@ describe('config.validate()', () => {
|
|||
`"[style_src]: using \\"nonce-*\\" is considered insecure and is not allowed"`
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if using `none` or `'none'`", () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -212,21 +170,6 @@ describe('config.validate()', () => {
|
|||
});
|
||||
|
||||
describe(`"connect_src"`, () => {
|
||||
it(`throws if 'rules' is also specified`, () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
rules: [
|
||||
`script-src 'unsafe-eval' 'self'`,
|
||||
`worker-src 'unsafe-eval' 'self'`,
|
||||
`style-src 'unsafe-eval' 'self'`,
|
||||
],
|
||||
connect_src: [`'self'`],
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if using an `nonce-*` value', () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -236,6 +179,7 @@ describe('config.validate()', () => {
|
|||
`"[connect_src]: using \\"nonce-*\\" is considered insecure and is not allowed"`
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if using `none` or `'none'`", () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -256,21 +200,6 @@ describe('config.validate()', () => {
|
|||
});
|
||||
|
||||
describe(`"default_src"`, () => {
|
||||
it(`throws if 'rules' is also specified`, () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
rules: [
|
||||
`script-src 'unsafe-eval' 'self'`,
|
||||
`worker-src 'unsafe-eval' 'self'`,
|
||||
`style-src 'unsafe-eval' 'self'`,
|
||||
],
|
||||
default_src: [`'self'`],
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if using an `nonce-*` value', () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -280,6 +209,7 @@ describe('config.validate()', () => {
|
|||
`"[default_src]: using \\"nonce-*\\" is considered insecure and is not allowed"`
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if using `none` or `'none'`", () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -300,21 +230,6 @@ describe('config.validate()', () => {
|
|||
});
|
||||
|
||||
describe(`"font_src"`, () => {
|
||||
it(`throws if 'rules' is also specified`, () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
rules: [
|
||||
`script-src 'unsafe-eval' 'self'`,
|
||||
`worker-src 'unsafe-eval' 'self'`,
|
||||
`style-src 'unsafe-eval' 'self'`,
|
||||
],
|
||||
font_src: [`'self'`],
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if using an `nonce-*` value', () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -324,6 +239,7 @@ describe('config.validate()', () => {
|
|||
`"[font_src]: using \\"nonce-*\\" is considered insecure and is not allowed"`
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if using `none` or `'none'`", () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -344,21 +260,6 @@ describe('config.validate()', () => {
|
|||
});
|
||||
|
||||
describe(`"frame_src"`, () => {
|
||||
it(`throws if 'rules' is also specified`, () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
rules: [
|
||||
`script-src 'unsafe-eval' 'self'`,
|
||||
`worker-src 'unsafe-eval' 'self'`,
|
||||
`style-src 'unsafe-eval' 'self'`,
|
||||
],
|
||||
frame_src: [`'self'`],
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if using an `nonce-*` value', () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -368,6 +269,7 @@ describe('config.validate()', () => {
|
|||
`"[frame_src]: using \\"nonce-*\\" is considered insecure and is not allowed"`
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if using `none` or `'none'`", () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -388,21 +290,6 @@ describe('config.validate()', () => {
|
|||
});
|
||||
|
||||
describe(`"img_src"`, () => {
|
||||
it(`throws if 'rules' is also specified`, () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
rules: [
|
||||
`script-src 'unsafe-eval' 'self'`,
|
||||
`worker-src 'unsafe-eval' 'self'`,
|
||||
`style-src 'unsafe-eval' 'self'`,
|
||||
],
|
||||
img_src: [`'self'`],
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if using an `nonce-*` value', () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -412,6 +299,7 @@ describe('config.validate()', () => {
|
|||
`"[img_src]: using \\"nonce-*\\" is considered insecure and is not allowed"`
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if using `none` or `'none'`", () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -432,21 +320,6 @@ describe('config.validate()', () => {
|
|||
});
|
||||
|
||||
describe(`"frame_ancestors"`, () => {
|
||||
it(`throws if 'rules' is also specified`, () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
rules: [
|
||||
`script-src 'unsafe-eval' 'self'`,
|
||||
`worker-src 'unsafe-eval' 'self'`,
|
||||
`style-src 'unsafe-eval' 'self'`,
|
||||
],
|
||||
frame_ancestors: [`'self'`],
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"\\"csp.rules\\" cannot be used when specifying per-directive additions such as \\"script_src\\", \\"worker_src\\" or \\"style_src\\""`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if using an `nonce-*` value', () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
@ -456,6 +329,7 @@ describe('config.validate()', () => {
|
|||
`"[frame_ancestors]: using \\"nonce-*\\" is considered insecure and is not allowed"`
|
||||
);
|
||||
});
|
||||
|
||||
it("throws if using `none` or `'none'`", () => {
|
||||
expect(() =>
|
||||
config.schema.validate({
|
||||
|
|
|
@ -39,7 +39,6 @@ const getDirectiveValueValidator = ({ allowNone, allowNonce }: DirectiveValidati
|
|||
|
||||
const configSchema = schema.object(
|
||||
{
|
||||
rules: schema.maybe(schema.arrayOf(schema.string())),
|
||||
script_src: schema.arrayOf(schema.string(), {
|
||||
defaultValue: [],
|
||||
validate: getDirectiveValidator({ allowNone: false, allowNonce: false }),
|
||||
|
@ -89,9 +88,6 @@ const configSchema = schema.object(
|
|||
},
|
||||
{
|
||||
validate: (cspConfig) => {
|
||||
if (cspConfig.rules && hasDirectiveSpecified(cspConfig)) {
|
||||
return `"csp.rules" cannot be used when specifying per-directive additions such as "script_src", "worker_src" or "style_src"`;
|
||||
}
|
||||
const hasUnsafeInlineScriptSrc =
|
||||
cspConfig.script_src.includes(`unsafe-inline`) ||
|
||||
cspConfig.script_src.includes(`'unsafe-inline'`);
|
||||
|
@ -106,22 +102,6 @@ const configSchema = schema.object(
|
|||
}
|
||||
);
|
||||
|
||||
const hasDirectiveSpecified = (rawConfig: CspConfigType): boolean => {
|
||||
return Boolean(
|
||||
rawConfig.script_src.length ||
|
||||
rawConfig.worker_src.length ||
|
||||
rawConfig.style_src.length ||
|
||||
rawConfig.connect_src.length ||
|
||||
rawConfig.default_src.length ||
|
||||
rawConfig.font_src.length ||
|
||||
rawConfig.frame_src.length ||
|
||||
rawConfig.img_src.length ||
|
||||
rawConfig.frame_ancestors.length ||
|
||||
rawConfig.report_uri.length ||
|
||||
rawConfig.report_to.length
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
@ -132,25 +112,4 @@ export const config: ServiceConfigDescriptor<CspConfigType> = {
|
|||
// ? https://github.com/elastic/kibana/pull/52251
|
||||
path: 'csp',
|
||||
schema: configSchema,
|
||||
deprecations: () => [
|
||||
(rawConfig, fromPath, addDeprecation) => {
|
||||
const cspConfig = rawConfig[fromPath];
|
||||
if (cspConfig?.rules) {
|
||||
addDeprecation({
|
||||
message:
|
||||
'`csp.rules` is deprecated in favor of directive specific configuration. Please use `csp.connect_src`, ' +
|
||||
'`csp.default_src`, `csp.font_src`, `csp.frame_ancestors`, `csp.frame_src`, `csp.img_src`, ' +
|
||||
'`csp.report_uri`, `csp.report_to`, `csp.script_src`, `csp.style_src`, and `csp.worker_src` instead.',
|
||||
correctiveActions: {
|
||||
manualSteps: [
|
||||
`Remove "csp.rules" from the Kibana config file."`,
|
||||
`Add directive specific configurations to the config file using "csp.connect_src", "csp.default_src", "csp.font_src", ` +
|
||||
`"csp.frame_ancestors", "csp.frame_src", "csp.img_src", "csp.report_uri", "csp.report_to", "csp.script_src", ` +
|
||||
`"csp.style_src", and "csp.worker_src".`,
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -34,11 +34,6 @@ describe('CspConfig', () => {
|
|||
CspConfig {
|
||||
"disableEmbedding": false,
|
||||
"header": "script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'",
|
||||
"rules": Array [
|
||||
"script-src 'unsafe-eval' 'self'",
|
||||
"worker-src blob: 'self'",
|
||||
"style-src 'unsafe-inline' 'self'",
|
||||
],
|
||||
"strict": true,
|
||||
"warnLegacyBrowsers": true,
|
||||
}
|
||||
|
@ -50,13 +45,6 @@ describe('CspConfig', () => {
|
|||
});
|
||||
|
||||
describe('partial config', () => {
|
||||
test('allows "rules" to be set and changes header', () => {
|
||||
const rules = [`foo 'self'`, `bar 'self'`];
|
||||
const config = new CspConfig({ ...defaultConfig, rules });
|
||||
expect(config.rules).toEqual(rules);
|
||||
expect(config.header).toMatchInlineSnapshot(`"foo 'self'; bar 'self'"`);
|
||||
});
|
||||
|
||||
test('allows "strict" to be set', () => {
|
||||
const config = new CspConfig({ ...defaultConfig, strict: false });
|
||||
expect(config.strict).toEqual(false);
|
||||
|
@ -70,67 +58,57 @@ describe('CspConfig', () => {
|
|||
expect(config.warnLegacyBrowsers).not.toEqual(CspConfig.DEFAULT.warnLegacyBrowsers);
|
||||
});
|
||||
|
||||
test('allows "worker_src" to be set and changes header', () => {
|
||||
test('allows "worker_src" to be set and changes header from defaults', () => {
|
||||
const config = new CspConfig({
|
||||
...defaultConfig,
|
||||
rules: [],
|
||||
worker_src: ['foo', 'bar'],
|
||||
});
|
||||
expect(config.rules).toEqual([`worker-src foo bar`]);
|
||||
expect(config.header).toEqual(`worker-src foo bar`);
|
||||
expect(config.header).toEqual(
|
||||
`script-src 'unsafe-eval' 'self'; worker-src blob: 'self' foo bar; style-src 'unsafe-inline' 'self'`
|
||||
);
|
||||
});
|
||||
|
||||
test('allows "style_src" to be set and changes header', () => {
|
||||
const config = new CspConfig({
|
||||
...defaultConfig,
|
||||
rules: [],
|
||||
style_src: ['foo', 'bar'],
|
||||
});
|
||||
expect(config.rules).toEqual([`style-src foo bar`]);
|
||||
expect(config.header).toEqual(`style-src foo bar`);
|
||||
|
||||
expect(config.header).toEqual(
|
||||
`script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self' foo bar`
|
||||
);
|
||||
});
|
||||
|
||||
test('allows "script_src" to be set and changes header', () => {
|
||||
const config = new CspConfig({
|
||||
...defaultConfig,
|
||||
rules: [],
|
||||
script_src: ['foo', 'bar'],
|
||||
});
|
||||
expect(config.rules).toEqual([`script-src foo bar`]);
|
||||
expect(config.header).toEqual(`script-src foo bar`);
|
||||
|
||||
expect(config.header).toEqual(
|
||||
`script-src 'unsafe-eval' 'self' foo bar; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'`
|
||||
);
|
||||
});
|
||||
|
||||
test('allows all directives to be set and changes header', () => {
|
||||
const config = new CspConfig({
|
||||
...defaultConfig,
|
||||
rules: [],
|
||||
script_src: ['script', 'foo'],
|
||||
worker_src: ['worker', 'bar'],
|
||||
style_src: ['style', 'dolly'],
|
||||
});
|
||||
expect(config.rules).toEqual([
|
||||
`script-src script foo`,
|
||||
`worker-src worker bar`,
|
||||
`style-src style dolly`,
|
||||
]);
|
||||
expect(config.header).toEqual(
|
||||
`script-src script foo; worker-src worker bar; style-src style dolly`
|
||||
`script-src 'unsafe-eval' 'self' script foo; worker-src blob: 'self' worker bar; style-src 'unsafe-inline' 'self' style dolly`
|
||||
);
|
||||
});
|
||||
|
||||
test('applies defaults when `rules` is undefined', () => {
|
||||
test('appends config directives to defaults', () => {
|
||||
const config = new CspConfig({
|
||||
...defaultConfig,
|
||||
rules: undefined,
|
||||
script_src: ['script'],
|
||||
worker_src: ['worker'],
|
||||
style_src: ['style'],
|
||||
});
|
||||
expect(config.rules).toEqual([
|
||||
`script-src 'unsafe-eval' 'self' script`,
|
||||
`worker-src blob: 'self' worker`,
|
||||
`style-src 'unsafe-inline' 'self' style`,
|
||||
]);
|
||||
expect(config.header).toEqual(
|
||||
`script-src 'unsafe-eval' 'self' script; worker-src blob: 'self' worker; style-src 'unsafe-inline' 'self' style`
|
||||
);
|
||||
|
@ -139,25 +117,15 @@ describe('CspConfig', () => {
|
|||
describe('allows "disableEmbedding" to be set', () => {
|
||||
const disableEmbedding = true;
|
||||
|
||||
test('and changes rules/header if custom rules are not defined', () => {
|
||||
test('and changes rules and header', () => {
|
||||
const config = new CspConfig({ ...defaultConfig, disableEmbedding });
|
||||
expect(config.disableEmbedding).toEqual(disableEmbedding);
|
||||
expect(config.disableEmbedding).not.toEqual(CspConfig.DEFAULT.disableEmbedding);
|
||||
expect(config.rules).toEqual(expect.arrayContaining([`frame-ancestors 'self'`]));
|
||||
expect(config.header).toMatchInlineSnapshot(
|
||||
`"script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'; frame-ancestors 'self'"`
|
||||
);
|
||||
});
|
||||
|
||||
test('and does not change rules/header if custom rules are defined', () => {
|
||||
const rules = [`foo 'self'`, `bar 'self'`];
|
||||
const config = new CspConfig({ ...defaultConfig, disableEmbedding, rules });
|
||||
expect(config.disableEmbedding).toEqual(disableEmbedding);
|
||||
expect(config.disableEmbedding).not.toEqual(CspConfig.DEFAULT.disableEmbedding);
|
||||
expect(config.rules).toEqual(rules);
|
||||
expect(config.header).toMatchInlineSnapshot(`"foo 'self'; bar 'self'"`);
|
||||
});
|
||||
|
||||
test('and overrides `frame-ancestors` if set', () => {
|
||||
const config = new CspConfig({
|
||||
...defaultConfig,
|
||||
|
|
|
@ -16,11 +16,6 @@ const DEFAULT_CONFIG = Object.freeze(config.schema.validate({}));
|
|||
* @public
|
||||
*/
|
||||
export interface ICspConfig {
|
||||
/**
|
||||
* The CSP rules used for Kibana.
|
||||
*/
|
||||
readonly rules: string[];
|
||||
|
||||
/**
|
||||
* Specify whether browsers that do not support CSP should be
|
||||
* able to use Kibana. Use `true` to block and `false` to allow.
|
||||
|
@ -34,8 +29,7 @@ export interface ICspConfig {
|
|||
readonly warnLegacyBrowsers: boolean;
|
||||
|
||||
/**
|
||||
* Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled *and* no custom rules have been
|
||||
* defined, a restrictive 'frame-ancestors' rule will be added to the default CSP rules.
|
||||
* Whether or not embedding (using iframes) should be allowed by the CSP. If embedding is disabled, a restrictive 'frame-ancestors' rule will be added to the default CSP rules.
|
||||
*/
|
||||
readonly disableEmbedding: boolean;
|
||||
|
||||
|
@ -54,7 +48,6 @@ export class CspConfig implements ICspConfig {
|
|||
static readonly DEFAULT = new CspConfig(DEFAULT_CONFIG);
|
||||
|
||||
readonly #directives: CspDirectives;
|
||||
public readonly rules: string[];
|
||||
public readonly strict: boolean;
|
||||
public readonly warnLegacyBrowsers: boolean;
|
||||
public readonly disableEmbedding: boolean;
|
||||
|
@ -66,14 +59,11 @@ export class CspConfig implements ICspConfig {
|
|||
*/
|
||||
constructor(rawCspConfig: CspConfigType) {
|
||||
this.#directives = CspDirectives.fromConfig(rawCspConfig);
|
||||
if (!rawCspConfig.rules?.length && rawCspConfig.disableEmbedding) {
|
||||
if (rawCspConfig.disableEmbedding) {
|
||||
this.#directives.clearDirectiveValues('frame-ancestors');
|
||||
this.#directives.addDirectiveValue('frame-ancestors', `'self'`);
|
||||
}
|
||||
|
||||
this.rules = this.#directives.getRules();
|
||||
this.header = this.#directives.getCspHeader();
|
||||
|
||||
this.strict = rawCspConfig.strict;
|
||||
this.warnLegacyBrowsers = rawCspConfig.warnLegacyBrowsers;
|
||||
this.disableEmbedding = rawCspConfig.disableEmbedding;
|
||||
|
|
|
@ -11,33 +11,12 @@ import { config as cspConfig } from './config';
|
|||
|
||||
describe('CspDirectives', () => {
|
||||
describe('#addDirectiveValue', () => {
|
||||
it('properly updates the rules', () => {
|
||||
const directives = new CspDirectives();
|
||||
directives.addDirectiveValue('style-src', 'foo');
|
||||
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"style-src foo",
|
||||
]
|
||||
`);
|
||||
|
||||
directives.addDirectiveValue('style-src', 'bar');
|
||||
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"style-src foo bar",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('properly updates the header', () => {
|
||||
const directives = new CspDirectives();
|
||||
directives.addDirectiveValue('style-src', 'foo');
|
||||
|
||||
expect(directives.getCspHeader()).toMatchInlineSnapshot(`"style-src foo"`);
|
||||
|
||||
directives.addDirectiveValue('style-src', 'bar');
|
||||
|
||||
expect(directives.getCspHeader()).toMatchInlineSnapshot(`"style-src foo bar"`);
|
||||
});
|
||||
|
||||
|
@ -50,12 +29,6 @@ describe('CspDirectives', () => {
|
|||
expect(directives.getCspHeader()).toMatchInlineSnapshot(
|
||||
`"style-src foo bar; worker-src dolly"`
|
||||
);
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"style-src foo bar",
|
||||
"worker-src dolly",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('removes duplicates', () => {
|
||||
|
@ -65,11 +38,6 @@ describe('CspDirectives', () => {
|
|||
directives.addDirectiveValue('style-src', 'bar');
|
||||
|
||||
expect(directives.getCspHeader()).toMatchInlineSnapshot(`"style-src foo bar"`);
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"style-src foo bar",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('automatically adds single quotes for keywords', () => {
|
||||
|
@ -106,18 +74,6 @@ describe('CspDirectives', () => {
|
|||
});
|
||||
|
||||
describe('#fromConfig', () => {
|
||||
it('returns the correct rules for the default config', () => {
|
||||
const config = cspConfig.schema.validate({});
|
||||
const directives = CspDirectives.fromConfig(config);
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"script-src 'unsafe-eval' 'self'",
|
||||
"worker-src blob: 'self'",
|
||||
"style-src 'unsafe-inline' 'self'",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('returns the correct header for the default config', () => {
|
||||
const config = cspConfig.schema.validate({});
|
||||
const directives = CspDirectives.fromConfig(config);
|
||||
|
@ -126,75 +82,6 @@ describe('CspDirectives', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('handles config with rules', () => {
|
||||
const config = cspConfig.schema.validate({
|
||||
rules: [`script-src 'self' http://foo.com`, `worker-src 'self'`],
|
||||
});
|
||||
const directives = CspDirectives.fromConfig(config);
|
||||
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"script-src 'self' http://foo.com",
|
||||
"worker-src 'self'",
|
||||
]
|
||||
`);
|
||||
expect(directives.getCspHeader()).toMatchInlineSnapshot(
|
||||
`"script-src 'self' http://foo.com; worker-src 'self'"`
|
||||
);
|
||||
});
|
||||
|
||||
it('adds single quotes for keyword for rules', () => {
|
||||
const config = cspConfig.schema.validate({
|
||||
rules: [`script-src self http://foo.com`, `worker-src self`],
|
||||
});
|
||||
const directives = CspDirectives.fromConfig(config);
|
||||
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"script-src 'self' http://foo.com",
|
||||
"worker-src 'self'",
|
||||
]
|
||||
`);
|
||||
expect(directives.getCspHeader()).toMatchInlineSnapshot(
|
||||
`"script-src 'self' http://foo.com; worker-src 'self'"`
|
||||
);
|
||||
});
|
||||
|
||||
it('handles multiple whitespaces when parsing rules', () => {
|
||||
const config = cspConfig.schema.validate({
|
||||
rules: [` script-src 'self' http://foo.com `, ` worker-src 'self' `],
|
||||
});
|
||||
const directives = CspDirectives.fromConfig(config);
|
||||
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"script-src 'self' http://foo.com",
|
||||
"worker-src 'self'",
|
||||
]
|
||||
`);
|
||||
expect(directives.getCspHeader()).toMatchInlineSnapshot(
|
||||
`"script-src 'self' http://foo.com; worker-src 'self'"`
|
||||
);
|
||||
});
|
||||
|
||||
it('supports unregistered directives', () => {
|
||||
const config = cspConfig.schema.validate({
|
||||
rules: [`script-src 'self' http://foo.com`, `img-src 'self'`, 'foo bar'],
|
||||
});
|
||||
const directives = CspDirectives.fromConfig(config);
|
||||
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"script-src 'self' http://foo.com",
|
||||
"img-src 'self'",
|
||||
"foo bar",
|
||||
]
|
||||
`);
|
||||
expect(directives.getCspHeader()).toMatchInlineSnapshot(
|
||||
`"script-src 'self' http://foo.com; img-src 'self'; foo bar"`
|
||||
);
|
||||
});
|
||||
|
||||
it('adds default value for config with directives', () => {
|
||||
const config = cspConfig.schema.validate({
|
||||
script_src: [`baz`],
|
||||
|
@ -203,13 +90,6 @@ describe('CspDirectives', () => {
|
|||
});
|
||||
const directives = CspDirectives.fromConfig(config);
|
||||
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"script-src 'unsafe-eval' 'self' baz",
|
||||
"worker-src blob: 'self' foo",
|
||||
"style-src 'unsafe-inline' 'self' bar dolly",
|
||||
]
|
||||
`);
|
||||
expect(directives.getCspHeader()).toMatchInlineSnapshot(
|
||||
`"script-src 'unsafe-eval' 'self' baz; worker-src blob: 'self' foo; style-src 'unsafe-inline' 'self' bar dolly"`
|
||||
);
|
||||
|
@ -227,22 +107,9 @@ describe('CspDirectives', () => {
|
|||
report_to: [`report-to`],
|
||||
});
|
||||
const directives = CspDirectives.fromConfig(config);
|
||||
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"script-src 'unsafe-eval' 'self'",
|
||||
"worker-src blob: 'self'",
|
||||
"style-src 'unsafe-inline' 'self'",
|
||||
"connect-src 'self' connect-src",
|
||||
"default-src 'self' default-src",
|
||||
"font-src 'self' font-src",
|
||||
"frame-src 'self' frame-src",
|
||||
"img-src 'self' img-src",
|
||||
"frame-ancestors 'self' frame-ancestors",
|
||||
"report-uri report-uri",
|
||||
"report-to report-to",
|
||||
]
|
||||
`);
|
||||
expect(directives.getCspHeader()).toMatchInlineSnapshot(
|
||||
`"script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'; connect-src 'self' connect-src; default-src 'self' default-src; font-src 'self' font-src; frame-src 'self' frame-src; img-src 'self' img-src; frame-ancestors 'self' frame-ancestors; report-uri report-uri; report-to report-to"`
|
||||
);
|
||||
});
|
||||
|
||||
it('adds single quotes for keywords in added directives', () => {
|
||||
|
@ -250,14 +117,6 @@ describe('CspDirectives', () => {
|
|||
script_src: [`unsafe-hashes`],
|
||||
});
|
||||
const directives = CspDirectives.fromConfig(config);
|
||||
|
||||
expect(directives.getRules()).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"script-src 'unsafe-eval' 'self' 'unsafe-hashes'",
|
||||
"worker-src blob: 'self'",
|
||||
"style-src 'unsafe-inline' 'self'",
|
||||
]
|
||||
`);
|
||||
expect(directives.getCspHeader()).toMatchInlineSnapshot(
|
||||
`"script-src 'unsafe-eval' 'self' 'unsafe-hashes'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'"`
|
||||
);
|
||||
|
|
|
@ -22,7 +22,7 @@ export type CspDirectiveName =
|
|||
| 'report-to';
|
||||
|
||||
/**
|
||||
* The default rules that are always applied
|
||||
* The default directives rules that are always applied
|
||||
*/
|
||||
export const defaultRules: Partial<Record<CspDirectiveName, string[]>> = {
|
||||
'script-src': [`'unsafe-eval'`, `'self'`],
|
||||
|
@ -58,21 +58,18 @@ export class CspDirectives {
|
|||
}
|
||||
|
||||
getCspHeader() {
|
||||
return this.getRules().join('; ');
|
||||
}
|
||||
|
||||
getRules() {
|
||||
return [...this.directives.entries()].map(([name, values]) => {
|
||||
return [name, ...values].join(' ');
|
||||
});
|
||||
return [...this.directives.entries()]
|
||||
.map(([name, values]) => {
|
||||
return [name, ...values].join(' ');
|
||||
})
|
||||
.join('; ');
|
||||
}
|
||||
|
||||
static fromConfig(config: CspConfigType): CspDirectives {
|
||||
const cspDirectives = new CspDirectives();
|
||||
|
||||
// adding `csp.rules` or `default` rules
|
||||
const initialRules = config.rules ? parseRules(config.rules) : { ...defaultRules };
|
||||
Object.entries(initialRules).forEach(([key, values]) => {
|
||||
// combining `default` directive configurations
|
||||
Object.entries(defaultRules).forEach(([key, values]) => {
|
||||
values?.forEach((value) => {
|
||||
cspDirectives.addDirectiveValue(key as CspDirectiveName, value);
|
||||
});
|
||||
|
@ -91,15 +88,6 @@ export class CspDirectives {
|
|||
}
|
||||
}
|
||||
|
||||
const parseRules = (rules: string[]): Partial<Record<CspDirectiveName, string[]>> => {
|
||||
const directives: Partial<Record<CspDirectiveName, string[]>> = {};
|
||||
rules.forEach((rule) => {
|
||||
const [name, ...values] = rule.replace(/\s+/g, ' ').trim().split(' ');
|
||||
directives[name as CspDirectiveName] = values;
|
||||
});
|
||||
return directives;
|
||||
};
|
||||
|
||||
const parseConfigDirectives = (cspConfig: CspConfigType): Map<CspDirectiveName, string[]> => {
|
||||
const map = new Map<CspDirectiveName, string[]>();
|
||||
|
||||
|
|
|
@ -12,12 +12,10 @@ import * as kbnTestServer from '../../../test_helpers/kbn_server';
|
|||
describe('http resources service', () => {
|
||||
describe('register', () => {
|
||||
let root: ReturnType<typeof kbnTestServer.createRoot>;
|
||||
const defaultCspRules = "script-src 'self'";
|
||||
const defaultCspRules =
|
||||
"script-src 'unsafe-eval' 'self'; worker-src blob: 'self'; style-src 'unsafe-inline' 'self'";
|
||||
beforeEach(async () => {
|
||||
root = kbnTestServer.createRoot({
|
||||
csp: {
|
||||
rules: [defaultCspRules],
|
||||
},
|
||||
plugins: { initialize: false },
|
||||
elasticsearch: { skipStartupConnectionCheck: true },
|
||||
});
|
||||
|
@ -44,7 +42,7 @@ describe('http resources service', () => {
|
|||
expect(response.text.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('attaches CSP header', async () => {
|
||||
it('applies default CSP header', async () => {
|
||||
const { http, httpResources } = await root.setup();
|
||||
|
||||
const router = http.createRouter('');
|
||||
|
|
|
@ -754,8 +754,6 @@ export class CspConfig implements ICspConfig {
|
|||
// (undocumented)
|
||||
readonly header: string;
|
||||
// (undocumented)
|
||||
readonly rules: string[];
|
||||
// (undocumented)
|
||||
readonly strict: boolean;
|
||||
// (undocumented)
|
||||
readonly warnLegacyBrowsers: boolean;
|
||||
|
@ -1135,7 +1133,6 @@ export type IContextProvider<Context extends RequestHandlerContext, ContextName
|
|||
export interface ICspConfig {
|
||||
readonly disableEmbedding: boolean;
|
||||
readonly header: string;
|
||||
readonly rules: string[];
|
||||
readonly strict: boolean;
|
||||
readonly warnLegacyBrowsers: boolean;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ kibana_vars=(
|
|||
console.enabled
|
||||
console.proxyConfig
|
||||
console.proxyFilter
|
||||
csp.rules
|
||||
csp.strict
|
||||
csp.warnLegacyBrowsers
|
||||
csp.script_src
|
||||
|
|
|
@ -61,23 +61,23 @@ describe('csp collector', () => {
|
|||
expect((await collector.fetch(mockedFetchContext)).warnLegacyBrowsers).toEqual(false);
|
||||
});
|
||||
|
||||
test('fetches whether the csp rules have been changed or not', async () => {
|
||||
test("fetches whether the csp directives's rules have been changed or not", async () => {
|
||||
const collector = new Collector(logger, createCspCollector(httpMock));
|
||||
|
||||
expect((await collector.fetch(mockedFetchContext)).rulesChangedFromDefault).toEqual(false);
|
||||
|
||||
updateCsp({ rules: ['not', 'default'] });
|
||||
updateCsp({ disableEmbedding: true });
|
||||
expect((await collector.fetch(mockedFetchContext)).rulesChangedFromDefault).toEqual(true);
|
||||
});
|
||||
|
||||
test('does not include raw csp rules under any property names', async () => {
|
||||
const collector = new Collector(logger, createCspCollector(httpMock));
|
||||
|
||||
// It's important that we do not send the value of csp.rules here as it
|
||||
// It's important that we do not send the raw values of csp cirectives here as they
|
||||
// can be customized with values that can be identifiable to given
|
||||
// installs, such as URLs
|
||||
//
|
||||
// We use a snapshot here to ensure csp.rules isn't finding its way into the
|
||||
// We use a snapshot here to ensure raw values aren't finding their way into the
|
||||
// payload under some new and unexpected variable name (e.g. cspRules).
|
||||
expect(await collector.fetch(mockedFetchContext)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue