Skip to content

refactor(renderer): split AppContext and ConfigTab concerns#131

Merged
Mehdi-Bl merged 4 commits into
mainfrom
feat/renderer-concern-split-appcontext-configtab
Feb 14, 2026
Merged

refactor(renderer): split AppContext and ConfigTab concerns#131
Mehdi-Bl merged 4 commits into
mainfrom
feat/renderer-concern-split-appcontext-configtab

Conversation

@Mehdi-Bl

@Mehdi-Bl Mehdi-Bl commented Feb 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • split AppContext helper logic into dedicated renderer context utility modules
  • split ConfigTab provider/form parsing and validation logic into dedicated helper modules
  • keep UI behavior and feature-flag behavior unchanged while reducing mixed concerns

Refactor details

  • added src/renderer/context/utils/{error-utils,config-storage,path-boundary,tree-selection}.ts
  • added src/renderer/components/config-tab/{config-form,provider-utils}.ts
  • updated src/renderer/context/AppContext.tsx to consume extracted utilities
  • updated src/renderer/components/ConfigTab.tsx to consume extracted form/provider modules

Validation

  • npm run lint
  • npm test -- --runInBand
  • npm run qa:screenshot
  • make sonar

Notes

  • attempted requested Claude CLI review command (cl -p ...) but cl is not installed in this environment

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of invalid configuration files—form state now persists when config parsing fails.
    • Enhanced security: API keys are now redacted before storage if configuration cannot be parsed.
  • Tests

    • Added tests for invalid YAML configuration handling and API key redaction during storage operations.

@sourcery-ai

sourcery-ai Bot commented Feb 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors renderer config and context logic by extracting provider/config form handling and tree/path/error helpers into dedicated utility modules, while updating AppContext and ConfigTab to consume these new abstractions without changing UI behavior.

Class diagram for extracted config-tab form and provider utilities

classDiagram
  direction LR

  class ConfigTab {
    +configContent: string
    +onConfigChange(config: string): void
    -formState: ConfigFormState
    -dispatch(action: ConfigFormAction): void
    -handleSave(): void
    -handleTestConnection(): void
  }

  class ConfigFormModule {
    <<module>>
    +ConfigFormState
    +ConfigFormAction
    +initialFormState: ConfigFormState
    +configFormReducer(state: ConfigFormState, action: ConfigFormAction) ConfigFormState
    +parseConfigContent(configContent: string) ConfigObject
    +applyBaseConfigState(config: ConfigObject, state: ConfigFormState) void
    +applyProviderConfigState(config: ConfigObject, state: ConfigFormState, aiSurfacesEnabled: boolean, translate: function) ProviderConfigSaveResult
  }

  class ProviderUtilsModule {
    <<module>>
    +ProviderOption
    +ProviderFields
    +PROVIDER_OPTIONS: ProviderOption[]
    +isSupportedProviderId(value: unknown) boolean
    +trimToUndefined(value: string) string
    +hasProviderInput(providerFields: ProviderFields) boolean
    +getProviderValidationErrors(providerFields: ProviderFields, translate: function) string[]
  }

  class ConfigFormState {
    +useCustomExcludes: boolean
    +useCustomIncludes: boolean
    +useGitignore: boolean
    +enableSecretScanning: boolean
    +excludeSuspiciousFiles: boolean
    +includeTreeView: boolean
    +showTokenCount: boolean
    +exportFormat: ExportFormat
    +fileExtensions: string
    +excludePatterns: string
    +providerId: ProviderId | ""
    +providerModel: string
    +providerApiKey: string
    +providerBaseUrl: string
  }

  class ProviderConfigSaveResult {
    +hasValidationErrors: boolean
    +validationErrors: string[]
  }

  class ConfigObject {
  }

  class ProviderFields {
    +providerId: ProviderId | ""
    +providerModel: string
    +providerApiKey: string
    +providerBaseUrl: string
  }

  class ProviderOption {
    +id: ProviderId
    +label: string
    +defaultBaseUrl: string
    +requiresApiKey: boolean
  }

  class ExportFormat {
  }

  class ProviderId {
  }

  %% Relationships
  ConfigTab --> ConfigFormModule : uses
  ConfigTab --> ProviderUtilsModule : uses

  ConfigFormModule --> ConfigFormState : defines
  ConfigFormModule --> ProviderConfigSaveResult : returns
  ConfigFormModule --> ConfigObject : parses
  ConfigFormModule --> ProviderUtilsModule : uses
  ConfigFormModule --> ExportFormat : uses

  ProviderUtilsModule --> ProviderFields : uses
  ProviderUtilsModule --> ProviderOption : defines
  ProviderUtilsModule --> ProviderId : uses
