Skip to content

Conversation

@urangel
Copy link
Contributor

@urangel urangel commented Dec 8, 2025

Description

Downstream type updates to Environment.
Refactor useSelectedTag to return a callback to avoid always calling both hooks.

Motivation and Context

Resolves BED-6602

Why is this change required? What problem does it solve?

How Has This Been Tested?

Please describe in detail how you tested your changes.
Include details of your testing environment, and the tests you ran to
see how your change affects other areas of the code, etc.

Screenshots (optional):

Types of changes

  • Chore (a change that does not modify the application functionality)

Checklist:

Summary by CodeRabbit

  • Bug Fixes

    • Selected tag resolution now uses placeholder and empty tag lists to avoid undefined/null results (removed highest-privilege fallback).
    • Abbreviated-number formatting now safely handles very large/unsafe integers.
  • New Features

    • Environment data includes exposure metrics (percent and associated tag).
    • Added a public selected-tag hook and hygiene-tag metadata (name, id, glyph).
  • Refactoring

    • Privilege Zones Details view now provides a default export alongside named exports.

✏️ Tip: You can customize this high-level summary in your review settings.

@urangel urangel self-assigned this Dec 8, 2025
@urangel urangel added the user interface A pull request containing changes affecting the UI code. label Dec 8, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 8, 2025

Walkthrough

Removes highest-privilege fallback from tag selection, adds hygiene tag constants and a top-level useSelectedTag hook, exposes Details as a default export in PrivilegeZones/Details, extends client response types with environment exposure fields, and adds a guard for very large numbers in abbreviatedNumber.

Changes

Cohort / File(s) Change Summary
Tag selection hook & public API
packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
Dropped useHighestPrivilegeTag usage; tag lists now read from useAssetGroupTags().data with default []; returns placeholderTag when no match. Added HYGIENE_TAG_NAME and HygieneTag constants. Added useSelectedTag(fromPathParams?: boolean) to choose path- or query-derived tag.
PrivilegeZones Details barrel export
packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx
Added export default Details while preserving existing named exports (EntityRulesInformation, SelectedDetails). Moved Details import to follow named exports.
Client response types
packages/javascript/js-client-library/src/responses.ts
Added EnvironmentExposure type and extended Environment with hygiene_attack_paths: number and exposures: EnvironmentExposure[].
Number formatting guard
packages/javascript/bh-shared-ui/src/utils/numberFormatting.ts
Added early guard in abbreviatedNumber to return '>9Q' for non-safe integers to avoid unsafe math on very large numbers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • specter-flq
  • Holocraft
  • mistahj67

Poem

🐰 I hopped through hooks and tags today,
swapped fallbacks for a clearer way.
Details now exported, types grew bold,
big numbers guarded from the cold.
A tiny hop, a tidy sway — hooray! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately captures the main changes: environment type updates and the ticket reference (BED-6602). It is concise and specific about the chore nature of the changes.
Description check ✅ Passed The description provides key details (downstream type updates, useSelectedTag refactor) and includes the resolved ticket (BED-6602). However, the 'How Has This Been Tested?' section is incomplete with only placeholder text.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BED-6602--per-zone-exposure

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f07141 and 92fd029.

📒 Files selected for processing (3)
  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx (1 hunks)
  • packages/javascript/js-client-library/src/responses.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: benwaples
Repo: SpecterOps/BloodHound PR: 1829
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneAnalysisIcon.tsx:26-26
Timestamp: 2025-08-28T19:26:03.304Z
Learning: In packages/javascript/bh-shared-ui/src/hooks/, useZonePathParams is exported through the useZoneParams barrel (useZoneParams/index.ts exports it via wildcard from useZonePathParams.tsx), and usePrivilegeZoneAnalysis is exported through useConfiguration.ts. Both are available via the main hooks barrel import.
Learnt from: mistahj67
Repo: SpecterOps/BloodHound PR: 1803
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/SummaryCard.tsx:24-24
Timestamp: 2025-08-25T20:12:35.629Z
Learning: The useHighestPrivilegeTagId hook is available through the hooks barrel export in packages/javascript/bh-shared-ui/src/hooks/index.ts via the wildcard export `export * from './useAssetGroupTags'`. The import `import { useHighestPrivilegeTagId } from '../../../hooks'` works correctly and doesn't cause build failures.
📚 Learning: 2025-08-28T19:26:03.304Z
Learnt from: benwaples
Repo: SpecterOps/BloodHound PR: 1829
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneAnalysisIcon.tsx:26-26
Timestamp: 2025-08-28T19:26:03.304Z
Learning: In packages/javascript/bh-shared-ui/src/hooks/, useZonePathParams is exported through the useZoneParams barrel (useZoneParams/index.ts exports it via wildcard from useZonePathParams.tsx), and usePrivilegeZoneAnalysis is exported through useConfiguration.ts. Both are available via the main hooks barrel import.

Applied to files:

  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx
  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-25T20:12:35.629Z
Learnt from: mistahj67
Repo: SpecterOps/BloodHound PR: 1803
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/SummaryCard.tsx:24-24
Timestamp: 2025-08-25T20:12:35.629Z
Learning: The useHighestPrivilegeTagId hook is available through the hooks barrel export in packages/javascript/bh-shared-ui/src/hooks/index.ts via the wildcard export `export * from './useAssetGroupTags'`. The import `import { useHighestPrivilegeTagId } from '../../../hooks'` works correctly and doesn't cause build failures.

