From 30d83731795b7658ebb7aaf6af90f97b32ad47b6 Mon Sep 17 00:00:00 2001 From: Thomas GENTILHOMME Date: Thu, 16 Jun 2022 14:35:59 +0200 Subject: [PATCH] refactor: improve warnings usage and TS definitions --- README.md | 43 +++-- index.d.ts | 172 ++++-------------- index.js | 58 +----- src/Analysis.js | 41 ++--- src/constants.js | 13 -- src/probes/isImportDeclaration.js | 5 +- src/probes/isLiteral.js | 6 +- src/probes/isLiteralRegex.js | 3 +- src/probes/isRegexObject.js | 3 +- src/probes/isRequire.js | 11 +- src/probes/isUnsafeCallee.js | 3 +- src/probes/isVariableDeclaration.js | 10 +- src/probes/isWeakCrypto.js | 11 +- src/utils.js | 22 --- src/warnings.js | 70 +++++++ test/obfuscated.spec.js | 17 +- .../weakCrypto/directCallExpression/md2.js | 0 .../weakCrypto/directCallExpression/md4.js | 0 .../weakCrypto/directCallExpression/md5.js | 0 .../directCallExpression/ripemd160.js | 0 .../weakCrypto/directCallExpression/sha1.js | 0 .../weakCrypto/memberExpression/md2.js | 0 .../weakCrypto/memberExpression/md4.js | 0 .../weakCrypto/memberExpression/md5.js | 0 .../weakCrypto/memberExpression/ripemd160.js | 0 .../weakCrypto/memberExpression/sha1.js | 0 .../weakCrypto/strongAlgorithms/sha256.js | 0 test/probes/isUnsafeCallee.spec.js | 3 +- test/{ => probes}/weakCrypto.spec.js | 12 +- test/searchRuntimeDependencies.spec.js | 53 +++--- test/warnings.spec.js | 46 +++++ types/api.d.ts | 44 +++++ types/astdeps.d.ts | 30 +++ types/warnings.d.ts | 33 ++++ 34 files changed, 351 insertions(+), 358 deletions(-) create mode 100644 src/warnings.js rename test/{ => probes}/fixtures/weakCrypto/directCallExpression/md2.js (100%) rename test/{ => probes}/fixtures/weakCrypto/directCallExpression/md4.js (100%) rename test/{ => probes}/fixtures/weakCrypto/directCallExpression/md5.js (100%) rename test/{ => probes}/fixtures/weakCrypto/directCallExpression/ripemd160.js (100%) rename test/{ => probes}/fixtures/weakCrypto/directCallExpression/sha1.js (100%) rename test/{ => probes}/fixtures/weakCrypto/memberExpression/md2.js (100%) rename test/{ => probes}/fixtures/weakCrypto/memberExpression/md4.js (100%) rename test/{ => probes}/fixtures/weakCrypto/memberExpression/md5.js (100%) rename test/{ => probes}/fixtures/weakCrypto/memberExpression/ripemd160.js (100%) rename test/{ => probes}/fixtures/weakCrypto/memberExpression/sha1.js (100%) rename test/{ => probes}/fixtures/weakCrypto/strongAlgorithms/sha256.js (100%) rename test/{ => probes}/weakCrypto.spec.js (86%) create mode 100644 test/warnings.spec.js create mode 100644 types/api.d.ts create mode 100644 types/astdeps.d.ts create mode 100644 types/warnings.d.ts diff --git a/README.md b/README.md index e440228d..20e23cdb 100644 --- a/README.md +++ b/README.md @@ -77,24 +77,23 @@ The analysis will return: `http` (in try), `crypto`, `util` and `fs`. This section describes how use `warnings` export. -The structure of the `warnings` is as follows: -```js -/** - * @property {object} warnings - The default values for Constants. - * @property {string} warnings[name] - The default warning name (parsingError, unsafeImport etc...). - * @property {string} warnings[name].i18n - i18n token. - * @property {string} warnings[name].code - Used to perform unit tests. - * @property {string} warnings[name].severity - Warning severity. - */ - -export const warnings = Object.freeze({ - parsingError: { - i18n: "sast_warnings.ast_error" - code: "ast-error", - severity: "Information" - }, - ...otherWarnings - }); +```ts +type WarningName = "parsing-error" +| "encoded-literal" +| "unsafe-regex" +| "unsafe-stmt" +| "unsafe-assign" +| "short-identifiers" +| "suspicious-literal" +| "obfuscated-code" +| "weak-crypto" +| "unsafe-import"; + +declare const warnings: Record; ``` We make a call to `i18n` through the package `NodeSecure/i18n` to get the translation. @@ -103,7 +102,7 @@ We make a call to `i18n` through the package `NodeSecure/i18n` to get the transl import * as jsxray from "@nodesecure/js-x-ray"; import * as i18n from "@nodesecure/i18n"; -console.log(i18n.getToken(jsxray.warnings.parsingError.i18n)); +console.log(i18n.getToken(jsxray.warnings["parsing-error"].i18n)); ``` ## Warnings Legends @@ -142,7 +141,7 @@ The method take a first argument which is the code you want to analyse. It will ```ts interface Report { dependencies: ASTDeps; - warnings: Warning[]; + warnings: Warning[]; idsLengthAvg: number; stringScore: number; isOneLineRequire: boolean; @@ -166,12 +165,12 @@ Run the SAST scanner on a given JavaScript file. ```ts export type ReportOnFile = { ok: true, - warnings: Warning[]; + warnings: Warning[]; dependencies: ASTDeps; isMinified: boolean; } | { ok: false, - warnings: Warning[]; + warnings: Warning[]; } ``` diff --git a/index.d.ts b/index.d.ts index 9188cc78..bfce343d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,140 +1,34 @@ -declare class ASTDeps { - constructor(); - removeByName(name: string): void; - add(depName: string): void; - getDependenciesInTryStatement(): IterableIterator; - - public isInTryStmt: boolean; - public dependencies: Record; - public readonly size: number; -} - -declare namespace JSXRay { - type kindWithValue = "parsing-error" - | "encoded-literal" - | "unsafe-regex" - | "unsafe-stmt" - | "unsafe-assign" - | "short-identifiers" - | "suspicious-literal" - | "obfuscated-code" - | "weak-crypto"; - - type WarningLocation = [[number, number], [number, number]]; - interface BaseWarning { - kind: "unsafe-import" | kindWithValue; - file?: string; - value: string; - location: WarningLocation | WarningLocation[]; - } - - type Warning = T extends { kind: kindWithValue } ? T : Omit; - - interface Report { - dependencies: ASTDeps; - warnings: Warning[]; - idsLengthAvg: number; - stringScore: number; - isOneLineRequire: boolean; - } - - interface SourceLocation { - start: { - line: number; - column: number; - }; - end: { - line: number; - column: number; - } - } - - interface Dependency { - unsafe: boolean; - inTry: boolean; - location?: SourceLocation; - } - - interface WarningsNames { - parsingError: { - code: "ast-error", - i18n: "sast_warnings.ast_error", - severity: "Information" - }, - unsafeImport: { - code: "unsafe-import", - i18n: "sast_warnings.unsafe_import", - severity: "Warning" - }, - unsafeRegex: { - code: "unsafe-regex", - i18n: "sast_warnings.unsafe_regex", - severity: "Warning" - }, - unsafeStmt: { - code: "unsafe-stmt", - i18n: "sast_warnings.unsafe_stmt", - severity: "Warning" - }, - unsafeAssign: { - code: "unsafe-assign", - i18n: "sast_warnings.unsafe_assign", - severity: "Warning" - }, - encodedLiteral: { - code: "encoded-literal", - i18n: "sast_warnings.encoded_literal", - severity: "Information" - }, - shortIdentifiers: { - code: "short-identifiers", - i18n: "sast_warnings.short_identifiers", - severity: "Warning" - }, - suspiciousLiteral: { - code: "suspicious-literal", - i18n: "sast_warnings.suspicious_literal", - severity: "Warning" - }, - obfuscatedCode: { - code: "obfuscated-code", - i18n: "sast_warnings.obfuscated_code", - severity: "Critical" - }, - weakCrypto: { - code: "weak-crypto", - i18n: "sast_warnings.weak_crypto", - severity: "Information", - experimental: true - } - } - - interface RuntimeOptions { - module?: boolean; - isMinified?: boolean; - } - - export function runASTAnalysis(str: string, options?: RuntimeOptions): Report; - - export type ReportOnFile = { - ok: true, - warnings: Warning[]; - dependencies: ASTDeps; - isMinified: boolean; - } | { - ok: false, - warnings: Warning[]; - } - - export interface RuntimeFileOptions { - packageName?: string; - module?: boolean; - } - - export function runASTAnalysisOnFile(pathToFile: string, options?: RuntimeFileOptions): Promise; - - export const warnings: WarningsNames; +import { + runASTAnalysis, + runASTAnalysisOnFile, + Report, + ReportOnFile, + RuntimeFileOptions, + RuntimeOptions +} from "./types/api"; +import { + Warning, + WarningDefault, + WarningLocation, + WarningName, + WarningNameWithValue +} from "./types/warnings"; +import { ASTDeps } from "./types/astdeps"; + +declare const warnings: Record>; + +export { + warnings, + runASTAnalysis, + runASTAnalysisOnFile, + Report, + ReportOnFile, + RuntimeFileOptions, + RuntimeOptions, + ASTDeps, + Warning, + WarningDefault, + WarningLocation, + WarningName, + WarningNameWithValue } - -export = JSXRay; -export as namespace JSXRay; diff --git a/index.js b/index.js index a872fff8..36db976a 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ import isMinified from "is-minified-code"; // Import Internal Dependencies import Analysis from "./src/Analysis.js"; +import { warnings } from "./src/warnings.js"; export function runASTAnalysis(str, options = Object.create(null)) { const { module = true, isMinified = false } = options; @@ -83,59 +84,4 @@ export async function runASTAnalysisOnFile(pathToFile, options = {}) { } } -export const warnings = Object.freeze({ - parsingError: { - code: "ast-error", - i18n: "sast_warnings.ast_error", - severity: "Information" - }, - unsafeImport: { - code: "unsafe-import", - i18n: "sast_warnings.unsafe_import", - severity: "Warning" - }, - unsafeRegex: { - code: "unsafe-regex", - i18n: "sast_warnings.unsafe_regex", - severity: "Warning" - }, - unsafeStmt: { - code: "unsafe-stmt", - i18n: "sast_warnings.unsafe_stmt", - severity: "Warning" - }, - unsafeAssign: { - code: "unsafe-assign", - i18n: "sast_warnings.unsafe_assign", - severity: "Warning" - }, - encodedLiteral: { - code: "encoded-literal", - i18n: "sast_warnings.encoded_literal", - severity: "Information" - }, - shortIdentifiers: { - code: "short-identifiers", - i18n: "sast_warnings.short_identifiers", - severity: "Warning" - }, - suspiciousLiteral: { - code: "suspicious-literal", - i18n: "sast_warnings.suspicious_literal", - severity: "Warning" - }, - obfuscatedCode: { - code: "obfuscated-code", - i18n: "sast_warnings.obfuscated_code", - severity: "Critical", - experimental: true - }, - weakCrypto: { - code: "weak-crypto", - i18n: "sast_warnings.weak_crypto", - severity: "Information", - experimental: true - } -}); - - +export { warnings }; diff --git a/src/Analysis.js b/src/Analysis.js index fef6f72d..0d57c575 100644 --- a/src/Analysis.js +++ b/src/Analysis.js @@ -2,8 +2,9 @@ import { Utils, Literal } from "@nodesecure/sec-literal"; // Import Internal Dependencies -import { rootLocation, toArrayLocation, generateWarning } from "./utils.js"; -import { warnings as _warnings, processMainModuleRequire } from "./constants.js"; +import { rootLocation, toArrayLocation } from "./utils.js"; +import { generateWarning } from "./warnings.js"; +import { processMainModuleRequire } from "./constants.js"; import ASTDeps from "./ASTDeps.js"; import { isObfuscatedCode, hasTrojanSource } from "./obfuscators/index.js"; import { runOnProbes } from "./probes/index.js"; @@ -15,19 +16,6 @@ const kDictionaryStrParts = [ "0123456789" ]; -const kWarningsNameStr = Object.freeze({ - [_warnings.parsingError]: "parsing-error", - [_warnings.unsafeImport]: "unsafe-import", - [_warnings.unsafeRegex]: "unsafe-regex", - [_warnings.unsafeStmt]: "unsafe-stmt", - [_warnings.unsafeAssign]: "unsafe-assign", - [_warnings.encodedLiteral]: "encoded-literal", - [_warnings.shortIdentifiers]: "short-identifiers", - [_warnings.suspiciousLiteral]: "suspicious-literal", - [_warnings.obfuscatedCode]: "obfuscated-code", - [_warnings.weakCrypto]: "weak-crypto" -}); - export default class Analysis { hasDictionaryString = false; hasPrefixedIdentifiers = false; @@ -56,23 +44,24 @@ export default class Analysis { this.literalScores = []; } - addWarning(symbol, value, location = rootLocation()) { - if (symbol === _warnings.encodedLiteral && this.handledEncodedLiteralValues.has(value)) { + addWarning(name, value, location = rootLocation()) { + const isEncodedLiteral = name === "encoded-literal"; + if (isEncodedLiteral && this.handledEncodedLiteralValues.has(value)) { const index = this.handledEncodedLiteralValues.get(value); this.warnings[index].location.push(toArrayLocation(location)); return; } - const warningName = kWarningsNameStr[symbol]; - this.warnings.push(generateWarning(warningName, { value, location })); - if (symbol === _warnings.encodedLiteral) { + + this.warnings.push(generateWarning(name, { value, location })); + if (isEncodedLiteral) { this.handledEncodedLiteralValues.set(value, this.warnings.length - 1); } } analyzeSourceString(sourceString) { if (hasTrojanSource(sourceString)) { - this.addWarning(_warnings.obfuscatedCode, "trojan-source"); + this.addWarning("obfuscated-code", "trojan-source"); } } @@ -107,7 +96,7 @@ export default class Analysis { this.counter.encodedArrayValue++; } else { - this.addWarning(_warnings.encodedLiteral, node.value, node.loc); + this.addWarning("encoded-literal", node.value, node.loc); } } } @@ -116,7 +105,7 @@ export default class Analysis { this.counter.identifiers = this.identifiersName.length; const [isObfuscated, kind] = isObfuscatedCode(this); if (isObfuscated) { - this.addWarning(_warnings.obfuscatedCode, kind || "unknown"); + this.addWarning("obfuscated-code", kind || "unknown"); } const identifiersLengthArr = this.identifiersName @@ -124,10 +113,10 @@ export default class Analysis { const [idsLengthAvg, stringScore] = [sum(identifiersLengthArr), sum(this.literalScores)]; if (!isMinified && identifiersLengthArr.length > 5 && idsLengthAvg <= 1.5) { - this.addWarning(_warnings.shortIdentifiers, idsLengthAvg); + this.addWarning("short-identifiers", idsLengthAvg); } if (stringScore >= 3) { - this.addWarning(_warnings.suspiciousLiteral, stringScore); + this.addWarning("suspicious-literal", stringScore); } return { idsLengthAvg, stringScore, warnings: this.warnings }; @@ -149,5 +138,3 @@ export default class Analysis { function sum(arr = []) { return arr.length === 0 ? 0 : (arr.reduce((prev, curr) => prev + curr, 0) / arr.length); } - -Analysis.Warnings = _warnings; diff --git a/src/constants.js b/src/constants.js index 345816a8..3c555074 100644 --- a/src/constants.js +++ b/src/constants.js @@ -33,16 +33,3 @@ export const unsafeUnicodeControlCharacters = [ "\u200F", "\u061C" ]; - -export const warnings = Object.freeze({ - parsingError: Symbol("ParsingError"), - unsafeImport: Symbol("UnsafeImport"), - unsafeRegex: Symbol("UnsafeRegex"), - unsafeStmt: Symbol("UnsafeStmt"), - unsafeAssign: Symbol("UnsafeAssign"), - encodedLiteral: Symbol("EncodedLiteral"), - shortIdentifiers: Symbol("ShortIdentifiers"), - suspiciousLiteral: Symbol("SuspiciousLiteral"), - obfuscatedCode: Symbol("ObfuscatedCode"), - weakCrypto: Symbol("WeakCrypto") -}); diff --git a/src/probes/isImportDeclaration.js b/src/probes/isImportDeclaration.js index db0d3318..d58aa49f 100644 --- a/src/probes/isImportDeclaration.js +++ b/src/probes/isImportDeclaration.js @@ -1,6 +1,3 @@ -// Require Internal Dependencies -import { warnings } from "../constants.js"; - /** * @description Search for ESM ImportDeclaration * @see https://github.com/estree/estree/blob/master/es2015.md#importdeclaration @@ -22,7 +19,7 @@ function main(node, options) { // Searching for dangerous import "data:text/javascript;..." statement. // see: https://2ality.com/2019/10/eval-via-import.html if (node.source.value.startsWith("data:text/javascript")) { - analysis.addWarning(warnings.unsafeImport, node.source.value, node.loc); + analysis.addWarning("unsafe-import", node.source.value, node.loc); } analysis.dependencies.add(node.source.value, node.loc); } diff --git a/src/probes/isLiteral.js b/src/probes/isLiteral.js index d32487f9..af6c4bcf 100644 --- a/src/probes/isLiteral.js +++ b/src/probes/isLiteral.js @@ -5,7 +5,7 @@ import { builtinModules } from "repl"; import { Hex } from "@nodesecure/sec-literal"; // Import Internal Dependencies -import { globalParts, warnings } from "../constants.js"; +import { globalParts } from "../constants.js"; // CONSTANTS const kNodeDeps = new Set(builtinModules); @@ -34,10 +34,10 @@ function main(node, options) { // then we add it to the dependencies list and we throw an unsafe-import at the current location. if (kNodeDeps.has(value)) { analysis.dependencies.add(value, node.loc); - analysis.addWarning(warnings.unsafeImport, null, node.loc); + analysis.addWarning("unsafe-import", null, node.loc); } else if (globalParts.has(value) || !Hex.isSafe(node.value)) { - analysis.addWarning(warnings.encodedLiteral, node.value, node.loc); + analysis.addWarning("encoded-literal", node.value, node.loc); } } // Else we are checking all other string with our suspect method diff --git a/src/probes/isLiteralRegex.js b/src/probes/isLiteralRegex.js index 4968ca93..1c405401 100644 --- a/src/probes/isLiteralRegex.js +++ b/src/probes/isLiteralRegex.js @@ -1,6 +1,5 @@ // Require Internal Dependencies import { isLiteralRegex } from "../utils.js"; -import { warnings } from "../constants.js"; // Require Third-party Dependencies import safeRegex from "safe-regex"; @@ -22,7 +21,7 @@ function main(node, options) { // We use the safe-regex package to detect whether or not regex is safe! if (!safeRegex(node.regex.pattern)) { - analysis.addWarning(warnings.unsafeRegex, node.regex.pattern, node.loc); + analysis.addWarning("unsafe-regex", node.regex.pattern, node.loc); } } diff --git a/src/probes/isRegexObject.js b/src/probes/isRegexObject.js index baaacdc6..367d7831 100644 --- a/src/probes/isRegexObject.js +++ b/src/probes/isRegexObject.js @@ -1,6 +1,5 @@ // Import Internal Dependencies import { isLiteralRegex } from "../utils.js"; -import { warnings } from "../constants.js"; // Import Third-party Dependencies import safeRegex from "safe-regex"; @@ -25,7 +24,7 @@ function main(node, options) { // We use the safe-regex package to detect whether or not regex is safe! if (!safeRegex(pattern)) { - analysis.addWarning(warnings.unsafeRegex, pattern, node.loc); + analysis.addWarning("unsafe-regex", pattern, node.loc); } } diff --git a/src/probes/isRequire.js b/src/probes/isRequire.js index 92552a6b..02a52406 100644 --- a/src/probes/isRequire.js +++ b/src/probes/isRequire.js @@ -2,7 +2,6 @@ // Import Internal Dependencies import { isRequireGlobalMemberExpr, getMemberExprName, arrExprToString, concatBinaryExpr } from "../utils.js"; -import { warnings } from "../constants.js"; // Import Third-party Dependencies import { Hex } from "@nodesecure/sec-literal"; @@ -52,7 +51,7 @@ function main(node, options) { analysis.dependencies.add(analysis.identifiers.get(arg.name), node.loc); } else { - analysis.addWarning(warnings.unsafeImport, null, node.loc); + analysis.addWarning("unsafe-import", null, node.loc); } break; @@ -65,7 +64,7 @@ function main(node, options) { case "ArrayExpression": { const value = arrExprToString(arg.elements, analysis.identifiers).trim(); if (value === "") { - analysis.addWarning(warnings.unsafeImport, null, node.loc); + analysis.addWarning("unsafe-import", null, node.loc); } else { analysis.dependencies.add(value, node.loc); @@ -81,7 +80,7 @@ function main(node, options) { const value = concatBinaryExpr(arg, analysis.identifiers); if (value === null) { - analysis.addWarning(warnings.unsafeImport, null, node.loc); + analysis.addWarning("unsafe-import", null, node.loc); } else { analysis.dependencies.add(value, node.loc); @@ -94,14 +93,14 @@ function main(node, options) { const { dependencies } = parseRequireCallExpression(arg); dependencies.forEach((depName) => analysis.dependencies.add(depName, node.loc, true)); - analysis.addWarning(warnings.unsafeImport, null, node.loc); + analysis.addWarning("unsafe-import", null, node.loc); // We skip walking the tree to avoid anymore warnings... return Symbol.for("skipWalk"); } default: - analysis.addWarning(warnings.unsafeImport, null, node.loc); + analysis.addWarning("unsafe-import", null, node.loc); } } diff --git a/src/probes/isUnsafeCallee.js b/src/probes/isUnsafeCallee.js index e981d640..41638e35 100644 --- a/src/probes/isUnsafeCallee.js +++ b/src/probes/isUnsafeCallee.js @@ -1,6 +1,5 @@ // Require Internal Dependencies import { isUnsafeCallee } from "../utils.js"; -import { warnings } from "../constants.js"; /** * @description Detect unsafe statement @@ -15,7 +14,7 @@ function validateNode(node) { function main(node, options) { const { analysis, data: calleeName } = options; - analysis.addWarning(warnings.unsafeStmt, calleeName, node.loc); + analysis.addWarning("unsafe-stmt", calleeName, node.loc); } export default { diff --git a/src/probes/isVariableDeclaration.js b/src/probes/isVariableDeclaration.js index 27ccf81c..9ac33bdc 100644 --- a/src/probes/isVariableDeclaration.js +++ b/src/probes/isVariableDeclaration.js @@ -1,6 +1,6 @@ // Require Internal Dependencies import { getIdName, getMemberExprName, isUnsafeCallee, isRequireGlobalMemberExpr } from "../utils.js"; -import { warnings, globalParts, processMainModuleRequire } from "../constants.js"; +import { globalParts, processMainModuleRequire } from "../constants.js"; // CONSTANTS const kUnsafeCallee = new Set(["eval", "Function"]); @@ -36,11 +36,11 @@ function main(mainNode, options) { // const r = require else if (node.init.type === "Identifier") { if (kUnsafeCallee.has(node.init.name)) { - analysis.addWarning(warnings.unsafeAssign, node.init.name, node.loc); + analysis.addWarning("unsafe-assign", node.init.name, node.loc); } else if (analysis.requireIdentifiers.has(node.init.name)) { analysis.requireIdentifiers.add(node.id.name); - analysis.addWarning(warnings.unsafeAssign, node.init.name, node.loc); + analysis.addWarning("unsafe-assign", node.init.name, node.loc); } else if (globalParts.has(node.init.name)) { analysis.globalParts.set(node.id.name, node.init.name); @@ -56,14 +56,14 @@ function main(mainNode, options) { if (analysis.globalParts.has(members[0]) || members.every((part) => globalParts.has(part))) { analysis.globalParts.set(node.id.name, members.slice(1).join(".")); - analysis.addWarning(warnings.unsafeAssign, value, node.loc); + analysis.addWarning("unsafe-assign", value, node.loc); } getRequirablePatterns(analysis.globalParts) .forEach((name) => analysis.requireIdentifiers.add(name)); if (isRequireStatement(value)) { analysis.requireIdentifiers.add(node.id.name); - analysis.addWarning(warnings.unsafeAssign, value, node.loc); + analysis.addWarning("unsafe-assign", value, node.loc); } } else if (isUnsafeCallee(node.init)[0]) { diff --git a/src/probes/isWeakCrypto.js b/src/probes/isWeakCrypto.js index 5339cfe6..7abefd1b 100644 --- a/src/probes/isWeakCrypto.js +++ b/src/probes/isWeakCrypto.js @@ -1,8 +1,5 @@ -// Internal Dependencies -import { warnings } from "../constants.js"; - -// Constants -const weakAlgorithms = new Set(["md5", "sha1", "ripemd160", "md4", "md2"]); +// CONSTANTS +const kWeakAlgorithms = new Set(["md5", "sha1", "ripemd160", "md4", "md2"]); function validateNode(node) { const isCallExpression = node.type === "CallExpression"; @@ -21,10 +18,10 @@ function main(node, { analysis }) { const isCryptoImported = analysis.dependencies.has("crypto"); if ( - weakAlgorithms.has(arg.value) && + kWeakAlgorithms.has(arg.value) && isCryptoImported ) { - analysis.addWarning(warnings.weakCrypto, arg.value, node.loc); + analysis.addWarning("weak-crypto", arg.value, node.loc); } } diff --git a/src/utils.js b/src/utils.js index df7283be..30d02fa9 100644 --- a/src/utils.js +++ b/src/utils.js @@ -164,25 +164,3 @@ export function toArrayLocation(location = rootLocation()) { return [[start.line || 0, start.column || 0], [end.line || 0, end.column || 0]]; } - -export function generateWarning(kind, options) { - const { location, file = null, value = null } = options; - - if (kind === "encoded-literal") { - return { kind, value, location: [toArrayLocation(location)] }; - } - - const result = { kind, location: toArrayLocation(location) }; - if (notNullOrUndefined(file)) { - result.file = file; - } - if (notNullOrUndefined(value)) { - result.value = value; - } - - if (kExperimentalWarnings.has(kind)) { - result.experimental = true; - } - - return result; -} diff --git a/src/warnings.js b/src/warnings.js new file mode 100644 index 00000000..df78664b --- /dev/null +++ b/src/warnings.js @@ -0,0 +1,70 @@ +// Import Internal Dependencies +import * as utils from "./utils.js"; + +export const warnings = Object.freeze({ + "ast-error": { + i18n: "sast_warnings.ast_error", + severity: "Information" + }, + "unsafe-import": { + i18n: "sast_warnings.unsafe_import", + severity: "Warning" + }, + "unsafe-regex": { + i18n: "sast_warnings.unsafe_regex", + severity: "Warning" + }, + "unsafe-stmt": { + code: "unsafe-stmt", + i18n: "sast_warnings.unsafe_stmt", + severity: "Warning" + }, + "unsafe-assign": { + i18n: "sast_warnings.unsafe_assign", + severity: "Warning" + }, + "encoded-literal": { + i18n: "sast_warnings.encoded_literal", + severity: "Information" + }, + "short-identifiers": { + i18n: "sast_warnings.short_identifiers", + severity: "Warning" + }, + "suspicious-literal": { + i18n: "sast_warnings.suspicious_literal", + severity: "Warning" + }, + "obfuscated-code": { + i18n: "sast_warnings.obfuscated_code", + severity: "Critical", + experimental: true + }, + "weak-crypto": { + i18n: "sast_warnings.weak_crypto", + severity: "Information", + experimental: true + } +}); + +export function generateWarning(kind, options) { + const { location, file = null, value = null } = options; + + if (kind === "encoded-literal") { + return Object.assign( + { kind, value, location: [utils.toArrayLocation(location)] }, + warnings[kind] + ); + } + + const result = { kind, location: utils.toArrayLocation(location) }; + if (utils.notNullOrUndefined(file)) { + result.file = file; + } + if (utils.notNullOrUndefined(value)) { + result.value = value; + } + + return Object.assign(result, warnings[kind]); +} + diff --git a/test/obfuscated.spec.js b/test/obfuscated.spec.js index 56be3d74..36488826 100644 --- a/test/obfuscated.spec.js +++ b/test/obfuscated.spec.js @@ -7,11 +7,10 @@ import { join, dirname } from "path"; import test from "tape"; // Import Internal Dependencies -import { runASTAnalysis, warnings, runASTAnalysisOnFile } from "../index.js"; +import { runASTAnalysis, runASTAnalysisOnFile } from "../index.js"; import { getWarningKind } from "./utils/index.js"; // CONSTANTS -const { obfuscatedCode, encodedLiteral } = warnings; const __dirname = dirname(fileURLToPath(import.meta.url)); const FIXTURE_PATH = join(__dirname, "fixtures/obfuscated"); @@ -20,7 +19,7 @@ test("should detect 'jsfuck' obfuscation", (tape) => { const { warnings } = runASTAnalysis(trycatch); tape.strictEqual(warnings.length, 1); - tape.deepEqual(getWarningKind(warnings), [obfuscatedCode.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["obfuscated-code"].sort()); tape.strictEqual(warnings[0].value, "jsfuck"); tape.end(); }); @@ -30,7 +29,7 @@ test("should detect 'jsfuck' obfuscation", (tape) => { // const { warnings } = runASTAnalysis(trycatch); // tape.strictEqual(warnings.length, 1); -// tape.deepEqual(getWarningKind(warnings), [obfuscatedCode.code].sort()); +// tape.deepEqual(getWarningKind(warnings), ["obfuscated-code"].sort()); // tape.strictEqual(warnings[0].value, "morse"); // tape.end(); // }); @@ -40,7 +39,7 @@ test("should detect 'jjencode' obfuscation", (tape) => { const { warnings } = runASTAnalysis(trycatch); tape.strictEqual(warnings.length, 1); - tape.deepEqual(getWarningKind(warnings), [obfuscatedCode.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["obfuscated-code"].sort()); tape.strictEqual(warnings[0].value, "jjencode"); tape.end(); }); @@ -51,7 +50,7 @@ test("should detect 'freejsobfuscator' obfuscation", (tape) => { tape.strictEqual(warnings.length, 3); tape.deepEqual(getWarningKind(warnings), [ - encodedLiteral.code, encodedLiteral.code, obfuscatedCode.code + "encoded-literal", "encoded-literal", "obfuscated-code" ].sort()); tape.strictEqual(warnings[2].value, "freejsobfuscator"); tape.end(); @@ -63,7 +62,7 @@ test("should detect 'obfuscator.io' obfuscation (with hexadecimal generator)", ( tape.strictEqual(warnings.length, 1); tape.deepEqual(getWarningKind(warnings), [ - obfuscatedCode.code + "obfuscated-code" ].sort()); tape.strictEqual(warnings[0].value, "obfuscator.io"); tape.end(); @@ -84,7 +83,7 @@ test("should detect 'trojan-source' when there is one unsafe unicode control cha `); tape.strictEqual(warnings.length, 1); - tape.deepEqual(getWarningKind(warnings), [obfuscatedCode.code]); + tape.deepEqual(getWarningKind(warnings), ["obfuscated-code"]); tape.deepEqual(warnings[0].value, "trojan-source"); tape.end(); }); @@ -93,7 +92,7 @@ test("should detect 'trojan-source' when there is atleast one unsafe unicode con const { warnings } = await runASTAnalysisOnFile(join(FIXTURE_PATH, "unsafe-unicode-chars.js")); tape.strictEqual(warnings.length, 1); - tape.deepEqual(getWarningKind(warnings), [obfuscatedCode.code]); + tape.deepEqual(getWarningKind(warnings), ["obfuscated-code"]); tape.deepEqual(warnings[0].value, "trojan-source"); tape.end(); }); diff --git a/test/fixtures/weakCrypto/directCallExpression/md2.js b/test/probes/fixtures/weakCrypto/directCallExpression/md2.js similarity index 100% rename from test/fixtures/weakCrypto/directCallExpression/md2.js rename to test/probes/fixtures/weakCrypto/directCallExpression/md2.js diff --git a/test/fixtures/weakCrypto/directCallExpression/md4.js b/test/probes/fixtures/weakCrypto/directCallExpression/md4.js similarity index 100% rename from test/fixtures/weakCrypto/directCallExpression/md4.js rename to test/probes/fixtures/weakCrypto/directCallExpression/md4.js diff --git a/test/fixtures/weakCrypto/directCallExpression/md5.js b/test/probes/fixtures/weakCrypto/directCallExpression/md5.js similarity index 100% rename from test/fixtures/weakCrypto/directCallExpression/md5.js rename to test/probes/fixtures/weakCrypto/directCallExpression/md5.js diff --git a/test/fixtures/weakCrypto/directCallExpression/ripemd160.js b/test/probes/fixtures/weakCrypto/directCallExpression/ripemd160.js similarity index 100% rename from test/fixtures/weakCrypto/directCallExpression/ripemd160.js rename to test/probes/fixtures/weakCrypto/directCallExpression/ripemd160.js diff --git a/test/fixtures/weakCrypto/directCallExpression/sha1.js b/test/probes/fixtures/weakCrypto/directCallExpression/sha1.js similarity index 100% rename from test/fixtures/weakCrypto/directCallExpression/sha1.js rename to test/probes/fixtures/weakCrypto/directCallExpression/sha1.js diff --git a/test/fixtures/weakCrypto/memberExpression/md2.js b/test/probes/fixtures/weakCrypto/memberExpression/md2.js similarity index 100% rename from test/fixtures/weakCrypto/memberExpression/md2.js rename to test/probes/fixtures/weakCrypto/memberExpression/md2.js diff --git a/test/fixtures/weakCrypto/memberExpression/md4.js b/test/probes/fixtures/weakCrypto/memberExpression/md4.js similarity index 100% rename from test/fixtures/weakCrypto/memberExpression/md4.js rename to test/probes/fixtures/weakCrypto/memberExpression/md4.js diff --git a/test/fixtures/weakCrypto/memberExpression/md5.js b/test/probes/fixtures/weakCrypto/memberExpression/md5.js similarity index 100% rename from test/fixtures/weakCrypto/memberExpression/md5.js rename to test/probes/fixtures/weakCrypto/memberExpression/md5.js diff --git a/test/fixtures/weakCrypto/memberExpression/ripemd160.js b/test/probes/fixtures/weakCrypto/memberExpression/ripemd160.js similarity index 100% rename from test/fixtures/weakCrypto/memberExpression/ripemd160.js rename to test/probes/fixtures/weakCrypto/memberExpression/ripemd160.js diff --git a/test/fixtures/weakCrypto/memberExpression/sha1.js b/test/probes/fixtures/weakCrypto/memberExpression/sha1.js similarity index 100% rename from test/fixtures/weakCrypto/memberExpression/sha1.js rename to test/probes/fixtures/weakCrypto/memberExpression/sha1.js diff --git a/test/fixtures/weakCrypto/strongAlgorithms/sha256.js b/test/probes/fixtures/weakCrypto/strongAlgorithms/sha256.js similarity index 100% rename from test/fixtures/weakCrypto/strongAlgorithms/sha256.js rename to test/probes/fixtures/weakCrypto/strongAlgorithms/sha256.js diff --git a/test/probes/isUnsafeCallee.spec.js b/test/probes/isUnsafeCallee.spec.js index 1f9c73c9..9ac34538 100644 --- a/test/probes/isUnsafeCallee.spec.js +++ b/test/probes/isUnsafeCallee.spec.js @@ -1,6 +1,5 @@ // Require Internal Dependencies import { parseScript, getSastAnalysis, getWarningOnAnalysisResult } from "../utils/index.js"; -import { warnings } from "../../index.js"; import isUnsafeCallee from "../../src/probes/isUnsafeCallee.js"; // Require Third-party dependencies @@ -14,7 +13,7 @@ import { readFileSync } from "fs"; const __dirname = dirname(fileURLToPath(import.meta.url)); const FIXTURE_PATH = join(__dirname, "fixtures/unsafeCallee"); -const warningUnsafeStmt = warnings.unsafeStmt.code; +const warningUnsafeStmt = "unsafe-stmt"; test("should detect eval", (tape) => { const str = readFileSync(join(FIXTURE_PATH, "1-unsafeCallee.js"), "utf-8"); diff --git a/test/weakCrypto.spec.js b/test/probes/weakCrypto.spec.js similarity index 86% rename from test/weakCrypto.spec.js rename to test/probes/weakCrypto.spec.js index fe3c0743..e26918dc 100644 --- a/test/weakCrypto.spec.js +++ b/test/probes/weakCrypto.spec.js @@ -1,6 +1,6 @@ // Node.Js Dependencies import { readFileSync } from "fs"; -import { readdir } from 'fs/promises'; +import { readdir } from "fs/promises"; import { fileURLToPath } from "url"; import { join, dirname } from "path"; @@ -8,13 +8,13 @@ import { join, dirname } from "path"; import test from "tape"; // Internal Dependencies -import { runASTAnalysis, warnings } from "../index.js"; +import { runASTAnalysis } from "../../index.js"; // Constants const __dirname = dirname(fileURLToPath(import.meta.url)); const FIXTURE_PATH = join(__dirname, "fixtures", "weakCrypto"); -test("it should report a warning in case of `createHash()` usage", async (tape) => { +test("it should report a warning in case of `createHash()` usage", async(tape) => { const fixturesDir = join(FIXTURE_PATH, "directCallExpression"); const fixtureFiles = await readdir(fixturesDir); @@ -24,14 +24,14 @@ test("it should report a warning in case of `createHash()` usage", as const [firstWarning] = outputWarnings; tape.strictEqual(outputWarnings.length, 1); - tape.deepEqual(firstWarning.kind, warnings.weakCrypto.code); + tape.deepEqual(firstWarning.kind, "weak-crypto"); tape.strictEqual(firstWarning.value, fixtureFile.split(".").at(0)); tape.strictEqual(firstWarning.experimental, true); } tape.end(); }); -test("it should report a warning in case of `[expression]createHash()` usage", async (tape) => { +test("it should report a warning in case of `[expression]createHash()` usage", async(tape) => { const fixturesDir = join(FIXTURE_PATH, "memberExpression"); const fixtureFiles = await readdir(fixturesDir); @@ -41,7 +41,7 @@ test("it should report a warning in case of `[expression]createHash() const [firstWarning] = outputWarnings; tape.strictEqual(outputWarnings.length, 1); - tape.deepEqual(firstWarning.kind, warnings.weakCrypto.code); + tape.deepEqual(firstWarning.kind, "weak-crypto"); tape.strictEqual(firstWarning.value, fixtureFile.split(".").at(0)); tape.strictEqual(firstWarning.experimental, true); } diff --git a/test/searchRuntimeDependencies.spec.js b/test/searchRuntimeDependencies.spec.js index 903b1277..4b408bf7 100644 --- a/test/searchRuntimeDependencies.spec.js +++ b/test/searchRuntimeDependencies.spec.js @@ -7,19 +7,10 @@ import { join, dirname } from "path"; import test from "tape"; // Import Internal Dependencies -import { runASTAnalysis, warnings } from "../index.js"; +import { runASTAnalysis } from "../index.js"; import { getWarningKind } from "./utils/index.js"; // CONSTANTS -const { - unsafeRegex, - unsafeStmt, - unsafeAssign, - unsafeImport, - shortIdentifiers, - encodedLiteral, - suspiciousLiteral -} = warnings; const __dirname = dirname(fileURLToPath(import.meta.url)); const FIXTURE_PATH = join(__dirname, "fixtures/searchRuntimeDependencies"); @@ -63,7 +54,7 @@ test("should return unsafe-import when a CallExpression is used in a require sta require(evil() + "s"); `); - tape.deepEqual(getWarningKind(warnings), [unsafeImport.code, unsafeImport.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-import", "unsafe-import"].sort()); tape.strictEqual(isOneLineRequire, false); tape.deepEqual([...dependencies], []); tape.end(); @@ -75,7 +66,7 @@ test("should return the string value of the encoded hexadecimal literal", (tape) const foo = "68747470"; `); - tape.deepEqual(getWarningKind(warnings), [unsafeImport.code, encodedLiteral.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-import", "encoded-literal"].sort()); tape.deepEqual([...dependencies], ["http"]); tape.end(); }); @@ -85,7 +76,7 @@ test("should detect an unsafe import because of the usage of data:text/javascrip import 'data:text/javascript;base64,Y29uc29sZS5sb2coJ2hlbGxvIHdvcmxkJyk7Cg=='; `); - tape.deepEqual(getWarningKind(warnings), [unsafeImport.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-import"].sort()); tape.deepEqual([...dependencies], ["data:text/javascript;base64,Y29uc29sZS5sb2coJ2hlbGxvIHdvcmxkJyk7Cg=="]); tape.end(); }); @@ -97,7 +88,7 @@ test("should be capable to reverse the CallExpression Buffer.from call with an A ); `); - tape.deepEqual(getWarningKind(warnings), [unsafeImport.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-import"].sort()); tape.deepEqual([...dependencies], ["dl-tar"]); tape.end(); }); @@ -110,7 +101,7 @@ test("should reverse the encoded hexadecimal value even if we can't follow unhex const px = require.resolve(unhex("646c2d746172")); `); - tape.deepEqual(getWarningKind(warnings), [unsafeImport.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-import"].sort()); tape.deepEqual([...dependencies], ["dl-tar"]); tape.end(); }); @@ -122,7 +113,7 @@ test("should be capable to reverse the CallExpression Buffer.from with an hexade ); `); - tape.deepEqual(getWarningKind(warnings), [unsafeImport.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-import"].sort()); tape.deepEqual([...dependencies], ["dl-tar"]); tape.end(); }); @@ -133,7 +124,7 @@ test("should return an unsafe-assign warning when a protected global is assigned r("http"); `); - tape.deepEqual(getWarningKind(warnings), [unsafeAssign.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-assign"].sort()); tape.deepEqual([...dependencies], ["http"]); tape.end(); }); @@ -145,7 +136,7 @@ test("should succesfully follow the require stmt when assigned multiple times an b("http"); `); - tape.deepEqual(getWarningKind(warnings), [unsafeAssign.code, unsafeAssign.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-assign", "unsafe-assign"].sort()); tape.deepEqual([...dependencies], ["http"]); tape.end(); }); @@ -155,7 +146,7 @@ test("should return unsafe-import when trying to require an empty ArrayExpressio require(["", ""]); `); - tape.deepEqual(getWarningKind(warnings), [unsafeImport.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-import"].sort()); tape.deepEqual([...dependencies], []); tape.end(); }); @@ -166,7 +157,7 @@ test("should detect unsafe eval statments", (tape) => { const g = eval("this"); `); - tape.deepEqual(getWarningKind(warnings), [unsafeStmt.code, unsafeStmt.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-stmt", "unsafe-stmt"].sort()); tape.end(); }); @@ -176,7 +167,7 @@ test("should detect unsafe Function statments", (tape) => { const g = Function("return this")(); `); - tape.deepEqual(getWarningKind(warnings), [unsafeStmt.code, unsafeStmt.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-stmt", "unsafe-stmt"].sort()); tape.end(); }); @@ -185,7 +176,7 @@ test("should detect unsafe-assign of eval", (tape) => { const e = eval; `); - tape.deepEqual(getWarningKind(warnings), [unsafeAssign.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-assign"].sort()); tape.end(); }); @@ -199,7 +190,7 @@ test("should be capable of following global parts", (tape) => { `); tape.deepEqual(getWarningKind(warnings), [ - unsafeAssign.code, unsafeAssign.code, unsafeAssign.code + "unsafe-assign", "unsafe-assign", "unsafe-assign" ].sort()); tape.deepEqual([...dependencies], ["http", "fs"]); tape.end(); @@ -226,7 +217,7 @@ test("should detect the suspicious string", (tape) => { const suspectString = readFileSync(join(FIXTURE_PATH, "suspect-string.js"), "utf-8"); const { warnings, stringScore } = runASTAnalysis(suspectString); - tape.deepEqual(getWarningKind(warnings), [suspiciousLiteral.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["suspicious-literal"].sort()); tape.strictEqual(stringScore, 8); tape.end(); }); @@ -236,11 +227,11 @@ test("should be capable to follow hexa computation members expr", (tape) => { const { warnings, dependencies } = runASTAnalysis(advancedComputation); tape.deepEqual(getWarningKind(warnings), [ - encodedLiteral.code, - unsafeAssign.code, - unsafeAssign.code, - unsafeImport.code, - unsafeStmt.code + "encoded-literal", + "unsafe-assign", + "unsafe-assign", + "unsafe-import", + "unsafe-stmt" ].sort()); tape.deepEqual([...dependencies], ["./test/data"]); tape.end(); @@ -258,7 +249,7 @@ test("should support runtime analysis of ESM and return http", (tape) => { test("should detect two unsafe regex", (tape) => { const regexUnsafe = readFileSync(join(FIXTURE_PATH, "unsafe-regex.js"), "utf-8"); const { warnings } = runASTAnalysis(regexUnsafe, { module: false }); - tape.deepEqual(getWarningKind(warnings), [unsafeRegex.code, unsafeRegex.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["unsafe-regex", "unsafe-regex"].sort()); tape.end(); }); @@ -266,7 +257,7 @@ test("should detect short identifiers!", (tape) => { const shortIds = readFileSync(join(FIXTURE_PATH, "short-ids.js"), "utf-8"); const { warnings } = runASTAnalysis(shortIds); - tape.deepEqual(getWarningKind(warnings), [shortIdentifiers.code].sort()); + tape.deepEqual(getWarningKind(warnings), ["short-identifiers"].sort()); tape.end(); }); diff --git a/test/warnings.spec.js b/test/warnings.spec.js new file mode 100644 index 00000000..014131b7 --- /dev/null +++ b/test/warnings.spec.js @@ -0,0 +1,46 @@ +// Import Third-party Dependencies +import test from "tape"; + +// Require Internal Dependencies +import { rootLocation } from "../src/utils.js"; +import { generateWarning } from "../src/warnings.js"; + +test("Given an encoded-literal kind it should generate a warning with deep location array", (tape) => { + const result = generateWarning("encoded-literal", { + location: rootLocation() + }); + + tape.deepEqual(result, { + kind: "encoded-literal", + value: null, + location: [ + [[0, 0], [0, 0]] + ], + i18n: "sast_warnings.encoded_literal", + severity: "Information" + }); + + tape.end(); +}); + +test("Given a weak-crypto kind it should generate a warning with value, simple location and experimental flag", (tape) => { + const result = generateWarning("weak-crypto", { + value: "md5", + location: rootLocation(), + file: "hello.js" + }); + + tape.deepEqual(result, { + kind: "weak-crypto", + value: "md5", + file: "hello.js", + location: [ + [0, 0], [0, 0] + ], + i18n: "sast_warnings.weak_crypto", + severity: "Information", + experimental: true + }); + + tape.end(); +}); diff --git a/types/api.d.ts b/types/api.d.ts new file mode 100644 index 00000000..9df8c40f --- /dev/null +++ b/types/api.d.ts @@ -0,0 +1,44 @@ +import { ASTDeps } from "./astdeps"; +import { Warning } from "./warnings"; + +export { + runASTAnalysis, + runASTAnalysisOnFile, + + RuntimeOptions, + RuntimeFileOptions, + + Report, + ReportOnFile +} + +interface RuntimeOptions { + module?: boolean; + isMinified?: boolean; +} + +interface Report { + dependencies: ASTDeps; + warnings: Warning[]; + idsLengthAvg: number; + stringScore: number; + isOneLineRequire: boolean; +} + +interface RuntimeFileOptions { + packageName?: string; + module?: boolean; +} + +type ReportOnFile = { + ok: true, + warnings: Warning[]; + dependencies: ASTDeps; + isMinified: boolean; +} | { + ok: false, + warnings: Warning[]; +} + +declare function runASTAnalysis(str: string, options?: RuntimeOptions): Report; +declare function runASTAnalysisOnFile(pathToFile: string, options?: RuntimeFileOptions): Promise; diff --git a/types/astdeps.d.ts b/types/astdeps.d.ts new file mode 100644 index 00000000..51910d0f --- /dev/null +++ b/types/astdeps.d.ts @@ -0,0 +1,30 @@ +export { + ASTDeps, + SourceLocation +} + +interface SourceLocation { + start: { + line: number; + column: number; + }; + end: { + line: number; + column: number; + } +} + +declare class ASTDeps { + constructor(); + removeByName(name: string): void; + add(depName: string): void; + getDependenciesInTryStatement(): IterableIterator; + + public isInTryStmt: boolean; + public dependencies: Record; + public readonly size: number; +} diff --git a/types/warnings.d.ts b/types/warnings.d.ts new file mode 100644 index 00000000..5fe70948 --- /dev/null +++ b/types/warnings.d.ts @@ -0,0 +1,33 @@ + +export { + Warning, + WarningDefault, + WarningLocation, + WarningName, + WarningNameWithValue +} + +type WarningNameWithValue = "parsing-error" +| "encoded-literal" +| "unsafe-regex" +| "unsafe-stmt" +| "unsafe-assign" +| "short-identifiers" +| "suspicious-literal" +| "obfuscated-code" +| "weak-crypto"; +type WarningName = WarningNameWithValue | "unsafe-import"; + +type WarningLocation = [[number, number], [number, number]]; + +interface WarningDefault { + kind: WarningName; + file?: string; + value: string; + location: WarningLocation | WarningLocation[]; + i18n: string; + severity: "Information" | "Warning" | "Critical"; + experimental?: boolean; +} + +type Warning = T extends { kind: WarningNameWithValue } ? T : Omit;