Giter VIP home page Giter VIP logo

tsec's Introduction

tsec - Extended TypeScript compiler checking Trusted Types compatibility

This is not an officially supported Google product.

tsec is a wrapper for the official TypeScript compiler tsc with additional checks on the codebase's compatibility with Trusted Types.

tsec supports most compilation flags as tsc does. For code pattern patterns that is potentially incompatible with Trusted Types, tsec emits compilation errors.

Supported checks

tsec perform a basket of security checks to find possible XSS issues in your code. In particular, the checks ban using dangerous DOM sink APIs with plain string values. Any violation of the checks can hinder Trusted Types adoption either directly or indirectly. To fix the violations, you should construct Trusted Types values to feed into these sinks. At the moment, tsec covers most of the Trusted Types sinks that are enforced by the browser. See here for the complete list of available checks. We will be adding the missing ones soon.

Run

First add tsec as a dev dependency of your TypeScript project.

  yarn add tsec --dev

Then choose the right configuration file to build the project with tsec and check its Trusted Types compatibility.

  yarn tsec -p tsconfig.json

Add --noEmit flag to skip emitting JS code from compilation.

Writing Trusted Type compatible code

At the moment, there is no official support for Trusted Types in TypeScript, meaning it can be tricky to write code that passes the checks of both tsec and the TS type checker. There are several solutions to this problem.

Using the safevalues library

We have released the Trusted Types utility library safevalues to help developers write TT-compatible code. Please refer to its documentation for details.

This is our recommended way to work with Trusted Types. All other workarounds have some limitations, e.g., not supporting function sinks like the constructor of Worker.

Workarounds without safevalues

Casting Trusted Type to unknown to string

For example:

declare const trustedHTML: TrustedHTML;
// the next line will be allowed by both tsc and tsec
document.body.innerHTML = trustedHTML as unknown as string;
Using Trusted Type union with string and casting to string

For example:

// such value can be created if application uses string as a fallback when
// Trusted Types are not enabled/supported
declare const trustedHTML: TrustedHTML | string;
// the next line will be allowed by both tsc and tsec
document.body.innerHTML = trustedHTML as string;
Using unwrapper function

The first argument to the unwrapper function must be the Trusted Type that is required by the specific sink and must return value accepted by the sink (string). The unwrapper function can have additional arguments or even accept TS union of values for the first parameter.

For example:

declare const trustedHTML: TrustedHTML;
declare const unwrapHTML: (html: TrustedHTML, ...other: any[]) => string;
// the next line will be allowed by both tsc and tsec
document.body.innerHTML = unwrapHTML(trustedHTML);

Note: All of these variants must be at the assignment/call of the particular sink and not before. For example:

declare const trustedHTML: TrustedHTML;
// cast before the actual usage in sink
const castedTrustedHTML = trustedHTML as unknown as string;
// tsec is flow insensitive and treats `castedTrustedHTML` as a regular string
document.body.innerHTML = castedTrustedHTML; // tsec violation!
Patching lib.dom.d.ts

We have seen some developers patching their local lib.dom.d.ts with Trusted Types support. For example, you may redefine the innerHTML property like this with TS 4.3 or above,

+interface TrustedHTML {}

 interface InnerHTML {
-    innerHTML: string;
+    get innerHTML(): string;
+    set innerHTML(innerHTML: string | TrustedHTML);
 }

With this patch, declare const trusted: TrustedHTML; elem.innerHTML = trusted becomes valid code and no additional type casts are needed. In that case, tsec will no longer consider the previous workarounds as safe code. Likewise, the pattern below will be flagged by tsec since a string may flow into the sink:

declare const trustedHtml: string | TrustedHTML;
elem.innerHTML = trustedHtml;

Note: We try to make tsec as smart as possible to recognize patched lib.dom.d.ts, but the heuristics likely will not cover all setups. Also, your local patch may not be compatible with official TT support from TypeScript in the future. Therefore, we strongly discourage patching lib.dom.d.ts. Please use the safevalues library whenever possible.

Language service plugin

