UNPKG

@nodesecure/js-x-ray

Version:
267 lines (213 loc) 12 kB
<p align="center"> <img src="https://user-images.githubusercontent.com/4438263/213887379-c873eb89-8786-4b5c-8a59-dcca49e01cb8.jpg" alt="@nodesecure/js-x-ray"> </p> **JS-X-Ray** is a JavaScript & TypeScript [SAST](https://github.com/resources/articles/what-is-sast) for identifying malicious patterns, security vulnerabilities, and code anomalies. Think of it as ESLint, but dedicated to security analysis. Originally created for [NodeSecure CLI](https://github.com/NodeSecure/cli), JS-X-Ray has become an independent and serious option for supply chain protection. ## 🔎 How It Works JS-X-Ray parses JS or TS code into an **Abstract Syntax Tree (AST)** with no extensive usage of RegEx or Semgrep rules. This enables variable tracing, dynamic import resolution, and detection of sophisticated obfuscation that pattern-matching tools miss. The tradeoff is that JS-X-Ray is purely dedicated to the JavaScript/TypeScript ecosystem. ## 💡 Features - Retrieve required dependencies and files for Node.js - Track `require()`, `import`, and dynamic imports with full tracing capabilities - Detect untraceable and malicious import patterns - Scan entire projects with multi-file analysis capabilities - Extract infrastructure components (URLs, IPs, hostnames, emails) - Detect malicious code patterns - Obfuscated code with tool identification (freejsobfuscator, jsfuck, jjencode, obfuscator.io, morse, Trojan Source) - Data exfiltration and unauthorized system information collection - Suspicious files with excessive encoded literals - Identify vulnerable code patterns - Unsafe statements (`eval()`, `Function()` constructor) - ReDoS vulnerabilities in regular expressions - SQL injection vulnerabilities - Unsafe shell commands in `spawn()` or `exec()` calls - `process.env` serialization attempts - Flag weak cryptographic usage - Deprecated algorithms (MD5, SHA1, MD4, MD2, RIPEMD160) - Weak scrypt parameters (insufficient cost, short or hardcoded salt) - Weak bcrypt parameters (low work factor, hardcoded salt) - Detect code quality issues - Monkey-patching of built-in prototypes - Encoded literals (hex, Unicode, base64) - Suspicious URLs and links - Short identifier lengths (obfuscation indicators) - Synchronous I/O and logging usage (optional) - Configurable sensitivity modes (conservative/aggressive) and extensible probe system - Support both JavaScript and TypeScript ## 💃 Getting Started This package is available in the Node package repository and can be easily installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or [yarn](https://yarnpkg.com). ```bash $ npm i @nodesecure/js-x-ray # or $ yarn add @nodesecure/js-x-ray ``` ## 👀 Usage example Create a local `.js` file with the following content: ```js try { require("http"); } catch (err) { // do nothing } const lib = "crypto"; require(lib); require("util"); require(Buffer.from("6673", "hex").toString()); ``` --- Then use `js-x-ray` to run an analysis of the JavaScript code: ```js import { AstAnalyser } from "@nodesecure/js-x-ray"; import { readFileSync } from "node:fs"; const scanner = new AstAnalyser(); const { warnings, dependencies } = await scanner.analyseFile( "./file.js" ); console.log(dependencies); console.dir(warnings, { depth: null }); ``` The analysis will return: `http` (in try), `crypto`, `util` and `fs`. > [!TIP] > There are also a lot of suspicious code examples in the `./workspaces/js-x-ray/examples` directory. Feel free to try the tool on these files. ### Scanning a complete project By itself, JS-X-Ray does not provide utilities to walk and scan a complete project. However, NodeSecure has packages to achieve that: ```ts import { ManifestManager } from "@nodesecure/mama"; import { NpmTarball } from "@nodesecure/tarball"; const mama = await ManifestManager.fromPackageJSON( "./path/to/package.json" ); const extractor = new NpmTarball(mama); const { composition, // Project composition (files, dependencies, extensions) conformance, // License conformance (SPDX) code // JS-X-Ray analysis results } = await extractor.scanFiles(); console.log(code); ``` The `NpmTarball` class uses JS-X-Ray under the hood, and `ManifestManager` locates entry (input) files for analysis. Alternatively, you can use `EntryFilesAnalyser` directly for multi-file analysis. See the [EntryFilesAnalyser API documentation](./docs/EntryFilesAnalyser.md) for more details. ## 📚 API - [AstAnalyser](./docs/AstAnalyser.md) - [EntryFilesAnalyser](./docs/EntryFilesAnalyser.md) - [CollectableSet](./docs/CollectableSet.md) - [SourceParser](./docs/SourceParser.md) - [VariableTracer](./docs/VariableTracer.md) ## Warnings ```ts type OptionalWarningName = | "synchronous-io" | "log-usage" | "crypto.weak-scrypt" | "crypto.unsafe-prehash" | "crypto.weak-bcrypt" | "crypto.password-shucking"; type WarningName = | "parsing-error" | "encoded-literal" | "unsafe-regex" | "unsafe-stmt" | "short-identifiers" | "suspicious-literal" | "suspicious-file" | "obfuscated-code" | "crypto.weak-algorithm" | "shady-link" | "unsafe-command" | "unsafe-import" | "serialize-environment" | "data-exfiltration" | "sql-injection" | "monkey-patch" | "prototype-pollution" | "unsafe-vm-context" | OptionalWarningName; interface Warning<T = WarningName> { kind: T | (string & {}); file?: string; value: string | null; source: string; location: null | SourceArrayLocation | SourceArrayLocation[]; i18n: string; severity: "Information" | "Warning" | "Critical"; experimental?: boolean; } declare const warnings: Record<WarningName, { i18n: string; severity: "Information" | "Warning" | "Critical"; experimental: boolean; }>; ``` ### Optional Warnings Some warnings are not included by default and must be explicitly requested through the `AstAnalyser` API. ```js import { AstAnalyser } from "@nodesecure/js-x-ray"; // Enable all optional warnings const scanner = new AstAnalyser({ optionalWarnings: true }); // Or enable specific optional warnings const scannerSpecific = new AstAnalyser({ optionalWarnings: ["synchronous-io", "log-usage", "crypto.weak-scrypt"] }); // Enable all crypto warnings at once using a glob pattern const scannerCrypto = new AstAnalyser({ optionalWarnings: ["crypto.*"] }); ``` The following warnings are optional: - `synchronous-io` - Detects synchronous I/O operations that could impact performance - `log-usage` - Tracks usage of logging functions (console.log, logger.info, etc.) - `crypto.weak-scrypt` - Detects weak scrypt parameters (low cost, short or hardcoded salt) - `crypto.unsafe-prehash` - Detects password pre-hashing fed into bcrypt without a safe (base64/hex) digest encoding - `crypto.weak-bcrypt` - Detects weak bcrypt parameters (low work factor, hardcoded salt) - `crypto.password-shucking` - Detects password pre-hashing with a plain hash function fed into bcrypt ### Internationalization (i18n) Warnings support internationalization through the `@nodesecure/i18n` package. Each warning has an i18n key that can be used to retrieve localized descriptions. ```js import * as jsxray from "@nodesecure/js-x-ray"; import * as i18n from "@nodesecure/i18n"; await i18n.extendFromSystemPath(jsxray.i18nLocation()); const message = i18n.getTokenSync( jsxray.warnings["parsing-error"].i18n ); console.log(message); ``` ### Warning Catalog Click on the warning **name** for detailed documentation and examples. #### Critical Severity | Name | Experimental | Description | | --- | :-: | --- | | [suspicious-file](https://github.com/NodeSecure/js-x-ray/blob/master/docs/suspicious-file.md) | No | Suspicious file containing more than ten encoded literals | | [obfuscated-code](https://github.com/NodeSecure/js-x-ray/blob/master/docs/obfuscated-code.md) | **Yes** | High probability of code obfuscation detected | #### Warning Severity | Name | Experimental | Description | | --- | :-: | --- | | [unsafe-import](https://github.com/NodeSecure/js-x-ray/blob/master/docs/unsafe-import.md) | No | Unable to follow an import (`require`, `require.resolve`) statement | | [unsafe-regex](https://github.com/NodeSecure/js-x-ray/blob/master/docs/unsafe-regex.md) | No | Unsafe regular expression that may be vulnerable to ReDoS attacks | | [unsafe-stmt](https://github.com/NodeSecure/js-x-ray/blob/master/docs/unsafe-stmt.md) | No | Usage of dangerous statements like `eval()` or `Function("")` | | [unsafe-command](https://github.com/NodeSecure/js-x-ray/blob/master/docs/unsafe-command.md) | **Yes** | Suspicious commands detected in `spawn()` or `exec()` | | [short-identifiers](https://github.com/NodeSecure/js-x-ray/blob/master/docs/short-identifiers.md) | No | Average identifier length below 1.5 characters (possible obfuscation) | | [suspicious-literal](https://github.com/NodeSecure/js-x-ray/blob/master/docs/suspicious-literal.md) | No | Suspicious literal values detected in source code | | [crypto.weak-algorithm](https://github.com/NodeSecure/js-x-ray/blob/master/docs/crypto.weak-algorithm.md) | No | Usage of weak cryptographic algorithms (MD5, SHA1, etc.) | | [shady-link](https://github.com/NodeSecure/js-x-ray/blob/master/docs/shady-link.md) | No | Suspicious or potentially malicious URLs detected | | [synchronous-io](https://github.com/NodeSecure/js-x-ray/blob/master/docs/synchronous-io.md) ⚠️ | **Yes** | Synchronous I/O operations that may impact performance | | [serialize-environment](https://github.com/NodeSecure/js-x-ray/blob/master/docs/serialize-environment.md) | No | Attempts to serialize `process.env` (potential data exfiltration) | | [data-exfiltration](https://github.com/NodeSecure/js-x-ray/blob/master/docs/data-exfiltration.md) | No | Potential unauthorized transfer of sensitive data | | [sql-injection](https://github.com/NodeSecure/js-x-ray/blob/master/docs/sql-injection.md) | No | Potential SQL injection vulnerability detected | | [monkey-patch](https://github.com/NodeSecure/js-x-ray/blob/master/docs/monkey-patch.md) | No | Modification of built-in JavaScript prototype properties | | [prototype-pollution](https://github.com/NodeSecure/js-x-ray/blob/master/docs/prototype-pollution.md) | No | Detected use of `__proto__` to pollute object prototypes | | [crypto.weak-scrypt](https://github.com/NodeSecure/js-x-ray/blob/master/docs/crypto.weak-scrypt.md) ⚠️ | **Yes** | Usage of weak scrypt parameters (low cost, short or hardcoded salt) | | [crypto.unsafe-prehash](https://github.com/NodeSecure/js-x-ray/blob/master/docs/crypto.unsafe-prehash.md) ⚠️ | **Yes** | Password pre-hashed and fed into bcrypt without a safe digest encoding | | [crypto.weak-bcrypt](https://github.com/NodeSecure/js-x-ray/blob/master/docs/crypto.weak-bcrypt.md) ⚠️ | **Yes** | Usage of weak bcrypt parameters (low work factor or hardcoded salt) | | [crypto.password-shucking](https://github.com/NodeSecure/js-x-ray/blob/master/docs/crypto.password-shucking.md) ⚠️ | **Yes** | Password pre-hashed with a plain hash function | | [unsafe-vm-context](https://github.com/NodeSecure/js-x-ray/blob/master/docs/unsafe-vm-context.md) ⚠️ | No | Usage of dangerous vm.runInNewContext and (vm.Script(code,options)).runInContext | #### Information Severity | Name | Experimental | Description | | --- | :-: | --- | | [parsing-error](https://github.com/NodeSecure/js-x-ray/blob/master/docs/parsing-error.md) | No | AST parser encountered an error while analyzing the code | | [encoded-literal](https://github.com/NodeSecure/js-x-ray/blob/master/docs/encoded-literal.md) | No | Encoded literal detected (hexadecimal, Unicode, base64) | | [log-usage](https://github.com/NodeSecure/js-x-ray/blob/master/docs/log-usage.md) ⚠️ | No | Usage of logging functions (console.log, logger methods, etc.) | > [!NOTE] > Warnings marked with ⚠️ are optional and must be explicitly enabled (see [Optional Warnings](#optional-warnings) section). ## License MIT