0.2.1 • Published 2 years ago

@siegrift/cypress-trusted-types v0.2.1

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

cypress-trusted-types ContinuousBuild

A library to simplify Cypress e2e testing of Trusted Types violations

Installation

To install this package run either:

yarn add @siegrift/cypress-trusted-types

or if you use npm

npm install @siegrift/cypress-trusted-types --save

Usage and API

After you install the library load the available support commands inside cypress support/index.js file by adding the following import:

import '@siegrift/cypress-trusted-types';

After the commands are loaded, you can begin writing your tests with Trusted Types assertions:

cy.enableCspThroughMetaTag(); // Addresses Cypress limitation which removes CSP from response headers
cy.visit('/'); // Make sure to call "enableCspThroughMetaTag" before the site is visited
cy.catchTrustedTypesViolations(); // Starts catching Trusted Types violations

cy.contains('unsafe html').click();
cy.assertTrustedTypesViolation({ type: 'TrustedHTML' });
cy.get('iframe').should('not.exist');

With TypeScript

Typings for TS can be added by modifying the tsconfig.json:

{
  "compilerOptions": {
    "types": ["cypress", "@siegrift/cypress-trusted-types"]
  }
}

You can find all of the library definitions here.

API

The most important are the following two methods:

  • enableCspThroughMetaTag(options) - copies the CSP header returned by server to the HTML body using the meta tag because Cypress currently removes CSP header sent by the server. See: https://github.com/cypress-io/cypress/issues/1030. By default intercepts every request. You can override this in the options using the urlPattern.

    This function must be called before you call cy.visit!

  • catchTrustedTypesViolations - Call this to setup an error handler which listens to uncaught errors and checks for Trusted Types violations. In case a Trusted Types violation is encountered, it will be remembered (and can be asserted) and more importantly the test will not fail.

Assertions

  • assertTrustedTypesViolations - asserts multiple Trusted Types violations for the current test.

    cy.contains('unsafe html').click();
    cy.contains('unsafe html').click();
    cy.contains('duplicate policy').click();
    cy.contains('unsafe script').click();
    
    cy.assertTrustedTypesViolations([
      {
        type: 'TrustedHTML',
        message:
          "Failed to set the 'srcdoc' property on 'HTMLIFrameElement': This document requires 'TrustedHTML' assignment.",
      },
      {}, // No assertion is made for this violation
      {
        type: 'TrustedTypePolicyFactory',
        message: `Failed to execute 'createPolicy' on 'TrustedTypePolicyFactory': Policy with name "my-policy" already exists.`,
      },
      { type: 'TrustedScript' },
    ]);
  • assertTrustedTypesViolation - similar to the assertion above, but expects only a single Trusted Types violation.

    cy.contains('unsafe html').click();
    
    cy.assertTrustedTypesViolation({ type: 'TrustedHTML' });
    cy.get('iframe').should('not.exist');
  • assertZeroTrustedTypesViolation - asserts that no Trusted Types violation was encountered.

    cy.contains('safe html').click();
    
    cy.assertZeroTrustedTypesViolation();
    cy.get('iframe').should('exist');
  • getTrustedTypesViolations - low level API to get the Trusted Types violations to make custom assertions against.

    cy.contains('new policy').click();
    
    cy.getTrustedTypesViolations().then((violations) => {
      expect(violations[0].error.message).to.contain('Policy "new-policy" disallowed.');
    });
  • clearTrustedTypesViolations - resets all violations already encountered by the current test.

    cy.contains('unsafe html').click();
    cy.assertTrustedTypesViolation({ type: 'TrustedHTML' });
    
    // Clears previous violations
    cy.clearTrustedTypesViolations();
    cy.assertZeroTrustedTypesViolation();
    
    // But does NOT prevent further violations
    cy.contains('unsafe script').click();
    cy.assertTrustedTypesViolation({ type: 'TrustedScript' });

Other commands

  • parseCspFromMetaTags - parses the CSP directives from the meta tags.

    cy.parseCspFromMetaTags().then((csp) => {
      expect(csp).to.deep.equal(["require-trusted-types-for 'script'; trusted-types my-policy other-policy;"]);
    });

Motivation and usage

Trusted Types is a more and more popular web API for tackling the insecurities of the DOM to prevent client-side Cross-site scripting (XSS) attacks.

Cypress is, according to state of JS 2021, one the most loved e2e testing frameworks and I can relate to this experience.

Out of the box support

There are many frameworks and libraries which already support Trusted Types and actually Cypress provides support for Trusted Types out of the box if you launch the test in a browser which supports Trusted Types.

This means that if you have a Trusted Types compliant application, you can use Cypress to launch it in the integrated browser and the app will just work since the Cypress commands mostly use read only queries which do not produce Trusted Types violations. (I haven't checked all Cypress commands and plugins so it is possible that there are some which will not work).

Cross browser support

Cypress supports cross browser testing - currently chromium browsers and firefox. This is extremely nice, since you can leverage this to test the application in browsers which do support Trusted Types (chromium) and the ones which do not (firefox).

API design

Reading the following section is not necessary to use and understand this library.

Unfortunately, Cypress has a limitation due to which it removes the CSP header sent by the server.For now the workaround is to copy the CSP header returned by the server to the HTML body using the meta tag. The issue for this is tracked in https://github.com/cypress-io/cypress/issues/1030.

The API focuses mostly on testing the Trusted Types violations, since they are harder to test. See the API section for the list of available commands.

You might be wondering where are the commands for testing policies and whether a value passed to sink indeed came through a policy. You might also want to assert that the value came through a specific policy (e.g. the default one). These commands are not (and will not be) implemented. The reason is that these assertions are impossible to implement without the help of an user. More importantly though, such tests are too low level for e2e testing. Instead of asserting that the value was properly sanitized/came through policy, prefer asserting that a certain "feature" worked successfully. For example, make sure that iframe is present on the page instead of checking that a value of an iframe srcDoc attribute was created by policy (the customers do not care whether you use Trusted Types or not).

Developer docs

Running tests

# Start the development server
yarn test:start-server
# Open cypress tests
yarn cypress:open

Publishing

To publish run:

yarn version
yarn publish --access public
git push --follow-tags
0.2.1

2 years ago

0.2.0

2 years ago

0.1.2

2 years ago

0.1.1

2 years ago

0.1.0

2 years ago