feat: add decoy object tagging (#2912)#2932
Conversation
|
All contributors have signed the CLA ✍️ ✅ |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (41)
🚧 Files skipped from review as they are similar to previous changes (38)
WalkthroughAdds Decoy as a first-class tag and object state across backend APIs, database, graph metadata, saved searches, and Explore UI. It also updates OpenAPI schemas, analysis metrics, tests, and entity-detail rendering. ChangesDecoy object support
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/javascript/bh-shared-ui/src/views/Explore/ContextMenu/ContextMenuPrivilegeZonesEnabled.tsx (1)
49-62: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winGate asset-group actions on a real node selection.
objectIdSelectorPayloadis built before confirmingselectedItemQuery.datais a node, and it falls back to''. For an edge/undefined selection,isOwnedObject/isDecoyObjectreturnfalse, so these menu items still render and can post a selector with an empty objectId seed.Suggested fix
- const node = selectedItemQuery.data ? (selectedItemQuery.data as NodeResponse) : undefined; + const node = selectedItemQuery.data && isNode(selectedItemQuery.data) ? selectedItemQuery.data : undefined; @@ - <AssetGroupMenuItem - addNodePayload={tierZeroPayload} - isCurrentMemberFn={isTierZero} - removeNodePathFn={(tag: AssetGroupTag) => tagDetailsLink(tag.id, 'zones')} - showConfirmationOnAdd - tagIdentifierFn={getIsTierZeroTag} - /> - - <AssetGroupMenuItem - addNodePayload={objectIdSelectorPayload} - isCurrentMemberFn={isOwnedObject} - removeNodePathFn={(tag: AssetGroupTag) => tagDetailsLink(tag.id, 'labels')} - tagIdentifierFn={getIsOwnedTag} - /> - - <AssetGroupMenuItem - addNodePayload={objectIdSelectorPayload} - isCurrentMemberFn={isDecoyObject} - removeNodePathFn={(tag: AssetGroupTag) => tagDetailsLink(tag.id, 'labels')} - tagIdentifierFn={getIsDecoyTag} - /> + {node && ( + <> + <AssetGroupMenuItem + addNodePayload={tierZeroPayload} + isCurrentMemberFn={isTierZero} + removeNodePathFn={(tag: AssetGroupTag) => tagDetailsLink(tag.id, 'zones')} + showConfirmationOnAdd + tagIdentifierFn={getIsTierZeroTag} + /> + + <AssetGroupMenuItem + addNodePayload={objectIdSelectorPayload} + isCurrentMemberFn={isOwnedObject} + removeNodePathFn={(tag: AssetGroupTag) => tagDetailsLink(tag.id, 'labels')} + tagIdentifierFn={getIsOwnedTag} + /> + + <AssetGroupMenuItem + addNodePayload={objectIdSelectorPayload} + isCurrentMemberFn={isDecoyObject} + removeNodePathFn={(tag: AssetGroupTag) => tagDetailsLink(tag.id, 'labels')} + tagIdentifierFn={getIsDecoyTag} + /> + </> + )}Also applies to: 111-123
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/javascript/bh-shared-ui/src/views/Explore/ContextMenu/ContextMenuPrivilegeZonesEnabled.tsx` around lines 49 - 62, Gate the asset-group actions in ContextMenuPrivilegeZonesEnabled on a confirmed NodeResponse selection instead of building objectIdSelectorPayload with empty fallbacks; only create/use the selector payload when selectedItemQuery.data is a real node, and otherwise hide or disable those menu items. Update the logic around objectIdSelectorPayload, isOwnedObject, and isDecoyObject so edge/undefined selections cannot satisfy the conditions or render actions that would submit an empty objectId seed.
🧹 Nitpick comments (2)
cmd/api/src/database/migration/agi_integration_test.go (1)
31-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the required default groups instead of the total count.
This already had to change from
2to3in this PR. Checking for the specific seeded groups/tags will make the migration test resilient to future default-group additions that are unrelated to this behavior.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/api/src/database/migration/agi_integration_test.go` around lines 31 - 37, The migration test is asserting only the total number of asset groups in SetupDB, which is too brittle. Update the AGI integration test to verify the specific seeded default groups/tags returned by GetAllAssetGroups instead of comparing len(assetGroups) to a fixed count. Use the existing setup in agi_integration_test.go and the GetAllAssetGroups call to assert the required default groups are present, so future unrelated default-group additions do not break the test.cmd/ui/src/ducks/assetgroups/reducer.ts (1)
76-78: 📐 Maintainability & Code Quality | 🔵 TrivialAlign the selector return type with its actual runtime behavior.
The implementation
find(...)?.idcan returnundefinedwhen the 'decoy' asset group is missing. The current signature: numberis incorrect understrictTypeScript mode and misrepresents the contract. Update the return type tonumber | undefinedto reflect that the value may be absent.Suggested fix
-export const selectDecoyAssetGroupId = (state: AppState): number => { +export const selectDecoyAssetGroupId = (state: AppState): number | undefined => { return state.assetgroups.assetGroups.find((assetGroup) => assetGroup.tag === 'decoy')?.id; };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cmd/ui/src/ducks/assetgroups/reducer.ts` around lines 76 - 78, The selector selectDecoyAssetGroupId currently returns assetGroup.id via find(...)?.id, so it can be undefined when no decoy asset group exists. Update the return type on selectDecoyAssetGroupId to number | undefined so the AppState selector contract matches the actual runtime behavior under strict TypeScript.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@cmd/api/src/database/migration/migrations/20260618120000_v9_add_decoy_asset_group.sql`:
- Around line 18-31: The rollback in this migration is not symmetric with the
seed logic because `Up` can reuse existing `asset_groups`, `kind`, and
`asset_group_tags` rows via `ON CONFLICT`, while `Down` unconditionally deletes
by decoy identifiers. Update the migration so `Down` only removes rows this
migration actually created, or change the `Up` path to avoid attaching to
pre-existing records; use the existing `asset_groups`, `kind`, and
`asset_group_tags` insert/delete statements as the targets for the fix.
In `@cmd/ui/src/views/Explore/ContextMenu/AssetGroupMenuItem.tsx`:
- Around line 59-71: The asset group members query is running without a
guaranteed real objectId, which can build an undefined object_id filter and
expose stale Add/Remove actions. Update the useQuery call in AssetGroupMenuItem
to only enable the members lookup when assetGroupId is set and
selectedItemQuery.data is a NodeResponse with a valid objectId, and make the
query key/fetcher depend on that resolved objectId. Apply the same guard to the
second members query in this menu so non-node selections and unresolved data do
not render member actions prematurely.
In `@packages/go/analysis/tiering/tiering_test.go`:
- Line 17: Update the test package declaration in tiering_test.go from the
internal package to the external test package so it exercises the public API
through imports. Since this test only covers exported symbols like IsDecoy and
KindTagDecoy, change the package name to the code package plus _test to match
the repo’s _test.go rule and preserve import-surface validation.
In `@packages/javascript/bh-shared-ui/src/commonSearchesAGI.ts`:
- Around line 133-135: The privilege ranking query in commonSearchesAGI still
counts decoy Computer nodes because the second MATCH in the Kerberoastable users
search does not apply excludeDecoyNodesAGI. Update the query for this search so
the Computer traversal is filtered the same way as decoy users, using the
existing decoy-exclusion helper/symbols around the u-to-c path before COUNT(c)
is computed. This keeps adminCount from being inflated by decoy endpoints while
preserving the rest of the search logic.
In `@packages/javascript/bh-shared-ui/src/commonSearchesAGT.ts`:
- Around line 133-135: The AGT ranking search still counts Computer targets
reached through decoy-tagged endpoints, so it is not fully aligned with the
decoy-exclusion behavior. Update the query in commonSearchesAGT’s saved search
entry (the one named “Kerberoastable users with most admin privileges”) to
exclude TAG_DECOY_AGT-tagged Computer nodes as well as decoy users, and make
sure the MATCH/WHERE logic only contributes non-decoy computers to adminCount.
In
`@packages/javascript/bh-shared-ui/src/components/EntityInfo/PotentialDecoyBanner.tsx`:
- Around line 255-256: Update the marked/unmarked decoy copy in
PotentialDecoyBanner so it is object-neutral for non-user nodes: keep the
heuristic warning user-specific, but replace the hard-coded “User marked as
decoy” / “User unmarked as decoy” style strings used in the switch, banner, and
notifications with generic decoy wording. Adjust the relevant logic in
PotentialDecoyBanner where the checked-state message and success toast key are
chosen so computers/groups already marked as decoys display correct text.
- Around line 163-166: The membership lookup is using a double-prefixed
object_id query value, which breaks the backend filter format; update
PotentialDecoyBanner’s listAssetGroupMembers call so params.object_id is built
as eq:${objectId} instead of object_id=eq:${objectId}. Also apply the same fix
in AssetGroupMenuItem where the same parameter pattern is used, keeping the
query value consistent with what the API expects.
In `@packages/javascript/bh-shared-ui/src/hooks/useExploreGraph/queries/utils.ts`:
- Line 79: The `isDecoyObject` derivation in the `useExploreGraph` query utils
only checks `system_tags`, so AGT decoys marked via `kinds`/`TAG_DECOY_AGT` are
missed. Update the logic that builds the node shape in this utils module to
treat either legacy `system_tags` or the AGT decoy kind/tag as decoy state when
setting `isDecoyObject`. Keep the existing `DECOY_OBJECT_TAG` check, but also
inspect the node’s `kinds` data so transformed nodes preserve the correct
boolean for downstream consumers.
---
Outside diff comments:
In
`@packages/javascript/bh-shared-ui/src/views/Explore/ContextMenu/ContextMenuPrivilegeZonesEnabled.tsx`:
- Around line 49-62: Gate the asset-group actions in
ContextMenuPrivilegeZonesEnabled on a confirmed NodeResponse selection instead
of building objectIdSelectorPayload with empty fallbacks; only create/use the
selector payload when selectedItemQuery.data is a real node, and otherwise hide
or disable those menu items. Update the logic around objectIdSelectorPayload,
isOwnedObject, and isDecoyObject so edge/undefined selections cannot satisfy the
conditions or render actions that would submit an empty objectId seed.
---
Nitpick comments:
In `@cmd/api/src/database/migration/agi_integration_test.go`:
- Around line 31-37: The migration test is asserting only the total number of
asset groups in SetupDB, which is too brittle. Update the AGI integration test
to verify the specific seeded default groups/tags returned by GetAllAssetGroups
instead of comparing len(assetGroups) to a fixed count. Use the existing setup
in agi_integration_test.go and the GetAllAssetGroups call to assert the required
default groups are present, so future unrelated default-group additions do not
break the test.
In `@cmd/ui/src/ducks/assetgroups/reducer.ts`:
- Around line 76-78: The selector selectDecoyAssetGroupId currently returns
assetGroup.id via find(...)?.id, so it can be undefined when no decoy asset
group exists. Update the return type on selectDecoyAssetGroupId to number |
undefined so the AppState selector contract matches the actual runtime behavior
under strict TypeScript.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: e7dffb9d-b806-4d01-8a6f-130bf634a31b
📒 Files selected for processing (41)
cmd/api/src/api/v2/assetgrouptags.gocmd/api/src/api/v2/assetgrouptags_test.gocmd/api/src/api/v2/cypherquery_test.gocmd/api/src/api/v2/etac.gocmd/api/src/database/assetgrouptags.gocmd/api/src/database/assetgrouptags_test.gocmd/api/src/database/migration/agi_integration_test.gocmd/api/src/database/migration/migrations/20260618120000_v9_add_decoy_asset_group.sqlcmd/api/src/model/assetgrouptags.gocmd/api/src/model/unified_graph.gocmd/api/src/model/unified_graph_test.gocmd/ui/src/ducks/assetgroups/reducer.tscmd/ui/src/views/Explore/ContextMenu/AssetGroupMenuItem.tsxcmd/ui/src/views/Explore/ContextMenu/ContextMenu.tsxcmd/ui/src/views/Explore/utils.test.tscmd/ui/src/views/Explore/utils.tspackages/go/analysis/agt.gopackages/go/analysis/metrics.gopackages/go/analysis/tiering/tiering.gopackages/go/analysis/tiering/tiering_test.gopackages/go/graphschema/ad/const.gopackages/go/openapi/doc/openapi.jsonpackages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yamlpackages/go/openapi/src/schemas/model.unified-graph.node.yamlpackages/javascript/bh-shared-ui/src/commonSearchesAGI.tspackages/javascript/bh-shared-ui/src/commonSearchesAGT.tspackages/javascript/bh-shared-ui/src/components/EntityInfo/EntityObjectInformation.tsxpackages/javascript/bh-shared-ui/src/components/EntityInfo/PotentialDecoyBanner.test.tsxpackages/javascript/bh-shared-ui/src/components/EntityInfo/PotentialDecoyBanner.tsxpackages/javascript/bh-shared-ui/src/constants.tspackages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.test.tsxpackages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsxpackages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useTagGlyphs.tspackages/javascript/bh-shared-ui/src/hooks/useExploreGraph/queries/utils.tspackages/javascript/bh-shared-ui/src/hooks/useGraphItem.tsxpackages/javascript/bh-shared-ui/src/mocks/factories/explore.tspackages/javascript/bh-shared-ui/src/utils/entityInfoDisplay.tspackages/javascript/bh-shared-ui/src/views/Explore/ContextMenu/ContextMenuPrivilegeZonesEnabled.test.tsxpackages/javascript/bh-shared-ui/src/views/Explore/ContextMenu/ContextMenuPrivilegeZonesEnabled.tsxpackages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsxpackages/javascript/js-client-library/src/types.ts
|
I have read the CLA Document and I hereby sign the CLA |
d176a1e to
78b3fc6
Compare
|
Addressed coderabbit feedback |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cmd/api/src/database/migration/agi_integration_test.go`:
- Around line 36-44: The asset group seeding test in agi_integration_test.go
only checks membership with slices.ContainsFunc, so it can miss duplicate or
extra default rows. Add an explicit assertion on the total length of assetGroups
in the same test before or alongside the existing checks, so the migration
validation in TestAgiIntegration still enforces the exact seeded set. Use the
existing assetGroups variable and the current ContainsFunc assertions as the
location to update.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: feeaac4b-d419-4d73-97cd-c0ae877a1069
📒 Files selected for processing (41)
cmd/api/src/api/v2/assetgrouptags.gocmd/api/src/api/v2/assetgrouptags_test.gocmd/api/src/api/v2/cypherquery_test.gocmd/api/src/api/v2/etac.gocmd/api/src/database/assetgrouptags.gocmd/api/src/database/assetgrouptags_test.gocmd/api/src/database/migration/agi_integration_test.gocmd/api/src/database/migration/migrations/20260618120000_v9_add_decoy_asset_group.sqlcmd/api/src/model/assetgrouptags.gocmd/api/src/model/unified_graph.gocmd/api/src/model/unified_graph_test.gocmd/ui/src/ducks/assetgroups/reducer.tscmd/ui/src/views/Explore/ContextMenu/AssetGroupMenuItem.tsxcmd/ui/src/views/Explore/ContextMenu/ContextMenu.tsxcmd/ui/src/views/Explore/utils.test.tscmd/ui/src/views/Explore/utils.tspackages/go/analysis/agt.gopackages/go/analysis/metrics.gopackages/go/analysis/tiering/tiering.gopackages/go/analysis/tiering/tiering_test.gopackages/go/graphschema/ad/const.gopackages/go/openapi/doc/openapi.jsonpackages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yamlpackages/go/openapi/src/schemas/model.unified-graph.node.yamlpackages/javascript/bh-shared-ui/src/commonSearchesAGI.tspackages/javascript/bh-shared-ui/src/commonSearchesAGT.tspackages/javascript/bh-shared-ui/src/components/EntityInfo/EntityObjectInformation.tsxpackages/javascript/bh-shared-ui/src/components/EntityInfo/PotentialDecoyBanner.test.tsxpackages/javascript/bh-shared-ui/src/components/EntityInfo/PotentialDecoyBanner.tsxpackages/javascript/bh-shared-ui/src/constants.tspackages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.test.tsxpackages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsxpackages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useTagGlyphs.tspackages/javascript/bh-shared-ui/src/hooks/useExploreGraph/queries/utils.tspackages/javascript/bh-shared-ui/src/hooks/useGraphItem.tsxpackages/javascript/bh-shared-ui/src/mocks/factories/explore.tspackages/javascript/bh-shared-ui/src/utils/entityInfoDisplay.tspackages/javascript/bh-shared-ui/src/views/Explore/ContextMenu/ContextMenuPrivilegeZonesEnabled.test.tsxpackages/javascript/bh-shared-ui/src/views/Explore/ContextMenu/ContextMenuPrivilegeZonesEnabled.tsxpackages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsxpackages/javascript/js-client-library/src/types.ts
✅ Files skipped from review due to trivial changes (4)
- cmd/api/src/api/v2/etac.go
- cmd/api/src/model/unified_graph.go
- packages/go/openapi/src/schemas/model.unified-graph.node.yaml
- packages/javascript/bh-shared-ui/src/utils/entityInfoDisplay.ts
🚧 Files skipped from review as they are similar to previous changes (36)
- packages/go/graphschema/ad/const.go
- packages/go/openapi/src/paths/asset-isolation.asset-group-tags.search.yaml
- cmd/ui/src/ducks/assetgroups/reducer.ts
- packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useTagGlyphs.ts
- packages/go/analysis/tiering/tiering_test.go
- packages/go/analysis/metrics.go
- packages/javascript/bh-shared-ui/src/components/EntityInfo/EntityObjectInformation.tsx
- packages/go/analysis/agt.go
- packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.test.tsx
- packages/javascript/bh-shared-ui/src/views/Explore/ContextMenu/ContextMenuPrivilegeZonesEnabled.test.tsx
- cmd/ui/src/views/Explore/ContextMenu/ContextMenu.tsx
- packages/javascript/bh-shared-ui/src/constants.ts
- packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsx
- packages/go/analysis/tiering/tiering.go
- packages/javascript/bh-shared-ui/src/hooks/useExploreGraph/queries/utils.ts
- cmd/ui/src/views/Explore/ContextMenu/AssetGroupMenuItem.tsx
- packages/javascript/bh-shared-ui/src/views/Explore/ContextMenu/ContextMenuPrivilegeZonesEnabled.tsx
- packages/javascript/bh-shared-ui/src/hooks/useGraphItem.tsx
- cmd/api/src/model/assetgrouptags.go
- cmd/api/src/database/migration/migrations/20260618120000_v9_add_decoy_asset_group.sql
- packages/javascript/js-client-library/src/types.ts
- cmd/ui/src/views/Explore/utils.ts
- packages/javascript/bh-shared-ui/src/components/EntityInfo/PotentialDecoyBanner.test.tsx
- cmd/api/src/model/unified_graph_test.go
- cmd/api/src/database/assetgrouptags.go
- cmd/api/src/database/assetgrouptags_test.go
- cmd/api/src/api/v2/cypherquery_test.go
- cmd/ui/src/views/Explore/utils.test.ts
- packages/javascript/bh-shared-ui/src/components/EntityInfo/PotentialDecoyBanner.tsx
- packages/go/openapi/doc/openapi.json
- packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx
- cmd/api/src/api/v2/assetgrouptags.go
- packages/javascript/bh-shared-ui/src/commonSearchesAGI.ts
- cmd/api/src/api/v2/assetgrouptags_test.go
- packages/javascript/bh-shared-ui/src/mocks/factories/explore.ts
- packages/javascript/bh-shared-ui/src/commonSearchesAGT.ts
78b3fc6 to
78923d7
Compare
|
Updated the branch to address the remaining CodeRabbit thread by restoring the asset group cardinality assertion in agi_integration_test.go |
Description
Adds Decoy object tagging to BloodHound CE.
This change introduces a default Decoy asset group tag, exposes decoy state through graph API responses, and adds UI support for marking and unmarking objects as Decoy from Explore/entity views. Decoy objects get their own graph label/glyph behavior and are treated similarly to Owned objects where that model already exists.
Inspired by: Honeypot detection
Notes: mbaedev.notion.site
The implementation includes:
Tag_Decoygraph kindtiering.IsDecoy()andUnifiedNode.isDecoyObjectsupportisDecoyObjectgraph node fieldThis also includes a small selector seed filtering fix: selector seed
valuefiltering now works as expected. The Decoy banner depends on that behavior to find existing object-id selectors, so it is included here intentionally rather than as accidental scope creep.Motivation and Context
Resolves #2912
BloodHound currently has strong support for marking high-value or controlled objects, but there is no equivalent way to mark deception assets such as decoy users, computers, or other intentionally placed objects.
That creates two practical problems:
This feature gives operators a way to mark those objects explicitly. The goal is not to hide decoys from the graph entirely, but to make them visible as Decoy objects and avoid treating them as normal targets in predefined queries.
How Has This Been Tested?
Tested locally from on branch
codex/decoy-asset-group, rebased onto currentupstream/main.The required repository prep command completed successfully:
Additional coverage added/updated includes:
tiering.IsDecoy()unit coverageisDecoyObjectI also manually validated the feature in a local Docker dev stack by marking a user as Decoy through the AGT selector flow and confirming the node received the
Tag_Decoylabel in Neo4j.Screenshots:
Types of changes
Checklist:
Summary by CodeRabbit
New Features
isDecoyObjectsignal for glyphs, entity details, and the potential-decoy banner/toggle.4.Bug Fixes
Documentation / Tests