Skip to content

Cache risk-rating SID lookups - #277

Merged
SamErde merged 6 commits into
mainfrom
samerde/cache-risk-sid-lookups
Sep 1, 2026
Merged

Cache risk-rating SID lookups#277
SamErde merged 6 commits into
mainfrom
samerde/cache-risk-sid-lookups

Conversation

@SamErde

@SamErde SamErde commented May 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Eliminates repeated Active Directory LDAP queries for the same SID during a Locksmith scan run.

Problem

Set-RiskRating had seven call sites all performing Get-ADObject -Filter { objectSid -eq $SID } | Select-Object objectClass. Common principals appear in many template ACLs and were re-queried on every issue.

Solution

New Get-SidObjectClass helper with $script:SidObjectClassCache in Private/Set-RiskRating.ps1. Transient ADWS errors are not cached (key stays absent for retry); errors re-emitted via Write-Error -ErrorRecord $_ so callers can detect failures and $ErrorActionPreference is honoured. All 7 call sites replaced.

Scope

  • Caches repeated SID lookups in Set-RiskRating.ps1
  • Preserves all risk score semantics
  • Invoke-Locksmith.ps1 manually updated for parity (Build/Build-Module.ps1 is NOT run in CI)
  • No Pester tests, no remediation/TLS changes, no broad refactoring

Static analysis

PSScriptAnalyzer: no new warnings introduced.

Replace seven identical Get-ADObject | Select-Object objectClass calls in
Private\Set-RiskRating.ps1 with a new Get-SidObjectClass helper that wraps
the query in a script-scoped hashtable cache (\).

During a Locksmith scan, the same SID (Domain Users, Authenticated Users,
Domain Computers, etc.) appears across many issues. Previously each issue
evaluation issued a separate LDAP query for each SID. With the cache, every
SID is resolved at most once per module session.

Changes:
- Add Get-SidObjectClass private helper at top of Set-RiskRating.ps1
- Replace all 7 Get-ADObject lookup sites with Get-SidObjectClass calls
- Preserve exact return types and semantics for all call sites:
  - Pattern A (6 sites): returns PSCustomObject with objectClass property
  - Pattern B (1 site, ESC5): extracts .objectClass from the PSCustomObject
- No changes to risk scoring logic, remediation, or test coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@SamErde
SamErde marked this pull request as ready for review May 6, 2026 09:25
Copilot AI review requested due to automatic review settings May 6, 2026 09:25
@SamErde
SamErde marked this pull request as draft May 6, 2026 09:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR reduces repeated Active Directory lookups during a Locksmith scan by introducing a script-scoped SID→objectClass cache and routing existing Get-ADObject calls through a new helper in Set-RiskRating.

Changes:

  • Added Get-SidObjectClass helper that caches Get-ADObject SID lookups in a script-scoped hashtable.
  • Replaced 7 repeated Get-ADObject ... | Select-Object objectClass call sites with Get-SidObjectClass to reuse cached results.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Private/Set-RiskRating.ps1
Comment thread Private/Set-RiskRating.ps1 Outdated
Use -ErrorAction Stop with try/catch so that a transient ADWS error
does not populate the cache with . Legitimate 'not found' (SID
absent from AD) still returns and caches  normally; transient
errors emit Write-Warning and leave the key absent, allowing retry
on the next call.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@SamErde
SamErde requested a review from Copilot May 6, 2026 09:41
@SamErde
SamErde marked this pull request as ready for review May 6, 2026 09:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

…nvoke-Locksmith.ps1

Invoke-Locksmith.ps1 is the manually-maintained monolithic script that
must stay in parity with the Private/*.ps1 sources. Apply the same
Get-SidObjectClass helper (with try/catch error-handling) and replace
the same 7 Get-ADObject | Select-Object objectClass call sites that
were already applied to Private\Set-RiskRating.ps1.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@SamErde
SamErde requested a review from Copilot May 6, 2026 13:33

@SamErde SamErde left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@copilot please review

Copilot AI commented May 6, 2026

Copy link
Copy Markdown
Contributor

@copilot please review

The current state of the PR looks good. Both issues from the previous review have been resolved:

  1. Transient error caching (commit a2961c4): Get-ADObject is now wrapped in try/catch with -ErrorAction Stop. Only successful results (including genuine "not found" $null) are stored in the cache; transient ADWS errors emit a Write-Warning and leave the key absent so the next call retries.

  2. Return type semantics: All 7 call sites preserve the same return type as the original Get-ADObject | Select-Object objectClass calls — 6 sites receive the PSCustomObject directly (Pattern A), and the ESC5 loop appends .objectClass to extract the raw value (Pattern B), matching the pre-existing behaviour exactly.

No new issues found.

Copilot finished work on behalf of SamErde May 6, 2026 13:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread Private/Set-RiskRating.ps1 Outdated
Comment thread Invoke-Locksmith.ps1
Replace Write-Warning with Write-Error -ErrorRecord $_ in the
Get-SidObjectClass catch block so that AD lookup failures surface on
the error stream and callers' $ErrorActionPreference (including Stop)
is respected. Failed lookups are still not cached so each SID can be
retried on the next call. Applied to both Private\Set-RiskRating.ps1
and Invoke-Locksmith.ps1.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@jakehildreth

Copy link
Copy Markdown
Owner

@SamErde Oooh, yes. I have a need for speed. But... can you separate the new function into its own file in /Private? tyilu

@SamErde

SamErde commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

@SamErde Oooh, yes. I have a need for speed. But... can you separate the new function into its own file in /Private? tyilu

Of course! That's a great catch.

@jakehildreth

Copy link
Copy Markdown
Owner

Sorry it took so long to review everything!

@SamErde
SamErde requested a lite review from Copilot September 1, 2026 15:21
@SamErde SamErde self-assigned this Sep 1, 2026
@SamErde SamErde added the enhancement New feature or request label Sep 1, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The changes are localized to lookup plumbing, preserve existing semantics, and the only remaining feedback is minor comment-based help wording clarification.

Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread Invoke-Locksmith.ps1
Comment on lines +3504 to +3519
.DESCRIPTION
Wraps Get-ADObject with a script-scoped hashtable cache so that repeated SID-to-objectClass lookups
within a single Locksmith scan run hit Active Directory only once per unique SID. Common principals
such as Domain Users, Authenticated Users, and Domain Computers appear across many issues and
benefit most from this cache.

.PARAMETER Sid
The SID string to look up.

.OUTPUTS
PSCustomObject with an objectClass property, or $null if the SID is not found in AD.

.NOTES
The cache ($script:SidObjectClassCache) persists for the lifetime of the module session. It is
intentionally not cleared between issues so that common principals are resolved only once per
scan run.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This might be worth a follow-up look because the module is the primary use case for Locksmith (assumption about real-world usage).

Comment thread Private/Set-RiskRating.ps1 Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@SamErde
SamErde merged commit 7e3bdbd into main Sep 1, 2026
6 checks passed
@SamErde
SamErde deleted the samerde/cache-risk-sid-lookups branch September 1, 2026 15:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants