Content Security Policy (CSP)

Last Updated:

Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. If you are running into an issue with your CSP, you might need to make an adjustment to allow Pendo full functionality.

This article outlines the minimum required directives to allow Pendo full functionality, as well as compatible guide content examples for applications with strict CSP.

If you are using Feedback, see Feedback's Content Security Policy.

Guide delivery settings

For applications with strict CSP, you must ensure your Guide Delivery Settings are set to XHR.

To access your Guide Delivery settings:

  1. Navigate to Settings > Subscription Settings.

  2. Open the Applications tab.

  3. Find and open the relevant app from your Applications list.

  4. Open the Agent Settings tab.

  5. Select Manage Production Settings.

  6. Ensure the XHR radio button is selected.

guideDisplay.png

CSP without CNAME

For descriptions of the directives and entries in the code block examples, below, see Glossary of directives and entries.

Note: Replace all occurrences of foo.example.com below with your application hostname. Replace SUB_ID with your Subscription ID. Your Subscription ID is displayed in the page URL when you're logged into Pendo. It uses the following formathttps://app.pendo.io/s/[SUB_ID]/. The subscription ID immediately follows /s/. Don't accidentally grab the unique ID for a guide or report that populates at the end of the URL path when navigating Pendo.

You may include https:// before any hostnames if desired.

Minimum Required CSP Directives for US/Worldwide Non-EU Clients:

Full functionality, including the Designer:

script-src foo.example.com 'unsafe-inline' 'unsafe-eval' app.pendo.io pendo-io-static.storage.googleapis.com cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io;
style-src foo.example.com 'unsafe-inline' app.pendo.io cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com;
img-src foo.example.com cdn.pendo.io app.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io;
connect-src app.pendo.io data.pendo.io pendo-static-SUB_ID.storage.googleapis.com;
frame-ancestors app.pendo.io;
frame-src app.pendo.io; child-src app.pendo.io;
Full agent functionality and displaying guides in production: 
(This excludes the Designer and any inline Guide styles or scripts.)
script-src foo.example.com pendo-io-static.storage.googleapis.com cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io;
style-src foo.example.com app.pendo.io cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com;
img-src foo.example.com cdn.pendo.io app.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io;
connect-src app.pendo.io data.pendo.io pendo-static-SUB_ID.storage.googleapis.com; frame-ancestors app.pendo.io;
Displaying guides in staging:
script-src foo.example.com app.pendo.io pendo-io-static.storage.googleapis.com cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io;
style-src foo.example.com app.pendo.io cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com;
img-src foo.example.com cdn.pendo.io app.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io;
connect-src app.pendo.io data.pendo.io pendo-static-SUB_ID.storage.googleapis.com; frame-ancestors app.pendo.io;

Minimum Required CSP Directives for EU Clients:

Full functionality including the Designer:

script-src foo.example.com 'unsafe-inline' 'unsafe-eval' app.eu.pendo.io pendo-eu-static.storage.googleapis.com cdn.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com data.eu.pendo.io;
style-src foo.example.com 'unsafe-inline' app.eu.pendo.io cdn.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com;
img-src foo.example.com cdn.eu.pendo.io app.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com data.eu.pendo.io;
connect-src app.eu.pendo.io data.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com;
frame-ancestors app.eu.pendo.io;
frame-src app.eu.pendo.io; child-src app.eu.pendo.io;
Full agent functionality and displaying guides in production:
(This excludes the Designer and any inline Guide styles or scripts.)
script-src foo.example.com pendo-eu-static.storage.googleapis.com cdn.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com data.eu.pendo.io;
style-src foo.example.com app.eu.pendo.io cdn.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com;
img-src foo.example.com cdn.eu.pendo.io app.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com data.eu.pendo.io;
connect-src app.eu.pendo.io data.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com; frame-ancestors app.eu.pendo.io;

Displaying guides in staging:

script-src foo.example.com app.eu.pendo.io pendo-eu-static.storage.googleapis.com cdn.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com data.eu.pendo.io;
style-src foo.example.com app.eu.pendo.io cdn.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com;
img-src foo.example.com cdn.eu.pendo.io app.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com data.eu.pendo.io;
connect-src app.eu.pendo.io data.eu.pendo.io pendo-eu-static-SUB-ID.storage.googleapis.com; frame-ancestors app.eu.pendo.io;

CSP with CNAME

For descriptions of the directives and entries in the code block examples, below, see Glossary of directives and entries. Some of these are replaced when using CSP with CNAME. For information, see Directive and entries when using CNAME.

If you're using CSP with CNAME, there are several steps required to properly setup CNAME without CSP interrupting Pendo service as hostnames change. Update your CSP directives to use the transition code while setting up CNAME with Pendo's Support team. Use the CNAME-only code after CNAME configuration is complete to only allow Pendo resources using your CNAME.

Note: Replace all occurrences of pendo.example.com after content. and data. below with the subdomain selected for CNAME.

CSP directives transitioning to CNAME

Full functionality, including the Designer:
script-src foo.example.com 'unsafe-inline' 'unsafe-eval' app.pendo.io pendo-io-static.storage.googleapis.com cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io content.pendo.example.com data.pendo.example.com;
style-src foo.example.com 'unsafe-inline' app.pendo.io cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com content.pendo.example.com data.pendo.example.com;
img-src foo.example.com cdn.pendo.io app.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io content.pendo.example.com data.pendo.example.com;
connect-src app.pendo.io data.pendo.io pendo-static-SUB_ID.storage.googleapis.com content.pendo.example.com data.pendo.example.com;
frame-ancestors app.pendo.io data.pendo.example.com;
frame-src app.pendo.io data.pendo.example.com;
child-src app.pendo.io data.pendo.example.com; frame-src https://app.pendo.io; script-src https://app.pendo.io
For Replay, add the following to your CSP configuration:
worker-src blob:
Full agent functionality and displaying guides in production:
script-src foo.example.com pendo-io-static.storage.googleapis.com cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io content.pendo.example.com data.pendo.example.com;
style-src foo.example.com app.pendo.io cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com content.pendo.example.com data.pendo.example.com;
img-src foo.example.com cdn.pendo.io app.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io content.pendo.example.com data.pendo.example.com;
connect-src app.pendo.io data.pendo.io pendo-static-SUB_ID.storage.googleapis.com content.pendo.example.com data.pendo.example.com;
Displaying guides in staging:
script-src foo.example.com app.pendo.io pendo-io-static.storage.googleapis.com cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io content.pendo.example.com data.pendo.example.com;
style-src foo.example.com app.pendo.io cdn.pendo.io pendo-static-SUB_ID.storage.googleapis.com content.pendo.example.com data.pendo.example.com;
img-src foo.example.com cdn.pendo.io app.pendo.io pendo-static-SUB_ID.storage.googleapis.com data.pendo.io content.pendo.example.com data.pendo.example.com;
connect-src app.pendo.io data.pendo.io pendo-static-SUB_ID.storage.googleapis.com content.pendo.example.com data.pendo.example.com;
frame-ancestors app.pendo.io data.pendo.example.com;

CSP directives using CNAME only

Full functionality, including the Designer:
script-src foo.example.com 'unsafe-inline' 'unsafe-eval' content.pendo.example.com data.pendo.example.com app.pendo.io;
style-src foo.example.com 'unsafe-inline' content.pendo.example.com data.pendo.example.com;
img-src foo.example.com content.pendo.example.com data.pendo.example.com app.pendo.io;
connect-src content.pendo.example.com data.pendo.example.com;
frame-ancestors app.pendo.io;
frame-src app.pendo.io;
Full agent functionality and displaying guides in production:
script-src foo.example.com content.pendo.example.com data.pendo.example.com;
style-src foo.example.com content.pendo.example.com data.pendo.example.com;
img-src foo.example.com content.pendo.example.com data.pendo.example.com;
connect-src content.pendo.example.com data.pendo.example.com;
Displaying guides in staging:
script-src foo.example.com content.pendo.example.com data.pendo.example.com;
style-src foo.example.com content.pendo.example.com data.pendo.example.com;
img-src foo.example.com content.pendo.example.com data.pendo.example.com;
connect-src content.pendo.example.com data.pendo.example.com;

Compatible guide content

While Pendo is compatible with strict CSP directives, it is the user’s responsibility to ensure the guide content is compatible with your CSP restrictions.

If unsafe-inline is not present within the style-src and script-src directives, inline CSS and JavaScript within the HTML tab of the guide will not function correctly. To prevent an unexpected guide experience, all inline styles and scripts should be moved to the CSS and JS tabs of the guide code block or template.

With properly set minimal CSP directives (no inline directives), you should be able to fully use the Pendo Designer to show and tag your features. The X-Frame-Options plugin should enable you to use the Designer in guide editing mode with strict CSP. It is important to test guide content outside of the designer and with the X-Frame-Options plugin turned off.

Example of strict CSP compatible content

The basic concept is to remove all "unsafe-inline" styling and JavaScript.

For example, all of the inline styling and JavaScript within this HTML button:

<button class="_pendo-guide-next_" style="color:blue;" onclick="pendo.onGuideAdvanced()">Next</button>

would be distributed across the HTML, CSS, and JS tabs of the guide. The below example illustrates how to break down elements in guide content. You or your team’s developers may have a different or even better approach:

HTML

<button class="_pendo-guide-next_ blue-button">Next</button>

CSS

.bluebutton { color: blue; }

JavaScript

