Skip to content

feat(secrets): add Snyk Secrets product type [IDE-1126][IDE-1749]#788

Merged
bastiandoetsch merged 57 commits intomasterfrom
feat/IDE-1749_add-secrets-product
Feb 24, 2026
Merged

feat(secrets): add Snyk Secrets product type [IDE-1126][IDE-1749]#788
bastiandoetsch merged 57 commits intomasterfrom
feat/IDE-1749_add-secrets-product

Conversation

@bastiandoetsch
Copy link
Collaborator

@bastiandoetsch bastiandoetsch commented Feb 19, 2026

User description

Summary

Add Snyk Secrets as a new product type to the IntelliJ plugin, enabling editor annotations, caching, and Language Server integration for secrets scanning results.

Changes

New Files

  • SnykSecretsAnnotator.kt — Editor annotator for Snyk Secrets issues (mirrors SnykCodeAnnotator)
  • secrets.svg / secrets_disabled.svg — Product icons
  • SnykCachedResultsTest.kt — Tests for secrets cache, product map lookup, and LsProduct mapping

Modified Files

  • ProductType.kt — Added SECRETS enum entry; renamed IAC treeName to "Snyk Infrastructure as Code"
  • Types.kt — Added Secrets to LsProduct enum; added SECRETS filterable type; updated title(), issueNaming(), details() for SECRETS
  • SnykCachedResults.kt — Added currentSecretsResultsLS and currentSecretsError; moved annotation refresh debouncing from SnykToolWindowPanel
  • SnykLanguageClient.kt — Handle LsProduct.Secrets in diagnostics pipeline and AI fix actions
  • Utils.kt — Added isSnykSecretsRunning() and SECRETS branch in getSnykCachedResultsForProduct
  • CodeActionIntention.kt — Added SECRETS icon and title (was TODO() crash)
  • SnykApplicationSettingsStateService.kt — Added secretsScanEnabled setting
  • SnykScanListener.kt — Added scanningSecretsFinished()
  • SnykToolWindowPanel.kt — Removed annotation refresh logic (moved to SnykCachedResults); secrets use HTML tree view
  • SnykToolWindowSnykScanListener.kt — Handle secrets scan lifecycle

Tests

  • 10 new tests in ScanIssueTest for SECRETS-specific methods
  • 6 new tests in SnykCachedResultsTest for secrets cache and LsProduct mapping
  • Updated SnykToolWindowSnykScanListenerTest for IAC treeName change

Verification

  • ✅ All tests pass
  • ✅ Snyk Code scan — 0 issues
  • ✅ Snyk SCA scan — no new issues
  • ✅ ktlint + spotless clean
  • ✅ Pre-commit hooks pass

PR Type

Enhancement


Description

  • Add support for Snyk Secrets scanning.

  • Integrate secrets scanning results into the plugin.

  • Centralize annotation refresh logic for performance.

  • Update data models and UI for the new product.


Diagram Walkthrough

flowchart LR
  A[New Product Type: Secrets] --> B(Data Model Updates);
  B --> C(UI Integration);
  B --> D(Annotator Integration);
  E[Annotation Refresh Logic] --> F(Centralized in Cache);
  D --> G(Plugin.xml);
  C --> H(Tool Window Panel);
  B --> I(Settings Service);
  B --> J(Utils);
  J --> K(Secrets Running Check);
  I --> L(Secrets Enabled Flag);
  F --> M(SnykCachedResults);
  M --> N(DaemonCodeAnalyzer);
  subgraph Core Plugin
    A
    B
    C
    D
    E
    F
    G
    H
    I
    J
    K
    L
    M
    N
  end
Loading

File Walkthrough

