Skip to content

GIT_CONFIG_COUNT clobber in clone/download paths drops inherited git hardening settings #2368

Description

@sergio-sisternes-epam

Problem

Two call sites in the dependency download layer construct a subprocess environment by dict-unpacking build_ado_bearer_git_env() or build_authorization_header_git_env() on top of an existing env dict:

# src/apm_cli/deps/clone_engine.py (bearer fallback in CloneEngine.execute)
bearer_env = {
    **host.git_env,
    **build_ado_bearer_git_env(bearer),
}

# src/apm_cli/deps/download_strategies.py (sparse-git file fetch)
git_env = {
    **auth_ctx.git_env,
    **build_authorization_header_git_env("Bearer", auth_ctx.token),
}

build_authorization_header_git_env returns a hardcoded
{"GIT_CONFIG_COUNT": "1", "GIT_CONFIG_KEY_0": ..., "GIT_CONFIG_VALUE_0": ...}.
When the base env already contains retained non-auth git config entries
(indices 0..N-1 with GIT_CONFIG_COUNT=N), the dict-unpack overwrites
GIT_CONFIG_COUNT to 1 and clobbers whatever was at index 0.

Impact

Git config entries that _clear_git_auth_env intentionally retains are silently
dropped from the subprocess environment:

  • safe.bareRepository=explicit -- security hardening that prevents git from
    traversing stray bare repos in the working directory.
  • credential.interactive=never -- defence-in-depth prompt suppression.

The loss is invisible in testing because git reads only indices
0..COUNT-1, so orphaned entries at higher indices are simply dead. But the
entry that was AT index 0 is directly overwritten, not orphaned -- it is gone.

Fix shape

Append the auth header at the next available index instead of replacing the
entire config set:

count = int(env.get("GIT_CONFIG_COUNT", "0") or "0")
env["GIT_CONFIG_COUNT"] = str(count + 1)
env[f"GIT_CONFIG_KEY_{count}"] = "http.extraheader"
env[f"GIT_CONFIG_VALUE_{count}"] = f"Authorization: {scheme} {credential}"

Affected files

  • src/apm_cli/deps/clone_engine.py (around line 299)
  • src/apm_cli/deps/download_strategies.py (around line 756)

Notes

The same defect was found and fixed in the core auth resolver
(_build_git_env) and in the marketplace ls-remote path
(marketplace/ref_resolver.py), where an _append_git_config_entry helper now
implements the append idiom. These two clone/download sites are the remaining
instances and are tracked separately to keep that change single-purpose.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/distributionInstallers (curl/PowerShell/Brew/Scoop), self-update, devcontainer, codespaces.bugDeprecated: use type/bug. Kept for issue history; will be removed in milestone 0.10.0.priority/highShips in current or next milestonestatus/acceptedDirection approved, safe to start work.status/triagedInitial agentic triage complete; pending maintainer ratification (silence = approval).theme/securitySecure by default. Content scanning, lockfile integrity, MCP trust boundaries.type/bugSomething does not work as documented.

    Type

    No type

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions