Просмотр исходного кода

Merge pull request #315 from sharelatex/bg-validate-filenames-client

validate filenames on the client
Brian Gough 8 лет назад
Родитель
Сommit
9224e164fe

+ 6 - 4
services/web/app/views/project/editor/file-tree.pug

@@ -119,6 +119,7 @@ script(type='text/ng-template', id='entityListItemTemplate')
 						ng-blur="finishRenaming()",
 						select-name-when="entity.renaming",
 						on-enter="finishRenaming()"
+						valid-file
 					)
 
 				span.dropdown.entity-menu-toggle(
@@ -207,6 +208,7 @@ script(type='text/ng-template', id='entityListItemTemplate')
 							ng-blur="finishRenaming()",
 							select-name-when="entity.renaming",
 							on-enter="finishRenaming()"
+							valid-file
 						)
 
 					span.dropdown.entity-menu-toggle(
@@ -316,10 +318,10 @@ script(type='text/ng-template', id='newDocModalTemplate')
 				ng-model="inputs.name",
 				on-enter="create()",
 				select-name-on="open",
-				ng-pattern="validFileRegex",
+				valid-file,
 				name="name"
 			)
-			.text-danger.row-spaced-small(ng-show="newDocForm.name.$error.pattern")
+			.text-danger.row-spaced-small(ng-show="newDocForm.name.$error.validFile")
 				| #{translate('files_cannot_include_invalid_characters')}
 	.modal-footer
 		button.btn.btn-default(
@@ -347,10 +349,10 @@ script(type='text/ng-template', id='newFolderModalTemplate')
 				ng-model="inputs.name",
 				on-enter="create()",
 				select-name-on="open",
-				ng-pattern="validFileRegex",
+				valid-file,
 				name="name"
 			)
-			.text-danger.row-spaced-small(ng-show="newFolderForm.name.$error.pattern")
+			.text-danger.row-spaced-small(ng-show="newFolderForm.name.$error.validFile")
 				| #{translate('files_cannot_include_invalid_characters')}
 	.modal-footer
 		button.btn.btn-default(

+ 1 - 0
services/web/public/coffee/ide.coffee

@@ -20,6 +20,7 @@ define [
 	"ide/hotkeys/index"
 	"ide/wordcount/index"
 	"ide/directives/layout"
+	"ide/directives/validFile"
 	"ide/services/ide"
 	"__IDE_CLIENTSIDE_INCLUDES__"
 	"analytics/AbTestingManager"

+ 47 - 0
services/web/public/coffee/ide/directives/SafePath.coffee

@@ -0,0 +1,47 @@
+# This file is shared between the frontend and server code of web, so that
+# filename validation is the same in both implementations.  
+# Both copies must be kept in sync:
+#   app/coffee/Features/Project/SafePath.coffee
+#   public/coffee/ide/directives/SafePath.coffee
+
+load = () ->
+	BADCHAR_RX = ///
+		[
+			\/ # no slashes
+			\* # no asterisk
+			\u0000-\u001F # no control characters (0-31)
+			\u007F        # no delete
+			\u0080-\u009F # no unicode control characters (C1)
+			\uD800-\uDFFF # no unicode surrogate characters
+		]
+		///g
+
+	BADFILE_RX = ///
+		(^\.$)      # reject . as a filename
+		|	(^\.\.$)  # reject .. as a filename
+		| (^\s+)    # reject leading space
+		| (\s+$)    # reject trailing space
+		///g
+
+	MAX_PATH = 1024 # Maximum path length, in characters. This is fairly arbitrary.
+
+	SafePath =
+		clean: (filename) ->
+			filename = filename.replace BADCHAR_RX, '_'
+			# for BADFILE_RX replace any matches with an equal number of underscores
+			filename = filename.replace BADFILE_RX, (match) -> 
+				return new Array(match.length + 1).join("_")
+			return filename
+
+		isCleanFilename: (filename) ->
+			return SafePath.isAllowedLength(filename) &&
+				not filename.match(BADCHAR_RX) &&
+				not filename.match(BADFILE_RX)
+
+		isAllowedLength: (pathname) ->
+			return pathname.length > 0 && pathname.length <= MAX_PATH
+
+if define?
+	define [], load
+else
+	module.exports = load()

+ 12 - 0
services/web/public/coffee/ide/directives/validFile.coffee

@@ -0,0 +1,12 @@
+define [
+	"base"
+	"ide/directives/SafePath"
+], (App, SafePath) ->
+
+	App.directive "validFile", () ->
+		return {
+			require: 'ngModel'
+			link: (scope, element, attrs, ngModelCtrl) ->
+				ngModelCtrl.$validators.validFile = (filename) ->
+					return SafePath.isCleanFilename filename
+		}

+ 0 - 4
services/web/public/coffee/ide/file-tree/controllers/FileTreeController.coffee

@@ -44,8 +44,6 @@ define [
 	App.controller "NewDocModalController", [
 		"$scope", "ide", "$modalInstance", "$timeout", "parent_folder",
 		($scope,   ide,   $modalInstance,   $timeout,   parent_folder) ->
-			$scope.validFileRegex = ide.validFileRegex
-			
 			$scope.inputs = 
 				name: "name.tex"
 			$scope.state =
@@ -78,8 +76,6 @@ define [
 	App.controller "NewFolderModalController", [
 		"$scope", "ide", "$modalInstance", "$timeout", "parent_folder",
 		($scope,   ide,   $modalInstance,   $timeout,   parent_folder) ->
-			$scope.validFileRegex = ide.validFileRegex
-
 			$scope.inputs = 
 				name: "name"
 			$scope.state =

+ 3 - 2
services/web/public/coffee/ide/file-tree/controllers/FileTreeEntityController.coffee

@@ -32,8 +32,9 @@ define [
 			return if !$scope.entity.renaming
 
 			name = $scope.inputs.name
-			
-			if !name.match(new RegExp(ide.validFileRegex))
+
+			# validator will set name to undefined for invalid filenames
+			if !name?
 				# Showing the modal blurs the rename box which calls us again
 				# so track this with the invalidModalShowing flag
 				return if invalidModalShowing