Loading

Class diagram for extracted AppContext utility modules

classDiagram
  direction LR

  class AppContextModule {
    <<context>>
    +AppProvider
    +useApp()
    -rootPath: string
    -directoryTree: DirectoryTreeItem[]
    -selectedFolders: string[]
    -selectedFiles: string[]
    -toggleFolderSelection(folderPath: string, isSelected: boolean) void
  }

  class TreeSelectionModule {
    <<module>>
    +findFolderByPath(items: DirectoryTreeItem[], targetPath: string) DirectoryTreeItem | null
    +collectSubFoldersWithinBoundary(folder: DirectoryTreeItem, rootPath: string) string[]
    +collectFilesWithinBoundary(folder: DirectoryTreeItem, rootPath: string) string[]
  }

  class PathBoundaryModule {
    <<module>>
    +normalizePathForBoundaryCheck(inputPath: string) string
    +isPathWithinRootBoundary(candidatePath: string, rootPath: string) boolean
  }

  class ConfigStorageModule {
    <<module>>
    +INITIAL_CONFIG_PLACEHOLDER: string
    +sanitizeConfigForStorage(configContent: string) string
  }

  class ErrorUtilsModule {
    <<module>>
    +ensureError(error: unknown) Error
  }

  class DirectoryTreeItem {
    +path: string
    +type: string
    +children: DirectoryTreeItem[]
  }

  class ConfigObject {
    +provider
  }

  AppContextModule --> TreeSelectionModule : uses
  AppContextModule --> PathBoundaryModule : uses
  AppContextModule --> ConfigStorageModule : uses
  AppContextModule --> ErrorUtilsModule : uses
  AppContextModule --> DirectoryTreeItem : manages

  TreeSelectionModule --> DirectoryTreeItem : traverses
  TreeSelectionModule --> PathBoundaryModule : uses

  PathBoundaryModule <.. TreeSelectionModule : boundary checks

  ConfigStorageModule --> ConfigObject : parses
Loading

File-Level Changes

Change Details Files
Extracted config form state management and provider config application/parsing logic from ConfigTab into dedicated helper modules.
  • Introduced config-form module encapsulating ConfigTab form state shape, reducer, initial state, YAML parsing, and application of base/provider config back to the ConfigObject.
  • Moved provider-related validation, supported provider definitions, and helpers (e.g., trim/hasInput) into provider-utils.
  • Updated ConfigTab component to import and use config-form and provider-utils instead of inline implementations, keeping JSX and behavior the same aside from a safer yaml import style.
src/renderer/components/ConfigTab.tsx
src/renderer/components/config-tab/config-form.ts
src/renderer/components/config-tab/provider-utils.ts
Split AppContext helper concerns (config storage, path boundary checks, and tree selection logic) into dedicated context utility modules.
  • Extracted config sanitization and initial placeholder constants into config-storage utility, and updated AppContext to use it for persisting config safely.
  • Moved path normalization and root-boundary checking to path-boundary utility, centralizing path handling.
  • Added tree-selection utility providing folder lookup and recursive file/folder collection within root boundaries, replacing inline implementations in AppContext.
src/renderer/context/AppContext.tsx
src/renderer/context/utils/config-storage.ts
src/renderer/context/utils/path-boundary.ts
src/renderer/context/utils/tree-selection.ts
Isolated generic error-normalization logic used in the renderer context into a reusable utility.
  • Created error-utils module with ensureError helper that normalizes unknown thrown values into Error instances.
  • Replaced AppContext’s inline ensureError implementation with the shared utility function.
src/renderer/context/AppContext.tsx
src/renderer/context/utils/error-utils.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Feb 14, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Mehdi-Bl has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 26 minutes and 14 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

This PR refactors configuration management by extracting duplicated logic from ConfigTab.tsx and AppContext.tsx into modular utilities—including config form state management, provider validation, path boundary checks, and tree traversal—while maintaining existing behavior and public APIs.

