Skip to content

[bug] Fix key material node collision across accounts with empty Identifier (#7)#8

Merged
p0dalirius merged 1 commit into
mainfrom
bugfix-keynode-empty-identifier-collision
May 21, 2026
Merged

[bug] Fix key material node collision across accounts with empty Identifier (#7)#8
p0dalirius merged 1 commit into
mainfrom
bugfix-keynode-empty-identifier-collision

Conversation

@p0dalirius

Copy link
Copy Markdown
Contributor

Linked Issue

Closes #7

Root Cause

ParseResults already handled an empty kc.Identifier for the KeyCredential node by falling back to a unique id derived from the owning account ("Unknown-<n>." + distinguishedName, see parse.go:52–57). The matching code path for the KeyMaterial node at parse.go:163 did not have this fallback:

keyNodeId := kc.Identifier + "." + nodeKind

When kc.Identifier == "", every key material of a given type collapsed onto the same id (".KeyCredentialRSAPublicKey", etc.). The next line then reuses any existing node with that id, so every unrelated account producing an empty-Identifier credential of the same key type ended up wired (via HasKeyMaterial) to the single first node. The README's "shared key material" Cypher query therefore reported false positives for these credentials.

Fix Description

Use the same fallback shape as the KeyCredential branch: when kc.Identifier is empty, derive keyNodeId from the credential-scoped keyCredentialNodeId (which already encodes the owning DN) rather than the global identifier. The non-empty path is unchanged so legitimate sharing of key material across accounts — when two credentials genuinely have the same Identifier — continues to produce a single shared node, as the README's Cypher query intends.

How Verified

Static: the patched parse.go:163–168 mirrors the empty-Identifier handling at parse.go:52–57, so:

  • Non-empty kc.Identifier: keyNodeId == kc.Identifier + "." + nodeKind (unchanged).
  • Empty kc.Identifier: keyNodeId == keyCredentialNodeId + "." + nodeKind == "Unknown-<idx>." + distinguishedName + "." + nodeKind, which is unique per credential per account.
    Runtime: go build -o KeyCredentialHound . builds cleanly; help output unchanged; the binary's runtime path up to the LDAP step is unaffected.

Test Coverage

None: the repository has no Go test suite. The change is a small, deterministic adjustment to a single string construction; the static analysis above covers both branches.

Scope of Change

  • Files changed: parse.go
  • Submodule pointer updated: no
  • Behavioral changes outside the bug fix: none

Risk and Rollout

Local to ParseResults. The only behavioral difference is that empty-Identifier key materials are no longer merged. Existing exported graphs from previous versions may show fewer false HasKeyMaterial overlaps after re-running the collector; consumers re-running their saved Cypher queries should see fewer spurious shared-key-material findings.

…#7)

When `kc.Identifier` is empty, `keyNodeId` previously evaluated to
`".<NodeKind>"` and collapsed every empty-Identifier credential of the
same key type into a single shared graph node, falsely linking unrelated
accounts as sharing key material. Fall back to the per-credential
`keyCredentialNodeId` (which itself includes the owning account's DN) so
the key material node is unique per credential when no Identifier is
available.
@p0dalirius p0dalirius self-assigned this May 21, 2026
@github-actions github-actions Bot changed the title Fix key material node collision across accounts with empty Identifier (#7) [bug] Fix key material node collision across accounts with empty Identifier (#7) May 21, 2026
@github-actions github-actions Bot added the bug Something isn't working label May 21, 2026
@p0dalirius p0dalirius merged commit 2a0669c into main May 21, 2026
8 checks passed
@p0dalirius p0dalirius deleted the bugfix-keynode-empty-identifier-collision branch May 21, 2026 08:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Key material nodes collide across unrelated accounts when Identifier is empty

1 participant