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.
Problem
Two call sites in the dependency download layer construct a subprocess environment by dict-unpacking
build_ado_bearer_git_env()orbuild_authorization_header_git_env()on top of an existing env dict:build_authorization_header_git_envreturns 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-1withGIT_CONFIG_COUNT=N), the dict-unpack overwritesGIT_CONFIG_COUNTto1and clobbers whatever was at index 0.Impact
Git config entries that
_clear_git_auth_envintentionally retains are silentlydropped from the subprocess environment:
safe.bareRepository=explicit-- security hardening that prevents git fromtraversing 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 theentry 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:
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_entryhelper nowimplements the append idiom. These two clone/download sites are the remaining
instances and are tracked separately to keep that change single-purpose.