Skip to content

RG-T117 Fixes - #454

Merged
ucswift merged 1 commit into
masterfrom
develop
Aug 8, 2026
Merged

RG-T117 Fixes#454
ucswift merged 1 commit into
masterfrom
develop

Conversation

@ucswift

@ucswift ucswift commented Aug 8, 2026

Copy link
Copy Markdown
Member

PR Description (RG-T117 Fixes)

This PR addresses multiple bugs and improvements across the Resgrid platform:

Chat & Assistant

  • Expanded chatbot calendar recognition: The keyword intent classifier now understands upcoming-calendar phrasings (e.g., "when is the next event?", "what's coming up", "upcoming events", "next meetings").
  • Idempotent chat reactions: Adding the same emoji reaction twice (common double-tap scenario) now no-ops before hitting the database, preventing error log flooding from unique-constraint violations.
  • Disabled message editing in assistant conversations: Edit is now blocked for chatbot channels both server-side (controller returns BadRequest) and client-side (edit affordance hidden), consistent with existing restrictions on reactions, threads, and deletes.
  • Excluded assistant channels from the chat popout: Chatbot channels and their unread counts no longer appear in the standard chat panel, since the assistant has its own dedicated drawer.
  • Fixed reactions popover clipping: The quick-reactions popover in narrow panels (footer popout / assistant drawer) now anchors to the left edge instead of being clipped on the right.
  • Conditionally rendered composer file input: The hidden image upload input is now only rendered when image attachments are allowed.

Database

  • Fixed misspelled column name: The initial migration created Catery instead of Category in the Notes and Documents tables, causing every save to fail. Migration 113 renames the column (for both SQL Server and PostgreSQL), guarded so already-correct databases are untouched.

Security & Reliability

  • Hardened reCAPTCHA validation: Switched from per-call HttpClient instantiation (socket exhaustion under load) to a shared static client with a 10-second timeout, changed from GET to POST to keep the secret out of URLs/logs, and added graceful failure handling for transient network errors.
  • Validated language endpoint input: The anonymous SetLanguage endpoint now whitelists culture values against supported locales, preventing scanner garbage (SQL fragments, paths, etc.) from triggering unhandled exceptions and 500 errors.

@Resgrid-Bot

Resgrid-Bot commented Aug 8, 2026

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

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.

@request-info

request-info Bot commented Aug 8, 2026

Copy link
Copy Markdown

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

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request updates chatbot intent matching and chat restrictions, prevents duplicate reactions, adds database column repair migrations, changes reCAPTCHA verification handling, validates language selection, and adjusts chat UI behavior.

Changes

Chat and assistant behavior

Layer / File(s) Summary
Chatbot matching and reaction handling
Core/Resgrid.Chatbot.NLU/Providers/KeywordIntentClassifier.cs, Core/Resgrid.Services/ChatMessageService.cs
Calendar phrases now map to list_calendar. Duplicate reactions return successfully before insertion.
Assistant conversation restrictions
Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs, Web/Resgrid.Web.Services/Resgrid.Web.Services.xml, Web/Resgrid.Web/Areas/User/Apps/src/components/chat/ConversationView.tsx, Web/Resgrid.Web/Areas/User/Apps/src/components/chat/atoms/MessageBubble.tsx
Assistant conversation messages cannot be edited through the API or UI. Supporting documentation lists edits as unsupported.
Chat presentation and inputs
Web/Resgrid.Web/Areas/User/Apps/src/components/chat/ChatPanelElement.tsx, Web/Resgrid.Web/Areas/User/Apps/src/components/chat/atoms/Composer.tsx, Web/Resgrid.Web/Areas/User/Apps/src/components/chat/chat.css
Chatbot channels are excluded from the chat panel and unread counts. Image inputs render only when enabled. Narrow-host reaction popovers anchor from the left.

Category column migrations

Layer / File(s) Summary
Category column repairs
Providers/Resgrid.Providers.Migrations/Migrations/M0113_FixNotesDocumentsCategoryColumn.cs, Providers/Resgrid.Providers.MigrationsPg/Migrations/M0113_FixNotesDocumentsCategoryColumnPg.cs
Migration 113 conditionally renames misspelled category columns in Notes and Documents for SQL Server and PostgreSQL. Rollbacks are no-ops.

