summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsoftwarefactory-project-zuul[bot] <33884098+softwarefactory-project-zuul[bot]@users.noreply.github.com>2020-12-10 16:32:53 +0100
committerGitHub <noreply@github.com>2020-12-10 16:32:53 +0100
commit4c5757b3bd1c3dd2cd6ca6988cd99215dbb74c82 (patch)
treec8c25865a31f4171379198e9a6783a8de651d6c8
parentMerge pull request #8790 from rooftopcellist/quantity_not_exported (diff)
parentFFox ESR 78 Compatibility (diff)
downloadawx-4c5757b3bd1c3dd2cd6ca6988cd99215dbb74c82.tar.xz
awx-4c5757b3bd1c3dd2cd6ca6988cd99215dbb74c82.zip
Merge pull request #8754 from ryanpetrello/strict-csp
Introduce a strict Content-Security-Policy Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
-rw-r--r--awx/settings/defaults.py1
-rw-r--r--awx/ui/context_processors.py8
-rw-r--r--awx/ui_next/package.json2
-rw-r--r--awx/ui_next/public/index.html15
-rw-r--r--awx/ui_next/src/index.jsx1
-rw-r--r--awx/ui_next/src/screens/Job/JobOutput/JobEvent.jsx77
-rw-r--r--awx/ui_next/src/screens/Job/JobOutput/JobEvent.test.jsx62
-rw-r--r--awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx236
-rw-r--r--awx/ui_next/src/setupCSP.js30
-rw-r--r--awx/ui_next/src/setupTests.js5
-rw-r--r--installer/roles/kubernetes/templates/configmap.yml.j22
-rw-r--r--installer/roles/local_docker/templates/nginx.conf.j22
-rw-r--r--tools/docker-compose/nginx.vh.default.conf4
13 files changed, 295 insertions, 150 deletions
diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py
index 6204486456..d47277eaed 100644
--- a/awx/settings/defaults.py
+++ b/awx/settings/defaults.py
@@ -248,6 +248,7 @@ TEMPLATES = [
'django.template.context_processors.static',
'django.template.context_processors.tz',
'django.contrib.messages.context_processors.messages',
+ 'awx.ui.context_processors.csp',
'social_django.context_processors.backends',
'social_django.context_processors.login_redirect',
],
diff --git a/awx/ui/context_processors.py b/awx/ui/context_processors.py
new file mode 100644
index 0000000000..87c071c285
--- /dev/null
+++ b/awx/ui/context_processors.py
@@ -0,0 +1,8 @@
+import base64
+import os
+
+
+def csp(request):
+ return {
+ 'csp_nonce': base64.encodebytes(os.urandom(32)).decode().rstrip(),
+ }
diff --git a/awx/ui_next/package.json b/awx/ui_next/package.json
index 252319a713..d223b47d29 100644
--- a/awx/ui_next/package.json
+++ b/awx/ui_next/package.json
@@ -53,7 +53,7 @@
},
"scripts": {
"start": "PORT=3001 HTTPS=true DANGEROUSLY_DISABLE_HOST_CHECK=true react-scripts start",
- "build": "react-scripts build",
+ "build": "INLINE_RUNTIME_CHUNK=false react-scripts build",
"test": "TZ='UTC' react-scripts test --coverage --watchAll=false",
"test-watch": "TZ='UTC' react-scripts test",
"eject": "react-scripts eject",
diff --git a/awx/ui_next/public/index.html b/awx/ui_next/public/index.html
index 2d7ff373b7..dc5174aa7c 100644
--- a/awx/ui_next/public/index.html
+++ b/awx/ui_next/public/index.html
@@ -1,6 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
+ <% if (process.env.NODE_ENV === 'production') { %>
+ <script nonce="{{ csp_nonce }}" type="text/javascript">
+ window.NONCE_ID = '{{ csp_nonce }}';
+ </script>
+ <meta
+ http-equiv="Content-Security-Policy"
+ content="default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'nonce-{{ csp_nonce }}' *.pendo.io; img-src 'self' *.pendo.io data:;"
+ />
+ <% } %>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
@@ -12,6 +21,10 @@
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
- <div id="app" style="height: 100%"></div>
+ <% if (process.env.NODE_ENV === 'production') { %>
+ <style nonce="{{ csp_nonce }}">.app{height: 100%;}</style><div id="app" class="app"></div>
+ <% } else { %>
+ <div id="app" style="height: 100%"></div>
+ <% } %>
</body>
</html>
diff --git a/awx/ui_next/src/index.jsx b/awx/ui_next/src/index.jsx
index ad616077ef..8bbeb9e866 100644
--- a/awx/ui_next/src/index.jsx
+++ b/awx/ui_next/src/index.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
+import './setupCSP';
import '@patternfly/react-core/dist/styles/base.css';
import App from './App';
import { BrandName } from './variables';
diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobEvent.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobEvent.jsx
index 59908f695c..29f02e8245 100644
--- a/awx/ui_next/src/screens/Job/JobOutput/JobEvent.jsx
+++ b/awx/ui_next/src/screens/Job/JobOutput/JobEvent.jsx
@@ -1,6 +1,3 @@
-import Ansi from 'ansi-to-html';
-import hasAnsi from 'has-ansi';
-import { AllHtmlEntities } from 'html-entities';
import React from 'react';
import {
JobEventLine,
@@ -9,84 +6,18 @@ import {
JobEventLineText,
} from './shared';
-const EVENT_START_TASK = 'playbook_on_task_start';
-const EVENT_START_PLAY = 'playbook_on_play_start';
-const EVENT_STATS_PLAY = 'playbook_on_stats';
-const TIME_EVENTS = [EVENT_START_TASK, EVENT_START_PLAY, EVENT_STATS_PLAY];
-
-const ansi = new Ansi({
- stream: true,
- colors: {
- 0: '#000',
- 1: '#A30000',
- 2: '#486B00',
- 3: '#795600',
- 4: '#00A',
- 5: '#A0A',
- 6: '#004368',
- 7: '#AAA',
- 8: '#555',
- 9: '#F55',
- 10: '#5F5',
- 11: '#FF5',
- 12: '#55F',
- 13: '#F5F',
- 14: '#5FF',
- 15: '#FFF',
- },
-});
-const entities = new AllHtmlEntities();
-
-function getTimestamp({ created }) {
- const date = new Date(created);
-
- const dateHours = date.getHours();
- const dateMinutes = date.getMinutes();
- const dateSeconds = date.getSeconds();
-
- const stampHours = dateHours < 10 ? `0${dateHours}` : dateHours;
- const stampMinutes = dateMinutes < 10 ? `0${dateMinutes}` : dateMinutes;
- const stampSeconds = dateSeconds < 10 ? `0${dateSeconds}` : dateSeconds;
-
- return `${stampHours}:${stampMinutes}:${stampSeconds}`;
-}
-
-function getLineTextHtml({ created, event, start_line, stdout }) {
- const sanitized = entities.encode(stdout);
- return sanitized.split('\r\n').map((lineText, index) => {
- let html;
- if (hasAnsi(lineText)) {
- html = ansi.toHtml(lineText);
- } else {
- html = lineText;
- }
-
- if (index === 1 && TIME_EVENTS.includes(event)) {
- const time = getTimestamp({ created });
- html += `<span class="time">${time}</span>`;
- }
-
- return {
- lineNumber: start_line + index,
- html,
- };
- });
-}
-
function JobEvent({
counter,
- created,
- event,
- isClickable,
- onJobEventClick,
stdout,
- start_line,
style,
type,
+ lineTextHtml,
+ isClickable,
+ onJobEventClick,
}) {
return !stdout ? null : (
<div style={style} type={type}>
- {getLineTextHtml({ created, event, start_line, stdout }).map(
+ {lineTextHtml.map(
({ lineNumber, html }) =>
lineNumber >= 0 && (
<JobEventLine
diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobEvent.test.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobEvent.test.jsx
index baceb1c6f4..e76ae0a3ac 100644
--- a/awx/ui_next/src/screens/Job/JobOutput/JobEvent.test.jsx
+++ b/awx/ui_next/src/screens/Job/JobOutput/JobEvent.test.jsx
@@ -1,6 +1,5 @@
import React from 'react';
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
-
import JobEvent from './JobEvent';
const mockOnPlayStartEvent = {
@@ -24,23 +23,64 @@ const selectors = {
lineText: 'JobEventLineText',
};
+const singleDigitTimestampEvent = {
+ ...mockOnPlayStartEvent,
+ created: '2019-07-11T08:01:02.906001Z',
+};
+
+const mockSingleDigitTimestampEventLineTextHtml = [
+ { lineNumber: 0, html: '' },
+ {
+ lineNumber: 1,
+ html:
+ 'PLAY [add hosts to inventory] **************************************************<span class="time">08:01:02</span>',
+ },
+];
+
+const mockAnsiLineTextHtml = [
+ {
+ lineNumber: 4,
+ html: '<span class="output--1977390340">ok: [localhost]</span>',
+ },
+];
+
+const mockOnPlayStartLineTextHtml = [
+ { lineNumber: 0, html: '' },
+ {
+ lineNumber: 1,
+ html:
+ 'PLAY [add hosts to inventory] **************************************************<span class="time">18:11:22</span>',
+ },
+];
+
describe('<JobEvent />', () => {
test('initially renders successfully', () => {
- mountWithContexts(<JobEvent {...mockOnPlayStartEvent} />);
+ mountWithContexts(
+ <JobEvent
+ lineTextHtml={mockOnPlayStartLineTextHtml}
+ {...mockOnPlayStartEvent}
+ />
+ );
});
test('playbook event timestamps are rendered', () => {
- let wrapper = mountWithContexts(<JobEvent {...mockOnPlayStartEvent} />);
+ let wrapper = mountWithContexts(
+ <JobEvent
+ lineTextHtml={mockOnPlayStartLineTextHtml}
+ {...mockOnPlayStartEvent}
+ />
+ );
let lineText = wrapper.find(selectors.lineText);
expect(
lineText.filterWhere(e => e.text().includes('18:11:22'))
).toHaveLength(1);
- const singleDigitTimestampEvent = {
- ...mockOnPlayStartEvent,
- created: '2019-07-11T08:01:02.906001Z',
- };
- wrapper = mountWithContexts(<JobEvent {...singleDigitTimestampEvent} />);
+ wrapper = mountWithContexts(
+ <JobEvent
+ lineTextHtml={mockSingleDigitTimestampEventLineTextHtml}
+ {...singleDigitTimestampEvent}
+ />
+ );
lineText = wrapper.find(selectors.lineText);
expect(
lineText.filterWhere(e => e.text().includes('08:01:02'))
@@ -48,12 +88,14 @@ describe('<JobEvent />', () => {
});
test('ansi stdout colors are rendered as html', () => {
- const wrapper = mountWithContexts(<JobEvent {...mockRunnerOnOkEvent} />);
+ const wrapper = mountWithContexts(
+ <JobEvent lineTextHtml={mockAnsiLineTextHtml} {...mockRunnerOnOkEvent} />
+ );
const lineText = wrapper.find(selectors.lineText);
expect(
lineText
.html()
- .includes('<span style="color:#486B00">ok: [localhost]</span>')
+ .includes('<span class="output--1977390340">ok: [localhost]</span>')
).toBe(true);
});
diff --git a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx
index 9c6b324891..4d78d3b364 100644
--- a/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx
+++ b/awx/ui_next/src/screens/Job/JobOutput/JobOutput.jsx
@@ -1,4 +1,4 @@
-import React, { Component } from 'react';
+import React, { Component, Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
@@ -10,6 +10,9 @@ import {
InfiniteLoader,
List,
} from 'react-virtualized';
+import Ansi from 'ansi-to-html';
+import hasAnsi from 'has-ansi';
+import { AllHtmlEntities } from 'html-entities';
import AlertModal from '../../../components/AlertModal';
import { CardBody } from '../../../components/Card';
@@ -32,6 +35,106 @@ import {
AdHocCommandsAPI,
} from '../../../api';
+const EVENT_START_TASK = 'playbook_on_task_start';
+const EVENT_START_PLAY = 'playbook_on_play_start';
+const EVENT_STATS_PLAY = 'playbook_on_stats';
+const TIME_EVENTS = [EVENT_START_TASK, EVENT_START_PLAY, EVENT_STATS_PLAY];
+
+const ansi = new Ansi({
+ stream: true,
+ colors: {
+ 0: '#000',
+ 1: '#A30000',
+ 2: '#486B00',
+ 3: '#795600',
+ 4: '#00A',
+ 5: '#A0A',
+ 6: '#004368',
+ 7: '#AAA',
+ 8: '#555',
+ 9: '#F55',
+ 10: '#5F5',
+ 11: '#FF5',
+ 12: '#55F',
+ 13: '#F5F',
+ 14: '#5FF',
+ 15: '#FFF',
+ },
+});
+const entities = new AllHtmlEntities();
+
+function getTimestamp({ created }) {
+ const date = new Date(created);
+
+ const dateHours = date.getHours();
+ const dateMinutes = date.getMinutes();
+ const dateSeconds = date.getSeconds();
+
+ const stampHours = dateHours < 10 ? `0${dateHours}` : dateHours;
+ const stampMinutes = dateMinutes < 10 ? `0${dateMinutes}` : dateMinutes;
+ const stampSeconds = dateSeconds < 10 ? `0${dateSeconds}` : dateSeconds;
+
+ return `${stampHours}:${stampMinutes}:${stampSeconds}`;
+}
+
+const styleAttrPattern = new RegExp('style="[^"]*"', 'g');
+
+function createStyleAttrHash(styleAttr) {
+ let hash = 0;
+ for (let i = 0; i < styleAttr.length; i++) {
+ hash = (hash << 5) - hash; // eslint-disable-line no-bitwise
+ hash += styleAttr.charCodeAt(i);
+ hash &= hash; // eslint-disable-line no-bitwise
+ }
+ return `${hash}`;
+}
+
+function replaceStyleAttrs(html) {
+ const allStyleAttrs = [...new Set(html.match(styleAttrPattern))];
+ const cssMap = {};
+ let result = html;
+ for (let i = 0; i < allStyleAttrs.length; i++) {
+ const styleAttr = allStyleAttrs[i];
+ const cssClassName = `output-${createStyleAttrHash(styleAttr)}`;
+
+ cssMap[cssClassName] = styleAttr.replace('style="', '').slice(0, -1);
+ result = result.split(styleAttr).join(`class="${cssClassName}"`);
+ }
+ return { cssMap, result };
+}
+
+function getLineTextHtml({ created, event, start_line, stdout }) {
+ const sanitized = entities.encode(stdout);
+ let lineCssMap = {};
+ const lineTextHtml = [];
+
+ sanitized.split('\r\n').forEach((lineText, index) => {
+ let html;
+ if (hasAnsi(lineText)) {
+ const { cssMap, result } = replaceStyleAttrs(ansi.toHtml(lineText));
+ html = result;
+ lineCssMap = { ...lineCssMap, ...cssMap };
+ } else {
+ html = lineText;
+ }
+
+ if (index === 1 && TIME_EVENTS.includes(event)) {
+ const time = getTimestamp({ created });
+ html += `<span class="time">${time}</span>`;
+ }
+
+ lineTextHtml.push({
+ lineNumber: start_line + index,
+ html,
+ });
+ });
+
+ return {
+ lineCssMap,
+ lineTextHtml,
+ };
+}
+
const HeaderTitle = styled.div`
display: inline-flex;
align-items: center;
@@ -54,6 +157,8 @@ const OutputWrapper = styled.div`
font-size: 15px;
height: calc(100vh - 350px);
outline: 1px solid #d7d7d7;
+ ${({ cssMap }) =>
+ Object.keys(cssMap).map(className => `.${className}{${cssMap[className]}}`)}
`;
const OutputFooter = styled.div`
@@ -122,6 +227,7 @@ class JobOutput extends Component {
remoteRowCount: 0,
isHostModalOpen: false,
hostEvent: {},
+ cssMap: {},
};
this.cache = new CellMeasurerCache({
@@ -164,7 +270,7 @@ class JobOutput extends Component {
componentDidUpdate(prevProps, prevState) {
// recompute row heights for any job events that have transitioned
// from loading to loaded
- const { currentlyLoading } = this.state;
+ const { currentlyLoading, cssMap } = this.state;
let shouldRecomputeRowHeights = false;
prevState.currentlyLoading
.filter(n => !currentlyLoading.includes(n))
@@ -172,6 +278,9 @@ class JobOutput extends Component {
shouldRecomputeRowHeights = true;
this.cache.clear(n);
});
+ if (Object.keys(cssMap).length !== Object.keys(prevState.cssMap).length) {
+ shouldRecomputeRowHeights = true;
+ }
if (shouldRecomputeRowHeights) {
if (this.listRef.recomputeRowHeights) {
this.listRef.recomputeRowHeights();
@@ -300,6 +409,13 @@ class JobOutput extends Component {
return isHost;
};
+ let actualLineTextHtml = [];
+ if (results[index]) {
+ const { lineTextHtml, lineCssMap } = getLineTextHtml(results[index]);
+ this.setState(({ cssMap }) => ({ cssMap: { ...cssMap, ...lineCssMap } }));
+ actualLineTextHtml = lineTextHtml;
+ }
+
return (
<CellMeasurer
key={key}
@@ -314,6 +430,7 @@ class JobOutput extends Component {
onJobEventClick={() => this.handleHostEventClick(results[index])}
className="row"
style={style}
+ lineTextHtml={actualLineTextHtml}
{...results[index]}
/>
) : (
@@ -389,7 +506,9 @@ class JobOutput extends Component {
handleResize({ width }) {
if (width !== this._previousWidth) {
this.cache.clearAll();
- this.listRef.recomputeRowHeights();
+ if (this.listRef?.recomputeRowHeights) {
+ this.listRef.recomputeRowHeights();
+ }
}
this._previousWidth = width;
}
@@ -404,6 +523,7 @@ class JobOutput extends Component {
hostEvent,
isHostModalOpen,
remoteRowCount,
+ cssMap,
} = this.state;
if (hasContentLoading) {
@@ -415,60 +535,62 @@ class JobOutput extends Component {
}
return (
- <CardBody>
- {isHostModalOpen && (
- <HostEventModal
- onClose={this.handleHostModalClose}
- isOpen={isHostModalOpen}
- hostEvent={hostEvent}
+ <Fragment>
+ <CardBody>
+ {isHostModalOpen && (
+ <HostEventModal
+ onClose={this.handleHostModalClose}
+ isOpen={isHostModalOpen}
+ hostEvent={hostEvent}
+ />
+ )}
+ <OutputHeader>
+ <HeaderTitle>
+ <StatusIcon status={job.status} />
+ <h1>{job.name}</h1>
+ </HeaderTitle>
+ <OutputToolbar job={job} onDelete={this.handleDeleteJob} />
+ </OutputHeader>
+ <HostStatusBar counts={job.host_status_counts} />
+ <PageControls
+ onScrollFirst={this.handleScrollFirst}
+ onScrollLast={this.handleScrollLast}
+ onScrollNext={this.handleScrollNext}
+ onScrollPrevious={this.handleScrollPrevious}
/>
- )}
- <OutputHeader>
- <HeaderTitle>
- <StatusIcon status={job.status} />
- <h1>{job.name}</h1>
- </HeaderTitle>
- <OutputToolbar job={job} onDelete={this.handleDeleteJob} />
- </OutputHeader>
- <HostStatusBar counts={job.host_status_counts} />
- <PageControls
- onScrollFirst={this.handleScrollFirst}
- onScrollLast={this.handleScrollLast}
- onScrollNext={this.handleScrollNext}
- onScrollPrevious={this.handleScrollPrevious}
- />
- <OutputWrapper>
- <InfiniteLoader
- isRowLoaded={this.isRowLoaded}
- loadMoreRows={this.loadMoreRows}
- rowCount={remoteRowCount}
- >
- {({ onRowsRendered, registerChild }) => (
- <AutoSizer onResize={this.handleResize}>
- {({ width, height }) => {
- return (
- <List
- ref={ref => {
- this.listRef = ref;
- registerChild(ref);
- }}
- deferredMeasurementCache={this.cache}
- height={height || 1}
- onRowsRendered={onRowsRendered}
- rowCount={remoteRowCount}
- rowHeight={this.cache.rowHeight}
- rowRenderer={this.rowRenderer}
- scrollToAlignment="start"
- width={width || 1}
- overscanRowCount={20}
- />
- );
- }}
- </AutoSizer>
- )}
- </InfiniteLoader>
- <OutputFooter />
- </OutputWrapper>
+ <OutputWrapper cssMap={cssMap}>
+ <InfiniteLoader
+ isRowLoaded={this.isRowLoaded}
+ loadMoreRows={this.loadMoreRows}
+ rowCount={remoteRowCount}
+ >
+ {({ onRowsRendered, registerChild }) => (
+ <AutoSizer nonce={window.NONCE_ID} onResize={this.handleResize}>
+ {({ width, height }) => {
+ return (
+ <List
+ ref={ref => {
+ this.listRef = ref;
+ registerChild(ref);
+ }}
+ deferredMeasurementCache={this.cache}
+ height={height || 1}
+ onRowsRendered={onRowsRendered}
+ rowCount={remoteRowCount}
+ rowHeight={this.cache.rowHeight}
+ rowRenderer={this.rowRenderer}
+ scrollToAlignment="start"
+ width={width || 1}
+ overscanRowCount={20}
+ />
+ );
+ }}
+ </AutoSizer>
+ )}
+ </InfiniteLoader>
+ <OutputFooter />
+ </OutputWrapper>
+ </CardBody>
{deletionError && (
<AlertModal
isOpen={deletionError}
@@ -480,7 +602,7 @@ class JobOutput extends Component {
<ErrorDetail error={deletionError} />
</AlertModal>
)}
- </CardBody>
+ </Fragment>
);
}
}
diff --git a/awx/ui_next/src/setupCSP.js b/awx/ui_next/src/setupCSP.js
new file mode 100644
index 0000000000..77d40c5775
--- /dev/null
+++ b/awx/ui_next/src/setupCSP.js
@@ -0,0 +1,30 @@
+/* eslint-disable */
+
+// Set a special variable to add `nonce` attributes to all styles/script tags
+// See https://github.com/webpack/webpack/pull/3210
+__webpack_nonce__ = window.NONCE_ID;
+
+// Send report when a CSP violation occurs
+// See: https://w3c.github.io/webappsec-csp/2/#violation-reports
+// See: https://developer.mozilla.org/en-US/docs/Web/API/SecurityPolicyViolationEvent
+document.addEventListener('securitypolicyviolation', e => {
+ const violation = {
+ 'csp-report': {
+ 'blocked-uri': e.blockedURI,
+ 'document-uri': e.documentURI,
+ 'effective-directive': e.effectiveDirective,
+ 'original-policy': e.originalPolicy,
+ referrer: e.referrer,
+ 'status-code': e.statusCode,
+ 'violated-directive': e.violatedDirective,
+ },
+ };
+ if (e.sourceFile) violation['csp-report']['source-file'] = e.sourceFile;
+ if (e.lineNumber) violation['csp-report']['line-number'] = e.lineNumber;
+ if (e.columnNumber) violation['csp-report']['column-number'] = e.columnNumber;
+
+ const xhr = new XMLHttpRequest();
+ xhr.open('POST', '/csp-violation/', true);
+ xhr.setRequestHeader('content-type', 'application/csp-report');
+ xhr.send(JSON.stringify(violation));
+});
diff --git a/awx/ui_next/src/setupTests.js b/awx/ui_next/src/setupTests.js
index 7d59ff1a4c..5518d62c93 100644
--- a/awx/ui_next/src/setupTests.js
+++ b/awx/ui_next/src/setupTests.js
@@ -19,3 +19,8 @@ global.console = {
...console,
debug: jest.fn(),
};
+
+// This global variable is part of our Content Security Policy framework
+// and so this mock ensures that we don't encounter a reference error
+// when running the tests
+global.__webpack_nonce__ = null;
diff --git a/installer/roles/kubernetes/templates/configmap.yml.j2 b/installer/roles/kubernetes/templates/configmap.yml.j2
index b7553811c1..b239b96783 100644
--- a/installer/roles/kubernetes/templates/configmap.yml.j2
+++ b/installer/roles/kubernetes/templates/configmap.yml.j2
@@ -69,8 +69,6 @@ data:
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
- add_header Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/";
- add_header X-Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/";
# Protect against click-jacking https://www.owasp.org/index.php/Testing_for_Clickjacking_(OTG-CLIENT-009)
add_header X-Frame-Options "DENY";
diff --git a/installer/roles/local_docker/templates/nginx.conf.j2 b/installer/roles/local_docker/templates/nginx.conf.j2
index 0c93510bc9..327b59a2fe 100644
--- a/installer/roles/local_docker/templates/nginx.conf.j2
+++ b/installer/roles/local_docker/templates/nginx.conf.j2
@@ -67,8 +67,6 @@ http {
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
- add_header Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/";
- add_header X-Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/";
# Protect against click-jacking https://www.owasp.org/index.php/Testing_for_Clickjacking_(OTG-CLIENT-009)
add_header X-Frame-Options "DENY";
diff --git a/tools/docker-compose/nginx.vh.default.conf b/tools/docker-compose/nginx.vh.default.conf
index ff7f604b5e..73a4d1cd8d 100644
--- a/tools/docker-compose/nginx.vh.default.conf
+++ b/tools/docker-compose/nginx.vh.default.conf
@@ -22,8 +22,6 @@ server {
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
- add_header Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/";
- add_header X-Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/";
location /static/ {
root /awx_devel;
@@ -84,8 +82,6 @@ server {
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
- add_header Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/";
- add_header X-Content-Security-Policy "default-src 'self'; connect-src 'self' ws: wss:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' *.pendo.io; img-src 'self' *.pendo.io data:; report-uri /csp-violation/";
location /static/ {
root /awx_devel;