Tsec can be integrated as a plugin to your TypeScript project allowing you to see the violations directly in your IDE. For this to work you need to:

  1. Use workspace version of TypeScript

  2. Add the plugin via plugins compiler option in the tsconfig. If you are using tsec as a package then the path to the plugin might look like this:

    {
      "compilerOptions": {
        "plugins": [
          {
            "name": "tsec"
          }
        ]
      }
    }
  3. Restart the editor to reload TS initialization features.

Make sure the LSP is using (requiring) the same workspace version of TS used by the IDE.

Debugging

Language service plugin is experimental, if it doesn't work, you can create an issue or try to debug locally. If you are using VSCode you can do so by following these steps:

  1. Turn on verbose tsserver logging in the settings.

  2. Restart the IDE. You can use Developer: Reload Window command for this.

  3. Use Developer: Open Logs Folder to open the log folder

  4. Find tsserver.log inside the folder (you can use find command line utility) and open the file(s). There should be an error somewhere in the logs which should get you started.

Configure exemptions

You can configure tsec to exempt certain violations. Add an "exemptionConfig" option in the configuration for the tsec language service plugin. The value of that field is a string indicating the path to the exemption list, relative to the path of tsconfig.json. See an exemption below.

{
  "compilerOptions": {
    "plugins": [
      {
        "name": "tsec",
        "exemptionConfig": "./exemption_list.json"
      }
    ]
  }
}

Note that although this configuration appears to be for the language service plugin, it also works for the command line use of tsec.

The exemption list is a JSON file of which each entry is the name of a rule. The value of the entry is a list of files that you would like to exempt from that rule.

Here is an example. Suppose you have a file src/foo.ts in your project that triggers the following error from tsec:

src/foo.ts:10:5 - error TS21228: Assigning directly to Element#innerHTML can result in XSS vulnerabilities.

10     element.innerHTML = someVariable;
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can exempt it by creating an exemption_list.json file along side your tsconfig.json with the following content:

{
  "ban-element-innerhtml-assignments": ["src/foo.ts"]
}

The exemption list supports the glob syntax. For example, if you want to completely disable a check, you can write:

{
  "ban-element-innerhtml-assignments": ["**/*.ts"]
}

Note that exemptions are granted at the file granularity. If you exempt a file from a rule, all violations in that file will be exempted.

You can exempt files from all rules by setting the exemption list for the wildcard rule name "*". This can be useful when the compiler configuration of your project include files for testing.

{
  "*": ["**/test/*.ts", "**/*.test.ts", "**/*.spec.ts"]
}

Configure other diagnostic reporting options

It is possible to ask tsec to ignore all errors that would have been reported by the vanilla TS compiler and keep only the tsec-specific ones. To do so, set the reportTsecDiagnosticsOnly field in plugin options:

{
  "compilerOptions": {
    "plugins": [
      {
        "name": "tsec",
        "reportTsecDiagnosticsOnly": true
      }
    ]
  }
}

Developing locally

We recommend developing using VS Code. We have preconfigured the project such that debugging works out of the box. If you press F5 (Debug: Start debugging) tsec will be freshly built and executed on the project files (files included in tsconfig). Currently, we have tests only internally at Google, but you can create a test.ts file with some violationg code anywhere in the project to get started. You can then add breakpoints in any tsec source file.

Contributing

See CONTRIBUTING.md.

tsec's People

Contributors

12wrigja avatar frigus02 avatar gunan avatar iteriani avatar lauraharker avatar lukesandberg avatar mprobst avatar nreid260 avatar uraj avatar vrana avatar zmyyzm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

tsec's Issues

Add option for ignoring TS errors

tsec is a wrapper around tsc and reports errors from both custom rules related to Trusted Types as well as standard TypeScript errors (e.g. strict checks). This is expected behavior in most cases as TypeScript errors might prevent tsec from doing it's job.

We have problem with this approach in complex CI/CD pipelines where we already have jobs that handle standard TypeScript errors. We would like to keep tsec job focused solely on issues related to Trusted Types - fail only on custom checks and ignore others as they are handled (and reported) elsewhere.

It would be great if we could add a switch/option into tsec that would be false by default and only enable this behavior when needed.

Suppress by error code

Extracted from microsoft/vscode#108682:

@jrieken: Would it be possible to teach tsec more than one error code and to able to suppress certain error codes (instead of files)?

The use case Johannes outlined in vscode is their usage of dynamic imports - there's nothing actionable for the projects to move off them, but tsec should be able to surface violations of a different kind in the files.

Invalid warning on Worker assignment

  • use vscode source
  • run yarn tsec-compile-check
  • apart from a few other errors this being reported
src/vs/workbench/services/extensions/worker/polyfillNestedWorker.ts:47:8 - error TS21228: [ban-worker-calls] Constructing Web Workers can cause code to be loaded from an untrusted URL.

47  	self.Worker = <any>class { constructor() { throw new TypeError('Nested workers from within nested worker are NOT supported.'); } };
    	     ~~~~~~

Support Trusted Types from DOM lib

We are still waiting on DOM lib to fully support Trusted Types (microsoft/TypeScript#30024). I was proposing a fix in microsoft/TypeScript-DOM-lib-generator#1246 but it might take a while to get this merged in and released (= Safari or Firefox start supporting TTs).

I ran tsec against codebase that was using this updated DOM lib and found out that tsec does not recognize these built-in TT types.

For one, the matcher expects the types to be defined in DefinitelyTyped (@types/trusted-types):

modulePathMatcher: '/node_modules/@types/trusted-types/',

It would be great if we could add this support to tsec for people (like us) who are already using DOM lib with TTs in their code.

Don't run tsc as a post install step

Problem
I'm trying to bump VS Code's version of TypeScript to the latest nightly. However installing this version into VS Code caused a tsec error (the error happens on yarn add typescript@next):

> [email protected] build /Users/matb/projects/vscode/node_modules/tsec
> tsc

node_modules/@types/node/globals.d.ts(134,5): error TS2687: All declarations of 'url' must have identical modifiers.
node_modules/@types/node/globals.d.ts(134,5): error TS2717: Subsequent property declarations must have the same type.  Property 'url' must be of type 'string | undefined', but here has type 'string'.
../typescript/lib/lib.dom.d.ts(618,5): error TS2687: All declarations of 'url' must have identical modifiers.
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! [email protected] build: `tsc`
npm ERR! Exit status 2
npm ERR! 
npm ERR! Failed at the [email protected] build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:

The issue is that tsec run tsc compile in a post install step:

   "build": "tsc",
    "postinstall": "npm run build"

The shipped code should likely be precompiled and the post install step removed

/cc @jrieken

Bazel Integration: support runfile resolution

In Angular, tsec is run through a bazel rule. The rule doesn't work on Windows since it relies on source file symlinking, which is not available on Windows. We need to revise tsec to make it able to resolve runfiles that are part of the source tree, using @bazel/runfiles. This should be doable by providing a custom CompilerHost and ParseConfigHost.

Guidance on usage with trusted types

I tried to use tsec in lit-html and ran into a couple of issues:

src/lit-html.ts:219:12 - error TS21228: Creating a Trusted Types policy requires a security review.

219     return window.trustedTypes.createPolicy('lit-html', policy);
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/lit-html.ts:371:5 - error TS21228: Assigning directly to Element#innerHTML can result in XSS vulnerabilities.

371     this.__element.innerHTML = policy.createHTML(html + this.__strings[l]);
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Is there a facility to run tsec with an allowlist or similar?

Why assigning Trusted Types to dangerous sinks still causes compilation error?

I'm using DOMPurify with types, and it returns TrustedHTML when RETURN_TRUSTED_TYPE option is provided.
https://github.com/DefinitelyTyped/DefinitelyTyped/blob/f6dad85222741e22707bb30d39caf58e80843f9c/types/dompurify/index.d.ts#L24

While using DOMPurify works with innerHTML, it doesn't work with DOMParser's parseFromString.

import { sanitize } from 'dompurify';

const template = document.createElement('template');
template.innerHTML = sanitize('test', { RETURN_TRUSTED_TYPE: true }) as unknown as string; // no tsec error

const parser = new DOMParser();
parser.parseFromString(sanitize('test', { RETURN_TRUSTED_TYPE: true }) as unknown as string, 'text/html');
// TS21228: [ban-domparser-parsefromstring] Using DOMParser#parseFromString to parse untrusted input into DOM elements can lead to XSS.

Why are there differences in Trusted Types usage behavior?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.