Skip to content

WIP: add security event evidence and redaction - #8574

Open
Bill Schnurr (bschnurr) wants to merge 3 commits into
microsoft:mainfrom
bschnurr:wip/fix-tests
Open

WIP: add security event evidence and redaction#8574
Bill Schnurr (bschnurr) wants to merge 3 commits into
microsoft:mainfrom
bschnurr:wip/fix-tests

Conversation

@bschnurr

Copy link
Copy Markdown
Member

No description provided.

@bschnurr
Bill Schnurr (bschnurr) requested a review from a team as a code owner July 7, 2026 16:52
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

- Use managed recursive delete with retries in FileUtils
- Fallback to registry search for interpreter discovery in tests
- Add async WaitAsync for process exit with timeout/cancel
- Always set env vars in REPL; log spurious ERRE/DONE as warnings
- Limit Python 2.x filtering to PythonCore entries
- Clean up orphan registry keys in environment list tests
- Make Django UI test assertions more robust to output changes
- Update urls.py for Django 2.x+ compatibility with fallback
@rchiodo

Rich Chiodo (rchiodo) commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

🔒 Automated review in progress — Rich Chiodo (@rchiodo) is auto-reviewing this PR.

@sonarqubecloud

sonarqubecloud Bot commented Jul 7, 2026

Copy link
Copy Markdown

