diff --git a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam.ps1 b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam.ps1 deleted file mode 100644 index 0f14d60e..00000000 --- a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam.ps1 +++ /dev/null @@ -1,147 +0,0 @@ -<# -.SYNOPSIS -This script will iterate over the Atomic Red Team yaml files, create objects for each test. -The aim is to allow defenders to excercise MITRE ATT&CK Techniques to test defenses. - -Function: Invoke-AtomicRedTeam -Author: Casey Smith @subTee -License: http://opensource.org/licenses/MIT -Required Dependencies: powershell-yaml , Install-Module powershell-yaml #https://github.com/cloudbase/powershell-yaml -Optional Dependencies: None -Version: 1.0 - -.DESCRIPTION -Create Atomic Tests from yaml files described in Atomic Red Team. https://github.com/redcanaryco/atomic-red-team - -.EXAMPLE -Convert Single Yaml File to Technique Object -$T1117 = Get-AtomicTechnique -Path ..\..\atomics\T1117\T1117.yaml -.EXAMPLE -Generate the Atomic Tests For A Given Technique, don't execute. -Invoke-AtomicTest $T1117 -GenerateOnly -.EXAMPLE -Execute the Atomic Tests For A Given Technique -$T1117 = Get-AtomicTechnique -Path ..\..\atomics\T1117\T1117.yaml -Invoke-AtomicTest $T1117 -.NOTES -This script converts Atomic Tests Expressed in YAML into PowerShell Objects. - -.LINK -Blog: http://subt0x11.blogspot.com/2018/08/invoke-atomictest-automating-mitre-att.html -Github repo: https://github.com/redcanaryco/atomic-red-team - -#> - -function Confirm-Dependencies { - - if (Get-Module -ListAvailable -Name 'powershell-yaml') { - Write-Host "PowerShell-Yaml Module Exists. Good To Go." -Foreground Green - } - else { - $Title = "PowerShell-Yaml Is Not Installed" - $Message = "Do you want to Install?" - $Yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Y-Yes", "Yes" - $No = New-Object System.Management.Automation.Host.ChoiceDescription "&N-No", "No" - $Options = [System.Management.Automation.Host.ChoiceDescription[]]($Yes, $No) - $Result = $Host.ui.PromptForChoice($Title, $Message, $Options, 0) - - switch ($Result) { - 0 { Install-Module -Name powershell-yaml } - 1 { Write-Host "Atomic Red Team Requires PowerShell-Yaml Exiting"; exit} - } - } -} - -function Get-AtomicTechnique { - [CmdletBinding()] - Param( - [string] - $Path - ) - # Returns A HashTable For Each File Passed In - BEGIN { } - PROCESS { - foreach ($File in $Path) { - $parsedYaml = (ConvertFrom-Yaml (Get-Content $File -Raw )) - Write-Output $parsedYaml - } - } - END { } -} - -function Invoke-AtomicTest { - [CmdletBinding()] - Param( - [System.Collections.Hashtable] - $AtomicTechnique, - - [switch] - $GenerateOnly - ) - BEGIN {} - PROCESS { - foreach ($Technique in $AtomicTechnique) { - $AtomicTest = $Technique.atomic_tests - - foreach ($Test in $AtomicTest) { - #Only Process Windows Tests For Now - if (-Not $Test.supported_platforms.Contains('windows')) { - return - } - - #Reject Manual Tests - if ($Test.executor.name.Contains('manual')) { - return - } - - Write-Host ("[********BEGIN TEST*******]`n" + - $Technique.display_name.ToString(), $Technique.attack_technique.ToString()) - Write-Host $Test.name.ToString() - Write-Host $Test.description.ToString() - - $finalCommand = $Test.executor.command - if ($Test.input_arguments.Count -gt 0) { - #Replace InputArgs with default values - $InputArgs = [Array]($Test.input_arguments.Keys).Split(" ") - $InputDefaults = [Array]($Test.input_arguments.Values | ForEach-Object {$_.default }).Split(" ") - - for ($i = 0; $i -lt $InputArgs.Length; $i++) { - $findValue = '#{' + $InputArgs[$i] + '}' - $finalCommand = $finalCommand.Replace($findValue, $InputDefaults[$i]) - } - } - - #Get Executor and Build Command Script - if ($GenerateOnly) { - Write-Host $finalCommand -Foreground Green - } - else { - switch ($Test.executor.name) { - - "command_prompt" { - Write-Host "Command Prompt:`n $finalCommand" -Foreground Green - $execCommand = $finalCommand.Split("`n") - $execCommand | ForEach-Object { Invoke-Expression "cmd.exe /c `"$_`" " } - break - } - "powershell" { - Write-Host "PowerShell`n $finalCommand" -Foreground Cyan - $execCommand = "Invoke-Command -ScriptBlock {$finalCommand}" - Invoke-Expression $execCommand - break - } - default { - "Unable to generate or execute the command line properly." - break - } - } - } - } - - Write-Host "[!!!!!!!!END TEST!!!!!!!]`n`n" -Foreground Yellow - } - } - END { } -} - -Confirm-Dependencies diff --git a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam.psd1 b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam.psd1 new file mode 100644 index 00000000..c1bba7f6 --- /dev/null +++ b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam.psd1 @@ -0,0 +1,123 @@ +# +# Module manifest for module 'Invoke-AtomicRedTeam' +# +# Generated by: Josh Rickard +# +# Generated on: 09/13/2018 +# + +@{ + + # Script module or binary module file associated with this manifest. + RootModule = 'Invoke-AtomicRedTeam.psm1' + + # Version number of this module. + ModuleVersion = '1.0.0.0' + + # Supported PSEditions + CompatiblePSEditions = @('Desktop') + + # ID used to uniquely identify this module + GUID = '8f492621-18f8-432e-9532-b1d54d3e90bd' + + # Author of this module + Author = 'Casey Smith @subTee, Josh Rickard @MS_dministrator' + + # Company or vendor of this module + CompanyName = 'Red Canary' + + # Copyright statement for this module + Copyright = '(c) 2018 Red Canary. All rights reserved.' + + # Description of the functionality provided by this module + Description = 'A PowerShell module that runs Atomic Red Team tests from yaml definition files.' + + # Minimum version of the Windows PowerShell engine required by this module + PowerShellVersion = '3.0' + + # Name of the Windows PowerShell host required by this module + # PowerShellHostName = '' + + # Minimum version of the Windows PowerShell host required by this module + # PowerShellHostVersion = '' + + # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # DotNetFrameworkVersion = '' + + # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. + # CLRVersion = '' + + # Processor architecture (None, X86, Amd64) required by this module + # ProcessorArchitecture = '' + + # Modules that must be imported into the global environment prior to importing this module + # RequiredModules = @() + + # Assemblies that must be loaded prior to importing this module + # RequiredAssemblies = @() + + # Script files (.ps1) that are run in the caller's environment prior to importing this module. + # ScriptsToProcess = @() + + # Type files (.ps1xml) to be loaded when importing this module + # TypesToProcess = @() + + # Format files (.ps1xml) to be loaded when importing this module + # FormatsToProcess = @() + + # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess + # NestedModules = @() + + # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. + FunctionsToExport = @() + + # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = '*' + + # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. + AliasesToExport = @() + + # DSC resources to export from this module + # DscResourcesToExport = @() + + # List of all modules packaged with this module + # ModuleList = @() + + # List of all files packaged with this module + # FileList = @() + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('Red Canary', 'Atomic', 'Red Team', 'MITRE', 'ATT&CK', 'ART') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/redcanaryco/atomic-red-team/blob/master/LICENSE.txt' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/redcanaryco/atomic-red-team' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + ReleaseNotes = 'http://subt0x11.blogspot.com/2018/08/invoke-atomictest-automating-mitre-att.html' + + } # End of PSData hashtable + + } # End of PrivateData hashtable + + # HelpInfo URI of this module + # HelpInfoURI = '' + + # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. + # DefaultCommandPrefix = '' + +} + diff --git a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam.psm1 b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam.psm1 new file mode 100644 index 00000000..212fce53 --- /dev/null +++ b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam.psm1 @@ -0,0 +1,17 @@ +#requires -Version 3.0 + +#Get public and private function definition files. +$Public = @( Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -Recurse -ErrorAction SilentlyContinue ) +$Private = @( Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -Recurse -ErrorAction SilentlyContinue ) + +#Dot source the files +Foreach ($import in @($Public + $Private)) { + Try { + . $import.fullname + } + Catch { + Write-Error -Message "Failed to import function $($import.fullname): $_" + } +} + +Export-ModuleMember -Function $Public.Basename \ No newline at end of file diff --git a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Private/.gitkeep b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Private/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Confirm-Dependencies.ps1 b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Confirm-Dependencies.ps1 new file mode 100644 index 00000000..78dbcc85 --- /dev/null +++ b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Confirm-Dependencies.ps1 @@ -0,0 +1,53 @@ +<# +.SYNOPSIS + Confirms and imports Invoke-AtomicRedTeam dependencies +.DESCRIPTION + Confirms and imports Invoke-AtomicRedTeam dependencies. You can specify an alternative ModulePath of powershell-yaml module. +.EXAMPLE Default Confirm-Dependencies + PS/> Confirm-Dependencies +.EXAMPLE Specify ModulePath of powershell-yaml module + PS/> Confirm-Dependencies -ModulePath C:\some\path\to\module\powershell-yaml.psm1 +.NOTES + Create Atomic Tests from yaml files described in Atomic Red Team. https://github.com/redcanaryco/atomic-red-team +.LINK + Blog: http://subt0x11.blogspot.com/2018/08/invoke-atomictest-automating-mitre-att.html + Github repo: https://github.com/redcanaryco/atomic-red-team +#> +function Confirm-Dependencies { + [CmdletBinding(DefaultParameterSetName = 'dependencies', + SupportsShouldProcess = $true, + ConfirmImpact = 'Medium')] + Param( + [Parameter(Mandatory = $false, + Position = 0, + ValueFromPipelineByPropertyName = $true, + ParameterSetName = 'dependencies')] + [ValidateNotNullOrEmpty()] + [string] + $ModulePath + ) + + Write-Verbose -Message 'Checking whether powershell-yaml is installed' + + try { + if ($PSBoundParameters.ContainsKey('ModulePath')) { + Import-Module $ModulePath + } + else { + Write-Error -ErrorRecord $Error[0] -RecommendedAction 'Could not import the provided module. Please ensure you can access the ModulePath location.' -ErrorAction Stop + } + + if (-not(Get-Module -Name 'powershell-yaml' -ListAvailable)) { + if ($pscmdlet.ShouldProcess("PowerShell-Yaml Module", "Install required module")) { + Install-Module -Name powershell-yaml -Force + Write-Verbose -Message 'Successfully installed powershell-yaml module' + + Write-Verbose -Message 'Importing powershell-yaml module' + Import-Module -Name powershell-yaml -Force + } + } + } + catch { + Write-Error -ErrorRecord $Error[0] -RecommendedAction 'Please install powershell-yaml before continuing' -ErrorAction Stop + } +} \ No newline at end of file diff --git a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Get-AtomicTechnique.ps1 b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Get-AtomicTechnique.ps1 new file mode 100644 index 00000000..491e5e5f --- /dev/null +++ b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Get-AtomicTechnique.ps1 @@ -0,0 +1,42 @@ +<# +.SYNOPSIS + Get Atomic Technique +.DESCRIPTION + Gets Atomic Technique(s) based on the provided path +.EXAMPLE Get Atomic Technique + PS/> $T1117 = Get-AtomicTechnique -Path ..\..\atomics\T1117\T1117.yaml +.NOTES + Create Atomic Tests from yaml files described in Atomic Red Team. https://github.com/redcanaryco/atomic-red-team +.LINK + Blog: http://subt0x11.blogspot.com/2018/08/invoke-atomictest-automating-mitre-att.html + Github repo: https://github.com/redcanaryco/atomic-red-team +#> +function Get-AtomicTechnique { + [CmdletBinding(DefaultParameterSetName = 'technique', + SupportsShouldProcess = $true, + PositionalBinding = $false, + ConfirmImpact = 'Medium')] + Param( + [Parameter(Mandatory = $true, + Position = 0, + ValueFromPipelineByPropertyName = $true, + ParameterSetName = 'technique')] + [ValidateNotNullOrEmpty()] + [string] + $Path + ) + + Write-Debug -Message "Getting atomic technique from $Path" + + Process + { + Write-Verbose -Message 'Attempting to convert files from yaml' + foreach ($file in $Path) { + if ($pscmdlet.ShouldProcess($file, 'Converting yaml file')) { + Write-Verbose -Message "Converting $file from Yaml"] + $parsedYaml = ConvertFrom-Yaml (Get-Content $file -Raw) + Write-Output $parsedYaml + } + } + } +} \ No newline at end of file diff --git a/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Invoke-AtomicTest.ps1 b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Invoke-AtomicTest.ps1 new file mode 100644 index 00000000..050f17d9 --- /dev/null +++ b/execution-frameworks/Invoke-AtomicRedTeam/Invoke-AtomicRedTeam/Public/Invoke-AtomicTest.ps1 @@ -0,0 +1,138 @@ +<# +.SYNOPSIS + Invokes provided Atomic test(s) +.DESCRIPTION + Invokes provided Atomic tests(s). Optionally, you can specify if you want to generate Atomic test(s) only. +.EXAMPLE Invokes Atomic Test + PS/> $T1117 = Get-AtomicTechnique -Path ..\..\atomics\T1117\T1117.yaml + PS/> Invoke-AtomicTest $T1117 +.EXAMPLE Generate Atomic Test + PS/> $T1117 = Get-AtomicTechnique -Path ..\..\atomics\T1117\T1117.yaml + PS/> Invoke-AtomicTest $T1117 -GenerateOnly +.NOTES + Create Atomic Tests from yaml files described in Atomic Red Team. https://github.com/redcanaryco/atomic-red-team +.LINK + Blog: http://subt0x11.blogspot.com/2018/08/invoke-atomictest-automating-mitre-att.html + Github repo: https://github.com/redcanaryco/atomic-red-team +#> +function Invoke-AtomicTest { + [CmdletBinding(DefaultParameterSetName = 'technique', + SupportsShouldProcess = $true, + PositionalBinding = $false, + ConfirmImpact = 'Medium')] + Param( + [Parameter(Mandatory = $true, + Position = 0, + ValueFromPipelineByPropertyName = $true, + ParameterSetName = 'technique')] + [ValidateNotNullOrEmpty()] + [System.Collections.Hashtable] + $AtomicTechnique, + + [Parameter(Mandatory = $false, + Position = 1, + ValueFromPipelineByPropertyName = $true, + ParameterSetName = 'technique')] + [switch] + $GenerateOnly + ) + BEGIN { } # Intentionally left blank and can be removed + PROCESS { + Write-Verbose -Message 'Attempting to run Atomic Techniques' + + $techniqueCount = 0 + foreach ($technique in $AtomicTechnique) { + + $techniqueCount++ + + $props = @{ + Activity = "Running $($technique.display_name.ToString()) Technique" + Status = 'Progress:' + PercentComplete = ($techniqueCount / ($AtomicTechnique).Count * 100) + } + Write-Progress @props + + Write-Debug -Message "Gathering tests for Technique $technique" + + $testCount = 0 + foreach ($test in $technique.atomic_tests) { + $testCount++ + + $props = @{ + Activity = 'Running Atomic Tests' + Status = 'Progress:' + PercentComplete = ($testCount / ($technique.atomic_tests).Count * 100) + } + Write-Progress @props + + Write-Verbose -Message 'Determining tests for Windows' + + if (-Not $test.supported_platforms.Contains('windows')) { + Write-Verbose -Message 'Unable to run non-Windows tests' + continue + } + + Write-Verbose -Message 'Determining manual tests' + + if ($test.executor.name.Contains('manual')) { + Write-Verbose -Message 'Unable to run manual tests' + continue + } + + Write-Information -MessageData ("[********BEGIN TEST*******]`n" + + $technique.display_name.ToString(), $technique.attack_technique.ToString()) -Tags 'Details' + + Write-Information -MessageData $test.name.ToString() -Tags 'Details' + Write-Information -MessageData $test.description.ToString() -Tags 'Details' + + Write-Debug -Message 'Gathering final Atomic test command' + + $finalCommand = $test.executor.command + + if ($test.input_arguments.Count -gt 0) { + Write-Verbose -Message 'Replacing inputArgs with default values' + $inputArgs = [Array]($test.input_arguments.Keys).Split(" ") + $inputDefaults = [Array]($test.input_arguments.Values | ForEach-Object {$_.default }).Split(" ") + + for ($i = 0; $i -lt $inputArgs.Length; $i++) { + $findValue = '#{' + $inputArgs[$i] + '}' + $finalCommand = $finalCommand.Replace($findValue, $inputDefaults[$i]) + } + } + + Write-Debug -Message 'Getting executor and build command script' + + if ($GenerateOnly) { + Write-Information -MessageData $finalCommand -Tags 'Command' + } + else { + Write-Verbose -Message 'Invoking Atomic Tests using defined executor' + if ($pscmdlet.ShouldProcess(($test.name.ToString()), 'Execute Atomic Test')) { + switch ($test.executor.name) { + "command_prompt" { + Write-Information -MessageData "Command Prompt:`n $finalCommand" -Tags 'AtomicTest' + $execCommand = $finalCommand.Split("`n") + $execCommand | ForEach-Object { Invoke-Expression "cmd.exe /c `"$_`" " } + continue + } + "powershell" { + Write-Information -MessageData "PowerShell`n $finalCommand" -Tags 'AtomicTest' + $execCommand = "Invoke-Command -ScriptBlock {$finalCommand}" + Invoke-Expression $execCommand + continue + } + default { + Write-Warning -Message "Unable to generate or execute the command line properly." + continue + } + } # End of executor switch + } # End of if ShouldProcess block + } # End of else statement + } # End of foreach Test in single Atomic Technique + + Write-Information -MessageData "[!!!!!!!!END TEST!!!!!!!]`n`n" -Tags 'Details' + + } # End of foreach Technique in Atomic Tests + } # End of PROCESS block + END { } # Intentionally left blank and can be removed +} \ No newline at end of file diff --git a/execution-frameworks/Invoke-AtomicRedTeam/README.md b/execution-frameworks/Invoke-AtomicRedTeam/README.md index d67f9190..c9db478e 100644 --- a/execution-frameworks/Invoke-AtomicRedTeam/README.md +++ b/execution-frameworks/Invoke-AtomicRedTeam/README.md @@ -1,29 +1,85 @@ -Requires Installation of PowerShell-Yaml +# Invoke-AtomicRedTeam - Install-Module powershell-yaml +## Requires Installation of PowerShell-Yaml + +```powershell +Install-Module -Name powershell-yaml +``` For Additional Details: [PowerShell-Yaml](https://github.com/cloudbase/powershell-yaml) -Basic usage Examples: +## Basic usage Examples -- Load PowerShell Script: - `Import-Module .\Invoke-AtomicRedTeam.ps1` +#### Load PowerShell Script -- Execute Single Test: +```powershell +Import-Module .\Invoke-AtomicRedTeam.psm1` +``` - `$T1117 = Get-AtomicTechnique -Path ..\..\atomics\T1117\T1117.yaml` - `Invoke-AtomicTest $T1117` +#### Execute Single Test -- Generate All Tests +```powershell +$T1117 = Get-AtomicTechnique -Path ..\..\atomics\T1117\T1117.yaml +Invoke-AtomicTest $T1117 +``` - `[System.Collections.HashTable]$AllAtomicTests = @{};` - `$AtomicFilePath = 'C:\AtomicRedTeam\atomics\';` - `Get-Childitem $AtomicFilePath -Recurse -Filter *.yaml -File | ForEach-Object {` - `$currentTechnique = [System.IO.Path]::GetFileNameWithoutExtension($_.FullName);` - `$parsedYaml = (ConvertFrom-Yaml (Get-Content $_.FullName -Raw ));` - `$AllAtomicTests.Add($currentTechnique, $parsedYaml); }` - `$AllAtomicTests.GetEnumerator() | %{ Invoke-AtomicTest $_.Value -GenerateOnly }` +## Additional Examples +If you would like output when running tests using the following: -- Feedback Welcome +#### Informational Stream + +```powershell +Invoke-AtomicTest $T1117 -InformationAction Continue +``` + +#### Verbose Stream + +```powershell +Invoke-AtomicTest $T1117 -Verbose +``` + +#### Debug Stream + +```powershell +Invoke-AtomicTest $T1117 -Debug +``` + +#### WhatIf + +If you would like to see what would happen without running the test + +```powershell +Invoke-AtomicTest $T1117 -WhatIf +``` + +#### Confirm + +To run all tests without confirming them run using the Confirm switch to false + +```powershell +Invoke-AtomicTest $T1117 -Confirm:$false +``` + +Or you can set your `$ConfirmPreference` to 'Medium' + +```powershell +$ConfirmPreference = 'Medium' +Invoke-AtomicTest $T1117 +``` + +## Generate All Tests + +```powershell +[System.Collections.HashTable]$AllAtomicTests = @{} +$AtomicFilePath = 'C:\AtomicRedTeam\atomics\' +Get-ChildItem $AtomicFilePath -Recurse -Filter *.yaml -File | ForEach-Object { + $currentTechnique = [System.IO.Path]::GetFileNameWithoutExtension($_.FullName) + $parsedYaml = (ConvertFrom-Yaml (Get-Content $_.FullName -Raw )) + $AllAtomicTests.Add($currentTechnique, $parsedYaml); +} +$AllAtomicTests.GetEnumerator() | Foreach-Object { Invoke-AtomicTest $_.Value -GenerateOnly } +``` + +### Feedback Welcome diff --git a/execution-frameworks/Invoke-AtomicRedTeam/Tests/Invoke-AtomicRedTeam.Tests.ps1 b/execution-frameworks/Invoke-AtomicRedTeam/Tests/Invoke-AtomicRedTeam.Tests.ps1 new file mode 100644 index 00000000..00bed149 --- /dev/null +++ b/execution-frameworks/Invoke-AtomicRedTeam/Tests/Invoke-AtomicRedTeam.Tests.ps1 @@ -0,0 +1,72 @@ +$TemplatePowerShellModule = 'Invoke-AtomicRedTeam' + +$here = "$(Split-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) -Parent)\$TemplatePowerShellModule" + +Describe "$TemplatePowerShellModule PowerShell Module Tests" { + Context 'Module Setup' { + It "has the root module $TemplatePowerShellModule.psm1" { + "$here\$TemplatePowerShellModule.psm1" | Should -Exist + } + It "has the manifest file $TemplatePowerShellModule.psd1" { + "$here\$TemplatePowerShellModule.psd1" | should exist + } + It "$TemplatePowerShellModule has functions" { + "$here\Public\*.ps1" | Should exist + } + It "$TemplatePowerShellModule is valid PowerShell Code" { + $psFile = Get-Content -Path "$here\$TemplatePowerShellModule.psm1" -ErrorAction Stop + $errors = $null + $null = [System.Management.Automation.PSParser]::Tokenize($psFile, [ref]$errors) + $errors.count | Should be 0 + } + } +} + +$pubFunctions = ('Confirm-Dependencies', 'Get-AtomicTechnique', 'Invoke-AtomicTest') + +$folders = ( 'Public') + +foreach ($folder in $folders) { + Describe 'Folders Tests' { + It "$folder should exist" { + "$here\$Folder" | Should Exist + } + } +} + +Describe 'Function Tests' { + foreach ($function in $pubFunctions) { + Context 'Public Functions' { + It "$function.ps1 should exist" { + "$here\Public\$function.ps1" | Should Exist + } + It "$function.ps1 should have help block" { + "$here\Public\$function.ps1" | Should -FileContentMatch '<#' + "$here\Public\$function.ps1" | Should -FileContentMatch '#>' + } + It "$function.ps1 should have a SYNOPSIS section in the help block" { + "$here\Public\$function.ps1" | Should -FileContentMatch '.SYNOPSIS' + } + It "$function.ps1 should have a DESCRIPTION section in the help block" { + "$here\Public\$function.ps1" | Should -FileContentMatch '.DESCRIPTION' + } + It "$function.ps1 should have a EXAMPLE section in the help block" { + "$here\Public\$function.ps1" | Should -FileContentMatch '.EXAMPLE' + } + It "$function.ps1 should be an advanced function" { + "$here\Public\$function.ps1" | Should -FileContentMatch 'function' + "$here\Public\$function.ps1" | Should -FileContentMatch 'CmdLetBinding' + "$here\Public\$function.ps1" | Should -FileContentMatch 'param' + } + It "$function.ps1 should contain Write-Verbose blocks" { + "$here\Public\$function.ps1" | Should -FileContentMatch 'Write-Verbose' + } + It "$function.ps1 is valid PowerShell code" { + $psFile = Get-Content -Path "$here\Public\$function.ps1" -ErrorAction Stop + $errors = $null + $null = [System.Management.Automation.PSParser]::Tokenize($psFile, [ref]$errors) + $errors.count | Should be 0 + } + }# Context Public Function Tests + } # end of Public function foreach +} # end of describe block \ No newline at end of file