josdejong / jsoneditor

A web-based tool to view, edit, format, and validate JSON
http://jsoneditoronline.org
Apache License 2.0
11.62k stars 2.04k forks source link

Add support for Trusted Types #1621

Open DeepSnowNeeL opened 2 weeks ago

DeepSnowNeeL commented 2 weeks ago

Hello,

I'm using your great library on angular with the @maaxgr/ang-jsoneditor (14.0.0) wrapper & jsoneditor (10.1.0).

I recently started implementing Trusted Types for security reasons and find myself unable to authorize in my CSP the jsoneditor due to a lack of trusted type usage.

image

As it is still experimental I would get it if you want to wait before implementing this, but the polyfill-like approach could be used to mitigate risk if they make breaking changes in the future.

Could you please add support for it ?

Thanks !

josdejong commented 1 week ago

Thanks for your suggestion. I hadn't heard of Trusted Types before.

So what would it require to make jsoneditor work with Trusted Types?

DeepSnowNeeL commented 1 week ago

You'll need to create a policy and name it like "jsoneditor" so in our CSP header we would define that "jsoneditor" policy is authorized to inject html.

Then everywhere you inject direct html such as doing element.innerHTML = x would need to be wrapped by the policy and if needed pass through a sanitizer method.

Here is an example with typescript/angular (pseudo-ish code) :

export class Component {
  public static trustedTypePolicy: TrustedTypePolicy;

  constructor(){
   if (window.trustedTypes == undefined) {
        (window as any).trustedTypes = {
          createPolicy: (_name: string, rules: TrustedTypePolicyOptions) => rules,
        };
      }

      if (!Component.trustedTypePolicy)
        // Creating the policy (a default one here which does not sanitize but it still is usefull to developers 
        // to make them "think" about it when they inject html/scripts)
        Component.trustedTypePolicy = window.trustedTypes.createPolicy('jsoneditor', {
          createHTML: (input) => {
            return input;
          },
          createScript: (input) => {
            return input;
          },
          createScriptURL: (input) => {
            return input;
          },
        });
  }

  insertHTML(){
     let el = this._renderer2.createElement('p');
     el.innerHTML = Component.trustedTypePolicy.createHTML('<span>hello</span>');
  }

}

Here is some more links for info :

josdejong commented 1 week ago

Thanks for the explanation.

I think a first step is to reduce the usages of innerHTML. I think in many (or all) cases we can replace it with using innerText (we need to be careful with different behavior of newlines though).

Anyone interested in working out a solution?