@forge/manifest 7.0.1-next.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/out/builder/processor-builder.js +2 -1
- package/out/processor/abstract-validation-processor.js +2 -2
- package/out/scopes/index.js +6 -7
- package/out/text/errors.d.ts +1 -0
- package/out/text/errors.d.ts.map +1 -1
- package/out/text/errors.js +2 -1
- package/out/utils/index.d.ts +1 -0
- package/out/utils/index.d.ts.map +1 -1
- package/out/utils/index.js +1 -0
- package/out/utils/manifest-parser.d.ts +15 -0
- package/out/utils/manifest-parser.d.ts.map +1 -0
- package/out/utils/manifest-parser.js +42 -0
- package/out/utils/module-references.js +1 -1
- package/out/validators/app-features-validator.js +8 -4
- package/out/validators/connect-authentication-validator.js +8 -4
- package/out/validators/connect-modules-validator.js +18 -3
- package/out/validators/connect-remote-validator.js +16 -7
- package/out/validators/data-classification-validator.js +8 -5
- package/out/validators/display-conditions-validator.js +8 -7
- package/out/validators/entity-property-validator.js +9 -6
- package/out/validators/jql-function-validator.js +9 -6
- package/out/validators/modules-validator.js +136 -49
- package/out/validators/modules-validators/bitbucket/validateBackendModuleEndpoints.js +16 -8
- package/out/validators/modules-validators/confluence/validateCrossModulePropertyUniqueness.js +6 -2
- package/out/validators/modules-validators/confluence/validateCustomContentHierarchy.js +12 -2
- package/out/validators/modules-validators/confluence/validateKeyboardShortcuts.js +7 -2
- package/out/validators/modules-validators/confluence/validatePropertyUniqueness.js +6 -2
- package/out/validators/modules-validators/confluence/validateSingleProperty.js +6 -1
- package/out/validators/modules-validators/jira/ui-modifications.js +6 -1
- package/out/validators/modules-validators/jira/validate-full-admin-page.js +21 -11
- package/out/validators/modules-validators/jira/validate-subpages-in-module.js +12 -2
- package/out/validators/modules-validators/remote/validate-storage-operation.js +8 -4
- package/out/validators/modules-validators/validateModuleScopes.js +7 -2
- package/out/validators/package-validator.js +1 -2
- package/out/validators/permissions-validator.js +32 -24
- package/out/validators/product-trigger-scopes-validator.js +11 -6
- package/out/validators/providers-validator.js +35 -16
- package/out/validators/resources-validator.js +54 -15
- package/out/validators/schema-validator.js +104 -84
- package/out/validators/snapshot-validator.js +3 -4
- package/out/validators/storage-validator.js +41 -14
- package/out/validators/yaml-validator.d.ts +2 -0
- package/out/validators/yaml-validator.d.ts.map +1 -1
- package/out/validators/yaml-validator.js +10 -6
- package/package.json +3 -2
|
@@ -45,17 +45,21 @@ class PermissionsValidator {
|
|
|
45
45
|
}
|
|
46
46
|
addValidationErrors(result, element, values, manifest) {
|
|
47
47
|
values.forEach((value) => {
|
|
48
|
-
result.push(
|
|
48
|
+
result.push({
|
|
49
|
+
message: text_1.errors.permissions.invalidPermission(element, value),
|
|
50
|
+
reference: text_1.References.Permissions,
|
|
51
|
+
level: 'error',
|
|
52
|
+
...(0, utils_1.findPosition)(value, manifest.yamlContentByLine)
|
|
53
|
+
});
|
|
49
54
|
});
|
|
50
55
|
}
|
|
51
56
|
validateExternalPermissionURLs(result, extPermType, perms, manifest) {
|
|
52
|
-
const invalidPerms = perms
|
|
53
|
-
if (invalidPerms
|
|
57
|
+
const invalidPerms = perms?.filter((key) => !this.isValidURL(key));
|
|
58
|
+
if (invalidPerms?.length) {
|
|
54
59
|
this.addValidationErrors(result, extPermType, invalidPerms, manifest);
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
62
|
async validate(manifest) {
|
|
58
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
|
|
59
63
|
if (!manifest || !manifest.typedContent || !manifest.typedContent.permissions) {
|
|
60
64
|
return {
|
|
61
65
|
success: false,
|
|
@@ -64,40 +68,44 @@ class PermissionsValidator {
|
|
|
64
68
|
}
|
|
65
69
|
const errors = [];
|
|
66
70
|
const ALL_SCOPES = shipyard_scopes_json_1.default.concat(deprecated_shipyard_scopes_json_1.default);
|
|
67
|
-
const invalidScopes =
|
|
68
|
-
if (invalidScopes
|
|
71
|
+
const invalidScopes = manifest.typedContent.permissions.scopes?.filter((key) => !ALL_SCOPES.includes(key));
|
|
72
|
+
if (invalidScopes?.length) {
|
|
69
73
|
this.addValidationErrors(errors, 'scopes', invalidScopes, manifest);
|
|
70
74
|
}
|
|
71
|
-
const invalidScripts =
|
|
72
|
-
if (invalidScripts
|
|
75
|
+
const invalidScripts = manifest.typedContent.permissions.content?.scripts?.filter((key) => !egress_types_1.EGRESS_TYPES.ALLOWED_CSP_TYPES.includes(key) && !this.isValidHash(key));
|
|
76
|
+
if (invalidScripts?.length) {
|
|
73
77
|
this.addValidationErrors(errors, 'content.scripts', invalidScripts, manifest);
|
|
74
78
|
}
|
|
75
|
-
const invalidBackendStrings =
|
|
76
|
-
if (invalidBackendStrings
|
|
79
|
+
const invalidBackendStrings = manifest.typedContent.permissions.external?.fetch?.backend?.filter((item) => typeof item === 'string' && !this.isValidURL(item));
|
|
80
|
+
if (invalidBackendStrings?.length) {
|
|
77
81
|
this.addValidationErrors(errors, 'external.fetch.backend', invalidBackendStrings, manifest);
|
|
78
82
|
}
|
|
79
|
-
const remoteMap =
|
|
80
|
-
const invalidBackendRemotes =
|
|
81
|
-
(
|
|
82
|
-
|
|
83
|
+
const remoteMap = manifest.typedContent.remotes?.reduce((prev, item) => prev.set(item.key, item.baseUrl), new Map());
|
|
84
|
+
const invalidBackendRemotes = manifest.typedContent.permissions.external?.fetch?.backend
|
|
85
|
+
?.filter((item) => typeof item === 'object' &&
|
|
86
|
+
(!remoteMap || !remoteMap.has(item.remote) || !this.isValidURL(remoteMap.get(item.remote))))
|
|
87
|
+
.map((item) => item.remote);
|
|
88
|
+
if (invalidBackendRemotes?.length) {
|
|
83
89
|
this.addValidationErrors(errors, 'external.fetch.backend', invalidBackendRemotes, manifest);
|
|
84
90
|
}
|
|
85
|
-
const invalidClientStrings =
|
|
91
|
+
const invalidClientStrings = manifest.typedContent.permissions.external?.fetch?.client?.filter((item) => typeof item === 'string' && !this.isValidURL(item));
|
|
86
92
|
if (invalidClientStrings) {
|
|
87
93
|
this.addValidationErrors(errors, 'external.fetch.client', invalidClientStrings, manifest);
|
|
88
94
|
}
|
|
89
|
-
const invalidClients =
|
|
90
|
-
(
|
|
95
|
+
const invalidClients = manifest.typedContent.permissions.external?.fetch?.client
|
|
96
|
+
?.filter((item) => typeof item === 'object' &&
|
|
97
|
+
(!remoteMap || !remoteMap.has(item.remote) || !this.isValidURL(remoteMap.get(item.remote))))
|
|
98
|
+
.map((item) => item.remote);
|
|
91
99
|
if (invalidClients) {
|
|
92
100
|
this.addValidationErrors(errors, 'external.fetch.client', invalidClients, manifest);
|
|
93
101
|
}
|
|
94
|
-
this.validateExternalPermissionURLs(errors, 'external.navigation',
|
|
95
|
-
this.validateExternalPermissionURLs(errors, 'external.images',
|
|
96
|
-
this.validateExternalPermissionURLs(errors, 'external.frames',
|
|
97
|
-
this.validateExternalPermissionURLs(errors, 'external.scripts',
|
|
98
|
-
this.validateExternalPermissionURLs(errors, 'external.styles',
|
|
99
|
-
this.validateExternalPermissionURLs(errors, 'external.media',
|
|
100
|
-
this.validateExternalPermissionURLs(errors, 'external.fonts',
|
|
102
|
+
this.validateExternalPermissionURLs(errors, 'external.navigation', manifest.typedContent.permissions.external?.navigation, manifest);
|
|
103
|
+
this.validateExternalPermissionURLs(errors, 'external.images', manifest.typedContent.permissions.external?.images, manifest);
|
|
104
|
+
this.validateExternalPermissionURLs(errors, 'external.frames', manifest.typedContent.permissions.external?.frames, manifest);
|
|
105
|
+
this.validateExternalPermissionURLs(errors, 'external.scripts', manifest.typedContent.permissions.external?.scripts, manifest);
|
|
106
|
+
this.validateExternalPermissionURLs(errors, 'external.styles', manifest.typedContent.permissions.external?.styles, manifest);
|
|
107
|
+
this.validateExternalPermissionURLs(errors, 'external.media', manifest.typedContent.permissions.external?.media, manifest);
|
|
108
|
+
this.validateExternalPermissionURLs(errors, 'external.fonts', manifest.typedContent.permissions.external?.fonts, manifest);
|
|
101
109
|
return {
|
|
102
110
|
success: errors.length === 0,
|
|
103
111
|
errors
|
|
@@ -5,11 +5,11 @@ const text_1 = require("../text");
|
|
|
5
5
|
const utils_1 = require("../utils");
|
|
6
6
|
const scopes_1 = require("../scopes");
|
|
7
7
|
class ProductTriggerScopesValidator {
|
|
8
|
+
config;
|
|
8
9
|
constructor(config) {
|
|
9
10
|
this.config = config;
|
|
10
11
|
}
|
|
11
12
|
async validate(manifest) {
|
|
12
|
-
var _a;
|
|
13
13
|
if (!manifest || !manifest.typedContent) {
|
|
14
14
|
return {
|
|
15
15
|
success: false,
|
|
@@ -18,14 +18,19 @@ class ProductTriggerScopesValidator {
|
|
|
18
18
|
}
|
|
19
19
|
const validationErrors = [];
|
|
20
20
|
const getRequiredScopes = (productEvent) => {
|
|
21
|
-
var _a;
|
|
22
21
|
const emptyRequiredScopes = { current: [] };
|
|
23
|
-
return (
|
|
22
|
+
return (this.config.mapping.find((value) => value.productEvent == productEvent)?.oAuthScopes || emptyRequiredScopes);
|
|
24
23
|
};
|
|
25
24
|
const addValidationError = (scope, event) => {
|
|
26
|
-
validationErrors.push(
|
|
25
|
+
validationErrors.push({
|
|
26
|
+
message: text_1.errors.permissions.missingPermissionFromScope(scope, event),
|
|
27
|
+
reference: text_1.References.MissingScopes,
|
|
28
|
+
level: 'error',
|
|
29
|
+
metadata: {
|
|
27
30
|
missingPermission: scope
|
|
28
|
-
}
|
|
31
|
+
},
|
|
32
|
+
...(0, utils_1.findPosition)('scopes', manifest.yamlContentByLine)
|
|
33
|
+
});
|
|
29
34
|
};
|
|
30
35
|
if (!manifest.typedContent.modules || !manifest.typedContent.modules.trigger) {
|
|
31
36
|
return {
|
|
@@ -33,7 +38,7 @@ class ProductTriggerScopesValidator {
|
|
|
33
38
|
manifestObject: manifest
|
|
34
39
|
};
|
|
35
40
|
}
|
|
36
|
-
const manifestScopes =
|
|
41
|
+
const manifestScopes = manifest.typedContent.permissions?.scopes || [];
|
|
37
42
|
for (const element of manifest.typedContent.modules.trigger) {
|
|
38
43
|
for (const event of element.events) {
|
|
39
44
|
const requiredScopes = getRequiredScopes(event);
|
|
@@ -15,28 +15,38 @@ class ProvidersValidator {
|
|
|
15
15
|
const auth = manifest.typedContent.providers.auth;
|
|
16
16
|
const remotes = manifest.typedContent.remotes;
|
|
17
17
|
const permissions = manifest.typedContent.permissions;
|
|
18
|
-
auth
|
|
19
|
-
var _a, _b;
|
|
18
|
+
auth?.forEach((provider) => {
|
|
20
19
|
if ('remotes' in provider) {
|
|
21
|
-
const hasMissing =
|
|
20
|
+
const hasMissing = provider.remotes?.find((key) => !remotes?.find((item) => item.key === key));
|
|
22
21
|
if (hasMissing) {
|
|
23
|
-
validationErrors.push(
|
|
22
|
+
validationErrors.push({
|
|
23
|
+
message: text_1.errors.providers.missingRemote(provider.key, hasMissing),
|
|
24
|
+
reference: text_1.References.Providers,
|
|
25
|
+
level: 'error',
|
|
26
|
+
...(0, utils_1.findPosition)(hasMissing, manifest.yamlContentByLine)
|
|
27
|
+
});
|
|
24
28
|
}
|
|
25
|
-
const missingEgressPermission =
|
|
26
|
-
|
|
27
|
-
const remote = remotes === null || remotes === void 0 ? void 0 : remotes.find((item) => item.key === key);
|
|
29
|
+
const missingEgressPermission = provider.remotes?.find((key) => {
|
|
30
|
+
const remote = remotes?.find((item) => item.key === key);
|
|
28
31
|
if (remote) {
|
|
29
|
-
const egressPermission =
|
|
32
|
+
const egressPermission = permissions?.external?.fetch?.backend?.find((item) => typeof item === 'string' ? item === remote.baseUrl : item.remote === remote.key);
|
|
30
33
|
return !egressPermission;
|
|
31
34
|
}
|
|
32
35
|
return false;
|
|
33
36
|
});
|
|
34
37
|
if (missingEgressPermission) {
|
|
35
|
-
validationErrors.push(
|
|
38
|
+
validationErrors.push({
|
|
39
|
+
message: text_1.errors.providers.missingEgress(missingEgressPermission),
|
|
40
|
+
reference: text_1.References.Providers,
|
|
41
|
+
level: 'error',
|
|
42
|
+
...(0, utils_1.findPosition)(missingEgressPermission, manifest.yamlContentByLine)
|
|
43
|
+
});
|
|
36
44
|
}
|
|
37
45
|
}
|
|
38
46
|
});
|
|
39
|
-
auth
|
|
47
|
+
auth
|
|
48
|
+
?.filter((provider) => 'actions' in provider)
|
|
49
|
+
.forEach((provider) => {
|
|
40
50
|
Object.values(provider.actions).forEach((action) => {
|
|
41
51
|
const message = action && this.isActionValidRemote(provider, action, manifest, remotes);
|
|
42
52
|
if (message) {
|
|
@@ -44,7 +54,7 @@ class ProvidersValidator {
|
|
|
44
54
|
}
|
|
45
55
|
});
|
|
46
56
|
});
|
|
47
|
-
auth
|
|
57
|
+
auth?.forEach((provider) => {
|
|
48
58
|
const message = this.isValidProfileRetrieverFunction(provider, manifest);
|
|
49
59
|
if (message) {
|
|
50
60
|
validationErrors.push(message);
|
|
@@ -56,18 +66,27 @@ class ProvidersValidator {
|
|
|
56
66
|
};
|
|
57
67
|
}
|
|
58
68
|
isActionValidRemote(provider, action, manifest, remotes) {
|
|
59
|
-
const remote = remotes
|
|
69
|
+
const remote = remotes?.find((item) => item.key === action?.remote);
|
|
60
70
|
if (action && !remote) {
|
|
61
|
-
return
|
|
71
|
+
return {
|
|
72
|
+
message: text_1.errors.providers.missingRemote(provider.key, action.remote),
|
|
73
|
+
reference: text_1.References.Providers,
|
|
74
|
+
level: 'error',
|
|
75
|
+
...(0, utils_1.findPosition)(action.remote, manifest.yamlContentByLine)
|
|
76
|
+
};
|
|
62
77
|
}
|
|
63
78
|
}
|
|
64
79
|
isValidProfileRetrieverFunction(provider, manifest) {
|
|
65
|
-
var _a, _b, _c;
|
|
66
80
|
if ('actions' in provider && 'function' in provider.actions.retrieveProfile) {
|
|
67
81
|
const { function: functionKey } = provider.actions.retrieveProfile;
|
|
68
|
-
const hasFunction =
|
|
82
|
+
const hasFunction = manifest.typedContent?.modules?.function?.find((func) => func.key === functionKey);
|
|
69
83
|
if (!hasFunction) {
|
|
70
|
-
return
|
|
84
|
+
return {
|
|
85
|
+
message: text_1.errors.providers.missingProfileFunction(provider.key, functionKey),
|
|
86
|
+
reference: text_1.References.Providers,
|
|
87
|
+
level: 'error',
|
|
88
|
+
...(0, utils_1.findPosition)(functionKey, manifest.yamlContentByLine)
|
|
89
|
+
};
|
|
71
90
|
}
|
|
72
91
|
}
|
|
73
92
|
}
|
|
@@ -26,16 +26,25 @@ class ResourcesValidator {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
if (resources.length > MAX_RESOURCE_COUNT) {
|
|
29
|
-
validationErrors.push(
|
|
29
|
+
validationErrors.push({
|
|
30
|
+
message: text_1.errors.resources.tooManyResourcesError(MAX_RESOURCE_COUNT),
|
|
31
|
+
reference: text_1.References.Resources,
|
|
32
|
+
level: 'error',
|
|
33
|
+
...(0, utils_1.findPosition)('resource', yamlContentByLine)
|
|
34
|
+
});
|
|
30
35
|
}
|
|
31
36
|
if (modules) {
|
|
32
37
|
const resourceMap = new Map(resources.map(({ key, path }) => [key, path]));
|
|
33
38
|
const manifestDir = (0, path_1.dirname)(filePath);
|
|
34
39
|
(0, utils_1.getValidModules)(modules).forEach((moduleKey) => {
|
|
35
|
-
|
|
36
|
-
(_a = modules[moduleKey]) === null || _a === void 0 ? void 0 : _a.forEach((module) => {
|
|
40
|
+
modules[moduleKey]?.forEach((module) => {
|
|
37
41
|
(0, utils_2.findInvalidResourceReferences)(module, resources).forEach((resourceKey) => {
|
|
38
|
-
validationErrors.push(
|
|
42
|
+
validationErrors.push({
|
|
43
|
+
message: text_1.errors.modules.wrongResourceReference(moduleKey, resourceKey),
|
|
44
|
+
reference: text_1.References.Resources,
|
|
45
|
+
level: 'error',
|
|
46
|
+
...(0, utils_1.findPosition)(moduleKey, yamlContentByLine)
|
|
47
|
+
});
|
|
39
48
|
});
|
|
40
49
|
const resourcePath = resourceMap.get(module.resource);
|
|
41
50
|
if (resourcePath === undefined)
|
|
@@ -43,16 +52,31 @@ class ResourcesValidator {
|
|
|
43
52
|
const resourcePathDir = (0, path_1.resolve)(resourcePath);
|
|
44
53
|
if (module.render === 'native') {
|
|
45
54
|
if (fs_1.default.lstatSync(resourcePathDir).isDirectory()) {
|
|
46
|
-
validationErrors.push(
|
|
55
|
+
validationErrors.push({
|
|
56
|
+
message: text_1.errors.modules.wrongResourceType(resourcePath),
|
|
57
|
+
reference: text_1.References.Resources,
|
|
58
|
+
level: 'error',
|
|
59
|
+
...(0, utils_1.findPosition)(moduleKey, yamlContentByLine)
|
|
60
|
+
});
|
|
47
61
|
}
|
|
48
62
|
}
|
|
49
63
|
else {
|
|
50
64
|
if (fs_1.default.lstatSync(resourcePathDir).isDirectory() &&
|
|
51
65
|
!fs_1.default.existsSync((0, path_1.resolve)(manifestDir, resourcePath, 'index.html'))) {
|
|
52
|
-
validationErrors.push(
|
|
66
|
+
validationErrors.push({
|
|
67
|
+
message: text_1.errors.resources.missingEntrypoint(resourcePath, moduleKey),
|
|
68
|
+
reference: text_1.References.Resources,
|
|
69
|
+
level: 'error',
|
|
70
|
+
...(0, utils_1.findPosition)(resourcePath, yamlContentByLine)
|
|
71
|
+
});
|
|
53
72
|
}
|
|
54
73
|
else if (!fs_1.default.lstatSync(resourcePathDir).isDirectory()) {
|
|
55
|
-
validationErrors.push(
|
|
74
|
+
validationErrors.push({
|
|
75
|
+
message: text_1.errors.resources.nonDirectory(resourcePath, moduleKey),
|
|
76
|
+
reference: text_1.References.Resources,
|
|
77
|
+
level: 'error',
|
|
78
|
+
...(0, utils_1.findPosition)(resourcePath, yamlContentByLine)
|
|
79
|
+
});
|
|
56
80
|
}
|
|
57
81
|
}
|
|
58
82
|
});
|
|
@@ -60,7 +84,6 @@ class ResourcesValidator {
|
|
|
60
84
|
(0, utils_1.getValidModules)(modules).forEach((moduleKey) => {
|
|
61
85
|
const uniquePaths = new Set(modules[moduleKey].map(({ resource }) => resourceMap.get(resource)));
|
|
62
86
|
uniquePaths.forEach((path) => {
|
|
63
|
-
var _a, _b, _c;
|
|
64
87
|
if (!path)
|
|
65
88
|
return;
|
|
66
89
|
if (fs_1.default.existsSync((0, path_1.resolve)(manifestDir, path, 'index.html'))) {
|
|
@@ -69,16 +92,22 @@ class ResourcesValidator {
|
|
|
69
92
|
const cspContent = $('meta[http-equiv="Content-Security-Policy"]').attr('content');
|
|
70
93
|
if (cspContent) {
|
|
71
94
|
const cspStyleSrc = cspContent.split(';').find((s) => s.startsWith('style-src'));
|
|
72
|
-
if (cspStyleSrc
|
|
73
|
-
const existingStylesPermissions =
|
|
95
|
+
if (cspStyleSrc?.includes("'unsafe-inline'")) {
|
|
96
|
+
const existingStylesPermissions = manifest.typedContent?.permissions?.content?.styles;
|
|
74
97
|
let shouldShowError = false;
|
|
75
|
-
if (!
|
|
98
|
+
if (!existingStylesPermissions?.length || !existingStylesPermissions?.includes('unsafe-inline')) {
|
|
76
99
|
shouldShowError = true;
|
|
77
100
|
}
|
|
78
101
|
if (shouldShowError) {
|
|
79
|
-
validationErrors.push(
|
|
102
|
+
validationErrors.push({
|
|
103
|
+
message: text_1.errors.resources.deprecatedCspPolicyDefinition(path),
|
|
104
|
+
reference: text_1.References.Resources,
|
|
105
|
+
level: 'error',
|
|
106
|
+
metadata: {
|
|
80
107
|
missingContentStylePermission: 'unsafe-inline'
|
|
81
|
-
}
|
|
108
|
+
},
|
|
109
|
+
...(0, utils_1.findPosition)(path, yamlContentByLine)
|
|
110
|
+
});
|
|
82
111
|
}
|
|
83
112
|
}
|
|
84
113
|
}
|
|
@@ -92,10 +121,20 @@ class ResourcesValidator {
|
|
|
92
121
|
const manifestDir = (0, path_1.dirname)(filePath);
|
|
93
122
|
const resourceDirPath = (0, path_1.resolve)(manifestDir, path);
|
|
94
123
|
if (!fs_1.default.existsSync(resourceDirPath)) {
|
|
95
|
-
return
|
|
124
|
+
return {
|
|
125
|
+
message: text_1.errors.resources.missingResource(path, key),
|
|
126
|
+
reference: text_1.References.Resources,
|
|
127
|
+
level: 'error',
|
|
128
|
+
...(0, utils_1.findPosition)(path, yamlContentByLine)
|
|
129
|
+
};
|
|
96
130
|
}
|
|
97
131
|
else if (fs_1.default.lstatSync(resourceDirPath).isDirectory() && fs_1.default.readdirSync(resourceDirPath).length === 0) {
|
|
98
|
-
return
|
|
132
|
+
return {
|
|
133
|
+
message: text_1.errors.resources.emptyDirectory(path, key),
|
|
134
|
+
reference: text_1.References.Resources,
|
|
135
|
+
level: 'error',
|
|
136
|
+
...(0, utils_1.findPosition)(path, yamlContentByLine)
|
|
137
|
+
};
|
|
99
138
|
}
|
|
100
139
|
})
|
|
101
140
|
.filter((error) => error !== undefined);
|
|
@@ -7,91 +7,87 @@ const ajv_formats_1 = tslib_1.__importDefault(require("ajv-formats"));
|
|
|
7
7
|
const utils_1 = require("../utils");
|
|
8
8
|
const text_1 = require("../text");
|
|
9
9
|
class SchemaValidator {
|
|
10
|
+
schema;
|
|
11
|
+
validateSchema;
|
|
10
12
|
constructor(schema) {
|
|
11
13
|
this.schema = schema;
|
|
12
|
-
this.processErrorMessage = (error) => {
|
|
13
|
-
var _a;
|
|
14
|
-
if (error.keyword === 'required' && error.schemaPath.includes('oneOf')) {
|
|
15
|
-
return undefined;
|
|
16
|
-
}
|
|
17
|
-
if (error.keyword === 'not') {
|
|
18
|
-
return text_1.errors.schema.notAllowed(error.schema.required);
|
|
19
|
-
}
|
|
20
|
-
if (error.keyword === 'enum') {
|
|
21
|
-
return text_1.errors.schema.enumValues(error.params.allowedValues);
|
|
22
|
-
}
|
|
23
|
-
if (error.keyword === 'oneOf') {
|
|
24
|
-
if (error.params.passingSchemas) {
|
|
25
|
-
const requiredProps = error.params.passingSchemas.map((v) => error.schema[v].required);
|
|
26
|
-
return text_1.errors.schema.oneOf(requiredProps);
|
|
27
|
-
}
|
|
28
|
-
else if ((_a = error.parentSchema) === null || _a === void 0 ? void 0 : _a.oneOf) {
|
|
29
|
-
const requiredProps = error.parentSchema.oneOf
|
|
30
|
-
.map((v) => v.required)
|
|
31
|
-
.filter((v) => !!v);
|
|
32
|
-
return text_1.errors.schema.oneOf(requiredProps);
|
|
33
|
-
}
|
|
34
|
-
return text_1.errors.schema.oneOf(undefined);
|
|
35
|
-
}
|
|
36
|
-
if (error.keyword === 'additionalProperties' && 'additionalProperty' in error.params) {
|
|
37
|
-
return text_1.errors.schema.additionalProperties(error.params.additionalProperty);
|
|
38
|
-
}
|
|
39
|
-
return error.message;
|
|
40
|
-
};
|
|
41
|
-
this.handleModuleError = (path, error, manifest) => {
|
|
42
|
-
const hasProperty = path.length > 1;
|
|
43
|
-
const propertyName = path[path.length - 1];
|
|
44
|
-
const errorMessage = this.processErrorMessage(error);
|
|
45
|
-
if (!errorMessage) {
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
if (hasProperty) {
|
|
49
|
-
path.splice(path.length - 1, 0, 'property');
|
|
50
|
-
}
|
|
51
|
-
const data = typeof error.data !== 'string' ? undefined : error.data;
|
|
52
|
-
const searchString = data && hasProperty ? `${propertyName}: ${data}` : path[0];
|
|
53
|
-
return Object.assign({ message: text_1.errors.schemaError(data, path, errorMessage), reference: text_1.References.SchemaError, level: 'error' }, (0, utils_1.findPosition)(searchString, manifest.yamlContentByLine));
|
|
54
|
-
};
|
|
55
|
-
this.handleGenericError = (path, error, manifest) => {
|
|
56
|
-
const hasProperty = path.length > 1;
|
|
57
|
-
const propertyName = path[path.length - 1];
|
|
58
|
-
const errorMessage = this.processErrorMessage(error);
|
|
59
|
-
if (!errorMessage) {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
if (hasProperty) {
|
|
63
|
-
path.splice(path.length - 1, 0, 'property');
|
|
64
|
-
}
|
|
65
|
-
const data = typeof error.data !== 'string' ? undefined : error.data;
|
|
66
|
-
const searchString = error.data && hasProperty ? `${propertyName}: ${error.data}` : propertyName;
|
|
67
|
-
return Object.assign({ message: text_1.errors.schemaError(data, path, errorMessage), reference: text_1.References.SchemaError, level: 'error' }, (0, utils_1.findPosition)(searchString, manifest.yamlContentByLine));
|
|
68
|
-
};
|
|
69
|
-
this.isDeprecatedField = (section, error) => {
|
|
70
|
-
if (error.keyword === 'additionalProperties') {
|
|
71
|
-
return section === 'app' && error.params.additionalProperty === 'name';
|
|
72
|
-
}
|
|
73
|
-
return false;
|
|
74
|
-
};
|
|
75
|
-
this.handleDeprecatedField = (section, error, manifest) => {
|
|
76
|
-
const property = error.params.additionalProperty;
|
|
77
|
-
return Object.assign({ message: text_1.errors.schema.deprecatedValue(section, property, this.getDeprecationInfo(section, property)), reference: text_1.References.Deprecated, level: 'warning' }, (0, utils_1.findPosition)(`${section}:`, manifest.yamlContentByLine));
|
|
78
|
-
};
|
|
79
|
-
this.handleError = (path, error, manifest) => {
|
|
80
|
-
const rootManifestSection = path[0];
|
|
81
|
-
const manifestSection = path[path.length - 1];
|
|
82
|
-
if (this.isDeprecatedField(manifestSection, error)) {
|
|
83
|
-
return this.handleDeprecatedField(manifestSection, error, manifest);
|
|
84
|
-
}
|
|
85
|
-
if (rootManifestSection === 'modules') {
|
|
86
|
-
return this.handleModuleError(path.slice(1), error, manifest);
|
|
87
|
-
}
|
|
88
|
-
return this.handleGenericError(path, error, manifest);
|
|
89
|
-
};
|
|
90
14
|
const ajv = new ajv_1.default({ allErrors: true, verbose: true, strict: false });
|
|
91
15
|
(0, ajv_formats_1.default)(ajv);
|
|
92
16
|
ajv.addVocabulary(['defaultValue', 'fieldDescription', 'fieldTitle', 'shortClassName']);
|
|
93
17
|
this.validateSchema = ajv.compile(this.schema);
|
|
94
18
|
}
|
|
19
|
+
processErrorMessage = (error) => {
|
|
20
|
+
if (error.keyword === 'required' && error.schemaPath.includes('oneOf')) {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
if (error.keyword === 'not') {
|
|
24
|
+
return text_1.errors.schema.notAllowed(error.schema.required);
|
|
25
|
+
}
|
|
26
|
+
if (error.keyword === 'enum') {
|
|
27
|
+
return text_1.errors.schema.enumValues(error.params.allowedValues);
|
|
28
|
+
}
|
|
29
|
+
if (error.keyword === 'oneOf') {
|
|
30
|
+
if (error.params.passingSchemas) {
|
|
31
|
+
const requiredProps = error.params.passingSchemas.map((v) => error.schema[v].required);
|
|
32
|
+
return text_1.errors.schema.oneOf(requiredProps);
|
|
33
|
+
}
|
|
34
|
+
else if (error.parentSchema?.oneOf) {
|
|
35
|
+
const requiredProps = error.parentSchema.oneOf
|
|
36
|
+
.map((v) => v.required)
|
|
37
|
+
.filter((v) => !!v);
|
|
38
|
+
return text_1.errors.schema.oneOf(requiredProps);
|
|
39
|
+
}
|
|
40
|
+
return text_1.errors.schema.oneOf(undefined);
|
|
41
|
+
}
|
|
42
|
+
if (error.keyword === 'additionalProperties' && 'additionalProperty' in error.params) {
|
|
43
|
+
return text_1.errors.schema.additionalProperties(error.params.additionalProperty);
|
|
44
|
+
}
|
|
45
|
+
return error.message;
|
|
46
|
+
};
|
|
47
|
+
handleModuleError = (path, error, manifest) => {
|
|
48
|
+
const hasProperty = path.length > 1;
|
|
49
|
+
const propertyName = path[path.length - 1];
|
|
50
|
+
const errorMessage = this.processErrorMessage(error);
|
|
51
|
+
if (!errorMessage) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
if (hasProperty) {
|
|
55
|
+
path.splice(path.length - 1, 0, 'property');
|
|
56
|
+
}
|
|
57
|
+
const data = typeof error.data !== 'string' ? undefined : error.data;
|
|
58
|
+
const searchString = data && hasProperty ? `${propertyName}: ${data}` : path[0];
|
|
59
|
+
return {
|
|
60
|
+
message: text_1.errors.schemaError(data, path, errorMessage),
|
|
61
|
+
reference: text_1.References.SchemaError,
|
|
62
|
+
level: 'error',
|
|
63
|
+
...(0, utils_1.findPosition)(searchString, manifest.yamlContentByLine)
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
handleGenericError = (path, error, manifest) => {
|
|
67
|
+
const hasProperty = path.length > 1;
|
|
68
|
+
const propertyName = path[path.length - 1];
|
|
69
|
+
const errorMessage = this.processErrorMessage(error);
|
|
70
|
+
if (!errorMessage) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
if (hasProperty) {
|
|
74
|
+
path.splice(path.length - 1, 0, 'property');
|
|
75
|
+
}
|
|
76
|
+
const data = typeof error.data !== 'string' ? undefined : error.data;
|
|
77
|
+
const searchString = error.data && hasProperty ? `${propertyName}: ${error.data}` : propertyName;
|
|
78
|
+
return {
|
|
79
|
+
message: text_1.errors.schemaError(data, path, errorMessage),
|
|
80
|
+
reference: text_1.References.SchemaError,
|
|
81
|
+
level: 'error',
|
|
82
|
+
...(0, utils_1.findPosition)(searchString, manifest.yamlContentByLine)
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
isDeprecatedField = (section, error) => {
|
|
86
|
+
if (error.keyword === 'additionalProperties') {
|
|
87
|
+
return section === 'app' && error.params.additionalProperty === 'name';
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
};
|
|
95
91
|
getDeprecationInfo(section, property) {
|
|
96
92
|
const deprecationInfo = text_1.errors.deprecationInfo;
|
|
97
93
|
const deprecationSection = deprecationInfo[section];
|
|
@@ -102,8 +98,27 @@ class SchemaValidator {
|
|
|
102
98
|
return deprecationSection[property];
|
|
103
99
|
}
|
|
104
100
|
}
|
|
101
|
+
handleDeprecatedField = (section, error, manifest) => {
|
|
102
|
+
const property = error.params.additionalProperty;
|
|
103
|
+
return {
|
|
104
|
+
message: text_1.errors.schema.deprecatedValue(section, property, this.getDeprecationInfo(section, property)),
|
|
105
|
+
reference: text_1.References.Deprecated,
|
|
106
|
+
level: 'warning',
|
|
107
|
+
...(0, utils_1.findPosition)(`${section}:`, manifest.yamlContentByLine)
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
handleError = (path, error, manifest) => {
|
|
111
|
+
const rootManifestSection = path[0];
|
|
112
|
+
const manifestSection = path[path.length - 1];
|
|
113
|
+
if (this.isDeprecatedField(manifestSection, error)) {
|
|
114
|
+
return this.handleDeprecatedField(manifestSection, error, manifest);
|
|
115
|
+
}
|
|
116
|
+
if (rootManifestSection === 'modules') {
|
|
117
|
+
return this.handleModuleError(path.slice(1), error, manifest);
|
|
118
|
+
}
|
|
119
|
+
return this.handleGenericError(path, error, manifest);
|
|
120
|
+
};
|
|
105
121
|
async validate(manifest) {
|
|
106
|
-
var _a;
|
|
107
122
|
if (!manifest || !manifest.yamlContent) {
|
|
108
123
|
return {
|
|
109
124
|
success: false,
|
|
@@ -111,10 +126,12 @@ class SchemaValidator {
|
|
|
111
126
|
};
|
|
112
127
|
}
|
|
113
128
|
let success = this.validateSchema(manifest.yamlContent);
|
|
114
|
-
const errors =
|
|
129
|
+
const errors = this.validateSchema.errors
|
|
130
|
+
?.map((error) => {
|
|
115
131
|
const values = error.instancePath.replace(/\/\d+/, '').split('/').slice(1);
|
|
116
132
|
return this.handleError(values, error, manifest);
|
|
117
|
-
})
|
|
133
|
+
})
|
|
134
|
+
.filter((e) => e !== undefined);
|
|
118
135
|
const _isEqual = (e1, e2) => {
|
|
119
136
|
return (e1.level === e2.level &&
|
|
120
137
|
e1.reference === e2.reference &&
|
|
@@ -123,18 +140,21 @@ class SchemaValidator {
|
|
|
123
140
|
e1.message === e2.message);
|
|
124
141
|
};
|
|
125
142
|
const dedupedErrors = [];
|
|
126
|
-
void
|
|
143
|
+
void errors?.forEach((e) => {
|
|
127
144
|
if (!(dedupedErrors.filter((de) => _isEqual(e, de)).length > 0)) {
|
|
128
145
|
dedupedErrors.push(e);
|
|
129
146
|
}
|
|
130
|
-
})
|
|
147
|
+
});
|
|
131
148
|
const warningLevel = dedupedErrors.filter((e) => e.level === 'warning');
|
|
132
149
|
if (warningLevel.length === dedupedErrors.length) {
|
|
133
150
|
success = true;
|
|
134
151
|
}
|
|
135
152
|
return {
|
|
136
153
|
success,
|
|
137
|
-
manifestObject:
|
|
154
|
+
manifestObject: {
|
|
155
|
+
...manifest,
|
|
156
|
+
typedContent: success ? manifest.yamlContent : undefined
|
|
157
|
+
},
|
|
138
158
|
errors: dedupedErrors
|
|
139
159
|
};
|
|
140
160
|
}
|
|
@@ -11,16 +11,15 @@ const handleRedundantSnapshot = () => {
|
|
|
11
11
|
};
|
|
12
12
|
class SnapshotValidator {
|
|
13
13
|
async validate(manifest) {
|
|
14
|
-
|
|
15
|
-
if (!(manifest === null || manifest === void 0 ? void 0 : manifest.typedContent)) {
|
|
14
|
+
if (!manifest?.typedContent) {
|
|
16
15
|
return {
|
|
17
16
|
success: false,
|
|
18
17
|
manifestObject: manifest
|
|
19
18
|
};
|
|
20
19
|
}
|
|
21
20
|
const validationErrors = [];
|
|
22
|
-
if (
|
|
23
|
-
(
|
|
21
|
+
if (manifest.typedContent.app.runtime?.snapshots === true &&
|
|
22
|
+
(manifest.typedContent.app.runtime?.name ?? 'sandbox') !== 'sandbox') {
|
|
24
23
|
validationErrors.push(handleRedundantSnapshot());
|
|
25
24
|
}
|
|
26
25
|
const warningLevel = validationErrors.filter((e) => e.level === 'warning');
|