Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit82eee1f

Browse files
authored
impl: strict URL validation (#164)
This commit rejects any URL that is opaque, not hierarchical, not usinghttp or https protocol, or it misses the hostname. The rejection ishandled in the connection/auth screen and also in the URI protocolhandling logic<img width="486" height="746" alt="image"src="https://github.com/user-attachments/assets/489964c8-491c-4766-9891-42c63cfd353e"/><img width="486" height="746" alt="image"src="https://github.com/user-attachments/assets/dec6acae-4a5e-4a2a-8e59-69b74ba52a9e"/><img width="486" height="746" alt="image"src="https://github.com/user-attachments/assets/802558be-60dc-43e3-9512-ff9007aa50af"/>
1 parentc5f8e12 commit82eee1f

File tree

5 files changed

+147
-6
lines changed

5 files changed

+147
-6
lines changed

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
##Unreleased
44

5+
###Changed
6+
7+
- URL validation is stricter in the connection screen and URI protocol handler
8+
59
##0.6.0 - 2025-07-25
610

711
###Changed

‎src/main/kotlin/com/coder/toolbox/util/CoderProtocolHandler.kt‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.coder.toolbox.sdk.CoderRestClient
99
importcom.coder.toolbox.sdk.v2.models.Workspace
1010
importcom.coder.toolbox.sdk.v2.models.WorkspaceAgent
1111
importcom.coder.toolbox.sdk.v2.models.WorkspaceStatus
12+
importcom.coder.toolbox.util.WebUrlValidationResult.Invalid
1213
importcom.jetbrains.toolbox.api.remoteDev.connection.RemoteToolsHelper
1314
importkotlinx.coroutines.Job
1415
importkotlinx.coroutines.TimeoutCancellationException
@@ -107,6 +108,11 @@ open class CoderProtocolHandler(
107108
context.logAndShowError(CAN_T_HANDLE_URI_TITLE,"Query parameter\"$URL\" is missing from URI")
108109
returnnull
109110
}
111+
val validationResult= deploymentURL.validateStrictWebUrl()
112+
if (validationResultisInvalid) {
113+
context.logAndShowError(CAN_T_HANDLE_URI_TITLE,"\"$URL\" is invalid:${validationResult.reason}")
114+
returnnull
115+
}
110116
return deploymentURL
111117
}
112118

‎src/main/kotlin/com/coder/toolbox/util/URLExtensions.kt‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,44 @@
11
packagecom.coder.toolbox.util
22

3+
importcom.coder.toolbox.util.WebUrlValidationResult.Invalid
4+
importcom.coder.toolbox.util.WebUrlValidationResult.Valid
35
importjava.net.IDN
46
importjava.net.URI
57
importjava.net.URL
68

79
fun String.toURL():URL=URI.create(this).toURL()
810

11+
fun String.validateStrictWebUrl():WebUrlValidationResult=try {
12+
val uri=URI(this)
13+
14+
when {
15+
uri.isOpaque->Invalid(
16+
"The URL\"$this\" is invalid because it is not in the standard format."+
17+
"Please enter a full web address like\"https://example.com\""
18+
)
19+
20+
!uri.isAbsolute->Invalid(
21+
"The URL\"$this\" is missing a scheme (like https://)."+
22+
"Please enter a full web address like\"https://example.com\""
23+
)
24+
uri.scheme?.lowercase()!insetOf("http","https")->
25+
Invalid(
26+
"The URL\"$this\" must start with http:// or https://, not\"${uri.scheme}\""
27+
)
28+
uri.authority.isNullOrBlank()->
29+
Invalid(
30+
"The URL\"$this\" does not include a valid website name."+
31+
"Please enter a full web address like\"https://example.com\""
32+
)
33+
else->Valid
34+
}
35+
}catch (_:Exception) {
36+
Invalid(
37+
"The input\"$this\" is not a valid web address."+
38+
"Please enter a full web address like\"https://example.com\""
39+
)
40+
}
41+
942
fun URL.withPath(path:String):URL=URL(
1043
this.protocol,
1144
this.host,
@@ -30,3 +63,8 @@ fun URI.toQueryParameters(): Map<String, String> = (this.query ?: "")
3063
parts[0] to""
3164
}
3265
}
66+
67+
sealedclassWebUrlValidationResult {
68+
object Valid : WebUrlValidationResult()
69+
data classInvalid(valreason:String) : WebUrlValidationResult()
70+
}

‎src/main/kotlin/com/coder/toolbox/views/DeploymentUrlStep.kt‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package com.coder.toolbox.views
22

