Skip to content

Develop - #478

Closed
ucswift wants to merge 2 commits into
masterfrom
develop
Closed

Develop#478
ucswift wants to merge 2 commits into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Aug 21, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a broad authentication and account-security upgrade across the platform, centered on session tracking, safer password reset flows, stronger SSO enforcement, and more secure web/API/eventing integration.

What changed

Added first-class user session tracking and revocation

  • Introduces persistent user session records, session validation, session activity tracking, and session revocation support.
  • Adds account-level authentication state fields so all existing credentials can be invalidated when needed.
  • Exposes active-session management in both the web UI and API, including:
    • viewing active sessions
    • revoking a single session
    • revoking other sessions
    • revoking all sessions
  • Automatically revokes sessions when:
    • a password changes or is reset
    • a username or email changes
    • a membership is disabled/deleted
    • an account is deactivated

Hardened password reset and credential-change flows

  • Replaces the old admin password-reset email pattern with more secure flows:
    • support for short-lived, single-use password recovery links
    • optional department setting to require admin-initiated resets to happen by email instead of letting admins choose the new password
  • Adds a public password recovery flow that:
    • uses one-time recovery tokens
    • rate-limits requests by account and IP
    • hides token details from URLs after initial use
    • revokes all sessions and tokens after the password is successfully changed
  • Adds notification emails for:
    • password recovery requests
    • administrator-changed passwords
  • Adds dedicated UI for users to change their own username/password and review/revoke sessions.

Added stronger MFA protection for privileged operations

  • Extends recent-2FA enforcement so specific sensitive operations can require step-up MFA even if department-wide admin MFA enforcement is otherwise disabled.
  • Applies this to administrator password reset actions and records the MFA verification time in audit data.

Improved SSO and external identity handling

  • Adds durable external identity link storage for SSO/SCIM-linked users.
  • Tightens SSO login linking rules by relying on stable external subject identifiers rather than mutable email alone.
  • Supports tracking whether accounts and emails are externally managed by SSO/SCIM.
  • Prevents local password login when SSO-linked accounts or department SSO policy should block it.
  • Prevents changing local credentials for SSO-managed accounts in the web UI.
  • Creates external identity links for SCIM-provisioned users and revokes department sessions when SCIM deactivates/deletes a user.

Secured SignalR/eventing and web-to-API access

  • Changes eventing hubs from anonymous access to authenticated access.
  • Adds per-request/per-invocation session validation for API and eventing traffic so revoked sessions stop working immediately.
  • Restricts event publishing methods to internal publisher identities.
  • Replaces browser-stored API bearer token usage with a same-origin web BFF pattern for web app API calls and eventing token acquisition.
  • Updates frontend callers to use the new BFF endpoints and anti-forgery protection instead of reading tokens from local storage.

Expanded auditing

  • Adds new audit/system-audit types for:
    • password changes
    • administrator password resets
    • password-reset link sending
    • public password reset completion
    • username changes
    • email changes
    • session revocations
    • external identity link changes
  • Expands system audit records with target user, session, and correlation identifiers to support authentication investigations.

Added department-level password reset policy

  • Adds a new department setting: Require password resets by email
  • Surfaces this option in department settings and updates UI text/localization accordingly.
  • Updates personnel/profile screens so reset actions reflect whether a department requires email-based resets.

Other fixes and security improvements

  • Fixes cached-object handling so empty/corrupt cache payloads are treated as misses instead of blank valid objects.
  • Adds serialization support for cached GIF search results and run card models so cache writes succeed.
  • Prevents weather alert messages from sending over SMS/chatbot channels when they are intended to be email/push only.
  • Hardens IP handling to rely on forwarded-header middleware instead of trusting raw forwarded headers directly.
  • Restricts SSL bypass usage to development/staging environments.
  • Converts several sensitive GET actions/forms to anti-forgery-protected POST flows.
  • Removes hardcoded OIDC connection string defaults and adds trusted client configuration for long-lived refresh tokens.

Functional impact

From a user and administrator perspective, this PR delivers:

  • better control over signed-in devices and sessions
  • safer password resets and credential changes
  • stronger protection for privileged account actions
  • clearer behavior for SSO-managed users
  • improved auditing and traceability for authentication-related actions
  • more secure browser/API/eventing communication patterns

It also lays the data and service foundation for session-aware authentication across web, API, eventing, SCIM, and console tooling.

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 151 files, which is 1 over the limit of 150.

