Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhanced Defender license warnings for policy groups 2 and 4 #929

Open
wants to merge 65 commits into
base: main
Choose a base branch
from

Conversation

Dylan-MITRE
Copy link
Contributor

@Dylan-MITRE Dylan-MITRE commented Feb 21, 2024

πŸ—£ Description

Add licenses check for policy groups 2 and 4 in addition to the current check, make the report details more precise. And let user be more aware of the missing licenses.

πŸ’­ Motivation and context

closes #599

πŸ§ͺ Testing

Run Scuba for Defender
Change the json file mainly "defender_license" to "false"
Run Scuba for Defender again
Will see warning about Defender license missing during the run
Will see notes on reports indicates license missing

Note: the check for policy group 2 and 4 will still pass or fail based on the environment setting because we are modifying the json file from an tenant with required licenses. The result is still be accurate based on the environment information. Note that, normally it will fail if they don't have license due to missing information from the provider JSON.

βœ… Pre-approval checklist

  • This PR has an informative and human-readable title.
  • PR targets the correct parent branch (e.g., main or release-name) for merge.
  • Changes are limited to a single goal - eschew scope creep!
  • Changes are sized such that they do not touch excessive number of files.
  • All future TODOs are captured in issues, which are referenced in code comments.
  • These code changes follow the ScubaGear content style guide.
  • Related issues these changes resolve are linked preferably via closing keywords.
  • All relevant type-of-change labels added.
  • All relevant project fields are set.
  • All relevant repo and/or project documentation updated to reflect these changes.
  • Unit tests added/updated to cover PowerShell and Rego changes.
  • Functional tests added/updated to cover PowerShell and Rego changes.
  • All relevant functional tests passed.
  • All automated checks (e.g., linting, static analysis, unit/smoke tests) passed.

βœ… Pre-merge checklist

  • PR passed smoke test check.

  • Feature branch has been rebased against changes from parent branch, as needed

    Use Rebase branch button below or use this reference to rebase from the command line.

  • Resolved all merge conflicts on branch

  • Notified merge coordinator that PR is ready for merge via comment mention

βœ… Post-merge checklist

  • Feature branch deleted after merge to clean up repository.
  • Verified that all checks pass on parent branch (e.g., main or release-name) after merge.

@Dylan-MITRE Dylan-MITRE linked an issue Feb 21, 2024 that may be closed by this pull request
@Dylan-MITRE Dylan-MITRE changed the title 599 missing licenses not reported for defender policy groups 2 and 4 Add licenses check for policy groups 2 and 4 Feb 22, 2024
@Dylan-MITRE Dylan-MITRE self-assigned this Feb 22, 2024
@Dylan-MITRE Dylan-MITRE added the bug This issue or pull request addresses broken functionality label Feb 22, 2024
@Dylan-MITRE Dylan-MITRE added this to the Glacier milestone Feb 22, 2024
@Dylan-MITRE Dylan-MITRE force-pushed the 599-missing-licenses-not-reported-for-defender-policy-groups-2-and-4 branch from 1e35219 to dc3dc6e Compare February 22, 2024 18:23
@Dylan-MITRE Dylan-MITRE marked this pull request as ready for review February 22, 2024 19:48
Copy link
Collaborator

@schrolla schrolla left a comment

Choose a reason for hiding this comment

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

The licensing relationship to policy group 4, particularly 4.2 is a bit more granular and tricky than just adding a warning. This might require some additional thinking about how best to report status on a policy item that contains a mix of both license required and non-license required items. Technically, without the license a tenant cannot meet the requirement, but without details about specific locations the admin doesn't have sufficient feedback to determine if the issue is due to license, DLP policy config, or a combination of both. See full comments below.

PowerShell/ScubaGear/Rego/DefenderConfig.rego Outdated Show resolved Hide resolved
PowerShell/ScubaGear/Rego/DefenderConfig.rego Outdated Show resolved Hide resolved
PowerShell/ScubaGear/Rego/DefenderConfig.rego Outdated Show resolved Hide resolved
PowerShell/ScubaGear/Rego/DefenderConfig.rego Outdated Show resolved Hide resolved
PowerShell/ScubaGear/Rego/Utils/Defender.rego Outdated Show resolved Hide resolved
Copy link
Collaborator

@schrolla schrolla left a comment

Choose a reason for hiding this comment

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

Getting closer, but recommend additional code to better delineate which locations are not meeting the baseline rather than a generic message. See comments below.

@@ -972,9 +917,10 @@ test_Locations_Incorrect_V6 if {
"defender_license": true
}

ReportDetailString := "No DLP policy matching all types found for evaluation."
ReportDetailString := "No enabled policy found that applies to: Devices, Exchange, OneDrive, SharePoint, Teams"
Copy link
Collaborator

Choose a reason for hiding this comment

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

As is, I don't believe this string provides the necessary additional details. It lists all the locations from the baseline policy, regardless of those in a current DLP policy. The goal is to provide the specific locations not present in an otherwise matching DLP policy. For example, if a tenant had a DLP policy with all the sensitive info types for 4.1, but only set to locations Exchange, OneDrive, and SharePoint, then the report details should indicate the policy name that matches 4.1 and which locations it is missing (Devices, Teams). Additionally, there could be multiple DLP policies matching 4.1 but missing locations. So it might be a set of DLP policies and listing the missing locations for each. Although at least a set of locations, even it if doesn't call out specific policies.

Not easy, but those are the details that would be useful to the user.

Copy link
Collaborator

Choose a reason for hiding this comment

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

While I agree, there is also the complexity of the number of policies. There could be multiple policies covering different parts or multiple policies covering one part like teams. De tangling that and accounting for that in the code can become complex & result in to long of a string fast. So I believe we opted for putting the additional information about policies, their state, & relation to the products in the results json under ActualValue

Copy link
Collaborator

Choose a reason for hiding this comment

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

That's a good point. I think the missing bit here in the report details then is a string that tells them where to look for additional details and confirming those details are present in ActualValue. So I'm going to rework this accordingly.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@Sloane4 Updated error message to include a line pointing users to the ActualValue field in the TestResults.json file. The filename and path are taken from the scuba_config key so they will be correct for any given invocation.

Comment on lines 220 to 221
"your tenant does not have a license for Microsoft Defender",
"for Office 365 Plan 1 or Plan 2, which is required for this feature.**"
Copy link
Collaborator

Choose a reason for hiding this comment

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

This utility method was intentionally vague about licenses because it is used by both policy group 1 (which relies on Office365 Plan 1 or 2) and policy group 4 (which relies on either DLP for Teams and DLP for Endpoint). So you can't replace the generic string directly with Office365 Plan 1 or 2. To provide more specific messaging, you would need to have the function take the policy ID and change the string based on the policy group, perhaps with the old generic default for groups other than 1 and 4 as a backup.

Copy link
Collaborator

Choose a reason for hiding this comment

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

You can chain the functions, you just have to update the previous uses of the functions

# If a defender license is not present, assume failure and
# replace the message with the warning for Office 365 license
ApplyLicenseWarning(_, "Office") := concat(" ", [FAIL, LicenseWarning]) if {
    input.defender_license == false
    LicenseWarning := concat(" ", [
        "**NOTE: Either you do not have sufficient permissions or",
        "your tenant does not have a license for Microsoft Defender",
        "for Office 365 Plan 1, which is required for this feature.**"
    ])
}

# If a defender license is not present, assume failure and
# replace the message with the warning for DLP license
ApplyLicenseWarning(_, "Dlp") := concat(" ", [FAIL, LicenseWarning]) if {
    input.defender_license == false
    LicenseWarning := concat(" ", [
        "**NOTE: Either you do not have sufficient permissions or",
        "your tenant does not have a license for Microsoft Defender",
        "for DLP for Teams or DLP for Endpoint, which is required for this feature.**"
    ])
}

This way you don't have to make another method for every license.

Example use case: "ReportDetails": ApplyLicenseWarning(Status, "Office")

Copy link
Collaborator

Choose a reason for hiding this comment

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

It got a bit more complex than that given the variety of scenarios. But the updated results are such that the error message ends up including just the right information for any given scenario and only points out the specific license requirement for the specific missing locations. See implementation in the DefenderConfig.rego for 4.2.

@Dylan-MITRE Dylan-MITRE linked an issue Mar 7, 2024 that may be closed by this pull request
@Dylan-MITRE
Copy link
Contributor Author

Also added fix for powershell bugs #976

@Dylan-MITRE Dylan-MITRE force-pushed the 599-missing-licenses-not-reported-for-defender-policy-groups-2-and-4 branch from 1a644f8 to 9b00f72 Compare March 13, 2024 13:11
@schrolla schrolla assigned schrolla and unassigned Dylan-MITRE Mar 14, 2024
@schrolla schrolla requested review from Sloane4 and removed request for schrolla March 14, 2024 15:14
@schrolla schrolla force-pushed the 599-missing-licenses-not-reported-for-defender-policy-groups-2-and-4 branch from f996684 to 81f2021 Compare March 18, 2024 14:19
Comment on lines +125 to +130
$Tracker.AddUnSuccessfulCommand("Get-DlpCompliancePolicy")
$Tracker.AddUnSuccessfulCommand("Get-DlpComplianceRule")
$Tracker.AddUnSuccessfulCommand("Get-ProtectionAlert")
$Tracker.AddSuccessfulCommand("Get-DlpCompliancePolicy")
$Tracker.AddSuccessfulCommand("Get-DlpComplianceRule")
$Tracker.AddSuccessfulCommand("Get-ProtectionAlert")
Copy link
Collaborator

