Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@kitajs/ts-html-plugin

Package Overview
Dependencies
Maintainers
0
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@kitajs/ts-html-plugin - npm Package Compare versions

Comparing version 4.0.3 to 4.1.0

9

dist/cli.js

@@ -7,3 +7,2 @@ #!/usr/bin/env node

const node_fs_1 = tslib_1.__importDefault(require("node:fs"));
const node_os_1 = require("node:os");
const node_path_1 = tslib_1.__importDefault(require("node:path"));

@@ -17,3 +16,3 @@ const typescript_1 = tslib_1.__importDefault(require("typescript"));

ts-html-plugin v${version} - A CLI tool & TypeScript LSP for finding XSS vulnerabilities in your TypeScript code.
@kitajs/ts-html-plugin v${version} - A CLI tool & TypeScript LSP for finding XSS vulnerabilities in your TypeScript code.

@@ -110,3 +109,3 @@ Usage: xss-scan [options] <file> <file>...

const simplified = !!(args.simplified || args.s);
const diagnosticFormatter = simplified
const diagnosticFormatter = !process.stdout.isTTY || simplified
? typescript_1.default.formatDiagnostics

@@ -120,5 +119,5 @@ : typescript_1.default.formatDiagnosticsWithColorAndContext;

const diagnosticHost = {
getCurrentDirectory: () => root,
getCurrentDirectory: typescript_1.default.sys.getCurrentDirectory,
getCanonicalFileName: (fileName) => fileName,
getNewLine: () => node_os_1.EOL
getNewLine: () => typescript_1.default.sys.newLine
};

