refactor(auth): centralize indexed Git config cleanup - #2411
Draft
Pybsama wants to merge 2 commits into
Draft
Conversation
Move retain/reindex policy into utils/git_env.py and route both consumers through the shared owner. Add regression coverage and an architecture mutation guard for microsoft#2398.
Add the required Unreleased changelog entry using PR microsoft#2411. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
refactor(auth): centralize indexed Git config cleanup
TL;DR
Centralize ownership of indexed
GIT_CONFIG_*authentication-header cleanup inapm_cli.utils.git_env.This removes two subtly different cleanup implementations from the authentication
resolver and the GitHub-host helper.
The shared owner now:
http.*.extraheaderentries whose value contains an actualAuthorization:header field;Fixes #2398.
Problem
APM has two consumers that need to remove stale Git authentication headers before
constructing a child-process environment:
AuthResolver._clear_git_auth_env;set_authorization_header_git_env.Each consumer previously implemented its own scan, filtering, and reindexing.
The two implementations did not agree on what counted as an authentication entry.
One checked whether the entire value started with
authorization:while the otherremoved any value containing the substring
authorization.That split ownership created two concrete risks:
extraheadervalue could retain a stale credential when itsAuthorization field was not first;
x-authorization-modecould bediscarded even though it was not an Authorization header.
Because the environment is inherited by Git subprocesses, both false negatives and
false positives matter: stale credentials are a security boundary, while removed
proxy or TLS configuration can make a valid clone fail.
Approach
Introduce one canonical helper that owns the indexed representation and make both
call sites delegate to it.
Authorizationfield namehttp.*.extraheaderentriesThe helper stages the new indexed state before mutating the environment. This
avoids leaving a partially rewritten sequence if the input contains malformed
count metadata.
Implementation
Canonical owner
src/apm_cli/utils/git_env.pynow provides:
_is_git_auth_config_entry(key, value)for exact field recognition;strip_git_auth_config_entries(env)for filtering, cleanup, and stablecompaction.
The function returns the next free index so a caller that needs to append a new
header can do so without recomputing the sequence.
It treats a missing, negative, non-integer, or otherwise stale count defensively
and still inspects actual indexed key slots present in the environment.
Consumers
AuthResolver._clear_git_auth_envdelegates indexed cleanup to the shared helper,
then continues to clear the resolver-owned direct credential channels.
set_authorization_header_git_envrejects CR/LF in the new token, delegates stale
indexed cleanup, and appends exactly one fresh Authorization header at the returned
index.
Its contract now explicitly describes that sole indexed Git authentication header.
Ownership guard
scripts/check_git_auth_config_owner.pyestablishes the helper as the only
production owner of indexed Git authentication cleanup.
The architecture lint invokes this checker as AC20.
The checker understands relevant syntax rather than relying on a broad text search,
so comments, fixtures, and approved helper implementation details do not create
false positives.
The owner declaration is mirrored in
.apm,.github, and the lock file so thepublished and repository-local architecture guidance remain aligned.
Tests
The change adds focused tests for:
Diagrams
flowchart LR subgraph Consumers["Consumers"] C1["AuthResolver._clear_git_auth_env"] C2["set_authorization_header_git_env"] end subgraph Owner["Canonical owner"] O1["strip_git_auth_config_entries"]:::new O2["_is_git_auth_config_entry"]:::new end subgraph Result["Indexed environment"] R1["Stable retained entries"] R2["Fresh header appended by setter"] end C1 --> O1 C2 --> O1 O1 --> O2 O1 --> R1 O1 --> C2 C2 --> R2 classDef new stroke-dasharray: 5 5The dashed nodes are introduced by this change. Existing consumers now share the
same filtering and compaction path before continuing with their own responsibilities.
Trade-offs
This preserves vendor-neutral configuration while still rejecting a real
Authorization field anywhere in a multiline value.
addition to a valid declared range. This makes stale metadata recoverable without
trusting an arbitrarily large count.
the indexed-config utility would broaden its contract beyond issue Extract shared retain/reindex predicate for git auth-config entries (follow-up to #2368) #2398.
informal ownership rule into an executable boundary.
Benefits
Validation
Automated checks
119 passed in 0.40s19098 passed, 2 skipped, 21 xfailed, 19 warnings, 87 subtests passed in 162.63sAll checks passed!1550 files already formattedYour code has been rated at 10.00/10[+] auth-signal lint clean[+] architecture boundary lint cleansource distribution and wheel both built successfully with
uv buildgit diff --checkcompleted with no outputMutation evidence
All mutations were temporary and the correct implementation was restored before
the full validation run.
Scenario Evidence
How to test
From the repository root:
uv run pytest tests/unit/test_git_env.py tests/unit/test_auth.py tests/unit/test_github_host.py -q.uv run pytest tests/unit -q.uv run ruff check .anduv run ruff format --check ..uv build.Repository checklist
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com