Web request validation

Layer / File(s) Summary
reCAPTCHA verification
Web/Resgrid.Web/Attributes/GoogleReCaptchaValidationAttribute.cs
Verification uses a shared 10-second HttpClient, form-encoded POST requests, and logged fail-closed handling for request exceptions and cancellation.
Locale validation
Web/Resgrid.Web/Controllers/AccountController.cs
Language selection updates culture state only when the trimmed, case-insensitive value matches a supported locale.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • Resgrid/Core#428: Both changes update calendar intent patterns in KeywordIntentClassifier.cs.
  • Resgrid/Core#450: Both changes modify chat UI and chatbot channel behavior.
  • Resgrid/Core#453: Both changes extend chatbot conversation restrictions across chat components and services.

Suggested reviewers: github-actions

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title identifies a ticket and indicates fixes, but it does not describe the main changes in the pull request. Replace the generic title with a concise summary of the primary changes, such as chatbot chat restrictions, migration fixes, and validation updates.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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)
Core/Resgrid.Services/ChatMessageService.cs (1)

346-358: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Optional: narrow the duplicate-reaction check.

The duplicate check loads every reaction on the message just to test for one emoji/user match. For messages with many reactions, a targeted existence query (for example ExistsAsync(chatMessageId, participantType, userId, unitId, emoji)) would avoid loading the full reaction set on every AddReactionAsync call.

Reaction counts per message are usually small, so this is a minor concern. Consider it only if the repository already exposes (or can cheaply expose) a narrower lookup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Core/Resgrid.Services/ChatMessageService.cs` around lines 346 - 358, The
duplicate check in AddReactionAsync currently loads all reactions through
GetByMessageIdsAsync before matching one reaction. If the repository supports or
can cheaply add a targeted existence lookup, update this check to query by
chatMessageId, participant identity, unitId, and emoji while preserving the
existing true-return behavior; otherwise leave the current implementation
unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Web/Resgrid.Web/Attributes/GoogleReCaptchaValidationAttribute.cs`:
- Around line 43-49: Replace the synchronous verification in
GoogleReCaptchaValidationAttribute.IsValid with an asynchronous action filter or
equivalent request component that awaits both the reCAPTCHA POST and
response-body read without blocking request threads. Preserve the existing
validation outcome for non-OK responses and verification failures, and add those
failures to ModelState before the action executes.
- Around line 49-50: Update the JSON handling in
GoogleReCaptchaValidationAttribute so JObject.Parse failures for malformed or
non-object responses are caught separately from transport errors. Log the
parsing exception through Framework.Logging.LogException and return the existing
retryable validation result, preserving the current behavior for valid object
responses.

---

Nitpick comments:
In `@Core/Resgrid.Services/ChatMessageService.cs`:
- Around line 346-358: The duplicate check in AddReactionAsync currently loads
all reactions through GetByMessageIdsAsync before matching one reaction. If the
repository supports or can cheaply add a targeted existence lookup, update this
check to query by chatMessageId, participant identity, unitId, and emoji while
preserving the existing true-return behavior; otherwise leave the current
implementation unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a1d315c1-04a4-4142-bb8f-defd530abaec

📥 Commits

Reviewing files that changed from the base of the PR and between 01c6337 and ba8fb04.