Choose a reason for hiding this comment

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

If this is triggered for unsuccessful commands, why are they added to successful commands as well?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Similar to the section above it for ATP related commands, it is a case where we don't want the auto-generated error in the report about the unsuccessful commands because we handle this case with the dlp_license flag instead and have the Rego provide a more detailed DLP license warning error instead. The missing commands are how we detect the condition. By adding it to both unsuccessful and successful, we prevent the CreateReport module from adding a generic warning about missing commands that would indicate a code bug.

Comment on lines 534 to 543
# Show matching policies
has_policies contains "Exchange" if count(Policies.Exchange) > 0

DefenderErrorMessage4_2 := ErrorMessage if {
count(PoliciesWithFullProtection) == 0
ErrorMessage := "No DLP policy matching all types found for evaluation."
}
has_policies contains "SharePoint" if count(Policies.SharePoint) > 0

has_policies contains "OneDrive" if count(Policies.OneDrive) > 0

has_policies contains "Teams" if count(Policies.Teams) > 0

has_policies contains "Devices" if count(Policies.Devices) > 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

could this be shortened to simple set arithmetic? We already have error_policies to work off of, so we can just subtract a set of all names form the resulting error_policies set.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, see the assignment to PresentLocations on line 631 in the 4.2 test. Uses set arithmetic as you indicated.

Comment on lines 220 to 221
"your tenant does not have a license for Microsoft Defender",
"for Office 365 Plan 1 or Plan 2, which is required for this feature.**"
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can chain the functions, you just have to update the previous uses of the functions

# If a defender license is not present, assume failure and
# replace the message with the warning for Office 365 license
ApplyLicenseWarning(_, "Office") := concat(" ", [FAIL, LicenseWarning]) if {
    input.defender_license == false
    LicenseWarning := concat(" ", [
        "**NOTE: Either you do not have sufficient permissions or",
        "your tenant does not have a license for Microsoft Defender",
        "for Office 365 Plan 1, which is required for this feature.**"
    ])
}

# If a defender license is not present, assume failure and
# replace the message with the warning for DLP license
ApplyLicenseWarning(_, "Dlp") := concat(" ", [FAIL, LicenseWarning]) if {
    input.defender_license == false
    LicenseWarning := concat(" ", [
        "**NOTE: Either you do not have sufficient permissions or",
        "your tenant does not have a license for Microsoft Defender",
        "for DLP for Teams or DLP for Endpoint, which is required for this feature.**"
    ])
}

This way you don't have to make another method for every license.

Example use case: "ReportDetails": ApplyLicenseWarning(Status, "Office")

@@ -972,9 +917,10 @@ test_Locations_Incorrect_V6 if {
"defender_license": true
}

ReportDetailString := "No DLP policy matching all types found for evaluation."
ReportDetailString := "No enabled policy found that applies to: Devices, Exchange, OneDrive, SharePoint, Teams"
Copy link
Collaborator

Choose a reason for hiding this comment

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

While I agree, there is also the complexity of the number of policies. There could be multiple policies covering different parts or multiple policies covering one part like teams. De tangling that and accounting for that in the code can become complex & result in to long of a string fast. So I believe we opted for putting the additional information about policies, their state, & relation to the products in the results json under ActualValue

@rgbrow1949 rgbrow1949 modified the milestones: Glacier, Halibut Mar 28, 2024
@schrolla schrolla force-pushed the 599-missing-licenses-not-reported-for-defender-policy-groups-2-and-4 branch 2 times, most recently from b952bc7 to ccafb10 Compare April 12, 2024 14:41
@schrolla schrolla force-pushed the 599-missing-licenses-not-reported-for-defender-policy-groups-2-and-4 branch from ccafb10 to 6d0ab73 Compare May 9, 2024 13:40
@schrolla schrolla removed the request for review from isab-m May 10, 2024 18:53
@schrolla schrolla force-pushed the 599-missing-licenses-not-reported-for-defender-policy-groups-2-and-4 branch from 32e0145 to 9567336 Compare May 16, 2024 14:16
@schrolla schrolla changed the title Add licenses check for policy groups 2 and 4 Enhanced Defender license warnings for policy groups 2 and 4 May 17, 2024
@schrolla schrolla requested review from Sloane4 and buidav May 17, 2024 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue or pull request addresses broken functionality
Projects
None yet
4 participants