Skip to content

feat(config): centralized configuration [IDE-1786]#1162

Open
bastiandoetsch wants to merge 108 commits intomainfrom
refactor/IDE-1786_folder-config-refactoring
Open

feat(config): centralized configuration [IDE-1786]#1162
bastiandoetsch wants to merge 108 commits intomainfrom
refactor/IDE-1786_folder-config-refactoring

Conversation

@bastiandoetsch
Copy link
Copy Markdown
Collaborator

@bastiandoetsch bastiandoetsch commented Mar 3, 2026

User description

Summary

Phase 2 of the IDE-1786 configuration resolution refactor:

  • GAF flagset-native config resolution: Wire ConfigResolver to read settings from GAF prefix keys instead of struct fields. Inject ConfigResolver into context for scanner access.
  • Unified map-based configuration protocol: Replace Settings struct with map[string]*ConfigSetting for LSP configuration exchange. Add LspFolderConfig with per-folder settings maps.
  • SastSettings migration: Move SastSettings from FolderConfig struct field to GAF configuration with Get/SetSastSettings helpers.
  • Dynamic persistence: Replace hardcoded persistence arrays (persistedUserFolderSettings, persistedFolderMetadataSettings) and RegisterFolderPersistence() with SetFolderUserSetting / SetFolderMetadataSetting helpers that combine Set + PersistInStorage.
  • Migration code removal: Remove all legacy migration code (legacyFolderConfigJSON, MigrateLegacyFolderConfig, MigrateFolderConfigOrgSettings, OrgMigratedFromGlobalConfig, settings_migration.go).
  • CliSettings dissolution: Dissolve CliSettings struct into Config, promoting all fields and methods directly. Remove back-pointer pattern and separate mutex.
  • Precedence smoke tests: Add precedence_smoke_test.go and scanner_precedence_test.go for config resolution precedence validation.

Test plan

  • make lint — 0 issues
  • make test — all unit tests pass
  • SMOKE_TESTS=1 make test — smoke tests pass (except pre-existing Test_InvalidExpiredCredentialsSendMessageRequest flake)
  • INTEG_TESTS=1 make test — integration tests
  • Manual verification of folder config persistence round-trip

PR Type

Enhancement, Refactor


Description

  • Centralize configuration resolution using GAF components.
    • Update core services to use workflow engine and configuration resolver.
    • Remove legacy configuration resolution and migration code.

