Summary
Togoder Security scanned the npm package tsconfig-paths@3.15.0 on Oct 6, 2026. An AI review of 21 source files produced 7 medium, 5 low severity findings. The overall verdict is medium: the findings flag risky but common patterns (dynamic code, unsafe defaults, broad file or network access) rather than confirmed malware.
Findings 12
Dynamic require with user-controlled path
NPS-2282D4DC7432
readJsonFromDiskSync uses require(packageJsonPath) where the path is provided as a function argument. If an attacker can control this path, it could load arbitrary JavaScript files (not just JSON), potentially leading to code execution. The function name implies JSON reading but require() can execute any .js/.json file.
Opaque import and side effect at import time
NPS-38D162BBCF77
This file immediately requires the package root ('.') and calls .register() on whatever that module exports. The actual behavior is hidden in the package root's entry point (typically index.js), so this file itself performs no direct I/O but triggers arbitrary code execution on import. Because .register() is invoked unconditionally at module load, all side effects execute whenever this file is imported, without caller control. Without inspecting the referenced module, the true network, filesystem, or process behavior cannot be ruled out; the pattern is characteristic of packages that execute registration hooks at import time.
Potential path traversal / arbitrary file read
NPS-D55D38E99EC1
readJsonFromDiskSync and readJsonFromDiskAsync accept fully caller-supplied paths and read them from disk. No validation is performed to ensure the path stays within the package or an expected directory, enabling reading of arbitrary files if callers pass untrusted input. readJsonFromDiskSync even loads them as code via require().
Dynamic code loading
NPS-8058DF87ECF8
readJsonFromDiskSync uses require(packageJsonPath) with a caller-controlled path for dynamic module loading. require() executes the target file as JavaScript. If an attacker can control the path (e.g. to a malicious .js file), this leads to arbitrary code execution. A safer pattern would be fs.readFileSync + JSON.parse.
File system read via dynamic input
NPS-A555C9695677
The findFirstExistingPath function calls readJson(tryPath.path) where tryPath.path is constructed based on the requested module and base URL. If the requestedModule is user-controlled and not properly sanitized, an attacker could potentially cause the function to read arbitrary package.json files on the file system by crafting path traversal sequences. However, the path construction logic is delegated to TryPath.getPathsToTry, and this file does not itself sanitize input. This is a potential path traversal risk if consumers pass untrusted input.
File system reads outside expected scope
NPS-C5B83BEBF547
loadTsconfig recursively follows extends chains and reads arbitrary files via fs.readFileSync. Combined with absolute paths or .. traversal in extends, this can access files outside the project directory. No validation confines reads to the package or project scope.
Path traversal via extends field
NPS-6345A7734704
The loadTsconfigFromExtends function constructs file paths by joining currentDir with the user-controlled extendedConfigValue from the tsconfig extends field. This allows reading arbitrary JSON/JSON5 files on the filesystem (e.g., extends: '../../../../etc/secret'). While this is inherent to tsconfig extends behavior and not overtly malicious, it can be abused to read sensitive files if an attacker can control the tsconfig content.
Unsafe JSON parsing without error handling
NPS-A5BE1460E185
readJsonFromDiskAsync uses JSON.parse(result) without a try/catch block. If the file contains invalid JSON, this will throw an uncaught exception in the callback, potentially crashing the process. While not directly malicious, this is a robustness concern.
Dynamic module resolution using require.extensions
NPS-9498306DD2EA
The code uses Object.keys(require.extensions) as a default value for the extensions parameter in matchFromAbsolutePaths. This accesses all registered file extensions from Node.js's internal require.extensions object. While this is a common pattern for resolving paths in TypeScript path mapping (used by tsconfig-paths), accessing require.extensions exposes the module system's internal registry and could theoretically be abused if the function is invoked in a malicious context. However, in this specific file, the extensions are only used to generate candidate file paths, not to dynamically load or execute modules.
File existence probing based on dynamic input
NPS-8108A761C83D
The function checks for the existence of files at paths derived from the requested module name (fileExists(tryPath.path)). This could be used as an oracle for file system enumeration if an attacker can observe the return values (e.g., via error messages or timing). Again, the risk depends on how consumers use this function and whether requestedModule is attacker-controlled.
Environment variable sourcing from external input
NPS-70858C29D7AC
tsConfigLoader accepts a getEnv callback and reads TS_NODE_PROJECT and TS_NODE_BASEURL. These values flow into loadSync and influence file resolution. This is by design (ts-node configuration) but means the module inherits trust from the environment-provided getEnv.
TOCTOU race condition on file type check
NPS-4F11F693B3FE
resolveConfigPath uses fs.lstatSync(filename).isDirectory() followed by path.resolve, and fs.statSync(cwd).isFile() before reading. These check-then-use patterns are susceptible to time-of-check/time-of-use races, though impact is limited in this context.
Files reviewed
| File | Verdict | What the reviewer saw |
|---|---|---|
| lib/filesystem.js | medium | Utility filesystem module with no obvious malicious patterns, but uses dynamic require() on a caller-supplied path which could enable arbitrary code loading if the path is attacker-controlled. |
| register.js | medium | This stub unconditionally imports the package root and invokes a hidden register() hook at load time, delegating all potentially malicious behavior to unreviewed code outside this file. |
| src/filesystem.ts | medium | No outright exfiltration or backdoors, but readJsonFromDiskSync loads an arbitrary, unvalidated file path via require(), which can lead to arbitrary code execution or arbitrary file read if the path is attacker-influenced. |
| src/match-path-sync.ts | medium | This appears to be the legitimate tsconfig-paths library code for resolving TypeScript path mappings; no overtly malicious patterns like data exfiltration, credential harvesting, or shell execution were found, though the dynamic file system probing based on input may pose a path traversal risk if used with untrusted input. |
| src/tsconfig-loader.ts | medium | No overt malicious patterns, but the module performs recursive, user-influenced filesystem reads via tsconfig extends resolution without scope confinement, which could enable sensitive file disclosure in hostile configurations. |
| lib/config-loader.js | safe | No malicious patterns detected; the code is a straightforward configuration loader that reads tsconfig.json and environment variables without any exfiltration, code execution, or suspicious behavior. |
| lib/index.js | safe | No malicious patterns detected |
| lib/mapping-entry.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/match-path-async.js | safe | No malicious patterns detected; the code is a legitimate TypeScript path-matching utility with no network, credential, or process-manipulation activity. |
| lib/match-path-sync.js | safe | No malicious patterns detected; the code is a legitimate TypeScript path-mapping resolver with no network, credential, process, or obfuscation behavior. |
| lib/options.js | safe | No malicious patterns detected; the file only parses CLI arguments for a project directory path and sets a working directory option. |
| lib/register.js | safe | The code patches Node's module resolution to support TypeScript path mapping (tsconfig-paths), a standard and legitimate operation with no malicious patterns detected. |
| lib/try-path.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/tsconfig-loader.js | safe | No malicious patterns detected |
| src/config-loader.ts | safe | No malicious patterns detected |
| src/index.ts | safe | Cleared by Jev triage; no further analysis needed |
| src/mapping-entry.ts | safe | Cleared by Jev triage; no further analysis needed |
| src/match-path-async.ts | safe | No malicious patterns detected |
| src/options.ts | safe | No malicious patterns detected |
| src/register.ts | safe | No malicious patterns detected |
| src/try-path.ts | safe | Cleared by Jev triage; no further analysis needed |
Frequently asked questions
Is tsconfig-paths safe to use?
No confirmed malware was found in tsconfig-paths@3.15.0, but the review flagged 7 medium, 5 low severity findings for risky patterns worth checking before you rely on it.
Does tsconfig-paths contain malware?
No malware was identified in tsconfig-paths@3.15.0 when Togoder Security scanned it on Oct 6, 2026. A new version can still introduce malicious code, so scan the exact versions in your lockfile.
How was tsconfig-paths checked?
Togoder Security downloaded the published npm package and had an AI model read its 21 source files, looking for install scripts, credential access, network exfiltration, obfuscation, backdoors and crypto-wallet theft. The results are cached by file hash and shown here.
How do I scan tsconfig-paths together with the rest of my dependencies?
Upload your lockfile at https://security.togoder.click/scan or call the API documented at https://security.togoder.click/api-docs. Files that have already been scanned, like the ones in tsconfig-paths@3.15.0, cost nothing.