Skip to content

Commit

Permalink
feat: inject custom styling [IDE-240] (#535)
Browse files Browse the repository at this point in the history
* feat: inject custom styling

* refactor: move nonce computation to ide

* fix: log-level to debug instead of trace

* chore: add comment to test

* refactor: remove unused imports

* fix: hide ignore footer

---------

Co-authored-by: Bastian Doetsch <[email protected]>
  • Loading branch information
teodora-sandu and bastiandoetsch authored Jun 3, 2024
1 parent e82b6ac commit 6e5ec6d
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 13 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
build/
out/
/bin/
src/main/resources/stylesheets/*.css
src/main/resources/stylesheets/*.css.map

#################################
# Gradle files #
Expand Down
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Snyk Security Changelog

## [2.8.1]
### Added
- Injects custom styling for the HTML panel used by Snyk Code for consistent ignores.

## [2.8.0]

### Added
- Serve Snyk Code functionality via language server. This enables auto-scanning on startup / save, code actions for Snyk Learn and, if enabled, Snyk Auto-Fix. The number of uploaded files is not shown anymore.
- Serve Snyk Code functionality via language server. This enables auto-scanning on startup / save, code actions for Snyk Learn and, if enabled, Snyk Auto-Fix. The number of uploaded files is not shown anymore.

## [2.7.21]

Expand Down
14 changes: 14 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ plugins {
id("org.jetbrains.kotlin.jvm") version "1.9.23"
id("io.gitlab.arturbosch.detekt") version ("1.23.6")
id("pl.allegro.tech.build.axion-release") version "1.17.0"
id("io.miret.etienne.sass") version "1.5.0"
}

version = scmVersion.version
Expand Down Expand Up @@ -88,6 +89,19 @@ detekt {
}

tasks {
// plugin from https://github.com/EtienneMiret/sass-gradle-plugin
compileSass {
sourceDir = project.file("${projectDir}/src/main/resources/stylesheets")
outputDir = project.file("${projectDir}/src/main/resources/stylesheets")
}

processResources{
dependsOn(compileSass)
}
compileKotlin{
dependsOn(processResources)
}

withType<KotlinCompile> {
kotlinOptions.jvmTarget = jdk
kotlinOptions.languageVersion = "1.9"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package io.snyk.plugin.ui.toolwindow.panels

import com.intellij.uiDesigner.core.GridLayoutManager
import com.intellij.util.ui.JBUI
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.SnykFile
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.ui.DescriptionHeaderPanel
import io.snyk.plugin.ui.SnykBalloonNotificationHelper
import io.snyk.plugin.ui.baseGridConstraintsAnchorWest
Expand All @@ -15,6 +15,7 @@ import io.snyk.plugin.ui.toolwindow.SnykToolWindowPanel
import io.snyk.plugin.ui.wrapWithScrollPane
import snyk.common.ProductType
import snyk.common.lsp.ScanIssue
import stylesheets.SnykStylesheets
import java.awt.BorderLayout
import java.awt.Font
import javax.swing.JLabel
Expand All @@ -37,7 +38,8 @@ class SuggestionDescriptionPanelFromLS(
issue.canLoadSuggestionPanelFromHTML()
) {
val openFileLoadHandlerGenerator = OpenFileLoadHandlerGenerator(snykFile)
val jbCefBrowserComponent = JCEFUtils.getJBCefBrowserComponentIfSupported(issue.details()) {
val html = this.getStyledHTML()
val jbCefBrowserComponent = JCEFUtils.getJBCefBrowserComponentIfSupported(html) {
openFileLoadHandlerGenerator.generate(it)
}
if (jbCefBrowserComponent == null) {
Expand Down Expand Up @@ -111,6 +113,29 @@ class SuggestionDescriptionPanelFromLS(
}
return Pair(panel, lastRowToAddSpacer)
}

fun getStyledHTML(): String {
var html = issue.details()
var ideStyle = ""
if (issue.additionalData.getProductType() == ProductType.CODE_SECURITY || issue.additionalData.getProductType() == ProductType.CODE_QUALITY) {
ideStyle = SnykStylesheets.SnykCodeSuggestion

}
html = html.replace("\${ideStyle}", "<style nonce=\${nonce}>$ideStyle</style>")

val nonce = getNonce()
html = html.replace("\${nonce}", nonce)

return html
}

private fun getNonce(): String {
val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9')
return (1..32)
.map { allowedChars.random() }
.joinToString("")
}

}

fun defaultFontLabel(labelText: String, bold: Boolean = false): JLabel {
Expand Down
11 changes: 11 additions & 0 deletions src/main/kotlin/stylesheets/SnykStylesheets.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package stylesheets

object SnykStylesheets {
private fun getStylesheet(name: String): String {
return this::class.java.getResource(name)?.readText()
?: ""
}

val SnykCodeSuggestion = getStylesheet("/stylesheets/snyk_code_suggestion.css")
}

77 changes: 77 additions & 0 deletions src/main/resources/stylesheets/snyk_code_suggestion.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
::-webkit-scrollbar-thumb {
background: var(--scrollbar-thumb-color);
}

::-webkit-scrollbar-thumb:hover {
background: #595a5c;
}

body {
color: var(--text-color);
}

a,
.link {
color: var(--link-color);
}

.delimiter {
border: 1px solid #505254;
}


.ignore-warning {
background: #FFF4ED;
color: #B6540B;
border: 1px solid #E27122;
}


.ignore-badge {
background: #FFF4ED;
color: #B6540B;
border: 1px solid #E27122;
}


.data-flow-body {
background-color: var(--data-flow-body-color);
}


.data-flow-clickable-row {
color: var(--link-color);
}


.data-flow-delimiter {
color: #BBBBBB;
}

.tabs-nav {
border-bottom: 1px solid var(--tabs-bottom-color);
}

.tab-item-icon path {
fill: var(--tab-item-github-icon-color);
}

.tab-item:hover {
background-color: var(--tab-item-hover-color);
}

.tab-item.is-selected {
border-bottom: 3px solid #3474f0;
}

.example-line.added {
background-color: var(--example-line-added-color);
}

.example-line.removed {
background-color: var(--example-line-removed-color);
}

.ignore-action-container {
display: none;
}
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,18 @@ class SuggestionDescriptionPanelFromLSCodeTest : BasePlatformTestCase() {
val actualBrowser = getJLabelByText(cut, "<html>HTML message</html>")
assertNotNull(actualBrowser)
}

@Test
fun `test getStyledHTML should inject CSS into the HTML`() {
pluginSettings().isGlobalIgnoresFeatureEnabled = true

every { issue.details() } returns "<html><head>\${ideStyle}</head>HTML message</html>"
every { issue.canLoadSuggestionPanelFromHTML() } returns true
cut = SuggestionDescriptionPanelFromLS(snykFile, issue)

val actual = cut.getStyledHTML()
assertFalse(actual.contains("\${ideStyle}"))
assertFalse(actual.contains("\${nonce}"))
assertTrue(actual.contains(".ignore-warning"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,22 @@ import com.intellij.psi.PsiFile
import com.intellij.testFramework.fixtures.BasePlatformTestCase
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.unmockkAll
import io.snyk.plugin.Severity
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.resetSettings
import io.snyk.plugin.SnykFile
import io.snyk.plugin.ui.jcef.JCEFUtils
import io.snyk.plugin.ui.toolwindow.panels.SuggestionDescriptionPanelFromLS
import org.eclipse.lsp4j.Position
import org.eclipse.lsp4j.Range
import org.junit.Before
import org.junit.Test
import snyk.UIComponentFinder.getActionLinkByText
import snyk.UIComponentFinder.getJBCEFBrowser
import snyk.UIComponentFinder.getJButtonByText
import snyk.UIComponentFinder.getJLabelByText
import snyk.UIComponentFinder.getJPanelByName
import snyk.code.annotator.SnykCodeAnnotator
import snyk.common.ProductType
import snyk.common.lsp.CommitChangeLine
import snyk.common.lsp.DataFlow
import snyk.common.lsp.ExampleCommitFix
import snyk.common.lsp.IssueData
import snyk.common.lsp.ScanIssue
import java.nio.file.Paths
import javax.swing.JLabel

class SuggestionDescriptionPanelFromLSOSSTest : BasePlatformTestCase() {
private lateinit var cut: SuggestionDescriptionPanelFromLS
Expand Down Expand Up @@ -115,4 +105,19 @@ class SuggestionDescriptionPanelFromLSOSSTest : BasePlatformTestCase() {
val ossOverviewPanel = getJPanelByName(cut, "overviewPanel")
assertNotNull(ossOverviewPanel)
}

@Test
fun `test getStyledHTML should inject CSS into the HTML`() {
pluginSettings().isGlobalIgnoresFeatureEnabled = true

every { issue.details() } returns "<html><head><style>\${ideStyle}</style></head>HTML message</html>"
every { issue.canLoadSuggestionPanelFromHTML() } returns true
cut = SuggestionDescriptionPanelFromLS(snykFile, issue)

val actual = cut.getStyledHTML()

// we don't apply any custom style for oss
assertFalse(actual.contains("\${ideStyle}"))
assertFalse(actual.contains(".ignore-warning"))
}
}

0 comments on commit 6e5ec6d

Please sign in to comment.