Changes

Cohort / File(s) Summary
Config Form & Provider Utilities
src/renderer/components/config-tab/config-form.ts, src/renderer/components/config-tab/provider-utils.ts
New modules introducing ConfigFormState, ConfigFormAction, configFormReducer, parseConfigContent, applyBaseConfigState, and applyProviderConfigState for centralized config form handling, validation, and provider field management with comprehensive error collection.
Path & Tree Navigation Utilities
src/renderer/context/utils/path-boundary.ts, src/renderer/context/utils/tree-selection.ts
New modules providing normalizePathForBoundaryCheck, isPathWithinRootBoundary for path validation, and findFolderByPath, collectSubFoldersWithinBoundary, collectFilesWithinBoundary for recursive directory tree navigation within boundaries.
Config Storage & Error Utilities
src/renderer/context/utils/config-storage.ts, src/renderer/context/utils/error-utils.ts
New modules introducing sanitizeConfigForStorage with api_key redaction logic and ensureError for robust error normalization.
ConfigTab Component Refactoring
src/renderer/components/ConfigTab.tsx
Replaces in-file YAML parsing, provider validation, and state management with imports from config-form, provider-utils, and modular utilities; maintains UI surface but delegates logic to external helpers.
AppContext Component Refactoring
src/renderer/context/AppContext.tsx
Extracts path validation, config sanitization, and tree traversal helpers to dedicated utility modules; replaces custom implementations with imported utility functions while preserving functional behavior.
Test Additions
tests/unit/components/app.test.tsx, tests/unit/components/config-tab.test.tsx
New tests verifying api_key redaction on parse failures and form state preservation when receiving invalid YAML configuration.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 From tangled tabs, new paths emerge so neat,
Config forms dance with providers' heartbeat,
Trees traverse with boundaries held tight,
Storage keeps secrets—api keys out of sight! 🔐✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main refactoring objective: extracting and separating AppContext and ConfigTab concerns into modular utilities.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/renderer-concern-split-appcontext-configtab

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 and usage tips.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Review Summary by Qodo

Refactor renderer concerns into dedicated utility modules

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Extract form parsing and validation logic into dedicated modules
• Extract renderer context utilities into separate files
• Reduce mixed concerns in AppContext and ConfigTab components
• Maintain existing UI behavior and feature flags unchanged
Diagram
flowchart LR
  ConfigTab["ConfigTab.tsx"]
  ConfigForm["config-form.ts"]
  ProviderUtils["provider-utils.ts"]
  AppContext["AppContext.tsx"]
  ConfigStorage["config-storage.ts"]
  ErrorUtils["error-utils.ts"]
  PathBoundary["path-boundary.ts"]
  TreeSelection["tree-selection.ts"]
  
  ConfigTab -- "imports form logic" --> ConfigForm
  ConfigTab -- "imports provider utilities" --> ProviderUtils
  ConfigForm -- "uses provider validation" --> ProviderUtils
  AppContext -- "imports config storage" --> ConfigStorage
  AppContext -- "imports error handling" --> ErrorUtils
  AppContext -- "imports path utilities" --> PathBoundary
  AppContext -- "imports tree utilities" --> TreeSelection
  TreeSelection -- "uses path boundary" --> PathBoundary
Loading

Grey Divider

File Changes

1. src/renderer/components/config-tab/config-form.ts ✨ Enhancement +195/-0

Form state management and config parsing utilities

• New file extracting form state management and config parsing logic
• Defines ConfigFormState type and configFormReducer for form state updates
• Implements config loading, parsing, and serialization functions
• Provides provider configuration application with validation result handling

src/renderer/components/config-tab/config-form.ts


2. src/renderer/components/config-tab/provider-utils.ts ✨ Enhancement +103/-0

Provider validation and configuration utilities

• New file containing provider-related validation and utility functions
• Defines PROVIDER_OPTIONS constant with supported AI providers
• Implements provider ID validation, field validation, and error collection
• Exports ProviderFields type and helper functions for provider input handling

src/renderer/components/config-tab/provider-utils.ts


3. src/renderer/context/utils/config-storage.ts ✨ Enhancement +34/-0

Config storage and sanitization utilities

