Mar 5, 2026·5 min read·6 visits
Gogs failed to properly sanitize Git tag names during release deletion, allowing attackers to inject command-line flags into the `git tag -d` execution. This can cause service crashes or information leaks. Fixed in version 0.14.2.
A high-severity command option injection vulnerability exists in the Gogs self-hosted Git service prior to version 0.14.2. The flaw resides in the `DeleteReleaseOfRepoByID` function, where user-supplied Git tag names are passed directly to a system shell command without adequate sanitization or argument separation. This allows an attacker to inject arbitrary flags into the underlying `git` binary execution, potentially leading to Denial of Service (DoS) or unauthorized information disclosure.
CVE-2026-26194 is a Command Option Injection vulnerability (CWE-88) affecting the Gogs self-hosted Git service. The vulnerability manifests in the release management workflow, specifically when a repository release is deleted. Gogs interacts with the underlying operating system by invoking the git binary to manage repository state, including the deletion of Git tags associated with releases.
In affected versions, the system constructs a command line to delete a tag using the format git tag -d <tag_name>. Crucially, the application fails to distinguish between the command options (flags) and the positional arguments (the tag name). If a tag name begins with a hyphen (e.g., - or --), the Git binary interprets it as a configuration flag rather than a reference name. This behavior allows an attacker to alter the execution flow of the Git process by supplying a crafted tag name, even though they cannot execute arbitrary system commands (as in standard Command Injection).
The root cause of this vulnerability lies in the improper construction of the system command within the DeleteReleaseOfRepoByID function in internal/database/release.go. The application uses a wrapper function, process.ExecDir, to spawn the Git process but passes the user-controlled rel.TagName directly as an argument without the necessary end-of-options separator (--).
When a program parses command-line arguments, it typically treats strings starting with - as options. The Git CLI adheres to the POSIX standard, which defines -- as a delimiter indicating the end of options. Any argument following this delimiter is treated strictly as a positional argument. By omitting this delimiter, Gogs inadvertently allowed the content of the tag name to be parsed as flags.
For example, if an attacker creates a tag named --help, the constructed command becomes git tag -d --help. Instead of deleting a tag named --help, Git executes the help command and exits, failing the original operation and potentially exposing unexpected output or blocking execution.
The vulnerability exists in the database layer where release deletion logic is handled. Below is the comparison between the vulnerable implementation and the secure fix applied in version 0.14.2.
In the pre-patch code, the command is built manually using string arguments. The rel.TagName is appended directly after the -d flag.
// internal/database/release.go (Pre-patch)
// The tag name is passed directly to the ExecDir function
_, stderr, err := process.ExecDir(-1, repo.RepoPath(),
fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID),
"git", "tag", "-d", rel.TagName) // <--- VULNERABLE: No separation
if err != nil && !strings.Contains(stderr, "not found") {
return errors.Newf("git tag -d: %v - %s", err, stderr)
}The fix abandons the manual process execution in favor of a higher-level abstraction provided by the git-module library. This library handles the safe construction of Git commands, ensuring arguments are treated correctly as data rather than directives.
// internal/database/release.go (Patched)
// Use the safer git abstraction layer
gitRepo, err := git.Open(repo.RepoPath())
if err != nil {
return errors.Newf("open repository: %v", err)
}
// DeleteTag handles the plumbing safely, likely using '--' internally
err = gitRepo.DeleteTag(rel.TagName)
if err != nil && !strings.Contains(err.Error(), "not found") {
return errors.Newf("delete tag: %v", err)
}An attacker can exploit this vulnerability by pushing a malicious tag to a repository they have access to. While the Gogs web interface may have input validation for tag creation, the Git protocol itself allows pushing arbitrary reference names.
git tag command. For example:
git tag "--paginate"
git push origin "--paginate"DeleteReleaseOfRepoByID.git tag -d --paginate. Depending on the injected flag, the process might hang (DoS), output excessive data, or fail in a way that reveals system paths in error logs.While this does not provide direct Remote Code Execution (RCE) in the sense of executing /bin/sh, it allows manipulation of the Git binary's behavior, leading to high-impact availability issues.
The impact of CVE-2026-26194 is primarily focused on system availability, with a secondary risk to confidentiality. The vulnerability has been assigned a CVSS v4.0 score of 8.8 (High).
The vector CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:H highlights that this attack is network-exploitable without authentication (if public registration is enabled) or privileges, and requires no user interaction.
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
Gogs Gogs | < 0.14.2 | 0.14.2 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-88 |
| Vulnerability Type | Command Option Injection |
| CVSS v4.0 | 8.8 (High) |
| Attack Vector | Network |
| Exploit Status | None / PoC |
| Remediation | Upgrade to v0.14.2 |
The product constructs a string for a command to be executed by a separate component, but it does not properly separate the arguments, allowing the injection of options.