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

Type checking with security sanitization in place #28

Closed
rictic opened this issue Jul 12, 2019 · 4 comments · Fixed by #62
Closed

Type checking with security sanitization in place #28

rictic opened this issue Jul 12, 2019 · 4 comments · Fixed by #62
Labels
enhancement New feature or request

Comments

@rictic
Copy link
Collaborator

rictic commented Jul 12, 2019

When running in an environment with https://github.com/WICG/trusted-types* the types of some security sensitive built-in attributes and properties are effectively changed. For example, an iframe's src isn't string, but TrustedUrl.

Full list of overrides here: https://wicg.github.io/trusted-types/dist/spec/#enforcement-in-sinks

As the spec isn't yet finalized, the types aren't yet included automatically in the main typings. Further, inside google we use a slightly different system for the time being based on closure library types (e.g. https://google.github.io/closure-library/api/goog.html.SafeUrl.html).

Type typing these with these types doesn't have to be perfect, the canonical security layer is at runtime.

*or when running lit-html with lit/lit#750 enabled, which is kinda-sorta a ponyfill of the trusted types spec purely for lit-html templates

@rictic
Copy link
Collaborator Author

rictic commented Jul 12, 2019

I've got something really hacky working by just checking the names of the types, rather than needing to have a reference to the proper types. The core of the check is roughly:

const securityOverrideMap = {
  'iframe': {
    'src': ['TrustedResourceUrl'],
  },
  'a': {
    'href': ['TrustedResourceUrl', 'SafeUrl', 'string'],
  },
  'img': {
    'src': ['TrustedResourceUrl', 'SafeUrl', 'string'],
  },
  // ... etc
};
const globalSecurityOverrides = {
  'style': ['SafeStyle', 'string'],
};

function checkSecurityAssignability(typeB, htmlAttr, document) {
  const overrideNode = securityOverrideMap[htmlAttr.htmlNode.tagName];
  const overriddenTypes = (overrideNode && overrideNode[htmlAttr.name]) || (globalSecurityOverrides[htmlAttr.name]);
  if (!overriddenTypes) {
    return undefined;
  }
  if (!matchOverriddenTypes(overriddenTypes, typeB)) {
    return [{
      kind: exports.LitHtmlDiagnosticKind.INVALID_ATTRIBUTE_EXPRESSION_TYPE,
      message:
          `Type '${tsSimpleType.toTypeString(typeB)}' is not assignable to '${
              overriddenTypes.join(' | ')}'.`,
      severity: 'error',
      location: {document, ...htmlAttr.location.name},
      htmlAttr,
      typeB
    }];
  }
  return [];
}

function matchOverriddenTypes(typeNames, typeB) {
  if (typeNames.includes(typeB.name)) {
    return true;
  }
  switch (typeB.kind) {
    case 'UNION':
      return typeB.types.every((t) => matchOverriddenTypes(typeNames, t));
    case 'STRING_LITERAL':
    case 'STRING':
      return typeNames.includes('string');
    default:
      return false;
  }
}

@runem
Copy link
Owner

runem commented Aug 15, 2019

I would like to start making progress on this issue now. Before I start, I have some thoughts/questions that I want to clarify:

  1. I would like to enable trusted type support through a config option: trustedTypes: true (or safeTypes: true). Alternatively, if trusted types are always present, I fear it might be confusing for users to see that various types are a union of eg. string | SafeUrl.

  2. When trusted types are enabled, should a given trusted type be the only type available where applicable or should it also be possible to use existing types? Eg. string | SafeUrl vs just SafeUrl.

  3. Do you think it would make sense to extend with types from both WICG/trusted-types and closure safe types at the same time? (eg. a.href: TrustedResourceUrl | SafeUrl | string | TrustedURL). Or do you think I should just focus on closure safe types for now?

  4. I think I will end up type checking type names like your prototype. This seems to be the most simple and reliable way as of now.

@rictic
Copy link
Collaborator Author

rictic commented Aug 24, 2019

Sweet! Fair warning, things are a little up in the air still with trusted types, so depending on how much we want to keep iterating on this it might make sense to wait for them to stabilize a bit more before landing this.

  1. Seems like a good idea to at least start with it behind a flag. Trusted Types are promising but they're not an accepted standard yet.

  2. This might need to be behind a config as well. As I understand it, a page may or may not specify a default trusted types policy that can accept (and possibly sanitize) strings before they're passed along to DOM sinks. They can do this on a per type basis, so to accurately map this onto people's options, the config should be able to specify whether to allow string for each of the four types (TrustedHTML, TrustedURL, TrustedScriptURL, and TrustedScript).

  3. We're working on integrating lit-html with Trusted Types in Add trusted types support to lit html lit/lit#970. It might make sense to first wait for that to land first before landing TrustedTypes support (or even to wait for the spec to stabilize a bit more, it sounds like things are still changing). I'm unsure.

  4. +1

One subtle thing is that the security types should only apply to data bindings. So this is fine, because the url is from a programmer-written constant:

html`
  <script src="/foo.js"></script>
`;

But this should be rejected, because there's no way for the runtime to tell that the url doesn't come from untrusted resource:

const url = '/foo.js';
html`
  <script src="${url}"></script>
`;

@rictic
Copy link
Collaborator Author

rictic commented Sep 13, 2019

Related bug: microsoft/TypeScript#30024

@runem runem added the enhancement New feature or request label Sep 22, 2019
rictic added a commit to rictic/ts-simple-type that referenced this issue Nov 6, 2019
This is crashing when comparing an instance of an empty class against a primitive type (e.g. a boolean). Showed up in writing tests for runem/lit-analyzer#28
@runem runem closed this as completed in #62 Nov 19, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants