UNPKG

lit-analyzer

Version:

CLI that type checks bindings in lit-html templates

113 lines (112 loc) 4.18 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isAssignableBindingUnderSecuritySystem = void 0; var ts_simple_type_1 = require("ts-simple-type"); var is_lit_directive_js_1 = require("../directive/is-lit-directive.js"); var range_util_js_1 = require("../../../analyze/util/range-util.js"); /** * If the user's security policy overrides normal type checking for this * attribute binding, returns a (possibly empty) array of diagnostics. * * If the security policy does not apply to this binding, then */ function isAssignableBindingUnderSecuritySystem(htmlAttr, _a, context) { var typeA = _a.typeA, typeB = _a.typeB; var securityPolicy = context.config.securitySystem; switch (securityPolicy) { case "off": return undefined; // No security checks apply. case "ClosureSafeTypes": return checkClosureSecurityAssignability(typeB, htmlAttr, context); default: { var never = securityPolicy; context.logger.error("Unexpected security policy: ".concat(never)); return undefined; } } } exports.isAssignableBindingUnderSecuritySystem = isAssignableBindingUnderSecuritySystem; var closureScopedOverrides = { iframe: { src: ["TrustedResourceUrl"] }, a: { href: ["TrustedResourceUrl", "SafeUrl", "string"] }, img: { src: ["TrustedResourceUrl", "SafeUrl", "string"] }, script: { src: ["TrustedResourceUrl"] }, source: { src: ["TrustedResourceUrl", "SafeUrl"] } }; var closureGlobalOverrides = { style: ["SafeStyle", "string"] }; function checkClosureSecurityAssignability(typeB, htmlAttr, context) { var scopedOverride = closureScopedOverrides[htmlAttr.htmlNode.tagName]; var overriddenTypes = (scopedOverride && scopedOverride[htmlAttr.name]) || closureGlobalOverrides[htmlAttr.name]; if (overriddenTypes === undefined) { return undefined; } // `any` is allowed to bind to anything. if (typeB.kind === "ANY") { return undefined; } // Directives are responsible for their own security. if ((0, is_lit_directive_js_1.isLitDirective)(typeB)) { return undefined; } var typeMatch = matchesAtLeastOneNominalType(overriddenTypes, typeB); if (typeMatch === false) { /*const nominalType: SimpleType = { kind: SimpleTypeKind.INTERFACE, members: [], name: "A security type" };*/ context.report({ location: (0, range_util_js_1.rangeFromHtmlNodeAttr)(htmlAttr), message: "Type '".concat((0, ts_simple_type_1.typeToString)(typeB), "' is not assignable to '").concat(overriddenTypes.join(" | "), "'. This is due to Closure Safe Type enforcement.") }); return false; } return true; } function normalizeTypeName(typeName) { // Attempt to take a clutz type name for a goog.module type, which looks like // module$contents$goog$html$SafeUrl_SafeUrl and extract the // actual type name (SafeUrl in that case) var match = typeName.match(/module\$.*_(.*)/); if (match == null) { return undefined; } return match[1]; } function matchesAtLeastOneNominalType(typeNames, typeB) { // Check if typeB.name is in typeNames, either before or after normalization. var typeBName = typeB.name; if (typeBName !== undefined) { if (typeNames.includes(typeBName)) { return true; } var normalized = normalizeTypeName(typeBName); if (normalized !== undefined && typeNames.includes(normalized)) { return true; } } // Otherwise, check for other cases beyond just a simple named type. switch (typeB.kind) { case "UNION": return typeB.types.every(function (t) { return matchesAtLeastOneNominalType(typeNames, t); }); case "STRING_LITERAL": case "STRING": return typeNames.includes("string"); case "GENERIC_ARGUMENTS": return matchesAtLeastOneNominalType(typeNames, typeB.target); default: return false; } }