mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #1608 from sharelatex/bg-allow-reserved-names-safely
allow reserved names safely GitOrigin-RevId: d0c5f287411f5cd6d0639f57ef925409571acf8a
This commit is contained in:
parent
0f22a626c8
commit
32925f0c4e
5 changed files with 56 additions and 16 deletions
|
@ -122,6 +122,9 @@ module.exports = ProjectEntityMongoUpdateHandler = self =
|
||||||
return callback(err) if err?
|
return callback(err) if err?
|
||||||
ProjectLocator.findElement {project, element_id: entity_id, type: entityType}, (err, entity, entityPath)->
|
ProjectLocator.findElement {project, element_id: entity_id, type: entityType}, (err, entity, entityPath)->
|
||||||
return callback(err) if err?
|
return callback(err) if err?
|
||||||
|
# Prevent top-level docs/files with reserved names (to match v1 behaviour)
|
||||||
|
if self._blockedFilename entityPath, entityType
|
||||||
|
return callback new Errors.InvalidNameError("blocked element name")
|
||||||
self._checkValidMove project, entityType, entity, entityPath, destFolderId, (error) ->
|
self._checkValidMove project, entityType, entity, entityPath, destFolderId, (error) ->
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
ProjectEntityHandler.getAllEntitiesFromProject project, (error, oldDocs, oldFiles) ->
|
ProjectEntityHandler.getAllEntitiesFromProject project, (error, oldDocs, oldFiles) ->
|
||||||
|
@ -153,10 +156,13 @@ module.exports = ProjectEntityMongoUpdateHandler = self =
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
ProjectLocator.findElement {project:project, element_id:entity_id, type:entityType}, (error, entity, entPath, parentFolder)=>
|
ProjectLocator.findElement {project:project, element_id:entity_id, type:entityType}, (error, entity, entPath, parentFolder)=>
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
|
endPath = path.join(path.dirname(entPath.fileSystem), newName)
|
||||||
|
# Prevent top-level docs/files with reserved names (to match v1 behaviour)
|
||||||
|
if self._blockedFilename {fileSystem: endPath}, entityType
|
||||||
|
return callback new Errors.InvalidNameError("blocked element name")
|
||||||
# check if the new name already exists in the current folder
|
# check if the new name already exists in the current folder
|
||||||
self._checkValidElementName parentFolder, newName, (error) =>
|
self._checkValidElementName parentFolder, newName, (error) =>
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
endPath = path.join(path.dirname(entPath.fileSystem), newName)
|
|
||||||
conditions = {_id:project_id}
|
conditions = {_id:project_id}
|
||||||
update = "$set":{}, "$inc":{}
|
update = "$set":{}, "$inc":{}
|
||||||
namePath = entPath.mongo+".name"
|
namePath = entPath.mongo+".name"
|
||||||
|
@ -257,6 +263,9 @@ module.exports = ProjectEntityMongoUpdateHandler = self =
|
||||||
# check if the path would be too long
|
# check if the path would be too long
|
||||||
if not SafePath.isAllowedLength newPath.fileSystem
|
if not SafePath.isAllowedLength newPath.fileSystem
|
||||||
return callback new Errors.InvalidNameError("path too long")
|
return callback new Errors.InvalidNameError("path too long")
|
||||||
|
# Prevent top-level docs/files with reserved names (to match v1 behaviour)
|
||||||
|
if self._blockedFilename newPath, type
|
||||||
|
return callback new Errors.InvalidNameError("blocked element name")
|
||||||
self._checkValidElementName folder, element.name, (err) =>
|
self._checkValidElementName folder, element.name, (err) =>
|
||||||
return callback(err) if err?
|
return callback(err) if err?
|
||||||
id = element._id+''
|
id = element._id+''
|
||||||
|
@ -276,6 +285,18 @@ module.exports = ProjectEntityMongoUpdateHandler = self =
|
||||||
return callback(err)
|
return callback(err)
|
||||||
callback(err, {path:newPath}, newProject)
|
callback(err, {path:newPath}, newProject)
|
||||||
|
|
||||||
|
_blockedFilename: (entityPath, entityType) ->
|
||||||
|
# check if name would be blocked in v1
|
||||||
|
# javascript reserved names are forbidden for docs and files
|
||||||
|
# at the top-level (but folders with reserved names are allowed).
|
||||||
|
isFolder = (entityType in ['folder', 'folders'])
|
||||||
|
[dir, file] = [path.dirname(entityPath.fileSystem), path.basename(entityPath.fileSystem)]
|
||||||
|
isTopLevel = dir is '/'
|
||||||
|
if isTopLevel and !isFolder && SafePath.isBlockedFilename file
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
|
||||||
_checkValidElementName: (folder, name, callback = (err) ->) ->
|
_checkValidElementName: (folder, name, callback = (err) ->) ->
|
||||||
# check if the name is already taken by a doc, file or
|
# check if the name is already taken by a doc, file or
|
||||||
# folder. If so, return an error "file already exists".
|
# folder. If so, return an error "file already exists".
|
||||||
|
|
|
@ -67,8 +67,10 @@ load = () ->
|
||||||
isCleanFilename: (filename) ->
|
isCleanFilename: (filename) ->
|
||||||
return SafePath.isAllowedLength(filename) &&
|
return SafePath.isAllowedLength(filename) &&
|
||||||
!BADCHAR_RX.test(filename) &&
|
!BADCHAR_RX.test(filename) &&
|
||||||
!BADFILE_RX.test(filename) &&
|
!BADFILE_RX.test(filename)
|
||||||
!BLOCKEDFILE_RX.test(filename)
|
|
||||||
|
isBlockedFilename: (filename) ->
|
||||||
|
return BLOCKEDFILE_RX.test(filename)
|
||||||
|
|
||||||
# returns whether a full path is 'clean' - e.g. is a full or relative path
|
# returns whether a full path is 'clean' - e.g. is a full or relative path
|
||||||
# that points to a file, and each element passes the rules in 'isCleanFilename'
|
# that points to a file, and each element passes the rules in 'isCleanFilename'
|
||||||
|
@ -81,6 +83,9 @@ load = () ->
|
||||||
for element in elements
|
for element in elements
|
||||||
return false if element.length > 0 && !SafePath.isCleanFilename element
|
return false if element.length > 0 && !SafePath.isCleanFilename element
|
||||||
|
|
||||||
|
# check for a top-level reserved name
|
||||||
|
return false if BLOCKEDFILE_RX.test(path.replace(/^\/?/,'')) # remove leading slash if present
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
isAllowedLength: (pathname) ->
|
isAllowedLength: (pathname) ->
|
||||||
|
|
|
@ -87,8 +87,7 @@ prototype\
|
||||||
return (
|
return (
|
||||||
SafePath.isAllowedLength(filename) &&
|
SafePath.isAllowedLength(filename) &&
|
||||||
!BADCHAR_RX.test(filename) &&
|
!BADCHAR_RX.test(filename) &&
|
||||||
!BADFILE_RX.test(filename) &&
|
!BADFILE_RX.test(filename)
|
||||||
!BLOCKEDFILE_RX.test(filename)
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -168,16 +168,18 @@ define([
|
||||||
}
|
}
|
||||||
|
|
||||||
getMultiSelectedEntityChildNodes() {
|
getMultiSelectedEntityChildNodes() {
|
||||||
|
// use pathnames with a leading slash to avoid
|
||||||
|
// problems with reserved Object properties
|
||||||
const entities = this.getMultiSelectedEntities()
|
const entities = this.getMultiSelectedEntities()
|
||||||
const paths = {}
|
const paths = {}
|
||||||
for (var entity of Array.from(entities)) {
|
for (var entity of Array.from(entities)) {
|
||||||
paths[this.getEntityPath(entity)] = entity
|
paths["/" + this.getEntityPath(entity)] = entity
|
||||||
}
|
}
|
||||||
const prefixes = {}
|
const prefixes = {}
|
||||||
for (var path in paths) {
|
for (var path in paths) {
|
||||||
entity = paths[path]
|
entity = paths[path]
|
||||||
const parts = path.split('/')
|
const parts = path.split('/')
|
||||||
if (parts.length <= 1) {
|
if (parts.length <= 2) {
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
// Record prefixes a/b/c.tex -> 'a' and 'a/b'
|
// Record prefixes a/b/c.tex -> 'a' and 'a/b'
|
||||||
|
@ -186,7 +188,7 @@ define([
|
||||||
asc ? i <= end : i >= end;
|
asc ? i <= end : i >= end;
|
||||||
asc ? i++ : i--
|
asc ? i++ : i--
|
||||||
) {
|
) {
|
||||||
prefixes[parts.slice(0, i).join('/')] = true
|
prefixes["/" + parts.slice(0, i).join('/')] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,17 +63,18 @@ describe 'SafePath', ->
|
||||||
result = @SafePath.isCleanFilename 'foo\uD800\uDFFFbar'
|
result = @SafePath.isCleanFilename 'foo\uD800\uDFFFbar'
|
||||||
result.should.equal false
|
result.should.equal false
|
||||||
|
|
||||||
it 'should not accept javascript property names', ->
|
it 'should accept javascript property names', ->
|
||||||
result = @SafePath.isCleanFilename 'prototype'
|
result = @SafePath.isCleanFilename 'prototype'
|
||||||
result.should.equal false
|
result.should.equal true
|
||||||
|
|
||||||
it 'should not accept javascript property names in the prototype', ->
|
it 'should accept javascript property names in the prototype', ->
|
||||||
result = @SafePath.isCleanFilename 'hasOwnProperty'
|
result = @SafePath.isCleanFilename 'hasOwnProperty'
|
||||||
result.should.equal false
|
result.should.equal true
|
||||||
|
|
||||||
it 'should not accept javascript property names resulting from substitutions', ->
|
# this test never worked correctly because the spaces are not replaced by underscores in isCleanFilename
|
||||||
result = @SafePath.isCleanFilename ' proto '
|
# it 'should not accept javascript property names resulting from substitutions', ->
|
||||||
result.should.equal false
|
# result = @SafePath.isCleanFilename ' proto '
|
||||||
|
# result.should.equal false
|
||||||
|
|
||||||
# it 'should not accept a trailing .', ->
|
# it 'should not accept a trailing .', ->
|
||||||
# result = @SafePath.isCleanFilename 'hello.'
|
# result = @SafePath.isCleanFilename 'hello.'
|
||||||
|
@ -136,6 +137,18 @@ describe 'SafePath', ->
|
||||||
result = @SafePath.isCleanPath 'foo//*/bar'
|
result = @SafePath.isCleanPath 'foo//*/bar'
|
||||||
result.should.equal false
|
result.should.equal false
|
||||||
|
|
||||||
|
it 'should not accept javascript property names', ->
|
||||||
|
result = @SafePath.isCleanPath 'prototype'
|
||||||
|
result.should.equal false
|
||||||
|
|
||||||
|
it 'should not accept javascript property names in the prototype', ->
|
||||||
|
result = @SafePath.isCleanPath 'hasOwnProperty'
|
||||||
|
result.should.equal false
|
||||||
|
|
||||||
|
it 'should not accept javascript property names resulting from substitutions', ->
|
||||||
|
result = @SafePath.isCleanPath ' proto '
|
||||||
|
result.should.equal false
|
||||||
|
|
||||||
describe 'isAllowedLength', ->
|
describe 'isAllowedLength', ->
|
||||||
it 'should accept a valid path "main.tex"', ->
|
it 'should accept a valid path "main.tex"', ->
|
||||||
result = @SafePath.isAllowedLength 'main.tex'
|
result = @SafePath.isAllowedLength 'main.tex'
|
||||||
|
|
Loading…
Reference in a new issue