Relevant files
Enhancement
12 files
SnykIcons.kt
Add Snyk Secrets icons                                                                     
+2/-0     
Utils.kt
Add secrets running check and cache lookup                             
+4/-0     
SnykApplicationSettingsStateService.kt
Add secrets scan enablement setting                                           
+16/-6   
SnykProjectSettingsConfigurable.kt
Use ParametersListUtil for CLI arguments parsing                 
+3/-5     
SnykToolWindowSnykScanListener.kt
Clear secrets cache and handle scan completion                     
+9/-4     
ProductType.kt
Add Snyk Secrets product type                                                       
+9/-1     
SnykCachedResults.kt
Add secrets cache, centralize annotation refresh logic     
+99/-1   
CodeActionIntention.kt
Support Snyk Secrets in code actions                                         
+2/-0     
SnykSecretsAnnotator.kt
Implement Snyk Secrets annotator                                                 
+26/-0   
SnykLanguageClient.kt
Handle Snyk Secrets in LSP communication                                 
+30/-7   
Types.kt
Add LsProduct.Secrets and update ScanIssue for secrets     
+25/-51 
plugin.xml
Register Snyk Secrets annotator                                                   
+1/-0     
Documentation
1 files
SnykScanListener.kt
Minor comment updates for scan listener                                   
+2/-0     
Bug fix
2 files
SnykCliDownloaderService.kt
Guard CLI download if not auto-managed                                     
+13/-16 
FolderConfigSettings.kt
Ensure baseBranch has default value                                           
+1/-0     
Refactoring
2 files
SnykToolWindowPanel.kt
Remove annotation refresh logic, note secrets use HTML view
+2/-78   
SnykAnnotator.kt
Refactor file issue retrieval for cache lookup                     
+5/-11   
Tests
5 files
SnykProjectSettingsConfigurableTest.kt
Test parsing of CLI arguments for folder configs                 
+72/-0   
TreeViewBridgeHandlerTest.kt
Update tests to use await for async verifications               
+9/-7     
SnykToolWindowSnykScanListenerTest.kt
Update IAC tree name and verify root nodes                             
+1/-1     
SnykCachedResultsTest.kt
Add tests for secrets cache and annotation refresh drain 
+160/-0 
ScanIssueTest.kt
Add tests for ScanIssue handling of Snyk Secrets type       
+226/-0 

Implement Phase 7 of the HTML tree view integration. When the
IntelliJ Registry flag `snyk.useHtmlTreeView` is enabled, the native
Swing tree + severity toolbar in SnykToolWindowPanel is replaced with
a JCEF-based HtmlTreePanel that renders server-driven HTML from the
`$/snyk.treeView` LSP notification.

Changes:
- Add `snyk.useHtmlTreeView` registry key in plugin.xml
- Add `isHtmlTreeViewEnabled()` helper in Utils.kt
- Add `SnykTreeViewParams` data class and `SnykTreeViewListener` topic
- Add `@JsonNotification("$/snyk.treeView")` handler in SnykLanguageClient
- Add `executeCommandWithArgs()` public method on LanguageServerWrapper
- Add `TreeViewBridgeHandler` with unified `__ideExecuteCommand__` JS bridge
- Add `HtmlTreePanel` JCEF panel subscribing to tree view notifications
- Conditionally swap TreePanel/HtmlTreePanel in SnykToolWindowPanel
- Hide native ViewActions and expand/collapse toolbar when flag is ON
- Add unit tests for bridge dispatch logic and LSP notification handler
…wBridgeHandler [IDE-1750]

- Add HtmlTreePanelTest: init, dispose, JCEF null path, notification
  handling, resource file validation, placeholder replacement
- Add executeCommandWithArgs tests to LanguageServerWrapperTest:
  not-initialized guard, successful execution, argument passing, null result
- Extend TreeViewBridgeHandlerTest: concurrent dispatch, LS exception
  handling, null callbackExecutor, default values, missing fields
…DE-1750]

- Remove result != null guard in dispatchCommand so callbacks fire even
  when LanguageServerWrapper.executeCommandWithArgs returns null (e.g.
  navigateToRange side-effect commands). Prevents leaked entries in
  window.__ideCallbacks__ and stalled UI state.