• New file extracting config storage and sanitization logic
• Implements sanitizeConfigForStorage to remove sensitive API keys before storage
• Exports INITIAL_CONFIG_PLACEHOLDER constant for loading state
• Handles YAML parsing and serialization with error recovery

src/renderer/context/utils/config-storage.ts


View more (5)
4. src/renderer/context/utils/error-utils.ts ✨ Enhancement +23/-0

Error normalization utility function

• New file extracting error handling utility function
• Implements ensureError to normalize various error types to Error objects
• Handles primitives, objects, and unknown error types gracefully
• Provides consistent error handling across the application

src/renderer/context/utils/error-utils.ts


5. src/renderer/context/utils/path-boundary.ts ✨ Enhancement +39/-0

Path normalization and boundary validation

• New file extracting path normalization and boundary checking logic
• Implements normalizePathForBoundaryCheck for cross-platform path handling
• Provides isPathWithinRootBoundary to validate path containment
• Handles Windows drive letters, relative paths, and path traversal

src/renderer/context/utils/path-boundary.ts


6. src/renderer/context/utils/tree-selection.ts ✨ Enhancement +65/-0

Directory tree traversal and selection utilities

• New file extracting directory tree traversal and selection logic
• Implements findFolderByPath for recursive folder lookup
• Provides collectSubFoldersWithinBoundary and collectFilesWithinBoundary functions
• Uses path boundary validation to ensure safe file system operations

src/renderer/context/utils/tree-selection.ts


7. src/renderer/components/ConfigTab.tsx ✨ Enhancement +18/-288

Remove extracted form and provider logic

• Removes 270+ lines of extracted form and provider logic
• Imports form state management from config-form module
• Imports provider utilities from provider-utils module
• Simplifies component to focus on UI rendering and event handling

src/renderer/components/ConfigTab.tsx


8. src/renderer/context/AppContext.tsx ✨ Enhancement +12/-151

Remove extracted utility functions and imports

• Removes 100+ lines of extracted utility functions
• Imports config storage utilities from config-storage module
• Imports error handling from error-utils module
• Imports path and tree utilities from dedicated modules
• Simplifies context to focus on application state management

src/renderer/context/AppContext.tsx


Grey Divider

Qodo Logo

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello @Mehdi-Bl, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the modularity and maintainability of the renderer by systematically separating concerns within both the ConfigTab and AppContext components. It extracts various helper functions and logic into dedicated utility modules, reducing complexity in the main components and making the codebase easier to understand and extend. The changes ensure that UI behavior and feature-flag functionality remain consistent.

Highlights

  • Refactoring ConfigTab: The ConfigTab component has been refactored to separate its concerns, moving helper logic and provider/form parsing and validation into dedicated utility modules.
  • New Config-Tab Utilities: New modules config-form.ts and provider-utils.ts were introduced under src/renderer/components/config-tab to manage configuration form state, parsing, and provider-specific logic.
  • Refactoring AppContext: The AppContext component has been refactored to extract helper functions related to config storage, error handling, path boundary checks, and tree selection into new utility modules.
  • New AppContext Utilities: New modules config-storage.ts, error-utils.ts, path-boundary.ts, and tree-selection.ts were added under src/renderer/context/utils to centralize common utility functions.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/renderer/components/ConfigTab.tsx
    • Refactored to import and utilize external utility modules for config form state management and provider validation, removing internal helper functions.
  • src/renderer/components/config-tab/config-form.ts
    • Added to encapsulate config form state management logic, including reducer, initial state, and functions for parsing and applying config.
  • src/renderer/components/config-tab/provider-utils.ts
    • Added to centralize provider-related utilities, such as options, ID validation, input trimming, and validation error generation.
  • src/renderer/context/AppContext.tsx
    • Refactored to import and use external utility modules for error handling, path boundary checks, and directory tree traversal, removing internal implementations.
  • src/renderer/context/utils/config-storage.ts
    • Added to store functions related to config storage, including placeholder and sanitization logic.
  • src/renderer/context/utils/error-utils.ts
    • Added to provide a centralized utility function for consistent error handling.
  • src/renderer/context/utils/path-boundary.ts
    • Added to define utility functions for normalizing paths and checking if a path is within a given root boundary.
  • src/renderer/context/utils/tree-selection.ts
    • Added to contain utility functions for traversing and collecting items from a directory tree structure.
