SafePath.coffee 2.1 KB

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