]>
Commit | Line | Data |
---|---|---|
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 | ||
8 | const PLUGINS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.PLUGINS | |
9 | ||
10 | function isPluginTypeValid (value: any) { | |
11 | return exists(value) && validator.isInt('' + value) && PluginType[value] !== undefined | |
12 | } | |
13 | ||
14 | function isPluginNameValid (value: string) { | |
15 | return exists(value) && | |
16 | validator.isLength(value, PLUGINS_CONSTRAINTS_FIELDS.NAME) && | |
17 | validator.matches(value, /^[a-z-0-9]+$/) | |
18 | } | |
19 | ||
20 | function isNpmPluginNameValid (value: string) { | |
21 | return exists(value) && | |
22 | validator.isLength(value, PLUGINS_CONSTRAINTS_FIELDS.NAME) && | |
23 | validator.matches(value, /^[a-z\-._0-9]+$/) && | |
24 | (value.startsWith('peertube-plugin-') || value.startsWith('peertube-theme-')) | |
25 | } | |
26 | ||
27 | function isPluginDescriptionValid (value: string) { | |
28 | return exists(value) && validator.isLength(value, PLUGINS_CONSTRAINTS_FIELDS.DESCRIPTION) | |
29 | } | |
30 | ||
31 | function isPluginVersionValid (value: string) { | |
32 | if (!exists(value)) return false | |
33 | ||
34 | const parts = (value + '').split('.') | |
35 | ||
36 | return parts.length === 3 && parts.every(p => validator.isInt(p)) | |
37 | } | |
38 | ||
39 | function isPluginEngineValid (engine: any) { | |
40 | return exists(engine) && exists(engine.peertube) | |
41 | } | |
42 | ||
43 | function isPluginHomepage (value: string) { | |
44 | return exists(value) && (!value || isUrlValid(value)) | |
45 | } | |
46 | ||
47 | function isPluginBugs (value: string) { | |
48 | return exists(value) && (!value || isUrlValid(value)) | |
49 | } | |
50 | ||
51 | function areStaticDirectoriesValid (staticDirs: any) { | |
52 | if (!exists(staticDirs) || typeof staticDirs !== 'object') return false | |
53 | ||
54 | for (const key of Object.keys(staticDirs)) { | |
55 | if (!isSafePath(staticDirs[key])) return false | |
56 | } | |
57 | ||
58 | return true | |
59 | } | |
60 | ||
61 | function areClientScriptsValid (clientScripts: any[]) { | |
62 | return isArray(clientScripts) && | |
63 | clientScripts.every(c => { | |
64 | return isSafePath(c.script) && isArray(c.scopes) | |
65 | }) | |
66 | } | |
67 | ||
68 | function areTranslationPathsValid (translations: any) { | |
69 | if (!exists(translations) || typeof translations !== 'object') return false | |
70 | ||
71 | for (const key of Object.keys(translations)) { | |
72 | if (!isSafePath(translations[key])) return false | |
73 | } | |
74 | ||
75 | return true | |
76 | } | |
77 | ||
78 | function areCSSPathsValid (css: any[]) { | |
79 | return isArray(css) && css.every(c => isSafePath(c)) | |
80 | } | |
81 | ||
82 | function isThemeNameValid (name: string) { | |
83 | return isPluginNameValid(name) | |
84 | } | |
85 | ||
86 | function isPackageJSONValid (packageJSON: PluginPackageJson, pluginType: PluginType) { | |
87 | let result = true | |
88 | const badFields: string[] = [] | |
89 | ||
90 | if (!isNpmPluginNameValid(packageJSON.name)) { | |
91 | result = false | |
92 | badFields.push('name') | |
93 | } | |
94 | ||
95 | if (!isPluginDescriptionValid(packageJSON.description)) { | |
96 | result = false | |
97 | badFields.push('description') | |
98 | } | |
99 | ||
100 | if (!isPluginEngineValid(packageJSON.engine)) { | |
101 | result = false | |
102 | badFields.push('engine') | |
103 | } | |
104 | ||
105 | if (!isPluginHomepage(packageJSON.homepage)) { | |
106 | result = false | |
107 | badFields.push('homepage') | |
108 | } | |
109 | ||
110 | if (!exists(packageJSON.author)) { | |
111 | result = false | |
112 | badFields.push('author') | |
113 | } | |
114 | ||
115 | if (!isPluginBugs(packageJSON.bugs)) { | |
116 | result = false | |
117 | badFields.push('bugs') | |
118 | } | |
119 | ||
120 | if (pluginType === PluginType.PLUGIN && !isSafePath(packageJSON.library)) { | |
121 | result = false | |
122 | badFields.push('library') | |
123 | } | |
124 | ||
125 | if (!areStaticDirectoriesValid(packageJSON.staticDirs)) { | |
126 | result = false | |
127 | badFields.push('staticDirs') | |
128 | } | |
129 | ||
130 | if (!areCSSPathsValid(packageJSON.css)) { | |
131 | result = false | |
132 | badFields.push('css') | |
133 | } | |
134 | ||
135 | if (!areClientScriptsValid(packageJSON.clientScripts)) { | |
136 | result = false | |
137 | badFields.push('clientScripts') | |
138 | } | |
139 | ||
140 | if (!areTranslationPathsValid(packageJSON.translations)) { | |
141 | result = false | |
142 | badFields.push('translations') | |
143 | } | |
144 | ||
145 | return { result, badFields } | |
146 | } | |
147 | ||
148 | function isLibraryCodeValid (library: any) { | |
149 | return typeof library.register === 'function' && | |
150 | typeof library.unregister === 'function' | |
151 | } | |
152 | ||
153 | export { | |
154 | isPluginTypeValid, | |
155 | isPackageJSONValid, | |
156 | isThemeNameValid, | |
157 | isPluginHomepage, | |
158 | isPluginVersionValid, | |
159 | isPluginNameValid, | |
160 | isPluginDescriptionValid, | |
161 | isLibraryCodeValid, | |
162 | isNpmPluginNameValid | |
163 | } |