33
importcom.coder.toolbox.CoderToolboxContext
44
importcom.coder.toolbox.settings.SignatureFallbackStrategy
5+
importcom.coder.toolbox.util.WebUrlValidationResult.Invalid
56
importcom.coder.toolbox.util.toURL
7+
importcom.coder.toolbox.util.validateStrictWebUrl
68
importcom.coder.toolbox.views.state.CoderCliSetupContext
79
importcom.coder.toolbox.views.state.CoderCliSetupWizardState
810
importcom.jetbrains.toolbox.api.ui.components.CheckboxField
@@ -69,16 +71,11 @@ class DeploymentUrlStep(
6971

7072
overridefunonNext():Boolean {
7173
context.settingsStore.updateSignatureFallbackStrategy(signatureFallbackStrategyField.checkedState.value)
72-
var url= urlField.textState.value
74+
val url= urlField.textState.value
7375
if (url.isBlank()) {
7476
errorField.textState.update { context.i18n.ptrl("URL is required") }
7577
returnfalse
7678
}
77-
url=if (!url.startsWith("http://")&&!url.startsWith("https://")) {
78-
"https://$url"
79-
}else {
80-
url
81-
}
8279
try {
8380
CoderCliSetupContext.url= validateRawUrl(url)
8481
}catch (e:MalformedURLException) {
@@ -98,6 +95,10 @@ class DeploymentUrlStep(
9895
*/
9996
privatefunvalidateRawUrl(url:String):URL {
10097
try {
98+
val result= url.validateStrictWebUrl()
99+
if (resultisInvalid) {
100+
throwMalformedURLException(result.reason)
101+
}
101102
return url.toURL()
102103
}catch (e:Exception) {
103104
throwMalformedURLException(e.message)

‎src/test/kotlin/com/coder/toolbox/util/URLExtensionsTest.kt‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,96 @@ internal class URLExtensionsTest {
6060
)
6161
}
6262
}
63+
64+
@Test
65+
fun`valid http URL should return Valid`() {
66+
val result="http://coder.com".validateStrictWebUrl()
67+
assertEquals(WebUrlValidationResult.Valid, result)
68+
}
69+
70+
@Test
71+
fun`valid https URL with path and query should return Valid`() {
72+
val result="https://coder.com/bin/coder-linux-amd64?query=1".validateStrictWebUrl()
73+
assertEquals(WebUrlValidationResult.Valid, result)
74+
}
75+
76+
@Test
77+
fun`relative URL should return Invalid with appropriate message`() {
78+
val url="/bin/coder-linux-amd64"
79+
val result= url.validateStrictWebUrl()
80+
assertEquals(
81+
WebUrlValidationResult.Invalid("The URL\"/bin/coder-linux-amd64\" is missing a scheme (like https://). Please enter a full web address like\"https://example.com\""),
82+
result
83+
)
84+
}
85+
86+
@Test
87+
fun`opaque URI like mailto should return Invalid`() {
88+
val url="mailto:user@coder.com"
89+
val result= url.validateStrictWebUrl()
90+
assertEquals(
91+
WebUrlValidationResult.Invalid("The URL\"mailto:user@coder.com\" is invalid because it is not in the standard format. Please enter a full web address like\"https://example.com\""),
92+
result
93+
)
94+
}
95+
96+
@Test
97+
fun`unsupported scheme like ftp should return Invalid`() {
98+
val url="ftp://coder.com"
99+
val result= url.validateStrictWebUrl()
100+
assertEquals(
101+
WebUrlValidationResult.Invalid("The URL\"ftp://coder.com\" must start with http:// or https://, not\"ftp\""),
102+
result
103+
)
104+
}
105+
106+
@Test
107+
fun`http URL with missing authority should return Invalid`() {
108+
val url="http:///bin/coder-linux-amd64"
109+
val result= url.validateStrictWebUrl()
110+
assertEquals(
111+
WebUrlValidationResult.Invalid("The URL\"http:///bin/coder-linux-amd64\" does not include a valid website name. Please enter a full web address like\"https://example.com\""),
112+
result
113+
)
114+
}
115+
116+
@Test
117+
fun`malformed URI should return Invalid with parsing error message`() {
118+
val url="http://[invalid-uri]"
119+
val result= url.validateStrictWebUrl()
120+
assertEquals(
121+
WebUrlValidationResult.Invalid("The input\"http://[invalid-uri]\" is not a valid web address. Please enter a full web address like\"https://example.com\""),
122+
result
123+
)
124+
}
125+
126+
@Test
127+
fun`URI without colon should return Invalid as URI is not absolute`() {
128+
val url="http//coder.com"
129+
val result= url.validateStrictWebUrl()
130+
assertEquals(
131+
WebUrlValidationResult.Invalid("The URL\"http//coder.com\" is missing a scheme (like https://). Please enter a full web address like\"https://example.com\""),
132+
result
133+
)
134+
}
135+
136+
@Test
137+
fun`URI without double forward slashes should return Invalid because the URI is not hierarchical`() {
138+
val url="http:coder.com"
139+
val result= url.validateStrictWebUrl()
140+
assertEquals(
141+
WebUrlValidationResult.Invalid("The URL\"http:coder.com\" is invalid because it is not in the standard format. Please enter a full web address like\"https://example.com\""),
142+
result
143+
)
144+
}
145+
146+
@Test
147+
fun`URI without a single forward slash should return Invalid because the URI does not have a hostname`() {
148+
val url="https:/coder.com"
149+
val result= url.validateStrictWebUrl()
150+
assertEquals(
151+
WebUrlValidationResult.Invalid("The URL\"https:/coder.com\" does not include a valid website name. Please enter a full web address like\"https://example.com\""),
152+
result
153+
)
154+
}
63155
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp