diff --git a/src/main/resources/update/gitbucket-core_4.36.xml b/src/main/resources/update/gitbucket-core_4.36.xml
new file mode 100644
index 0000000..8e7c9bf
--- /dev/null
+++ b/src/main/resources/update/gitbucket-core_4.36.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/main/scala/gitbucket/core/GitBucketCoreModule.scala b/src/main/scala/gitbucket/core/GitBucketCoreModule.scala
index 3d63b0c..a1adfd6 100644
--- a/src/main/scala/gitbucket/core/GitBucketCoreModule.scala
+++ b/src/main/scala/gitbucket/core/GitBucketCoreModule.scala
@@ -118,4 +118,5 @@
new Version("4.35.1"),
new Version("4.35.2"),
new Version("4.35.3"),
+ new Version("4.36.0", new LiquibaseMigration("update/gitbucket-core_4.36.xml"))
)
diff --git a/src/main/scala/gitbucket/core/controller/ControllerBase.scala b/src/main/scala/gitbucket/core/controller/ControllerBase.scala
index 9b3f0e4..7a650f4 100644
--- a/src/main/scala/gitbucket/core/controller/ControllerBase.scala
+++ b/src/main/scala/gitbucket/core/controller/ControllerBase.scala
@@ -253,7 +253,7 @@
repository: RepositoryService.RepositoryInfo
): Unit = {
JGitUtil.getObjectLoaderFromId(git, objectId) { loader =>
- contentType = FileUtil.getSafeMimeType(path)
+ contentType = FileUtil.getSafeMimeType(path, repository.repository.options.safeMode)
if (loader.isLarge) {
response.setContentLength(loader.getSize.toInt)
diff --git a/src/main/scala/gitbucket/core/controller/FileUploadController.scala b/src/main/scala/gitbucket/core/controller/FileUploadController.scala
index 36a4775..e66b1ee 100644
--- a/src/main/scala/gitbucket/core/controller/FileUploadController.scala
+++ b/src/main/scala/gitbucket/core/controller/FileUploadController.scala
@@ -40,7 +40,7 @@
.writeByteArrayToFile(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(fileId)), file.get())
session += Keys.Session.Upload(fileId) -> file.name
},
- FileUtil.isImage
+ FileUtil.isImage(_)
)
}
@@ -191,9 +191,9 @@
}
}
- private def execute(f: (FileItem, String) => Unit, mimeTypeChcker: (String) => Boolean) =
+ private def execute(f: (FileItem, String) => Unit, mimeTypeChecker: (String) => Boolean) =
fileParams.get("file") match {
- case Some(file) if mimeTypeChcker(file.name) =>
+ case Some(file) if mimeTypeChecker(file.name) =>
val fileId = FileUtil.generateFileId
f(file, fileId)
contentType = "text/plain"
diff --git a/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala b/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala
index 7a4d08d..2d57129 100644
--- a/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala
+++ b/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala
@@ -57,7 +57,8 @@
externalWikiUrl: Option[String],
allowFork: Boolean,
mergeOptions: Seq[String],
- defaultMergeOption: String
+ defaultMergeOption: String,
+ safeMode: Boolean
)
val optionsForm = mapping(
@@ -69,7 +70,8 @@
"externalWikiUrl" -> trim(label("External Wiki URL", optional(text(maxlength(200))))),
"allowFork" -> trim(label("Allow Forking", boolean())),
"mergeOptions" -> mergeOptions,
- "defaultMergeOption" -> trim(label("Default merge strategy", text(required)))
+ "defaultMergeOption" -> trim(label("Default merge strategy", text(required))),
+ "safeMode" -> trim(label("XSS protection", boolean()))
)(OptionsForm.apply).verifying { form =>
if (!form.mergeOptions.contains(form.defaultMergeOption)) {
Seq("defaultMergeOption" -> s"This merge strategy isn't enabled.")
@@ -150,7 +152,8 @@
form.externalWikiUrl,
form.allowFork,
form.mergeOptions,
- form.defaultMergeOption
+ form.defaultMergeOption,
+ form.safeMode
)
flash.update("info", "Repository settings has been updated.")
redirect(s"/${repository.owner}/${repository.name}/settings/options")
diff --git a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala
index 7660665..e57fdaf 100644
--- a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala
+++ b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala
@@ -423,7 +423,7 @@
repository = repository,
pathList = paths.take(paths.size - 1).toList,
fileName = Some(paths.last),
- content = JGitUtil.getContentInfo(git, path, objectId),
+ content = JGitUtil.getContentInfo(git, path, objectId, repository.repository.options.safeMode),
protectedBranch = protectedBranch,
commit = revCommit.getName,
newLineMode = info.newLineMode,
@@ -448,7 +448,7 @@
repository = repository,
pathList = paths.take(paths.size - 1).toList,
fileName = paths.last,
- content = JGitUtil.getContentInfo(git, path, objectId),
+ content = JGitUtil.getContentInfo(git, path, objectId, repository.repository.options.safeMode),
commit = revCommit.getName
)
} getOrElse NotFound()
@@ -693,7 +693,7 @@
branch = id,
repository = repository,
pathList = path.split("/").toList,
- content = JGitUtil.getContentInfo(git, path, objectId),
+ content = JGitUtil.getContentInfo(git, path, objectId, repository.repository.options.safeMode),
latestCommit = new JGitUtil.CommitInfo(JGitUtil.getLastModifiedCommit(git, revCommit, path)),
hasWritePermission = hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
isBlame = request.paths(2) == "blame",
diff --git a/src/main/scala/gitbucket/core/model/Repository.scala b/src/main/scala/gitbucket/core/model/Repository.scala
index b536c82..358a889 100644
--- a/src/main/scala/gitbucket/core/model/Repository.scala
+++ b/src/main/scala/gitbucket/core/model/Repository.scala
@@ -24,6 +24,7 @@
val allowFork = column[Boolean]("ALLOW_FORK")
val mergeOptions = column[String]("MERGE_OPTIONS")
val defaultMergeOption = column[String]("DEFAULT_MERGE_OPTION")
+ val safeMode = column[Boolean]("SAFE_MODE")
def * =
(
@@ -41,7 +42,16 @@
parentUserName.?,
parentRepositoryName.?
),
- (issuesOption, externalIssuesUrl.?, wikiOption, externalWikiUrl.?, allowFork, mergeOptions, defaultMergeOption)
+ (
+ issuesOption,
+ externalIssuesUrl.?,
+ wikiOption,
+ externalWikiUrl.?,
+ allowFork,
+ mergeOptions,
+ defaultMergeOption,
+ safeMode
+ )
).shaped.<>(
{
case (repository, options) =>
@@ -112,5 +122,6 @@
externalWikiUrl: Option[String],
allowFork: Boolean,
mergeOptions: String,
- defaultMergeOption: String
+ defaultMergeOption: String,
+ safeMode: Boolean
)
diff --git a/src/main/scala/gitbucket/core/service/RepositoryService.scala b/src/main/scala/gitbucket/core/service/RepositoryService.scala
index 7b11310..da7ef91 100644
--- a/src/main/scala/gitbucket/core/service/RepositoryService.scala
+++ b/src/main/scala/gitbucket/core/service/RepositoryService.scala
@@ -60,7 +60,8 @@
externalWikiUrl = None,
allowFork = true,
mergeOptions = "merge-commit,squash,rebase",
- defaultMergeOption = "merge-commit"
+ defaultMergeOption = "merge-commit",
+ safeMode = true
)
)
@@ -460,7 +461,7 @@
.filter { case (t1, t2) => t2.removed === false.bind }
.map { case (t1, t2) => t1 }
// for Normal Users
- case Some(x) if (!x.isAdmin || limit) =>
+ case Some(x) =>
Repositories
.join(Accounts)
.on(_.userName === _.userName)
@@ -550,7 +551,8 @@
externalWikiUrl: Option[String],
allowFork: Boolean,
mergeOptions: Seq[String],
- defaultMergeOption: String
+ defaultMergeOption: String,
+ safeMode: Boolean
)(implicit s: Session): Unit = {
Repositories
@@ -566,6 +568,7 @@
r.allowFork,
r.mergeOptions,
r.defaultMergeOption,
+ r.safeMode,
r.updatedDate
)
}
@@ -579,6 +582,7 @@
allowFork,
mergeOptions.mkString(","),
defaultMergeOption,
+ safeMode,
currentDate
)
}
diff --git a/src/main/scala/gitbucket/core/util/FileUtil.scala b/src/main/scala/gitbucket/core/util/FileUtil.scala
index 8b4780a..04be98d 100644
--- a/src/main/scala/gitbucket/core/util/FileUtil.scala
+++ b/src/main/scala/gitbucket/core/util/FileUtil.scala
@@ -24,13 +24,18 @@
}
}
- def getSafeMimeType(name: String): String = {
- getMimeType(name)
+ def getSafeMimeType(name: String, safeMode: Boolean = true): String = {
+ val mimeType = getMimeType(name)
.replace("text/html", "text/plain")
- .replace("image/svg+xml", "text/plain; charset=UTF-8")
+
+ if (safeMode) {
+ mimeType.replace("image/svg+xml", "text/plain; charset=UTF-8")
+ } else {
+ mimeType
+ }
}
- def isImage(name: String): Boolean = getSafeMimeType(name).startsWith("image/")
+ def isImage(name: String, safeMode: Boolean = true): Boolean = getSafeMimeType(name, safeMode).startsWith("image/")
def isLarge(size: Long): Boolean = (size > 1024 * 1000)
diff --git a/src/main/scala/gitbucket/core/util/JGitUtil.scala b/src/main/scala/gitbucket/core/util/JGitUtil.scala
index 74864de..05fe9fb 100644
--- a/src/main/scala/gitbucket/core/util/JGitUtil.scala
+++ b/src/main/scala/gitbucket/core/util/JGitUtil.scala
@@ -1047,13 +1047,13 @@
!loader.isLarge && new String(loader.getBytes(), "UTF-8").startsWith("version https://git-lfs.github.com/spec/v1")
}
- def getContentInfo(git: Git, path: String, objectId: ObjectId): ContentInfo = {
+ def getContentInfo(git: Git, path: String, objectId: ObjectId, safeMode: Boolean): ContentInfo = {
// Viewer
Using.resource(git.getRepository.getObjectDatabase) { db =>
val loader = db.open(objectId)
val isLfs = isLfsPointer(loader)
val large = FileUtil.isLarge(loader.getSize)
- val viewer = if (FileUtil.isImage(path)) "image" else if (large) "large" else "other"
+ val viewer = if (FileUtil.isImage(path, safeMode)) "image" else if (large) "large" else "other"
val bytes = if (viewer == "other") JGitUtil.getContentFromId(git, objectId, false) else None
val size = Some(getContentSize(loader))
diff --git a/src/main/twirl/gitbucket/core/settings/options.scala.html b/src/main/twirl/gitbucket/core/settings/options.scala.html
index 504b615..e967ef3 100644
--- a/src/main/twirl/gitbucket/core/settings/options.scala.html
+++ b/src/main/twirl/gitbucket/core/settings/options.scala.html
@@ -33,8 +33,6 @@
You choose who can see and commit to this repository.
-
-
diff --git a/src/test/scala/gitbucket/core/api/ApiSpecModels.scala b/src/test/scala/gitbucket/core/api/ApiSpecModels.scala
index 237d8d0..781f34b 100644
--- a/src/test/scala/gitbucket/core/api/ApiSpecModels.scala
+++ b/src/test/scala/gitbucket/core/api/ApiSpecModels.scala
@@ -2,7 +2,6 @@
import java.util.{Calendar, Date, TimeZone}
-import gitbucket.core.api.ApiBranchProtection.EnforcementLevel
import gitbucket.core.model._
import gitbucket.core.plugin.PluginInfo
import gitbucket.core.service.ProtectedBranchService.ProtectedBranchInfo
@@ -69,7 +68,8 @@
externalWikiUrl = Some("https://external.com/gitbucket"),
allowFork = true,
mergeOptions = "merge-commit,squash,rebase",
- defaultMergeOption = "merge-commit"
+ defaultMergeOption = "merge-commit",
+ safeMode = true
)
)
diff --git a/src/test/scala/gitbucket/core/service/MergeServiceSpec.scala b/src/test/scala/gitbucket/core/service/MergeServiceSpec.scala
index 027f595..a6e2c6b 100644
--- a/src/test/scala/gitbucket/core/service/MergeServiceSpec.scala
+++ b/src/test/scala/gitbucket/core/service/MergeServiceSpec.scala
@@ -239,7 +239,8 @@
externalWikiUrl = None,
allowFork = true,
mergeOptions = "merge-commit,squash,rebase",
- defaultMergeOption = "merge-commit"
+ defaultMergeOption = "merge-commit",
+ safeMode = true
)
),
issueCount = 0,
diff --git a/src/test/scala/gitbucket/core/util/FileUtilSpec.scala b/src/test/scala/gitbucket/core/util/FileUtilSpec.scala
new file mode 100644
index 0000000..2eb65e5
--- /dev/null
+++ b/src/test/scala/gitbucket/core/util/FileUtilSpec.scala
@@ -0,0 +1,15 @@
+package gitbucket.core.util
+
+import org.scalatest.funsuite.AnyFunSuite
+
+class FileUtilSpec extends AnyFunSuite {
+
+ test("getSafeMimeType") {
+ val contentType1 = FileUtil.getSafeMimeType("test.svg", true)
+ assert(contentType1 == "text/plain; charset=UTF-8")
+
+ val contentType2 = FileUtil.getSafeMimeType("test.svg", false)
+ assert(contentType2 == "image/svg+xml")
+ }
+
+}
diff --git a/src/test/scala/gitbucket/core/util/GitSpecUtil.scala b/src/test/scala/gitbucket/core/util/GitSpecUtil.scala
index d556817..11ebdd5 100644
--- a/src/test/scala/gitbucket/core/util/GitSpecUtil.scala
+++ b/src/test/scala/gitbucket/core/util/GitSpecUtil.scala
@@ -93,7 +93,7 @@
}
_getPathObjectId
}
- JGitUtil.getContentInfo(git, path, objectId)
+ JGitUtil.getContentInfo(git, path, objectId, true)
}
def mergeAndCommit(git: Git, into: String, branch: String, message: String = null): Unit = {