- Change snyk.useHtmlTreeView registry key to restartRequired=true since
  the panel swap only happens at SnykToolWindowPanel init time.
- Update test to assert callback IS invoked with null result (TDD).
- Fix JCEF browser memory leak: store browser as field, dispose in dispose()
- Replace SimpleToolWindowPanel with JPanel (fixes UiDataProvider warning)
- Add command allowlist to TreeViewBridgeHandler (security hardening)
- Add callbackId validation regex to prevent JS injection
- Skip native tree refresh when HTML tree view is enabled (performance)
- Replace Thread.sleep with Awaitility in tests
- Add tests for buildBridgeScript, ALLOWED_COMMANDS, and all new guards
Add JBCefApp.isSupported() check before creating HtmlTreePanel to prevent
blank panel on systems without JCEF support. Falls back to native Swing
TreePanel gracefully.
…ion [IDE-1750]

The LS snyk.navigateToRange command calls window/showDocument with a file
URI back to the client. The previous implementation fell through to
super.showDocument() which threw UnsupportedOperationException.

Now handles file URIs by resolving the VirtualFile and navigating to the
selection range. Unsupported schemes return ShowDocumentResult(false)
gracefully instead of throwing.
…etNodeExpanded [IDE-1750]

The JS tree view uses snyk.setNodeExpanded for expand/collapse state
persistence but it was missing from the allowlist. snyk.getTreeView was
listed but never used by the JS.
…[IDE-1750]

- onShowIssueDetail: bypass native tree search when HTML tree enabled,
  directly create SuggestionDescriptionPanel from cached ScanIssue
- HtmlTreePanel: skip loadHTML when raw HTML hash unchanged to prevent
  tree collapse on redundant LS re-emissions
- showDocument: match any snyk:// URI with showInDetailPanel action,
  not just Snyk Code. Map product string to correct ProductType.
- onShowIssueDetail: bypass native tree for HTML tree mode, directly
  create SuggestionDescriptionPanel from cached ScanIssue.
- Add diagnostic logging to trace showDocument and cache lookup flow.
…or [IDE-1750]

Add 7 missing --vscode-* CSS variable mappings needed by the HTML tree
view styles.css: tree-indentGuidesStroke, sideBar-background,
badge-background/foreground, list-activeSelectionBackground/Foreground,
list-hoverBackground.
- Add HtmlTreePanel.selectNode() to execute __selectTreeNode__ JS bridge
- Store HtmlTreePanel reference in SnykToolWindowPanel
- Wire selectNodeAndDisplayDescription to also select in HTML tree
- Expands ancestor nodes and scrolls issue into view
Wire onShowIssueDetail to also call htmlTreePanel.selectNode when
processing snyk:// showDocument requests, so the tree highlights the
corresponding issue node.
- Add HtmlTreePanel.reset() to reload initial empty HTML
- Call reset() from doCleanUi so 'Clean all results' clears the HTML tree
…750]

TreeViewInit.html now renders the same structure as the LS tree:
- Filter toolbar with severity SVG buttons (all active)
- Expand/collapse all buttons
- 3 product nodes (Open Source, Code Security, Infrastructure As Code)
  with their inline SVG icons, matching the LS tree.html template
- Uses the same CSS classes for theme integration via var(--vscode-*)
When isHtmlTreeViewEnabled(), selectNodeAndDisplayDescription now
directly loads SuggestionDescriptionPanel instead of relying on
native JTree node lookup (which may not have nodes populated).
Fixes gutter icon click sometimes not updating the description panel.
… offset [IDE-1750]

- Extract duplicated HTML tree description panel update logic into
  private selectNodeInHtmlTreeAndShowDescription method, called from
  both onShowIssueDetail and selectNodeAndDisplayDescription.
- Fix unsafe offset calculation in showDocument file URI handler by
  clamping startOffset/endOffset to document.textLength, preventing
  IndexOutOfBoundsException when character exceeds line length.
…E-1750]

