Skip to content

Commit

Permalink
Do not leak unknown asset paths into HTML (Kotlin#3061)
Browse files Browse the repository at this point in the history
  • Loading branch information
IgnatBeresnev authored Jul 7, 2023
1 parent f23a4a9 commit 14cd0f3
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class DefaultTemplateModelFactory(val context: DokkaContext) : TemplateModelFact

private fun Appendable.resourcesForPage(pathToRoot: String, resources: List<String>): Unit =
resources.forEach {
append(with(createHTML()) {
val resourceHtml = with(createHTML()) {
when {
it.URIExtension == "css" ->
link(
Expand All @@ -112,7 +112,11 @@ class DefaultTemplateModelFactory(val context: DokkaContext) : TemplateModelFact
it.isImage() -> link(href = if (it.isAbsolute) it else "$pathToRoot$it")
else -> null
}
} ?: it)
}

if (resourceHtml != null) {
append(resourceHtml)
}
}
}

Expand Down
89 changes: 89 additions & 0 deletions plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ import org.jetbrains.dokka.plugability.DokkaPluginApiPreview
import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement
import org.jetbrains.dokka.transformers.pages.PageTransformer
import org.jsoup.Jsoup
import org.jsoup.nodes.TextNode
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import utils.TestOutputWriterPlugin
import utils.assertContains
import java.io.File
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

class ResourceLinksTest : BaseAbstractTest() {
class TestResourcesAppenderPlugin(val resources: List<String>) : DokkaPlugin() {
Expand Down Expand Up @@ -205,4 +208,90 @@ class ResourceLinksTest : BaseAbstractTest() {
}
}
}

@Test // see #3040; plain text added to <head> can be rendered by engines inside <body> as well
fun `should not add unknown resources as text to the head or body section`() {
val configuration = dokkaConfiguration {
sourceSets {
sourceSet {
sourceRoots = listOf("src/main/kotlin")
}
}

pluginsConfigurations = mutableListOf(
PluginConfigurationImpl(
DokkaBase::class.java.canonicalName,
DokkaConfiguration.SerializationFormat.JSON,
toJsonString(
DokkaBaseConfiguration(
customAssets = listOf(File("test/unknown-file.ext"))
)
)
)
)
}

val writerPlugin = TestOutputWriterPlugin()
testInline(
"""
|/src/main/kotlin/test/Test.kt
|package test
|
|class Test
""".trimMargin(),
configuration,
pluginOverrides = listOf(writerPlugin)
) {
renderingStage = { _, _ ->
val testClassPage = writerPlugin.writer.contents
.getValue("root/test/-test/-test.html")
.let { Jsoup.parse(it) }

val headChildNodes = testClassPage.head().childNodes()
assertTrue("<head> section should not contain non-blank text nodes") {
headChildNodes.all { it !is TextNode || it.isBlank }
}

val bodyChildNodes = testClassPage.body().childNodes()
assertTrue("<body> section should not contain non-blank text nodes. Something leaked from head?") {
bodyChildNodes.all { it !is TextNode || it.isBlank }
}
}
}
}

@Test
fun `should load script as defer if name ending in _deferred`() {
val configuration = dokkaConfiguration {
sourceSets {
sourceSet {
sourceRoots = listOf("src/main/kotlin")
}
}
}

val writerPlugin = TestOutputWriterPlugin()
testInline(
"""
|/src/main/kotlin/test/Test.kt
|package test
|
|class Test
""".trimMargin(),
configuration,
pluginOverrides = listOf(writerPlugin)
) {
renderingStage = { _, _ ->
val generatedFiles = writerPlugin.writer.contents

assertContains(generatedFiles.keys, "scripts/symbol-parameters-wrapper_deferred.js")

val scripts = generatedFiles.getValue("root/test/-test/-test.html").let { Jsoup.parse(it) }.select("script")
val deferredScriptSources = scripts.filter { element -> element.hasAttr("defer") }.map { it.attr("src") }

// important to check symbol-parameters-wrapper_deferred specifically since it might break some features
assertContains(deferredScriptSources, "../../../scripts/symbol-parameters-wrapper_deferred.js")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -177,40 +177,6 @@ class PageTransformerBuilderTest : BaseAbstractTest() {
}
}

@Test
fun `should load script as defer if name ending in _deferred`() {
val configuration = dokkaConfiguration {
sourceSets {
sourceSet {
sourceRoots = listOf("src/main/kotlin")
}
}
}
val writerPlugin = TestOutputWriterPlugin()
testInline(
"""
|/src/main/kotlin/test/Test.kt
|package test
|
|class Test
""".trimMargin(),
configuration,
pluginOverrides = listOf(writerPlugin)
) {
renderingStage = { _, _ ->
val generatedFiles = writerPlugin.writer.contents

assertContains(generatedFiles.keys, "scripts/symbol-parameters-wrapper_deferred.js")

val scripts = generatedFiles.getValue("root/test/-test/-test.html").let { Jsoup.parse(it) }.select("script")
val deferredScriptSources = scripts.filter { element -> element.hasAttr("defer") }.map { it.attr("src") }

// important to check symbol-parameters-wrapper_deferred specifically since it might break some features
assertContains(deferredScriptSources, "../../../scripts/symbol-parameters-wrapper_deferred.js")
}
}
}

private fun <T> Collection<T>.assertCount(n: Int, prefix: String = "") =
assert(count() == n) { "${prefix}Expected $n, got ${count()}" }

Expand Down

0 comments on commit 14cd0f3

Please sign in to comment.