To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to Pro+ to raise the limit.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a3ac34c2-6155-4787-a2a9-ba32d9f0f1dc

📥 Commits

Reviewing files that changed from the base of the PR and between d4ddd40 and b5320e7.

⛔ Files ignored due to path filters (25)
  • Core/Resgrid.Localization/Areas/User/Department/Department.en.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Department/Department.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Profile/Profile.ar.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Profile/Profile.de.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Profile/Profile.el.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Profile/Profile.en.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Profile/Profile.es.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Profile/Profile.fr.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Profile/Profile.it.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Profile/Profile.pl.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Profile/Profile.sv.resx is excluded by !**/*.resx
  • Core/Resgrid.Localization/Areas/User/Profile/Profile.uk.resx is excluded by !**/*.resx
  • Tests/Resgrid.Tests/Framework/CachedTypeSerializationTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/ClientSessionMetadataParserTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/CommunicationServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/DepartmentSettingsServicePasswordResetTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/DepartmentSsoServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/DepartmentsServiceCachedDepartmentTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/ExternalIdentityLinkServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/LocalIpLocationProviderTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/PasswordRecoveryServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/UserSessionServiceTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Web/Services/ConnectControllerSsoTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Web/TwoFactorEnforcementEvaluatorTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Web/User/ProfileControllerPasswordResetSecurityTests.cs is excluded by !**/Tests/**
📒 Files selected for processing (151)
  • Core/Resgrid.Config/OidcConfig.cs
  • Core/Resgrid.Config/SessionSecurityConfig.cs
  • Core/Resgrid.Model/AuditLogTypes.cs
  • Core/Resgrid.Model/DepartmentSettingTypes.cs
  • Core/Resgrid.Model/ExternalIdentityLinkMethod.cs
  • Core/Resgrid.Model/Identity/IdentityUser.cs
  • Core/Resgrid.Model/Providers/IEmailProvider.cs
  • Core/Resgrid.Model/Providers/IGifProvider.cs
  • Core/Resgrid.Model/Repositories/IIdentityRepository.cs
  • Core/Resgrid.Model/Repositories/IUserExternalIdentityLinksRepository.cs
  • Core/Resgrid.Model/Repositories/IUserSessionsRepository.cs
  • Core/Resgrid.Model/RunCard.cs
  • Core/Resgrid.Model/RunCardAlarmLevel.cs
  • Core/Resgrid.Model/RunCardAvailabilitySelection.cs
  • Core/Resgrid.Model/RunCardRoleRequirement.cs
  • Core/Resgrid.Model/RunCardTrigger.cs
  • Core/Resgrid.Model/RunCardUnitRequirement.cs
  • Core/Resgrid.Model/Security/PasswordRecoveryContracts.cs
  • Core/Resgrid.Model/Security/SessionClaimTypes.cs
  • Core/Resgrid.Model/Security/SessionCreationDeniedException.cs
  • Core/Resgrid.Model/Security/UserSessionContracts.cs
  • Core/Resgrid.Model/Services/IClientSessionMetadataParser.cs
  • Core/Resgrid.Model/Services/IDepartmentSettingsService.cs
  • Core/Resgrid.Model/Services/IEmailService.cs
  • Core/Resgrid.Model/Services/IExternalIdentityLinkService.cs
  • Core/Resgrid.Model/Services/IIpLocationProvider.cs
  • Core/Resgrid.Model/Services/IPasswordRecoveryService.cs
  • Core/Resgrid.Model/Services/IUserSessionService.cs
  • Core/Resgrid.Model/Services/IUsersService.cs
  • Core/Resgrid.Model/SystemAudit.cs
  • Core/Resgrid.Model/SystemAuditSystems.cs
  • Core/Resgrid.Model/SystemAuditTypes.cs
  • Core/Resgrid.Model/TwoFactor/TwoFactorEnforcementContext.cs
  • Core/Resgrid.Model/TwoFactor/TwoFactorEnforcementEvaluator.cs
  • Core/Resgrid.Model/UserExternalIdentityLink.cs
  • Core/Resgrid.Model/UserSession.cs
  • Core/Resgrid.Model/UserSessionAuthenticationMethod.cs
  • Core/Resgrid.Model/UserSessionClientApplication.cs
  • Core/Resgrid.Model/UserSessionRevocationReason.cs
  • Core/Resgrid.Model/UserSessionState.cs
  • Core/Resgrid.Services/AuditService.cs
  • Core/Resgrid.Services/ClientSessionMetadataParser.cs
  • Core/Resgrid.Services/CommunicationService.cs
  • Core/Resgrid.Services/DeleteService.cs
  • Core/Resgrid.Services/DepartmentSettingsService.cs
  • Core/Resgrid.Services/DepartmentSsoService.cs
  • Core/Resgrid.Services/DepartmentsService.cs
  • Core/Resgrid.Services/EmailService.cs
  • Core/Resgrid.Services/ExternalIdentityLinkService.cs
  • Core/Resgrid.Services/LocalIpLocationProvider.cs
  • Core/Resgrid.Services/PasswordRecoveryService.cs
  • Core/Resgrid.Services/ServicesModule.cs
  • Core/Resgrid.Services/UserSessionService.cs
  • Core/Resgrid.Services/UsersService.cs
  • Providers/Resgrid.Providers.Bus/SignalrProvider.cs
  • Providers/Resgrid.Providers.Cache/AzureRedisCacheProvider.cs
  • Providers/Resgrid.Providers.Claims/ClaimsPrincipalFactory.cs
  • Providers/Resgrid.Providers.Email/PostmarkTemplateProvider.cs
  • Providers/Resgrid.Providers.Email/Resgrid.Providers.Email.csproj
  • Providers/Resgrid.Providers.Email/Template/PasswordChangedByAdministrator.html
  • Providers/Resgrid.Providers.Email/Template/PasswordRecovery.html
  • Providers/Resgrid.Providers.Email/Template/PasswordReset.html
  • Providers/Resgrid.Providers.Migrations/Migrations/M0120_AddUserAuthenticationState.cs
  • Providers/Resgrid.Providers.Migrations/Migrations/M0121_AddUserSessions.cs
  • Providers/Resgrid.Providers.Migrations/Migrations/M0122_AddUserExternalIdentityLinks.cs
  • Providers/Resgrid.Providers.Migrations/Migrations/M0123_AddAuthenticationAuditContext.cs
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0120_AddUserAuthenticationStatePg.cs
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0121_AddUserSessionsPg.cs
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0122_AddUserExternalIdentityLinksPg.cs
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0123_AddAuthenticationAuditContextPg.cs
  • Repositories/Resgrid.Repositories.DataRepository/IdentityRepository.cs
  • Repositories/Resgrid.Repositories.DataRepository/Modules/ApiDataModule.cs
  • Repositories/Resgrid.Repositories.DataRepository/Modules/DataModule.cs
  • Repositories/Resgrid.Repositories.DataRepository/Modules/NonWebDataModule.cs
  • Repositories/Resgrid.Repositories.DataRepository/Modules/TestingDataModule.cs
  • Repositories/Resgrid.Repositories.DataRepository/UserExternalIdentityLinksRepository.cs
  • Repositories/Resgrid.Repositories.DataRepository/UserSessionsRepository.cs
  • Tools/Resgrid.Console/Commands/ResetPasswordCommand.cs
  • Web/Resgrid.Web.Eventing/Hubs/EventingHub.cs
  • Web/Resgrid.Web.Eventing/Middleware/SessionValidationHubFilter.cs
  • Web/Resgrid.Web.Eventing/Middleware/SessionValidationMiddleware.cs
  • Web/Resgrid.Web.Eventing/Startup.cs
  • Web/Resgrid.Web.Mcp/Startup.cs
  • Web/Resgrid.Web.Services/Controllers/v4/ConnectController.cs
  • Web/Resgrid.Web.Services/Controllers/v4/ScimController.cs
  • Web/Resgrid.Web.Services/Controllers/v4/SessionsController.cs
  • Web/Resgrid.Web.Services/Helpers/IpAddressHelper.cs
  • Web/Resgrid.Web.Services/Hubs/EventingHub.cs
  • Web/Resgrid.Web.Services/Middleware/SessionValidationHubFilter.cs
  • Web/Resgrid.Web.Services/Middleware/SessionValidationMiddleware.cs
  • Web/Resgrid.Web.Services/Resgrid.Web.Services.xml
  • Web/Resgrid.Web.Services/Startup.cs
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/chatHub.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/api.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/auth.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/browserConfig.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/eventingToken.ts
  • Web/Resgrid.Web/Areas/User/Apps/src/runtime/signalr.ts
  • Web/Resgrid.Web/Areas/User/Controllers/AccountController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/AccountSecurityController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/DepartmentController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/HomeController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/PersonnelController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs
  • Web/Resgrid.Web/Areas/User/Controllers/TwoFactorController.cs
  • Web/Resgrid.Web/Areas/User/Models/DepartmentSettingsModel.cs
  • Web/Resgrid.Web/Areas/User/Models/EditProfileModel.cs
  • Web/Resgrid.Web/Areas/User/Models/Personnel/PersonnelForJson.cs
  • Web/Resgrid.Web/Areas/User/Models/PersonnelModel.cs
  • Web/Resgrid.Web/Areas/User/Models/Profile/ResetPasswordForUserView.cs
  • Web/Resgrid.Web/Areas/User/Models/Security/AccountCredentialViews.cs
  • Web/Resgrid.Web/Areas/User/Models/Security/ActiveSessionsView.cs
  • Web/Resgrid.Web/Areas/User/Views/AccountSecurity/ChangePassword.cshtml
  • Web/Resgrid.Web/Areas/User/Views/AccountSecurity/ChangeUsername.cshtml
  • Web/Resgrid.Web/Areas/User/Views/AccountSecurity/Sessions.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Department/Settings.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Home/EditUserProfile.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Personnel/Index.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Profile/ResetPasswordForUser.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Profile/YourDepartments.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Shared/_Navigation.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Shared/_TopNavbar.cshtml
  • Web/Resgrid.Web/Areas/User/Views/Shared/_UserLayout.cshtml
  • Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/History.cshtml
  • Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Index.cshtml
  • Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Settings.cshtml
  • Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Zones.cshtml
  • Web/Resgrid.Web/Attributes/RequiresRecentTwoFactorAttribute.cs
  • Web/Resgrid.Web/Controllers/AccountController.cs
  • Web/Resgrid.Web/Controllers/WebApiBffController.cs
  • Web/Resgrid.Web/Helpers/ApiAuthHelper.cs
  • Web/Resgrid.Web/Helpers/IpAddressHelper.cs
  • Web/Resgrid.Web/Helpers/JavasriptHelpers.cs
  • Web/Resgrid.Web/Middleware/SessionValidationMiddleware.cs
  • Web/Resgrid.Web/Models/AccountViewModels/ForcePasswordChangeViewModel.cs
  • Web/Resgrid.Web/Models/AccountViewModels/ResetPasswordViewModel.cs
  • Web/Resgrid.Web/Startup.cs
  • Web/Resgrid.Web/Views/Account/ForcePasswordChange.cshtml
  • Web/Resgrid.Web/Views/Account/ResetPassword.cshtml
  • Web/Resgrid.Web/Views/Shared/Error.cshtml
  • Web/Resgrid.Web/Views/Shared/Unauthorized.cshtml
  • Web/Resgrid.Web/Views/Shared/_RecoveryLayout.cshtml
  • Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.addArchivedCall.js
  • Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.editcall.js
  • Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.newcall.js
  • Web/Resgrid.Web/wwwroot/js/app/internal/profile/resgrid.profile.yourdepartments.js
  • Web/Resgrid.Web/wwwroot/js/app/internal/routes/resgrid.routes.edit.js
  • Web/Resgrid.Web/wwwroot/js/app/internal/routes/resgrid.routes.new.js
  • Web/Resgrid.Web/wwwroot/js/app/public/resgrid.password-recovery.js
  • Workers/Resgrid.Workers.Console/Tasks/CleanOIDCScheduleTask.cs
  • Workers/Resgrid.Workers.Framework/Logic/AuditQueueLogic.cs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Review on demand using usage pricing

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

@request-info

request-info Bot commented Aug 21, 2026

Copy link
Copy Markdown

Thanks for opening this, but we'd appreciate a little more information. Could you update it with more details?

@Resgrid-Bot

Resgrid-Bot commented Aug 21, 2026

Copy link
Copy Markdown

Code Review Could Not Complete ⚠️

The review failed before suggestions could be generated.

Reason: Rate limit reached on the provider (openai). Try again in a few minutes.

After fixing the issue, comment @kody review on this PR to re-run the review.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

@ucswift ucswift closed this Aug 21, 2026
/// Comma-separated, registered client IDs allowed to receive the longer mobile
/// refresh-token lifetime. Anonymous requests and caller-supplied scopes never qualify.
/// </summary>
public static string TrustedLongLivedClientIds = "";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Mutable configuration state identified in Core/Resgrid.Config/OidcConfig.cs and the related fields in Core/Resgrid.Config/SessionSecurityConfig.cs:7-20, Core/Resgrid.Services/DepartmentSettingsService.cs:27, and Tests/Resgrid.Tests/Services/UserSessionServiceTests.cs:19-24. Mark TrustedLongLivedClientIds as readonly to communicate immutable configuration intent and prevent accidental reassignment.

Kody rule violation: Use `readonly` or `const` for Immutable Data

public static readonly string TrustedLongLivedClientIds = string.Empty;
Prompt for LLM

File Core/Resgrid.Config/OidcConfig.cs:

Line 28:

Mutable configuration state identified in Core/Resgrid.Config/OidcConfig.cs and the related fields in Core/Resgrid.Config/SessionSecurityConfig.cs:7-20, Core/Resgrid.Services/DepartmentSettingsService.cs:27, and Tests/Resgrid.Tests/Services/UserSessionServiceTests.cs:19-24. Mark TrustedLongLivedClientIds as readonly to communicate immutable configuration intent and prevent accidental reassignment.

Suggested Code:

		public static readonly string TrustedLongLivedClientIds = string.Empty;

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

/// <param name="departmentName">Name of the department.</param>
Task<bool> SendPasswordResetEmail(string emailAddress, string name, string userName, string password,
Task<bool> SendPasswordRecoveryEmail(string emailAddress, string name, string departmentName,
string resetUrl, string ipAddress, string userAgent, DateTime requestedOn, bool isSsoManaged);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Sensitive diagnostics exposure identified in Core/Resgrid.Model/Services/IEmailService.cs through ipAddress and userAgent, with related usage in Core/Resgrid.Model/UserSession.cs:44, Web/Resgrid.Web.Services/Controllers/v4/SessionsController.cs:89, Providers/Resgrid.Providers.Email/Template/PasswordRecovery.html:40, and the listed JavaScript files. Ensure implementations redact or hash ipAddress and userAgent before logging, and avoid passing raw values unless strictly necessary.

Kody rule violation: Mask PII and secrets in logs

Prompt for LLM

File Core/Resgrid.Model/Services/IEmailService.cs:

Line 30:

Sensitive diagnostics exposure identified in Core/Resgrid.Model/Services/IEmailService.cs through ipAddress and userAgent, with related usage in Core/Resgrid.Model/UserSession.cs:44, Web/Resgrid.Web.Services/Controllers/v4/SessionsController.cs:89, Providers/Resgrid.Providers.Email/Template/PasswordRecovery.html:40, and the listed JavaScript files. Ensure implementations redact or hash ipAddress and userAgent before logging, and avoid passing raw values unless strictly necessary.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

/// <param name="departmentName">Name of the department.</param>
Task<bool> SendPasswordResetEmail(string emailAddress, string name, string userName, string password,
Task<bool> SendPasswordRecoveryEmail(string emailAddress, string name, string departmentName,
string resetUrl, string ipAddress, string userAgent, DateTime requestedOn, bool isSsoManaged);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Privacy-sensitive telemetry exposure identified in Core/Resgrid.Model/Services/IEmailService.cs through ipAddress and userAgent, with related usage in Providers/Resgrid.Providers.Email/Template/PasswordRecovery.html:39-40, Web/Resgrid.Web.Services/Controllers/v4/SessionsController.cs:89, and the listed JavaScript files. Minimize downstream logging and metrics of raw PII and include purpose or lawful-basis context wherever diagnostics are emitted.

Kody rule violation: Redact PII in logs and metrics by default

Prompt for LLM

File Core/Resgrid.Model/Services/IEmailService.cs:

Line 30:

Privacy-sensitive telemetry exposure identified in Core/Resgrid.Model/Services/IEmailService.cs through ipAddress and userAgent, with related usage in Providers/Resgrid.Providers.Email/Template/PasswordRecovery.html:39-40, Web/Resgrid.Web.Services/Controllers/v4/SessionsController.cs:89, and the listed JavaScript files. Minimize downstream logging and metrics of raw PII and include purpose or lawful-basis context wherever diagnostics are emitted.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

return string.Equals(storedToken, bearerToken, StringComparison.Ordinal);
return FixedTimeSecretEquals(storedToken, bearerToken);
}
catch (Exception ex)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Undiagnosable exception path identified in Core/Resgrid.Services/DepartmentSsoService.cs and the related catch sites. Log the failure with structured context in catch (Exception ex), including departmentId and departmentCode, so SCIM bearer token validation failures remain traceable.

Kody rule violation: Include error context in structured logs

catch (Exception ex)
{
	_logger.LogError(ex, "SCIM bearer token validation failed", new { departmentId, departmentCode });
	return false;
}
Prompt for LLM

File Core/Resgrid.Services/DepartmentSsoService.cs:

Line 354:

Undiagnosable exception path identified in Core/Resgrid.Services/DepartmentSsoService.cs and the related catch sites. Log the failure with structured context in catch (Exception ex), including departmentId and departmentCode, so SCIM bearer token validation failures remain traceable.

Suggested Code:

			catch (Exception ex)
			{
				_logger.LogError(ex, "SCIM bearer token validation failed", new { departmentId, departmentCode });
				return false;
			}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

{
if (!IPAddress.TryParse(ipAddress, out var address) ||
string.IsNullOrWhiteSpace(SessionSecurityConfig.IpLocationDatabasePath))
return Task.FromResult<IpLocationResult>(null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules critical

Nullable Task result identified in Core/Resgrid.Services/LocalIpLocationProvider.cs. Returning Task.FromResult(null) from a Task-returning method can produce unexpected null dereferences after await, so use an explicitly handled non-null task result pattern or redesign the contract.

Kody rule violation: Avoid Returning Null in Non-Async Task Methods

return Task.FromResult<IpLocationResult>(default(IpLocationResult));
Prompt for LLM

File Core/Resgrid.Services/LocalIpLocationProvider.cs:

Line 33:

Nullable Task result identified in Core/Resgrid.Services/LocalIpLocationProvider.cs. Returning Task.FromResult<IpLocationResult>(null) from a Task-returning method can produce unexpected null dereferences after await, so use an explicitly handled non-null task result pattern or redesign the contract.

Suggested Code:

return Task.FromResult<IpLocationResult>(default(IpLocationResult));

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

// bypass SSL certificate validation
clientHandler.ServerCertificateCustomValidationCallback +=
(sender, certificate, chain, sslPolicyErrors) => { return true; };
clientHandler.ServerCertificateCustomValidationCallback =

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules critical

TLS certificate validation bypass identified in Providers/Resgrid.Providers.Bus/SignalrProvider.cs:136 and Web/Resgrid.Web/Startup.cs:149 via clientHandler.ServerCertificateCustomValidationCallback. Skipping server certificate validation allows impersonation and man-in-the-middle interception of secure communications.

Kody rule violation: Verify SSL/TLS Server Certificates

Prompt for LLM

File Providers/Resgrid.Providers.Bus/SignalrProvider.cs:

Line 104:

TLS certificate validation bypass identified in Providers/Resgrid.Providers.Bus/SignalrProvider.cs:136 and Web/Resgrid.Web/Startup.cs:149 via clientHandler.ServerCertificateCustomValidationCallback. Skipping server certificate validation allows impersonation and man-in-the-middle interception of secure communications.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

newEmail.To.Add(email);
return await _emailSender.Send(newEmail);
}
catch (Exception)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Swallowed exception identified in Providers/Resgrid.Providers.Email/PostmarkTemplateProvider.cs and Web/Resgrid.Web.Eventing/Middleware/SessionValidationHubFilter.cs:109. Capture the exception in catch (Exception ex), log it with relevant identifiers such as email, departmentName, and userName, and then return false or rethrow so failures remain diagnosable.

Kody rule violation: Avoid empty catch blocks

catch (Exception ex)
{
	_logger.LogError(ex, "Failed to send password-changed-by-administrator email", new { email, departmentName, userName });
	return false;
}
Prompt for LLM

File Providers/Resgrid.Providers.Email/PostmarkTemplateProvider.cs:

Line 374:

Swallowed exception identified in Providers/Resgrid.Providers.Email/PostmarkTemplateProvider.cs and Web/Resgrid.Web.Eventing/Middleware/SessionValidationHubFilter.cs:109. Capture the exception in catch (Exception ex), log it with relevant identifiers such as email, departmentName, and userName, and then return false or rethrow so failures remain diagnosable.

Suggested Code:

			catch (Exception ex)
			{
				_logger.LogError(ex, "Failed to send password-changed-by-administrator email", new { email, departmentName, userName });
				return false;
			}

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

<div class="details">
<strong>Request details</strong><br>
Time (UTC): {{requested_on}}<br>
IP address: {{ip_address}}<br>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Sensitive request metadata exposure identified in Providers/Resgrid.Providers.Email/Template/PasswordRecovery.html and the related sites in Web/Resgrid.Web.Services/Controllers/v4/SessionsController.cs:89 and the listed JavaScript files. Avoid displaying or logging raw IP address values like {{ip_address}}, or replace them with redacted or tokenized data if operationally required.

Kody rule violation: Do not log PHI; mask and drop sensitive fields

Prompt for LLM

File Providers/Resgrid.Providers.Email/Template/PasswordRecovery.html:

Line 39:

Sensitive request metadata exposure identified in Providers/Resgrid.Providers.Email/Template/PasswordRecovery.html and the related sites in Web/Resgrid.Web.Services/Controllers/v4/SessionsController.cs:89 and the listed JavaScript files. Avoid displaying or logging raw IP address values like {{ip_address}}, or replace them with redacted or tokenized data if operationally required.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

var path = Path.GetTempFileName();
try
{
await File.WriteAllTextAsync(path, """

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Unannotated file-system failure path identified in Tests/Resgrid.Tests/Services/LocalIpLocationProviderTests.cs:38 and the related I/O call sites. Wrap File.WriteAllTextAsync(path, ...) in try/catch for IOException or UnauthorizedAccessException and include the operation and temp file path in the failure message so test failures remain diagnosable.

Kody rule violation: Add try-catch blocks for external calls

try
{
	await File.WriteAllTextAsync(path, """
	[
	  { "network": "203.0.0.0/16", "country": "US", "region": "Broad" },
	  { "network": "203.0.113.0/24", "country": "US", "region": "California", "city": "Example City" }
	]
	""");
}
catch (IOException ex)
{
	Assert.Fail($"Failed to write temp IP location database for test '{nameof(longest_matching_local_cidr_provides_only_coarse_location)}' at path '{path}': {ex.Message}");
}
Prompt for LLM

File Tests/Resgrid.Tests/Services/LocalIpLocationProviderTests.cs:

Line 20:

Unannotated file-system failure path identified in Tests/Resgrid.Tests/Services/LocalIpLocationProviderTests.cs:38 and the related I/O call sites. Wrap File.WriteAllTextAsync(path, ...) in try/catch for IOException or UnauthorizedAccessException and include the operation and temp file path in the failure message so test failures remain diagnosable.

Suggested Code:

				try
				{
					await File.WriteAllTextAsync(path, """
					[
					  { "network": "203.0.0.0/16", "country": "US", "region": "Broad" },
					  { "network": "203.0.113.0/24", "country": "US", "region": "California", "city": "Example City" }
					]
					""");
				}
				catch (IOException ex)
				{
					Assert.Fail($"Failed to write temp IP location database for test '{nameof(longest_matching_local_cidr_provides_only_coarse_location)}' at path '{path}': {ex.Message}");
				}


Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

} catch (e) {}
return '';
function getAntiForgeryToken() {
return document.querySelector('meta[name="request-verification-token"]').content;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Null dereference risk identified in Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Settings.cshtml and the related querySelector call sites, including Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Zones.cshtml:166, Providers/Resgrid.Providers.Claims/ClaimsPrincipalFactory.cs:67, Web/Resgrid.Web/wwwroot/js/app/internal/profile/resgrid.profile.yourdepartments.js:51, Tests/Resgrid.Tests/Web/User/ProfileControllerPasswordResetSecurityTests.cs:51 and :61, and Web/Resgrid.Web/wwwroot/js/app/public/resgrid.password-recovery.js:11-12. querySelector(...) can return null, so access .content with optional chaining and a fallback.

Kody rule violation: Add null checks to prevent NullReferenceException

return document.querySelector('meta[name="request-verification-token"]')?.content ?? '';
Prompt for LLM

File Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Settings.cshtml:

Line 304:

Null dereference risk identified in Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Settings.cshtml and the related querySelector call sites, including Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Zones.cshtml:166, Providers/Resgrid.Providers.Claims/ClaimsPrincipalFactory.cs:67, Web/Resgrid.Web/wwwroot/js/app/internal/profile/resgrid.profile.yourdepartments.js:51, Tests/Resgrid.Tests/Web/User/ProfileControllerPasswordResetSecurityTests.cs:51 and :61, and Web/Resgrid.Web/wwwroot/js/app/public/resgrid.password-recovery.js:11-12. querySelector(...) can return null, so access .content with optional chaining and a fallback.

Suggested Code:

			return document.querySelector('meta[name="request-verification-token"]')?.content ?? '';

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

} catch (e) {}
return '';
function getAntiForgeryToken() {
return document.querySelector('meta[name="request-verification-token"]').content;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Null dereference risk identified in Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Settings.cshtml and Tests/Resgrid.Tests/Web/User/ProfileControllerPasswordResetSecurityTests.cs:51. document.querySelector('meta[name="request-verification-token"]') can return null, so access .content with optional chaining and a default value.

Kody rule violation: Add null checks before accessing properties

return document.querySelector('meta[name="request-verification-token"]')?.content ?? '';
Prompt for LLM

File Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Settings.cshtml:

Line 304:

Null dereference risk identified in Web/Resgrid.Web/Areas/User/Views/WeatherAlerts/Settings.cshtml and Tests/Resgrid.Tests/Web/User/ProfileControllerPasswordResetSecurityTests.cs:51. document.querySelector('meta[name="request-verification-token"]') can return null, so access .content with optional chaining and a default value.

Suggested Code:

			return document.querySelector('meta[name="request-verification-token"]')?.content ?? '';

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

{
if (RequireForOperation)
{
context.Result = new StatusCodeResult(StatusCodes.Status503ServiceUnavailable);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules critical

Blocking async call identified in Web/Resgrid.Web/Attributes/RequiresRecentTwoFactorAttribute.cs, including line 88. Using .Result or .Wait() can deadlock the request pipeline and degrades asynchronous execution; replace it with await.

Kody rule violation: Avoid Blocking Calls to Async Methods

Prompt for LLM

File Web/Resgrid.Web/Attributes/RequiresRecentTwoFactorAttribute.cs:

Line 75:

Blocking async call identified in Web/Resgrid.Web/Attributes/RequiresRecentTwoFactorAttribute.cs, including line 88. Using .Result or .Wait() can deadlock the request pipeline and degrades asynchronous execution; replace it with await.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

{
if (RequireForOperation)
{
context.Result = new StatusCodeResult(StatusCodes.Status503ServiceUnavailable);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Async blocking identified in Web/Resgrid.Web/Attributes/RequiresRecentTwoFactorAttribute.cs, including line 88. Blocking with .Result or .Wait() can deadlock the request path and breaks the team's async rule; use async/await end-to-end and configure awaits appropriately.

Kody rule violation: Await async operations properly

Prompt for LLM

File Web/Resgrid.Web/Attributes/RequiresRecentTwoFactorAttribute.cs:

Line 75:

Async blocking identified in Web/Resgrid.Web/Attributes/RequiresRecentTwoFactorAttribute.cs, including line 88. Blocking with .Result or .Wait() can deadlock the request path and breaks the team's async rule; use async/await end-to-end and configure awaits appropriately.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

}),
contentType: 'application/json',
contentType: 'application/json',
headers: { 'RequestVerificationToken': $('input[name="__RequestVerificationToken"]').first().val() },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Missing antiforgery token guard identified in Web/Resgrid.Web/wwwroot/js/app/internal/profile/resgrid.profile.yourdepartments.js and the related request setup sites. Validate $('input[name="__RequestVerificationToken"]').first().val() before issuing the async POST so the operation fails deterministically instead of sending an invalid request.

Kody rule violation: Handle async operations with proper error handling

const token = $('input[name="__RequestVerificationToken"]').first().val();
if (!token) {
	console.error('switchActiveDepartment missing antiforgery token', { op: 'switchActiveDepartment', departmentId });
	return;
}
headers: { RequestVerificationToken: token },
Prompt for LLM

File Web/Resgrid.Web/wwwroot/js/app/internal/profile/resgrid.profile.yourdepartments.js:

Line 51:

Missing antiforgery token guard identified in Web/Resgrid.Web/wwwroot/js/app/internal/profile/resgrid.profile.yourdepartments.js and the related request setup sites. Validate $('input[name="__RequestVerificationToken"]').first().val() before issuing the async POST so the operation fails deterministically instead of sending an invalid request.

Suggested Code:

					const token = $('input[name="__RequestVerificationToken"]').first().val();
					if (!token) {
						console.error('switchActiveDepartment missing antiforgery token', { op: 'switchActiveDepartment', departmentId });
						return;
					}
					headers: { RequestVerificationToken: token },

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

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.

2 participants