Activity
  • All linting and formatting checks passed successfully.
  • Markdown documentation links and styles were validated without issues.
  • Test catalog validation passed, confirming 38 references and 36 discovered tests.
  • Jest tests executed, with numerous console.info messages from i18next noted in the output.
  • Build processes for TypeScript, CSS, and Webpack completed, though Webpack reported warnings regarding asset size limits.
  • UI screenshots were successfully captured for various locales and source states.
  • A SonarQube scan was performed, generating a fresh Jest coverage report and completing successfully with analysis of 40 JavaScript/TypeScript source files.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `src/renderer/components/config-tab/config-form.ts:32-37` </location>
<code_context>
+  providerBaseUrl: string;
+};
+
+export type ConfigFormAction =
+  | {
+      type: 'SET_FIELD';
+      field: keyof ConfigFormState;
+      value: ConfigFormState[keyof ConfigFormState];
+    }
+  | { type: 'LOAD_FROM_CONFIG'; config: ConfigObject; aiSurfacesEnabled: boolean };
+
</code_context>

<issue_to_address>
**suggestion:** Tighten ConfigFormAction typing so `field` and `value` stay in sync

With `value` typed as `ConfigFormState[keyof ConfigFormState]`, TypeScript allows mismatches (e.g. dispatching a string to a boolean field). You can make this type‑safe by tying `field` and `value` together via a generic discriminated union:

```ts
export type ConfigFormAction =
  | { type: 'LOAD_FROM_CONFIG'; config: ConfigObject; aiSurfacesEnabled: boolean }
  | ({ type: 'SET_FIELD' } & {
      [K in keyof ConfigFormState]: { field: K; value: ConfigFormState[K] }
    }[keyof ConfigFormState]);
```

This ensures `value`’s type always matches the selected `field`, preventing incorrect dispatches at compile time.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/renderer/components/config-tab/config-form.ts Outdated
@github-actions

github-actions Bot commented Feb 14, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Feb 14, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

✅ 1. parseConfigContent logs raw error 📘 Rule violation ⛨ Security
Description
parseConfigContent() writes the caught YAML parsing error directly to console.error, which is
unstructured and may include sensitive config details (e.g., API keys) in logs. This can leak
secrets via logs and makes auditing/monitoring harder.
Code

src/renderer/components/config-tab/config-form.ts[R123-133]

