Summary
Togoder Security scanned the npm package semantic-release@25.0.9 on Oct 6, 2026. An AI review of 27 source files produced 9 medium, 11 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 20
Runtime code execution via plugin loading
NPS-BECFD8FF2C33
The application is designed to load and execute plugins based on the 'plugins', 'extends', and other plugin-related options parsed from the command line. These options are coerced from user input and passed to the main module. This constitutes a dynamic module loading mechanism where user-controlled input determines what code gets executed. If an attacker can control the CLI arguments or configuration, they may achieve arbitrary code execution.
Dynamic import based on configuration
NPS-76667BCCEF20
The CLI dynamically imports './index.js' at runtime after parsing user-controlled options from process.argv. While the module path is static, the options parsed from CLI arguments are passed directly into the imported module. In the context of semantic-release, these options include plugin names and shareable configurations which are resolved and executed as code. An attacker who can influence the CLI arguments (e.g., via CI/CD configuration or a malicious .releaserc file) could cause arbitrary plugin code to be loaded and executed.
Dynamic module loading from external input
NPS-C1CB37E99ED9
The extends config option allows loading arbitrary modules via importFrom.silent(__dirname, extendPath) and importFrom(cwd, extendPath), where extendPath comes directly from the configuration. This enables loading and executing code from any resolvable module path, which could be exploited if an attacker can control the config (e.g., a malicious shared config). This is a legitimate feature of semantic-release, but it represents a code execution vector.
Environment variable and credential harvesting
NPS-20F3FD813224
The code reads multiple sensitive environment variables (GH_TOKEN, GITHUB_TOKEN, GL_TOKEN, GITLAB_TOKEN, BB_TOKEN, BITBUCKET_TOKEN, GIT_CREDENTIALS) to extract git authentication credentials. While this is the intended functionality for semantic-release to authenticate with git providers, it demonstrates credential harvesting behavior that could be abused if the package were compromised or if the collected credentials were sent elsewhere.
Command injection risk
NPS-96EDF25CA61D
getCommits uses gitLogParser.parse with a format string constructed from user-supplied from and to values: { _: ${from ? from + '..' : ''}${to} }. These values are passed to git-log-parser, which may construct a git log command. If the parser does not safely shell-escape these values, an attacker controlling from or to (e.g., via branch/tag names in a CI context) could inject git options or shell metacharacters.
Dynamic module loading from external input
NPS-451FE1491FF2
The code calls loadPlugin(context, name, pluginsPath) where 'name' and 'pluginsPath' are derived from plugin configuration. This function likely dynamically imports or requires modules based on user-supplied or configuration-supplied names, which could be abused to load arbitrary code if the plugin name is not properly validated. While this is typical for a plugin system (semantic-release), it represents a dynamic import risk.
Code execution via plugin function invocation
NPS-6937C56AD7CE
The plugin function (or method) retrieved from the loaded module is invoked with configuration and context data. This is expected behavior for a plugin framework but means arbitrary code from third-party plugins will execute. If a malicious plugin is installed, it would run with access to environment, file system, and network.
Plugin loading mechanism
NPS-5C076302324C
The code loads external plugins from the filesystem based on names provided in configuration. While this is expected behavior for a plugin system, it represents a code execution vector if the configuration is not properly validated or if an attacker can influence plugin paths.
Dynamic import with computed path
NPS-2009962D3AAB
The loadPlugin function uses dynamic import() with a computed file path derived from user-controlled plugin names and configuration. This could potentially load and execute arbitrary modules if an attacker can control the plugin name or path, though resolve-from is used to resolve paths relative to known directories.
dynamic module loading
NPS-83A904B1DE67
Uses createRequire to load package.json for engine constraints. This is standard practice for ESM modules needing to read package metadata.
process execution
NPS-A8F98F710643
This is the CLI entry point for semantic-release. It checks Node.js and Git versions before invoking the main CLI. The execa('git', ['--version']) call is a legitimate version check, not a security concern.
Debug information exposure
NPS-12F760EBEC7F
When the 'debug' flag is set, debug logging is enabled for the 'semantic-release:*' namespace. Debug logs may contain sensitive information such as tokens or credentials used during the release process. If debug output is captured in logs (common in CI environments), this could lead to credential leakage.
Error output potentially containing sensitive data
NPS-348A390496F6
On error, the code writes the inspected error object to stderr. It uses a 'hideSensitive' utility to redact sensitive environment variables, which is a good practice. However, if the redaction is incomplete or if sensitive data exists outside of environment variables (e.g., passed as CLI arguments), it could still be exposed in error output.
Environment variable access
NPS-1EEEB922A411
The repoUrl({ cwd, env }) call passes the environment object into the git URL resolution logic. While this file itself does not read secrets directly, environment access is delegated to ./git.js. This is normal for CI tools but warrants review of the imported module.
Potential credential/secret exposure in debug output
NPS-BE16A7779AF3
debug("options values: %O", options) logs the full resolved options object, which may include tokens, repository credentials, or environment-derived secrets passed via CLI options or config. Debug output could leak sensitive values if logs are captured.
Sensitive credential handling in URLs
NPS-DCC7C6F4446E
Function formatAuthUrl embeds raw credentials into URLs (auth: gitCredentials) and these URLs are then passed to git.verifyAuth. If these URLs were logged, leaked, or exfiltrated, credentials could be exposed. Debug logging is present but appears to log URLs in some cases.
Spawning processes or shell commands
NPS-A6F60E09211D
The code calls verifyAuth (imported from ./git.js) which invokes git operations. Although the actual command execution is in the imported module, this file orchestrates spawning git processes to verify authentication URLs, passing user-controlled credentials as part of the URL.
Spawning external processes
NPS-7A392B01E66F
The module extensively spawns the git binary via execa with arguments derived from parameters such as repositoryUrl, branch, tagName, ref, and note. Several of these are used with -- separators or passed as distinct argv entries (mitigating shell injection), but ref/branch/tag names flow into git subcommands (rev-list, tag, check-ref-format, show-ref, ls-remote). If callers do not validate these inputs, malicious values could influence git behavior (e.g., option injection via leading dashes).
JSON parsing of git note content
NPS-5A53B2D6DB13
getTagsNotes executes git log with --notes=refs/notes/semantic-release* and JSON.parse()s the resulting note payloads. Git notes are repository-controlled data; a malicious commit/note could contain a crafted JSON payload. JSON.parse itself is safe from code execution, but the parsed object is later used (e.g., channels) and could influence release behavior.
Environment variable access
NPS-46DAE188FA0A
getCommits reads process.env and merges it with execaOptions.env when invoking git-log-parser. This propagates all environment variables (including credentials like GITHUB_TOKEN, NPM_TOKEN) into the child git process. While this is common for git operations, it expands the exposure surface if any downstream logging or error path leaks the env.
Files reviewed
| File | Verdict | What the reviewer saw |
|---|---|---|
| cli.js | medium | The CLI is part of semantic-release and dynamically loads plugins and configurations based on user-controlled input, which is an inherent risk of arbitrary code execution if input is not trusted, though no direct malicious intent or obfuscation is present. |
| lib/get-config.js | medium | This appears to be legitimate semantic-release configuration loading code, but it dynamically imports user-specified 'extends' modules (code execution vector) and logs full options which may contain credentials. |
| lib/get-git-auth-url.js | medium | This is the legitimate semantic-release get-git-auth-url module; it harvests git tokens from environment variables to build authenticated repository URLs, which is expected behavior but involves sensitive credential handling and process spawning. |
| lib/git.js | medium | This is legitimate semantic-release git utility code that shells out to git; the main concerns are potential command/option injection through unvalidated refs and propagation of process.env to child git processes, but no data exfiltration, credential harvesting, obfuscation, or backdoor patterns were found. |
| lib/plugins/normalize.js | medium | No direct malicious patterns (exfiltration, credential theft, obfuscation, backdoors) were found, but the code implements a dynamic plugin loader that executes third-party code, which is a potential security risk if plugin sources are not trusted. |
| lib/plugins/utils.js | medium | The code implements a plugin loading system with dynamic imports and filesystem resolution; while no obvious malicious patterns are present, the dynamic module loading from computed paths warrants caution. |
| bin/semantic-release.js | safe | The code is a standard CLI entry point for semantic-release that performs version checks and delegates to the main CLI; no malicious patterns were found. |
| index.js | safe | This is the legitimate entry point for the semantic-release package, with no malicious patterns such as data exfiltration, credential harvesting, obfuscation, or unauthorized process execution detected. |
| lib/branches/expand.js | safe | No malicious patterns detected |
| lib/branches/get-tags.js | safe | No malicious patterns detected |
| lib/branches/index.js | safe | No malicious patterns detected; the code is a standard branch validation module using expected dependencies and no suspicious operations. |
| lib/branches/normalize.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/definitions/branches.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/definitions/constants.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/definitions/errors.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/definitions/plugins.js | safe | No malicious patterns detected; the code is a legitimate plugin configuration module for semantic-release and exhibits no data exfiltration, credential harvesting, obfuscation, process spawning, or other suspicious behavior. |
| lib/get-commits.js | safe | No malicious patterns detected; the file simply retrieves git commits using the debug logger and a local getCommits helper. |
| lib/get-error.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/get-last-release.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/get-logger.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/get-next-version.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/get-release-to-add.js | safe | Cleared by Jev triage; no further analysis needed |
| lib/hide-sensitive.js | safe | No malicious patterns detected; the code only redacts sensitive environment variable values from log output. |
| lib/plugins/index.js | safe | No malicious patterns detected in the plugin loading and configuration logic. |
| lib/plugins/pipeline.js | safe | Cleared by Jev triage; no further analysis needed |
Show 2 more files
| File | Verdict | What the reviewer saw |
|---|---|---|
| lib/utils.js | safe | No malicious patterns detected |
| lib/verify.js | safe | No malicious patterns detected |
Frequently asked questions
Is semantic-release safe to use?
No confirmed malware was found in semantic-release@25.0.9, but the review flagged 9 medium, 11 low severity findings for risky patterns worth checking before you rely on it.
Does semantic-release contain malware?
No malware was identified in semantic-release@25.0.9 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 semantic-release checked?
Togoder Security downloaded the published npm package and had an AI model read its 27 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 semantic-release 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 semantic-release@25.0.9, cost nothing.