Skip to content

Conversation

@voommen-livefront
Copy link
Collaborator

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-20578

📔 Objective

Persist the Risk insights report to the database and retrieve from DB, unless, there is no data in the DB or there is a specific request to refresh.

📸 Screenshots

Default report - retrieve from DB
image

Report after the Refresh was requested
image

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

@voommen-livefront voommen-livefront requested a review from a team as a code owner June 24, 2025 20:02
@github-actions
Copy link
Contributor

github-actions bot commented Jun 24, 2025

Logo
Checkmarx One – Scan Summary & Detailsfc48f9d3-b349-432e-b475-8cdee7eada24

Great job, no security vulnerabilities found in this Pull Request

@codecov
Copy link

codecov bot commented Jun 24, 2025

Codecov Report

Attention: Patch coverage is 3.22581% with 150 lines in your changes missing coverage. Please review.

Project coverage is 36.82%. Comparing base (8b0e8b9) to head (5730a0b).

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...sk-insights/services/risk-insights-data.service.ts 0.00% 49 Missing ⚠️
.../access-intelligence/all-applications.component.ts 0.00% 41 Missing ⚠️
...ights/services/risk-insights-encryption.service.ts 0.00% 29 Missing ⚠️
...irt/access-intelligence/risk-insights.component.ts 0.00% 17 Missing ⚠️
...isk-insights/services/risk-insights-api.service.ts 41.66% 7 Missing ⚠️
...ss-intelligence/critical-applications.component.ts 0.00% 4 Missing ⚠️
.../access-intelligence/access-intelligence.module.ts 0.00% 2 Missing ⚠️
...n/src/dirt/reports/risk-insights/services/index.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #15321      +/-   ##
==========================================
- Coverage   36.86%   36.82%   -0.04%     
==========================================
  Files        3228     3230       +2     
  Lines       93388    93490     +102     
  Branches    14056    14069      +13     
==========================================
+ Hits        34426    34431       +5     
- Misses      57536    57633      +97     
  Partials     1426     1426              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sonarqubecloud
Copy link

reportContentEncryptionKey,
);

const wrappedReportContentEncryptionKey = await this.encryptService.wrapSymmetricKey(
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment: Nice work! I think this adequately addresses the immediate key-rotation concern from the encryption side. Thank you for addressing it!

Comment on lines +76 to +78
const reportDataInJson = JSON.parse(riskInsightsReportResponse.reportData);
const reportEncrypted = reportDataInJson.data;
const wrappedReportContentEncryptionKey = reportDataInJson.key;
Copy link
Contributor

@quexten quexten Jun 25, 2025

Choose a reason for hiding this comment

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

Issue (blocking):

As per the suggestion in the other comment, we should not encode to json here, and store the key on a separate column / have it separately in the API response, to both gain type safety/validate inputs, but also to make the updates cheaper from the database perspective.

Suggested change
const reportDataInJson = JSON.parse(riskInsightsReportResponse.reportData);
const reportEncrypted = reportDataInJson.data;
const wrappedReportContentEncryptionKey = reportDataInJson.key;
const reportEncrypted = riskInsightsReportResponse.reportData;
const wrappedReportContentEncryptionKey = riskInsightsReportResponse.reportKey;

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I will implement this in the next PR. I think your point about separating the content and key to facilitate key rotation is a very good point.

Comment on lines +56 to +60
totalMembers: 0,
totalAtRiskMembers: 0,
totalApplications: 0,
totalAtRiskApplications: 0,
totalCriticalApplications: 0,
Copy link
Contributor

Choose a reason for hiding this comment

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

Question:

Suggested change
totalMembers: 0,
totalAtRiskMembers: 0,
totalApplications: 0,
totalAtRiskApplications: 0,
totalCriticalApplications: 0,

I think the server-side no longer uses them (https://github.com/bitwarden/server/blob/2af4e9ccfa7c3848ca9f4b82adf9dad01268736f/src/Core/Dirt/Reports/ReportFeatures/Requests/AddOrganizationReportRequest.cs#L5) due to the potential vulnerability reported on the previous instance of the PR.

Can we remove these? Did we otherwise address this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yes, this can be removed. I didn't realize this was there. This has been addressed and we are not saving a summary in the DB. The summary is a part of the encrypted data.

const riskInsightReport = {
organizationId: organizationId,
date: new Date().toISOString(),
reportData: JSON.stringify(reportDataWithWrappedKey),
Copy link
Contributor

@quexten quexten Jun 25, 2025

Choose a reason for hiding this comment

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

Suggested change
reportData: JSON.stringify(reportDataWithWrappedKey),
reportData: reportEncrypted.encryptedString,
reportKey: wrappedReportContentEncryptionKey.encryptedString,

Issue (blocking):

On ciphers, we store the key in a separate column. See:

We should:
Change this to not stingify "reportDataWithWrappedKey". The request should probably look as follows:

{
  organizationId: organizationId,
  date: new Date().toISOString(),
  reportData: reportEncrypted.encryptedString,
  reportKey: wrappedReportContentEncryptionKey.encryptedString,
...
}

the server-side should validate both EncStrings in the request model. Here is how we do that for ciphers for example:
https://github.com/bitwarden/server/blob/2af4e9ccfa7c3848ca9f4b82adf9dad01268736f/src/Api/Vault/Models/Request/CipherRequestModel.cs#L29

This improves API type safety because the encstrings are validated.
Additionally, when KM gets towards implementing key-rotation for organizations, we need to re-encrypt all wrappedReportContentEncryptionKey entries. While this now no longer requires sending over the re-encrypted reportData over the network, this, as currently designed, still requires the server to read and parse the stored JSON, and replace the key in it. Having it on a separate column means only the column is replaced.

Especially when we get to larger organizations, and them having to rotate many (hundreds or thousands? of reports), each of which are 60KB+ in the sample I have seen, we cannot read all JSON entries from the database, so moving the key to as separate column is blocking.

So, to summarize, we need to split out the key in the request model to a separate field, and on the server side add a new column for the key.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We will implement this. This makes a lot of sense.

@Banrion Banrion changed the base branch from main to dirt/feature/risk-insights-saving-reports June 25, 2025 16:16
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.

3 participants