SafePath.mjs 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS101: Remove unnecessary use of Array.from
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * DS207: Consider shorter variations of null checks
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. // This file is shared between the frontend and server code of web, so that
  15. // filename validation is the same in both implementations.
  16. // The logic in all copies must be kept in sync:
  17. // app/src/Features/Project/SafePath.js
  18. // frontend/js/ide/directives/SafePath.js
  19. // frontend/js/features/file-tree/util/safe-path.js
  20. const load = function () {
  21. let SafePath
  22. // eslint-disable-next-line prefer-regex-literals
  23. const BADCHAR_RX = new RegExp(
  24. `\
  25. [\
  26. \\/\
  27. \\\\\
  28. \\*\
  29. \\u0000-\\u001F\
  30. \\u007F\
  31. \\u0080-\\u009F\
  32. \\uD800-\\uDFFF\
  33. ]\
  34. `,
  35. 'g'
  36. )
  37. // eslint-disable-next-line prefer-regex-literals
  38. const BADFILE_RX = new RegExp(
  39. `\
  40. (^\\.$)\
  41. |(^\\.\\.$)\
  42. |(^\\s+)\
  43. |(\\s+$)\
  44. `,
  45. 'g'
  46. )
  47. // Put a block on filenames which match javascript property names, as they
  48. // can cause exceptions where the code puts filenames into a hash. This is a
  49. // temporary workaround until the code in other places is made safe against
  50. // property names.
  51. //
  52. // The list of property names is taken from
  53. // ['prototype'].concat(Object.getOwnPropertyNames(Object.prototype))
  54. // eslint-disable-next-line prefer-regex-literals
  55. const BLOCKEDFILE_RX = new RegExp(`\
  56. ^(\
  57. prototype\
  58. |constructor\
  59. |toString\
  60. |toLocaleString\
  61. |valueOf\
  62. |hasOwnProperty\
  63. |isPrototypeOf\
  64. |propertyIsEnumerable\
  65. |__defineGetter__\
  66. |__lookupGetter__\
  67. |__defineSetter__\
  68. |__lookupSetter__\
  69. |__proto__\
  70. )$\
  71. `)
  72. const MAX_PATH = 1024 // Maximum path length, in characters. This is fairly arbitrary.
  73. return (SafePath = {
  74. // convert any invalid characters to underscores in the given filename
  75. clean(filename) {
  76. filename = filename.replace(BADCHAR_RX, '_')
  77. // for BADFILE_RX replace any matches with an equal number of underscores
  78. filename = filename.replace(BADFILE_RX, match =>
  79. new Array(match.length + 1).join('_')
  80. )
  81. // replace blocked filenames 'prototype' with '@prototype'
  82. filename = filename.replace(BLOCKEDFILE_RX, '@$1')
  83. return filename
  84. },
  85. // returns whether the filename is 'clean' (does not contain any invalid
  86. // characters or reserved words)
  87. isCleanFilename(filename) {
  88. return (
  89. SafePath.isAllowedLength(filename) &&
  90. !filename.match(BADCHAR_RX) &&
  91. !filename.match(BADFILE_RX)
  92. )
  93. },
  94. isBlockedFilename(filename) {
  95. return BLOCKEDFILE_RX.test(filename)
  96. },
  97. // returns whether a full path is 'clean' - e.g. is a full or relative path
  98. // that points to a file, and each element passes the rules in 'isCleanFilename'
  99. isCleanPath(path) {
  100. const elements = path.split('/')
  101. const lastElementIsEmpty = elements[elements.length - 1].length === 0
  102. if (lastElementIsEmpty) {
  103. return false
  104. }
  105. for (const element of Array.from(elements)) {
  106. if (element.length > 0 && !SafePath.isCleanFilename(element)) {
  107. return false
  108. }
  109. }
  110. // check for a top-level reserved name
  111. if (BLOCKEDFILE_RX.test(path.replace(/^\/?/, ''))) {
  112. return false
  113. } // remove leading slash if present
  114. return true
  115. },
  116. isAllowedLength(pathname) {
  117. return pathname.length > 0 && pathname.length <= MAX_PATH
  118. },
  119. })
  120. }
  121. export default load()