Skip to content

Conversation

@andrewrobinsonhodges-snyk
Copy link
Contributor

@andrewrobinsonhodges-snyk andrewrobinsonhodges-snyk commented Jan 27, 2026

Description

LDX-Sync Caching

  • New LDXSyncConfigCache (internal/types/ldx_sync_config.go) - Caches org configs returned from LDX-Sync API
  • LDXSyncOrgConfig stores per-org settings with locked/enforced field metadata
  • Folder-to-org mapping - Maps workspace folders to their determined org IDs
  • Cache is populated when RefreshConfigFromLdxSync runs after authentication/folder changes

Config Resolution by Org IDE-1711

  • New ConfigResolver (internal/types/config_resolver.go) - Resolves effective config values with priority:

    1. User overrides (stored per-folder)
    2. LDX-Sync org config (locked/enforced fields)
    3. Global IDE settings
    4. Defaults
  • Folder-aware config accessors on Config: FilterSeverityForFolder(), IsSnykOssEnabledForFolder(), etc.

  • LDXSyncAdapter (internal/types/ldx_sync_adapter.go) - Converts LDX-Sync API responses to internal config format

Separation of Stored Config vs LSP Messages

  • StoredFolderConfig (internal/types/stored_folder_config.go) - Persisted config with user overrides, org settings, feature flags, metadata from LDX-sync
  • LspFolderConfig (internal/types/lsp.go) - Wire format for $/snyk.folderConfigs notification with PATCH semantics:
    • Omitted = don't change
    • Null = reset to default
    • Value = set override
  • LspConfigurationParam - New notification ($/snyk.configuration) for global/machine-wide settings IDE-1637
  • NullableField[T] - Generic type supporting three-state JSON semantics (omitted/null/value)

Settings Validation [IDE-1644]

  • Locked field validation - validateLockedFields() rejects changes to fields locked by org policy
  • Enforced field handling - Enforced values from LDX-Sync override user settings
  • clearLockedOverridesFromStoredFolderConfigs() - Clears user overrides when a field becomes locked

Key Files Changed

  • internal/types/stored_folder_config.go - New persistent config type
  • internal/types/config_resolver.go - Config resolution logic
  • internal/types/ldx_sync_config.go - LDX-Sync cache types
  • application/server/configuration.go - Processing of incoming LspFolderConfig
  • domain/ide/command/folder_handler.go - Building outgoing notifications

Checklist

  • Tests added and all succeed
  • Regenerated mocks, etc. (make generate)
  • Linted (make lint-fix)
  • README.md updated, if user-facing
  • License file updated, if new 3rd-party dependency is introduced

nick-y-snyk and others added 13 commits January 23, 2026 19:04
…roach

- Introduced LdxSyncService interface for LDX-Sync configuration management
- Added in-memory caching of LDX-Sync results at Config level
- Implemented parallel fetching of LDX-Sync config for workspace folders
- Cache refreshes on login and workspace folder changes
- Replaced OrgResolver interface with direct cache-based lookups
- Removed deprecated GetBestOrgFromLdxSync function
- Updated all tests to use GetOrgFromCachedLdxSync directly
- Changed successful fetch logging from Info to Debug level
- Added ABOUTME comments to new ldx_sync_service.go file
- Documented 4 skipped integration tests in SKIPPED_TESTS_IDE-1636.md

This refactor improves performance by avoiding repeated API calls and
provides a cleaner architecture with better separation of concerns.
- Remove unnecessary network mocking in tests (mock at service level instead)
- Use generated MockLdxSyncService instead of manual mocks
- Add ResolveOrg method to LdxSyncService interface
- Fix fallback tests by properly initializing engine mock
- Delete SKIPPED_TESTS file as all tests now pass
…into service

- Changed ResolveOrg signature to accept folderPath instead of result
- Service now handles cache lookup internally, providing single entry point for LDX-Sync operations
- Removed GetOrgFromCachedLdxSync from folder_handler (moved into service)
- Added warning log when falling back to global org
- Replaced mock with real service implementation in tests
- Removed redundant tests that were testing GAF's resolver logic (already tested in GAF)
- All remaining tests cover our facade responsibilities: cache lookup and fallback
@snyk-io
Copy link

snyk-io bot commented Jan 27, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @andrewrobinsonhodges-snyk, 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 enhances the Snyk Language Server's ability to manage and apply configuration settings dynamically based on the user's organization and specific workspace folders. By integrating with LDX-Sync and introducing a robust ConfigResolver, the system can now enforce organization-defined policies, respect user-specific overrides, and apply context-sensitive settings, leading to a more tailored and compliant user experience. This change impacts how various features, such as product enablement, severity filtering, and auto-scanning, behave across different projects within the IDE.

Highlights

  • LDX-Sync Integration: Introduced deep integration with Snyk's LDX-Sync service to fetch and cache organization-specific and folder-specific configuration settings, moving towards a more granular configuration model.
  • Configuration Resolution Logic: A new ConfigResolver component has been added to centralize and manage configuration precedence, considering LDX-Sync locked/enforced settings, user overrides, global settings, and defaults across machine, organization, and folder scopes.
  • Folder-Aware Settings: Many existing configuration accessors, such as product enablement, severity filters, and delta findings, have been updated to be folder-aware, allowing different settings to apply to different workspace folders based on their context.
  • User Overrides and Persistence: The FolderConfig now supports explicit user overrides for settings, which are persisted to storage and respected in the configuration resolution hierarchy, giving users more control over their project settings.
  • Refactored Dependency Injection: The dependency injection (DI) container and command service have been updated to incorporate the new LdxSyncService and remove the old OrgResolver, streamlining the architecture for configuration management.

