plugins.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import { exists, isArray, isSafePath } from './misc'
  2. import validator from 'validator'
  3. import { PluginType } from '../../../shared/models/plugins/plugin.type'
  4. import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
  5. import { PluginPackageJson } from '../../../shared/models/plugins/plugin-package-json.model'
  6. import { isUrlValid } from './activitypub/misc'
  7. const PLUGINS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.PLUGINS
  8. function isPluginTypeValid (value: any) {
  9. return exists(value) &&
  10. (value === PluginType.PLUGIN || value === PluginType.THEME)
  11. }
  12. function isPluginNameValid (value: string) {
  13. return exists(value) &&
  14. validator.isLength(value, PLUGINS_CONSTRAINTS_FIELDS.NAME) &&
  15. validator.matches(value, /^[a-z-0-9]+$/)
  16. }
  17. function isNpmPluginNameValid (value: string) {
  18. return exists(value) &&
  19. validator.isLength(value, PLUGINS_CONSTRAINTS_FIELDS.NAME) &&
  20. validator.matches(value, /^[a-z\-._0-9]+$/) &&
  21. (value.startsWith('peertube-plugin-') || value.startsWith('peertube-theme-'))
  22. }
  23. function isPluginDescriptionValid (value: string) {
  24. return exists(value) && validator.isLength(value, PLUGINS_CONSTRAINTS_FIELDS.DESCRIPTION)
  25. }
  26. function isPluginVersionValid (value: string) {
  27. if (!exists(value)) return false
  28. const parts = (value + '').split('.')
  29. return parts.length === 3 && parts.every(p => validator.isInt(p))
  30. }
  31. function isPluginEngineValid (engine: any) {
  32. return exists(engine) && exists(engine.peertube)
  33. }
  34. function isPluginHomepage (value: string) {
  35. return exists(value) && (!value || isUrlValid(value))
  36. }
  37. function isPluginBugs (value: string) {
  38. return exists(value) && (!value || isUrlValid(value))
  39. }
  40. function areStaticDirectoriesValid (staticDirs: any) {
  41. if (!exists(staticDirs) || typeof staticDirs !== 'object') return false
  42. for (const key of Object.keys(staticDirs)) {
  43. if (!isSafePath(staticDirs[key])) return false
  44. }
  45. return true
  46. }
  47. function areClientScriptsValid (clientScripts: any[]) {
  48. return isArray(clientScripts) &&
  49. clientScripts.every(c => {
  50. return isSafePath(c.script) && isArray(c.scopes)
  51. })
  52. }
  53. function areTranslationPathsValid (translations: any) {
  54. if (!exists(translations) || typeof translations !== 'object') return false
  55. for (const key of Object.keys(translations)) {
  56. if (!isSafePath(translations[key])) return false
  57. }
  58. return true
  59. }
  60. function areCSSPathsValid (css: any[]) {
  61. return isArray(css) && css.every(c => isSafePath(c))
  62. }
  63. function isThemeNameValid (name: string) {
  64. return isPluginNameValid(name)
  65. }
  66. function isPackageJSONValid (packageJSON: PluginPackageJson, pluginType: PluginType) {
  67. let result = true
  68. const badFields: string[] = []
  69. if (!isNpmPluginNameValid(packageJSON.name)) {
  70. result = false
  71. badFields.push('name')
  72. }
  73. if (!isPluginDescriptionValid(packageJSON.description)) {
  74. result = false
  75. badFields.push('description')
  76. }
  77. if (!isPluginEngineValid(packageJSON.engine)) {
  78. result = false
  79. badFields.push('engine')
  80. }
  81. if (!isPluginHomepage(packageJSON.homepage)) {
  82. result = false
  83. badFields.push('homepage')
  84. }
  85. if (!exists(packageJSON.author)) {
  86. result = false
  87. badFields.push('author')
  88. }
  89. if (!isPluginBugs(packageJSON.bugs)) {
  90. result = false
  91. badFields.push('bugs')
  92. }
  93. if (pluginType === PluginType.PLUGIN && !isSafePath(packageJSON.library)) {
  94. result = false
  95. badFields.push('library')
  96. }
  97. if (!areStaticDirectoriesValid(packageJSON.staticDirs)) {
  98. result = false
  99. badFields.push('staticDirs')
  100. }
  101. if (!areCSSPathsValid(packageJSON.css)) {
  102. result = false
  103. badFields.push('css')
  104. }
  105. if (!areClientScriptsValid(packageJSON.clientScripts)) {
  106. result = false
  107. badFields.push('clientScripts')
  108. }
  109. if (!areTranslationPathsValid(packageJSON.translations)) {
  110. result = false
  111. badFields.push('translations')
  112. }
  113. return { result, badFields }
  114. }
  115. function isLibraryCodeValid (library: any) {
  116. return typeof library.register === 'function' &&
  117. typeof library.unregister === 'function'
  118. }
  119. export {
  120. isPluginTypeValid,
  121. isPackageJSONValid,
  122. isThemeNameValid,
  123. isPluginHomepage,
  124. isPluginVersionValid,
  125. isPluginNameValid,
  126. isPluginDescriptionValid,
  127. isLibraryCodeValid,
  128. isNpmPluginNameValid
  129. }