fix: release auth write lock before post-credential hook [IDE-1901]#1211
fix: release auth write lock before post-credential hook [IDE-1901]#1211nick-y-snyk wants to merge 6 commits intorefactor/IDE-1786_folder-config-refactoringfrom
Conversation
Race condition: after login, IDE receives $/snyk.hasAuthenticated and triggers scan before SAST settings are cached → "Snyk Code not enabled". Fix: postCredentialUpdateHook runs populateAllFolderConfigs between credential storage and auth notification, ensuring feature flags and SAST settings are available before the IDE reacts. Review feedback addressed: - Use unenriched folder config (no git enrichment for feature flag fetch) - Loop only on trusted folders - Remove unnecessary Clone() - Set ConfigResolver on folder config so SetFeatureFlag persists to GAF - Make sendFolderConfigs synchronous in login command - Case-insensitive error string matching in shouldCauseLogout - Extract isTransientNetworkError to reduce cyclomatic complexity Includes regression test verifying feature flags populate before auth notification is sent (fails when hook is removed).
- Synchronize SetPostCredentialUpdateHook with a.m mutex to prevent data race - Skip hook when token is empty (logout path) to avoid wasteful API calls - Wrap hook invocation in recover() to prevent LSP server crash on panic
…DE-1901-sast-not-enabled-after-login
…ed [IDE-1901] Reverts populateAllFolderConfigs to iterate ws.Folders() instead of ws.GetFolderTrust(). Fixes regression where newly trusted folders never had feature flags populated, leading to "Snyk Code is not enabled" errors.
…-1901] Authenticate() held a.m.Lock() for the entire duration including the postCredentialUpdateHook (feature flag HTTP calls). This blocked all IsAuthenticated() RLock callers (status bar, scanner, etc.) for seconds, making the LS unresponsive during login. Split updateCredentials into token storage (under lock) and a returned post-action closure (hook + notification) that callers run after releasing the lock. Ordering preserved: token → flags → notification.
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Error must be the last return argument per staticcheck ST1008.
This comment has been minimized.
This comment has been minimized.
PR Reviewer Guide 🔍
|
Summary
Authenticate()held a write lock (a.m.Lock()) for the entire duration, including thepostCredentialUpdateHookwhich does feature flag HTTP calls viapopulateAllFolderConfigs()IsAuthenticated()callers (status bar, scanner, IDE heartbeat) blocked onRLock()for seconds → LS became unresponsive during loginupdateCredentials()now returns a post-action closure (hook + notification) that runs after the write lock is released. Ordering invariant preserved: token stored → feature flags populated → auth notification sentBefore (broken)
sequenceDiagram participant A as Authenticate() participant L as a.m (RWMutex) participant N as Network participant I as IsAuthenticated() A->>L: Lock() 🔒 activate L Note over L: write lock held A->>A: authProvider.Authenticate() A->>A: tokenService.SetToken() A->>N: postCredentialUpdateHook() activate N Note over N: HTTP: feature flags<br/>HTTP: SAST API<br/>(seconds...) I->>L: RLock() ⏳ Note over I,L: BLOCKED — waiting for<br/>write lock release N-->>A: hook done deactivate N A->>A: notifier.Send(AuthParams) A->>L: Unlock() 🔓 deactivate L L-->>I: RLock() acquired ✓ Note over I: finally unblockedAfter (fixed)
sequenceDiagram participant A as Authenticate() participant L as a.m (RWMutex) participant N as Network participant I as IsAuthenticated() A->>L: Lock() 🔒 activate L Note over L: write lock held A->>A: authProvider.Authenticate() A->>A: tokenService.SetToken() A->>A: capture hook ref A->>A: configureProviders() A->>L: Unlock() 🔓 deactivate L I->>L: RLock() ✅ Note over I: immediately unblocked,<br/>sees new token A->>N: postAction(): hook() activate N Note over N: HTTP: feature flags<br/>HTTP: SAST API<br/>(no lock held) N-->>A: hook done deactivate N A->>A: notifier.Send(AuthParams) Note over A: ordering preserved:<br/>token → flags → notificationLock scope comparison
gantt title Write lock duration comparison dateFormat X axisFormat %s section Before Lock held (token + hook + notify) :crit, 0, 5 Hook network calls (blocking) :active, 1, 4 section After Lock held (token only) :crit, 0, 1 Hook runs (no lock) :done, 1, 4updateCredentials caller flow
flowchart TD UC["updateCredentials(token, notify, url)"] UC --> STORE["Store token<br/>Remove old from cache<br/>Reset notif dedup"] STORE --> CAPTURE["Capture hook ref"] CAPTURE --> RETURN["Return postAction closure"] RETURN --> AUTH["Authenticate()<br/>runs AFTER Unlock()"] RETURN --> UPCRED["UpdateCredentials()<br/>runs AFTER Unlock()"] RETURN --> LOGOUT["logout()<br/>runs inline<br/>(hook skipped: token='')"] RETURN --> CALLBACK["credentialsCallback<br/>runs in goroutine<br/>(already no lock)"] style AUTH fill:#2d6,stroke:#333,color:#fff style UPCRED fill:#2d6,stroke:#333,color:#fff style LOGOUT fill:#69c,stroke:#333,color:#fff style CALLBACK fill:#69c,stroke:#333,color:#fffTest plan
Test_PostCredentialUpdateHook_CalledBeforeNotification— hook before notification orderingTestLoginCommand_Execute_FeatureFlagsPopulatedBeforeAuthNotification— end-to-end login orderingTest_UpdateCredentials— all credential update variantsTest_Authenticate— endpoint config after auth./infrastructure/authentication/...and./domain/ide/command/...