SafePath.coffee 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. # This file is shared between the frontend and server code of web, so that
  2. # filename validation is the same in both implementations.
  3. # Both copies must be kept in sync:
  4. # app/coffee/Features/Project/SafePath.coffee
  5. # public/coffee/ide/directives/SafePath.coffee
  6. load = () ->
  7. BADCHAR_RX = ///
  8. [
  9. \/ # no forward slashes
  10. \\ # no back slashes
  11. \* # no asterisk
  12. \u0000-\u001F # no control characters (0-31)
  13. \u007F # no delete
  14. \u0080-\u009F # no unicode control characters (C1)
  15. \uD800-\uDFFF # no unicode surrogate characters
  16. ]
  17. ///g
  18. BADFILE_RX = ///
  19. (^\.$) # reject . as a filename
  20. | (^\.\.$) # reject .. as a filename
  21. | (^\s+) # reject leading space
  22. | (\s+$) # reject trailing space
  23. ///g
  24. # Put a block on filenames which match javascript property names, as they
  25. # can cause exceptions where the code puts filenames into a hash. This is a
  26. # temporary workaround until the code in other places is made safe against
  27. # property names.
  28. #
  29. # The list of property names is taken from
  30. # ['prototype'].concat(Object.getOwnPropertyNames(Object.prototype))
  31. BLOCKEDFILE_RX = ///
  32. ^(
  33. prototype
  34. |constructor
  35. |toString
  36. |toLocaleString
  37. |valueOf
  38. |hasOwnProperty
  39. |isPrototypeOf
  40. |propertyIsEnumerable
  41. |__defineGetter__
  42. |__lookupGetter__
  43. |__defineSetter__
  44. |__lookupSetter__
  45. |__proto__
  46. )$
  47. ///
  48. MAX_PATH = 1024 # Maximum path length, in characters. This is fairly arbitrary.
  49. SafePath =
  50. clean: (filename) ->
  51. filename = filename.replace BADCHAR_RX, '_'
  52. # for BADFILE_RX replace any matches with an equal number of underscores
  53. filename = filename.replace BADFILE_RX, (match) ->
  54. return new Array(match.length + 1).join("_")
  55. # replace blocked filenames 'prototype' with '@prototype'
  56. filename = filename.replace BLOCKEDFILE_RX, "@$1"
  57. return filename
  58. isCleanFilename: (filename) ->
  59. return SafePath.isAllowedLength(filename) &&
  60. not filename.match(BADCHAR_RX) &&
  61. not filename.match(BADFILE_RX) &&
  62. not filename.match(BLOCKEDFILE_RX)
  63. isAllowedLength: (pathname) ->
  64. return pathname.length > 0 && pathname.length <= MAX_PATH
  65. if define?
  66. define [], load
  67. else
  68. module.exports = load()