Diagram Walkthrough

    flowchart LR
      subgraph Previous System
        C["config.Config"]
      end
      subgraph New System
        GAF_Conf["configuration.Configuration (GAF)"]
        CR["ConfigResolver (types)"]
        LS["LS Core Services<br/>(codeaction, scanner, etc.)"]
        NewInterface["New Config Accessors"]
      end
      C -->|Data Loaded Into| GAF_Conf
      GAF_Conf -->|Resolution Logic| CR
      CR -->|Provides Unified Access| LS
      LS -->|Uses| NewInterface
      C -->|Removed| Deprecated
    ```



<details> <summary><h3> File Walkthrough</h3></summary>

<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Additional files</strong></td><td><details><summary>101 files</summary><table>
<tr>
  <td><strong>mcp.json</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-19eb5f2245594c429cc37560d082721fbabff68227984d8d40bad80fd3514b83">[link]</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>coder.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b89595e8329168380c8d0d806f10fe48e0c74d6480bdd36d5f52fb50e60b38e7">+112/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>planner.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-3da7d9f10c1d522c26d34593af5559179e560e5f132e48d11925d54bc7e3ae8c">+95/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>qa.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4399ee622fcd198ee75d7ce0c8f8bf764146afab8534c4b73be17d1b0d2e83c4">+126/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>commit.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-8a7812887cf57f02817a41095c0bc96e39f12a86dcedc8b9c7717843fb9fecff">+217/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>create-implementation-plan.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c1589a6ef920a2e344d943924041069ef37a00f0c35b10b665b908aa4c3fe12e">+126/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>implementation.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b7db59ef37d8544cde3bd3cb4463d2753bbcd1ef3da3e4d743d6d469476ab938">+202/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>verification.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e63ba9b85e4f0149745953751025c71f5b9b3afd6dfa43b55ece2d3b0bfa56e7">+270/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>upload-to-s3.sh</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4ec24b2c614b7504e801da062ef7c0a0ea927a853af71949117f9be1416955bd">+0/-12</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>.goreleaser.yaml</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-7326b55c062b0f46fe9e39aace0a25f4515cf206040fb91a6fd2cae839f5e826">+1/-4</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>.tmp_ignore</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-3a301d08bc19d4e6a95a86d0f5f652932e70406c818acb7c1497c799dce6d2c7">[link]</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>launch.json</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-bd5430ee7c51dc892a67b3f2829d1f5b6d223f0fd48b82322cfd45baf9f5e945">+0/-23</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tasks.json</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-7d76d7533653c23b753fc7ce638cf64bdb5e419927d276af836d3a03fdf1745a">+0/-24</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>coder.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-54b2c28a8e7b934853b4ab315640e41e7d75915418749972b73660a73dd2b8b6">+71/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>planner.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-3de3f79204a2442206f81923eaf79f4784cd2a508dde3722261ae346b527b4ba">+70/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>qa.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4b3ee0d066f86a40697b5a63f015144677bddd2e4afc190c20f1e5d4316b1dbd">+117/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>general.mdc</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-56f646b00fd9a6c0573b57c0974764abd7f601a2deb90984147b1f66f7b4e6af">+112/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>SKILL.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-3eee1259a01cb01a6e29a2472eefcf3975d16e88e4c4841d7cb954dcedc57bdb">+260/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>SKILL.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-1e09e3863ee46b9fdc6f65d71c688175b12b8a2e7f4b0a994925ce4f52095352">+116/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>SKILL.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-773a18b37f68a8d4a8d73eb1a67eb4a70fd56c90d24f00295e36778a199e53b3">+241/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>SKILL.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-01767e331487ad3b20729c5e0747a8f44b4395e7ffdf7038b2fb8eb622a23e9b">+333/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>CLAUDE.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-6ebdb617a8104a7756d0cf36578ab01103dc9f07e4dc6feb751296b9c402faf7">+114/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>Makefile</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-76ed074a9305c04054cdebb9e9aad2d818052b07091de1f20cad0bbac34ffb52">+2/-2</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>README.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5">+26/-22</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>helpers.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-939d219344d7e0ee2fabad9bd93cf499c569729779d098d7d7da97fefc181d15">+1/-15</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>configuration_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e141faafb3f0507956b85e92ac3b584c7c706bd96e7f428c7ef95d0d8b2df564">+752/-756</a></td>

</tr>

<tr>
  <td><strong>ldx_sync_smoke_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e86bba96f063a23a5407b87f1d43156fbf47ff435cf44fc7324550746bc13305">+134/-61</a></td>

</tr>

<tr>
  <td><strong>scan_notifier.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-1b61c83cfc491251abf25e52610132617ca8ea6b0c1d9347b948fde580eaae42">+2/-5</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>scan_notifier_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-5fdc92093964521489fec0d3eb3f9384994e19496fc0d274e0d6a3c3086a975d">+32/-24</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>notification_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-09b80ca28f0c9f8db60c941c24e92dfc65ded48c06249e70e2004faec30b9891">+26/-25</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>parallelization_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-dfb264976fa1c23451dc8d19b018920487af818f524aa24ea49211bd8b6326e9">+30/-24</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>precedence_smoke_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-1c743b9b799604d498e5a5290961ffc135dec375c44150ad820cba81f3926d39">+1077/-0</a></td>

</tr>

<tr>
  <td><strong>secrets_smoke_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c1510bd6a7151e3624d073a590787bd9af09d36b87a53a56067ffc0f3c8603fd">+24/-23</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>server_smoke_treeview_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-abf8d4beebabcdeea5250f1698dc768bfbd744c22da6c343b552b5ed02908147">+11/-10</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>server_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-165bad981a0cc202462a2fea7f236eab9ca3a72bd47fe8bae6b3adad801351fb">+272/-231</a></td>

</tr>

<tr>
  <td><strong>trust_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e429a057cdac11e281a6ee8603bf8a5ab6dfaf0daedaec873982c47b28d7d431">+51/-49</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>parser_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-56f8b9f576282024cef366a3d2024e23c393bc424bd1a7af8917142efcf913e0">+4/-4</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>configuration.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-17ed18489a956f326ec0fe4040850c5bc9261d4631fb42da4c52891d74a59180">+712/-0</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>configuration-effective-org.mmd</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-973fec236ae811f6a90a055f6b7d0da65dc4d11596911ce8ad89250223805426">+13/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>configuration-ide-to-ls.mmd</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-d96f7a3876e2a3103d4016e855aeed2eaf4e094231824ade00f1d15863ca2117">+29/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>configuration-ldx-sync-triggers.mmd</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-df333a4abf24cf3d87db395e0abe48a2faa721094e4c5c85350a3e539f7f5136">+39/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>configuration-locked-fields.mmd</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-38952676823280ad4b068d0c895c67c0793a9f12baa5fafbc4272e24b92e2188">+23/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>configuration-ls-to-ide.mmd</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e87e10816f9abd36bfa4484439e3525b9094a79520d362815a983bfedd8f9587">+35/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>configuration-precedence-folder.mmd</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-cf55942689f0076c1c0d5e590504f623b7d4a2163177bf6a9c38b5fe2abc61e9">+43/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>configuration-precedence-machine.mmd</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-1a8c5dae9ef9700a8d68771cf78fe1053bd3a2b0d08b7dc6392ae9bce67ddcb8">+27/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>configuration-precedence-org.mmd</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-45c665222948a0e96307bcec7893b2c2eb30a1940831992d8bd16c015a7dca50">+31/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tree-view.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-539534acc1cc1dac4c4790bdca3d442787d7e99ae04349bee000213f08300a28">+2/-2</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>ui-rendering.md</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c5a817d72c0cce20d0ef1e30f346d1533d38bf440472d1a0944fb0ad0c82f98a">+3/-3</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>clear_cache_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-f59e398f3890e4a5979f96e46822865c12c0a1c24a04488066e1110e28f217ee">+12/-9</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>code_fix_diffs_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-48255cf8a46398666fde50497322859b28c595de794ffd6670ece78b57ae7f75">+5/-7</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>code_fix_feedback_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-9ecc2e3d8ac8d287a04858a39ff2b791deda6c161b005159f1df9cd201561be2">+24/-22</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>code_fix_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b6e42c27f7465637e31a6b441bb21a325b705c2e602cc5208182658553a53e71">+28/-18</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>command_factory.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b7c8bb6d3fa17d1b86a8de859670a7d849bdcedceb8d9a27f4e7f1a74b5d340d">+35/-31</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>command_service.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-f91e9940ae7491a26a9708f682296ad692d4044fb82fad0d67811bc13f50fa0f">+12/-6</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>command_service_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4e5012d752e62ca48321ab14a65a40b32718552b72d0510af51c9eccfb981154">+4/-3</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>configuration_command_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4e6fcd9606a6e827d54cddeaf143ea61e51516a7e24ebcee13d0968284546bb0">+6/-5</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>connectivity_check.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-bd2f39aac9325739fa4bc8f0086a9dbeee25ae8965cfaaebb59ebaf24bfc5892">+6/-7</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>connectivity_check_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-d099e6ed42cac1be0e693600339935f0954b0ee310f46436ccb1fcc9b7264c46">+7/-7</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>directory_diagnostics.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c063651ad364c31d1c991853bd8e83c7c7dcde73b16f929e878721e11d1df8a5">+12/-6</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>directory_diagnostics_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-cf8506645d2be31c6eb3d3f8ad413863197d6b1793829408240eced9782744cb">+17/-11</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>folder_handler.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-85f453996002ebf21ce07aa3db62fcd3d18a1861f517804721583decb421a90d">+103/-199</a></td>

</tr>

<tr>
  <td><strong>folder_handler_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-657ad7fa06e97a16b4e8eb7cddc64cd5e53343e6513ef874563e83ffd694416b">+181/-344</a></td>

</tr>

<tr>
  <td><strong>generate_issue_description.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-20aaf0cdc220b7a600d894877440f647f93c34bbc4e4f023e33422b4f2e77ccf">+17/-15</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>get_active_user_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c5b8060a5d409b868500ca069b1916a9a35584924e1e61be677148d12a44064d">+18/-16</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>get_feature_flag_status_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-29de3939a8441248dd44aa2f2eb09f23a57909cf126d85840d0a1519580e25f3">+11/-6</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>get_tree_view_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-443abf9c8f12ff271e64a1eed61e5ddc426eca805d6d5be05cffab1a0e176166">+6/-6</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>ignores_integration_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-79ebbec5beb7c426863a36683370db366beb6d364890c7e7155c4a8936527277">+25/-31</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>ignores_request.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-06098845100f93443eaa531928a39a803862d1031ccea9ecccc83b8658a58b52">+61/-51</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>ignores_request_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-592f7e7bcfc9e8bde581d845d4ae40f18b2f5c70aeb9d9f9481c92439c3f0e74">+35/-46</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>ldx_sync_service.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-96079acff1e919961c5c93b80089075c62ed6b1f044fcf18fa0021dcf86fd30b">+149/-119</a></td>

</tr>

<tr>
  <td><strong>ldx_sync_service_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-aca1dc6a8fa97db48aa613e9fe3d8e075ff47071ab18848a1369c1217bf8863e">+438/-208</a></td>

</tr>

<tr>
  <td><strong>login.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-21b93267e7967e84d95e104e3b17f130b4b47b4a37ac176c5bba7dd4c35cee31">+10/-6</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>logout.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-22e316f4a260c54802f276ff85692d92b3c1aba01d3dfd7d73f313c567fb5aab">+5/-3</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>logout_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-e3d549e46654c52dd2506b8941fc83546f129917fb37045d8e1bb4c8eb4d1c91">+12/-8</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>ldx_sync_service_mock.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-3c7bf69a15757a5bbe63c8758c6596bee98f8b3d7d7b6a9664fc7502ee05f7ed">+6/-5</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>navigate_to_range_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-1cde128b3e8be4bc13c1ae238266409330a5bcef310bee386343660b932056b8">+12/-12</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>report_analytics.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-62743cd3ce4566b6111230201a62f4d1fea670f1fd60c593265bde16353d8585">+10/-6</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>report_analytics_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-20363c3fc3625f9eba95434b9eac8946f2aad36f0ca47165d56a412a78da766a">+29/-29</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>sast_enabled.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-66bf16df3a0885d711b00264fad42f006b0346950036f8b48dc7ae6478dc6df4">+1/-1</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>toggle_tree_filter_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-b68b006463ac72f1f5bd3786814cdcd0b103cac0245c1f3fdc0efbd0266c6b63">+25/-24</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>update_folder_config.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-a05ebb1468908293f4d3cfe250b1748079a0b150076d62ba0df69156131ea8c7">+28/-17</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>update_folder_config_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-ded99b4b398d892e849ca2c76051626e1fec77a62c3b6d777e532cf09653f049">+37/-33</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>workspace_folder_scan.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-6cb7a834c5afbe811a8145bd6d12892ca661ae68a983846d9f8077beabd6e757">+10/-6</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>workspace_scan.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-7815afb61f4ee4d064ffc994582c7306f6ba37d1014c9db5d44ee109dbdc6682">+5/-3</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>initializer.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-549679250b6998b23ae6754c4684adc9c107201a71db89c46e227fcc2b6c3a9d">+5/-3</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tree_builder.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-13a01423d69e8880eef4d1b357edd592e256151c20fe2e708922ec9f369d258e">+7/-3</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tree_html.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-47c0b3a66e860f79eb96752b6c2d226c640efecc95340807757164ca16cf1f85">+8/-7</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tree_html_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-a05e59c0bf8dd60c31ffcd4d99f9c41b4e14113da3327539b810a53f4f99c00d">+56/-56</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>tree_node_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-914f0695e35f0166328ffb0e48acb6527945999894fce96f1292a00f29fabc64">+1/-1</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tree_scan_emitter.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-c86a1bb522364b4f14326b1a2915a724532c1d4f10d39ed3a51ca4a648715456">+13/-8</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>tree_scan_emitter_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-406a88e6d83f47e407cb1bd5a26bd0967e5866aa11529d659a54afd480d356f0">+23/-19</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>folder_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-bb03ee76aabde8be49355b245229584151a114e49ec791407738dcaeb631acaf">+138/-162</a></td>

</tr>

<tr>
  <td><strong>workspace_trust_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-ac8e52aa69f3b6fb4fd604078ddeef7df2cfe4814df32258a855a7e2254d9af1">+4/-2</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>scan_state_aggregator_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-dacb10414b37097b40d2960a603ee1a3ec84ee95c6d096386bceb1c5aa893b42">+65/-51</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>summary_html_external_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-f9eb5dd6ed9a5462d93db22db8c10746d506824a6f2927b6ef59869a6f53b592">+15/-7</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>issues.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-4bdec45b5c8738e4bf742694b67c57c1102cfa09083bd638e325bfa650f8362a">+0/-3</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>git_persistence_provider_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-d11404eaca9a26e07e5b47c9a72b80378bfadb2ba02238363b7a445faf6ad955">+80/-79</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>base_scan.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-f37e6aa696f750af79d9ed24805074033bcea09bd38405ca384d14e4c63135f4">+43/-20</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>base_scan_test.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-29ac0a06174ae18093e1504044ee8b0ef1574ab063ca1a5c9210e28cba07b6ea">+47/-39</a>&nbsp; </td>

</tr>

<tr>
  <td><strong>pre_scan_command.go</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-eb3aa1acbc37cb13f25f84053ef3e81f4f48f558536a7d5e99a3fe429e4878f8">+10/-4</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>Additional files not shown</strong></td>
  <td><a href="https://github.com/snyk/snyk-ls/pull/1162/changes#diff-2f328e4cd8dbe3ad193e49d92bcf045f47a6b72b1e9487d366f6b8288589b4ca"></a></td>

</tr>
</table></details></td></tr></tr></tbody></table>

</details>

___

Loading

… [IDE-1786]

Phase 2.2 of folder config refactoring: wire snyk-ls to use GAF
ConfigResolver with prefix-key-based configuration.

New files:
- RegisterAllConfigurations: 31 settings with pflag annotations
- MigrateSettingsToLocalFields: convert legacy Settings to LocalConfigField
- ConfigResolver GAF delegation tests (FC-046 through FC-058)

Key changes:
- ConfigResolver delegates to GAF ConfigResolver when wired
- SetGlobalSettings stores reference; SyncGlobalSettingsToConfiguration
  writes reconciled values after Config is updated
- FolderConfig dual-writes UserOverrides to GAF prefix keys
- LDX-Sync adapter dual-writes remote config to GAF prefix keys
- clearLockedOverridesFromFolderConfigs clears GAF UserFolderKey
- batchClearOrgScopedOverridesOnGlobalChange uses ModifyStoredConfig
  for atomic read-modify-write with mutex protection
- processSingleLspFolderConfig sets fc.conf for dual-write
- IsSnykSecretsEnabledForFolder delegates to isSettingEnabledForFolder
- Compile-time ConfigResolverInterface satisfaction check
- Remove enforced precedence from resolution (locked + regular only)
- cmp.Equal uses cmpopts.IgnoreUnexported for FolderConfig
Replace builder.WriteString(fmt.Sprintf(...)) with fmt.Fprintf(&builder, ...)
as suggested by golangci-lint.
@bastiandoetsch
Copy link
Copy Markdown
Collaborator Author

/describe

@snyk-io
Copy link
Copy Markdown

snyk-io bot commented Mar 3, 2026

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

Status Scan Engine 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.

…nners [IDE-1786]

Phase 2.3 of folder config refactoring: inject ConfigResolver into scanner
context via enrichContextAndLogger, and migrate all four scanners (OSS, Code,
IaC, Secrets) to read ConfigResolver from context with fallback to struct field.

- Add DepConfigResolver constant and context helpers (ConfigResolverFromContext,
  NewContextWithConfigResolver) in internal/context
- Inject ConfigResolver into deps map in DelegatingConcurrentScanner
- Add getConfigResolver(ctx) helper to each scanner with context-first fallback
- Generate ConfigResolverInterface mock via gomock
- Tests FC-060 through FC-068 cover injection, retrieval, and fallback paths
…786]

Replace NullableField with *LocalConfigField in LSP wire protocol.
Unify $/snyk.folderConfigs into $/snyk.configuration using map-based
ConfigSetting for both global and per-folder settings.

Refactor ConfigResolver to delegate to GAF resolver for all settings
except folder metadata (AutoDeterminedOrg, LocalBranches) which are
read directly from FolderMetadataKey. Remove legacy fallback paths.

Update SyncToConfiguration to correctly write user-settable folder
settings to UserFolderKey and LS-enriched metadata to FolderMetadataKey.

Migrate scanners (base_scan, pre_scan_command, cli_scanner) to use
ConfigResolver for all configurable values instead of direct struct
field access.
…ve migration code [IDE-1786]

- Move SastSettings from FolderConfig struct to GAF configuration with
  Get/SetSastSettings helpers
- Replace hardcoded persistence lists with dynamic Set+PersistInStorage
  via SetFolderUserSetting and SetFolderMetadataSetting helpers
- Remove all legacy migration code: legacyFolderConfigJSON,
  legacyStoredConfig, MigrateLegacyFolderConfig,
  RegisterFolderPersistence, getStoredConfigForMigration,
  MigrateFolderConfigOrgSettings, OrgMigratedFromGlobalConfig flag,
  settings_migration.go
- Fix updateFolderOrgIfNeeded: check orgSettingsChanged before global
  org inheritance to prevent overwriting explicit user org changes
- Rename tests and comments to remove stale migration references
- Add precedence and scanner precedence smoke tests
- Add config resolver delegation tests
Move cliPath, Insecure, AdditionalOssParameters, and ReleaseChannel
fields from CliSettings struct directly into Config. Promote all
CliSettings methods (Installed, IsPathDefined, Path, SetPath,
DefaultBinaryInstallPath) to Config methods with Cli prefix.

Remove CliSettings struct, NewCliSettings constructor, CliSettings()
accessor, SetCliSettings() mutator, and the dedicated
cliPathAccessMutex (Config's existing mutex provides thread safety).

Update all ~80 call sites across 30 files from c.CliSettings().X()
to c.X() pattern.
@bastiandoetsch bastiandoetsch changed the title refactor(types): wire snyk-ls to GAF flagset-native config resolution [IDE-1786] refactor(config): Phase 2 - GAF configuration, dynamic persistence, struct cleanup [IDE-1786] Mar 5, 2026
@bastiandoetsch
Copy link
Copy Markdown
Collaborator Author

/describe

Document the complete configuration architecture including:
- Configuration scopes (machine, org, folder) with prefix key storage
- Flag registration via pflag.FlagSet with annotations
- Precedence resolution rules per scope (locked > user > remote > default)
- Effective organization resolution flow
- LDX-Sync remote configuration with 4 sync triggers
- Locked field enforcement (sync-time clearing + edit-time rejection)
- IDE ↔ LS protocol with ConfigSetting wire type
- FolderConfig thin wrapper architecture
- Persistence via SetFolderUserSetting/SetFolderMetadataSetting helpers

Include mermaid sequence diagrams for all major flows:
- Machine and org scope precedence resolution
- Effective org determination
- LDX-Sync trigger lifecycle
- Locked field enforcement
- IDE→LS and LS→IDE configuration exchange
…n [IDE-1786]

Migrate boolean, string, and integer Config struct fields to use GAF
Configuration as the backing store via UserGlobalKey prefix keys. Config
getters/setters now delegate to gafGetBool/gafGetString/gafGetInt helpers,
removing the need for struct fields and mutex locking (GAF is thread-safe).

Key changes:
- Add gafConf/gafGetBool/gafSetBool/gafGetString/gafSetString/gafGetInt/gafSetInt
  helper methods on Config for type-safe GAF access
- Register flags via RegisterAllConfigurations in newConfig() so defaults
  are available immediately after engine init
- Migrate 16 boolean fields (product toggles, feature flags, proxy settings)
- Migrate 6 string fields (cliBaseDownloadURL, proxy*, authenticationMethod, etc.)
- Migrate 2 integer fields (hoverVerbosity, riskScoreThreshold)
- Skip Phase 1 raw writes in processConfigSettings for migrated settings
  (apply functions write validated values via setters)
- Update SetUpEngineMock to copy GAF settings when replacing engine
- Replace MockConfiguration with real configuration.NewWithOpts() in tests
  for simpler, more robust test setup
…ated settings [IDE-1786]

Now that boolean and int settings are backed by GAF UserGlobalKey,
the ConfigResolver's fallback to ConfigProvider methods is redundant.
GetValue already resolves through the full prefix key chain including
UserGlobalKey defaults.

Key changes:
- Shrink ConfigProvider interface to only FilterSeverity + IssueViewOptions
  (composite types still on Config struct)
- Replace isSettingEnabledForFolder(fallback) with GetBool delegation
- Remove r.c nil guards from methods that no longer access ConfigProvider
- Add DefaultConfigResolver test helper with proper prefix key wiring
- Register pflag flags in SetUpEngineMock so GAF ConfigResolver has scope
  metadata for resolution
- Update all defaultResolver functions in tests to use proper wiring
- Replace MockConfiguration with real configuration.NewWithOpts() in tests
@bastiandoetsch
Copy link
Copy Markdown
Collaborator Author

/review

…age ordering, clean up dead code [IDE-1786]

- Define 27 new Setting* constants for all internal configuration keys
  (device_id, offline, format, trusted_folders, etc.) in ldx_sync_config.go
- Replace all raw strings in UserGlobalKey("...") calls across 50+ files
  with the corresponding types.Setting* constants
- Fix PersistInStorage ordering: must be called BEFORE Set, not after,
  in all folder config write paths (5 files)
- Remove stale ConfigProvider mock expectations (FilterSeverity,
  IssueViewOptions) after interface was emptied
- Remove dead StoredConfig struct, FolderConfigsParam, and related
  serialization code from xdg.go
- Remove 125+ thin getter/setter wrapper methods from Config struct
- Extract 9 business logic methods into standalone package-level functions
- Increase test timeout from 45m to 60m for smoke test suite
@bastiandoetsch
Copy link
Copy Markdown
Collaborator Author

/describe

…ckFileName, add engine/conf context helpers [IDE-1786]

Phase 3.5 completion:
- Inline SetConfigFile into main.go and ls_extension/main.go, remove method
- Extract SetOrganization to standalone function, update ~35 callers across 17 files
- Inline CLIDownloadLockFileName instance method, update 6 callers to use standalone

Phase 3.6.1:
- Add Engine and Configuration context helpers to internal/context
  (NewContextWithEngine/EngineFromContext, NewContextWithConfiguration/ConfigurationFromContext)
- Add DepEngine and DepConfiguration dependency keys
- Table-driven tests for all dependency-map context helpers (Workspace, Engine, Configuration)
…te NewConfigResolver signature [IDE-1786]

Remove all references to LDXSyncConfigCache from test files across the codebase.
Update all NewConfigResolver(cache, logger) calls to NewConfigResolver(logger).

Replace cache-based org storage with GAF-native equivalents:
- cache.SetOrgConfig -> WriteOrgConfigToConfiguration
- cache.SetFolderOrg -> SetAutoDeterminedOrg
- cache.GetOrgIdForFolder -> ReadFolderConfigSnapshot.AutoDeterminedOrg

Fix Test_processResults_ShouldCountSeverityByProduct: create folder after
SetUpEngineMock so f.conf and workspace storage use the same configuration.

Update Test_RefreshConfigFromLdxSync_EmptyFolderPath: document that empty
paths are skipped by ReadFolderConfigSnapshot (PathKey returns empty string
leading to early return).
@bastiandoetsch
Copy link
Copy Markdown
Collaborator Author

/describe

… smoke tests [IDE-1786]

- Replace time.Sleep with require.Never in oss_test.go for
  Test_scheduleNewScanWithProductDisabled_NoScanRun and
  Test_scheduleNewScan_ContextCancelledAfterScanScheduled_NoScanRun
- Replace time.Sleep with require.Never in precedence_smoke_test.go for
  Test_SmokeScanPrecedence_AllDisabled_NoScansRun and
  Test_SmokeScanPrecedence_UserOverrideDisablesProduct
- Use TempDirWithRetry for storage directory in redirectConfigAndDataHome
  to prevent flaky TempDir cleanup failures when goroutines outlive tests
- Call c.SetStorage(s) in redirectConfigAndDataHome so c.Storage() is
  non-nil, fixing nil pointer panic in NewOAuthProvider during OAuth
  smoke tests (Test_InvalidExpiredCredentialsSendMessageRequest)
…DE-1786]

Phase 3.6.2: Update server.Start signature

- Change Start(c *config.Config) -> Start(engine workflow.Engine)
- Derive conf from engine.GetConfiguration() inside Start
- Derive logger from engine.GetLogger() after ConfigureLogging sets it
- Update jrpc2 ServerOptions logger closures to use engine.GetLogger()
- Use config.CurrentConfig().ConfigureLogging(srv) as interim bridge
  until Phase 3.6.7 extracts token scrubbing from Config
- Update main.go and ls_extension/main.go callers to pass c.Engine()
…ow.Engine [IDE-1786]

Replace *config.Config parameter with workflow.Engine across multiple packages
as part of Phase 3.6.6 of the config refactoring (remove LDXSyncConfigCache).

Changes include:
- infrastructure/code: migrate code_html.go, sarif_utils.go, ai_fix_handler.go,
  sast_local_engine.go, snyk_code_http_client.go, autofix.go, code.go
- infrastructure/oss: migrate oss_html.go (NewHtmlRenderer)
- infrastructure/secrets: migrate secrets_html.go (NewHtmlRenderer)
- domain/ide/command: replace config.CurrentConfig() with cmd.engine in
  code_fix_apply_edit.go, code_fix_diffs.go, code_fix_feedback.go,
  navigate_to_range.go, generate_issue_description.go and all other command files
- application/server/configuration.go: thread engine through updateFolderOrgIfNeeded
- Update all callers and test files to pass engine/configuration instead of *config.Config
…ndant calls [IDE-1786]

Introduce types.GetGlobalOrganization(conf) to enforce correct precedence
(UserGlobalKey first, then configuration.ORGANIZATION fallback), replacing
16 direct conf.GetString(configuration.ORGANIZATION) reads across 9 files.

Add types.SettingConfigFileLegacy constant to replace magic "configfile"
string in main.go, ls_extension/main.go, and config.go.

Eliminate redundant engine.GetConfiguration()/GetLogger() calls in ~27
locations by caching conf and logger at function entry and passing to
helpers.
Create types.TokenService interface and TokenServiceImpl to encapsulate
token lifecycle: writing to GAF configuration, adding scrub terms to the
logger, and notifying change listeners.

Update NewAuthenticationService and NewDelegatingScanner constructors to
accept TokenService as a dependency. Auth service uses tokenService.SetToken()
directly instead of config.SetToken(), and scanner uses
tokenService.TokenChangesChannel() instead of config.TokenChangesChannel().

Config.SetToken/TokenChangesChannel now delegate to the internal
TokenService; these will be removed when Config struct is deleted (3.6.8).
…alone functions [IDE-1786]

Create SetupLogging(engine, tokenService, server) as standalone
replacement for Config.ConfigureLogging(). Manage log file and scrubbing
writer as package-level state instead of Config fields.

Add types.IsDefaultEnvReady(conf) and types.WaitForDefaultEnv(ctx, conf)
backed by a channel stored in GAF configuration under
SettingDefaultEnvReadyChannel. Replace config.CurrentConfig().IsDefaultEnvReady()
call in configuration.go with the standalone function.

Add DisableFileLogging(conf, logger) as standalone replacement for
Config.DisableLoggingToFile(). Remove unused logFile field from Config.
nick-y-snyk and others added 10 commits March 23, 2026 15:35
Add auth method selector, dirty-state tracking, auth field monitor,
and updated config HTML template with full settings page JS stack.
Consolidate JS tests under js-tests/ with ESLint-based ES5 checking
and node --test auto-discovery. Remove old js-tests from treeview template.
Update Makefile JS test targets, .gitignore, and goreleaser config
for the new js-tests layout and ES5 ESLint check.
…configuration.go

Replace duplicate logic in applyApiEndpoints and applyAuthenticationMethod with
the shared helpers from apply_auth_config.go. Also removes the redundant
ConfigureProviders call after Logout in the endpoint-change path.
…g to scan all JS

- Add space before ( in anonymous function declarations in dirty-tracker.js
- Move eslint.config.js to repo root so base path covers all template JS files
- Run eslint on .. to scan whole repo instead of only js-tests directory
- Exclude Go template files with ${} placeholders that can't be parsed, ESM
  test files, and minified vendor polyfills from ES5 linting
- Execute now returns an error for any arg count other than 0 or 3
- Replace time.Sleep with firstStarted channel in CancellationPreservesExistingToken test
- Add missing second Authenticate call to actually trigger cancellation in the test
- Add clarifying comments to login_test.go tests explaining argument intent
…ation

Move change listener notification out of checkDirty() so that DOM
mutations (e.g. auth-field-monitor clearing the token field) happen
before the dirty comparison, eliminating the double collectDataFn()
call and the split-reality between the passed data snapshot and the
live DOM within the notification loop.

- Add DirtyTracker.runChangeListeners(): collects current form data
  and notifies all registered listeners; callers invoke this before
  checkDirty() so side-effects settle first.
- Simplify checkDirty() to a single collect-and-compare with no
  listener notification.
- Update triggerChangeHandlers() in form-state.js to call
  runChangeListeners() then checkDirty() in sequence.
- Fix reset-handler.js: was referencing window.ConfigApp.dirtyTracker
  (always undefined) instead of window.dirtyTracker; both reset paths
  now correctly call runChangeListeners() + checkDirty().
- Update dirty-tracker tests: rename listener tests to target
  runChangeListeners(), add assertion that checkDirty() does not
  invoke listeners.

[IDE-1701]
…progress

ConfigureProviders (called in applyAuthConfig) acquires the auth mutex, but a
blocking Authenticate holds that same mutex while waiting for an OAuth callback.
Without cancelling the in-progress auth first, calling ConfigureProviders
deadlocks because Authenticate (which would cancel the old auth) never runs.

- Add CancelOngoingAuth() to AuthenticationService interface and implement it:
  cancels the in-progress auth context without acquiring the auth mutex.
- Call CancelOngoingAuth() in loginCommand.Execute before applyAuthConfig,
  so the blocking Authenticate releases the mutex before ConfigureProviders
  tries to acquire it.
- Use CancelOngoingAuth() inside Authenticate itself to deduplicate the
  cancel-previous logic.
- Add BlockingFakeAuthProvider test double and regression test that deadlocks
  without the fix.
…n [IDE-1701]

Remove fallback to AUTHENTICATION_TOKEN and OAuth token keys when the

structured user-global token is not set, matching WriteTokenToConfig as the single source for GetToken.

Made-with: Cursor
Copy link
Copy Markdown
Contributor

@rrama rrama left a comment

Choose a reason for hiding this comment

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

Submitting my comments so far. I am still a long way off, but I want to at least feedback some of the bugs I spotted.

Concern:

  1. To me it was not immediately clear that there is special folder config paths for OrgSetByUser, PreferredOrg, and AutoDeterminedOrg that do not have wiring, but all other folder configs need it. I am wondering if we should switch to calling them something else to make it clearer. They could be FolderOrgSelectors or something (not so keen on calling them "native"), then if those functions are renamed to follow suit it would be clear if you are getting wiring for full or not. Then could do similar for the Git enrichments (not so keen on calling them "metadata").
  2. You have to know a lot about a key to get its value, it would be easier if all the information about a setting and how to get it's value was all in one place and you just use that everywhere. Currently it is spread between files like configuration.go, config.go, config_readers.go, ldx_sync_config.go and register_configurations.go, so there is a bit of jumping back and forth to figure out that some user global keys are overridden by LDX-Sync, etc., but feels weird I have to know it is a user global key in some of the places instead of that being stored with the key definition.

Testing improvements:

  1. Maybe an additional test to test a bit further / more multi-step than just Test_RefreshConfigFromLdxSync_PreservesNonLockedOverrides: A test which starts off with user set at folder risk score at 200, then a remote admin locks it to 300 and user fetches, then the remote admin unlocks and unsets the setting plus the user fetches, then verify that we get back the default of 0 and not the stale 200.

Personal things to still check:

  1. Do we need migration code?
  2. Look into the bug AI thought it found:

    Tracing ApplyLspUpdate (IDE → LS folder config writes)
    FolderConfig.ApplyLspUpdate (folder_config.go:255) → applyFolderScopeUpdates (:344):

    applyBasicFolderFields (:395): Handles SettingBaseBranch, SettingReferenceBranch, SettingLocalBranches, SettingAdditionalParameters, SettingAdditionalEnvironment, SettingReferenceFolder, SettingScanCommandConfig. All written to UserFolderKey as *LocalConfigField{Value, Changed:true} except SettingLocalBranches which goes to FolderMetadataKey.
    applyPreferredOrg (:488): Sets both SettingPreferredOrg and SettingOrgSetByUser together via UserFolderKey. Marks both as handled.
    applyOrgSetByUser (:520): Handles standalone SettingOrgSetByUser changes. Note: applyPreferredOrg already marks SettingOrgSetByUser as handled at line 490, but applyOrgSetByUser re-adds it at line 521 — this is fine since the handled map is only used by the generic PATCH loop.
    Generic PATCH loop (:373-391): For any remaining folder-scoped settings not in handled, applies Changed:true values to UserFolderKey, or Unsets on Value:nil.
    Issue: getSettingValue doesn't check Changed flag
    getSettingValue (folder_config.go:264-276) does not check cs.Changed. This means applyBasicFolderFields and applyPreferredOrg will process settings even when Changed: false. The generic PATCH loop at line 374 correctly checks !cs.Changed, but the explicit handlers don't. This is inconsistent with the documented PATCH semantics. In practice, it's mitigated by equality checks (e.g., baseBranch != cur), but a setting sent with Changed:false and a different value than current would still be applied.

- Folder Config Notification
- method: `$/snyk.folderConfigs`
- params: `types.FolderConfigsParam`
- Configuration Notification (protocol v25+)
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.

Nitpick: Don't state the protocol version, if someone wants historic analysis, they can get it from the Git history.

* limitations under the License.
*/

package types
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.

Why is this whole file in the types package?

// goroutine. SetEngineDefaults only sets defaults when the key is not yet present,
// so a pre-seeded configuration ensures the goroutine reads the test-specific paths
// rather than the system defaults (which in CI can include many unrelated Java installs).
conf := configuration.NewWithOpts(configuration.WithAutomaticEnv())
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.

Minor nitpick: Comment should also explain we use this technique to be more realistic, as in prod we have WithAutomaticEnv().

func initEngineForClientSettingsTest(t *testing.T) workflow.Engine {
t.Helper()
e, _ := InitEngine(nil)
e.GetConfiguration().Set(configresolver.UserGlobalKey(types.SettingBinarySearchPaths), []string{})
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.

Not meant to be keyed.

func initEngineForConfigTest(t *testing.T) (workflow.Engine, *TokenServiceImpl) {
t.Helper()
engine, ts := InitEngine(nil)
engine.GetConfiguration().Set(configresolver.UserGlobalKey(types.SettingBinarySearchPaths), []string{})
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.

Same, not meant to be keyed.

modified := c.enableDeltaFindings != enabled
c.enableDeltaFindings = enabled
return modified
return getCustomEndpointUrlFromSnykApi(types.GetGlobalString(conf, types.SettingApiEndpoint), "deeproxy")
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.

Nitpick: Again may be worth calling out in a comment that LDX-Sync will directly override the user global key for API endpoint.

// AuthenticationMethodMatchesCredentials returns true if the token matches the configured authentication method.
func AuthenticationMethodMatchesCredentials(token string, method types.AuthenticationMethod, logger *zerolog.Logger) bool {
if method == types.FakeAuthentication {
return true // We allow any value for the token in unit tests which use FakeAuthentication.
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.

Nitpick: Leave the comment in.

if org != "" {
// Store the resolved org so that defaultFuncOrganization's UUID fast-path
// returns it directly next time, avoiding /rest/self.
conf.Set(configuration.ORGANIZATION, org)
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.

Not needed. After a resolved defaultFunc GAF will cache it.
Although standalone and tests do not have the cache enabled... See https://snyksec.atlassian.net/browse/IDE-1727
So a future enhancement to remove this then.

c.deviceId = deviceId
}
if engine == nil {
conf := configuration.NewWithOpts(configuration.WithAutomaticEnv())
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.

Future enhancement reminder: We should enable the cache for standalone / tests: See https://snyksec.atlassian.net/browse/IDE-1727

}
if v, ok := settingBool(settings, types.SettingIssueViewIgnoredIssues); ok {
ivo.IgnoredIssues = v
}
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.

If only one is set then the other will be defaulted back to `false. TODO - Check severity filters too.