@"(?ix)(\b(?:password|passwd|pwd|token|access[_-]?token|refresh[_-]?token|secret|client[_-]?secret|api[_-]?key|subscription[_-]?key|credential|authorization|connection\s*string|connectionstring|sharedaccesssignature|sig|key)\b\s*[:=]\s*)(['""']?)([^'"";\s,&]+)(['""']?)",
RegexOptions.CultureInvariant
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/Common/Infrastructure/SensitiveDataRedactor.cs:35
Verified security bypass (Skeptic executed the byte-identical Python twin; Advocate conceded). The SecretKeyValue pattern requires the keyword to be immediately followed by :/=, so quoted-key forms leak verbatim: {"password": "hunter2"}, {'api_key': 'plain-secret'}, and HTTP_AUTHORIZATION (the _ word char defeats \bAuthorization\b) all pass through unchanged. This is the dominant serialized-secret shape for the very sinks this control protects (WSGI environ, ex.ToString(), TaskDialog details), so the redactor silently leaks what it exists to stop. Allow an optional quote between key and separator (e.g. \b(?:...)\b['\"]?\s*[:=]\s*), match a leading quote on the key, and add regression cases for JSON, Python-dict-repr, and HTTP_* keys. The current tests only cover the bare key=/key: forms that already work, giving false confidence.

[verified]

r"\b(password|passwd|pwd|token|access[_-]?token|refresh[_-]?token|secret|client[_-]?secret|"
r"api[_-]?key|subscription[_-]?key|credential|authorization|connection\s*string|connectionstring|"
r"sharedaccesssignature|sig|key)\b(\s*[:=]\s*)(['\"]?)([^'\";\s,&]+)(['\"]?)",
re.I

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/WFastCgi/wfastcgi.py:341
Same verified quoted-key bypass as the C# redactor (regexes are character-identical). Because environ is literally a dict and FastCGI logs frequently carry JSON/dict-repr, _sanitize_log_text will pass {"password": "..."} / HTTP_AUTHORIZATION straight into WSGI_LOG and AppInsights track_event. Fix the shared spec (optional quote before the separator; match leading key quote) and add the same regression cases here.

[verified]

}

/// <summary>
/// An exception that should not be silently handled and logged.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/Cookiecutter/Shared/Infrastructure/ExceptionExtensions.cs:62
Review-rule violation (reuse-core-utils): this private SensitiveDataRedactor is a character-for-character duplicate of Python/Product/Common/Infrastructure/SensitiveDataRedactor.cs (Architect verified byte-identical regexes via read_file). The using System.Text.RegularExpressions; added only for this copy signals the Common dependency was avoided rather than wired in. This copy is internal and untested, so it will inevitably drift from the Common one — any pattern fix (e.g. the quoted-key bypass) now has to land in three places across two languages. Remove this class and reference Microsoft.PythonTools.Infrastructure.SensitiveDataRedactor (link it as shared source or add a project reference to Common). Per the shared-review-policy hard gate, this stays an issue until the rule itself is amended.

[verified]

sanitized = SecretKeyValue.Replace(sanitized, match =>
match.Groups[1].Value + match.Groups[2].Value + RedactedValue + match.Groups[4].Value
);
return sanitized;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/Common/Infrastructure/SensitiveDataRedactor.cs:44
Verified partial leak: the value class [^'\";\s,&]+ stops at the first whitespace/comma, so any secret containing a space, comma, ;, &, or quote leaks its tail. Confirmed: password=my secret phrasepassword=<redacted> secret phrase, token=a,b,ctoken=<redacted>,b,c, and a connection string Server=x;Password=p@ss w0rd;... leaks w0rd. Passphrases and comma-joined token lists are realistic. For quoted values, consume to the closing quote instead of stopping at whitespace/comma.

[verified]

@"(?i)\b([a-z][a-z0-9+.-]*://)([^/\s:@]+(?::[^/\s@]*)?@)",
RegexOptions.CultureInvariant
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/Common/Infrastructure/SensitiveDataRedactor.cs:30
Verified partial leak in UriUserInfo: the userinfo class [^/\s:@]*/[^/\s@]* stops at the first @, so a password containing @ (legal and common) partially leaks — https://user:p@ssword@host/pathhttps://<redacted>@ssword@host/path, leaving ssword@host. Consume up to the last @ before the authority instead.

[verified]

@"(?ix)(\b(?:password|passwd|pwd|token|access[_-]?token|refresh[_-]?token|secret|client[_-]?secret|api[_-]?key|subscription[_-]?key|credential|authorization|connection\s*string|connectionstring|sharedaccesssignature|sig|key)\b\s*[:=]\s*)(['""']?)([^'"";\s,&]+)(['""']?)",
RegexOptions.CultureInvariant
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/Common/Infrastructure/SensitiveDataRedactor.cs:35
Verified over-redaction: the bare keywords key and sig are generic enough to mangle ordinary diagnostic text — Parsing failed at key: value in configkey: <redacted>, and the design sig = v2sig = <redacted>. This erodes the diagnostic value the evidence file claims to preserve. Consider dropping bare key/sig or requiring a tighter context (e.g. api_key, signing_sig). Note the current test deliberately encodes sig redaction as desired, so decide the intended behavior explicitly.

[verified]

match.Groups[1].Value + match.Groups[2].Value + RedactedValue + match.Groups[4].Value
);
return sanitized;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/Common/Infrastructure/SensitiveDataRedactor.cs:40
Architectural concern (Architect, structurally confirmed): redaction is applied by remembering to wrap each producer (TaskDialog, CustomCommand, ExceptionExtensions, wfastcgi.log) rather than at the logging-sink boundary. This makes coverage aspirational — any new logging call site silently bypasses redaction, which the evidence file already admits ("outside the covered redaction paths"). Consider redacting once where text enters the Trace/EventLog/ActivityLog/AppInsights writers so the guarantee becomes structural. Additionally, share a golden input/expected corpus between the C# and Python copies to enforce spec parity across languages.

[verified]

// reloaded.
_canExecute = false;
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 .github/compliance/evidence/reports/security/2984085-security-events-sensitive-errors-evidence-2026-07-03.md:91
Given the verified JSON/quoted-key bypass, the Evidence 3/4/5 "Control coverage: … avoids common secret-bearing key/value patterns" claims are overstated — they hold only for unquoted k=v/k: v, not the JSON/dict/HTTP_* forms these sinks most often carry. Temper this wording (or fix the redactor first) before this file is cited as coverage, so the compliance artifact doesn't create false confidence. The file's honest PARTIAL framing is otherwise good.

[verified]

best.Configuration.GetPrefixPath(),
best.Configuration.InterpreterPath
) : null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/Cookiecutter/Model/CookiecutterClientProvider.cs:79
Coupling concern (Architect): the new FindCompatibleInterpreterFromRegistry fallback is a production interpreter-selection branch added "for tests" (comment: "used when no IServiceProvider is available (for example, in unit tests)"). Test needs leaking into product code is a smell, and this path is now coupled to the sibling PythonRegistrySearch 2.x-filter change so they must move together. Consider injecting a test seam instead of branching production logic on the absence of a service provider. The compModel?.GetService null-guard added here is a genuine fix (removes a real NRE).

[verified]