Add tests for:
- reset() reloads init HTML into browser
- reset() skipped after dispose
- reset() no-op when JCEF is null
- selectNode() no-op when JCEF is null
- duplicate HTML content is skipped (hash check)

HtmlTreePanel coverage: 71.2% -> 86.4%
…st [IDE-1750]

Add snyk.showScanErrorDetails and snyk.updateFolderConfig to the
JCEF bridge allowlist so tree.js can invoke them via
__ideExecuteCommand__.
Add snyk.showScanErrorDetails and snyk.updateFolderConfig to the
expected commands in TreeViewBridgeHandlerTest.
@snyk-pr-review-bot

This comment has been minimized.

@bastiandoetsch bastiandoetsch force-pushed the feat/IDE-1749_add-secrets-product branch from 5f1c290 to ef6bc18 Compare February 23, 2026 09:40
@snyk-pr-review-bot

This comment has been minimized.

@snyk-pr-review-bot

This comment has been minimized.

@bastiandoetsch bastiandoetsch changed the title feat(secrets): add Snyk Secrets product type feat(secrets): add Snyk Secrets product type [IDE-1126][IDE-1749] Feb 23, 2026
… silent file loss [IDE-1749]

Replace non-atomic toList().also { clear() } with removeIf-based drain in
flushPendingAnnotationRefreshes. A file added between toList() and clear() was
silently dropped and would only be re-queued on the next diagnostic update.

With ConcurrentHashMap.newKeySet().removeIf, each element is removed and
collected atomically per-element. Any file added after the drain has passed its
bucket stays in the set and is processed on the next flush cycle — never lost.

Extract the drain into internal drainPendingAnnotationRefreshFiles() for
testability. Add table tests covering empty, single, multiple, and pre-drain
concurrent-add scenarios.
@snyk-pr-review-bot

This comment has been minimized.

@bastiandoetsch
Copy link
Collaborator Author

/describe

@snyk-pr-review-bot

This comment has been minimized.

@snyk-pr-review-bot
Copy link

PR Description updated to latest commit (ba502ea)

…sSeverityEnablementChanged [IDE-1749]

These calls had no effect after their return values were unassigned — the
methods have no side effects, so calling them without using the result
is pure dead code.
@snyk-pr-review-bot

This comment has been minimized.

@bastiandoetsch
Copy link
Collaborator Author

/describe

@snyk-pr-review-bot
Copy link

PR Description updated to latest commit (0ab243f)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Annotation Refresh moved to Cache

displaySnykCodeResults(results)
this.snykToolWindowPanel.triggerSelectionListeners = true
}
refreshAnnotationsForOpenFiles(project)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved to cache

)
}

@Test
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Tests the parser bug (IDE-1126)

"snyk.toggleTreeFilter",
listOf("severity", "high", true),
)
await().atMost(2, TimeUnit.SECONDS).untilAsserted {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fix flaky under windows

@nick-y-snyk
Copy link
Contributor

Code review

Found 1 issue:

  1. isSnykSecretsRunning is added in Utils.kt but is not included in isScanRunning(), which remains isOssRunning || isSnykCodeRunning || isIacRunning. As a result, the Stop scan action (SnykStopScanAction.kt:23), the Run scan action guard (SnykRunScanAction.kt:26), and the tool window scanning indicator (SnykToolWindowPanel.kt:741) will not detect a running secrets scan — the Stop button will appear disabled and no "Scanning..." message will show while secrets is scanning.

isProductScanRunning(project, ProductType.SECRETS)
fun isIacRunning(project: Project): Boolean = isProductScanRunning(project, ProductType.IAC)
fun isScanRunning(project: Project): Boolean =

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

Copy link
Contributor

@nick-y-snyk nick-y-snyk left a comment

Choose a reason for hiding this comment

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

Looks good to me, claude found "bug" in isScanRunning

val SNYK_SCAN_TOPIC = Topic.create("Snyk scan LS", SnykScanListener::class.java)
}

// these update the tree and should not be needed for the HTML tree anymore
Copy link
Contributor

