Skip to content

Conversation

@r-tome
Copy link
Contributor

@r-tome r-tome commented Oct 16, 2025

🎟️ Tracking

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

📔 Objective

Add server-side validation to the request body and return the appropriate HTTP status code and error message.
For Policy.Data and SavePolicyModel.Metadata in PUT organizations/{orgId}/policies/{type} > For Policy.Data in PUT organizations/{orgId}/policies/{type}

⏰ 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

@r-tome r-tome marked this pull request as ready for review October 16, 2025 16:18
@r-tome r-tome requested a review from a team as a code owner October 16, 2025 16:18
@r-tome r-tome requested a review from jrmccannon October 16, 2025 16:18
@codecov
Copy link

codecov bot commented Oct 16, 2025

Codecov Report

❌ Patch coverage is 96.66667% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.19%. Comparing base (de56b7f) to head (62976da).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...Core/AdminConsole/Utilities/PolicyDataValidator.cs 94.28% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6460      +/-   ##
==========================================
+ Coverage   52.15%   52.19%   +0.03%     
==========================================
  Files        1908     1909       +1     
  Lines       84413    84431      +18     
  Branches     7537     7537              
==========================================
+ Hits        44026    44065      +39     
+ Misses      38675    38652      -23     
- Partials     1712     1714       +2     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Contributor

github-actions bot commented Oct 16, 2025

Logo
Checkmarx One – Scan Summary & Detailsd05cf3ca-068b-4699-aa7e-f30093691e5e

New Issues (3)

Checkmarx found the following issues in this Pull Request

Severity Issue Source File / Package Checkmarx Insight
MEDIUM CSRF /src/Api/Auth/Controllers/AccountsController.cs: 427
detailsMethod at line 427 of /src/Api/Auth/Controllers/AccountsController.cs gets a parameter from a user request from model. This parameter value flow...
ID: qB2Oh4bvPkc2tAsuqY%2B%2FiPKPcCE%3D
Attack Vector
MEDIUM CSRF /src/Api/Vault/Controllers/CiphersController.cs: 1537
detailsMethod at line 1537 of /src/Api/Vault/Controllers/CiphersController.cs gets a parameter from a user request from id. This parameter value flows ...
ID: oS5I%2FDxqQus8L80ybnB6qqHIyTo%3D
Attack Vector
MEDIUM CSRF /src/Api/Vault/Controllers/CiphersController.cs: 1408
detailsMethod at line 1408 of /src/Api/Vault/Controllers/CiphersController.cs gets a parameter from a user request from id. This parameter value flows ...
ID: siIZQHKaoXC15rRHhlVxewk6xUo%3D
Attack Vector
Fixed Issues (2)

Great job! The following issues were fixed in this Pull Request

Severity Issue Source File / Package
MEDIUM CSRF /src/Api/Billing/Controllers/VNext/AccountBillingVNextController.cs: 72
MEDIUM CSRF /src/Api/Vault/Controllers/CiphersController.cs: 300

…ing of invalid data types in policy updates.
…stModel to utilize PolicyDataValidator for data validation and serialization, removing redundant methods and improving code clarity.
@r-tome r-tome requested a review from jrmccannon October 17, 2025 11:04
…d Metadata properties with empty dictionaries.
@r-tome r-tome requested a review from jrmccannon October 17, 2025 14:23
@r-tome r-tome requested a review from jrmccannon October 17, 2025 14:55
jrmccannon
jrmccannon previously approved these changes Oct 17, 2025
@jrmccannon jrmccannon dismissed their stale review October 17, 2025 15:07

Didn't see the tests were failing

…empty data and metadata, and remove null assignments in test cases for improved clarity.
@r-tome r-tome requested a review from jrmccannon October 17, 2025 16:17
jrmccannon
jrmccannon previously approved these changes Oct 17, 2025
Copy link
Contributor

@jrmccannon jrmccannon left a comment

Choose a reason for hiding this comment

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

🚀

@r-tome
Copy link
Contributor Author

r-tome commented Oct 23, 2025

@jrmccannon, I have updated the error message to explicitly say which field had the wrong type.

jrmccannon
jrmccannon previously approved these changes Oct 23, 2025
JimmyVo16
JimmyVo16 previously approved these changes Oct 27, 2025
…able dictionaries for Data and Metadata properties; enhance validation methods in PolicyDataValidator to handle null cases.
@r-tome r-tome dismissed stale reviews from JimmyVo16 and jrmccannon via e5e901d October 28, 2025 14:41
@claude
Copy link
Contributor

claude bot commented Oct 28, 2025

Claude finished @r-tome's task —— View job


Comprehensive PR Review

I've completed a thorough review of this PR which adds server-side validation for policy data and metadata. The implementation centralizes validation logic in a new PolicyDataValidator utility class and adds comprehensive test coverage.


Summary of Changes Since Last Review