nick-y-snyk and others added 14 commits March 30, 2026 18:40
…-1701]

WriteTokenToConfig was falling back to AUTHENTICATION_TOKEN and
CONFIG_KEY_OAUTH_TOKEN when SettingToken was not set, while GetToken
only reads from SettingToken. This mismatch caused spurious
token-change notifications when the fallback keys held a value
(e.g. from environment), breaking
Test_TokenChangedToSameToken_ChannelsNotInformed.

Also removes redundant handleProviderInconsistencies call in
authenticate() since the provider is already checked immediately after.
…701]

Update GAF to v0.0.0-20260331101019-870612009636 which includes
the fix for math.MaxInt64 overflow on 32-bit targets (linux_386,
windows_386) in the conversion package.
Add linux_386 and windows_386 binaries to the upload script to match
the goreleaser build targets. Without this, the SHA256SUMS verification
fails because it includes checksums for 386 binaries that were never
copied to the verification directory.
When logout is called before any provider has been selected (e.g. during
endpoint change triggered logout), authProvider is nil and
ClearAuthentication panics. Add a nil check before calling it.
feat(IDE-1701): add auth params to login command and fix auth mutex [rebased onto IDE-1786]
# Conflicts:
#	application/codeaction/codeaction.go
#	application/config/config.go
#	application/server/configuration.go
#	application/server/server_smoke_test.go
#	application/server/server_test.go
#	domain/ide/command/execute_cli.go
#	domain/ide/workspace/folder.go
#	domain/scanstates/scan_state_aggregator.go
#	domain/scanstates/scan_state_aggregator_test.go
#	domain/scanstates/summary_html.go
#	domain/snyk/scanner/scanner.go
#	domain/snyk/scanner/scanner_test.go
#	domain/snyk/scanner/test_scanner.go
#	go.mod
#	go.sum
#	infrastructure/code/snyk_code_http_client.go
#	infrastructure/oss/cli_scanner.go
#	infrastructure/secrets/secrets.go
#	infrastructure/secrets/secrets_test.go
#	internal/folderconfig/folder_config.go
#	internal/types/config_resolver.go
#	internal/types/mock_types/scan_mock.go
#	internal/types/scan.go
Comment on lines +186 to +189
key := configresolver.FolderMetadataKey(dst, name)
conf.PersistInStorage(key)
conf.Set(key, v)
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why would we need to persist config for base scan ?

