Skip to content

Commit 3a474ab

Browse files
anatoliykmetyukeed3si9n
authored andcommitted
Allowlist-based approach to VCS string sanitization
1 parent 1ce945b commit 3a474ab

File tree

2 files changed

+32
-6
lines changed

2 files changed

+32
-6
lines changed

main/src/main/scala/sbt/internal/VcsUriFragment.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@ private[sbt] object VcsUriFragment {
1414
def validate(fragment: String): Unit = {
1515
if (fragment == null)
1616
throw new IllegalArgumentException("VCS URI fragment must not be null")
17+
if (fragment.isEmpty)
18+
throw new IllegalArgumentException("VCS URI fragment must not be empty")
1719
fragment.foreach { c =>
18-
if (c == '&' || c == '|' || c == ';')
20+
if (!isAllowed(c))
1921
throw new IllegalArgumentException(
20-
"Invalid character in VCS URI fragment (shell metacharacters are not allowed)"
21-
)
22-
if (Character.isISOControl(c))
23-
throw new IllegalArgumentException(
24-
"Invalid character in VCS URI fragment (control characters are not allowed)"
22+
"Invalid character in VCS URI fragment (only ASCII letters, digits, and - _ . / + are allowed)"
2523
)
2624
}
2725
}
26+
27+
private def isAllowed(c: Char): Boolean =
28+
(c >= 'a' && c <= 'z') ||
29+
(c >= 'A' && c <= 'Z') ||
30+
(c >= '0' && c <= '9') ||
31+
c == '-' || c == '_' || c == '.' || c == '/' || c == '+'
2832
}

main/src/test/scala/sbt/internal/VcsUriFragmentTest.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ import hedgehog.runner.*
1515
object VcsUriFragmentTest extends Properties:
1616
override def tests: List[Test] = List(
1717
example("accepts typical branch and tag names", testAcceptsSafe),
18+
example("accepts hex commit id fragment", testAcceptsHexSha),
19+
example("rejects empty fragment", testRejectsEmpty),
1820
example("rejects ampersand", testRejectsAmpersand),
1921
example("rejects pipe", testRejectsPipe),
2022
example("rejects semicolon", testRejectsSemicolon),
23+
example("rejects space", testRejectsSpace),
24+
example("rejects percent", testRejectsPercent),
25+
example("rejects greater-than", testRejectsGreaterThan),
2126
example("rejects newline", testRejectsNewline),
2227
example("rejects DEL", testRejectsDel),
2328
)
@@ -26,8 +31,16 @@ object VcsUriFragmentTest extends Properties:
2631
VcsUriFragment.validate("develop")
2732
VcsUriFragment.validate("v1.2.3")
2833
VcsUriFragment.validate("feature/foo-bar")
34+
VcsUriFragment.validate("release/1.0.0+build")
2935
Result.success
3036

37+
def testAcceptsHexSha: Result =
38+
VcsUriFragment.validate("abc123def4567890abcdef1234567890abcdef12")
39+
Result.success
40+
41+
def testRejectsEmpty: Result =
42+
interceptIllegal("")
43+
3144
def testRejectsAmpersand: Result =
3245
interceptIllegal("a&b")
3346

@@ -37,6 +50,15 @@ object VcsUriFragmentTest extends Properties:
3750
def testRejectsSemicolon: Result =
3851
interceptIllegal("a;b")
3952

53+
def testRejectsSpace: Result =
54+
interceptIllegal("a b")
55+
56+
def testRejectsPercent: Result =
57+
interceptIllegal("a%20b")
58+
59+
def testRejectsGreaterThan: Result =
60+
interceptIllegal("a>b")
61+
4062
def testRejectsNewline: Result =
4163
interceptIllegal("a\nb")
4264

0 commit comments

Comments
 (0)