🧠 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.

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.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

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 introduces a significant and well-executed refactoring to support organization-specific configurations via LDX-Sync. The introduction of the ConfigResolver and folder-aware configuration accessors provides a clear and robust way to manage settings with a defined precedence. The changes are consistently applied across different parts of the application, including scanning, diagnostics, and command handling. I've identified an opportunity to reduce code duplication in the new folder-aware product enablement checks and a TODO comment that needs clarification. Overall, this is a solid improvement to the configuration management system.

nick-y-snyk and others added 5 commits January 28, 2026 11:07
Refactored LdxSyncService to use dependency injection, enabling full mocking of external LDX-Sync API calls in tests. Added 16 test cases covering RefreshConfigFromLdxSync and ResolveOrg including success paths, error handling, caching, parallel execution, and boundary conditions.
@snyk-pr-review-bot

This comment has been minimized.

return FilterReasonSeverity
}
riskScoreInCLIEnabled := featureflag.UseOsTestWorkflow(folderConfig)
riskScoreInCLIEnabled := featureflag.UseOsTestWorkflowFromReader(folderConfig)
Copy link
Collaborator

Choose a reason for hiding this comment

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

FromReader?

// All methods are safe for concurrent use.
type LDXSyncConfigCache struct {
mu sync.RWMutex
OrgConfigs map[string]*LDXSyncOrgConfig `json:"orgconfigs"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

use imCache (or the GAF cache) with an expiry

})
}

func TestLDXSyncConfigCache_ConcurrentAccess(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

looks like this and the next test can be deduplicated a bit :)

if fc == nil {
return ""
}
return fc.FolderPath
Copy link
Collaborator

Choose a reason for hiding this comment

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

just to be safe: should we normalize here?

@snyk-pr-review-bot

This comment has been minimized.

@snyk-pr-review-bot

This comment has been minimized.

@snyk-pr-review-bot

This comment has been minimized.

@snyk-pr-review-bot

This comment has been minimized.

@snyk-pr-review-bot

This comment has been minimized.

@snyk-pr-review-bot

This comment has been minimized.

@snyk-pr-review-bot
Copy link

PR Reviewer Guide 🔍

🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Logic Error 🟠 [major]

The ApplyLspUpdate method contains a logic error that prevents users from switching a folder back to "Auto Select Organization" mode once a manual organization has been set.

Specifically, applyPreferredOrg forces fc.OrgSetByUser = true whenever the PreferredOrg field changes (including when it is cleared to an empty string to enable auto-selection). Subsequently, applyOrgFlags skips applying the actual OrgSetByUser value from the update if preferredOrgUpdated is true. This means an IDE update intended to set {"preferredOrg": "", "orgSetByUser": false} will result in the LS state remaining {"preferredOrg": "", "orgSetByUser": true}, effectively forcing the folder into a broken manual mode with an empty organization instead of returning to automatic resolution.

if update.PreferredOrg != nil && *update.PreferredOrg != fc.PreferredOrg {
	fc.PreferredOrg = *update.PreferredOrg
	fc.OrgSetByUser = true
	return true
}
Performance Issue 🟠 [major]

The UpdateSettings flow triggers excessive disk I/O by calling propagateOrgScopedGlobalSettingsToStoredFolderConfigs up to 9 times for different settings (severity, risk score, product enablement, etc.).

Each call to propagate... performs a full read of the configuration storage and a full write back to disk. This results in up to 9 sequential read/write cycles of the same storage file during a single settings update, which will cause significant performance degradation and disk churn for users with many projects or slower storage backends. This propagation logic should be batched to perform a single read/write cycle for all affected settings.

sc, err := storedconfig.GetStoredConfig(gafConfig, &logger, true)
if err != nil {
	logger.Err(err).Msg("Failed to get stored config for propagating global setting")
	return
}

cache := c.GetLdxSyncOrgConfigCache()
modified := false

for folderPath, fc := range sc.StoredFolderConfigs {
	if fc == nil {
		continue
	}

	// Check if this field is locked for this folder's org
	effectiveOrg := cache.GetOrgIdForFolder(folderPath)
	if effectiveOrg != "" {
		orgConfig := cache.GetOrgConfig(effectiveOrg)
		if orgConfig != nil {
			field := orgConfig.GetField(settingName)
			if field != nil && field.IsLocked {
				logger.Debug().
					Str("folder", string(folderPath)).
					Str("org", effectiveOrg).
					Msg("Skipping propagation - field is locked by org policy")
				continue
			}
		}
	}

	// Propagate the global setting to this folder's UserOverrides
	fc.SetUserOverride(settingName, value)
	modified = true
	logger.Debug().
		Str("folder", string(folderPath)).
		Interface("value", value).
		Msg("Propagated global setting to folder")
}

if modified {
	if err := storedconfig.Save(gafConfig, sc); err != nil {
		logger.Err(err).Msg("Failed to save stored config after propagating global setting")
📚 Repository Context Analyzed

This review considered 153 relevant code sections from 13 files (average relevance: 1.04)

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