+export const parseConfigContent = (configContent: string): ConfigObject => {
+  try {
+    const parsedConfig = yaml.parse(configContent) as ConfigObject;
+    if (!parsedConfig || typeof parsedConfig !== 'object') {
+      return {};
+    }
+    return parsedConfig;
+  } catch (error) {
+    console.error('Error parsing config content, using empty config:', error);
+    return {};
+  }
Evidence
The checklist requires secure, structured logging without sensitive data (PR Compliance ID 5). The
added implementation logs the raw caught error object via console.error(...), which can contain
sensitive parse context and is not structured.

Rule 5: Generic: Secure Logging Practices
src/renderer/components/config-tab/config-form.ts[123-133]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`parseConfigContent()` logs the raw caught YAML parsing error via `console.error(...)`. YAML parse errors can include contextual information that may reveal sensitive config values, and `console.*` output is unstructured.
## Issue Context
Config content may contain `provider.api_key` and other secrets. Logging should be structured/redacted and must not emit secrets.
## Fix Focus Areas
- src/renderer/components/config-tab/config-form.ts[123-133]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


✅ 2. sanitizeConfigForStorage leaks api_key 📘 Rule violation ⛨ Security
Description
sanitizeConfigForStorage() returns the original configContent on parse failures, which can
persist secrets like provider.api_key to storage unredacted. This is insecure handling of
sensitive data in an error/edge-case path.
Code

src/renderer/context/utils/config-storage.ts[R7-33]

+export const sanitizeConfigForStorage = (configContent: string): string => {
+  try {
+    const parsedConfig = yaml.parse(configContent);
+    if (!parsedConfig || typeof parsedConfig !== 'object') {
+      return configContent;
+    }
+
+    const config = parsedConfig as ConfigObject;
+    if (!config.provider || typeof config.provider !== 'object' || !config.provider.api_key) {
+      return configContent;
+    }
+
+    const sanitizedProvider = { ...config.provider };
+    delete sanitizedProvider.api_key;
+
+    const sanitizedConfig: ConfigObject = { ...config };
+    const providerValues = Object.values(sanitizedProvider).filter((value) => value !== undefined);
+    if (providerValues.length === 0) {
+      delete sanitizedConfig.provider;
+    } else {
+      sanitizedConfig.provider = sanitizedProvider;
+    }
+
+    return yaml.stringify(sanitizedConfig);
+  } catch {
+    return configContent;
+  }
Evidence
The checklist requires sensitive inputs to be handled securely and not exposed (PR Compliance ID 6).
The added sanitizer explicitly returns raw configContent when parsing fails, bypassing the
api_key removal and enabling secret persistence/exposure in this edge case.

Rule 6: Generic: Security-First Input Validation and Data Handling
src/renderer/context/utils/config-storage.ts[7-33]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`sanitizeConfigForStorage()` can return unsanitized `configContent` (including `provider.api_key`) when YAML parsing fails, causing secrets to be persisted/exposed in an edge-case path.
## Issue Context
This function is intended to sanitize secrets before storage; the error path currently bypasses redaction.
## Fix Focus Areas
- src/renderer/context/utils/config-storage.ts[7-33]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


✅ 3. Parse errors reset form 🐞 Bug ⛯ Reliability
Description
ConfigTab’s load effect now uses parseConfigContent(), which catches YAML parse errors and returns
{}. This means invalid YAML no longer triggers the surrounding try/catch path and instead reloads
the form with defaults, clearing provider validation/test feedback and potentially confusing users
while editing broken YAML.
Code

src/renderer/components/ConfigTab.tsx[R46-52]

// Load form state from config prop
useEffect(() => {
  try {
-      const config = (yaml.parse(configContent) || {}) as ConfigObject;
+      const config = parseConfigContent(configContent);
    dispatch({ type: 'LOAD_FROM_CONFIG', config, aiSurfacesEnabled });
    setProviderValidationErrors([]);
    setProviderTestResult(null);
Evidence
ConfigTab unconditionally dispatches LOAD_FROM_CONFIG using parseConfigContent(). parseConfigContent
always returns an object ({} on failure) because it catches parse exceptions internally, so
ConfigTab’s catch block won’t run for YAML parse errors; instead the reducer will load from an empty
config, resetting fields to their default-derived values.

src/renderer/components/ConfigTab.tsx[46-56]
src/renderer/components/config-tab/config-form.ts[123-134]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
ConfigTab reloads form state from YAML using `parseConfigContent()`. Because `parseConfigContent()` swallows YAML parse errors and returns `{}`, the ConfigTab effect will dispatch `LOAD_FROM_CONFIG` even when YAML is invalid, resetting the form to defaults and clearing provider feedback.
## Issue Context
We want to preserve the previous behavior of *not* reloading the form on parse failure (or at least make the failure user-visible), rather than silently treating invalid YAML as an empty config.
## Fix Focus Areas
- src/renderer/components/config-tab/config-form.ts[123-134]
- src/renderer/components/ConfigTab.tsx[46-56]
## Suggested approach
1. Update `parseConfigContent` to either:
- return `null` (or `{ config: null, error }`) when parsing fails, OR
- rethrow after logging (and let callers handle).
2. In `ConfigTab`’s load effect, only dispatch `LOAD_FROM_CONFIG` when parsing succeeds; otherwise keep the existing form state and optionally set a UI-visible error state (instead of clearing provider validation/test result).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +123 to +133
export const parseConfigContent = (configContent: string): ConfigObject => {
try {
const parsedConfig = yaml.parse(configContent) as ConfigObject;
if (!parsedConfig || typeof parsedConfig !== 'object') {
return {};
}
return parsedConfig;
} catch (error) {
console.error('Error parsing config content, using empty config:', error);
return {};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. parseconfigcontent logs raw error 📘 Rule violation ⛨ Security

parseConfigContent() writes the caught YAML parsing error directly to console.error, which is
unstructured and may include sensitive config details (e.g., API keys) in logs. This can leak
secrets via logs and makes auditing/monitoring harder.
Agent Prompt
## Issue description
`parseConfigContent()` logs the raw caught YAML parsing error via `console.error(...)`. YAML parse errors can include contextual information that may reveal sensitive config values, and `console.*` output is unstructured.

## Issue Context
Config content may contain `provider.api_key` and other secrets. Logging should be structured/redacted and must not emit secrets.

## Fix Focus Areas
- src/renderer/components/config-tab/config-form.ts[123-133]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +7 to +33
export const sanitizeConfigForStorage = (configContent: string): string => {
try {
const parsedConfig = yaml.parse(configContent);
if (!parsedConfig || typeof parsedConfig !== 'object') {
return configContent;
}

const config = parsedConfig as ConfigObject;
if (!config.provider || typeof config.provider !== 'object' || !config.provider.api_key) {
return configContent;
}

const sanitizedProvider = { ...config.provider };
delete sanitizedProvider.api_key;

const sanitizedConfig: ConfigObject = { ...config };
const providerValues = Object.values(sanitizedProvider).filter((value) => value !== undefined);
if (providerValues.length === 0) {
delete sanitizedConfig.provider;
} else {
sanitizedConfig.provider = sanitizedProvider;
}

return yaml.stringify(sanitizedConfig);
} catch {
return configContent;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. sanitizeconfigforstorage leaks api_key 📘 Rule violation ⛨ Security

sanitizeConfigForStorage() returns the original configContent on parse failures, which can
persist secrets like provider.api_key to storage unredacted. This is insecure handling of
sensitive data in an error/edge-case path.
Agent Prompt
## Issue description
`sanitizeConfigForStorage()` can return unsanitized `configContent` (including `provider.api_key`) when YAML parsing fails, causing secrets to be persisted/exposed in an edge-case path.

## Issue Context
This function is intended to sanitize secrets before storage; the error path currently bypasses redaction.

## Fix Focus Areas
- src/renderer/context/utils/config-storage.ts[7-33]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 46 to 52
// Load form state from config prop
useEffect(() => {
try {
const config = (yaml.parse(configContent) || {}) as ConfigObject;
const config = parseConfigContent(configContent);
dispatch({ type: 'LOAD_FROM_CONFIG', config, aiSurfacesEnabled });
setProviderValidationErrors([]);
setProviderTestResult(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.

Action required

3. Parse errors reset form 🐞 Bug ⛯ Reliability

ConfigTab’s load effect now uses parseConfigContent(), which catches YAML parse errors and returns
{}. This means invalid YAML no longer triggers the surrounding try/catch path and instead reloads
the form with defaults, clearing provider validation/test feedback and potentially confusing users
while editing broken YAML.
Agent Prompt
## Issue description
ConfigTab reloads form state from YAML using `parseConfigContent()`. Because `parseConfigContent()` swallows YAML parse errors and returns `{}`, the ConfigTab effect will dispatch `LOAD_FROM_CONFIG` even when YAML is invalid, resetting the form to defaults and clearing provider feedback.

## Issue Context
We want to preserve the previous behavior of *not* reloading the form on parse failure (or at least make the failure user-visible), rather than silently treating invalid YAML as an empty config.

## Fix Focus Areas
- src/renderer/components/config-tab/config-form.ts[123-134]
- src/renderer/components/ConfigTab.tsx[46-56]

## Suggested approach
1. Update `parseConfigContent` to either:
   - return `null` (or `{ config: null, error }`) when parsing fails, OR
   - rethrow after logging (and let callers handle).
2. In `ConfigTab`’s load effect, only dispatch `LOAD_FROM_CONFIG` when parsing succeeds; otherwise keep the existing form state and optionally set a UI-visible error state (instead of clearing provider validation/test result).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request successfully refactors the codebase by splitting the concerns of AppContext and ConfigTab into dedicated utility modules, significantly improving code organization, maintainability, and testability. However, critical security vulnerabilities have been identified, including insecure deserialization of YAML from untrusted sources, which could lead to prototype pollution, and a high-severity Server-Side Request Forgery (SSRF) vulnerability due to insufficient validation of user-provided URLs for provider configuration. Additionally, there are suggestions for further improvement regarding performance optimization in the file collection logic and enhancing function purity.

Comment on lines +91 to +100
if (providerBaseUrl.trim()) {
try {
const parsedUrl = new URL(providerBaseUrl.trim());
if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
errors.push(translate('config.validation.baseUrlProtocol'));
}
} catch {
errors.push(translate('config.validation.baseUrlValid'));
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-high high

The validation for the provider's baseUrl is insufficient. It only checks if the protocol is http: or https:, but it does not prevent requests to internal network addresses (e.g., localhost, 192.168.x.x, 10.x.x.x) or cloud metadata endpoints (e.g., 169.254.169.254). An attacker could convince a user to enter a malicious URL, which would cause the application's backend (Electron main process) to make a request to an arbitrary address on the user's local network. This can be used to scan the internal network, access non-public services, or exploit other vulnerabilities.

default:
return state;
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

The application uses yaml.parse() to deserialize YAML content that can be controlled by a user or loaded from localStorage. Untrusted data from localStorage should be treated as malicious. Parsing untrusted YAML content without protection against prototype pollution can allow an attacker to modify the prototype of core JavaScript objects. This can lead to application-wide security vulnerabilities, such as Denial of Service (DoS), or can be chained to achieve Cross-Site Scripting (XSS) or Remote Code Execution (RCE). An attacker would need to find a way to inject malicious YAML into localStorage (e.g., via an XSS vulnerability) to exploit this.

Suggested change
};
const parsedConfig = yaml.parse(configContent, { schema: 'core' }) as ConfigObject;


export const sanitizeConfigForStorage = (configContent: string): string => {
try {
const parsedConfig = yaml.parse(configContent);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

security-medium medium

The application uses yaml.parse() to deserialize YAML content in the sanitizeConfigForStorage function. This content originates from localStorage or user input and is therefore untrusted. Parsing this untrusted YAML without protection against prototype pollution can allow an attacker to modify the prototype of core JavaScript objects, leading to potential application-wide vulnerabilities like DoS, XSS, or RCE.

Suggested change
const parsedConfig = yaml.parse(configContent);
const parsedConfig = yaml.parse(configContent, { schema: 'core' });

Comment on lines +154 to +195
hasValidationErrors: boolean;
validationErrors: string[];
};

export const applyProviderConfigState = (
config: ConfigObject,
state: ConfigFormState,
aiSurfacesEnabled: boolean,
translate: (key: string) => string
): ProviderConfigSaveResult => {
if (!aiSurfacesEnabled) {
return { hasValidationErrors: false, validationErrors: [] };
}

const providerFields = {
providerId: state.providerId,
providerModel: state.providerModel,
providerApiKey: state.providerApiKey,
providerBaseUrl: state.providerBaseUrl,
};
const validationErrors = getProviderValidationErrors(providerFields, translate);
const hasValidationErrors = validationErrors.length > 0;

if (hasValidationErrors) {
if (config.provider) {
delete config.provider;
}
return { hasValidationErrors, validationErrors };
}

if (hasProviderInput(providerFields) && state.providerId) {
config.provider = {
id: state.providerId,
model: state.providerModel.trim(),
api_key: trimToUndefined(state.providerApiKey),
base_url: trimToUndefined(state.providerBaseUrl),
};
return { hasValidationErrors, validationErrors };
}

if (config.provider) {
delete config.provider;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The function applyProviderConfigState currently mutates the config object passed to it, which can lead to unexpected side effects. It would be more robust and easier to reason about if it were a pure function.

Consider refactoring it to return a new config object along with the validation results, instead of modifying it in place. This would align better with functional programming principles and improve predictability. A similar change could be applied to applyBaseConfigState for consistency.

files.push(item.path);
}
} else if (item.type === 'directory') {
files = [...files, ...collectFilesWithinBoundary(item, rootPath)];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using spread syntax [...array1, ...array2] to concatenate arrays inside a loop can be inefficient for large directory trees, as it creates a new array in each iteration. A more performant approach is to mutate the existing array using push().

Suggested change
files = [...files, ...collectFilesWithinBoundary(item, rootPath)];
files.push(...collectFilesWithinBoundary(item, rootPath));

@sonarqubecloud

Copy link
Copy Markdown

@Mehdi-Bl Mehdi-Bl merged commit 619d101 into main Feb 14, 2026
25 checks passed
@Mehdi-Bl Mehdi-Bl deleted the feat/renderer-concern-split-appcontext-configtab branch February 14, 2026 22:22
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.

1 participant