Applied to files:

  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx
  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-09-08T19:22:49.284Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsx:34-35
Timestamp: 2025-09-08T19:22:49.284Z
Learning: In BloodHound's TagToZoneLabelDialog component (packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsx), importing AssetGroupTag type from 'js-client-library' to type tag shapes is incorrect - this type should not be used for typing tags in this context.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
🧬 Code graph analysis (1)
packages/javascript/js-client-library/src/responses.ts (1)
packages/javascript/js-client-library/src/types.ts (1)
  • AssetGroupTag (138-149)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-analysis
  • GitHub Check: build-ui
  • GitHub Check: run-tests
🔇 Additional comments (2)
packages/javascript/js-client-library/src/responses.ts (1)

54-57: Environment exposure typings look consistent

The new EnvironmentExposure type and the hygiene_attack_paths / exposures additions to Environment are coherent with existing response shapes and naming conventions; no issues from a typing/contract perspective based on this file alone.

Also applies to: 65-66

packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx (1)

22-23: Default export wiring for Details looks good

Re-exporting Details as the default while keeping the existing named exports is clean and preserves current API surface; no issues here.

@urangel urangel added the blocked This pull request cannot be completed and should not be merged label Dec 8, 2025
Comment on lines 52 to 55
export const useSelectedTag = (fromPathParams = false) => {
const pathTag = useSelectedTagPathParams();
const queryTag = useSelectedTagQueryParams();
const pathTag = useSelectedTagPathParams;
const queryTag = useSelectedTagQueryParams;

return fromPathParams ? pathTag : queryTag;
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious as to why not something like:

return fromPathParams ? useSelectedTagPathParams() : useSelectedTagQueryParams();

That way we don't have to change all the other files to have the hook be a Self Invoking function and be in tune with what code rabbit is suggestion which is that it returns the values and not just another function, which is what is maybe expected.

Copy link
Contributor

Choose a reason for hiding this comment

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

plus was getting a TS error with the Self Invoking functions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The motivation was to avoid running both hooks. With the updates we return the callback that is needed and only run that one.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yup i get that part and with running the below it only runs one hook. I console logged to confirm. Works exactly the same as the callback but with less changes and without the need of self invoking functions off a hook.

return fromPathParams ? useSelectedTagPathParams() : useSelectedTagQueryParams();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This suggestion breaks the rules of hooks

React Hook "useSelectedTagQueryParams" is called conditionally. React Hooks must be called in the exact same order in every component render. (eslint react-hooks/rules-of-hooks)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the current change also technically violate the rule of hooks, since React can't guarantee anymore which hook will run on each render; I think in this case its just obfuscating that from the linter.

The way I have done it in the past has been the original pattern, where you just invoke both and then return the implementation you want to use. If you're trying to correct for some performance issues from running both, maybe two possible solutions would be:

  • Combine these two hooks into a single hook that only conditionally computes the expensive operation
  • In the components where this hook is called conditionally, instead conditionally render one of two wrapper components that each call one of these hooks and pass along the data to their children

Copy link
Contributor Author

Choose a reason for hiding this comment

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

noooo Wes get outta here with your reason. It was cumbersome mocking both for tests -__-

Copy link
Contributor

@specter-flq specter-flq Dec 12, 2025

Choose a reason for hiding this comment

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

@urangel true true sorry about that the linter played a dirty one on me. Should have picked up the conditional restriction on hooks.

Good suggestions @maffkipp, I agree and feel that the solution should come from/or in the logic of the hook itself and the invocation of the hook should stay as commonly used and maybe stay away from the conditionals and self invoking as we dont really see a lot of that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've put this back to how it was originally so that we are calling both hooks

@urangel urangel force-pushed the BED-6602--per-zone-exposure branch 3 times, most recently from ae2c6d6 to 7efeb5c Compare December 15, 2025 14:36
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (1)

16-37: Consider type‑only import (and confirm AssetGroupTag is the right domain type here)

AssetGroupTag is only used as a type in this hook. You can avoid a runtime dependency on js-client-library by switching to a type‑only import, and it’s worth double‑checking that this hook’s tags are truly the API AssetGroupTag shape (prior discussions discouraged reusing that type for unrelated tag shapes in some UI code).

Example:

-import { AssetGroupTag } from 'js-client-library';
+import type { AssetGroupTag } from 'js-client-library';

If these tags diverge from the API model, consider defining a local SelectedTag UI type instead.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4400b0 and 7efeb5c.

📒 Files selected for processing (3)
  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (2 hunks)
  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx (1 hunks)
  • packages/javascript/js-client-library/src/responses.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: mistahj67
Repo: SpecterOps/BloodHound PR: 1803
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/SummaryCard.tsx:24-24
Timestamp: 2025-08-25T20:12:35.629Z
Learning: The useHighestPrivilegeTagId hook is available through the hooks barrel export in packages/javascript/bh-shared-ui/src/hooks/index.ts via the wildcard export `export * from './useAssetGroupTags'`. The import `import { useHighestPrivilegeTagId } from '../../../hooks'` works correctly and doesn't cause build failures.
Learnt from: benwaples
Repo: SpecterOps/BloodHound PR: 1829
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneAnalysisIcon.tsx:26-26
Timestamp: 2025-08-28T19:26:03.304Z
Learning: In packages/javascript/bh-shared-ui/src/hooks/, useZonePathParams is exported through the useZoneParams barrel (useZoneParams/index.ts exports it via wildcard from useZonePathParams.tsx), and usePrivilegeZoneAnalysis is exported through useConfiguration.ts. Both are available via the main hooks barrel import.
📚 Learning: 2025-08-28T19:26:03.304Z
Learnt from: benwaples
Repo: SpecterOps/BloodHound PR: 1829
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneAnalysisIcon.tsx:26-26
Timestamp: 2025-08-28T19:26:03.304Z
Learning: In packages/javascript/bh-shared-ui/src/hooks/, useZonePathParams is exported through the useZoneParams barrel (useZoneParams/index.ts exports it via wildcard from useZonePathParams.tsx), and usePrivilegeZoneAnalysis is exported through useConfiguration.ts. Both are available via the main hooks barrel import.

Applied to files:

  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx
  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-25T20:12:35.629Z
Learnt from: mistahj67
Repo: SpecterOps/BloodHound PR: 1803
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/SummaryCard.tsx:24-24
Timestamp: 2025-08-25T20:12:35.629Z
Learning: The useHighestPrivilegeTagId hook is available through the hooks barrel export in packages/javascript/bh-shared-ui/src/hooks/index.ts via the wildcard export `export * from './useAssetGroupTags'`. The import `import { useHighestPrivilegeTagId } from '../../../hooks'` works correctly and doesn't cause build failures.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-09-08T19:22:49.284Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsx:34-35
Timestamp: 2025-09-08T19:22:49.284Z
Learning: In BloodHound's TagToZoneLabelDialog component (packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsx), importing AssetGroupTag type from 'js-client-library' to type tag shapes is incorrect - this type should not be used for typing tags in this context.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-27T15:12:49.129Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/hooks/useSavedQueries.ts:98-105
Timestamp: 2025-08-27T15:12:49.129Z
Learning: In the BloodHound codebase, for the `useDeleteQueryPermissions` hook in `packages/javascript/bh-shared-ui/src/hooks/useSavedQueries.ts`, the team prefers to use broad cache invalidation with `queryClient.invalidateQueries(savedQueryKeys.permissions)` rather than targeted invalidation for specific permission entries.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-18T22:09:32.551Z
Learnt from: TheNando
Repo: SpecterOps/BloodHound PR: 1793
File: packages/javascript/bh-shared-ui/src/hooks/useFinishedJobs/useFinishedJobs.test.ts:45-53
Timestamp: 2025-08-18T22:09:32.551Z
Learning: When suggesting changes to hook dependencies (like adding permissionsLoaded/isSuccess checks), always mention that existing tests mocking those hooks will likely need updates to include the new dependencies, as omitting them can cause unexpected test behavior or failures.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-27T19:58:12.996Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/SaveQueryDialog.tsx:89-97
Timestamp: 2025-08-27T19:58:12.996Z
Learning: In BloodHound's SaveQueryDialog component (packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/SaveQueryDialog.tsx), the useEffect that sets isNew based on selectedQuery existence (rather than id presence) is intentional behavior - the logic for determining whether a query is new vs existing should not be changed to check for id presence.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
🧬 Code graph analysis (2)
packages/javascript/js-client-library/src/responses.ts (1)
packages/javascript/js-client-library/src/types.ts (1)
  • AssetGroupTag (138-149)
packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (1)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useAssetGroupTags (442-452)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: build-ui
  • GitHub Check: run-tests
  • GitHub Check: run-analysis
🔇 Additional comments (4)
packages/javascript/js-client-library/src/responses.ts (1)

54-57: EnvironmentExposure and new Environment fields look consistent; confirm API contract

The new EnvironmentExposure shape and the added hygiene_attack_paths / exposures fields on Environment fit the existing response-style patterns and AssetGroupTag usage. Please just confirm that the backing API always returns these fields (or that callers defensively handle undefined at runtime) so we don’t introduce silent assumptions in consumers.

Also applies to: 65-66

packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx (1)

17-23: Default export for Details is straightforward and non‑breaking

Keeping the named exports while adding export default Details is safe and matches common index patterns; no issues from this change.

packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (2)

35-49: Query/path param selection logic is safe and handles hygiene/placeholder cases

Using data: tags = [] from useAssetGroupTags avoids null/undefined issues, the hygiene sentinel (HYGIENE_AGT_ID) is handled explicitly in the query‑param path, and both query and path variants fall back cleanly to placeholderTag when no match is found. This is a reasonable and robust defaulting strategy.


51-55: useSelectedTag correctly composes hooks and returns tag values

The wrapper now unconditionally calls both useSelectedTagPathParams and useSelectedTagQueryParams and returns the appropriate tag value, which keeps consumers’ usage simple and adheres to React’s Rules of Hooks (the hook order is stable on every render). Any extra cost should be minimal since the underlying tag query is shared.

@urangel urangel force-pushed the BED-6602--per-zone-exposure branch from 7efeb5c to 7a4c079 Compare December 22, 2025 21:19
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (1)

48-48: Remove unnecessary optional chaining for consistency.

Line 48 uses tags?.find with optional chaining, but line 41 uses tags.find without it. Since both default to [] on lines 46 and 37, the optional chaining is redundant and creates inconsistency.

🔎 Proposed fix
-    return tags?.find((zone: AssetGroupTag) => zone.id.toString() === tagId) || placeholderTag;
+    return tags.find((tag: AssetGroupTag) => tag.id.toString() === tagId) || placeholderTag;

Also renamed the callback parameter from zone to tag for clarity, since the type is AssetGroupTag.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7efeb5c and 7a4c079.

📒 Files selected for processing (3)
  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx
  • packages/javascript/js-client-library/src/responses.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/javascript/js-client-library/src/responses.ts
  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: mistahj67
Repo: SpecterOps/BloodHound PR: 1803
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/SummaryCard.tsx:24-24
Timestamp: 2025-08-25T20:12:35.629Z
Learning: The useHighestPrivilegeTagId hook is available through the hooks barrel export in packages/javascript/bh-shared-ui/src/hooks/index.ts via the wildcard export `export * from './useAssetGroupTags'`. The import `import { useHighestPrivilegeTagId } from '../../../hooks'` works correctly and doesn't cause build failures.
Learnt from: benwaples
Repo: SpecterOps/BloodHound PR: 1829
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneAnalysisIcon.tsx:26-26
Timestamp: 2025-08-28T19:26:03.304Z
Learning: In packages/javascript/bh-shared-ui/src/hooks/, useZonePathParams is exported through the useZoneParams barrel (useZoneParams/index.ts exports it via wildcard from useZonePathParams.tsx), and usePrivilegeZoneAnalysis is exported through useConfiguration.ts. Both are available via the main hooks barrel import.
📚 Learning: 2025-08-25T20:12:35.629Z
Learnt from: mistahj67
Repo: SpecterOps/BloodHound PR: 1803
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/SummaryCard.tsx:24-24
Timestamp: 2025-08-25T20:12:35.629Z
Learning: The useHighestPrivilegeTagId hook is available through the hooks barrel export in packages/javascript/bh-shared-ui/src/hooks/index.ts via the wildcard export `export * from './useAssetGroupTags'`. The import `import { useHighestPrivilegeTagId } from '../../../hooks'` works correctly and doesn't cause build failures.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-09-08T19:22:49.284Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsx:34-35
Timestamp: 2025-09-08T19:22:49.284Z
Learning: In BloodHound's TagToZoneLabelDialog component (packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsx), importing AssetGroupTag type from 'js-client-library' to type tag shapes is incorrect - this type should not be used for typing tags in this context.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-28T19:26:03.304Z
Learnt from: benwaples
Repo: SpecterOps/BloodHound PR: 1829
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneAnalysisIcon.tsx:26-26
Timestamp: 2025-08-28T19:26:03.304Z
Learning: In packages/javascript/bh-shared-ui/src/hooks/, useZonePathParams is exported through the useZoneParams barrel (useZoneParams/index.ts exports it via wildcard from useZonePathParams.tsx), and usePrivilegeZoneAnalysis is exported through useConfiguration.ts. Both are available via the main hooks barrel import.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-27T15:12:49.129Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/hooks/useSavedQueries.ts:98-105
Timestamp: 2025-08-27T15:12:49.129Z
Learning: In the BloodHound codebase, for the `useDeleteQueryPermissions` hook in `packages/javascript/bh-shared-ui/src/hooks/useSavedQueries.ts`, the team prefers to use broad cache invalidation with `queryClient.invalidateQueries(savedQueryKeys.permissions)` rather than targeted invalidation for specific permission entries.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-18T22:09:32.551Z
Learnt from: TheNando
Repo: SpecterOps/BloodHound PR: 1793
File: packages/javascript/bh-shared-ui/src/hooks/useFinishedJobs/useFinishedJobs.test.ts:45-53
Timestamp: 2025-08-18T22:09:32.551Z
Learning: When suggesting changes to hook dependencies (like adding permissionsLoaded/isSuccess checks), always mention that existing tests mocking those hooks will likely need updates to include the new dependencies, as omitting them can cause unexpected test behavior or failures.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-27T19:58:12.996Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/SaveQueryDialog.tsx:89-97
Timestamp: 2025-08-27T19:58:12.996Z
Learning: In BloodHound's SaveQueryDialog component (packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/SaveQueryDialog.tsx), the useEffect that sets isNew based on selectedQuery existence (rather than id presence) is intentional behavior - the logic for determining whether a query is new vs existing should not be changed to check for id presence.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
🧬 Code graph analysis (1)
packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (1)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useAssetGroupTags (462-472)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-tests
  • GitHub Check: build-ui
  • GitHub Check: run-analysis
🔇 Additional comments (4)
packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (4)

16-18: LGTM: Import updates align with the refactored fallback behavior.

The removal of useHighestPrivilegeTag import correctly reflects the change from highest-privilege fallback to placeholder fallback.


35-42: LGTM: Simplified fallback logic removes highest-privilege assumption.

The change from highestPrivilegeTag to placeholderTag fallback is a clear behavioral shift. The default empty array on line 37 ensures tags.find() is safe without optional chaining.


51-56: LGTM: Hook follows React's Rules of Hooks correctly.

Both hooks are invoked unconditionally at the top level, which is the proper pattern to comply with React's Rules of Hooks. This design was confirmed in the past review discussion as preferable to conditional hook calls.


20-27: Fix return type to reflect actual shape differences between tag objects.

The useSelectedTagQueryParams return type declares AssetGroupTag but returns HygieneTag and placeholderTag, which have incomplete shapes. HygieneTag is missing required fields from AssetGroupTag (kind_id, type, position, require_certify, description, analysis_enabled, counts). Create a shared tag interface or union type that accurately represents all three possible returns instead of using AssetGroupTag for objects that don't implement its full contract.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a4c079 and bebfaa6.

📒 Files selected for processing (1)
  • packages/javascript/bh-shared-ui/src/utils/numberFormatting.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: mistahj67
Repo: SpecterOps/BloodHound PR: 1803
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/SummaryCard.tsx:24-24
Timestamp: 2025-08-25T20:12:35.629Z
Learning: The useHighestPrivilegeTagId hook is available through the hooks barrel export in packages/javascript/bh-shared-ui/src/hooks/index.ts via the wildcard export `export * from './useAssetGroupTags'`. The import `import { useHighestPrivilegeTagId } from '../../../hooks'` works correctly and doesn't cause build failures.
Learnt from: benwaples
Repo: SpecterOps/BloodHound PR: 1829
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneAnalysisIcon.tsx:26-26
Timestamp: 2025-08-28T19:26:03.304Z
Learning: In packages/javascript/bh-shared-ui/src/hooks/, useZonePathParams is exported through the useZoneParams barrel (useZoneParams/index.ts exports it via wildcard from useZonePathParams.tsx), and usePrivilegeZoneAnalysis is exported through useConfiguration.ts. Both are available via the main hooks barrel import.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-tests
  • GitHub Check: build-ui
  • GitHub Check: run-analysis

Comment on lines +18 to +19
// Exit early in case number in response is larger than the max safe integer to avoid doing math on it and getting erronious numbers
if (!Number.isSafeInteger(num)) return '>9Q';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Guard incorrectly rejects decimal numbers.

Number.isSafeInteger(num) returns false for non-integer values, causing the function to return '>9Q' for valid decimal inputs like 1500.5 instead of '1.5K'. This breaks the function's intended behavior.

Additionally, the comment contains a typo: "erronious" should be "erroneous".

🔎 Proposed fix
-    // Exit early in case number in response is larger than the max safe integer to avoid doing math on it and getting erronious numbers
-    if (!Number.isSafeInteger(num)) return '>9Q';
+    // Exit early for invalid numbers or numbers too large to abbreviate accurately
+    if (!isFinite(num)) return 'N/A';
+    if (Math.abs(num) > Number.MAX_SAFE_INTEGER) return '>9Q';

This approach:

  • Handles NaN and Infinity explicitly with 'N/A'
  • Allows decimal numbers to pass through correctly
  • Only guards against integers that exceed safe representation
🤖 Prompt for AI Agents
packages/javascript/bh-shared-ui/src/utils/numberFormatting.ts around lines
18-19: the current guard uses Number.isSafeInteger(num) which wrongly rejects
valid decimals; change the checks to: first handle NaN or non-finite values by
returning 'N/A', then only reject numbers that are integers and exceed
Number.MAX_SAFE_INTEGER (return '>9Q'), allowing decimal values like 1500.5 to
proceed to formatting; also fix the comment typo from "erronious" to
"erroneous".

@urangel urangel force-pushed the BED-6602--per-zone-exposure branch from bebfaa6 to b3c3013 Compare December 22, 2025 23:04
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (1)

48-48: Remove unnecessary optional chaining for consistency.

Since tags defaults to [] on line 46, the optional chaining on line 48 (tags?.find()) is unnecessary. For consistency with line 41 which uses tags.find() directly, consider removing the optional chaining.

🔎 Proposed fix
-    return tags?.find((zone: AssetGroupTag) => zone.id.toString() === tagId) || placeholderTag;
+    return tags.find((zone: AssetGroupTag) => zone.id.toString() === tagId) || placeholderTag;
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bebfaa6 and b3c3013.

📒 Files selected for processing (4)
  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
  • packages/javascript/bh-shared-ui/src/utils/numberFormatting.ts
  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx
  • packages/javascript/js-client-library/src/responses.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/javascript/bh-shared-ui/src/views/PrivilegeZones/Details/index.tsx
  • packages/javascript/bh-shared-ui/src/utils/numberFormatting.ts
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: mistahj67
Repo: SpecterOps/BloodHound PR: 1803
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/SummaryCard.tsx:24-24
Timestamp: 2025-08-25T20:12:35.629Z
Learning: The useHighestPrivilegeTagId hook is available through the hooks barrel export in packages/javascript/bh-shared-ui/src/hooks/index.ts via the wildcard export `export * from './useAssetGroupTags'`. The import `import { useHighestPrivilegeTagId } from '../../../hooks'` works correctly and doesn't cause build failures.
Learnt from: benwaples
Repo: SpecterOps/BloodHound PR: 1829
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneAnalysisIcon.tsx:26-26
Timestamp: 2025-08-28T19:26:03.304Z
Learning: In packages/javascript/bh-shared-ui/src/hooks/, useZonePathParams is exported through the useZoneParams barrel (useZoneParams/index.ts exports it via wildcard from useZonePathParams.tsx), and usePrivilegeZoneAnalysis is exported through useConfiguration.ts. Both are available via the main hooks barrel import.
📚 Learning: 2025-08-25T20:12:35.629Z
Learnt from: mistahj67
Repo: SpecterOps/BloodHound PR: 1803
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/SummaryCard.tsx:24-24
Timestamp: 2025-08-25T20:12:35.629Z
Learning: The useHighestPrivilegeTagId hook is available through the hooks barrel export in packages/javascript/bh-shared-ui/src/hooks/index.ts via the wildcard export `export * from './useAssetGroupTags'`. The import `import { useHighestPrivilegeTagId } from '../../../hooks'` works correctly and doesn't cause build failures.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-09-08T19:22:49.284Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsx:34-35
Timestamp: 2025-09-08T19:22:49.284Z
Learning: In BloodHound's TagToZoneLabelDialog component (packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/TagToZoneLabelDialog.tsx), importing AssetGroupTag type from 'js-client-library' to type tag shapes is incorrect - this type should not be used for typing tags in this context.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-28T19:26:03.304Z
Learnt from: benwaples
Repo: SpecterOps/BloodHound PR: 1829
File: packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneAnalysisIcon.tsx:26-26
Timestamp: 2025-08-28T19:26:03.304Z
Learning: In packages/javascript/bh-shared-ui/src/hooks/, useZonePathParams is exported through the useZoneParams barrel (useZoneParams/index.ts exports it via wildcard from useZonePathParams.tsx), and usePrivilegeZoneAnalysis is exported through useConfiguration.ts. Both are available via the main hooks barrel import.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-27T15:12:49.129Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/hooks/useSavedQueries.ts:98-105
Timestamp: 2025-08-27T15:12:49.129Z
Learning: In the BloodHound codebase, for the `useDeleteQueryPermissions` hook in `packages/javascript/bh-shared-ui/src/hooks/useSavedQueries.ts`, the team prefers to use broad cache invalidation with `queryClient.invalidateQueries(savedQueryKeys.permissions)` rather than targeted invalidation for specific permission entries.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-18T22:09:32.551Z
Learnt from: TheNando
Repo: SpecterOps/BloodHound PR: 1793
File: packages/javascript/bh-shared-ui/src/hooks/useFinishedJobs/useFinishedJobs.test.ts:45-53
Timestamp: 2025-08-18T22:09:32.551Z
Learning: When suggesting changes to hook dependencies (like adding permissionsLoaded/isSuccess checks), always mention that existing tests mocking those hooks will likely need updates to include the new dependencies, as omitting them can cause unexpected test behavior or failures.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
📚 Learning: 2025-08-27T19:58:12.996Z
Learnt from: jvacca-specterops
Repo: SpecterOps/BloodHound PR: 1823
File: packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/SaveQueryDialog.tsx:89-97
Timestamp: 2025-08-27T19:58:12.996Z
Learning: In BloodHound's SaveQueryDialog component (packages/javascript/bh-shared-ui/src/views/Explore/ExploreSearch/SavedQueries/SaveQueryDialog.tsx), the useEffect that sets isNew based on selectedQuery existence (rather than id presence) is intentional behavior - the logic for determining whether a query is new vs existing should not be changed to check for id presence.

Applied to files:

  • packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts
🧬 Code graph analysis (2)
packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (2)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useAssetGroupTags (462-472)
packages/javascript/bh-shared-ui/src/hooks/usePZParams/usePZPathParams.tsx (1)
  • usePZPathParams (31-101)
packages/javascript/js-client-library/src/responses.ts (1)
packages/javascript/js-client-library/src/types.ts (1)
  • AssetGroupTag (140-151)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build BloodHound Container Image / Build and Package Container
  • GitHub Check: run-tests
  • GitHub Check: build-ui
  • GitHub Check: run-analysis
🔇 Additional comments (2)
packages/javascript/js-client-library/src/responses.ts (1)

54-57: LGTM! Clean type definition.

The EnvironmentExposure type is well-defined with appropriate field types and naming conventions consistent with the backend API patterns in this file.

packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts (1)

51-56: LGTM! Correctly follows React's Rules of Hooks.

The implementation properly invokes both useSelectedTagPathParams() and useSelectedTagQueryParams() unconditionally at the top level, then returns the appropriate value based on the fromPathParams parameter. This correctly adheres to React's Rules of Hooks.

Note: While both hooks internally call useAssetGroupTags(), React Query will deduplicate these requests since they share the same query key, so there's no performance penalty.

Based on learnings, this resolution aligns with the team's discussion in past review comments where conditional hook calls and function reference returns were both deemed incorrect.

Comment on lines 35 to 42
export const useSelectedTagQueryParams = (): AssetGroupTag | typeof HygieneTag | typeof placeholderTag => {
const tags = useAssetGroupTags().data ?? [];
const { assetGroupTagId } = usePZQueryParams();
const { tag: highestPrivilegeTag } = useHighestPrivilegeTag();
const { data: tags = [] } = useAssetGroupTags();

if (assetGroupTagId === HYGIENE_AGT_ID) return HygieneTag;

return tags.find((tag) => tag.id === assetGroupTagId) || highestPrivilegeTag || placeholderTag;
return tags.find((tag) => tag.id === assetGroupTagId) || placeholderTag;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n -A 10 -B 5 "useSelectedTagQueryParams|useSelectedTagPathParams" packages/javascript/bh-shared-ui/src --type ts --type tsx

Repository: SpecterOps/BloodHound

Length of output: 92


🏁 Script executed:

rg -n -A 10 -B 5 "useSelectedTagQueryParams|useSelectedTagPathParams" packages/javascript/bh-shared-ui/src --glob "*.ts" --glob "*.tsx"

Repository: SpecterOps/BloodHound

Length of output: 2676


🏁 Script executed:

rg -n "import.*useSelectedTag" packages/javascript/bh-shared-ui/src --glob "*.ts" --glob "*.tsx" -A 3

Repository: SpecterOps/BloodHound

Length of output: 47


🏁 Script executed:

rg -n "useSelectedTag(|useSelectedTagQueryParams(|useSelectedTagPathParams(" packages/javascript/bh-shared-ui/src --glob "*.ts" --glob "*.tsx" -B 5 -A 8

Repository: SpecterOps/BloodHound

Length of output: 251


🏁 Script executed:

rg -n "useSelectedTag\(|useSelectedTagQueryParams\(|useSelectedTagPathParams\(" packages/javascript/bh-shared-ui/src --glob "*.ts" --glob "*.tsx" -B 3 -A 8

Repository: SpecterOps/BloodHound

Length of output: 790


🏁 Script executed:

# Search for exports from this hook file and check if it's in index.ts (barrel export)
rg -n "useSelectedTag" packages/javascript/bh-shared-ui/src/hooks/index.ts

Repository: SpecterOps/BloodHound

Length of output: 101


🏁 Script executed:

# Search for usage of these hooks outside the hooks directory
rg -n "useSelectedTag" packages/javascript/bh-shared-ui/src --glob "*.ts" --glob "*.tsx" | grep -v "hooks/useSelectedTag.ts"

Repository: SpecterOps/BloodHound

Length of output: 153


🏁 Script executed:

# Search more broadly for files that might import from hooks
find packages/javascript/bh-shared-ui/src -type f \( -name "*.ts" -o -name "*.tsx" \) | head -20

Repository: SpecterOps/BloodHound

Length of output: 1620


🏁 Script executed:

# Search across entire src for useSelectedTag usage (import or function call)
rg "useSelectedTag" packages/javascript/bh-shared-ui/src -l --glob "*.ts" --glob "*.tsx" | grep -v node_modules

Repository: SpecterOps/BloodHound

Length of output: 177


🏁 Script executed:

# Search across entire repository packages for useSelectedTag usage
rg "useSelectedTag" packages/javascript --glob "*.ts" --glob "*.tsx" | grep -v node_modules | head -30

Repository: SpecterOps/BloodHound

Length of output: 769


🏁 Script executed:

# Look at useAssetGroupTags hook to understand what data it provides
rg -n -A 20 "export const useAssetGroupTags|export function useAssetGroupTags" packages/javascript/bh-shared-ui/src/hooks --glob "*.ts" --glob "*.tsx"

Repository: SpecterOps/BloodHound

Length of output: 2571


🏁 Script executed:

# Check for any tests or usage hints
cat -n packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts | head -60

Repository: SpecterOps/BloodHound

Length of output: 2347


🏁 Script executed:

# Check if there are similar hooks that DO expose loading states to understand the pattern
rg -n "isLoading|isError" packages/javascript/bh-shared-ui/src/hooks --glob "*.ts" --glob "*.tsx" | head -40

Repository: SpecterOps/BloodHound

Length of output: 5776


🏁 Script executed:

# Check useOrderedTags return type to see the pattern for exposing loading states
rg -n -A 15 "export const useOrderedTags" packages/javascript/bh-shared-ui/src/hooks --glob "*.ts" --glob "*.tsx"

Repository: SpecterOps/BloodHound

Length of output: 1975


Ensure return type includes loading and error states if consumers need to distinguish loading states from missing data.

The return types for useSelectedTagQueryParams and useSelectedTagPathParams (lines 35-49) hide loading and error states by returning placeholderTag when data is unavailable. While this works when data is undefined during loading, it makes placeholderTag indistinguishable from the "no match found" case. Other hooks in the codebase (e.g., useOrderedTags) follow the pattern of explicitly exposing isLoading and isError. Clarify whether the current design is intentional or if the return type should follow the established pattern.

🤖 Prompt for AI Agents
In packages/javascript/bh-shared-ui/src/hooks/useSelectedTag.ts around lines 35
to 42, the hook currently returns a Tag or placeholderTag which hides
loading/error states; change the hook to return an object { tag: AssetGroupTag |
typeof HygieneTag | typeof placeholderTag | null, isLoading: boolean, isError:
boolean } (or similar shape consistent with useOrderedTags), derive
isLoading/isError from useAssetGroupTags(), return null (or placeholderTag only
for “no match”) when data is loaded but no match exists, and update the function
signature and all consumers to destructure the new properties so callers can
distinguish loading/error from “no match.”

Comment on lines +65 to +66
hygiene_attack_paths: number; // While improbable this number could possibly be higher than the JavaScript max safe integer in the response
exposures: EnvironmentExposure[];
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, find the file and examine lines 65-66
echo "=== Finding responses.ts file ==="
find . -name "responses.ts" -type f 2>/dev/null | head -20

echo -e "\n=== Checking for file in expected location ==="
ls -la "packages/javascript/js-client-library/src/responses.ts" 2>/dev/null || echo "File not found at expected path"

Repository: SpecterOps/BloodHound

Length of output: 307


🏁 Script executed:

#!/bin/bash
# Read the file to see lines 65-66 and surrounding context
echo "=== Reading lines around 65-66 ==="
sed -n '50,80p' packages/javascript/js-client-library/src/responses.ts | cat -n

echo -e "\n=== Searching for EnvironmentExposure type definition ==="
rg -n "EnvironmentExposure" packages/javascript/js-client-library/src/responses.ts -A 5 -B 2

echo -e "\n=== Searching for MAX_SAFE_INTEGER usage anywhere in codebase ==="
rg -n "MAX_SAFE_INTEGER" --type ts --type tsx

echo -e "\n=== Searching for hygiene_attack_paths usage ==="
rg -n "hygiene_attack_paths" --type ts --type tsx -B 2 -A 2

Repository: SpecterOps/BloodHound

Length of output: 1887


🏁 Script executed:

#!/bin/bash
# Search for abbreviatedNumber utility and number handling patterns
echo "=== Searching for abbreviatedNumber utility ==="
rg -n "abbreviatedNumber" --type ts -B 2 -A 5

echo -e "\n=== Searching for Environment type usage/consumption ==="
rg -n "Environment" packages/javascript/js-client-library/src/responses.ts

echo -e "\n=== Looking for JSON parsing or response deserialization patterns ==="
rg -n "JSON|parse|deserialize" packages/javascript/js-client-library/src/ --type ts -l | head -10

echo -e "\n=== Checking if there's a utility directory for number handling ==="
find packages/javascript/js-client-library/src -name "*util*" -o -name "*helper*" | head -20

Repository: SpecterOps/BloodHound

Length of output: 4476


🏁 Script executed:

#!/bin/bash
# Check how hygiene_attack_paths is actually used in the UI
echo "=== Searching for hygiene_attack_paths usage in components ==="
rg -n "hygiene_attack_paths" --type ts -B 3 -A 3

echo -e "\n=== Searching for abbreviatedNumber usage with Environment/exposures context ==="
rg -n "abbreviatedNumber" --type ts -B 5 -A 2 | grep -A 10 -B 10 "hygiene\|Environment\|exposure" || echo "No direct context found"

echo -e "\n=== Checking how Environment response is consumed/parsed ==="
rg -n "Environment" packages/javascript -B 2 -A 2 --type ts | head -50

Repository: SpecterOps/BloodHound

Length of output: 4587


🏁 Script executed:

#!/bin/bash
# Final check: see if there's custom JSON parsing/deserialization for Environment type
echo "=== Checking for custom JSON parsing or ReviveJSON patterns ==="
rg -n "revive|custom.*parse|JSON.parse" packages/javascript/js-client-library/src --type ts -B 2 -A 2 | head -30

echo -e "\n=== Check if exposures field has any safeguards ==="
rg -n "exposure" --type ts | grep -i "number\|safe\|bigint"

Repository: SpecterOps/BloodHound

Length of output: 356


Use string type or add runtime validation for hygiene_attack_paths.

The field can exceed JavaScript's MAX_SAFE_INTEGER (2^53 - 1 = 9,007,199,254,740,991), causing precision loss and incorrect comparisons. While an abbreviatedNumber utility exists that checks Number.isSafeInteger() for display purposes, standard JSON.parse() silently corrupts large integers before safeguards run, potentially causing wrong data or financial mismatches.

If this field is used beyond display (calculations, comparisons, storage), the number type provides no protection. Either:

  1. Change type to string and parse when needed
  2. Add custom JSON deserialization to handle values beyond the safe range
  3. Confirm with the backend that values will never exceed MAX_SAFE_INTEGER and document this constraint

The same consideration applies to exposure_percent in EnvironmentExposure.

🤖 Prompt for AI Agents
In packages/javascript/js-client-library/src/responses.ts around lines 65-66,
the hygiene_attack_paths field is currently typed as number which risks
precision loss for values above Number.MAX_SAFE_INTEGER; change
hygiene_attack_paths to string (and parse/validate where consumers need numeric
operations) or add JSON deserialization/runtime validation that preserves big
integers (e.g., read as string and validate range/convert with BigInt) to
prevent silent corruption; apply the same treatment to exposure_percent in
EnvironmentExposure (either string or validated parse) and update
types/comments/tests/docs to reflect the chosen approach.

@urangel urangel merged commit 3ceada4 into main Dec 23, 2025
13 checks passed
@urangel urangel deleted the BED-6602--per-zone-exposure branch December 23, 2025 15:00
@github-actions github-actions bot locked and limited conversation to collaborators Dec 23, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

blocked This pull request cannot be completed and should not be merged user interface A pull request containing changes affecting the UI code.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants