@servicenow/sdk-build-plugins 4.4.0 → 4.5.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/dist/acl-plugin.js +54 -4
- package/dist/acl-plugin.js.map +1 -1
- package/dist/applicability-plugin.js +2 -0
- package/dist/applicability-plugin.js.map +1 -1
- package/dist/application-menu-plugin.js +2 -0
- package/dist/application-menu-plugin.js.map +1 -1
- package/dist/arrow-function-plugin.d.ts +6 -1
- package/dist/arrow-function-plugin.js +105 -12
- package/dist/arrow-function-plugin.js.map +1 -1
- package/dist/atf/test-plugin.js +2 -0
- package/dist/atf/test-plugin.js.map +1 -1
- package/dist/basic-syntax-plugin.js +20 -0
- package/dist/basic-syntax-plugin.js.map +1 -1
- package/dist/call-expression-plugin.js +1 -0
- package/dist/call-expression-plugin.js.map +1 -1
- package/dist/claims-plugin.js +1 -0
- package/dist/claims-plugin.js.map +1 -1
- package/dist/client-script-plugin.js +1 -0
- package/dist/client-script-plugin.js.map +1 -1
- package/dist/column-plugin.js +1 -0
- package/dist/column-plugin.js.map +1 -1
- package/dist/cross-scope-privilege-plugin.js +1 -0
- package/dist/cross-scope-privilege-plugin.js.map +1 -1
- package/dist/dashboard/dashboard-plugin.js +2 -0
- package/dist/dashboard/dashboard-plugin.js.map +1 -1
- package/dist/data-plugin.js +1 -0
- package/dist/data-plugin.js.map +1 -1
- package/dist/email-notification-plugin.js +9 -13
- package/dist/email-notification-plugin.js.map +1 -1
- package/dist/flow/constants/flow-plugin-constants.d.ts +1 -1
- package/dist/flow/constants/flow-plugin-constants.js +1 -1
- package/dist/flow/constants/flow-plugin-constants.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.d.ts +82 -2
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.js +48 -40
- package/dist/flow/flow-logic/flow-logic-plugin-helpers.js.map +1 -1
- package/dist/flow/flow-logic/flow-logic-plugin.js +1 -0
- package/dist/flow/flow-logic/flow-logic-plugin.js.map +1 -1
- package/dist/flow/plugins/approval-rules-plugin.js +1 -0
- package/dist/flow/plugins/approval-rules-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-action-definition-plugin.js +4 -2
- package/dist/flow/plugins/flow-action-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-data-pill-plugin.js +1 -0
- package/dist/flow/plugins/flow-data-pill-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-definition-plugin.js +8 -3
- package/dist/flow/plugins/flow-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-diagnostics-plugin.js +1 -0
- package/dist/flow/plugins/flow-diagnostics-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-instance-plugin.js +68 -12
- package/dist/flow/plugins/flow-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/flow-trigger-instance-plugin.js +1 -0
- package/dist/flow/plugins/flow-trigger-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/inline-script-plugin.js +1 -0
- package/dist/flow/plugins/inline-script-plugin.js.map +1 -1
- package/dist/flow/plugins/step-definition-plugin.js +3 -2
- package/dist/flow/plugins/step-definition-plugin.js.map +1 -1
- package/dist/flow/plugins/step-instance-plugin.js +1 -0
- package/dist/flow/plugins/step-instance-plugin.js.map +1 -1
- package/dist/flow/plugins/trigger-plugin.js +2 -0
- package/dist/flow/plugins/trigger-plugin.js.map +1 -1
- package/dist/flow/plugins/wfa-datapill-plugin.js +1 -0
- package/dist/flow/plugins/wfa-datapill-plugin.js.map +1 -1
- package/dist/flow/post-install.d.ts +2 -0
- package/dist/flow/post-install.js +58 -0
- package/dist/flow/post-install.js.map +1 -0
- package/dist/flow/utils/complex-objects.js +4 -2
- package/dist/flow/utils/complex-objects.js.map +1 -1
- package/dist/flow/utils/flow-constants.d.ts +24 -0
- package/dist/flow/utils/flow-constants.js +29 -2
- package/dist/flow/utils/flow-constants.js.map +1 -1
- package/dist/flow/utils/flow-to-xml.d.ts +3 -2
- package/dist/flow/utils/flow-to-xml.js +3 -4
- package/dist/flow/utils/flow-to-xml.js.map +1 -1
- package/dist/flow/utils/label-cache-processor.d.ts +5 -0
- package/dist/flow/utils/label-cache-processor.js +14 -2
- package/dist/flow/utils/label-cache-processor.js.map +1 -1
- package/dist/flow/utils/service-catalog.js +5 -1
- package/dist/flow/utils/service-catalog.js.map +1 -1
- package/dist/form-plugin.d.ts +2 -0
- package/dist/form-plugin.js +1134 -0
- package/dist/form-plugin.js.map +1 -0
- package/dist/html-import-plugin.js +1 -0
- package/dist/html-import-plugin.js.map +1 -1
- package/dist/import-sets-plugin.js +2 -0
- package/dist/import-sets-plugin.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/instance-scan-plugin.d.ts +2 -0
- package/dist/instance-scan-plugin.js +298 -0
- package/dist/instance-scan-plugin.js.map +1 -0
- package/dist/json-plugin.js +1 -0
- package/dist/json-plugin.js.map +1 -1
- package/dist/list-plugin.js +1 -0
- package/dist/list-plugin.js.map +1 -1
- package/dist/now-attach-plugin.js +1 -0
- package/dist/now-attach-plugin.js.map +1 -1
- package/dist/now-config-plugin.js +659 -51
- package/dist/now-config-plugin.js.map +1 -1
- package/dist/now-id-plugin.js +1 -0
- package/dist/now-id-plugin.js.map +1 -1
- package/dist/now-include-plugin.js +1 -0
- package/dist/now-include-plugin.js.map +1 -1
- package/dist/now-ref-plugin.js +1 -0
- package/dist/now-ref-plugin.js.map +1 -1
- package/dist/now-unresolved-plugin.js +1 -0
- package/dist/now-unresolved-plugin.js.map +1 -1
- package/dist/package-json-plugin.js +1 -0
- package/dist/package-json-plugin.js.map +1 -1
- package/dist/property-plugin.js +3 -1
- package/dist/property-plugin.js.map +1 -1
- package/dist/record-plugin.d.ts +30 -0
- package/dist/record-plugin.js +37 -1
- package/dist/record-plugin.js.map +1 -1
- package/dist/repack/lint/Rules.d.ts +11 -2
- package/dist/repack/lint/Rules.js +160 -16
- package/dist/repack/lint/Rules.js.map +1 -1
- package/dist/repack/lint/index.d.ts +10 -5
- package/dist/repack/lint/index.js +76 -50
- package/dist/repack/lint/index.js.map +1 -1
- package/dist/rest-api-plugin.js +14 -0
- package/dist/rest-api-plugin.js.map +1 -1
- package/dist/role-plugin.js +1 -0
- package/dist/role-plugin.js.map +1 -1
- package/dist/schedule-script/index.d.ts +1 -0
- package/dist/schedule-script/index.js +18 -0
- package/dist/schedule-script/index.js.map +1 -0
- package/dist/schedule-script/scheduled-script-plugin.d.ts +2 -0
- package/dist/schedule-script/scheduled-script-plugin.js +551 -0
- package/dist/schedule-script/scheduled-script-plugin.js.map +1 -0
- package/dist/schedule-script/timeZoneConverter.d.ts +61 -0
- package/dist/schedule-script/timeZoneConverter.js +170 -0
- package/dist/schedule-script/timeZoneConverter.js.map +1 -0
- package/dist/script-action-plugin.js +2 -0
- package/dist/script-action-plugin.js.map +1 -1
- package/dist/script-include-plugin.js +2 -0
- package/dist/script-include-plugin.js.map +1 -1
- package/dist/server-module-plugin/index.js +13 -2
- package/dist/server-module-plugin/index.js.map +1 -1
- package/dist/service-catalog/catalog-clientscript-plugin.js +2 -0
- package/dist/service-catalog/catalog-clientscript-plugin.js.map +1 -1
- package/dist/service-catalog/catalog-item-plugin.js +2 -0
- package/dist/service-catalog/catalog-item-plugin.js.map +1 -1
- package/dist/service-catalog/catalog-ui-policy-plugin.js +2 -0
- package/dist/service-catalog/catalog-ui-policy-plugin.js.map +1 -1
- package/dist/service-catalog/sc-record-producer-plugin.js +2 -0
- package/dist/service-catalog/sc-record-producer-plugin.js.map +1 -1
- package/dist/service-catalog/service-catalog-diagnostics.d.ts +6 -0
- package/dist/service-catalog/service-catalog-diagnostics.js +20 -0
- package/dist/service-catalog/service-catalog-diagnostics.js.map +1 -1
- package/dist/service-catalog/shape-to-record.js +7 -2
- package/dist/service-catalog/shape-to-record.js.map +1 -1
- package/dist/service-catalog/variable-set-plugin.js +2 -0
- package/dist/service-catalog/variable-set-plugin.js.map +1 -1
- package/dist/service-portal/angular-provider-plugin.js +2 -0
- package/dist/service-portal/angular-provider-plugin.js.map +1 -1
- package/dist/service-portal/dependency-plugin.js +5 -31
- package/dist/service-portal/dependency-plugin.js.map +1 -1
- package/dist/service-portal/menu-plugin.d.ts +2 -0
- package/dist/service-portal/menu-plugin.js +353 -0
- package/dist/service-portal/menu-plugin.js.map +1 -0
- package/dist/service-portal/page-plugin.d.ts +2 -0
- package/dist/service-portal/page-plugin.js +702 -0
- package/dist/service-portal/page-plugin.js.map +1 -0
- package/dist/service-portal/portal-plugin.d.ts +2 -0
- package/dist/service-portal/portal-plugin.js +296 -0
- package/dist/service-portal/portal-plugin.js.map +1 -0
- package/dist/service-portal/theme-plugin.d.ts +2 -0
- package/dist/service-portal/theme-plugin.js +112 -0
- package/dist/service-portal/theme-plugin.js.map +1 -0
- package/dist/service-portal/utils.d.ts +8 -0
- package/dist/service-portal/utils.js +50 -0
- package/dist/service-portal/utils.js.map +1 -0
- package/dist/service-portal/widget-plugin.js +45 -8
- package/dist/service-portal/widget-plugin.js.map +1 -1
- package/dist/sla-plugin.js +2 -0
- package/dist/sla-plugin.js.map +1 -1
- package/dist/static-content-plugin.js +1 -0
- package/dist/static-content-plugin.js.map +1 -1
- package/dist/table-plugin.js +1 -0
- package/dist/table-plugin.js.map +1 -1
- package/dist/ui-action-plugin.js +2 -0
- package/dist/ui-action-plugin.js.map +1 -1
- package/dist/ui-page-plugin.js +33 -8
- package/dist/ui-page-plugin.js.map +1 -1
- package/dist/ui-policy-plugin.js +1 -0
- package/dist/ui-policy-plugin.js.map +1 -1
- package/dist/user-preference-plugin.js +2 -0
- package/dist/user-preference-plugin.js.map +1 -1
- package/dist/utils.d.ts +20 -2
- package/dist/utils.js +34 -3
- package/dist/utils.js.map +1 -1
- package/dist/ux-list-menu-config-plugin.js +2 -0
- package/dist/ux-list-menu-config-plugin.js.map +1 -1
- package/dist/view-plugin.js +1 -0
- package/dist/view-plugin.js.map +1 -1
- package/dist/workspace-plugin.js +2 -0
- package/dist/workspace-plugin.js.map +1 -1
- package/package.json +10 -11
- package/src/_types/eslint-community-eslint-utils.d.ts +15 -0
- package/src/acl-plugin.ts +97 -8
- package/src/applicability-plugin.ts +2 -0
- package/src/application-menu-plugin.ts +2 -0
- package/src/arrow-function-plugin.ts +128 -13
- package/src/atf/test-plugin.ts +2 -0
- package/src/basic-syntax-plugin.ts +21 -0
- package/src/call-expression-plugin.ts +1 -0
- package/src/claims-plugin.ts +1 -0
- package/src/client-script-plugin.ts +2 -1
- package/src/column-plugin.ts +1 -0
- package/src/cross-scope-privilege-plugin.ts +2 -1
- package/src/dashboard/dashboard-plugin.ts +2 -0
- package/src/data-plugin.ts +1 -0
- package/src/email-notification-plugin.ts +3 -23
- package/src/flow/constants/flow-plugin-constants.ts +1 -1
- package/src/flow/flow-logic/flow-logic-plugin-helpers.ts +47 -45
- package/src/flow/flow-logic/flow-logic-plugin.ts +1 -0
- package/src/flow/plugins/approval-rules-plugin.ts +1 -0
- package/src/flow/plugins/flow-action-definition-plugin.ts +4 -2
- package/src/flow/plugins/flow-data-pill-plugin.ts +1 -0
- package/src/flow/plugins/flow-definition-plugin.ts +10 -4
- package/src/flow/plugins/flow-diagnostics-plugin.ts +1 -0
- package/src/flow/plugins/flow-instance-plugin.ts +103 -14
- package/src/flow/plugins/flow-trigger-instance-plugin.ts +1 -0
- package/src/flow/plugins/inline-script-plugin.ts +1 -0
- package/src/flow/plugins/step-definition-plugin.ts +3 -2
- package/src/flow/plugins/step-instance-plugin.ts +1 -0
- package/src/flow/plugins/trigger-plugin.ts +2 -0
- package/src/flow/plugins/wfa-datapill-plugin.ts +1 -0
- package/src/flow/post-install.ts +92 -0
- package/src/flow/utils/complex-objects.ts +10 -2
- package/src/flow/utils/flow-constants.ts +30 -1
- package/src/flow/utils/flow-to-xml.ts +4 -4
- package/src/flow/utils/label-cache-processor.ts +14 -2
- package/src/flow/utils/service-catalog.ts +5 -2
- package/src/form-plugin.ts +1411 -0
- package/src/html-import-plugin.ts +1 -0
- package/src/import-sets-plugin.ts +2 -0
- package/src/index.ts +9 -0
- package/src/instance-scan-plugin.ts +318 -0
- package/src/json-plugin.ts +1 -0
- package/src/list-plugin.ts +2 -1
- package/src/now-attach-plugin.ts +1 -0
- package/src/now-config-plugin.ts +833 -53
- package/src/now-id-plugin.ts +1 -0
- package/src/now-include-plugin.ts +1 -0
- package/src/now-ref-plugin.ts +1 -0
- package/src/now-unresolved-plugin.ts +1 -0
- package/src/package-json-plugin.ts +1 -0
- package/src/property-plugin.ts +3 -1
- package/src/record-plugin.ts +42 -2
- package/src/repack/lint/Rules.ts +171 -22
- package/src/repack/lint/index.ts +80 -56
- package/src/rest-api-plugin.ts +21 -1
- package/src/role-plugin.ts +2 -1
- package/src/schedule-script/index.ts +1 -0
- package/src/schedule-script/scheduled-script-plugin.ts +679 -0
- package/src/schedule-script/timeZoneConverter.ts +188 -0
- package/src/script-action-plugin.ts +2 -0
- package/src/script-include-plugin.ts +2 -0
- package/src/server-module-plugin/index.ts +14 -2
- package/src/service-catalog/catalog-clientscript-plugin.ts +2 -0
- package/src/service-catalog/catalog-item-plugin.ts +2 -0
- package/src/service-catalog/catalog-ui-policy-plugin.ts +2 -0
- package/src/service-catalog/sc-record-producer-plugin.ts +2 -0
- package/src/service-catalog/service-catalog-diagnostics.ts +30 -0
- package/src/service-catalog/shape-to-record.ts +8 -2
- package/src/service-catalog/variable-set-plugin.ts +2 -0
- package/src/service-portal/angular-provider-plugin.ts +2 -0
- package/src/service-portal/dependency-plugin.ts +6 -53
- package/src/service-portal/menu-plugin.ts +435 -0
- package/src/service-portal/page-plugin.ts +830 -0
- package/src/service-portal/portal-plugin.ts +319 -0
- package/src/service-portal/theme-plugin.ts +135 -0
- package/src/service-portal/utils.ts +69 -0
- package/src/service-portal/widget-plugin.ts +79 -9
- package/src/sla-plugin.ts +2 -0
- package/src/static-content-plugin.ts +1 -0
- package/src/table-plugin.ts +2 -1
- package/src/ui-action-plugin.ts +2 -0
- package/src/ui-page-plugin.ts +34 -8
- package/src/ui-policy-plugin.ts +2 -1
- package/src/user-preference-plugin.ts +2 -0
- package/src/utils.ts +42 -2
- package/src/ux-list-menu-config-plugin.ts +2 -0
- package/src/view-plugin.ts +1 -0
- package/src/workspace-plugin.ts +2 -0
- package/src/_types/eslint-plugin-es-x.d.ts +0 -17
package/src/now-config-plugin.ts
CHANGED
|
@@ -1,11 +1,455 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
NowConfig,
|
|
3
|
+
ObjectShape,
|
|
4
|
+
MODULE_RESOLUTION,
|
|
5
|
+
Plugin,
|
|
6
|
+
Shape,
|
|
7
|
+
type Record as SdkRecord,
|
|
8
|
+
type Factory,
|
|
9
|
+
type Diagnostics,
|
|
10
|
+
} from '@servicenow/sdk-build-core'
|
|
2
11
|
|
|
3
12
|
import { JsonFileShape } from './json-plugin'
|
|
4
13
|
|
|
5
14
|
export class NowConfigShape extends ObjectShape {}
|
|
6
15
|
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Application Runtime Policy Helper Functions
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Maps Fluent mode values to ServiceNow XML state values for performance policy
|
|
22
|
+
*/
|
|
23
|
+
const PERFORMANCE_MODE_MAP = {
|
|
24
|
+
disabled: 'disabled',
|
|
25
|
+
enforced: 'enforced',
|
|
26
|
+
logOnly: 'log_only',
|
|
27
|
+
} as const satisfies Record<string, string>
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Reverse mapping: ServiceNow XML state values to Fluent mode values
|
|
31
|
+
*/
|
|
32
|
+
const PERFORMANCE_MODE_REVERSE_MAP = {
|
|
33
|
+
disabled: 'disabled',
|
|
34
|
+
enforced: 'enforced',
|
|
35
|
+
log_only: 'logOnly',
|
|
36
|
+
} as const satisfies Record<string, string>
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Maps applicationRuntimePolicy to performance policy mode
|
|
40
|
+
*/
|
|
41
|
+
const ARP_TO_PERFORMANCE_POLICY_MODE_MAP = {
|
|
42
|
+
none: 'disabled',
|
|
43
|
+
tracking: 'log_only',
|
|
44
|
+
enforcing: 'enforced',
|
|
45
|
+
} as const satisfies Record<string, string>
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Maps Application Runtime Policy values to required Runtime Access Tracking values
|
|
49
|
+
* - none: RAT can be anything (no restriction)
|
|
50
|
+
* - tracking: RAT must be permissive
|
|
51
|
+
* - enforcing: RAT must be enforcing
|
|
52
|
+
*/
|
|
53
|
+
const ARP_TO_RAT_MAP = {
|
|
54
|
+
none: null, // No restriction - RAT can be any value
|
|
55
|
+
tracking: 'permissive',
|
|
56
|
+
enforcing: 'enforcing',
|
|
57
|
+
} as const satisfies Record<string, string | null>
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Network Policy Validation Functions
|
|
61
|
+
// ============================================================================
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Validates host field format and content
|
|
65
|
+
* Uses URL API for parsing with manual wildcard validation (URL can't handle wildcards)
|
|
66
|
+
* Matches platform business rule validation logic
|
|
67
|
+
*/
|
|
68
|
+
function validateHost(host: string | undefined, policyType: string, diagnostics: Diagnostics, source: Shape): boolean {
|
|
69
|
+
if (!host || !host.trim()) {
|
|
70
|
+
return true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const hostValue = host.trim()
|
|
74
|
+
|
|
75
|
+
// Use URL API for scheme/port parsing — replace wildcards with placeholder since URL can't handle them
|
|
76
|
+
const parseableUrl = hostValue.replace(/\*/g, 'wildcard-placeholder')
|
|
77
|
+
let url: URL
|
|
78
|
+
try {
|
|
79
|
+
url = new URL(parseableUrl)
|
|
80
|
+
} catch {
|
|
81
|
+
// URL throws for invalid ports (>65535) among other reasons
|
|
82
|
+
// Check if the host has an out-of-range port and diagnose it
|
|
83
|
+
const portMatch = hostValue.match(/:(\d+)\s*$/)
|
|
84
|
+
if (portMatch) {
|
|
85
|
+
const port = parseInt(portMatch[1]!, 10)
|
|
86
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
87
|
+
diagnostics.error(source, `Invalid port "${portMatch[1]}". Must be 1-65535.`)
|
|
88
|
+
return false
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Zod regex handles basic format validation; if URL can't parse for other reasons, skip
|
|
92
|
+
return true
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const scheme = url.protocol.slice(0, -1) // Remove trailing ':'
|
|
96
|
+
|
|
97
|
+
// Validate scheme based on policy type
|
|
98
|
+
// Platform uses system properties to configure allowed schemes,
|
|
99
|
+
// but we use hardcoded defaults here for build-time validation
|
|
100
|
+
const allowedSchemes = getValidSchemes(policyType)
|
|
101
|
+
if (!allowedSchemes.includes(scheme)) {
|
|
102
|
+
diagnostics.error(
|
|
103
|
+
source,
|
|
104
|
+
`Invalid URL scheme "${scheme}" for policy type "${policyType}". Allowed schemes: ${allowedSchemes.join(', ')}`
|
|
105
|
+
)
|
|
106
|
+
return false
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Validate wildcards on original hostname (not the placeholder)
|
|
110
|
+
const originalHostname = url.hostname.replace(/wildcard-placeholder/g, '*')
|
|
111
|
+
if (!validateWildcards(originalHostname, diagnostics, source)) {
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Validate port range using URL's parsed port
|
|
116
|
+
if (url.port) {
|
|
117
|
+
const port = parseInt(url.port, 10)
|
|
118
|
+
if (port < 1 || port > 65535) {
|
|
119
|
+
diagnostics.error(source, `Invalid port "${url.port}". Must be 1-65535.`)
|
|
120
|
+
return false
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return true
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Gets valid URL schemes based on policy type
|
|
129
|
+
* Matches platform business rule defaults for glide.arp.csp.allowed.url.schemes
|
|
130
|
+
* and glide.arp.server.outbound.allowed.url.schemes
|
|
131
|
+
*/
|
|
132
|
+
function getValidSchemes(policyType: string): string[] {
|
|
133
|
+
// CSP script-src only allows HTTPS
|
|
134
|
+
if (policyType === 'csp_script_src') {
|
|
135
|
+
return ['https']
|
|
136
|
+
}
|
|
137
|
+
// CSP connect-src allows HTTPS and WSS
|
|
138
|
+
if (policyType === 'csp_connect_src') {
|
|
139
|
+
return ['https', 'wss']
|
|
140
|
+
}
|
|
141
|
+
// Server outbound allows all four protocols
|
|
142
|
+
if (policyType === 'now_outbound') {
|
|
143
|
+
return ['http', 'https', 'ws', 'wss']
|
|
144
|
+
}
|
|
145
|
+
// For inbound policies, host should be empty, but if provided, use https
|
|
146
|
+
return ['https']
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Validates wildcard patterns in hostname
|
|
151
|
+
*/
|
|
152
|
+
function validateWildcards(hostPart: string, diagnostics: Diagnostics, source: Shape): boolean {
|
|
153
|
+
if (hostPart === '*') {
|
|
154
|
+
diagnostics.error(source, 'Single wildcard "*" not allowed. Use "*.example.com" format.')
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (hostPart.indexOf('*') === -1) {
|
|
159
|
+
return true
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const parts = hostPart.split('.')
|
|
163
|
+
let wildcardCount = 0
|
|
164
|
+
|
|
165
|
+
for (let i = 0; i < parts.length; i++) {
|
|
166
|
+
const part = parts[i]!
|
|
167
|
+
if (part === '*') {
|
|
168
|
+
wildcardCount++
|
|
169
|
+
if (i !== 0 || wildcardCount > 1) {
|
|
170
|
+
diagnostics.error(source, `Wildcard must be first segment only. Invalid: "${hostPart}"`)
|
|
171
|
+
return false
|
|
172
|
+
}
|
|
173
|
+
} else if (part.indexOf('*') !== -1) {
|
|
174
|
+
diagnostics.error(source, `Partial wildcards not allowed: "${part}"`)
|
|
175
|
+
return false
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return true
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Validates path field format
|
|
184
|
+
* Each path in the array must start with single forward slash
|
|
185
|
+
*/
|
|
186
|
+
function validatePath(path: string[] | undefined, diagnostics: Diagnostics, source: Shape): boolean {
|
|
187
|
+
if (!path || path.length === 0) {
|
|
188
|
+
return true
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
for (const singlePath of path) {
|
|
192
|
+
const trimmedPath = singlePath.trim()
|
|
193
|
+
if (!trimmedPath.startsWith('/') || trimmedPath.startsWith('//')) {
|
|
194
|
+
diagnostics.error(source, `Path must start with single "/": "${trimmedPath}"`)
|
|
195
|
+
return false
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return true
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Validates network policy fields for cross-field and business rules
|
|
204
|
+
*/
|
|
205
|
+
function validateNetworkPolicy(
|
|
206
|
+
policy: { policyType?: unknown; host?: unknown; path?: unknown },
|
|
207
|
+
diagnostics: Diagnostics,
|
|
208
|
+
source: Shape
|
|
209
|
+
): boolean {
|
|
210
|
+
let isValid = true
|
|
211
|
+
|
|
212
|
+
const policyType = typeof policy.policyType === 'string' ? policy.policyType : ''
|
|
213
|
+
const host = typeof policy.host === 'string' ? policy.host : undefined
|
|
214
|
+
const path = Array.isArray(policy.path) ? policy.path : undefined
|
|
215
|
+
|
|
216
|
+
if (host && !validateHost(host, policyType, diagnostics, source)) {
|
|
217
|
+
isValid = false
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (path && !validatePath(path, diagnostics, source)) {
|
|
221
|
+
isValid = false
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return isValid
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Creates network policy records from config
|
|
229
|
+
*/
|
|
230
|
+
async function createNetworkPolicyRecords(
|
|
231
|
+
networkPolicies: Shape[],
|
|
232
|
+
factory: Factory,
|
|
233
|
+
source: Shape,
|
|
234
|
+
diagnostics: Diagnostics
|
|
235
|
+
): Promise<SdkRecord[]> {
|
|
236
|
+
const records: SdkRecord[] = []
|
|
237
|
+
|
|
238
|
+
for (const policy of networkPolicies) {
|
|
239
|
+
const policyObj = policy.asObject()
|
|
240
|
+
|
|
241
|
+
// Validate policy fields (complex validations that can't be done in schema)
|
|
242
|
+
const policyData = {
|
|
243
|
+
policyType: policyObj.get('policyType')?.getValue(),
|
|
244
|
+
host: policyObj.get('host')?.getValue(),
|
|
245
|
+
path: policyObj.get('path')?.getValue(),
|
|
246
|
+
}
|
|
247
|
+
validateNetworkPolicy(policyData, diagnostics, policy)
|
|
248
|
+
|
|
249
|
+
const record = await factory.createRecord({
|
|
250
|
+
source: source,
|
|
251
|
+
table: 'sys_arp_network_policy',
|
|
252
|
+
explicitId: policyObj.get('$id'),
|
|
253
|
+
properties: policyObj.transform(({ $ }) => ({
|
|
254
|
+
active: $.def(true),
|
|
255
|
+
policy_type: $.from('policyType'),
|
|
256
|
+
status: $,
|
|
257
|
+
host: $,
|
|
258
|
+
scheme: $,
|
|
259
|
+
// Join path array with newlines for XML storage
|
|
260
|
+
path: $.from('path').map(
|
|
261
|
+
(pathShape) =>
|
|
262
|
+
pathShape
|
|
263
|
+
.ifArray()
|
|
264
|
+
?.getElements()
|
|
265
|
+
.map((e: Shape) => e.getValue())
|
|
266
|
+
.join('\n') ?? ''
|
|
267
|
+
),
|
|
268
|
+
resource: $,
|
|
269
|
+
short_description: $.from('shortDescription'),
|
|
270
|
+
})),
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
records.push(record)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return records
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Creates wildcard policy record from config
|
|
281
|
+
*/
|
|
282
|
+
async function createWildcardPolicyRecord(
|
|
283
|
+
wildcardPolicy: ObjectShape,
|
|
284
|
+
factory: Factory,
|
|
285
|
+
source: Shape,
|
|
286
|
+
scopeId: string,
|
|
287
|
+
diagnostics: Diagnostics
|
|
288
|
+
): Promise<SdkRecord> {
|
|
289
|
+
// Extract nested pillar objects
|
|
290
|
+
const networkPillar = wildcardPolicy.get('network')?.ifDefined()?.asObject()
|
|
291
|
+
const scriptingPillar = wildcardPolicy.get('scripting')?.ifDefined()?.asObject()
|
|
292
|
+
const arlPillar = wildcardPolicy.get('arl')?.ifDefined()?.asObject()
|
|
293
|
+
|
|
294
|
+
// Validate pillar active/wildcard consistency
|
|
295
|
+
const networkActive = networkPillar?.get('active')?.toBoolean().getValue() ?? false
|
|
296
|
+
const networkWildcardShape = networkPillar?.get('networkWildcard')?.ifArray()
|
|
297
|
+
const networkWildcard = networkWildcardShape?.getElements()
|
|
298
|
+
if (!networkActive && networkWildcard && networkWildcard.length > 0) {
|
|
299
|
+
diagnostics.warn(networkWildcardShape!, 'networkWildcard values will be ignored when network.active is false')
|
|
300
|
+
}
|
|
301
|
+
if (networkActive && (!networkWildcard || networkWildcard.length === 0)) {
|
|
302
|
+
diagnostics.hint(
|
|
303
|
+
networkPillar!.get('active'),
|
|
304
|
+
'Network pillar is active but has no networkWildcard selections. Consider adding networkWildcard values or setting active to false.'
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const scriptingActive = scriptingPillar?.get('active')?.toBoolean().getValue() ?? false
|
|
309
|
+
const scriptingWildcardShape = scriptingPillar?.get('scriptingWildcard')?.ifArray()
|
|
310
|
+
const scriptingWildcard = scriptingWildcardShape?.getElements()
|
|
311
|
+
if (!scriptingActive && scriptingWildcard && scriptingWildcard.length > 0) {
|
|
312
|
+
diagnostics.warn(
|
|
313
|
+
scriptingWildcardShape!,
|
|
314
|
+
'scriptingWildcard values will be ignored when scripting.active is false'
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
if (scriptingActive && (!scriptingWildcard || scriptingWildcard.length === 0)) {
|
|
318
|
+
diagnostics.hint(
|
|
319
|
+
scriptingPillar!.get('active'),
|
|
320
|
+
'Scripting pillar is active but has no scriptingWildcard selections. Consider adding scriptingWildcard values or setting active to false.'
|
|
321
|
+
)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const arlActive = arlPillar?.get('active')?.toBoolean().getValue() ?? false
|
|
325
|
+
const arlWildcardShape = arlPillar?.get('arlWildcard')?.ifArray()
|
|
326
|
+
const arlWildcard = arlWildcardShape?.getElements()
|
|
327
|
+
if (!arlActive && arlWildcard && arlWildcard.length > 0) {
|
|
328
|
+
diagnostics.warn(arlWildcardShape!, 'arlWildcard values will be ignored when arl.active is false')
|
|
329
|
+
}
|
|
330
|
+
if (arlActive && (!arlWildcard || arlWildcard.length === 0)) {
|
|
331
|
+
diagnostics.hint(
|
|
332
|
+
arlPillar!.get('active'),
|
|
333
|
+
'ARL pillar is active but has no arlWildcard selections. Consider adding arlWildcard values or setting active to false.'
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return await factory.createRecord({
|
|
338
|
+
source: source,
|
|
339
|
+
table: 'sys_arp_segment_policy',
|
|
340
|
+
explicitId: wildcardPolicy.get('$id'),
|
|
341
|
+
properties: wildcardPolicy.transform(({ $ }) => ({
|
|
342
|
+
sys_scope: $.val(scopeId),
|
|
343
|
+
active: $.def(false),
|
|
344
|
+
short_description: $.from('shortDescription').def(''),
|
|
345
|
+
arp_record: $.from('record').toBoolean().def(false),
|
|
346
|
+
// Network pillar
|
|
347
|
+
arp_network: $.val(networkPillar?.get('active')?.toBoolean().getValue() ?? false),
|
|
348
|
+
arp_network_wildcard: $.val(
|
|
349
|
+
networkPillar
|
|
350
|
+
?.get('networkWildcard')
|
|
351
|
+
?.ifArray()
|
|
352
|
+
?.getElements()
|
|
353
|
+
.map((e) => e.getValue())
|
|
354
|
+
.join(',') ?? ''
|
|
355
|
+
),
|
|
356
|
+
// Scripting pillar
|
|
357
|
+
arp_script: $.val(scriptingPillar?.get('active')?.toBoolean().getValue() ?? false),
|
|
358
|
+
arp_script_wildcard: $.val(
|
|
359
|
+
scriptingPillar
|
|
360
|
+
?.get('scriptingWildcard')
|
|
361
|
+
?.ifArray()
|
|
362
|
+
?.getElements()
|
|
363
|
+
.map((e) => e.getValue())
|
|
364
|
+
.join(',') ?? ''
|
|
365
|
+
),
|
|
366
|
+
// ARL pillar
|
|
367
|
+
arp_arl: $.val(arlPillar?.get('active')?.toBoolean().getValue() ?? false),
|
|
368
|
+
arp_arl_wildcard: $.val(
|
|
369
|
+
arlPillar
|
|
370
|
+
?.get('arlWildcard')
|
|
371
|
+
?.ifArray()
|
|
372
|
+
?.getElements()
|
|
373
|
+
.map((e) => e.getValue())
|
|
374
|
+
.join(',') ?? ''
|
|
375
|
+
),
|
|
376
|
+
})),
|
|
377
|
+
})
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Creates performance policy record from config
|
|
382
|
+
* Mode is auto-derived from applicationRuntimePolicy if not explicitly set
|
|
383
|
+
*/
|
|
384
|
+
async function createPerformancePolicyRecord(
|
|
385
|
+
performancePolicy: ObjectShape,
|
|
386
|
+
factory: Factory,
|
|
387
|
+
source: Shape,
|
|
388
|
+
scopeId: string,
|
|
389
|
+
applicationRuntimePolicy: string,
|
|
390
|
+
diagnostics: Diagnostics
|
|
391
|
+
): Promise<SdkRecord> {
|
|
392
|
+
// Derive state from applicationRuntimePolicy
|
|
393
|
+
const derivedMode =
|
|
394
|
+
ARP_TO_PERFORMANCE_POLICY_MODE_MAP[
|
|
395
|
+
applicationRuntimePolicy as keyof typeof ARP_TO_PERFORMANCE_POLICY_MODE_MAP
|
|
396
|
+
] ?? 'log_only'
|
|
397
|
+
|
|
398
|
+
// Check if user explicitly set mode and it differs from auto-derived value
|
|
399
|
+
const explicitModeShape = performancePolicy.get('mode').ifDefined()
|
|
400
|
+
if (explicitModeShape) {
|
|
401
|
+
const explicitMode = explicitModeShape.toString().getValue()
|
|
402
|
+
const explicitModeInXml = PERFORMANCE_MODE_MAP[explicitMode as keyof typeof PERFORMANCE_MODE_MAP]
|
|
403
|
+
if (explicitModeInXml && explicitModeInXml !== derivedMode) {
|
|
404
|
+
const derivedModeInFluent = Object.entries(PERFORMANCE_MODE_MAP).find(
|
|
405
|
+
([_, xml]) => xml === derivedMode
|
|
406
|
+
)?.[0]
|
|
407
|
+
diagnostics.warn(
|
|
408
|
+
explicitModeShape,
|
|
409
|
+
`Performance policy mode '${explicitMode}' differs from auto-derived mode '${derivedModeInFluent}' based on applicationRuntimePolicy='${applicationRuntimePolicy}'. The explicit mode will be used.`
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return await factory.createRecord({
|
|
415
|
+
source: source,
|
|
416
|
+
table: 'sys_app_resource_limit_template',
|
|
417
|
+
explicitId: performancePolicy.get('$id'),
|
|
418
|
+
properties: performancePolicy.transform(({ $ }) => ({
|
|
419
|
+
template_name: $.from('name'),
|
|
420
|
+
// Auto-derive appCondition to query current scope
|
|
421
|
+
app_condition: $.val(`sys_id=${scopeId}`),
|
|
422
|
+
scheduled_job_limit: $.from('scheduledJobLimit').def(20),
|
|
423
|
+
event_handler_limit: $.from('eventHandlerLimit').def(20),
|
|
424
|
+
api_transaction_limit: $.from('apiTransactionLimit').def(30),
|
|
425
|
+
interactive_transaction_limit: $.from('interactiveTransactionLimit').def(30),
|
|
426
|
+
// State is auto-derived from applicationRuntimePolicy unless explicitly overridden
|
|
427
|
+
state: $.from('mode')
|
|
428
|
+
.map((v) => {
|
|
429
|
+
const explicitMode = v.getValue() as string
|
|
430
|
+
const mappedMode = PERFORMANCE_MODE_MAP[explicitMode as keyof typeof PERFORMANCE_MODE_MAP]
|
|
431
|
+
if (explicitMode && !mappedMode) {
|
|
432
|
+
// Invalid mode value - should have been caught by schema validation
|
|
433
|
+
// but add a diagnostic warning just in case
|
|
434
|
+
diagnostics.warn(
|
|
435
|
+
v,
|
|
436
|
+
`Invalid mode value '${explicitMode}'. Valid values are: disabled, enforced, logOnly. Using auto-derived mode instead.`
|
|
437
|
+
)
|
|
438
|
+
}
|
|
439
|
+
return mappedMode ?? derivedMode
|
|
440
|
+
})
|
|
441
|
+
.def(derivedMode),
|
|
442
|
+
enable_auto_gen_limits: $.val(true), // Always enabled, hidden from user
|
|
443
|
+
order: $.val(100), // Default order, hidden from user
|
|
444
|
+
})),
|
|
445
|
+
})
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ============================================================================
|
|
449
|
+
|
|
7
450
|
export const NowConfigPlugin = Plugin.create({
|
|
8
451
|
name: 'NowConfigPlugin',
|
|
452
|
+
docs: [],
|
|
9
453
|
files: [
|
|
10
454
|
{
|
|
11
455
|
matcher: /\Wnow\.config\.json$/,
|
|
@@ -14,15 +458,243 @@ export const NowConfigPlugin = Plugin.create({
|
|
|
14
458
|
],
|
|
15
459
|
records: {
|
|
16
460
|
sys_app: {
|
|
17
|
-
|
|
461
|
+
relationships: {
|
|
462
|
+
sys_arp_network_policy: {
|
|
463
|
+
via: 'sys_scope',
|
|
464
|
+
descendant: true,
|
|
465
|
+
},
|
|
466
|
+
sys_arp_segment_policy: {
|
|
467
|
+
via: 'sys_scope',
|
|
468
|
+
descendant: true,
|
|
469
|
+
},
|
|
470
|
+
sys_app_resource_limit_template: {
|
|
471
|
+
via: 'sys_scope',
|
|
472
|
+
descendant: true,
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
toShape(record, { packageJson, config, database }) {
|
|
18
476
|
const { name: packageName } = packageJson
|
|
477
|
+
|
|
478
|
+
// Query policy records directly from database
|
|
479
|
+
// During transform, the database only contains records from the current scope's XML
|
|
480
|
+
const networkPolicyRecords = database.query('sys_arp_network_policy')
|
|
481
|
+
const segmentPolicyRecords = database.query('sys_arp_segment_policy')
|
|
482
|
+
const resourceLimitRecords = database.query('sys_app_resource_limit_template')
|
|
483
|
+
|
|
484
|
+
// Transform network policies (exclude default values)
|
|
485
|
+
const networkPolicies = networkPolicyRecords.map((policyRecord) => {
|
|
486
|
+
const policyType = policyRecord.get('policy_type').toString().getValue()
|
|
487
|
+
const result: Record<string, unknown> = {
|
|
488
|
+
$id: policyRecord.getId().getValue(),
|
|
489
|
+
policyType: policyType,
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Only include active if not default (true)
|
|
493
|
+
const active = policyRecord.get('active').toBoolean().getValue()
|
|
494
|
+
if (active !== true) {
|
|
495
|
+
result['active'] = active
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Status is required - always include it (default to 'requested' if missing for backwards compatibility)
|
|
499
|
+
const status = policyRecord.get('status').toString().getValue() || 'requested'
|
|
500
|
+
result['status'] = status
|
|
501
|
+
|
|
502
|
+
const host = policyRecord.get('host').ifDefined()?.toString().getValue()
|
|
503
|
+
if (host) {
|
|
504
|
+
result['host'] = host
|
|
505
|
+
}
|
|
506
|
+
const scheme = policyRecord.get('scheme').ifDefined()?.toString().getValue()
|
|
507
|
+
if (scheme) {
|
|
508
|
+
result['scheme'] = scheme
|
|
509
|
+
}
|
|
510
|
+
const pathVal = policyRecord.get('path').ifDefined()?.toString().getValue()
|
|
511
|
+
if (pathVal) {
|
|
512
|
+
// Split path string by newlines and clean each path
|
|
513
|
+
// ServiceNow stores multi-line fields with CRLF (\r\n) which get XML-encoded as \n
|
|
514
|
+
// The fast-xml-parser doesn't decode numeric character references, so we strip them manually
|
|
515
|
+
result['path'] = pathVal
|
|
516
|
+
.split('\n')
|
|
517
|
+
.map((p) => p.replace(/ /g, '').trim())
|
|
518
|
+
.filter((p) => p)
|
|
519
|
+
}
|
|
520
|
+
const resource = policyRecord.get('resource').ifDefined()?.toString().getValue()
|
|
521
|
+
if (resource) {
|
|
522
|
+
result['resource'] = resource
|
|
523
|
+
}
|
|
524
|
+
const shortDesc = policyRecord.get('short_description').ifDefined()?.toString().getValue()
|
|
525
|
+
if (shortDesc) {
|
|
526
|
+
result['shortDescription'] = shortDesc
|
|
527
|
+
}
|
|
528
|
+
return result
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
// Transform wildcard/segment policy (only one per scope, exclude default values)
|
|
532
|
+
let wildcardPolicy: Record<string, unknown> | undefined
|
|
533
|
+
const segmentRecord = segmentPolicyRecords[0]
|
|
534
|
+
if (segmentRecord) {
|
|
535
|
+
wildcardPolicy = {
|
|
536
|
+
$id: segmentRecord.getId().getValue(),
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Only include active if not default (false)
|
|
540
|
+
const active = segmentRecord.get('active').toBoolean().getValue()
|
|
541
|
+
if (active !== false) {
|
|
542
|
+
wildcardPolicy['active'] = active
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const shortDesc = segmentRecord.get('short_description').ifDefined()?.toString().getValue()
|
|
546
|
+
if (shortDesc) {
|
|
547
|
+
wildcardPolicy['shortDescription'] = shortDesc
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Network pillar
|
|
551
|
+
const networkActive = segmentRecord.get('arp_network').ifDefined()?.toBoolean().getValue()
|
|
552
|
+
const networkWildcardStr = segmentRecord
|
|
553
|
+
.get('arp_network_wildcard')
|
|
554
|
+
.ifDefined()
|
|
555
|
+
?.toString()
|
|
556
|
+
.getValue()
|
|
557
|
+
const networkWildcardArr = networkWildcardStr
|
|
558
|
+
? networkWildcardStr.split(',').filter((s: string) => s.trim())
|
|
559
|
+
: []
|
|
560
|
+
if (networkActive !== undefined || networkWildcardArr.length > 0) {
|
|
561
|
+
const networkPillar: Record<string, unknown> = {}
|
|
562
|
+
// Only include active if not default (false)
|
|
563
|
+
if (networkActive !== false) {
|
|
564
|
+
networkPillar['active'] = networkActive ?? false
|
|
565
|
+
}
|
|
566
|
+
if (networkWildcardArr.length > 0) {
|
|
567
|
+
networkPillar['networkWildcard'] = networkWildcardArr
|
|
568
|
+
}
|
|
569
|
+
if (Object.keys(networkPillar).length > 0) {
|
|
570
|
+
wildcardPolicy['network'] = networkPillar
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Scripting pillar
|
|
575
|
+
const scriptingActive = segmentRecord.get('arp_script').ifDefined()?.toBoolean().getValue()
|
|
576
|
+
const scriptWildcardStr = segmentRecord
|
|
577
|
+
.get('arp_script_wildcard')
|
|
578
|
+
.ifDefined()
|
|
579
|
+
?.toString()
|
|
580
|
+
.getValue()
|
|
581
|
+
const scriptWildcardArr = scriptWildcardStr
|
|
582
|
+
? scriptWildcardStr.split(',').filter((s: string) => s.trim())
|
|
583
|
+
: []
|
|
584
|
+
if (scriptingActive !== undefined || scriptWildcardArr.length > 0) {
|
|
585
|
+
const scriptingPillar: Record<string, unknown> = {}
|
|
586
|
+
// Only include active if not default (false)
|
|
587
|
+
if (scriptingActive !== false) {
|
|
588
|
+
scriptingPillar['active'] = scriptingActive ?? false
|
|
589
|
+
}
|
|
590
|
+
if (scriptWildcardArr.length > 0) {
|
|
591
|
+
scriptingPillar['scriptingWildcard'] = scriptWildcardArr
|
|
592
|
+
}
|
|
593
|
+
if (Object.keys(scriptingPillar).length > 0) {
|
|
594
|
+
wildcardPolicy['scripting'] = scriptingPillar
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// ARL pillar
|
|
599
|
+
const arlActive = segmentRecord.get('arp_arl').ifDefined()?.toBoolean().getValue()
|
|
600
|
+
const arlWildcardStr = segmentRecord.get('arp_arl_wildcard').ifDefined()?.toString().getValue()
|
|
601
|
+
const arlWildcardArr = arlWildcardStr
|
|
602
|
+
? arlWildcardStr.split(',').filter((s: string) => s.trim())
|
|
603
|
+
: []
|
|
604
|
+
if (arlActive !== undefined || arlWildcardArr.length > 0) {
|
|
605
|
+
const arlPillar: Record<string, unknown> = {}
|
|
606
|
+
// Only include active if not default (false)
|
|
607
|
+
if (arlActive !== false) {
|
|
608
|
+
arlPillar['active'] = arlActive ?? false
|
|
609
|
+
}
|
|
610
|
+
if (arlWildcardArr.length > 0) {
|
|
611
|
+
arlPillar['arlWildcard'] = arlWildcardArr
|
|
612
|
+
}
|
|
613
|
+
if (Object.keys(arlPillar).length > 0) {
|
|
614
|
+
wildcardPolicy['arl'] = arlPillar
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Record flag - only include if not default (false)
|
|
619
|
+
const arpRecord = segmentRecord.get('arp_record').ifDefined()?.toBoolean().getValue()
|
|
620
|
+
if (arpRecord !== undefined && arpRecord !== false) {
|
|
621
|
+
wildcardPolicy['record'] = arpRecord
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// If wildcardPolicy is empty, set to undefined
|
|
625
|
+
if (Object.keys(wildcardPolicy).length === 0) {
|
|
626
|
+
wildcardPolicy = undefined
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Transform performance/resource limit policy (only one per scope, exclude default values)
|
|
631
|
+
let performancePolicy: Record<string, unknown> | undefined
|
|
632
|
+
const limitRecord = resourceLimitRecords[0]
|
|
633
|
+
if (limitRecord) {
|
|
634
|
+
performancePolicy = {
|
|
635
|
+
$id: limitRecord.getId().getValue(),
|
|
636
|
+
name: limitRecord.get('template_name').toString().getValue(),
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Only include quota limits if not default values
|
|
640
|
+
const scheduledJobLimit = limitRecord.get('scheduled_job_limit').ifDefined()?.toNumber().getValue()
|
|
641
|
+
if (scheduledJobLimit !== undefined && scheduledJobLimit !== 20) {
|
|
642
|
+
performancePolicy['scheduledJobLimit'] = scheduledJobLimit
|
|
643
|
+
}
|
|
644
|
+
const eventHandlerLimit = limitRecord.get('event_handler_limit').ifDefined()?.toNumber().getValue()
|
|
645
|
+
if (eventHandlerLimit !== undefined && eventHandlerLimit !== 20) {
|
|
646
|
+
performancePolicy['eventHandlerLimit'] = eventHandlerLimit
|
|
647
|
+
}
|
|
648
|
+
const apiTransactionLimit = limitRecord
|
|
649
|
+
.get('api_transaction_limit')
|
|
650
|
+
.ifDefined()
|
|
651
|
+
?.toNumber()
|
|
652
|
+
.getValue()
|
|
653
|
+
if (apiTransactionLimit !== undefined && apiTransactionLimit !== 30) {
|
|
654
|
+
performancePolicy['apiTransactionLimit'] = apiTransactionLimit
|
|
655
|
+
}
|
|
656
|
+
const interactiveTransactionLimit = limitRecord
|
|
657
|
+
.get('interactive_transaction_limit')
|
|
658
|
+
.ifDefined()
|
|
659
|
+
?.toNumber()
|
|
660
|
+
.getValue()
|
|
661
|
+
if (interactiveTransactionLimit !== undefined && interactiveTransactionLimit !== 30) {
|
|
662
|
+
performancePolicy['interactiveTransactionLimit'] = interactiveTransactionLimit
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Only include mode if it differs from auto-derived value based on applicationRuntimePolicy
|
|
666
|
+
// This ensures round-trip consistency with toRecord behavior
|
|
667
|
+
const arpValue =
|
|
668
|
+
record.get('application_runtime_policy').ifDefined()?.toString().getValue() || 'none'
|
|
669
|
+
const autoDerivedMode =
|
|
670
|
+
ARP_TO_PERFORMANCE_POLICY_MODE_MAP[
|
|
671
|
+
arpValue as keyof typeof ARP_TO_PERFORMANCE_POLICY_MODE_MAP
|
|
672
|
+
] ?? 'log_only'
|
|
673
|
+
const state = limitRecord.get('state').ifDefined()?.toString().getValue()
|
|
674
|
+
if (state && state !== autoDerivedMode) {
|
|
675
|
+
const mode =
|
|
676
|
+
PERFORMANCE_MODE_REVERSE_MAP[state as keyof typeof PERFORMANCE_MODE_REVERSE_MAP] ?? state
|
|
677
|
+
performancePolicy['mode'] = mode
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
19
681
|
return {
|
|
20
682
|
success: true,
|
|
21
683
|
value: new NowConfigShape({
|
|
22
684
|
source: record,
|
|
23
685
|
properties: record.transform(({ $ }) => ({
|
|
24
|
-
|
|
686
|
+
// Required fields
|
|
687
|
+
scope: $.from('scope').toString(),
|
|
688
|
+
scopeId: $.val(record.getId().getValue()),
|
|
689
|
+
name: $.from('name').toString().def(packageName),
|
|
25
690
|
active: $.toBoolean().def(true),
|
|
691
|
+
// Include ARP configuration
|
|
692
|
+
applicationRuntimePolicy: $.from('application_runtime_policy').def('none'),
|
|
693
|
+
// Policy records are always included in config regardless of ARP mode
|
|
694
|
+
// The platform will honor or ignore them based on applicationRuntimePolicy value
|
|
695
|
+
...(networkPolicies.length > 0 ? { networkPolicies: $.val(networkPolicies) } : {}),
|
|
696
|
+
...(wildcardPolicy ? { wildcardPolicy: $.val(wildcardPolicy) } : {}),
|
|
697
|
+
...(performancePolicy ? { performancePolicy: $.val(performancePolicy) } : {}),
|
|
26
698
|
accessControls: $.from(
|
|
27
699
|
'scoped_administration',
|
|
28
700
|
'restrict_table_access',
|
|
@@ -121,6 +793,28 @@ export const NowConfigPlugin = Plugin.create({
|
|
|
121
793
|
}
|
|
122
794
|
},
|
|
123
795
|
},
|
|
796
|
+
|
|
797
|
+
// Policy table configurations for transform (XML to Fluent)
|
|
798
|
+
// These records are merged into now.config.json via sys_app.toShape descendants query
|
|
799
|
+
// toNoOpShape prevents RecordPlugin from creating separate files for these descendants
|
|
800
|
+
sys_arp_network_policy: {
|
|
801
|
+
coalesce: ['policy_type', 'host', 'scheme', 'path', 'resource'],
|
|
802
|
+
toShape(record) {
|
|
803
|
+
return { success: true, value: Shape.noOp(record) }
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
sys_arp_segment_policy: {
|
|
807
|
+
coalesce: ['sys_scope'],
|
|
808
|
+
toShape(record) {
|
|
809
|
+
return { success: true, value: Shape.noOp(record) }
|
|
810
|
+
},
|
|
811
|
+
},
|
|
812
|
+
sys_app_resource_limit_template: {
|
|
813
|
+
coalesce: ['template_name'],
|
|
814
|
+
toShape(record) {
|
|
815
|
+
return { success: true, value: Shape.noOp(record) }
|
|
816
|
+
},
|
|
817
|
+
},
|
|
124
818
|
},
|
|
125
819
|
shapes: [
|
|
126
820
|
{
|
|
@@ -150,58 +844,144 @@ export const NowConfigPlugin = Plugin.create({
|
|
|
150
844
|
const accessControls = config.get('accessControls').ifDefined()?.asObject()
|
|
151
845
|
const licensing = config.get('licensing').ifDefined()?.asObject()
|
|
152
846
|
|
|
847
|
+
// Application Runtime Policy configuration
|
|
848
|
+
const applicationRuntimePolicy = config.get('applicationRuntimePolicy').ifString()?.getValue() ?? 'none'
|
|
849
|
+
const networkPoliciesShape = config.get('networkPolicies')?.ifArray()?.getElements() ?? []
|
|
850
|
+
const wildcardPolicyShape = config.get('wildcardPolicy')?.ifDefined()?.asObject()
|
|
851
|
+
const performancePolicyShape = config.get('performancePolicy')?.ifDefined()?.asObject()
|
|
852
|
+
|
|
853
|
+
const hasPolicyDefinitions =
|
|
854
|
+
networkPoliciesShape.length > 0 || wildcardPolicyShape || performancePolicyShape
|
|
855
|
+
|
|
856
|
+
// Warn if ARP is 'none' but policies are defined
|
|
857
|
+
if (applicationRuntimePolicy === 'none' && hasPolicyDefinitions) {
|
|
858
|
+
diagnostics.warn(
|
|
859
|
+
config,
|
|
860
|
+
`Application Runtime Policy is set to 'none'. Policy records will be created but the ServiceNow platform will not enforce them. Set applicationRuntimePolicy to 'tracking' or 'enforcing' to enable policy enforcement.`
|
|
861
|
+
)
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Create sys_app record
|
|
865
|
+
const sysAppRecord = await factory.createRecord({
|
|
866
|
+
source: config,
|
|
867
|
+
table: 'sys_app',
|
|
868
|
+
properties: config.transform(({ $ }) => ({
|
|
869
|
+
active: $.toBoolean().def(true),
|
|
870
|
+
// Application Runtime Policy field - only include if not 'none' (default)
|
|
871
|
+
...(applicationRuntimePolicy && applicationRuntimePolicy !== 'none'
|
|
872
|
+
? { application_runtime_policy: $.val(applicationRuntimePolicy) }
|
|
873
|
+
: {}),
|
|
874
|
+
scoped_administration: $.val(accessControls?.get('scopedAdministration'))
|
|
875
|
+
.toBoolean()
|
|
876
|
+
.def(false),
|
|
877
|
+
can_edit_in_studio: $.val(accessControls?.get('canEditInStudio')).toBoolean().def(true),
|
|
878
|
+
js_level: $.from('jsLevel').toString().def('es_latest'),
|
|
879
|
+
restrict_table_access: $.val(accessControls?.get('restrictTableAccess')).toBoolean().def(false),
|
|
880
|
+
runtime_access_tracking: $.map(() => {
|
|
881
|
+
const ratShape = accessControls?.get('runtimeAccessTracking')
|
|
882
|
+
const userProvidedRat = ratShape?.getValue() as string | undefined
|
|
883
|
+
|
|
884
|
+
// ARP may require a specific RAT value
|
|
885
|
+
const requiredRat = ARP_TO_RAT_MAP[applicationRuntimePolicy as keyof typeof ARP_TO_RAT_MAP]
|
|
886
|
+
|
|
887
|
+
// Error only when user *explicitly* set a conflicting value — not when it was omitted
|
|
888
|
+
if (userProvidedRat && requiredRat && userProvidedRat !== requiredRat) {
|
|
889
|
+
diagnostics.error(
|
|
890
|
+
ratShape || config.get('accessControls'),
|
|
891
|
+
`Runtime Access Tracking is set to '${userProvidedRat}' but Application Runtime Policy '${applicationRuntimePolicy}' requires '${requiredRat}'. ` +
|
|
892
|
+
`Set runtimeAccessTracking to '${requiredRat}' to match the Application Runtime Policy.`
|
|
893
|
+
)
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Hint when ARP auto-derives RAT and user also set it explicitly (even if not conflicting)
|
|
897
|
+
if (userProvidedRat && requiredRat && userProvidedRat === requiredRat) {
|
|
898
|
+
diagnostics.hint(
|
|
899
|
+
ratShape || config.get('accessControls'),
|
|
900
|
+
`runtimeAccessTracking is auto-derived from applicationRuntimePolicy='${applicationRuntimePolicy}'. ` +
|
|
901
|
+
`The explicit value '${userProvidedRat}' can be removed.`
|
|
902
|
+
)
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Auto-derive from ARP when it specifies a required value; otherwise fall back to
|
|
906
|
+
// the user-provided value or the platform default of 'permissive'.
|
|
907
|
+
const rat = requiredRat ?? userProvidedRat ?? 'permissive'
|
|
908
|
+
|
|
909
|
+
// Convert 'none' to undefined for XML (empty field).
|
|
910
|
+
// Round-trip safe: toShape maps empty string back to 'none' (line 796-798)
|
|
911
|
+
if (rat === 'none') {
|
|
912
|
+
return undefined
|
|
913
|
+
}
|
|
914
|
+
return rat
|
|
915
|
+
}),
|
|
916
|
+
licensable: $.val(licensing?.get('licensable')).toBoolean().def(true),
|
|
917
|
+
enforce_license: $.val(licensing?.get('enforceLicense')).toString().def('log'),
|
|
918
|
+
license_model: $.val(licensing?.get('licenseModel')).toString().def('none'),
|
|
919
|
+
menu: $.toString().def(''),
|
|
920
|
+
user_role: $.val(accessControls?.get('userRole')).toString().def(''),
|
|
921
|
+
short_description: $.from('description').toString().def(''),
|
|
922
|
+
logo: $.toString().def(''),
|
|
923
|
+
guided_setup_guid: $.from('guidedSetupGuid').toString().def(''),
|
|
924
|
+
subscription_entitlement: $.val(licensing?.get('subscriptionEntitlement')).toString().def(''),
|
|
925
|
+
private: $.val(accessControls?.get('private')).toBoolean().def(false),
|
|
926
|
+
trackable: $.val(accessControls?.get('trackable')).toBoolean().def(true),
|
|
927
|
+
uninstall_blocked: $.val(accessControls?.get('uninstallBlocked')).toBoolean().def(false),
|
|
928
|
+
hide_on_ui: $.val(accessControls?.get('hideOnUI')).toBoolean().def(false),
|
|
929
|
+
installed_as_dependency: $.from('installedAsDependency').toBoolean().def(false),
|
|
930
|
+
license_category: $.val(licensing?.get('licenseCategory')).toString().def('none'),
|
|
931
|
+
scope: $.val(scope),
|
|
932
|
+
package_json: $.val(packageJsonPath),
|
|
933
|
+
name: $.val(name),
|
|
934
|
+
source: $.val(scope),
|
|
935
|
+
sys_id: $.from('scopeId'),
|
|
936
|
+
version: $.val(version),
|
|
937
|
+
package_resolver_version: $.from('packageResolverVersion').def(
|
|
938
|
+
scope === 'global' ? MODULE_RESOLUTION.V2 : MODULE_RESOLUTION.V1
|
|
939
|
+
),
|
|
940
|
+
})),
|
|
941
|
+
})
|
|
942
|
+
|
|
943
|
+
const policyRecords: SdkRecord[] = []
|
|
944
|
+
const scopeId = config.get('scopeId').asString().getValue()
|
|
945
|
+
|
|
946
|
+
// Create network policy records
|
|
947
|
+
if (networkPoliciesShape.length > 0) {
|
|
948
|
+
const networkRecords = await createNetworkPolicyRecords(
|
|
949
|
+
networkPoliciesShape,
|
|
950
|
+
factory,
|
|
951
|
+
config,
|
|
952
|
+
diagnostics
|
|
953
|
+
)
|
|
954
|
+
policyRecords.push(...networkRecords)
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// Create wildcard policy record
|
|
958
|
+
if (wildcardPolicyShape) {
|
|
959
|
+
const wildcardRecord = await createWildcardPolicyRecord(
|
|
960
|
+
wildcardPolicyShape,
|
|
961
|
+
factory,
|
|
962
|
+
config,
|
|
963
|
+
scopeId,
|
|
964
|
+
diagnostics
|
|
965
|
+
)
|
|
966
|
+
policyRecords.push(wildcardRecord)
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// Create performance policy record
|
|
970
|
+
if (performancePolicyShape) {
|
|
971
|
+
const performanceRecord = await createPerformancePolicyRecord(
|
|
972
|
+
performancePolicyShape,
|
|
973
|
+
factory,
|
|
974
|
+
config,
|
|
975
|
+
scopeId,
|
|
976
|
+
applicationRuntimePolicy,
|
|
977
|
+
diagnostics
|
|
978
|
+
)
|
|
979
|
+
policyRecords.push(performanceRecord)
|
|
980
|
+
}
|
|
981
|
+
|
|
153
982
|
return {
|
|
154
983
|
success: true,
|
|
155
|
-
value:
|
|
156
|
-
source: config,
|
|
157
|
-
table: 'sys_app',
|
|
158
|
-
properties: config.transform(({ $ }) => ({
|
|
159
|
-
active: $.toBoolean().def(true),
|
|
160
|
-
scoped_administration: $.val(accessControls?.get('scopedAdministration'))
|
|
161
|
-
.toBoolean()
|
|
162
|
-
.def(false),
|
|
163
|
-
can_edit_in_studio: $.val(accessControls?.get('canEditInStudio')).toBoolean().def(true),
|
|
164
|
-
js_level: $.from('jsLevel').toString().def('es_latest'),
|
|
165
|
-
restrict_table_access: $.val(accessControls?.get('restrictTableAccess'))
|
|
166
|
-
.toBoolean()
|
|
167
|
-
.def(false),
|
|
168
|
-
runtime_access_tracking: $.map(() => {
|
|
169
|
-
const rac = accessControls?.get('runtimeAccessTracking').getValue()
|
|
170
|
-
if (!rac) {
|
|
171
|
-
return 'permissive'
|
|
172
|
-
} else if (rac === 'none') {
|
|
173
|
-
return undefined
|
|
174
|
-
}
|
|
175
|
-
return rac
|
|
176
|
-
}),
|
|
177
|
-
licensable: $.val(licensing?.get('licensable')).toBoolean().def(true),
|
|
178
|
-
enforce_license: $.val(licensing?.get('enforceLicense')).toString().def('log'),
|
|
179
|
-
license_model: $.val(licensing?.get('licenseModel')).toString().def('none'),
|
|
180
|
-
menu: $.toString().def(''),
|
|
181
|
-
user_role: $.val(accessControls?.get('userRole')).toString().def(''),
|
|
182
|
-
short_description: $.from('description').toString().def(''),
|
|
183
|
-
logo: $.toString().def(''),
|
|
184
|
-
guided_setup_guid: $.from('guidedSetupGuid').toString().def(''),
|
|
185
|
-
subscription_entitlement: $.val(licensing?.get('subscriptionEntitlement'))
|
|
186
|
-
.toString()
|
|
187
|
-
.def(''),
|
|
188
|
-
private: $.val(accessControls?.get('private')).toBoolean().def(false),
|
|
189
|
-
trackable: $.val(accessControls?.get('trackable')).toBoolean().def(true),
|
|
190
|
-
uninstall_blocked: $.val(accessControls?.get('uninstallBlocked')).toBoolean().def(false),
|
|
191
|
-
hide_on_ui: $.val(accessControls?.get('hideOnUI')).toBoolean().def(false),
|
|
192
|
-
installed_as_dependency: $.from('installedAsDependency').toBoolean().def(false),
|
|
193
|
-
license_category: $.val(licensing?.get('licenseCategory')).toString().def('none'),
|
|
194
|
-
scope: $.val(scope),
|
|
195
|
-
package_json: $.val(packageJsonPath),
|
|
196
|
-
name: $.val(name),
|
|
197
|
-
source: $.val(scope),
|
|
198
|
-
sys_id: $.from('scopeId'),
|
|
199
|
-
version: $.val(version),
|
|
200
|
-
package_resolver_version: $.from('packageResolverVersion').def(
|
|
201
|
-
scope === 'global' ? MODULE_RESOLUTION.V2 : MODULE_RESOLUTION.V1
|
|
202
|
-
),
|
|
203
|
-
})),
|
|
204
|
-
}),
|
|
984
|
+
value: sysAppRecord.with(...policyRecords),
|
|
205
985
|
}
|
|
206
986
|
},
|
|
207
987
|
},
|