This PR has evolved significantly since the initial commits, incorporating feedback from previous reviews:

  1. Centralized Validation: Created PolicyDataValidator utility class to consolidate validation logic
  2. Nullable Dictionary Handling: Changed from empty dictionary initialization to nullable dictionaries (Dictionary<string, object>?)
  3. Enhanced Error Messages: Added field-specific error information when validation fails (e.g., field 'minLength' has invalid type)
  4. Comprehensive Test Coverage: Added both unit and integration tests for all three policy endpoints (PUT, PUT vnext, and Public API)
  5. Null Safety: Removed #nullable disable pragma and made code properly null-safe

Critical Issues

1. Incomplete Validation Coverage

Severity: ⚠️ High

The PolicyDataValidator only validates 3 out of 19 policy types:

  • MasterPassword
  • SendOptions
  • ResetPassword
  • ❌ All other 16 policy types (no validation)
Details & Impact

Location: src/Core/AdminConsole/Utilities/PolicyDataValidator.cs:30-40

Current Code:

switch (policyType)
{
    case PolicyType.MasterPassword:
        CoreHelpers.LoadClassFromJsonData<MasterPasswordPolicyData>(json);
        break;
    case PolicyType.SendOptions:
        CoreHelpers.LoadClassFromJsonData<SendOptionsPolicyData>(json);
        break;
    case PolicyType.ResetPassword:
        CoreHelpers.LoadClassFromJsonData<ResetPasswordDataModel>(json);
        break;
}

Problem: The other 16 policy types receive NO validation. Invalid data types will be accepted and stored, potentially causing runtime errors when the data is later deserialized for use.

Example Attack Vector:

// This would be accepted for PasswordGenerator policy without validation:
{
  "minLength": "not a number",  // Wrong type
  "useNumbers": "yes"            // Wrong type
}

Recommendation:

  1. Audit all policy types to identify which ones have data models
  2. Add validation for all policy types that accept structured data
  3. Consider adding a comment documenting which policy types intentionally have no data structure
  4. Alternative: Use a registry pattern or attribute-based approach to ensure new policy types don't forget validation

2. Silent Metadata Validation Failures

Severity: ⚠️ Medium-High

The ValidateAndDeserializeMetadata method silently swallows validation errors and returns EmptyMetadataModel instead of informing the client.

Details & Impact

Location: src/Core/AdminConsole/Utilities/PolicyDataValidator.cs:58-80

Current Code:

try
{
    var json = JsonSerializer.Serialize(metadata);
    return policyType switch
    {
        PolicyType.OrganizationDataOwnership =>
            CoreHelpers.LoadClassFromJsonData<OrganizationModelOwnershipPolicyModel>(json),
        _ => new EmptyMetadataModel()
    };
}
catch (JsonException)
{
    return new EmptyMetadataModel();  // Silent failure!
}

Problem: If a client sends invalid metadata (wrong types, bad format), the request succeeds with HTTP 200, but the metadata is silently ignored. This violates the principle of least surprise and makes debugging difficult.

Scenario:

POST /organizations/{id}/policies/OrganizationDataOwnership/vnext
{
  "policy": { "type": 5, "enabled": true },
  "metadata": { "defaultUserCollectionName": 12345 }  // Wrong type!
}
// Returns 200 OK, but metadata is silently dropped

Recommendation:

  1. Preferred: Throw BadRequestException on metadata validation failure (consistent with data validation)
  2. Alternative: Add logging to track when metadata validation fails for monitoring/debugging
  3. Update tests to verify error responses for invalid metadata

Inconsistency Note: The ValidateAndSerialize method correctly throws exceptions, but ValidateAndDeserializeMetadata swallows them. This asymmetry is confusing.

3. Missing JSON Deserialization Options

Severity: ⚠️ Medium

The validator uses default JsonSerializer options, but CoreHelpers.LoadClassFromJsonData uses custom _jsonSerializerOptions. This inconsistency could cause subtle bugs.

Details & Impact

Location: src/Core/AdminConsole/Utilities/PolicyDataValidator.cs:28, 67

Current Code:

var json = JsonSerializer.Serialize(data);  // Uses DEFAULT options
// Then later...
CoreHelpers.LoadClassFromJsonData<T>(json);  // Uses CUSTOM _jsonSerializerOptions

Problem: The initial serialization might produce JSON that doesn't match what CoreHelpers.LoadClassFromJsonData expects, or vice versa.

Recommendation:

  1. Use CoreHelpers.ClassToJsonData() for serialization to ensure consistency
  2. Or extract the serializer options and use them explicitly in both places

Significant Issues & Improvements

4. Incomplete Error Context

Severity: 🎨 Medium

Error messages could be more helpful by including the actual received value type.

Example Improvement

Current:

Invalid data for MasterPassword policy: field 'minLength' has invalid type.

Better:

Invalid data for MasterPassword policy: field 'minLength' expects integer but received string.

Implementation Suggestion:

