safe-path.js 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  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. // The logic in all copies must be kept in sync:
  4. // app/src/Features/Project/SafePath.js
  5. // frontend/js/ide/directives/SafePath.js
  6. // frontend/js/features/file-tree/util/safe-path.js
  7. const BADCHAR_RX = new RegExp(
  8. `\
  9. [\
  10. \\/\
  11. \\\\\
  12. \\*\
  13. \\u0000-\\u001F\
  14. \\u007F\
  15. \\u0080-\\u009F\
  16. \\uD800-\\uDFFF\
  17. ]\
  18. `,
  19. 'g'
  20. )
  21. const BADFILE_RX = new RegExp(
  22. `\
  23. (^\\.$)\
  24. |(^\\.\\.$)\
  25. |(^\\s+)\
  26. |(\\s+$)\
  27. `,
  28. 'g'
  29. )
  30. // Put a block on filenames which match javascript property names, as they
  31. // can cause exceptions where the code puts filenames into a hash. This is a
  32. // temporary workaround until the code in other places is made safe against
  33. // property names.
  34. //
  35. // The list of property names is taken from
  36. // ['prototype'].concat(Object.getOwnPropertyNames(Object.prototype))
  37. const BLOCKEDFILE_RX = new RegExp(`\
  38. ^(\
  39. prototype\
  40. |constructor\
  41. |toString\
  42. |toLocaleString\
  43. |valueOf\
  44. |hasOwnProperty\
  45. |isPrototypeOf\
  46. |propertyIsEnumerable\
  47. |__defineGetter__\
  48. |__lookupGetter__\
  49. |__defineSetter__\
  50. |__lookupSetter__\
  51. |__proto__\
  52. )$\
  53. `)
  54. const MAX_PATH = 1024 // Maximum path length, in characters. This is fairly arbitrary.
  55. export function clean(filename) {
  56. filename = filename.replace(BADCHAR_RX, '_')
  57. // for BADFILE_RX replace any matches with an equal number of underscores
  58. filename = filename.replace(BADFILE_RX, match =>
  59. new Array(match.length + 1).join('_')
  60. )
  61. // replace blocked filenames 'prototype' with '@prototype'
  62. filename = filename.replace(BLOCKEDFILE_RX, '@$1')
  63. return filename
  64. }
  65. export function isCleanFilename(filename) {
  66. return (
  67. isAllowedLength(filename) &&
  68. !filename.match(BADCHAR_RX) &&
  69. !filename.match(BADFILE_RX)
  70. )
  71. }
  72. export function isAllowedLength(pathname) {
  73. return pathname.length > 0 && pathname.length <= MAX_PATH
  74. }