From 3123d98132ba392025469d459846f7ccc44b6040 Mon Sep 17 00:00:00 2001 From: yaakovschectman <109111084+yaakovschectman@users.noreply.github.com> Date: Thu, 18 Jan 2024 12:32:49 -0500 Subject: [PATCH] Add check for Bank of Brazil security module to Windows Flutter Doctor validators (#141135) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a warning to Flutter Doctor if Topaz OFD is found as a process on the system. The protection module used by the Bank of Brazil has been identified as causing build failures when using VS with CMake for Windows (see https://github.com/flutter/flutter/issues/121366#issuecomment-1845703728). Disabling the software allows the build to succeed again. If a running process is found by `flutter doctor` whose path contains `Topaz OFD\Warsaw\core.exe`, a warning message is generated to convey this. Addresses https://github.com/flutter/flutter/issues/121366 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [Features we expect every widget to implement]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat --------- Co-authored-by: Elias Yishak <42216813+eliasyishak@users.noreply.github.com> Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com> --- packages/flutter_tools/lib/src/doctor.dart | 1 + .../windows/windows_version_validator.dart | 64 +++++++++++++++- .../windows_version_validator_test.dart | 76 ++++++++++++++++++- 3 files changed, 135 insertions(+), 6 deletions(-) diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart index 787c777e63a..765ea616a54 100644 --- a/packages/flutter_tools/lib/src/doctor.dart +++ b/packages/flutter_tools/lib/src/doctor.dart @@ -142,6 +142,7 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { if (platform.isWindows) WindowsVersionValidator( operatingSystemUtils: globals.os, + processLister: ProcessLister(globals.processManager), ), if (androidWorkflow!.appliesToHostPlatform) GroupedValidator([androidValidator!, androidLicenseValidator!]), diff --git a/packages/flutter_tools/lib/src/windows/windows_version_validator.dart b/packages/flutter_tools/lib/src/windows/windows_version_validator.dart index 1d0e19921b3..cd90d88449b 100644 --- a/packages/flutter_tools/lib/src/windows/windows_version_validator.dart +++ b/packages/flutter_tools/lib/src/windows/windows_version_validator.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:process/process.dart'; + +import '../base/io.dart'; import '../base/os.dart'; import '../doctor_validator.dart'; @@ -16,14 +19,44 @@ const List kUnsupportedVersions = [ /// (ie. 10.5.4123) const String kWindowsOSVersionSemVerPattern = r'([0-9]+)\.([0-9]+)\.([0-9\.]+)'; +/// Regex pattern for identifying a running instance of the Topaz OFD process. +/// This is a known process that interferes with the build toolchain. +/// See https://github.com/flutter/flutter/issues/121366 +const String kCoreProcessPattern = r'Topaz\s+OFD\\Warsaw\\core\.exe'; + /// Validator for supported Windows host machine operating system version. class WindowsVersionValidator extends DoctorValidator { const WindowsVersionValidator({ required OperatingSystemUtils operatingSystemUtils, + required ProcessLister processLister, }) : _operatingSystemUtils = operatingSystemUtils, + _processLister = processLister, super('Windows Version'); final OperatingSystemUtils _operatingSystemUtils; + final ProcessLister _processLister; + + Future _topazScan() async { + final ProcessResult getProcessesResult = await _processLister.getProcessesWithPath(); + if (getProcessesResult.exitCode != 0) { + return const ValidationResult(ValidationType.missing, [ValidationMessage.hint('Get-Process failed to complete')]); + } + final RegExp topazRegex = RegExp(kCoreProcessPattern, caseSensitive: false, multiLine: true); + final String processes = getProcessesResult.stdout as String; + final bool topazFound = topazRegex.hasMatch(processes); + if (topazFound) { + return const ValidationResult( + ValidationType.missing, + [ + ValidationMessage.hint( + 'The Topaz OFD Security Module was detected on your machine. ' + 'You may need to disable it to build Flutter applications.', + ), + ], + ); + } + return const ValidationResult(ValidationType.success, []); + } @override Future validate() async { @@ -34,12 +67,26 @@ class WindowsVersionValidator extends DoctorValidator { // Use the string split method to extract the major version // and check against the [kUnsupportedVersions] list - final ValidationType windowsVersionStatus; - final String statusInfo; + ValidationType windowsVersionStatus; + final List messages = []; + String statusInfo; if (matches.length == 1 && !kUnsupportedVersions.contains(matches.elementAt(0).group(1))) { windowsVersionStatus = ValidationType.success; statusInfo = 'Installed version of Windows is version 10 or higher'; + + // Check if the Topaz OFD security module is running, and warn the user if it is. + // See https://github.com/flutter/flutter/issues/121366 + final List subResults = [ + await _topazScan(), + ]; + for (final ValidationResult subResult in subResults) { + if (subResult.type != ValidationType.success) { + statusInfo = 'Problem detected with Windows installation'; + windowsVersionStatus = ValidationType.partial; + messages.addAll(subResult.messages); + } + } } else { windowsVersionStatus = ValidationType.missing; statusInfo = @@ -48,8 +95,19 @@ class WindowsVersionValidator extends DoctorValidator { return ValidationResult( windowsVersionStatus, - const [], + messages, statusInfo: statusInfo, ); } } + +class ProcessLister { + ProcessLister(this.processManager); + + final ProcessManager processManager; + + Future getProcessesWithPath() async { + const String argument = 'Get-Process | Format-List Path'; + return processManager.run(['powershell', '-command', argument]); + } +} diff --git a/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart b/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart index e186de32be3..f6ebb9d7a9f 100644 --- a/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart +++ b/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/doctor_validator.dart'; import 'package:flutter_tools/src/windows/windows_version_validator.dart'; @@ -19,6 +20,29 @@ class FakeValidOperatingSystemUtils extends Fake final String name; } +class FakeProcessLister extends Fake implements ProcessLister { + FakeProcessLister({required this.result, this.exitCode = 0}); + final String result; + final int exitCode; + + @override + Future getProcessesWithPath() async { + return ProcessResult(0, exitCode, result, null); + } +} + +FakeProcessLister ofdRunning() { + return FakeProcessLister(result: r'Path: "C:\Program Files\Topaz OFD\Warsaw\core.exe"'); +} + +FakeProcessLister ofdNotRunning() { + return FakeProcessLister(result: r'Path: "C:\Program Files\Google\Chrome\Application\chrome.exe'); +} + +FakeProcessLister failure() { + return FakeProcessLister(result: r'Path: "C:\Program Files\Google\Chrome\Application\chrome.exe', exitCode: 10); +} + /// The expected validation result object for /// a passing windows version test const ValidationResult validWindows10ValidationResult = ValidationResult( @@ -35,12 +59,32 @@ const ValidationResult invalidWindowsValidationResult = ValidationResult( statusInfo: 'Unable to confirm if installed Windows version is 10 or greater', ); +const ValidationResult ofdFoundRunning = ValidationResult( + ValidationType.partial, + [ + ValidationMessage.hint( + 'The Topaz OFD Security Module was detected on your machine. ' + 'You may need to disable it to build Flutter applications.', + ), + ], + statusInfo: 'Problem detected with Windows installation', +); + +const ValidationResult getProcessFailed = ValidationResult( + ValidationType.partial, + [ + ValidationMessage.hint('Get-Process failed to complete'), + ], + statusInfo: 'Problem detected with Windows installation', +); + void main() { testWithoutContext('Successfully running windows version check on windows 10', () async { final WindowsVersionValidator windowsVersionValidator = WindowsVersionValidator( - operatingSystemUtils: FakeValidOperatingSystemUtils()); + operatingSystemUtils: FakeValidOperatingSystemUtils(), + processLister: ofdNotRunning()); final ValidationResult result = await windowsVersionValidator.validate(); @@ -56,7 +100,8 @@ void main() { final WindowsVersionValidator windowsVersionValidator = WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils( - 'Microsoft Windows [versão 10.0.22621.1105]')); + 'Microsoft Windows [versão 10.0.22621.1105]'), + processLister: ofdNotRunning()); final ValidationResult result = await windowsVersionValidator.validate(); @@ -70,7 +115,8 @@ void main() { final WindowsVersionValidator windowsVersionValidator = WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils( - 'Microsoft Windows [Version 8.0.22621.1105]')); + 'Microsoft Windows [Version 8.0.22621.1105]'), + processLister: ofdNotRunning()); final ValidationResult result = await windowsVersionValidator.validate(); @@ -99,4 +145,28 @@ OS 版本: 10.0.22621 暂缺 Build 22621 expect(matches.length, 5, reason: 'There should be only 5 matches for the pattern provided'); }); + + testWithoutContext('Successfully checks for Topaz OFD when it is running', () async { + final WindowsVersionValidator validator = + WindowsVersionValidator( + operatingSystemUtils: FakeValidOperatingSystemUtils(), + processLister: ofdRunning()); + final ValidationResult result = await validator.validate(); + expect(result.type, ofdFoundRunning.type, reason: 'The ValidationResult type should be the same (partial)'); + expect(result.statusInfo, ofdFoundRunning.statusInfo, reason: 'The ValidationResult statusInfo should be the same'); + expect(result.messages.length, 1, reason: 'The ValidationResult should have precisely 1 message'); + expect(result.messages[0].message, ofdFoundRunning.messages[0].message, reason: 'The ValidationMessage message should be the same'); + }); + + testWithoutContext('Reports failure of Get-Process', () async { + final WindowsVersionValidator validator = + WindowsVersionValidator( + operatingSystemUtils: FakeValidOperatingSystemUtils(), + processLister: failure()); + final ValidationResult result = await validator.validate(); + expect(result.type, getProcessFailed.type, reason: 'The ValidationResult type should be the same (partial)'); + expect(result.statusInfo, getProcessFailed.statusInfo, reason: 'The ValidationResult statusInfo should be the same'); + expect(result.messages.length, 1, reason: 'The ValidationResult should have precisely 1 message'); + expect(result.messages[0].message, getProcessFailed.messages[0].message, reason: 'The ValidationMessage message should be the same'); + }); }