From ddf1d6e65e3027e3472752e077190a5f693c4770 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Mon, 5 Feb 2018 16:56:52 +0000 Subject: [PATCH 1/4] add valid-file directive for front-end --- services/web/app/views/project/editor/file-tree.pug | 10 ++++++---- services/web/public/coffee/ide.coffee | 1 + .../public/coffee/ide/directives/validFile.coffee | 12 ++++++++++++ .../file-tree/controllers/FileTreeController.coffee | 4 ---- .../controllers/FileTreeEntityController.coffee | 5 +++-- 5 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 services/web/public/coffee/ide/directives/validFile.coffee diff --git a/services/web/app/views/project/editor/file-tree.pug b/services/web/app/views/project/editor/file-tree.pug index fade8e0782..0ac316e922 100644 --- a/services/web/app/views/project/editor/file-tree.pug +++ b/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( diff --git a/services/web/public/coffee/ide.coffee b/services/web/public/coffee/ide.coffee index 6f49b4bce8..078202655f 100644 --- a/services/web/public/coffee/ide.coffee +++ b/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" diff --git a/services/web/public/coffee/ide/directives/validFile.coffee b/services/web/public/coffee/ide/directives/validFile.coffee new file mode 100644 index 0000000000..d336b85dc9 --- /dev/null +++ b/services/web/public/coffee/ide/directives/validFile.coffee @@ -0,0 +1,12 @@ +define [ + "base" +], (App) -> + App.directive "validFile", () -> + return { + require: 'ngModel' + link: (scope, element, attrs, ngModelCtrl) -> + ngModelCtrl.$validators.validFile = (modelValue) -> + validFileRegex = /^[^\*\/]*$/ # Don't allow * and / + isValid = modelValue.match validFileRegex + return isValid + } diff --git a/services/web/public/coffee/ide/file-tree/controllers/FileTreeController.coffee b/services/web/public/coffee/ide/file-tree/controllers/FileTreeController.coffee index aee631f334..ba9eb8a81e 100644 --- a/services/web/public/coffee/ide/file-tree/controllers/FileTreeController.coffee +++ b/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 = diff --git a/services/web/public/coffee/ide/file-tree/controllers/FileTreeEntityController.coffee b/services/web/public/coffee/ide/file-tree/controllers/FileTreeEntityController.coffee index 55bf16a172..735d065cd8 100644 --- a/services/web/public/coffee/ide/file-tree/controllers/FileTreeEntityController.coffee +++ b/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 From 76281a3d79ff3cf2dabbd3a9405d3d86935503e5 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Tue, 6 Feb 2018 11:09:02 +0000 Subject: [PATCH 2/4] use extended file validation rules in frontend --- .../coffee/ide/directives/validFile.coffee | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/services/web/public/coffee/ide/directives/validFile.coffee b/services/web/public/coffee/ide/directives/validFile.coffee index d336b85dc9..54e0ad45f8 100644 --- a/services/web/public/coffee/ide/directives/validFile.coffee +++ b/services/web/public/coffee/ide/directives/validFile.coffee @@ -1,12 +1,36 @@ define [ "base" ], (App) -> + + # copied from app/coffee/Features/Project/SafePath.coffee + + 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. + App.directive "validFile", () -> return { require: 'ngModel' link: (scope, element, attrs, ngModelCtrl) -> - ngModelCtrl.$validators.validFile = (modelValue) -> - validFileRegex = /^[^\*\/]*$/ # Don't allow * and / - isValid = modelValue.match validFileRegex + ngModelCtrl.$validators.validFile = (filename) -> + isValid = filename.length > 0 && filename.length < MAX_PATH && + not filename.match(BADCHAR_RX) && + not filename.match(BADFILE_RX) return isValid } From c14380d56381daac7817a6e6e7eb089213f74912 Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 7 Feb 2018 15:50:29 +0000 Subject: [PATCH 3/4] use the SafePath.coffee file in the client too --- .../coffee/ide/directives/SafePath.coffee | 47 +++++++++++++++++++ .../coffee/ide/directives/validFile.coffee | 28 ++--------- 2 files changed, 50 insertions(+), 25 deletions(-) create mode 100644 services/web/public/coffee/ide/directives/SafePath.coffee diff --git a/services/web/public/coffee/ide/directives/SafePath.coffee b/services/web/public/coffee/ide/directives/SafePath.coffee new file mode 100644 index 0000000000..f5b49cdff7 --- /dev/null +++ b/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() \ No newline at end of file diff --git a/services/web/public/coffee/ide/directives/validFile.coffee b/services/web/public/coffee/ide/directives/validFile.coffee index 54e0ad45f8..32abd0fbe0 100644 --- a/services/web/public/coffee/ide/directives/validFile.coffee +++ b/services/web/public/coffee/ide/directives/validFile.coffee @@ -1,26 +1,7 @@ define [ "base" -], (App) -> - - # copied from app/coffee/Features/Project/SafePath.coffee - - 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 + "ide/directives/SafePath" +], (App, SafePath) -> MAX_PATH = 1024 # Maximum path length, in characters. This is fairly arbitrary. @@ -29,8 +10,5 @@ define [ require: 'ngModel' link: (scope, element, attrs, ngModelCtrl) -> ngModelCtrl.$validators.validFile = (filename) -> - isValid = filename.length > 0 && filename.length < MAX_PATH && - not filename.match(BADCHAR_RX) && - not filename.match(BADFILE_RX) - return isValid + return SafePath.isCleanFilename filename } From b083e4bc5fa0ce976f932d778451250ce1e30b3a Mon Sep 17 00:00:00 2001 From: Brian Gough Date: Wed, 7 Feb 2018 16:14:56 +0000 Subject: [PATCH 4/4] remove redundant MAX_PATH definition --- services/web/public/coffee/ide/directives/validFile.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/services/web/public/coffee/ide/directives/validFile.coffee b/services/web/public/coffee/ide/directives/validFile.coffee index 32abd0fbe0..b4ff0eab74 100644 --- a/services/web/public/coffee/ide/directives/validFile.coffee +++ b/services/web/public/coffee/ide/directives/validFile.coffee @@ -3,8 +3,6 @@ define [ "ide/directives/SafePath" ], (App, SafePath) -> - MAX_PATH = 1024 # Maximum path length, in characters. This is fairly arbitrary. - App.directive "validFile", () -> return { require: 'ngModel'