var env = processInfo.Environment;
foreach (var kv in _serviceProvider.GetPythonToolsService().GetFullEnvironment(Configuration)) {
env[kv.Key] = kv.Value;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/PythonTools/PythonTools/Repl/PythonInteractiveEvaluator.CommandProcessorThread.cs:104
Unexplained behavior change (Architect): removing the #if DEBUG / if (!debugMode) guard means debug builds now always apply the full environment via GetFullEnvironment, silently changing debug-build startup behavior. This is unrelated to redaction and undocumented in the diff. Add a rationale (or a separate PR) explaining why the debug-only carve-out is no longer needed.

[verified]

// Only filter Python 2.x for PythonCore-compatible entries.
// Custom configurable interpreters (e.g. company == "VisualStudio")
// may legitimately have no SysVersion set, and must not be filtered here.
if (pythonCoreCompatibility && sysVersion > new Version(0, 0) && sysVersion < new Version(3, 0)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/VSInterpreters/Interpreter/PythonRegistrySearch.cs:188
Behavior change riding a redaction PR (Advocate, Skeptic, Architect, all [unverified] runtime): the 2.x filter now only applies when pythonCoreCompatibility && sysVersion > (0,0), so PythonCore entries with a missing/unparseable SysVersion (previously defaulted to (0,0) and filtered out) now pass through. The change is narrowly guarded and the comment explains the intent (custom configurable interpreters), but it widens what surfaces and has no accompanying test in the diff. Add regression coverage for the missing-SysVersion case, or split this into its own PR.

[verified]

}

/// <summary>
/// Enables using 'await' on this object.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/Cookiecutter/Shared/Infrastructure/ProcessOutput.cs:741
No defect found — all three reviewers independently confirmed WaitAsync correctly closes the subscribe-after-exit race (_haveRaisedExitedEvent/HasExited recheck + idempotent TrySetResult), links cancellation with the delay CTS, cancels the delay timer on the exit-wins path, and unsubscribes in finally on every path. The only gap: this new non-trivial async primitive ships without a visible unit test in the diff ([unverified] whether coverage exists elsewhere). Add a targeted test for the timeout, cancellation, and already-exited paths.

[verified]


def log(txt):
"""Logs messages to a log file if WSGI_LOG env var is defined."""
txt = _sanitize_log_text(txt)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📍 Python/Product/WFastCgi/wfastcgi.py:361
Scope/cohesion note (Advocate, Architect): beyond redaction, this PR carries ~8 independent changes (FileUtils managed-delete, WaitAsync, PythonRegistrySearch filter, REPL #if DEBUG removal, REPL Debug.FailTrace.TraceWarning, Django re_path compat, UIA fallback, EnvironmentList orphan cleanup). Each is individually defensible, but the bundle can't be reviewed or reverted selectively — if the redaction work needs rollback given the verified leak, it drags unrelated stabilization fixes with it. Recommend splitting into (1) redaction+evidence, (2) test-infra stabilization, (3) interpreter/registry behavior. The WIP: title is appropriate for now.

[verified]

@rchiodo

Copy link
Copy Markdown
Contributor

Main blocker: the redaction regexes only match bare key=/key: v and miss the dominant serialized-secret shapes for these very sinks — JSON/dict-repr ({"password": "..."}), quoted keys, and HTTP_AUTHORIZATION (the _ defeats \bAuthorization\b). Fix the shared spec (allow an optional quote before the separator, match a leading key quote, handle HTTP_*) and add regression cases for those forms. Also de-duplicate the redactor into one shared implementation, and temper the evidence file's coverage claims accordingly. Secondary: several unrelated stabilization changes (WaitAsync, FileUtils delete, PythonRegistrySearch filter, REPL #if DEBUG removal) ride along without tests/rationale — consider splitting so the redaction work can be reverted independently if needed.

@rchiodo

Copy link
Copy Markdown
Contributor

Main theme: the new SensitiveDataRedactor is the security-critical core of this PR, but its regex only handles bare key=v/key: v forms — verified bypasses leak {"password": "..."}/dict-repr and HTTP_AUTHORIZATION-style keys straight into logs (C# and Python copies are character-identical, so both leak). Please fix the shared spec, add JSON/dict/HTTP_* regression cases, and reconcile the compliance-evidence wording with the real coverage. Separately, this WIP bundles ~8 unrelated stabilization/behavior changes that would be easier to review and revert if split out.

@heejaechang

Heejae Chang (heejaechang) commented Jul 20, 2026

Copy link
Copy Markdown

🔒 Automated review in progress — Heejae Chang (@heejaechang) is auto-reviewing this PR.

}

var sanitized = AuthorizationHeader.Replace(text, "$1" + RedactedValue);
sanitized = UriUserInfo.Replace(sanitized, "$1" + RedactedValue + "@");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Skeptic verified that JSON/quoted-key secrets pass through unredacted: {"password": "hunter2secret"} is returned verbatim. The SecretKeyValue separator group requires the :/= to immediately follow the bare keyword, but in JSON the character after password is ". Allow an optional quote between key and separator or explicitly match quoted JSON keys. This regex is triplicated in Cookiecutter and wfastcgi, so fix all copies.

}

var sanitized = AuthorizationHeader.Replace(text, "$1" + RedactedValue);
sanitized = UriUserInfo.Replace(sanitized, "$1" + RedactedValue + "@");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SECRET_KEY = '...' and other underscored compound keys are never redacted. secret requires a trailing \b, which fails before _, while key requires a leading \b, which fails after _. This leaves common Django secret values in cleartext. Add secret[_-]?key and corresponding compound forms to the alternation, or otherwise handle compound secret names in all three redactor copies.

return text;
}