Choose a reason for hiding this comment

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

leaked from another pr?

Copy link
Contributor

Choose a reason for hiding this comment

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

nevermind, I see it's merged

return if (this.additionalData.riskScore > 0) {
severityPriority + this.additionalData.riskScore
} else {
severityPriority + this.additionalData.priorityScore
Copy link
Contributor

Choose a reason for hiding this comment

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

is it possible that both riskScore and priorityScore are present?

Copy link
Contributor

Choose a reason for hiding this comment

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

discussed on daily

@bastiandoetsch
Copy link
Collaborator Author

Code review

Found 1 issue:

  1. isSnykSecretsRunning is added in Utils.kt but is not included in isScanRunning(), which remains isOssRunning || isSnykCodeRunning || isIacRunning. As a result, the Stop scan action (SnykStopScanAction.kt:23), the Run scan action guard (SnykRunScanAction.kt:26), and the tool window scanning indicator (SnykToolWindowPanel.kt:741) will not detect a running secrets scan — the Stop button will appear disabled and no "Scanning..." message will show while secrets is scanning.

isProductScanRunning(project, ProductType.SECRETS)
fun isIacRunning(project: Project): Boolean = isProductScanRunning(project, ProductType.IAC)
fun isScanRunning(project: Project): Boolean =

🤖 Generated with Claude Code

  • If this code review was useful, please react with 👍. Otherwise, react with 👎.

good catch, Claude :)

@snyk-pr-review-bot
Copy link

PR Reviewer Guide 🔍

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

Fragile Logic 🟠 [major]

The updateCache function relies on LsProduct.*.ordinal to identify which product's findings should be cleared when receiving empty diagnostics. This is highly fragile as any reordering or insertion of members in the LsProduct enum (defined in Types.kt) will silently break the mapping between the Language Server's product identifiers and the plugin's internal state. It is safer to use a static mapping or a dedicated method like LsProduct.fromInt(version) that does not depend on declaration order.

when (diagnosticsParams.version) {
  LsProduct.OpenSource.ordinal ->
    scanPublisher.onPublishDiagnostics(LsProduct.OpenSource, snykFile, emptySet())
  LsProduct.Code.ordinal ->
    scanPublisher.onPublishDiagnostics(LsProduct.Code, snykFile, emptySet())
  LsProduct.InfrastructureAsCode.ordinal ->
    scanPublisher.onPublishDiagnostics(LsProduct.InfrastructureAsCode, snykFile, emptySet())
  LsProduct.Secrets.ordinal ->
    scanPublisher.onPublishDiagnostics(LsProduct.Secrets, snykFile, emptySet())
Potential Data Loss 🟠 [major]

The else branch in the diagnosticsParams.version check clears the findings for all Snyk products (OSS, Code, IaC, and Secrets) for a given file. Since PublishDiagnosticsParams.version is defined in the LSP specification as the document version number, any diagnostic sent with a version number that does not match a product ordinal (e.g., version 10 after several edits) or a null version will inadvertently wipe all valid scan results for that file from the UI. This catch-all fallback should be removed or restricted to cases where clearing all products is explicitly intended.

  else -> {
    scanPublisher.onPublishDiagnostics(LsProduct.Code, snykFile, emptySet())
    scanPublisher.onPublishDiagnostics(LsProduct.OpenSource, snykFile, emptySet())
    scanPublisher.onPublishDiagnostics(LsProduct.InfrastructureAsCode, snykFile, emptySet())
    scanPublisher.onPublishDiagnostics(LsProduct.Secrets, snykFile, emptySet())
  }
}
📚 Repository Context Analyzed

This review considered 49 relevant code sections from 14 files (average relevance: 1.01)

@bastiandoetsch bastiandoetsch merged commit c54806f into master Feb 24, 2026
11 checks passed
@bastiandoetsch bastiandoetsch deleted the feat/IDE-1749_add-secrets-product branch February 24, 2026 14:55
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