catch (JsonException ex)
{
    var fieldInfo = !string.IsNullOrEmpty(ex.Path) 
        ? $": field '{ex.Path}' - {ex.Message}" 
        : $": {ex.Message}";
    throw new BadRequestException($"Invalid data for {policyType} policy{fieldInfo}.");
}

5. Missing Input Validation

Severity: 🎨 Low-Medium

The methods don't validate their policyType parameter, which could lead to confusing behavior if an invalid enum value is passed.

Details

Location: Both validation methods

Recommendation: Add enum validation at the start:

if (!Enum.IsDefined(typeof(PolicyType), policyType))
{
    throw new ArgumentException($"Invalid policy type: {policyType}", nameof(policyType));
}

6. Test Coverage Gaps

Severity: 🎨 Low-Medium

Based on the codecov report showing 96.67% coverage with 2 lines missing:

Missing Test Cases
  1. Empty dictionary handling: Tests check null but not new Dictionary<string, object>() (though implementation treats both the same)
  2. Metadata validation errors: No test for the catch (JsonException) block in ValidateAndDeserializeMetadata (line 76-79)
  3. Multiple field errors: Tests only verify single field errors, not when multiple fields have wrong types
  4. Edge cases: Very large data payloads, deeply nested objects, etc.

Note: The 2 missing lines identified by codecov are likely in the catch (JsonException) block for metadata validation at lines 76-79.

7. Documentation Gap

Severity: 📝 Low

The validator class lacks XML documentation explaining its purpose, when to use it, and the validation strategy for different policy types.

Suggested Addition
/// <summary>
/// Centralized validator for policy data and metadata across all policy endpoints.
/// This class ensures type safety and data integrity before policy data is persisted.
/// </summary>
/// <remarks>
/// Only policy types with structured data models are validated. Policy types without
/// validation switches will accept any valid JSON structure.
/// </remarks>
public static class PolicyDataValidator

Security Assessment

No critical security issues identified

The changes align with Bitwarden's security principles:

  1. Zero-Knowledge Preserved: Validation occurs on structure/types only, not on sensitive content
  2. Input Validation: Properly validates input before persistence (defense in depth)
  3. Error Information Disclosure: Error messages are appropriately detailed without exposing internals
  4. No SQL Injection Risk: Uses parameterized queries (inherited from existing infrastructure)

Minor Security Note: Consider rate limiting on validation failures to prevent potential DoS through expensive validation operations, though this is likely handled at the API gateway level.


Good Practices Observed

  • ✅ Centralized validation logic (DRY principle)
  • ✅ Comprehensive integration test coverage for all three endpoints
  • ✅ Proper null safety with nullable reference types
  • ✅ Clear separation of concerns (validation utility separate from models)
  • ✅ Consistent error handling with BadRequestException
  • ✅ Field-specific error messages for better debugging
  • ✅ Both positive and negative test cases

Action Items for Author

High Priority:

  1. ❌ Address incomplete validation coverage (Issue Identity Server  #1) - Either add validation for remaining policy types or document why they're excluded
  2. ❌ Fix silent metadata validation failures (Issue Password Requirements Validation #2) - Make behavior consistent with data validation

Medium Priority:
3. 🎨 Use consistent JSON serialization options (Issue #3)
4. 🎨 Enhance error messages with received type information (Issue #4)

Low Priority:
5. 📝 Add enum validation for policy type parameter (Issue #5)
6. 📝 Add test for metadata validation error path (Issue #6)
7. 📝 Add class-level XML documentation (Issue #7)


Architecture & Design

The overall approach is sound:

Strengths:

  • Validator pattern is appropriate for this use case
  • Static methods are suitable since there's no state
  • Clear method naming and separation of concerns

🤔 Considerations for Future:

  • As more policy types are added, consider a registry pattern or attribute-based validation to avoid forgetting to update the validator
  • Consider extracting an interface if unit testing becomes more complex
  • May want to add telemetry/logging for validation failures to track client issues

Conclusion

This is a well-structured PR that successfully centralizes validation logic and adds comprehensive test coverage. The main concerns are:

  1. Incomplete coverage - Only 3/19 policy types are validated
  2. Silent failures - Metadata validation errors are swallowed instead of reported

Once these issues are addressed, the PR will significantly improve the robustness of policy data handling. The existing reviewers have already approved, but I recommend addressing at least the two high-priority items before merging.

Overall Assessment: ⚠️ Approve with Changes Recommended

The implementation quality is good, but the incomplete validation coverage is a significant gap that should be addressed to fully meet the PR objectives.


@r-tome r-tome removed the needs-qa label Nov 3, 2025
@r-tome r-tome merged commit 1e2e4b9 into main Nov 3, 2025
41 of 42 checks passed
@r-tome r-tome deleted the ac/pm-26429/add-validation-to-policy-data-and-metadata branch November 3, 2025 15:44
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.

4 participants