Skip to content

Commit

Permalink
test: 🧪 Add tests for missing incidentConfiguration container
Browse files Browse the repository at this point in the history
  • Loading branch information
Fabian Bader committed Aug 6, 2024
1 parent cd4a4f8 commit d84db20
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 1 deletion.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ This way the following KQL query will be converted...

## Changelog

### 2.4.1
* FIX: Handle error if `incidentConfiguration` section is missing from source YAML in `Convert-SentinelARYamlToArm` when using `-DisableIncidentCreation`

### 2.4.0
* FEATURE: Support for MITRE sub-Techniques and update default ARM version to `2024-01-01-preview`
Thanks to [@Konverto-MartinGasser)](https://github.com/Konverto-MartinGasser) SentinelARConverter now supports MITRE sub-techniques which were introduced in ARM template version `2023-12-01-preview`.
Expand Down
2 changes: 1 addition & 1 deletion src/SentinelARConverter.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
RootModule = 'SentinelARConverter.psm1'

# Version number of this module.
ModuleVersion = '2.4.0'
ModuleVersion = '2.4.1'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand Down
25 changes: 25 additions & 0 deletions tests/Convert-SentinelARYamlToArm.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ param(
[String]
$exampleScheduledParameterFilePath = "./tests/examples/ScheduledParam.params.yaml",
[Parameter()]
[String]
$exampleIncidentConfigurationMissingFilePath = "./tests/examples/IncidentConfigurationMissing.yaml",
[Parameter()]
[Switch]
$RetainTestFiles = $false
)
Expand Down Expand Up @@ -439,6 +442,28 @@ Describe "Convert-SentinelARYamlToArm" {
}
}

Context "Scheduled with disabled incident creation and incidentConfiguration container missing" {
BeforeAll {
Copy-Item -Path $exampleIncidentConfigurationMissingFilePath -Destination "TestDrive:/Scheduled.yaml" -Force
Convert-SentinelARYamlToArm -Filename "TestDrive:/Scheduled.yaml" -OutFile "TestDrive:/Scheduled.json" -DisableIncidentCreation
$armTemplate = Get-Content -Path "TestDrive:/Scheduled.json" -Raw | ConvertFrom-Json
}

AfterEach {
if ( -not $RetainTestFiles) {
Remove-Item -Path "TestDrive:/*" -Include *.json -Force
}
}

It "Should have the incident creation disabled" {
$armTemplate.resources[0].properties.incidentConfiguration.createIncident | Should -Be $false
}

It "Should not fail when incidentConfiguration is missing" {
{ Convert-SentinelARYamlToArm -Filename "TestDrive:/Scheduled.yaml" -OutFile "TestDrive:/Scheduled.json" -DisableIncidentCreation } | Should -Not -Throw
}
}

Context "Scheduled with parameter file provided" {
BeforeAll {
Copy-Item -Path $exampleScheduledWithVariablesFilePath -Destination "TestDrive:/Scheduled.yaml" -Force
Expand Down
105 changes: 105 additions & 0 deletions tests/examples/IncidentConfigurationMissing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
id: bb616d82-108f-47d3-9dec-9652ea0d3bf6
name: Account Created and Deleted in Short Timeframe
description: |
'Search for user principal name (UPN) events. Look for accounts created and then deleted in under 24 hours. Attackers may create an account for their use, and then remove the account when no longer needed.
Ref : https://docs.microsoft.com/azure/active-directory/fundamentals/security-operations-user-accounts#short-lived-account'
severity: High
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
- InitialAccess
relevantTechniques:
- T1078.004
tags:
- AADSecOpsGuide
query: |
let queryfrequency = 1h;
let queryperiod = 1d;
AuditLogs
| where TimeGenerated > ago(queryfrequency)
| where OperationName =~ "Delete user"
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type == "User"
| extend TargetUserPrincipalName = extract(@'([a-f0-9]{32})?(.*)', 2, tostring(TargetResource.userPrincipalName))
)
| extend DeletedByApp = tostring(InitiatedBy.app.displayName),
DeletedByAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId),
DeletedByUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName),
DeletedByAadUserId = tostring(InitiatedBy.user.id),
DeletedByIPAddress = tostring(InitiatedBy.user.ipAddress)
| project Deletion_TimeGenerated = TimeGenerated, TargetUserPrincipalName, DeletedByApp, DeletedByAppServicePrincipalId, DeletedByUserPrincipalName, DeletedByAadUserId, DeletedByIPAddress,
Deletion_AdditionalDetails = AdditionalDetails, Deletion_InitiatedBy = InitiatedBy, Deletion_TargetResources = TargetResources
| join kind=inner (
AuditLogs
| where TimeGenerated > ago(queryperiod)
| where OperationName =~ "Add user"
| mv-apply TargetResource = TargetResources on
(
where TargetResource.type == "User"
| extend TargetUserPrincipalName = trim(@'"',tostring(TargetResource.userPrincipalName))
)
| project-rename Creation_TimeGenerated = TimeGenerated
) on TargetUserPrincipalName
| extend TimeDelta = Deletion_TimeGenerated - Creation_TimeGenerated
| where TimeDelta between (time(0s) .. queryperiod)
| extend CreatedByApp = tostring(InitiatedBy.app.displayName),
CreatedByAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId),
CreatedByUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName),
CreatedByAadUserId = tostring(InitiatedBy.user.id),
CreatedByIPAddress = tostring(InitiatedBy.user.ipAddress)
| project Creation_TimeGenerated, Deletion_TimeGenerated, TimeDelta, TargetUserPrincipalName, DeletedByApp, DeletedByAppServicePrincipalId, DeletedByUserPrincipalName, DeletedByAadUserId, DeletedByIPAddress,
CreatedByApp, CreatedByAppServicePrincipalId, CreatedByUserPrincipalName, CreatedByAadUserId, CreatedByIPAddress, Creation_AdditionalDetails = AdditionalDetails, Creation_InitiatedBy = InitiatedBy, Creation_TargetResources = TargetResources, Deletion_AdditionalDetails, Deletion_InitiatedBy, Deletion_TargetResources
| extend TargetName = tostring(split(TargetUserPrincipalName,'@',0)[0]), TargetUPNSuffix = tostring(split(TargetUserPrincipalName,'@',1)[0])
| extend CreatedByName = tostring(split(CreatedByUserPrincipalName,'@',0)[0]), CreatedByUPNSuffix = tostring(split(CreatedByUserPrincipalName,'@',1)[0])
| extend DeletedByName = tostring(split(DeletedByUserPrincipalName,'@',0)[0]), DeletedByUPNSuffix = tostring(split(DeletedByUserPrincipalName,'@',1)[0])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetUserPrincipalName
- identifier: Name
columnName: TargetName
- identifier: UPNSuffix
columnName: TargetUPNSuffix
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: CreatedByUserPrincipalName
- identifier: Name
columnName: CreatedByName
- identifier: UPNSuffix
columnName: CreatedByUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: CreatedByAadUserId
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: DeletedByUserPrincipalName
- identifier: Name
columnName: DeletedByName
- identifier: UPNSuffix
columnName: DeletedByUPNSuffix
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: DeletedByAadUserId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: CreatedByIPAddress
- entityType: IP
fieldMappings:
- identifier: Address
columnName: DeletedByIPAddress
version: 1.1.0
kind: Scheduled

0 comments on commit d84db20

Please sign in to comment.