⛔ Files ignored due to path filters (2)
  • Tests/Resgrid.Tests/Chatbot/ChatbotAvailabilityIntentClassifierTests.cs is excluded by !**/Tests/**
  • Tests/Resgrid.Tests/Services/ChatMessageServiceTests.cs is excluded by !**/Tests/**
📒 Files selected for processing (15)
  • Core/Resgrid.Chatbot.NLU/Providers/KeywordIntentClassifier.cs
  • Core/Resgrid.Services/ChatMessageService.cs
  • Providers/Resgrid.Providers.Migrations/Migrations/M0113_FixNotesDocumentsCategoryColumn.cs
  • Providers/Resgrid.Providers.Migrations/Sql/M0001_InitialMigration.sql
  • Providers/Resgrid.Providers.MigrationsPg/Migrations/M0113_FixNotesDocumentsCategoryColumnPg.cs
  • Providers/Resgrid.Providers.MigrationsPg/Sql/M0001_InitialMigration.sql
  • Web/Resgrid.Web.Services/Controllers/v4/ChatController.cs
  • Web/Resgrid.Web.Services/Resgrid.Web.Services.xml
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/ChatPanelElement.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/ConversationView.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/atoms/Composer.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/atoms/MessageBubble.tsx
  • Web/Resgrid.Web/Areas/User/Apps/src/components/chat/chat.css
  • Web/Resgrid.Web/Attributes/GoogleReCaptchaValidationAttribute.cs
  • Web/Resgrid.Web/Controllers/AccountController.cs

Comment on lines +43 to +49
var httpResponse = _httpClient.PostAsync("https://www.google.com/recaptcha/api/siteverify", content).GetAwaiter().GetResult();
if (httpResponse.StatusCode != HttpStatusCode.OK)
{
return errorResult.Value;
}

return ValidationResult.Success;
String jsonResponse = httpResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate existing async request-validation components and all reCAPTCHA uses.
rg -n -C 3 --glob '*.cs' \
  'GoogleReCaptchaValidationAttribute|IAsync(Action|Authorization|Resource)Filter|ModelState\.AddModelError' \
  Web

Repository: Resgrid/Core

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the reCAPTCHA attribute and focused usages without dumping the entire Web/Controllers match list.
printf -- '--- GoogleReCaptchaValidationAttribute.cs ---\n'
cat -n Web/Resgrid.Web/Attributes/GoogleReCaptchaValidationAttribute.cs

printf -- '\n--- GoogleReCaptchaValidationAttribute references ---\n'
rg -n --glob '*.cshtml' --glob '*.cs' --glob '*.razor' 'GoogleReCaptchaValidation|g-recaptcha|recaptcha' Web/Resgrid.Web

printf -- '\n--- AccountController POST snippets around register actions ---\n'
rg -n -A 80 -B 10 'Register|RegisterModel|GoogleReCaptcha|g-recaptcha' Web/Resgrid.Web/Controllers/AccountController.cs

Repository: Resgrid/Core

Length of output: 10069


Move reCAPTCHA verification out of synchronous model validation.

IsValid() blocks the request thread for both the POST and response-body read, up to the 10-second timeout. Replace the synchronous ValidationAttribute path with an async filter or equivalent request component so concurrent validation calls do not block request threads, and add failures to ModelState.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Web/Resgrid.Web/Attributes/GoogleReCaptchaValidationAttribute.cs` around
lines 43 - 49, Replace the synchronous verification in
GoogleReCaptchaValidationAttribute.IsValid with an asynchronous action filter or
equivalent request component that awaits both the reCAPTCHA POST and
response-body read without blocking request threads. Preserve the existing
validation outcome for non-OK responses and verification failures, and add those
failures to ModelState before the action executes.

Comment on lines +49 to +50
String jsonResponse = httpResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();
dynamic jsonData = JObject.Parse(jsonResponse);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Confirm the installed Newtonsoft.Json reference and inspect parsing exception handling.
rg -n -C 3 --glob '*.csproj' --glob 'packages.config' --glob '*.cs' \
  'Newtonsoft\.Json|JObject\.Parse|JsonException|JsonReaderException' \
  Web

Repository: Resgrid/Core

Length of output: 43035


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the validation attribute around the parsing and exception-handling logic.
sed -n '1,140p' Web/Resgrid.Web/Attributes/GoogleReCaptchaValidationAttribute.cs | cat -n

# Search for the exact method/attribute to ensure context is complete.
rg -n "class GoogleReCaptchaValidationAttribute|ValidationAttribute|LogException|TryVerify|OnAuthorization" Web/Resgrid.Web/Attributes/GoogleReCaptchaValidationAttribute.cs

Repository: Resgrid/Core

Length of output: 3195


🌐 Web query:

Newtonsoft.Json 13.0.3 JObject.Parse JsonException documentation

💡 Result:

In Newtonsoft.Json version 13.0.3, the JObject.Parse method is designed to parse a JSON string into a JObject [1][2]. If the provided string is not valid JSON or does not represent a JSON object (e.g., if it represents a JSON array or a primitive), the method throws a JsonReaderException [1][3][4]. JsonReaderException is a subclass of JsonException [5][6]. This exception typically includes detailed information about the error, such as the path in the JSON string where the error occurred, the line number, and the position [5]. Common scenarios that trigger this exception include: - Passing invalid JSON syntax [1][4]. - Passing a root JSON element that is not an object, such as a JSON array ("[]") [3]. In such cases, if you expect an array, JArray.Parse should be used instead; if the JSON structure is unknown, JToken.Parse is the appropriate method [3]. When handling parsing errors, it is standard practice to catch JsonReaderException specifically to handle malformed input, rather than catching the base JsonException or the generic System.Exception, which may obscure other issues [7][5][6].

Citations:


Fail closed when Google returns a non-object reCAPTCHA response.

JObject.Parse(jsonResponse) throws for malformed JSON and non-object status-200 payloads, but the filter at line 58 only catches transport failures. Catch the JSON parsing exception, log it with Framework.Logging.LogException, and return the same retryable validation result instead of 500-ing the page.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Web/Resgrid.Web/Attributes/GoogleReCaptchaValidationAttribute.cs` around
lines 49 - 50, Update the JSON handling in GoogleReCaptchaValidationAttribute so
JObject.Parse failures for malformed or non-object responses are caught
separately from transport errors. Log the parsing exception through
Framework.Logging.LogException and return the existing retryable validation
result, preserving the current behavior for valid object responses.

if (supported != null)
{
Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)), new CookieOptions { Expires = DateTime.UtcNow.AddYears(1) });
Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName, CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(supported)), new CookieOptions { Expires = DateTime.UtcNow.AddYears(1) });
// Upcoming-calendar phrasings: "when is the next event?", "what is upcoming in the
// calendar?", "upcoming events", "what's coming up", "next events".
(R(@"^when('?s|\s+is)\s+(the\s+)?next\s+(event|meeting|training|class)(s|es)?$"),
"list_calendar", 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 low

Hardcoded string literal "list_calendar" represents a finite intent set prone to typos and poor discoverability. Define an enum (e.g., IntentTypes.ListCalendar) or a constants class and reference it in place of the literal.

Kody rule violation: Use enums instead of magic strings

Prompt for LLM

File Core/Resgrid.Chatbot.NLU/Providers/KeywordIntentClassifier.cs:

Line 179:

Hardcoded string literal `"list_calendar"` represents a finite intent set prone to typos and poor discoverability. Define an enum (e.g., `IntentTypes.ListCalendar`) or a constants class and reference it in place of the literal.

Talk to Kody by mentioning @kody

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

// Upcoming-calendar phrasings: "when is the next event?", "what is upcoming in the
// calendar?", "upcoming events", "what's coming up", "next events".
(R(@"^when('?s|\s+is)\s+(the\s+)?next\s+(event|meeting|training|class)(s|es)?$"),
"list_calendar", 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 low

Shared string literal "list_calendar" repeats across multiple entries in KeywordIntentClassifier.cs:181 and KeywordIntentClassifier.cs:183, risking inconsistency. Define a constant (e.g., const string ListCalendarIntent = "list_calendar") in a shared intents class and reference it here.

Kody rule violation: Centralize string constants

Prompt for LLM

File Core/Resgrid.Chatbot.NLU/Providers/KeywordIntentClassifier.cs:

Line 179:

Shared string literal `"list_calendar"` repeats across multiple entries in `KeywordIntentClassifier.cs:181` and `KeywordIntentClassifier.cs:183`, risking inconsistency. Define a constant (e.g., `const string ListCalendarIntent = "list_calendar"`) in a shared intents class and reference it here.

Talk to Kody by mentioning @kody

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

// reaches the database (RepositoryBase logs every insert exception, so relying on the
// unique-violation catch below alone floods the error log). The catch still covers the
// genuine concurrent race two requests can win simultaneously.
var existingReactions = await _chatMessageReactionRepository.GetByMessageIdsAsync(new[] { chatMessageId });

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

Unguarded await: GetByMessageIdsAsync sits before the try block at line 359, so transient failures (timeouts, deadlocks) propagate unhandled. Move the read inside the try block or wrap it in its own try/catch that logs context (messageId, emoji) and returns a safe default.

Kody rule violation: Handle async operations with proper error handling

Prompt for LLM

File Core/Resgrid.Services/ChatMessageService.cs:

Line 350:

Unguarded await: `GetByMessageIdsAsync` sits before the try block at line 359, so transient failures (timeouts, deadlocks) propagate unhandled. Move the read inside the try block or wrap it in its own try/catch that logs context (`messageId`, `emoji`) and returns a safe default.

Talk to Kody by mentioning @kody

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

// reaches the database (RepositoryBase logs every insert exception, so relying on the
// unique-violation catch below alone floods the error log). The catch still covers the
// genuine concurrent race two requests can win simultaneously.
var existingReactions = await _chatMessageReactionRepository.GetByMessageIdsAsync(new[] { chatMessageId });

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

Unguarded external DB call: GetByMessageIdsAsync is not wrapped in a try/catch with context, violating the requirement that network/DB/external calls include structured context and map errors to application-level errors. Wrap the repository read in try/catch, log with structured context (chatMessageId, emoji, unitId, userId), and either fall through to the insert path or return a deterministic result on failure.

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

Prompt for LLM

File Core/Resgrid.Services/ChatMessageService.cs:

Line 350:

Unguarded external DB call: `GetByMessageIdsAsync` is not wrapped in a try/catch with context, violating the requirement that network/DB/external calls include structured context and map errors to application-level errors. Wrap the repository read in try/catch, log with structured context (`chatMessageId`, `emoji`, `unitId`, `userId`), and either fall through to the insert path or return a deterministic result on failure.

Talk to Kody by mentioning @kody

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

accept="image/*"
className="rgchat-fileinput"
style={{ display: 'none' }}
onChange={(event) => void handleFile(event.target.files?.[0])}

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

Inline arrow function in the onChange JSX prop creates a new function on every render, impacting performance. Move the function definition outside the render method.

Kody rule violation: Avoid using .bind() or arrow functions in JSX props

Prompt for LLM

File Web/Resgrid.Web/Areas/User/Apps/src/components/chat/atoms/Composer.tsx:

Line 322:

Inline arrow function in the `onChange` JSX prop creates a new function on every render, impacting performance. Move the function definition outside the render method.

Talk to Kody by mentioning @kody

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

{
// Transient network/DNS failure reaching Google: fail closed with a retryable
// validation message instead of letting the exception 500 the register page.
Framework.Logging.LogException(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

Missing structured context: the error log only passes the exception object with no operation name or relevant identifiers. Pass structured context, e.g., Framework.Logging.LogException(ex, new { op = "recaptcha.siteverify", member = validationContext.MemberName }), or use an overload that accepts a message and identifiers.

Kody rule violation: Include error context in structured logs

Prompt for LLM

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

Line 62:

Missing structured context: the error log only passes the exception object with no operation name or relevant identifiers. Pass structured context, e.g., `Framework.Logging.LogException(ex, new { op = "recaptcha.siteverify", member = validationContext.MemberName })`, or use an overload that accepts a message and identifiers.

Talk to Kody by mentioning @kody

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


return ValidationResult.Success;
String jsonResponse = httpResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();
dynamic jsonData = JObject.Parse(jsonResponse);

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 low

Untrusted JSON: JObject.Parse(Response) parses Google's reCAPTCHA endpoint response without validating its shape, and the surrounding catch filter (HttpRequestException/TaskCanceledException/OperationCanceledException) will not catch a JsonReaderException. Parse inside a try/catch that also handles Newtonsoft.Json.JsonException, then assert required fields (e.g., Data.success is JToken) before using them, returning the retryable ValidationResult on malformed input.

Kody rule violation: Always validate JSON parsing

Prompt for LLM

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

Line 50:

Untrusted JSON: `JObject.Parse(Response)` parses Google's reCAPTCHA endpoint response without validating its shape, and the surrounding catch filter (`HttpRequestException`/`TaskCanceledException`/`OperationCanceledException`) will not catch a `JsonReaderException`. Parse inside a try/catch that also handles `Newtonsoft.Json.JsonException`, then assert required fields (e.g., `Data.success` is `JToken`) before using them, returning the retryable `ValidationResult` on malformed input.

Talk to Kody by mentioning @kody

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

@ucswift

ucswift commented Aug 8, 2026

Copy link
Copy Markdown
Member Author

Approve

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This PR is approved.

@ucswift
ucswift merged commit 569f369 into master Aug 8, 2026
16 of 18 checks passed
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