var sanitized = AuthorizationHeader.Replace(text, "$1" + RedactedValue);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue · Please address or respond

The key/value regex does not redact JSON-quoted keys: {"password": "hunter2"} passes through because the closing quote between password and : prevents the delimiter match. Exception strings and FastCGI payloads commonly contain JSON, so allow an optional closing key quote and add a JSON test case. Apply the equivalent correction to each redactor copy.


public static string Sanitize(string text) {
if (string.IsNullOrEmpty(text)) {
return text;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

The value pattern stops at spaces, commas, semicolons, and ampersands, leaking the remainder of secrets with those characters. For example, password: p@ss w0rd_secret becomes password: <redacted> w0rd_secret. Redact through the matching quote or an appropriate value boundary instead.


public static string Sanitize(string text) {
if (string.IsNullOrEmpty(text)) {
return text;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

The bare key and sig alternatives redact ordinary diagnostics such as primary key=5 and dictionary key: customer_id. This can hide useful failure context; narrow these alternatives or add tests that document the intended behavior for bare key and sig.


**Control coverage**:

- User-visible expanded exception details avoid common secret-bearing key/value patterns, authorization headers, and URI user-info credentials.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

The evidence currently overstates redaction coverage: JSON-shaped secrets are not covered by the current key/value pattern. Update this claim together with the regex fix so the assessment accurately describes the implemented protection.

@rchiodo Rich Chiodo (rchiodo) added the review-auto:changes-requested Automated review: posted blocking findings to address. label Aug 5, 2026
@StellaHuang95

Stella Huang (StellaHuang95) commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🔒 Automated review in progress — Stella Huang (@StellaHuang95) is auto-reviewing this PR.

r"sharedaccesssignature|sig|key)\b(\s*[:=]\s*)(['\"]?)([^'\";\s,&]+)(['\"]?)",
re.I
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue · Please address or respond

The value class [^'";\s,&]+ stops at the first whitespace, so secrets containing spaces are only partially redacted: Password=P@ss w0rd; becomes Password=<redacted> w0rd;. Passphrases and connection-string password segments can contain spaces, so this leaks sensitive data. Match quoted values through their closing quote and redact unquoted values through their delimiter in all three redactor implementations. [verified]


public static string Sanitize(string text) {
if (string.IsNullOrEmpty(text)) {
return text;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue · Please address or respond

This pattern does not match JSON keys such as {"password": "secret"} because it expects : or = immediately after password, but the closing quote intervenes. JSON configuration and exception payloads can therefore log secrets unchanged. Support optionally quoted keys and add regression coverage for the C#, Cookiecutter, and WFastCGI redactors.

[TestMethod, Priority(UnitTestPriority.P0)]
public void SanitizesAuthorizationHeaders() {
var sanitized = SensitiveDataRedactor.Sanitize("Before\r\nAuthorization: Bearer abc123\r\nAfter");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

These tests cover only whitespace-free values, so they would not catch the verified space-in-value leak. Add quoted and unquoted secrets containing spaces plus semicolon/comma-delimited values; add equivalent coverage for the Cookiecutter and WFastCGI implementations to prevent the copies from drifting. [verified]

var env = processInfo.Environment;
foreach (var kv in _serviceProvider.GetPythonToolsService().GetFullEnvironment(Configuration)) {
env[kv.Key] = kv.Value;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

Removing the #if DEBUG / if (!debugMode) guard now injects GetFullEnvironment(Configuration) unconditionally in DEBUG builds, changing REPL startup behavior independently of redaction. Please confirm this is intentional and cover or separate the behavior change. [verified]

// Only filter Python 2.x for PythonCore-compatible entries.
// Custom configurable interpreters (e.g. company == "VisualStudio")
// may legitimately have no SysVersion set, and must not be filtered here.
if (pythonCoreCompatibility && sysVersion > new Version(0, 0) && sysVersion < new Version(3, 0)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

Gating the Python-2.x reject on pythonCoreCompatibility means known Python 2.x interpreters from non-PythonCore vendors are no longer filtered, although the previous behavior rejected all versions below 3.0. Add coverage for both an unset custom interpreter version and a known non-PythonCore Python 2.x version so the intended discovery policy is explicit. [verified]


private static readonly Regex SecretKeyValue = new Regex(
@"(?ix)(\b(?:password|passwd|pwd|token|access[_-]?token|refresh[_-]?token|secret|client[_-]?secret|api[_-]?key|subscription[_-]?key|credential|authorization|connection\s*string|connectionstring|sharedaccesssignature|sig|key)\b\s*[:=]\s*)(['""']?)([^'"";\s,&]+)(['""']?)",
RegexOptions.CultureInvariant

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue · Please address or respond

📍 Python/Product/Common/Infrastructure/SensitiveDataRedactor.cs:34
Verified inputs such as {"password":"hunter2"} remain unchanged, while password="correct horse battery staple" leaves horse battery staple exposed. Update the pattern to support quoted keys and consume complete quoted values, then add these cases to the C# conformance tests.

[verified]

r"\b(password|passwd|pwd|token|access[_-]?token|refresh[_-]?token|secret|client[_-]?secret|"
r"api[_-]?key|subscription[_-]?key|credential|authorization|connection\s*string|connectionstring|"
r"sharedaccesssignature|sig|key)\b(\s*[:=]\s*)(['\"]?)([^'\";\s,&]+)(['\"]?)",
re.I

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue · Please address or respond

📍 Python/Product/WFastCgi/wfastcgi.py:342
The exact Python regex was verified to leak JSON-style and whitespace-containing secrets before they reach both logging sinks. Mirror the corrected matching semantics here and add direct WFastCGI tests so the Python implementation cannot drift from C#.

[verified]

@"(?ix)(\b(?:password|passwd|pwd|token|access[_-]?token|refresh[_-]?token|secret|client[_-]?secret|api[_-]?key|subscription[_-]?key|credential|authorization|connection\s*string|connectionstring|sharedaccesssignature|sig|key)\b\s*[:=]\s*)(['""']?)([^'"";\s,&]+)(['""']?)",
RegexOptions.CultureInvariant
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

📍 Python/Product/Cookiecutter/Shared/Infrastructure/ExceptionExtensions.cs:51
This embeds a third independently maintained copy of the redaction policy, but only the Common implementation receives tests. Reuse one C# implementation where assembly boundaries permit, or drive every implementation from shared conformance vectors covering JSON, quoting, whitespace, URLs, and connection strings.

[verified]

// Best-effort cleanup; ignore failures.
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

📍 Python/Tests/Core.UI/EnvironmentListTests.cs:1127
This cleanup deletes every GUID-named key under the user's HKCU\Software\Python\VisualStudio, although configurable interpreters can legitimately use arbitrary names and carry no test-ownership marker. Restrict cleanup to IDs recorded by this test or use an unmistakable test-specific prefix/marker.

[verified]

return null;
// No service provider is available (e.g. when called from tests).
// Fall back to enumerating Python installations directly from the
// registry so we can still find a compatible interpreter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

📍 Python/Product/Cookiecutter/Model/CookiecutterClientProvider.cs:42
[unverified] A null service provider now triggers machine-wide registry discovery, changing production semantics specifically to accommodate tests. Prefer injecting an interpreter source for tests, or verify and document every production caller where this fallback is intended.

[verified]

if (sysVersion < new Version(3, 0)) {
// Only filter Python 2.x for PythonCore-compatible entries.
// Custom configurable interpreters (e.g. company == "VisualStudio")
// may legitimately have no SysVersion set, and must not be filtered here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

📍 Python/Product/VSInterpreters/Interpreter/PythonRegistrySearch.cs:188
[unverified] This condition preserves unknown 0.0 custom entries but also permits explicitly versioned Python 2 custom interpreters, despite the surrounding unsupported-version policy. Narrow the exception to unknown versions or add a test proving Python 2 custom entries must remain discoverable.

[verified]

} finally {
// Ensure we don't leak the event subscription on any exit path.
Exited -= onExited;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

📍 Python/Product/Cookiecutter/Shared/Infrastructure/ProcessOutput.cs:743
This substantial public concurrency API has no changed caller or regression tests and is unrelated to the security-focused PR. Move it and the other stabilization changes into focused PRs, or add concrete callers plus timeout, cancellation, exit-race, and handler-cleanup tests with a clear rationale.

[verified]

return;
} catch (InvalidOperationException) {
// Fall back to mouse input for controls that do not support UIA selection.
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

Returning after UIA selection bypasses the existing mouse movement and click path. If that path repairs stale mouse state for drag-and-drop tests, preserve the reset behavior or add coverage with the pointer intentionally left in the problematic state.

[verified]

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

Labels

review-auto:changes-requested Automated review: posted blocking findings to address.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants