Skip to content

bug(terraform): GIT_TERMINAL_PROMPT save/restore race in remote module resolver #10833

Open

Description

@nikpivkin

Summary

remoteResolver.download() saves, sets and restores the process-global GIT_TERMINAL_PROMPT environment variable on every call:

// pkg/iac/scanners/terraform/parser/resolvers/remote.go:88-93
terminalPrompt := os.Getenv("GIT_TERMINAL_PROMPT")
os.Setenv("GIT_TERMINAL_PROMPT", "0")
defer os.Setenv("GIT_TERMINAL_PROMPT", terminalPrompt)

The variable is process-global, so concurrent download() calls create a TOCTOU window. This is not a memory data race (Go serializes environment access internally) but a logical one.

  1. Lost original value. Goroutine B reads "0" (set by A) as the original, then restores to "0" on exit. The user's original value is lost for the rest of the process, affecting later git operations including pkg/downloader.
  2. Prompt re-enabled mid-clone. Goroutine A's deferred restore turns the prompt back on while B's git clone is still running, so git blocks waiting for interactive credentials on a non-interactive server instead of failing fast (hang / soft DoS).

Reachability

  • trivy config / trivy fs: module resolution is sequential — not affected.
  • trivy image / trivy vm: per-layer PostAnalyze runs concurrently (--parallel, default 5), so two layers can invoke the Terraform scanner and download() at the same time. Reachable within a single CLI process.
  • Trivy embedded as a library with concurrent Terraform scans.

Open question

Setting GIT_TERMINAL_PROMPT=0 once makes it process-wide, visible to embedders. This is acceptable in practice — the variable only controls whether git prompts for credentials interactively, which no application embedding a scanner relies on, and pkg/downloader already runs without restoring it. Confirm this is acceptable before implementing.

Activity

  1. added
    kind/bugCategorizes issue or PR as related to a bug.
    scan/misconfigurationIssues relating to misconfiguration scanning
    on Jun 11, 2026
  2. added theissue type on Jun 11, 2026
  3. gankur commented on Jun 21, 2026

    @gankur

    On the open question: I checked whether we can set the prompt for just the git command instead of the whole process. We can't with go-getter as it is today. Its git getter runs git and only sets a custom env when an SSH key is used; otherwise the git process just inherits the environment from Trivy. There's no option to pass an env for a single call. So setting it on the process is the only way — process-wide isn't just acceptable, it's the only option here.

    One small idea: set it once on the first remote download (a sync.Once inside download()) instead of at startup. That way nothing changes the environment unless a remote module is actually downloaded — trivy config/fs and anyone scanning only local modules are unaffected.

    I have a patch ready: a small disableGitTerminalPrompt() helper behind the Once, plus a simple test. Happy to open it once you confirm. If you'd rather set it at startup for simplicity, that's an easy change too.

  4. abidedavana commented on Jul 8, 2026

    @abidedavana

    Hi, I'd like to work on this

    @gankur you mentioned a few weeks ago you had a patch ready. If you're still planning to open it, go ahead and I'll pick something else. If not, I have a working fix I can open a PR for.

    My change does what was suggested above: set GIT_TERMINAL_PROMPT=0 once (with sync.OnceValue) on the first remote download, and don't restore it. So the environment isn't touched unless a remote module actually gets downloaded. If you'd rather set it at startup instead, I can change that easily.

    I tested on Linux: go test ./pkg/iac/scanners/terraform/parser/... passes, including TestResolveModuleFromCache and TestResolveModuleFromCacheWithDifferentSubdir, and golangci-lint v2.12.2 with --build-tags=integration is clean on the package. Let me know if I should open the PR.

  5. nikpivkin commented on Aug 5, 2026

    @nikpivkin
    ContributorAuthor

    @knqyf263 what do you think, where should GIT_TERMINAL_PROMPT=0 live? Setting it inside download() means the library silently mutates the embedder's process environment. Setting it once in cmd/trivy makes it a decision of the process owner, removes the need for any synchronisations, and also covers pkg/downloader (bundles, VEX repos, plugins), which doesn't disable the prompt today. The trade-off is that embedders become responsible for setting it themselves - which matters mainly for TTY-attached processes, since without a TTY git fails instead of prompting anyway.

  6. knqyf263 commented on Aug 6, 2026

    @knqyf263
    Collaborator

    @nikpivkin I tried a small proof of concept that replaces only go-getter's Git getter in the client-specific getter map:

    getters := maps.Clone(getter.Getters)
    getters["file"] = &getter.FileGetter{Copy: true}
    getters["git"] = newTrivyGitGetter()

    The Trivy-specific getter sets GIT_TERMINAL_PROMPT=0 only on each Git child process. The prototype confirmed that the Git child receives the variable while Trivy's process environment remains unchanged.

    However, implementing this getter entirely in Trivy would require copying most of go-getter's Git logic, including its handling of refs, shallow clones, SSH keys, checkout, and submodules.

    If go-getter allowed callers to add environment variables to its Git commands, Trivy could continue using the existing GitGetter. Conceptually, it could look like this:

    getters["git"] = &getter.GitGetter{
        Env: []string{"GIT_TERMINAL_PROMPT=0"},
    }

    Env is only an illustrative API. The exact API should be discussed with the go-getter maintainers.

    What do you think about opening an issue in hashicorp/go-getter first? If the maintainers are open to this, we could follow up with a small PR. This would avoid maintaining a copy of GitGetter in Trivy.

  7. nikpivkin commented on Aug 6, 2026

    @nikpivkin
    ContributorAuthor

    What do you think about opening an issue in hashicorp/go-getter first? If the maintainers are open to this, we could follow up with a small PR. This would avoid maintaining a copy of GitGetter in Trivy.

    Yes, I'll open an issue, and if the maintainers approve it, I can start working on the implementation. But I'm afraid we might get rejected or not receive a response at all, since some HashiCorp packages don't accept feature requests because they're primarily intended for internal tools.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugCategorizes issue or PR as related to a bug.scan/misconfigurationIssues relating to misconfiguration scanning

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions