Component: vulnfeeds/conversion/versions.go (NVD/CVE → OSV conversion), sink in vulnfeeds/git/repository.go
Summary
The GitLab-family host check in repo() uses a hostname prefix match instead of proper domain validation:
strings.HasPrefix(parsedURL.Hostname(), "gitlab.")
This appears at four call sites (approx. lines 213, 327, 356, 393). A related check for the generic git prefix has the same flaw:
var supportedHostPrefixes = []string{"git", "gitlab"}
...
if slices.Contains(supportedHosts, parsedURL.Hostname()) ||
slices.Contains(supportedHostPrefixes, strings.Split(parsedURL.Hostname(), ".")[0]) {
Neither validates the registrable domain. strings.HasPrefix(host, "gitlab.") matches gitlab.com, but also matches any attacker-controlled hostname starting with the literal string gitlab. — e.g. gitlab.attacker-controlled.example. The strings.Split(host, ".")[0] check has the identical flaw for git.attacker-controlled.example.
Impact
The value returned by repo() is passed to git.ValidRepo / git.NormalizeRepoTags (vulnfeeds/git/repository.go), which make a real outbound git-protocol connection via go-git (RemoteRepoRefsWithRetry). This path is reached from the live NVD CVE ingestion pipeline (vulnfeeds/cmd/converters/cve/nvd-cve-osv/main.go → nvd.FindRepos), which processes reference URLs sourced from public CVE records — fields populated by the originating CNA, with no per-record human review before the network call. This gives an attacker who can place a URL in one CVE reference field (a low bar, given routine PoC/advisory reference links) a way to trigger outbound requests from the ingestion worker to a host of their choosing, by registering any domain starting with gitlab. or git..
Proof of Concept
package main
import (
"fmt"
"github.com/google/osv/vulnfeeds/conversion"
)
func main() {
attackerURLs := []string{
"https://gitlab.attacker-controlled.example/x/y/commit/deadbeef",
"https://git.attacker-controlled.example/x/y",
}
for _, u := range attackerURLs {
repo, err := conversion.Repo(u)
if err != nil {
fmt.Printf("REJECTED: %q -> error: %v\n", u, err)
continue
}
fmt.Printf("ACCEPTED (BUG): %q -> trusted repo URL: %q\n", u, repo)
}
}
Both non-GitHub/GitLab/Bitbucket URLs are accepted as "supported" and returned as trusted-looking repo URLs, which are then used for real network fetches.
Suggested fix
Replace the prefix/first-label checks with exact or suffix-anchored domain validation:
func isGitLabHost(host string) bool {
host = strings.ToLower(host)
return host == "gitlab.com" || strings.HasSuffix(host, ".gitlab.com") ||
host == "gitlab.freedesktop.org" || strings.HasSuffix(host, ".gitlab.freedesktop.org")
// ... explicit list of every actually-supported GitLab instance
}
Similarly replace supportedHostPrefixes / strings.Split(hostname, ".")[0] with an explicit, exact-match list of intended git.* hosts — several are already enumerated individually elsewhere in the file (git.netfilter.org, git.ffmpeg.org, git.postgresql.org, git.kernel.org, etc.).
Reported to Google's OSS VRP; posting here per their suggestion, since the tier classification means it isn't monetarily rewarded but should still be fixed.
Component:
vulnfeeds/conversion/versions.go(NVD/CVE → OSV conversion), sink invulnfeeds/git/repository.goSummary
The GitLab-family host check in
repo()uses a hostname prefix match instead of proper domain validation:This appears at four call sites (approx. lines 213, 327, 356, 393). A related check for the generic
gitprefix has the same flaw:Neither validates the registrable domain.
strings.HasPrefix(host, "gitlab.")matchesgitlab.com, but also matches any attacker-controlled hostname starting with the literal stringgitlab.— e.g.gitlab.attacker-controlled.example. Thestrings.Split(host, ".")[0]check has the identical flaw forgit.attacker-controlled.example.Impact
The value returned by
repo()is passed togit.ValidRepo/git.NormalizeRepoTags(vulnfeeds/git/repository.go), which make a real outbound git-protocol connection via go-git (RemoteRepoRefsWithRetry). This path is reached from the live NVD CVE ingestion pipeline (vulnfeeds/cmd/converters/cve/nvd-cve-osv/main.go→nvd.FindRepos), which processes reference URLs sourced from public CVE records — fields populated by the originating CNA, with no per-record human review before the network call. This gives an attacker who can place a URL in one CVE reference field (a low bar, given routine PoC/advisory reference links) a way to trigger outbound requests from the ingestion worker to a host of their choosing, by registering any domain starting withgitlab.orgit..Proof of Concept
Both non-GitHub/GitLab/Bitbucket URLs are accepted as "supported" and returned as trusted-looking repo URLs, which are then used for real network fetches.
Suggested fix
Replace the prefix/first-label checks with exact or suffix-anchored domain validation:
Similarly replace
supportedHostPrefixes/strings.Split(hostname, ".")[0]with an explicit, exact-match list of intendedgit.*hosts — several are already enumerated individually elsewhere in the file (git.netfilter.org,git.ffmpeg.org,git.postgresql.org,git.kernel.org, etc.).Reported to Google's OSS VRP; posting here per their suggestion, since the tier classification means it isn't monetarily rewarded but should still be fixed.