Skip to content

Add a severity score to risky apps module#1999

Open
mitchelbaker-cisa wants to merge 17 commits intomainfrom
1995-add-severity-score-to-risky-apps
Open

Add a severity score to risky apps module#1999
mitchelbaker-cisa wants to merge 17 commits intomainfrom
1995-add-severity-score-to-risky-apps

Conversation

@mitchelbaker-cisa
Copy link
Collaborator

@mitchelbaker-cisa mitchelbaker-cisa commented Feb 19, 2026

🗣 Description

This PR adds a severity score to risky applications/service principals to more accurately determine their associated risk level during remediation efforts.

The severity score is based on the following parameters:

  • Admin consented permissions
  • Non-admin consented permissions
  • Multi-tenancy (applications only)
  • Third-party service principal (if a SP is owned by an external tenant; SP only)
  • Existence of privileged roles (SP only)
  • Existence of password credentials
  • Existence of key (certificate) credentials
  • Existence of federated credentials

A new risk level property was added to each risky permission in the RiskyPermissions.json file, indicating the associated risk level of each permission. Both application/delegated permissions are given different weighted points based on their risk level:

  • Critical: 25
  • High: 15
  • Medium: 5
  • Low: 2

Admin consented permissions have a max score of 50 points and non-admin consented permissions have a max score of 10 points as to not overly influence the other parameters comprising the final severity score. Other parameters like privileged roles and password/key/federated credentials also have max scores. Reference the Get-SeverityWeights function to see the full spread of weights/point totals for each parameter.

The final severity score uses the same risk level descriptions, but is based on the following point totals:

  • Critical: 75 - max points (90 for apps, 120 for service principals)
  • High: 45 - 74
  • Medium: 25 - 44
  • Low: 0-24

The SeverityScore, MaxScore, ScorePercentage, SeverityLevel, and ScoreBreakdown properties are added to each risky application/service principal object for analysis in the ScubaResults.json output. The severity score is also output in the Entra ID HTML report, with new improvements for sorting in ascending/descending order for each of the header rows.

image

A couple other bugs were resolved and some additional risky delegated permissions were added that require admin consent. Microsoft's recommended user consent policy indicates end users can consent for any user consentable delegated permissions except for the following:

image

💭 Motivation and context

Closes #1995

Created follow-up issues #2008, #2009, and #2010.

🧪 Testing

Testing JSON data:

  1. Run ScubaGear against this branch.
  2. Open the ScubaResults.json file, navigate to the risky_applications property and confirm if SeverityScore, MaxScore, ScorePercentage, SeverityLevel, and ScoreBreakdown properties are added.
  3. Navigate to the risky_third_party_service_principals property and confirm if SeverityScore, MaxScore, ScorePercentage, SeverityLevel, and ScoreBreakdown properties are added.

Testing HTML output:

  1. Open the Entra ID reported generated by ScubaGear.
  2. Confirm that applications/service principal tables are sorted in descending order by default. The sort order should be critical -> high -> medium -> low. Click the arrow icon in the header row to change the sort order to ascending, the sort order should be low -> medium -> high -> critical.
  3. Hover over the other header rows and click the sort button. For example, sorting the "Key Credentials" column should sort rows in descending order based on the total number of credentials. Ascending order should sort starting with none then increment from smallest to largest. For "Multi-tenant enabled", this should be sorted based on its boolean value of true/false.

To simulate service principals assigned privileged roles:

  1. We will be using Invoke-SCuBACached to expedite testing. You can either use the same output directory that was generated from testing the JSON data above or rerun to get a new output directory.
  2. For either option, find the output directory and open its ScubaResults.json file.
  3. Search for the "risky_third_party_service_principals" property, then find the "PrivilegedRoles" property in one of the risky third party service principals. For our test tenants, "PrivilegedRoles" should be set to an empty array.
  4. Update the array to include some privileged roles, e.g. "Exchange Administrator" or "Global Administrator".
image
  1. right click the output directory and select "Copy path".
  2. Run Invoke-SCuBACached with the following parameters:
Invoke-SCuBACached -ExportProvider $false `
-ProductNames aad `
-M365Environment gcc ` # or whichever environment you originally ran Invoke-SCuBA with
-OutPath "paste the path to the output directory you copied above"
  1. Open the Entra ID HTML report then scroll down to the risky third party service principals table.
  2. Confirm that under the Privileged roles column, it indicates "Total: x" where x is the total number of privileged roles you added to the ScubaResults.json.
image
  1. Click the "View Privileged Roles" button, then in the modal confirm that the privileged roles you entered display correctly. Use the search input to confirm search filters the list correctly.
image

For testing the validity of password/key/federated credential data, the majority of our test tenants should have apps/service principals with test credentials set. You can confirm the totals inside of Azure and cross check with the information in ScubaResults.json to confirm. Same for properties like multi-tenant status.

For confirming if a service principal is third party or not, check if it has a corresponding app registration in the "App Registrations" blade in Azure. If it doesn't, then it's a third party service principal.

For confirming which permissions are admin consented/non-admin consented, open an application in the "App Registrations" blade in Azure, there will be a green checkbox if the permission is admin consented, or there will be a yellow exclamation if the permission is not admin consented. This is only applicable for permissions that require admin consent.

✅ 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

  • Demonstrate changes to the team for questions and comments.
    (Note: Only required for issues of size Medium or larger)

✅ 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.

@mitchelbaker-cisa mitchelbaker-cisa self-assigned this Feb 19, 2026
@mitchelbaker-cisa mitchelbaker-cisa added bug This issue or pull request addresses broken functionality enhancement This issue or pull request will add new or improve existing functionality labels Feb 19, 2026
@mitchelbaker-cisa mitchelbaker-cisa added this to the Plankton milestone Feb 19, 2026
@mitchelbaker-cisa mitchelbaker-cisa changed the title Add a severity score to risky apps and other improvements Add a severity score to risky apps module Feb 25, 2026
@mitchelbaker-cisa mitchelbaker-cisa marked this pull request as ready for review February 25, 2026 22:12
@mitchelbaker-cisa mitchelbaker-cisa force-pushed the 1995-add-severity-score-to-risky-apps branch from 5e8f93c to 5671f46 Compare February 25, 2026 22:16
@FollyBeachGurl FollyBeachGurl requested review from skirkpatrickMSFT and removed request for MichaelHicks-MSFT March 10, 2026 17:33
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 enhancement This issue or pull request will add new or improve existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Assign a severity score to each risky app/service principal

1 participant