Skip to content

Commit e172ae4

Browse files
committed
GHSA-82j3-hf72-7x93 Make Location a bit safer to use out-of-the-box by immediately sanitazing invalid symbols
1 parent 279a472 commit e172ae4

File tree

6 files changed

+42
-26
lines changed

6 files changed

+42
-26
lines changed

Diff for: gradle/wrapper/gradle-wrapper.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

Diff for: reposilite-backend/src/main/kotlin/com/reposilite/frontend/infrastructure/FrontendHandler.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,18 @@ import com.reposilite.storage.getSimpleName
2727
import com.reposilite.storage.inputStream
2828
import com.reposilite.web.api.ReposiliteRoute
2929
import com.reposilite.web.api.ReposiliteRoutes
30-
import io.javalin.community.routing.Route
3130
import io.javalin.community.routing.Route.GET
3231
import io.javalin.http.ContentType
3332
import io.javalin.http.Context
3433
import io.javalin.http.HttpStatus.INTERNAL_SERVER_ERROR
35-
import panda.std.Result
36-
import panda.std.asSuccess
3734
import java.io.InputStream
3835
import java.nio.charset.StandardCharsets.UTF_8
3936
import java.nio.file.Files
4037
import java.nio.file.Path
4138
import kotlin.io.path.isDirectory
4239
import kotlin.streams.asSequence
40+
import panda.std.Result
41+
import panda.std.asSuccess
4342

4443
internal sealed class FrontendHandler(private val frontendFacade: FrontendFacade) : ReposiliteRoutes() {
4544

Diff for: reposilite-backend/src/main/kotlin/com/reposilite/storage/api/Location.kt

+30-15
Original file line numberDiff line numberDiff line change
@@ -17,49 +17,54 @@
1717
package com.reposilite.storage.api
1818

1919
import com.reposilite.storage.getExtension
20+
import java.nio.file.Path
21+
import java.nio.file.Paths
2022
import panda.std.Result
2123
import panda.std.asSuccess
2224
import panda.std.letIf
23-
import java.nio.file.Path
24-
import java.nio.file.Paths
2525

2626
/**
2727
* [Path] alternative, represents location of resource in [com.reposilite.storage.StorageProvider]
2828
*/
29-
@Suppress("DataClassPrivateConstructor")
30-
data class Location private constructor(private val uri: String) {
29+
class Location private constructor(private val uri: String) {
3130

3231
companion object {
3332

33+
private val empty = Location("")
3434
private val multipleSlashes = Regex("/+")
35+
private val multipleDirectoryOperators = Regex("\\.{2,}")
3536

3637
@JvmStatic
37-
fun of(uri: String): Location =
38-
uri.replace("\\", "/")
38+
fun of(uri: String): Location {
39+
return uri
40+
.replaceBefore(":", "")
41+
.replace(":", "")
42+
.replace(multipleDirectoryOperators, ".")
43+
.replace("\\", "/")
3944
.replace(multipleSlashes, "/")
4045
.letIf({ it.startsWith("/") }) { it.removePrefix("/") }
4146
.letIf({ it.endsWith("/") }) { it.removeSuffix("/") }
4247
.let { Location(it) }
48+
}
4349

4450
@JvmStatic
4551
fun of(path: Path): Location =
46-
path.toString().toLocation()
52+
of(path.normalize().toString())
4753

4854
@JvmStatic
4955
fun of(root: Path, path: Path): Location =
50-
of(root.relativize(path))
56+
of(root.relativize(path.normalize()))
5157

5258
@JvmStatic
5359
fun empty(): Location =
54-
"".toLocation()
60+
empty
5561

5662
}
5763

5864
fun toPath(): Result<Path, String> {
59-
if (uri.contains("..") || uri.contains(":") || uri.contains("\\")) {
65+
if (uri.contains(":") || uri.contains("\\") || uri.contains(multipleDirectoryOperators)) {
6066
return Result.error("Illegal path operator in URI")
6167
}
62-
6368
return Paths.get(uri).normalize().asSuccess()
6469
}
6570

@@ -99,13 +104,23 @@ data class Location private constructor(private val uri: String) {
99104
fun getSimpleName(): String =
100105
uri.substringAfterLast("/")
101106

107+
override fun equals(other: Any?): Boolean =
108+
when {
109+
this === other -> true
110+
javaClass != other?.javaClass -> false
111+
else -> uri == (other as Location).uri
112+
}
113+
114+
override fun hashCode(): Int =
115+
uri.hashCode()
116+
102117
override fun toString(): String =
103118
uri
104119

105120
}
106121

107122
fun String?.toLocation(): Location =
108-
if (this != null)
109-
Location.of(this)
110-
else
111-
Location.empty()
123+
when {
124+
this != null -> Location.of(this)
125+
else -> Location.empty()
126+
}

Diff for: reposilite-backend/src/main/kotlin/com/reposilite/token/infrastructure/RouteCommands.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ internal class RouteAdd(private val accessTokenFacade: AccessTokenFacade) : Repo
5555
val mappedPermissions = mapPermissions() ?: let {
5656
context.status = FAILED
5757
context.append("Unknown permission shortcuts (${permissions.toCharArray().joinToString()})")
58-
context.append("Available options (${RoutePermission.values().joinToString { perm -> perm.shortcut }})")
58+
context.append("Available options (${RoutePermission.entries.joinToString { perm -> perm.shortcut }})")
5959
return
6060
}
6161

@@ -89,7 +89,7 @@ internal class RouteRemove(private val accessTokenFacade: AccessTokenFacade) : R
8989
override fun execute(context: CommandContext) {
9090
accessTokenFacade.getAccessToken(name)
9191
?.also { token ->
92-
RoutePermission.values().forEach { accessTokenFacade.deleteRoute(token.identifier, Route(path, it)) }
92+
RoutePermission.entries.forEach { accessTokenFacade.deleteRoute(token.identifier, Route(path, it)) }
9393
context.append("Routes of token $name has been updated")
9494
}
9595
?: run {

Diff for: reposilite-backend/src/test/kotlin/com/reposilite/SharedSpecification.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ internal fun assertCollectionsEquals(actual: Collection<Any?>, expected: Collect
2323
return
2424
}
2525

26-
assertThat(actual.sortedBy { it.toString() }).isEqualTo(expected.sortedBy { it.toString() }) // pretty printing
26+
assertThat(actual.sortedBy { it.toString() }).containsExactlyElementsOf(expected.sortedBy { it.toString() }) // pretty printing
2727
}

Diff for: reposilite-backend/src/test/kotlin/com/reposilite/storage/LocationTest.kt

+6-4
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
package com.reposilite.storage
1818

1919
import com.reposilite.storage.api.Location
20+
import java.io.File
21+
import java.nio.file.Path
2022
import org.assertj.core.api.Assertions.assertThat
2123
import org.junit.jupiter.api.Test
22-
import panda.std.ResultAssertions.assertError
2324

2425
class LocationTest {
2526

@@ -33,9 +34,10 @@ class LocationTest {
3334
}
3435

3536
@Test
36-
fun `should drop corrupted paths`() {
37-
assertError(Location.of("../artifact").toPath())
38-
assertError(Location.of("C:/artifact").toPath())
37+
fun `should normalize corrupted paths`() {
38+
assertThat(Location.of(Path.of("../../artifact")).toPath().get().toString()).isEqualTo("artifact")
39+
assertThat(Location.of(Path.of("C:/artifact")).toPath().get().toString()).isEqualTo("artifact")
40+
assertThat(Location.of("artifact").resolve("../../root").toPath().get().toString()).isEqualTo("artifact" + File.separator + "root")
3941
}
4042

4143
}

0 commit comments

Comments
 (0)