for _, name := range userSettings {
if v := conf.Get(configresolver.UserFolderKey(src, name)); v != nil {
key := configresolver.UserFolderKey(dst, name)
conf.PersistInStorage(key)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Persist is now being called in many places, it should be centralized, it can be done on init and maybe when a folder is trusted

Comment on lines +170 to +174
userSettings := []string{
SettingBaseBranch, SettingReferenceBranch, SettingAdditionalParameters,
SettingAdditionalEnvironment, SettingReferenceFolder, SettingScanCommandConfig,
SettingPreferredOrg, SettingOrgSetByUser,
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we shouldn't do this, instead load config names via scope and iterate through them

gafConfiguration := conf

fs := pflag.NewFlagSet("snyk-ls-config", pflag.ContinueOnError)
types.RegisterAllConfigurations(fs)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this is also called when we create a new config so it's duplicated.

logger.Debug().Err(err).Str("method", "clientSettingsFromEnv").Msgf("couldn't parse oss config %s", oss)
}
c.SetSnykOssEnabled(parseBool)
conf.Set(configresolver.UserGlobalKey(types.SettingSnykOssEnabled), parseBool)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The API to set a config is very confusing, when setting a configuration we must know the the scope which is pretty much error prone.
Instead we should simply just call Set and it will determine what the scope of this setting is based on what we already registered.

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.

7 participants