@@ -125,0 +124,0 @@ if (tsconfig.errors) {

@@ -18,3 +18,3 @@ "use strict";

}
const source = program?.getSourceFile(filename);
const source = program.getSourceFile(filename);
if (!source) {

@@ -21,0 +21,0 @@ return diagnostics;

@@ -7,5 +7,5 @@ import ts, { type JsxFragment } from 'typescript';

export declare function diagnoseJsxElement(ts: typeof TS, node: JsxElement | JsxFragment, typeChecker: TypeChecker, diagnostics: Diagnostic[]): void;
export declare function isSafeAttribute(ts: typeof TS, type: Type | undefined, expression: ts.Expression, checker: TypeChecker): boolean;
export declare function isSafeAttribute(ts: typeof TS, type: Type | undefined, checker: TypeChecker, node: ts.Node): boolean;
export declare function getSafeAttribute(element: JsxOpeningElement): ts.JsxAttributeLike | undefined;
export declare function proxyObject<T extends object>(obj: T): T;
//# sourceMappingURL=util.d.ts.map

@@ -49,3 +49,2 @@ "use strict";

function diagnoseJsxElement(ts, node, typeChecker, diagnostics) {
const file = node.getSourceFile();
// Validations that does not applies to fragments or serlf closing elements

@@ -80,10 +79,14 @@ if (ts.isJsxElement(node)) {

// has inner expression
exp.expression &&
// is expression safe
isSafeAttribute(ts, typeChecker.getTypeAtLocation(exp.expression), exp.expression, typeChecker) &&
// does not starts with unsafe
!exp.expression.getText().startsWith('unsafe') &&
// Avoids double warnings
!diagnostics.some((d) => d.start === safeAttribute.pos + 1 && d.file === file)) {
diagnostics.push(diagnostic(safeAttribute, 'UnusedSafe', 'Warning'));
exp.expression) {
// gets this expression or array of sub expressions
const expressions = getNodeExpressions(exp.expression) || [exp.expression];
// at least one jsx inside another jsx with safe
if (expressions.some((inner) => ts.isJsxElement(inner))) {
diagnostics.push(diagnostic(safeAttribute, 'DoubleEscape', 'Error'));
continue;
}
// all of them must be safe
if (expressions.every((inner) => isSafeAttribute(ts, typeChecker.getTypeAtLocation(inner), typeChecker, inner))) {
diagnostics.push(diagnostic(safeAttribute, 'UnusedSafe', 'Warning'));
}
}

@@ -121,34 +124,13 @@ }

}
// Checks both sides
if (ts.isBinaryExpression(node)) {
// Ignores operations which results in a boolean
switch (node.operatorToken.kind) {
case ts.SyntaxKind.EqualsEqualsEqualsToken:
case ts.SyntaxKind.EqualsEqualsToken:
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
case ts.SyntaxKind.ExclamationEqualsToken:
case ts.SyntaxKind.GreaterThanToken:
case ts.SyntaxKind.GreaterThanEqualsToken:
case ts.SyntaxKind.LessThanEqualsToken:
case ts.SyntaxKind.LessThanToken:
case ts.SyntaxKind.InstanceOfKeyword:
case ts.SyntaxKind.InKeyword:
return;
const expressions = getNodeExpressions(node);
// ternary or binary expressions should be evaluated on each side
if (expressions) {
for (const inner of expressions) {
diagnoseExpression(ts, inner, typeChecker, diagnostics, isComponent);
}
// We do not need to evaluate the left side of the expression
// as its value will only be used if its falsy, which cannot have
// XSS content
diagnoseExpression(ts, node.right, typeChecker, diagnostics, isComponent);
return;
}
// Checks the inner expression
if (ts.isConditionalExpression(node)) {
diagnoseExpression(ts, node.whenTrue, typeChecker, diagnostics, isComponent);
diagnoseExpression(ts, node.whenFalse, typeChecker, diagnostics, isComponent);
// ignore node.condition because its value will never be rendered
return;
}
const type = typeChecker.getTypeAtLocation(node);
// Safe can be ignored
if (isSafeAttribute(ts, type, node, typeChecker)) {
if (isSafeAttribute(ts, type, typeChecker, node)) {
return;

@@ -180,3 +162,3 @@ }

}
function isSafeAttribute(ts, type, expression, checker) {
function isSafeAttribute(ts, type, checker, node) {
// Nothing to do if type cannot be resolved

@@ -192,7 +174,8 @@ if (!type) {

// Allows JSX.Element
if (type.aliasSymbol.escapedName === 'Element' &&
if (node &&
type.aliasSymbol.escapedName === 'Element' &&
// @ts-expect-error - Fast way of checking
type.aliasSymbol.parent?.escapedName === 'JSX' &&
// Only allows in .map(), other method calls or the expression itself
(ts.isCallExpression(expression) || ts.isIdentifier(expression))) {
(ts.isCallExpression(node) || ts.isIdentifier(node))) {
return true;

@@ -218,9 +201,14 @@ }

// Union types should be checked recursively
if (type.isUnion()) {
return type.types.every((t) => isSafeAttribute(ts, t, expression, checker));
if (type.isUnionOrIntersection()) {
return type.types.every((innerType) => isSafeAttribute(ts, innerType, checker, node));
}
// For Array or Promise, we check the type of the first generic
if (checker.isArrayType(type) || type.symbol?.escapedName === 'Promise') {
return isSafeAttribute(ts, type.resolvedTypeArguments?.[0], expression, checker);
return isSafeAttribute(ts, type.resolvedTypeArguments?.[0], checker, node);
}
const text = node.getText();
// manual unsafe variables should not pass
if (text.startsWith('unsafe')) {
return false;
}
// We allow literal string types here, as if they have XSS content,

@@ -235,3 +223,2 @@ // the user has explicitly written it

}
const text = expression.getText();
if (

@@ -263,2 +250,40 @@ // Variables starting with safe are suppressed

}
/**
* Returns more than one node if the node is a binary expression or a conditional
* expression
*/
function getNodeExpressions(node) {
// Checks operators
if (typescript_1.default.isBinaryExpression(node)) {
// Ignores operations which results in a boolean
if (isBooleanBinaryOperatorToken(node.operatorToken)) {
return [];
}
// Diagnose both sides since both sides can be executed, e.g:
// a empty string in the left side will execute the right side
return [node.left, node.right];
}
// Checks the inner expression
if (typescript_1.default.isConditionalExpression(node)) {
// ignore node.condition because its value will never be rendered
return [node.whenFalse, node.whenFalse];
}
return undefined;
}
function isBooleanBinaryOperatorToken(operator) {
switch (operator.kind) {
case typescript_1.default.SyntaxKind.EqualsEqualsEqualsToken:
case typescript_1.default.SyntaxKind.EqualsEqualsToken:
case typescript_1.default.SyntaxKind.ExclamationEqualsEqualsToken:
case typescript_1.default.SyntaxKind.ExclamationEqualsToken:
case typescript_1.default.SyntaxKind.GreaterThanToken:
case typescript_1.default.SyntaxKind.GreaterThanEqualsToken:
case typescript_1.default.SyntaxKind.LessThanEqualsToken:
case typescript_1.default.SyntaxKind.LessThanToken:
case typescript_1.default.SyntaxKind.InstanceOfKeyword:
case typescript_1.default.SyntaxKind.InKeyword:
return true;
}
return false;
}
//# sourceMappingURL=util.js.map
{
"name": "@kitajs/ts-html-plugin",
"version": "4.0.3",
"version": "4.1.0",
"homepage": "https://github.com/kitajs/html/tree/master/packages/ts-html-plugin#readme",

@@ -29,4 +29,4 @@ "bugs": "https://github.com/kitajs/html/issues",

"@swc-node/register": "^1.10.9",
"@swc/helpers": "^0.5.12",
"@types/node": "^22.5.2",
"@swc/helpers": "^0.5.13",
"@types/node": "^22.5.5",
"@types/yargs": "^17.0.33",

@@ -38,3 +38,3 @@ "fast-defer": "^1.1.8",

"@kitajs/html": "^4.2.2",
"typescript": "^5.3.3"
"typescript": "^5.6.2"
},

@@ -41,0 +41,0 @@ "scripts": {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc