Skip to content

Cache risk-rating SID lookups#277

Open
SamErde wants to merge 2 commits intomainfrom
samerde/cache-risk-sid-lookups
Open

Cache risk-rating SID lookups#277
SamErde wants to merge 2 commits intomainfrom
samerde/cache-risk-sid-lookups

Conversation

@SamErde
Copy link
Copy Markdown
Collaborator

@SamErde SamErde commented May 6, 2026

Summary

Eliminates repeated Active Directory LDAP queries for the same SID during a Locksmith scan run by introducing a session-scoped lookup cache in Private/Set-RiskRating.ps1.

Problem

Set-RiskRating is called once per issue during a scan. Seven call sites within that function all perform an identical query:

Get-ADObject -Filter { objectSid -eq $SID } | Select-Object objectClass

Common principals — Domain Users (S-1-5-21-...-513), Authenticated Users (S-1-5-11), Domain Computers (S-1-5-21-...-515), etc. — appear in the ACL of many templates and are re-queried on every issue that references them.

Solution

New helper: Get-SidObjectClass

Added at the top of Set-RiskRating.ps1 (before Set-RiskRating), automatically dot-sourced by Locksmith.psm1 with the rest of Private/*.ps1:

function Get-SidObjectClass {
    param ([Parameter(Mandatory)][string]$Sid)

    if ($null -eq $script:SidObjectClassCache) {
        $script:SidObjectClassCache = @{}
    }
    if (-not $script:SidObjectClassCache.ContainsKey($Sid)) {
        $script:SidObjectClassCache[$Sid] = Get-ADObject -Filter { objectSid -eq $Sid } |
            Select-Object objectClass
    }
    return $script:SidObjectClassCache[$Sid]
}

The $script: scope means the cache persists across all calls to Set-RiskRating within a single module session, hitting AD at most once per unique SID per scan run.

Call-site replacements (7 total)

All seven Get-ADObject | Select-Object objectClass calls replaced with Get-SidObjectClass:

Location Pattern Variable
ESC7 section A — returns PSCustomObject $IdentityReferenceObjectClass
Template section A — returns PSCustomObject $IdentityReferenceObjectClass
ESC3C2 loop A — returns PSCustomObject $escIdentityReferenceObjectClass
ESC15 loop A — returns PSCustomObject $escIdentityReferenceObjectClass
ESC2 loop A — returns PSCustomObject $escIdentityReferenceObjectClass
ESC3C1 loop A — returns PSCustomObject $escIdentityReferenceObjectClass
ESC5 loop B — extracts .objectClass $OtherIssueIdentityReferenceObjectClass

Pattern A preserves the PSCustomObject return type. Pattern B (ESC5) appends .objectClass to extract the raw array, preserving exact existing semantics.

Scope

  • ✅ Caches repeated SID lookups in Set-RiskRating.ps1
  • ✅ Preserves all risk score semantics exactly
  • Invoke-Locksmith.ps1 not edited — it is auto-generated by Build/Build-Module.ps1
  • ❌ No Pester tests added
  • ❌ No remediation or TLS changes
  • ❌ No broad refactoring

Static analysis

PSScriptAnalyzer (Warning+Error): no new warnings introduced. The pre-existing PSUseShouldProcessForStateChangingFunctions on Set-RiskRating is unchanged.

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
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants