Skip to content

Conversation

sara-rn
Copy link
Contributor

@sara-rn sara-rn commented Jul 15, 2025

Based on the code from Commando-VM, created a new Window that performs pre-installation checks. The same pre-checks that are done if -noGui is provided.
closes #509

@sara-rn sara-rn force-pushed the add-gui-preinstall-checks branch 4 times, most recently from 7dbc832 to 5c11551 Compare July 16, 2025 08:54
Copy link
Member

@Ana06 Ana06 left a comment

Choose a reason for hiding this comment

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

@sara-rn thanks for working on this enhacements!

As already mentioned in your previous PRs: when making UI changes, add a screenshot to make the review of the PR easier.
image

This code is duplicating a lot of code and logic in both the UI and command line. This makes it difficult to maintain the code and keep the logic consistent. As said in , we have to ensure we do not duplicate code to make it easy to keep both views consistent. One way to implement this is to have a checks array with an entry per check with the following information:
(name, description, function, allow_continue)

For example:
("PowerShell version >= 5", Check-PowerShell-Version, False)

The function should return an info message that we can display in the command line (as we do at the moment) and in the UI (extra info when a check fails, as the text below the check title). I think we can summarize some of the info messages if needed. For example:

function Check-PowerShell-Version {
    $psVersion = $PSVersionTable.PSVersion
    if ($psVersion -lt [System.Version]"5.0.0") {
         return "Your PowerShell version ($psVersion) is not supported"
    }
}

The PSAvoidUsingWMICmdlet linter complain should be easy to fix by replacing the used functions as explained in https://learn.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/rules/avoidusingwmicmdlet?view=ps-modules In any case, focus on implementing the code using a checks array, as I think this may fix the other offense as well.

$checksPassed = $true
$mandatoryChecksPassed = $true

################################# Functions that conduct Pre-Install Checks #################################
Copy link
Member

Choose a reason for hiding this comment

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

Keep new headers consistent with existent ones:

Suggested change
################################# Functions that conduct Pre-Install Checks #################################
################################################################################
Functions that conduct Pre-Install Checks
################################################################################

install.ps1 Outdated
Comment on lines 178 to 181
if ($psVersion -lt [System.Version]"5.0.0") {
Write-Host "`t[!] You are using PowerShell version $psVersion. This is an old version and it is not supported" -ForegroundColor Red
Read-Host "Press any key to exit..."
exit 1
return $false
} else {
Write-Host "`t[+] Installing with PowerShell version $psVersion" -ForegroundColor Green
return $true
Copy link
Member

@Ana06 Ana06 Jul 16, 2025

Choose a reason for hiding this comment

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

Replace all code blocks like:

if (condition) {
    return $false
} else {
    return $true
}

by:

return (inverse_condition)

In this case:

return ($psVersion -lt [System.Version]"5.0.0")

Write-Host "[+] Checking if PowerShell version is compatible..."
if (-not (Test-PSVersion)){
Write-Host "`t[!] You are using PowerShell version $psVersion. This is an old version and it is not supported" -ForegroundColor Red
$mandatoryChecksPassed = $false
Copy link
Member

Choose a reason for hiding this comment

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

[nitpick] I would prefer we exist immediately in the command line if a mandatory check fails. This is very easy to do if you implement it with an array of checks as I proposed above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok but we cannot exit in the GUI, correct?
I wanted to implement the checks the same way in both cli and the GUI so that's why it doesn't exit when a mandatory check fails

Copy link
Member

Choose a reason for hiding this comment

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

That's a good argument. Let's keep it consistent then by performing all the checks in both. Note the order of the checks at the moment is not consistent. The use of an array of checks would remove current code duplication and ensure consistency in order.

@sara-rn
Copy link
Contributor Author

sara-rn commented Jul 16, 2025

This code is duplicating a lot of code and logic in both the UI and command line. This makes it difficult to maintain the code and keep the logic consistent. As said in , we have to ensure we do not duplicate code to make it easy to keep both views consistent. One way to implement this is to have a checks array with an entry per check with the following information: (name, description, function, allow_continue)

For example: ("PowerShell version >= 5", Check-PowerShell-Version, False)

The function should return an info message that we can display in the command line (as we do at the moment) and in the UI (extra info when a check fails, as the text below the check title). I think we can summarize some of the info messages if needed. For example:

function Check-PowerShell-Version {
    $psVersion = $PSVersionTable.PSVersion
    if ($psVersion -lt [System.Version]"5.0.0") {
         return "Your PowerShell version ($psVersion) is not supported"
    }
}

The PSAvoidUsingWMICmdlet linter complain should be easy to fix by replacing the used functions as explained in https://learn.microsoft.com/en-us/powershell/utility-modules/psscriptanalyzer/rules/avoidusingwmicmdlet?view=ps-modules In any case, focus on implementing the code using a checks array, as I think this may fix the other offense as well.

in the proposed function you would return a message,then how would you check if a Test-Powershell-Version (the linter doesn't allow a function that starts with Check-) is not successful? by checking if the return value is not equal to "" ??

@sara-rn
Copy link
Contributor Author

sara-rn commented Jul 16, 2025

@Ana06 just to clarify, you still want to display messages via Write-Host even if the GUI is enabled right? Following the Commando-vm example I didn't display any mesage in the console when the gui is allowed (only when the Test-Webconnection was invoked cause we invoked three times and we should display at least there are some checks going on (also we really need to check connectivity of the three domains? but that's a different issue). In my opinion this might be confusing for the user cause you need to look at both the window and the console.

@sara-rn
Copy link
Contributor Author

sara-rn commented Jul 16, 2025

Screenshot From 2025-07-16 16-30-32

@Ana06
Copy link
Member

Ana06 commented Jul 16, 2025

@sara-rn

in the proposed function you would return a message,then how would you check if a Test-Powershell-Version (the linter doesn't allow a function that starts with Check-) is not successful? by checking if the return value is not equal to "" ??

An empty string is the same as False for PowerShell:

$error_info = Test-PowerShell-Version
if ($error_info) {
  // The check has failed
}

you still want to display messages via Write-Host even if the GUI is enabled right? Following the Commando-vm example I didn't display any mesage in the console when the gui is allowed (only when the Test-Webconnection was invoked cause we invoked three times and we should display at least there are some checks going on (also we really need to check connectivity of the three domains? but that's a different issue). In my opinion this might be confusing for the user cause you need to look at both the window and the console.

No. My proposal was to display the same message in the console and the GUI (only in one of them), but to display the exact same message by returning it by the Test function if the check fails.

Regarding the internet check, you can perform the check before displaying the UI. This is similar to preloading the packages for next UI view. I think we do need to check the three domains to avoid issues, but we do not need to log them, just a message for failure is enough.

@Ana06
Copy link
Member

Ana06 commented Jul 16, 2025

@sara-rn thanks for the last screenshot, can you make the Installation Checks box (and the text inside) and the text in the check boxes a bit longer so that there is not that much empty space to the right? I would also be nice to have the text in every of the check boxes in a single line (by making the box a bit bigger) and to add some space above the buttons.

@sara-rn
Copy link
Contributor Author

sara-rn commented Jul 17, 2025

@sara-rn thanks for the last screenshot, can you make the Installation Checks box (and the text inside) and the text in the check boxes a bit longer so that there is not that much empty space to the right? I would also be nice to have the text in every of the check boxes in a single line (by making the box a bit bigger) and to add some space above the buttons.
@Ana06 let me know if this is ok

Screenshot From 2025-07-17 12-05-31

@sara-rn
Copy link
Contributor Author

sara-rn commented Jul 17, 2025

return "Your PowerShell version ($psVersion) is not supported"

@Ana06 how about the Hint messages we display? Shall we only display them in the console right?
for example:

			Write-Host "`t[!] Please run this script after updating your execution policy to unrestricted" -ForegroundColor Red
			Write-Host "`t[-] Hint: Set-ExecutionPolicy Unrestricted" -ForegroundColor Yellow

@Ana06
Copy link
Member

Ana06 commented Jul 17, 2025

@Ana06 how about the Hint messages we display? Shall we only display them in the console right?

No, my proposal was to display the exact same messages in both the console and the UI (just a single message), but shortening them a bit if needed. For example: Re-run the script after updating the execution policy with "Set-ExecutionPolicy Unrestricted". I think some of these messages are too long and I think keeping both views consistent is important.

@sara-rn
Copy link
Contributor Author

sara-rn commented Jul 18, 2025

Re-run the script after updating the execution policy with "Set-ExecutionPolicy Unrestricted"

Thanks @Ana06 for the clarification, then all these links to articles would disappear as you would only summarize it in a single line:

			Write-Host "`t[!] A minimum of 60 GB hard drive space is preferred. Please increase hard drive space of the VM, reboot, and retry install" -ForegroundColor Red
			Write-Host "`t[+] If you have multiple drives, you may change the tool installation location via the envrionment variable %RAW_TOOLS_DIR% in config.xml or GUI" -ForegroundColor Yellow
			Write-Host "`t[+] However, packages provided from the Chocolatey community repository will install to their default location" -ForegroundColor Yellow
			Write-Host "`t[+] See: https://stackoverflow.com/questions/19752533/how-do-i-set-chocolatey-to-install-applications-onto-another-drive" -ForegroundColor Yellow

or these knowledge articles:

Write-Host "`t[+] Hint: https://support.microsoft.com/en-us/windows/prevent-changes-to-security-settings-with-tamper-protection-31d51aaa-645d-408e-6ce7-8d7f8e593f87" -ForegroundColor Yellow
			Write-Host "`t[+] Hint: https://www.tenforums.com/tutorials/123792-turn-off-tamper-protection-windows-defender-antivirus.html" -ForegroundColor Yellow
			Write-Host "`t[+] Hint: https://github.com/jeremybeaume/tools/blob/master/disable-defender.ps1" -ForegroundColor Yellow
			Write-Host "`t[+] Hint: https://lazyadmin.nl/win-11/turn-off-windows-defender-windows-11-permanently/" -ForegroundColor Yellow
			Write-Host "`t[!] Please disable Windows Defender through Group Policy, reboot, and rerun installer" -ForegroundColor Red
			Write-Host "`t[+] Hint: https://stackoverflow.com/questions/62174426/how-to-permanently-disable-windows-defender-real-time-protection-with-gpo" -ForegroundColor Yellow
			Write-Host "`t[+] Hint: https://www.windowscentral.com/how-permanently-disable-windows-defender-windows-10" -ForegroundColor Yellow
			

@Ana06
Copy link
Member

Ana06 commented Jul 18, 2025

@sara-rn

@Ana06 for the clarification, then all these links to articles would disappear as you would only summarize it in a single line

Yes, what about moving all this information to the README and linking it if any of them fail? We have already some of this information in the README and keeping the information in sync is tricky. So I think having shorter messages in the installer (consistent between the UI and the console) and linking extra documentation that we can maintain in a single place is an enhancement.

@sara-rn sara-rn force-pushed the add-gui-preinstall-checks branch from 5c11551 to 46959a8 Compare July 19, 2025 11:39
@sara-rn
Copy link
Contributor Author

sara-rn commented Jul 19, 2025

Added a message when the installation cannot continue in both cli and gui:
Screenshot From 2025-07-19 13-39-55

@sara-rn
Copy link
Contributor Author

sara-rn commented Jul 19, 2025

@sara-rn

@Ana06 for the clarification, then all these links to articles would disappear as you would only summarize it in a single line

Yes, what about moving all this information to the README and linking it if any of them fail? We have already some of this information in the README and keeping the information in sync is tricky. So I think having shorter messages in the installer (consistent between the UI and the console) and linking extra documentation that we can maintain in a single place is an enhancement.

@Ana06 where in the GUI you want to add the link to the README? below the two checkboxes?

@sara-rn
Copy link
Contributor Author

sara-rn commented Jul 19, 2025

@sara-rn thanks for working on this enhacements!

This code is duplicating a lot of code and logic in both the UI and command line. This makes it difficult to maintain the code and keep the logic consistent. As said in , we have to ensure we do not duplicate code to make it easy to keep both views consistent. One way to implement this is to have a checks array with an entry per check with the following information: (name, description, function, allow_continue)

For example: ("PowerShell version >= 5", Check-PowerShell-Version, False)

The function should return an info message that we can display in the command line (as we do at the moment) and in the UI (extra info when a check fails, as the text below the check title). I think we can summarize some of the info messages if needed. For example:

function Check-PowerShell-Version {
    $psVersion = $PSVersionTable.PSVersion
    if ($psVersion -lt [System.Version]"5.0.0") {
         return "Your PowerShell version ($psVersion) is not supported"
    }
}

I implemented all the Check functions as you suggested, however I didn't implement the checks_array because it requires a lot of duplication in any case as there are three components in the GUI that are created per check and this cannot be done dynamically. If a check is not successful we have to manually show the $error_info in the label and change the color from grey to red, so it not only shows False but the error message gets updated.

@sara-rn
Copy link
Contributor Author

sara-rn commented Jul 19, 2025

Screenshot From 2025-07-19 13-59-31

@sara-rn sara-rn force-pushed the add-gui-preinstall-checks branch 7 times, most recently from 2c9168b to 58fc51b Compare July 22, 2025 10:28
@sara-rn sara-rn requested review from Ana06 and emtuls July 22, 2025 10:30
Copy link
Member

@Ana06 Ana06 left a comment

Choose a reason for hiding this comment

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

Please use an array of checks as proposed to remove current code duplication, making the code easy to maintain and ensuring consistency between both modes. Note that the order of the checks is important and the following must be performed first: powershell version, admin rights, execution policy unrestricted. This is not the case at the moment in the GUI.

The GUI views fails with the following error:

PS C:\Users\flare\Desktop> .\install.ps1 -password password
[+] Starting GUI to allow user to edit configuration file...
[+] Checking for Internet connectivity (google.com)... (mandatory)
[+] Checking for Internet connectivity (github.com)... (mandatory)
[+] Checking for Internet connectivity (raw.githubusercontent.com)... (mandatory)
The property 'Visible' cannot be found on this object. Verify that the property exists and can be set.
At C:\Users\flare\Desktop\install.ps1:838 char:8
+                 $BreakMyInstallLabel.Visible = $false
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : PropertyNotFound

Write-Host "[+] Checking if PowerShell version is compatible..."
if (-not (Test-PSVersion)){
Write-Host "`t[!] You are using PowerShell version $psVersion. This is an old version and it is not supported" -ForegroundColor Red
$mandatoryChecksPassed = $false
Copy link
Member

Choose a reason for hiding this comment

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

That's a good argument. Let's keep it consistent then by performing all the checks in both. Note the order of the checks at the moment is not consistent. The use of an array of checks would remove current code duplication and ensure consistency in order.

@sara-rn
Copy link
Contributor Author

sara-rn commented Aug 13, 2025

Appart from that I have noticed that the individual internet connectivity checks are still displayed to the console. Should we create an issue to remove them in a separate PR? Or do we think it is ok to display them? image
@Ana06 that's on purpose, I left it because it is quite slow to check connectivity of the three domains so it makes sense to display the user even in GUI that that the three check are taking place instead of not displaying anything.

@sara-rn sara-rn force-pushed the add-gui-preinstall-checks branch 4 times, most recently from 1dd4b04 to f6728b3 Compare August 14, 2025 12:25
@sara-rn
Copy link
Contributor Author

sara-rn commented Aug 14, 2025

@sara-rn the UI looks good to me now, thanks for the changes! But these changes break the installer in my Windows 11 VM, so we can't merge the PR as it will break the FLARE-VM installer for some users: image

The previous version of the installer was working correctly: image

@Ana06 It should work on Windows 11 but there seems to be a problem with this Windows Build. The specific line is using WMI $defender = Get-CimInstance -Namespace "root\Microsoft\Windows\Defender" -Class MSFT_MpPreference. It is equivalent to using the cmdlet Get-MpPreference that fails, in fact other cmdlets that query the status of Defender such as Get-MpComputerStatus also fails, that works in Windows 11, for example Get-MpComputerStatus | Select-Object -Property AntivirusEnabled, AMServiceEnabled, RealTimeProtectionEnabled which also fails with the error Get-MpComputerStatus : Provider load failure
so we have the option to simply run it between try / catch and therefore this check wouldn't work (option 1) or revert the implementation to what we had before that works well in this Windows 11 build (option 2), however it doesn't fully work on Windows 10 (it would raise an exception) which would be:
Option 1

function Test-DefenderAndTamperProtection {
	
    try { 
		$defender = Get-CimInstance -Namespace "root\Microsoft\Windows\Defender" -Class MSFT_MpPreference
		if ($defender.DisableRealtimeMonitoring) {
			if (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Features" -Name "TamperProtection" -ea 0) {
				if ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Features" -Name "TamperProtection").TamperProtection -eq 5){
					return "Windows Defender and Tamper Protection are enabled"
				}
			}
		}
	} catch {
		return "It was not possible to determine if TamperProtection and Defender are enabled"
	}
}

Option 2

function Test-DefenderAndTamperProtection {
	try {
        $tpEnabled = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Features" -Name "TamperProtection" -ErrorAction Stop
        if ($tpEnabled.TamperProtection -eq 5) {
            return "Disable Tamper Protection, reboot, and rerun installer"
        } 
    } catch {
		return "Tamper Protection is either not enabled or not detected"
	}
	$defender = Get-Service -Name WinDefend -ea 0
	if ($null -ne $defender) {
        if ($defender.Status -eq "Running") {
			 return "Disable Windows Defender through Group Policy, reboot, and rerun installer"
	    }
	}
}

See screenshot from the old installer running in my Windows 10:
Screenshot From 2025-08-14 17-14-12

@Ana06
Copy link
Member

Ana06 commented Aug 15, 2025

@sara-rn I am not sure why you have changed the implementation of the Windows Defender Check and I do not understand why you say that reverting to the previous implementation is not an option because it raises an exception. Can you please provide more details? What exception exactly do you get? The previous version of the installer works for me.

@sara-rn can you also please explain in detail what every of the lines of the following code does? The code is not clear to me.

    $defender = Get-CimInstance -Namespace "root\Microsoft\Windows\Defender" -Class MSFT_MpPreference
    if ($defender.DisableRealtimeMonitoring) {
        if (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Features" -Name "TamperProtection" -ea 0) {
            if ((Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Defender\Features" -Name "TamperProtection").TamperProtection -eq 5){
			    return "Windows Defender and Tamper Protection are enabled"
			}
		}
    }

@Ana06
Copy link
Member

Ana06 commented Aug 15, 2025

@sara-rn

itt should work on Windows 11 but there seems to be a problem with this Windows Build.

I have done some more testing, there is not a problem with the build, it is caused by disabling Windows Defender and is expected. In Windows 11 10.0.26100 (which I downloaded from https://www.microsoft.com/en-us/software-download/windows11) the Get-CimInstance -Namespace "root\Microsoft\Windows\Defender" -Class MSFT_MpPreference does work. After I disable Windows Defender, It does not work anymore. This means that you could use Get-CimInstance -Namespace "root\Microsoft\Windows\Defender" -Class MSFT_MpPreference as a way to check if Windows Defender is installed, but you can't assume the command works.

In general (and independently of fixing this check which needs to be done), adding a try-catch to every of the checks is a good idea as it would ensure we display that we are not sure if the check is verified or not without failing the installer.

Please as said in my previous comment, explain why it was needed to change the implementation of the Windows Defender check and provide the error you get in Windows 10.

@sara-rn
Copy link
Contributor Author

sara-rn commented Aug 15, 2025

This is the error I get with the old implementation when I ran it in my Windows 10 VM after removing the try/catch:

Get-ItemProperty : Cannot find path 'HKLM:\SOFTWARE\Microsoft\Windows Defender\Features' because it does not exist.
At C:\Users\flaretest\Desktop\install_cat.ps1:192 char:22
+ ... tpEnabled = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows  ...

@Ana06
Copy link
Member

Ana06 commented Aug 15, 2025

I have confirmed with @sara-rn that that VM has Windows Defender enabled in the VM she gets the error and consequently the current code works as expected (as the check fails because Windows Defender may be enabled). I propose the following in order to merge this PR:

  • Leave the original Windows Defender Check as it was in the previous installer
  • Add a try-catch in all the checks are return Unable to ... is the check raises an exception to prevent the installer from failing
  • Ensure there is a clean commit history (single commit with a nice message)

I have added the error @sara-rn got to #722 (comment) where we are discussing improving the Windows Defender check. This is completely independent from this PR if we do not modify the check logic here.

@sara-rn sara-rn force-pushed the add-gui-preinstall-checks branch 3 times, most recently from 813e586 to a89be79 Compare August 15, 2025 12:17
Copy link
Member

@Ana06 Ana06 left a comment

Choose a reason for hiding this comment

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

Some minor changes requests including fixing typos introduced in the last change and keeping the messages consitent.

@sara-rn sara-rn force-pushed the add-gui-preinstall-checks branch 2 times, most recently from 4e63578 to b1ed672 Compare August 16, 2025 10:55
@sara-rn sara-rn requested a review from Ana06 August 16, 2025 10:56
@sara-rn sara-rn force-pushed the add-gui-preinstall-checks branch 5 times, most recently from e128bb4 to 30db070 Compare August 16, 2025 11:08
A new window will automatically check for preinstall checks, which
will allow the user to continue if the checks are not mandatory.
Two new global variables `$mandatoryChecksPassed` and `$checksPassed` are created
to determine if any of the mandatory or optional checks are passed or not.
If the mandatory checks are not passed the installation will display a message that
cannot continue..
New functions have been created for each of the preinstall checks.
Each function contains a try/catch to capture exceptions
Both in cli or gui mode the same functions must be invoked.
Install gui window is displayed inmediatelly before Boxstarter is installed
Preload gui controls of the custom configuration window
Add a checkbox in case some non-mandatory checks are not successful
Add a second checkbox to confirm a snapshot has been taken
Only display the first checkbox if some checks were not succesful
@sara-rn sara-rn force-pushed the add-gui-preinstall-checks branch from 2327521 to 19bac92 Compare August 16, 2025 11:12
@sara-rn
Copy link
Contributor Author

sara-rn commented Aug 16, 2025

Screenshot From 2025-08-16 13-15-55

Copy link
Member

@Ana06 Ana06 left a comment

Choose a reason for hiding this comment

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

Tested locally, everything looks good. Thanks for all the work @sara-rn to add the checks to the UI 🚀

@Ana06 Ana06 merged commit 3825725 into mandiant:main Aug 20, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💎 enhancement It is working, but it could be better

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add GUI for checks

2 participants