Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add documentation for experimentalCspAllowList and CSP information #5194

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions docs/guides/guides/content-security-policy.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
title: Content Security Policy
e2eSpecific: true
---

Content Security Policy (CSP) is a browser security feature that allows you to
restrict the resources that can be loaded into your application. This can be
problematic for Cypress, because it needs to inject JavaScript into your
application in order to run tests and interact with the DOM. This page describes
how Cypress handles CSP and how to configure it to work with your application.

There are two ways to implement CSP:

- [Meta tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#http-equiv)
- [HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy)

The `<meta>` tag implementation is fully supported by Cypress without any
configuration required. This is because Cypress loads the necessary `<script>`
tags into your application before any `<meta>` tag is parsed. This prevents any
CSP directives from being applied to the script loaded by Cypress.

The second implementation requires you to configure Cypress to allow the headers
to be sent to your application. By default, Cypress will remove any CSP headers
from the response before it is sent to the browser. This is done to prevent
Cypress from being blocked by the browser's CSP implementation.

For most application tests, this should not cause any issues. However, if you
are testing your application's CSP implementation, you will need to configure
Cypress to allow the headers to be sent to the browser. You can do this by
setting the
[`experimentalCspAllowList`](/guides/references/experiments#Experimental-CSP-Allow-List)
configuration option.

For more information on CSP, see the
[Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
documentation on MDN.
84 changes: 84 additions & 0 deletions docs/guides/references/experiments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,97 @@ configuration to Cypress.

| Option | Default | Description |
| --------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `experimentalCspAllowList` | `false` | Indicates the Content-Security-Policy directives to be permitted during a test run. See [Content-Security-Policy](/guides/guides/content-security-policy) for more information. |
| `experimentalFetchPolyfill` | `false` | Automatically replaces `window.fetch` with a polyfill that Cypress can spy on and stub. Note: `experimentalFetchPolyfill` has been deprecated in Cypress 6.0.0 and will be removed in a future release. Consider using [cy.intercept()](/api/commands/intercept) to intercept `fetch` requests instead. |
| `experimentalInteractiveRunEvents` | `false` | Allows listening to the [`before:run`](/api/plugins/before-run-api), [`after:run`](/api/plugins/after-run-api), [`before:spec`](/api/plugins/before-spec-api), and [`after:spec`](/api/plugins/after-spec-api) events in the [setupNodeEvents](/guides/tooling/plugins-guide#Using-a-plugin) function during interactive mode. |
| `experimentalMemoryManagement` | `false` | Enables support for improved memory management within Chromium-based browsers. |
| `experimentalModifyObstructiveThirdPartyCode` | `false` | Whether Cypress will search for and replace obstructive code in third party `.js` or `.html` files. NOTE: Setting this flag removes [Subresource Integrity (SRI)](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity). |
| `experimentalSourceRewriting` | `false` | Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm. See [#5273](https://github.com/cypress-io/cypress/issues/5273) for details. |
| `experimentalWebKitSupport` | `false` | Enable experimental support for running tests in WebKit. When set, installs of `playwright-webkit` will be detected and available in Cypress. See [Launching Browsers](/guides/guides/launching-browsers#WebKit-Experimental) for more information. |

### Experimental CSP Allow List

Cypress by default strips all CSP headers (`Content-Security-Policy` and
`Content-Security-Policy-Report-Only`) from the response before it is sent to
the browser. The `experimentalCspAllowList` option allows for more granular
control over which CSP directives are stripped from the CSP response headers,
allowing you to test your application with CSP enabled. Valid values for this
option are `false` (the default), `true`, or an array of CSP directive names.

| Value | Example |
| ----------------------------------------------------- | ------------------------------------------------------- |
| [`false` (default)](#Strip-All-CSP-Headers) | `experimentalCspAllowList=false` |
| [`true`](#Strip-Minimum-CSP-Directives) | `experimentalCspAllowList=true` |
| [`<CspDirectives>[]`](#Allow-Specific-CSP-Directives) | `experimentalCspAllowList=["default-src","script-src"]` |

#### Strip All CSP Headers

The value `experimentalCspAllowList=false` (default) will remove all CSP headers
from the response before it is sent to the browser. This option should be used
if you do not depend on CSP for any tests in your application.

#### Strip Minimum CSP Directives

If you need to test your application with CSP enabled, setting the
`experimentalCspAllowList` option will allow all CSP headers to be sent to the
browser _*except*_ those that could prevent Cypress from functioning normally.

The following CSP directives will always be stripped:

| Stripped Directive | Allowable | Reason |
| --------------------------- | --------- | ---------------------------------------------------------------- |
| `frame-ancestors` | No | Prevents Cypress from loading a test application into an iframe. |
| `navigate-to` | No | Affects Cypress' ability to navigate to different URLs. |
| `require-trusted-types-for` | No | Might prevent Cypress from rewriting the DOM. |
| `sandbox` | No | Can restrict access to script and iframe functionality. |
| `trusted-types` | No | Could cause Cypress injections to be marked as untrusted. |

When `experimentalCspAllowList=true` the following directives are also stripped
in addition to the ones listed above, but can be configured to be allowed to be
sent to the browser:

| Stripped Directive | Allowable | Reason |
| ------------------ | --------- | ----------------------------------------------------------------------------- |
| `child-src` | Yes | Could prevent iframes from loading in combination with other Cypress options. |
| `default-src` | Yes | Conditionally prevents Cypress from loading scripts and running. |
| `frame-src` | Yes | Could prevent iframes from loading in combination with other Cypress options. |
| `form-action` | Yes | Can prevent Cypress from monitoring form events. |
| `script-src` | Yes | Conditionally prevents Cypress from loading scripts and running. |
| `script-src-elem` | Yes | Conditionally prevents Cypress from loading scripts and running. |

#### Allow Specific CSP Directives

Set the `experimentalCspAllowList` option to an array of directive names marked
as "Allowable" from the list above. This will allow the specified CSP directives
to be sent to the browser.

The following configuration would allow the `default-src`, `script-src`, and
`script-src-elem` directives to be sent to the browser:

:::cypress-config-example

```js
{
experimentalCspAllowList: ['default-src', 'script-src', 'script-src-elem']
}
```

:::

:::caution

Defining `experimentalCspAllowList` _*may*_ cause Cypress to be unable to run
tests against your application. If you experience issues, reduce the directives
specified in your allow list to identify which directive is causing issues.

There is a known issue when using certain directives containing hash algorithm
values and the `modifyObstructiveCode` and/or `experimentalSourceRewriting`
options. Using these options in combination with the `experimentalCspAllowList`
option can cause a mismatch between the original hashed directive value, and the
modified HTML or JS value.

:::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a note that this experiment is not compatible with the experimentalSourceRewriting experiment?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilyrohrbough This should still be compatible in certain scenarios. Can we say may not be compatible?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pgoforth @AtofStryker had flagged this as a possible risk / scenario where this configuration opt-in may fail.

  • JavaScript is re-written - PR does not handle this scenario
    • What we do know: Similar to SRI tags, nonces/shas need to be provided on inline-scripts CSP into the document before the actual inline script is fetched. If the inline script is rewritten by the proxy, the nonce will work fine, but the sha will break and the script will not execute. This is why when experimentalModifyObstructiveThirdPartyCode is enabled, integrity tags are stripped out of HTML documents as it is really difficult to inform the browser of the SHA to expect before the resource is rewritten and SHAed on the server (a bit of the chicken or the egg problem)

Mind adding a blurb stating if there are issues and this may be an known issue

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pgoforth I didn't see any code changes in the rewriting that would indicate this wouldn't cause issues with the nonce injection. Did you test any by chance?

Copy link
Author

@pgoforth pgoforth Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emilyrohrbough Nonce injection is completely independent of SHA hashing. They are handled differently through CSP.

  • A SHA hash is an inline validation against the script that gets downloaded and executed. If the version downloaded does not match the SHA hash, the script is not executed.
  • A nonce value is randomly generated at runtime and does not rely on the file hash at all.

So even if Cypress made inline changes to a script, the nonce would still pass CSP validation. We are generating a nonce for inline script injections, but are currently not generating a nonce for scripts that have been modified by Cypress. experimentalSourceRewriting does not trigger a CSP violation by default unless a SHA is used on the script node in question. Therefore, allowing CSP headers is unlikely to cause a the source rewrite code to fail CSP unless there are already specific directive values in place that validate against those scripts. That's why I said it may be an issue, because it's totally dependent on the original CSP headers that Cypress intercepts.

Edit:
I think I was being unclear on re-reading what I've written. SRI integrity attributes do not function the same way CSP nonce attributes do. They are mutually exclusive. Adding a nonce after already removing the integrity attribute would be moot. The CSP headers that affect rewrites are 'trusted-types' and 'require-trusted-types-for', which we will ALWAYS strip.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the main issue with SHA inline scripts is if the script is rewritten and the sha doesnt match the actual hash. But I think it might be OK to say it may work with experimentalSourceRewriting because it is going to depend on the context of what is rewritten, similar to how things work now with the regex rewriter

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably specify the scenario (using '<hash-algorithm>-<base64-value>' inside a CSP directive) as that's the only place it could trip against experimentalSourceRewriting or experimentalModifyObstructiveThirdPartyCode/modifyObstructiveCode.

I also genuinely feel like that is a problem that can be solved given our ability to modify individual directive values. We would just need to match the original SHA in the CSP directive with the one we replaced, and swap the value for the replacement. Outside of the scope of this PR, but definitely doable in the near future.


## Testing Type-Specific Experiments

You can provide configuration options for either E2E or Component Testing by
Expand Down