(function wireGuideAdvanceButton (step) { step && step.attachEvent(step.guideElement[0], 'click', function (e) { var advanceButton = pendo.dom(e.target || e.srcElement).closest('._pendo-guide-next_'); if (advanceButton.length) { pendo.onGuideAdvanced(); } }); })(step,guide);

Glossary of directives and entries

Directive: script-src

These entries (hosts) allow Pendo scripts to be downloaded and run.

Host Description
cdn.pendo.io

Location of the Pendo agent, referenced in your install script ("snippet"). This is where the Pendo agent is downloaded from by default.

pendo-io-static.storage.googleapis.com

Location of the Pendo agent downloaded for staging domains.

pendo-static-{{ SUB_ID }}.storage.googleapis.com

Location of all guide content.

data.pendo.io

Used to download the list of guides that a Visitor is eligible for. This is only needed for JSONP delivery methods.

app.pendo.io

Only required to use the Designer.

'unsafe-inline'

Only required if using custom code blocks or classic guides while in the Designer.

'unsafe-eval'

Required if using custom code blocks or classic guides while in the Designer.

 

Directive: style-src

These entries (hosts) allow Pendo styles to be used on your site.

Host Description
pendo-io-static.storage.googleapis.com

Only needed if any classic guides are enabled. Default Guide CSS is loaded from this URL.

pendo-static-{{ SUB_ID }}.storage.googleapis.com

Guide styles and Global CSS.

'unsafe-inline'

Used for Guide pseudo styles (hover, carets in the Resource Center, number scale) and styles when the Designer is launched.

app.pendo.io

Only required to use the Designer.

 

Directive: img-src

These entries allow images hosted by Pendo to be displayed on your site.

Host Description
cdn.pendo.io

Used for default classic Guide badges.

data.pendo.io

Events are sent using the image src to this url.

pendo-static-{{ SUB_ID }}.storage.googleapis.com

Guide images are downloaded from this url.

app.pendo.io

Only required to use the Designer.

data:

Default badge images; if using custom images for badges, then this isn't needed.

 

Directive: connect-src

These entries allow scripts on your pages to communicate with Pendo.

Host Description
data.pendo.io Required for event communication and Pendo Replay.
pendo-static-{{ SUB_ID }}.storage.googleapis.com

Load the guide structure files here.

app.pendo.io

Only required to use the Designer.

 

Directive: frame-ancestors

This entry allows Pendo to load your site pages within a <frame> – only needed if you're using the Classic Designer.

Host Description
app.pendo.io

The Pendo host from where the Classic Designer loads your application.

 

Directive: frame-src

This entry allows Pendo to load the Designer within a <frame> over your site.

Host Description
app.pendo.io

The Visual Design Studio is loaded from this URL. Only required to design Guides.

 

Directive: worker-src

This entry allows the Pendo agent to start a worker thread to minimize performance impact on your application.

Host Description
blob:

The Pendo Agent starts a web worker to compress and send session capturing data for Pendo Replay. This is done to improve the performance of session capturing as a whole.

Directives and entries when using CNAME

When using CNAME, some of the entries listed under the Glossary of directives and entries, above, are replaced.

To prevent downtime, include both the above urls as well as their new content and data hosts while you're in the process of configuring CNAME. These include:

Host Description
cdn.pendo.io

Replaced everywhere with the content host, like content.pendo.example.com.

pendo-io-static.storage.googleapis.com

Replaced everywhere with the content host, like content.pendo.example.com.

pendo-static-{{ SUB_ID }}.storage.googleapis.com

Replaced everywhere with the content host, like content.pendo.example.com.

data.pendo.io

Replaced everywhere with the data host, like data.pendo.example.com.

app.pendo.io

Still required to use the Designer.

'unsafe-inline'

Still required if using custom code blocks or classic guides while in the Designer.

'unsafe-eval'

Still required if using custom code blocks or classic guides while in the Designer.

data:

Still required if following previously stated rules.

 

Trusted types

Trusted types are an experimental technology currently only supported by Chromium-based browsers. If your application requires the extra XSS protection provided by trusted types, add the following directive to your CSP header: trusted-types pendo;. Trusted types support requires agent version 2.184.0 or higher.

Frequently asked questions

Are there different directives for the Visual Design Studio and Classic Designer?

Yes. frame-src is used by the Visual Design Studio and frame-ancestors is used by the Classic Designer. If your subscription does not use the Classic Designer to manage guides built prior to the Visual Design Studio release, you do not need the frame-ancestors directives. All applications should allow Visual Design Studio content. It's the only tool available for page and feature tagging and building guides with the latest features.

Are unsafe-inline and unsafe-eval directives really required?

Yes. The unsafe-eval directive is required for full functionality of the Classic Designer and the unsafe-inline directive is required for full functionality of published guides and the Visual Design Studio.

Additional Resources

Was this article helpful?
6 out of 9 found this helpful