@servicenow/sdk-build-plugins 3.0.3 → 4.0.1
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.d.ts +19 -0
- package/dist/acl-plugin.js +210 -0
- package/dist/acl-plugin.js.map +1 -0
- package/dist/application-menu-plugin.d.ts +18 -0
- package/dist/application-menu-plugin.js +104 -0
- package/dist/application-menu-plugin.js.map +1 -0
- package/dist/arrow-function-plugin.d.ts +16 -0
- package/dist/arrow-function-plugin.js +107 -0
- package/dist/arrow-function-plugin.js.map +1 -0
- package/dist/atf/step-configs.d.ts +39 -0
- package/dist/atf/step-configs.js +2334 -0
- package/dist/atf/step-configs.js.map +1 -0
- package/dist/atf/test-plugin.d.ts +4 -0
- package/dist/atf/test-plugin.js +600 -0
- package/dist/atf/test-plugin.js.map +1 -0
- package/dist/basic-syntax-plugin.d.ts +2 -0
- package/dist/basic-syntax-plugin.js +636 -0
- package/dist/basic-syntax-plugin.js.map +1 -0
- package/dist/business-rule-plugin.d.ts +3 -0
- package/dist/business-rule-plugin.js +205 -0
- package/dist/business-rule-plugin.js.map +1 -0
- package/dist/call-expression-plugin.d.ts +2 -0
- package/dist/call-expression-plugin.js +175 -0
- package/dist/call-expression-plugin.js.map +1 -0
- package/dist/client-script-plugin.d.ts +29 -0
- package/dist/client-script-plugin.js +164 -0
- package/dist/client-script-plugin.js.map +1 -0
- package/dist/column/column-helper.d.ts +12 -0
- package/dist/column/column-helper.js +84 -0
- package/dist/column/column-helper.js.map +1 -0
- package/dist/column/column-to-record.d.ts +3 -0
- package/dist/column/column-to-record.js +105 -0
- package/dist/column/column-to-record.js.map +1 -0
- package/dist/column-plugin.d.ts +8 -0
- package/dist/column-plugin.js +453 -0
- package/dist/column-plugin.js.map +1 -0
- package/dist/cross-scope-privilege-plugin.d.ts +15 -0
- package/dist/cross-scope-privilege-plugin.js +82 -0
- package/dist/cross-scope-privilege-plugin.js.map +1 -0
- package/dist/html-import-plugin.d.ts +2 -0
- package/dist/html-import-plugin.js +72 -0
- package/dist/html-import-plugin.js.map +1 -0
- package/dist/index.d.ts +37 -21
- package/dist/index.js +40 -45
- package/dist/index.js.map +1 -1
- package/dist/json-plugin.d.ts +15 -0
- package/dist/json-plugin.js +104 -0
- package/dist/json-plugin.js.map +1 -0
- package/dist/list-plugin.d.ts +2 -0
- package/dist/list-plugin.js +191 -0
- package/dist/list-plugin.js.map +1 -0
- package/dist/now-config-plugin.d.ts +4 -0
- package/dist/now-config-plugin.js +154 -0
- package/dist/now-config-plugin.js.map +1 -0
- package/dist/now-id-plugin.d.ts +11 -0
- package/dist/now-id-plugin.js +71 -0
- package/dist/now-id-plugin.js.map +1 -0
- package/dist/now-include-plugin.d.ts +16 -0
- package/dist/now-include-plugin.js +118 -0
- package/dist/now-include-plugin.js.map +1 -0
- package/dist/now-ref-plugin.d.ts +2 -0
- package/dist/now-ref-plugin.js +43 -0
- package/dist/now-ref-plugin.js.map +1 -0
- package/dist/now-unresolved-plugin.d.ts +2 -0
- package/dist/now-unresolved-plugin.js +32 -0
- package/dist/now-unresolved-plugin.js.map +1 -0
- package/dist/package-json-plugin.d.ts +2 -0
- package/dist/package-json-plugin.js +70 -0
- package/dist/package-json-plugin.js.map +1 -0
- package/dist/property-plugin.d.ts +2 -0
- package/dist/property-plugin.js +105 -0
- package/dist/property-plugin.js.map +1 -0
- package/dist/record-plugin.d.ts +43 -0
- package/dist/record-plugin.js +147 -0
- package/dist/record-plugin.js.map +1 -0
- package/dist/repack/index.d.ts +1 -0
- package/dist/repack/index.js +10 -2
- package/dist/repack/index.js.map +1 -1
- package/dist/repack/lint/Rules.d.ts +1 -1
- package/dist/repack/lint/Rules.js +7 -3
- package/dist/repack/lint/Rules.js.map +1 -1
- package/dist/repack/lint/index.js +1 -1
- package/dist/rest-api-plugin.d.ts +2 -0
- package/dist/rest-api-plugin.js +469 -0
- package/dist/rest-api-plugin.js.map +1 -0
- package/dist/role-plugin.d.ts +2 -0
- package/dist/role-plugin.js +117 -0
- package/dist/role-plugin.js.map +1 -0
- package/dist/script-action-plugin.d.ts +2 -0
- package/dist/script-action-plugin.js +62 -0
- package/dist/script-action-plugin.js.map +1 -0
- package/dist/script-include-plugin.d.ts +2 -0
- package/dist/script-include-plugin.js +102 -0
- package/dist/script-include-plugin.js.map +1 -0
- package/dist/server-module-plugin/index.d.ts +14 -0
- package/dist/server-module-plugin/index.js +378 -0
- package/dist/server-module-plugin/index.js.map +1 -0
- package/dist/server-module-plugin/sbom-builder.d.ts +45 -0
- package/dist/server-module-plugin/sbom-builder.js +179 -0
- package/dist/server-module-plugin/sbom-builder.js.map +1 -0
- package/dist/service-portal/angular-provider-plugin.d.ts +2 -0
- package/dist/service-portal/angular-provider-plugin.js +78 -0
- package/dist/service-portal/angular-provider-plugin.js.map +1 -0
- package/dist/service-portal/dependency-plugin.d.ts +2 -0
- package/dist/service-portal/dependency-plugin.js +235 -0
- package/dist/service-portal/dependency-plugin.js.map +1 -0
- package/dist/service-portal/widget-plugin.d.ts +6 -0
- package/dist/service-portal/widget-plugin.js +230 -0
- package/dist/service-portal/widget-plugin.js.map +1 -0
- package/dist/static-content-plugin.d.ts +2 -0
- package/dist/static-content-plugin.js +272 -0
- package/dist/static-content-plugin.js.map +1 -0
- package/dist/table-plugin.d.ts +3 -0
- package/dist/table-plugin.js +1324 -0
- package/dist/table-plugin.js.map +1 -0
- package/dist/ui-action-plugin.d.ts +2 -0
- package/dist/ui-action-plugin.js +291 -0
- package/dist/ui-action-plugin.js.map +1 -0
- package/dist/ui-page-plugin.d.ts +2 -0
- package/dist/ui-page-plugin.js +165 -0
- package/dist/ui-page-plugin.js.map +1 -0
- package/dist/user-preference-plugin.d.ts +15 -0
- package/dist/user-preference-plugin.js +63 -0
- package/dist/user-preference-plugin.js.map +1 -0
- package/dist/utils.d.ts +17 -0
- package/dist/utils.js +72 -0
- package/dist/utils.js.map +1 -0
- package/dist/view-plugin.d.ts +2 -0
- package/dist/view-plugin.js +45 -0
- package/dist/view-plugin.js.map +1 -0
- package/package.json +17 -13
- package/src/acl-plugin.ts +256 -0
- package/src/application-menu-plugin.ts +109 -0
- package/src/arrow-function-plugin.ts +127 -0
- package/src/atf/step-configs.ts +2384 -0
- package/src/atf/test-plugin.ts +739 -0
- package/src/basic-syntax-plugin.ts +729 -0
- package/src/business-rule-plugin.ts +266 -0
- package/src/call-expression-plugin.ts +202 -0
- package/src/client-script-plugin.ts +179 -0
- package/src/column/column-helper.ts +119 -0
- package/src/column/column-to-record.ts +131 -0
- package/src/column-plugin.ts +506 -0
- package/src/cross-scope-privilege-plugin.ts +82 -0
- package/src/html-import-plugin.ts +79 -0
- package/src/index.ts +39 -21
- package/src/json-plugin.ts +128 -0
- package/src/list-plugin.ts +222 -0
- package/src/now-config-plugin.ts +194 -0
- package/src/now-id-plugin.ts +78 -0
- package/src/now-include-plugin.ts +140 -0
- package/src/now-ref-plugin.ts +48 -0
- package/src/now-unresolved-plugin.ts +30 -0
- package/src/package-json-plugin.ts +87 -0
- package/src/property-plugin.ts +118 -0
- package/src/record-plugin.ts +171 -0
- package/src/repack/index.ts +10 -1
- package/src/repack/lint/Rules.ts +5 -4
- package/src/repack/lint/index.ts +1 -1
- package/src/rest-api-plugin.ts +658 -0
- package/src/role-plugin.ts +128 -0
- package/src/script-action-plugin.ts +63 -0
- package/src/script-include-plugin.ts +110 -0
- package/src/server-module-plugin/index.ts +470 -0
- package/src/server-module-plugin/sbom-builder.ts +183 -0
- package/src/service-portal/angular-provider-plugin.ts +83 -0
- package/src/service-portal/dependency-plugin.ts +284 -0
- package/src/service-portal/widget-plugin.ts +263 -0
- package/src/static-content-plugin.ts +254 -0
- package/src/table-plugin.ts +1698 -0
- package/src/ui-action-plugin.ts +324 -0
- package/src/ui-page-plugin.ts +168 -0
- package/src/user-preference-plugin.ts +62 -0
- package/src/utils.ts +69 -0
- package/src/view-plugin.ts +46 -0
- package/dist/AttachmentPlugin.d.ts +0 -254
- package/dist/AttachmentPlugin.js +0 -220
- package/dist/AttachmentPlugin.js.map +0 -1
- package/dist/BusinessRulePlugin.d.ts +0 -30
- package/dist/BusinessRulePlugin.js +0 -149
- package/dist/BusinessRulePlugin.js.map +0 -1
- package/dist/CrossScopePrivilegePlugin.d.ts +0 -35
- package/dist/CrossScopePrivilegePlugin.js +0 -80
- package/dist/CrossScopePrivilegePlugin.js.map +0 -1
- package/dist/DefaultPlugin.d.ts +0 -86
- package/dist/DefaultPlugin.js +0 -226
- package/dist/DefaultPlugin.js.map +0 -1
- package/dist/HtmlTemplatePlugin.d.ts +0 -21
- package/dist/HtmlTemplatePlugin.js +0 -29
- package/dist/HtmlTemplatePlugin.js.map +0 -1
- package/dist/IdPlugin.d.ts +0 -28
- package/dist/IdPlugin.js +0 -68
- package/dist/IdPlugin.js.map +0 -1
- package/dist/IncludePlugin.d.ts +0 -34
- package/dist/IncludePlugin.js +0 -155
- package/dist/IncludePlugin.js.map +0 -1
- package/dist/JsonPlugin.d.ts +0 -28
- package/dist/JsonPlugin.js +0 -69
- package/dist/JsonPlugin.js.map +0 -1
- package/dist/ListPlugin.d.ts +0 -93
- package/dist/ListPlugin.js +0 -456
- package/dist/ListPlugin.js.map +0 -1
- package/dist/NowConfigPlugin.d.ts +0 -45
- package/dist/NowConfigPlugin.js +0 -64
- package/dist/NowConfigPlugin.js.map +0 -1
- package/dist/PackageJsonPlugin.d.ts +0 -34
- package/dist/PackageJsonPlugin.js +0 -63
- package/dist/PackageJsonPlugin.js.map +0 -1
- package/dist/PropertyPlugin.d.ts +0 -51
- package/dist/PropertyPlugin.js +0 -181
- package/dist/PropertyPlugin.js.map +0 -1
- package/dist/ScriptTemplatePlugin.d.ts +0 -13
- package/dist/ScriptTemplatePlugin.js +0 -55
- package/dist/ScriptTemplatePlugin.js.map +0 -1
- package/dist/ServerModulePlugin.d.ts +0 -75
- package/dist/ServerModulePlugin.js +0 -368
- package/dist/ServerModulePlugin.js.map +0 -1
- package/dist/UserPreferencePlugin.d.ts +0 -19
- package/dist/UserPreferencePlugin.js +0 -32
- package/dist/UserPreferencePlugin.js.map +0 -1
- package/dist/aclAndRole/AclPlugin.d.ts +0 -82
- package/dist/aclAndRole/AclPlugin.js +0 -262
- package/dist/aclAndRole/AclPlugin.js.map +0 -1
- package/dist/aclAndRole/RolePlugin.d.ts +0 -66
- package/dist/aclAndRole/RolePlugin.js +0 -179
- package/dist/aclAndRole/RolePlugin.js.map +0 -1
- package/dist/aclAndRole/Util.d.ts +0 -3
- package/dist/aclAndRole/Util.js +0 -90
- package/dist/aclAndRole/Util.js.map +0 -1
- package/dist/app/ApplicationMenuPlugin.d.ts +0 -34
- package/dist/app/ApplicationMenuPlugin.js +0 -112
- package/dist/app/ApplicationMenuPlugin.js.map +0 -1
- package/dist/db/ColumnPlugins.d.ts +0 -750
- package/dist/db/ColumnPlugins.js +0 -114
- package/dist/db/ColumnPlugins.js.map +0 -1
- package/dist/db/DBUtils.d.ts +0 -2
- package/dist/db/DBUtils.js +0 -27
- package/dist/db/DBUtils.js.map +0 -1
- package/dist/db/DocumentationPlugin.d.ts +0 -67
- package/dist/db/DocumentationPlugin.js +0 -258
- package/dist/db/DocumentationPlugin.js.map +0 -1
- package/dist/db/LicensingPlugin.d.ts +0 -60
- package/dist/db/LicensingPlugin.js +0 -117
- package/dist/db/LicensingPlugin.js.map +0 -1
- package/dist/db/RecordPlugin.d.ts +0 -133
- package/dist/db/RecordPlugin.js +0 -337
- package/dist/db/RecordPlugin.js.map +0 -1
- package/dist/db/TablePlugin.d.ts +0 -231
- package/dist/db/TablePlugin.js +0 -1630
- package/dist/db/TablePlugin.js.map +0 -1
- package/dist/db/index.d.ts +0 -6
- package/dist/db/index.js +0 -32
- package/dist/db/index.js.map +0 -1
- package/dist/scriptedRESTAPI/RESTDeserializationUtils.d.ts +0 -10
- package/dist/scriptedRESTAPI/RESTDeserializationUtils.js +0 -373
- package/dist/scriptedRESTAPI/RESTDeserializationUtils.js.map +0 -1
- package/dist/scriptedRESTAPI/RESTSerializationUtils.d.ts +0 -13
- package/dist/scriptedRESTAPI/RESTSerializationUtils.js +0 -177
- package/dist/scriptedRESTAPI/RESTSerializationUtils.js.map +0 -1
- package/dist/scriptedRESTAPI/RestApiPlugin.d.ts +0 -81
- package/dist/scriptedRESTAPI/RestApiPlugin.js +0 -345
- package/dist/scriptedRESTAPI/RestApiPlugin.js.map +0 -1
- package/dist/scriptedRESTAPI/RestSchemaUtils.d.ts +0 -190
- package/dist/scriptedRESTAPI/RestSchemaUtils.js +0 -53
- package/dist/scriptedRESTAPI/RestSchemaUtils.js.map +0 -1
- package/dist/scriptedRESTAPI/RestUtils.d.ts +0 -69
- package/dist/scriptedRESTAPI/RestUtils.js +0 -497
- package/dist/scriptedRESTAPI/RestUtils.js.map +0 -1
- package/dist/scripts/ClientScriptPlugin.d.ts +0 -64
- package/dist/scripts/ClientScriptPlugin.js +0 -170
- package/dist/scripts/ClientScriptPlugin.js.map +0 -1
- package/dist/scripts/scriptUtils.d.ts +0 -1
- package/dist/scripts/scriptUtils.js +0 -9
- package/dist/scripts/scriptUtils.js.map +0 -1
- package/dist/uxf/ExperiencePlugin.d.ts +0 -45
- package/dist/uxf/ExperiencePlugin.js +0 -61
- package/dist/uxf/ExperiencePlugin.js.map +0 -1
- package/dist/uxf/RoutesPlugin.d.ts +0 -29
- package/dist/uxf/RoutesPlugin.js +0 -181
- package/dist/uxf/RoutesPlugin.js.map +0 -1
- package/dist/uxf/UxfFormulaParser/cleanUxValue.d.ts +0 -4
- package/dist/uxf/UxfFormulaParser/cleanUxValue.js +0 -65
- package/dist/uxf/UxfFormulaParser/cleanUxValue.js.map +0 -1
- package/dist/uxf/UxfFormulaParser/grammerParser/api.d.ts +0 -189
- package/dist/uxf/UxfFormulaParser/grammerParser/api.js +0 -158
- package/dist/uxf/UxfFormulaParser/grammerParser/api.js.map +0 -1
- package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.d.ts +0 -13
- package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js +0 -604
- package/dist/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js.map +0 -1
- package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.d.ts +0 -12
- package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.js +0 -551
- package/dist/uxf/UxfFormulaParser/grammerParser/grammarParser.js.map +0 -1
- package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.d.ts +0 -31
- package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.js +0 -64
- package/dist/uxf/UxfFormulaParser/grammerParser/spanHelpers.js.map +0 -1
- package/dist/uxf/UxfFormulaParser/index.d.ts +0 -3
- package/dist/uxf/UxfFormulaParser/index.js +0 -11
- package/dist/uxf/UxfFormulaParser/index.js.map +0 -1
- package/dist/uxf/UxfFormulaParser/parser.d.ts +0 -8
- package/dist/uxf/UxfFormulaParser/parser.js +0 -87
- package/dist/uxf/UxfFormulaParser/parser.js.map +0 -1
- package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.d.ts +0 -8
- package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.js +0 -17
- package/dist/uxf/UxfFormulaParser/utils/getErrorMsg.js.map +0 -1
- package/dist/uxf/constants.d.ts +0 -2
- package/dist/uxf/constants.js +0 -8
- package/dist/uxf/constants.js.map +0 -1
- package/dist/uxf/index.d.ts +0 -2
- package/dist/uxf/index.js +0 -11
- package/dist/uxf/index.js.map +0 -1
- package/dist/uxf/tectonicIdGenerator.d.ts +0 -12
- package/dist/uxf/tectonicIdGenerator.js +0 -79
- package/dist/uxf/tectonicIdGenerator.js.map +0 -1
- package/src/AttachmentPlugin.ts +0 -268
- package/src/BusinessRulePlugin.ts +0 -238
- package/src/CrossScopePrivilegePlugin.ts +0 -115
- package/src/DefaultPlugin.ts +0 -288
- package/src/HtmlTemplatePlugin.ts +0 -31
- package/src/IdPlugin.ts +0 -74
- package/src/IncludePlugin.ts +0 -206
- package/src/JsonPlugin.ts +0 -76
- package/src/ListPlugin.ts +0 -570
- package/src/NowConfigPlugin.ts +0 -69
- package/src/PackageJsonPlugin.ts +0 -68
- package/src/PropertyPlugin.ts +0 -237
- package/src/ScriptTemplatePlugin.ts +0 -53
- package/src/ServerModulePlugin.ts +0 -480
- package/src/UserPreferencePlugin.ts +0 -45
- package/src/aclAndRole/AclPlugin.ts +0 -420
- package/src/aclAndRole/RolePlugin.ts +0 -254
- package/src/aclAndRole/Util.ts +0 -113
- package/src/app/ApplicationMenuPlugin.ts +0 -165
- package/src/db/ColumnPlugins.ts +0 -114
- package/src/db/DBUtils.ts +0 -36
- package/src/db/DocumentationPlugin.ts +0 -316
- package/src/db/LicensingPlugin.ts +0 -138
- package/src/db/RecordPlugin.ts +0 -459
- package/src/db/TablePlugin.ts +0 -2072
- package/src/db/index.ts +0 -6
- package/src/scriptedRESTAPI/RESTDeserializationUtils.ts +0 -419
- package/src/scriptedRESTAPI/RESTSerializationUtils.ts +0 -228
- package/src/scriptedRESTAPI/RestApiPlugin.ts +0 -469
- package/src/scriptedRESTAPI/RestSchemaUtils.ts +0 -72
- package/src/scriptedRESTAPI/RestUtils.ts +0 -569
- package/src/scripts/ClientScriptPlugin.ts +0 -257
- package/src/scripts/scriptUtils.ts +0 -5
- package/src/uxf/ExperiencePlugin.ts +0 -67
- package/src/uxf/RoutesPlugin.ts +0 -211
- package/src/uxf/UxfFormulaParser/cleanUxValue.ts +0 -73
- package/src/uxf/UxfFormulaParser/grammerParser/api.js +0 -166
- package/src/uxf/UxfFormulaParser/grammerParser/clientTransformMap.js +0 -606
- package/src/uxf/UxfFormulaParser/grammerParser/grammarParser.js +0 -551
- package/src/uxf/UxfFormulaParser/grammerParser/spanHelpers.js +0 -65
- package/src/uxf/UxfFormulaParser/index.ts +0 -4
- package/src/uxf/UxfFormulaParser/parser.ts +0 -64
- package/src/uxf/UxfFormulaParser/utils/getErrorMsg.ts +0 -13
- package/src/uxf/constants.ts +0 -4
- package/src/uxf/index.ts +0 -2
- package/src/uxf/tectonicIdGenerator.ts +0 -78
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Plugin,
|
|
3
|
+
Shape,
|
|
4
|
+
type Diagnostics,
|
|
5
|
+
type Transform,
|
|
6
|
+
type Result,
|
|
7
|
+
SourceFileShape,
|
|
8
|
+
StringShape,
|
|
9
|
+
type Source,
|
|
10
|
+
type Project,
|
|
11
|
+
type RecordContext,
|
|
12
|
+
type Compiler,
|
|
13
|
+
} from '@servicenow/sdk-build-core'
|
|
14
|
+
import { path as pathModule, ts, type FileSystem, type NowConfig } from '@servicenow/sdk-build-core'
|
|
15
|
+
import { RepackService } from '../repack'
|
|
16
|
+
import { SBOMBuilder } from './sbom-builder'
|
|
17
|
+
import isEqual from 'lodash/isEqual'
|
|
18
|
+
import { INVALID_XML_CHARACTERS, applyPathMappings } from '../utils'
|
|
19
|
+
import zip from 'lodash/zip'
|
|
20
|
+
|
|
21
|
+
const GLUE_CODE_PREFIX = '// @fluent-module'
|
|
22
|
+
const GLUE_CODE_META_REGEX = new RegExp(`^${GLUE_CODE_PREFIX} (.*);(true|false);(.*)`) // name;isDefault;path
|
|
23
|
+
const GLUE_CODE_WARNING = `
|
|
24
|
+
// WARNING: This code is generated by the ServiceNow SDK in order to provide
|
|
25
|
+
// support for modular JavaScript. Modifications of any kind are likely to
|
|
26
|
+
// result in unintended behavior. In most cases, you should edit the source
|
|
27
|
+
// file of the module imported below. If you are absolutely certain you want
|
|
28
|
+
// to take control of this code, you can remove this comment to prevent the
|
|
29
|
+
// SDK from regenerating it. However, you will then be responsible for the
|
|
30
|
+
// management of this code in your Fluent file.`
|
|
31
|
+
|
|
32
|
+
type GlueCodeMeta = {
|
|
33
|
+
name: string
|
|
34
|
+
path: string
|
|
35
|
+
isDefault: boolean
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isGlueCode(string: StringShape): boolean {
|
|
39
|
+
return string.startsWith(GLUE_CODE_PREFIX)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseMeta(string: StringShape): GlueCodeMeta {
|
|
43
|
+
const result = string.getValue().match(GLUE_CODE_META_REGEX)
|
|
44
|
+
const [, name, isDefault, path] = result ?? []
|
|
45
|
+
|
|
46
|
+
if (typeof name !== 'string' || typeof isDefault !== 'string' || typeof path !== 'string') {
|
|
47
|
+
throw new Error(`Invalid glue code format: ${string.getValue()}`)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return { name, path, isDefault: isDefault === 'true' }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class ModuleFunctionShape extends Shape {
|
|
54
|
+
constructor(
|
|
55
|
+
source: Source,
|
|
56
|
+
private readonly meta: GlueCodeMeta
|
|
57
|
+
) {
|
|
58
|
+
super({ source })
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override toString(
|
|
62
|
+
callExpressionProvider: (functionName: string) => string = (name) => `${name}()`,
|
|
63
|
+
defaultParams?: string[]
|
|
64
|
+
): StringShape {
|
|
65
|
+
const { name, path, isDefault } = this.meta
|
|
66
|
+
const moduleParamNames = getFunctionParameters(this.getSource())
|
|
67
|
+
const variables = zip(defaultParams ?? [], moduleParamNames).map(
|
|
68
|
+
([defaultParam, moduleParamName]) => defaultParam ?? moduleParamName
|
|
69
|
+
)
|
|
70
|
+
const moduleCallExpression = callExpressionProvider(name).replace('{{PARAMS}}', variables.join(', '))
|
|
71
|
+
|
|
72
|
+
const code = isDefault
|
|
73
|
+
? `const ${name} = require('${path}').default;\n${moduleCallExpression};`
|
|
74
|
+
: `const { ${name} } = require('${path}');\n${moduleCallExpression};`
|
|
75
|
+
|
|
76
|
+
return Shape.from(this, `${GLUE_CODE_PREFIX} ${name};${isDefault};${path}${GLUE_CODE_WARNING}\n${code}`)
|
|
77
|
+
.asString()
|
|
78
|
+
.withContentType('cdata')
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
override equals(other: Shape): boolean {
|
|
82
|
+
if (other.is(ModuleFunctionShape)) {
|
|
83
|
+
return isEqual(this.meta, other.meta)
|
|
84
|
+
} else if (other.isString()) {
|
|
85
|
+
if (!isGlueCode(other)) {
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return isEqual(this.meta, parseMeta(other))
|
|
90
|
+
} else {
|
|
91
|
+
return super.equals(other)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Removes invalid control characters that would cause XML parsing errors.
|
|
98
|
+
*/
|
|
99
|
+
function sanitizeModuleContent(content: string): string {
|
|
100
|
+
return content.replace(INVALID_XML_CHARACTERS, '')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getEmitOutput(
|
|
104
|
+
file: ts.SourceFile,
|
|
105
|
+
{
|
|
106
|
+
fs,
|
|
107
|
+
diagnostics,
|
|
108
|
+
config,
|
|
109
|
+
project,
|
|
110
|
+
compiler,
|
|
111
|
+
}: {
|
|
112
|
+
fs: FileSystem
|
|
113
|
+
diagnostics: Diagnostics
|
|
114
|
+
config: NowConfig
|
|
115
|
+
project: Project
|
|
116
|
+
compiler: Compiler
|
|
117
|
+
}
|
|
118
|
+
): string | undefined {
|
|
119
|
+
const path = file.getFilePath()
|
|
120
|
+
if (!path.endsWith('.ts')) {
|
|
121
|
+
return file.getFullText()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const tsConfigPath = config.tsconfigPath ? project.resolvePath(config.tsconfigPath) : undefined
|
|
125
|
+
return compiler.compileModule(file, fs, diagnostics, tsConfigPath)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getFunctionParameters(source: Source): string[] {
|
|
129
|
+
if (!(source instanceof ts.VariableDeclaration || source instanceof ts.FunctionDeclaration)) {
|
|
130
|
+
return []
|
|
131
|
+
}
|
|
132
|
+
const node = source as ts.VariableDeclaration | ts.VariableDeclaration
|
|
133
|
+
|
|
134
|
+
let functionNode: ts.FunctionDeclaration | ts.FunctionExpression | ts.ArrowFunction | undefined
|
|
135
|
+
if (ts.Node.isFunctionDeclaration(node)) {
|
|
136
|
+
functionNode = node
|
|
137
|
+
} else if (ts.Node.isVariableDeclaration(node) && node.getInitializerIfKind(ts.SyntaxKind.FunctionExpression)) {
|
|
138
|
+
functionNode = node.getInitializerIfKindOrThrow(ts.SyntaxKind.FunctionExpression)
|
|
139
|
+
} else if (ts.Node.isVariableDeclaration(node) && node.getInitializerIfKind(ts.SyntaxKind.ArrowFunction)) {
|
|
140
|
+
functionNode = node.getInitializerIfKindOrThrow(ts.SyntaxKind.ArrowFunction)
|
|
141
|
+
} else {
|
|
142
|
+
return []
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return functionNode.getParameters().map((param) => param.getName()) ?? []
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function parseDeclaration(
|
|
149
|
+
node: ts.FunctionDeclaration | ts.VariableDeclaration,
|
|
150
|
+
{ transform }: { transform: Transform }
|
|
151
|
+
): Promise<Result<ModuleFunctionShape>> {
|
|
152
|
+
const result = await transform.toRecord(node.getSourceFile())
|
|
153
|
+
if (!result.success) {
|
|
154
|
+
return result
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const record = result.value
|
|
158
|
+
if (record.getTable() !== 'sys_module') {
|
|
159
|
+
return { success: false }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
success: true,
|
|
164
|
+
value: new ModuleFunctionShape(node, {
|
|
165
|
+
name: node.getName() ?? 'functionModule',
|
|
166
|
+
path: record.get('path').asString().getValue(),
|
|
167
|
+
isDefault: node.isDefaultExport(),
|
|
168
|
+
}),
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const dependencyIgnoreList = ['@servicenow/glide', '@servicenow/sdk']
|
|
173
|
+
function isIgnoredDependency(name: string): boolean {
|
|
174
|
+
return dependencyIgnoreList.some((dependency) => name.startsWith(dependency))
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function validateAndGetModuleSpecifier(name: string): { name: string; entry?: string } {
|
|
178
|
+
const scopedRegex = /@[a-z\d][\w\-.]+\/[a-z\d][\w\-.]*/gi
|
|
179
|
+
scopedRegex.test(name)
|
|
180
|
+
const idx = scopedRegex.lastIndex
|
|
181
|
+
|
|
182
|
+
const isSubPathImport = name.indexOf('/', idx)
|
|
183
|
+
if (isSubPathImport > 0) {
|
|
184
|
+
return { name: name.slice(0, isSubPathImport), entry: name.slice(isSubPathImport + 1) }
|
|
185
|
+
}
|
|
186
|
+
return { name }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function isValidRequireCall(callExpression: ts.CallExpression, requirePath: ts.StringLiteral): boolean {
|
|
190
|
+
const expression = callExpression.getExpression()
|
|
191
|
+
const isRequire = ts.Node.isIdentifier(expression) && expression.getText() === 'require'
|
|
192
|
+
const isRelativePath =
|
|
193
|
+
requirePath.getLiteralText().startsWith('../') || requirePath.getLiteralText().startsWith('./')
|
|
194
|
+
return isRequire && !isRelativePath
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
class ModuleDependencyShape extends Shape {
|
|
198
|
+
private readonly moduleName: string
|
|
199
|
+
|
|
200
|
+
constructor({
|
|
201
|
+
node,
|
|
202
|
+
moduleName,
|
|
203
|
+
}: {
|
|
204
|
+
node: ts.ImportDeclaration | ts.ExportDeclaration | ts.CallExpression
|
|
205
|
+
moduleName: string
|
|
206
|
+
}) {
|
|
207
|
+
super({ source: node })
|
|
208
|
+
this.moduleName = moduleName
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
getModuleName(): string {
|
|
212
|
+
return this.moduleName
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// TODO: Need to have some invalidation mechanism. Maybe the plugin framework can provide plugins with a managed cache to use for stuff like this?
|
|
217
|
+
const DEPENDENCY_CACHE: globalThis.Record<string, Result<ModuleDependencyShape>> = {}
|
|
218
|
+
|
|
219
|
+
function parseModuleDependency(
|
|
220
|
+
node: ts.ImportDeclaration | ts.ExportDeclaration | ts.CallExpression
|
|
221
|
+
): Result<ModuleDependencyShape> {
|
|
222
|
+
let moduleName: string | undefined
|
|
223
|
+
if (ts.Node.isImportDeclaration(node) && !node.isModuleSpecifierRelative()) {
|
|
224
|
+
moduleName = node.getModuleSpecifierValue()
|
|
225
|
+
} else if (ts.Node.isExportDeclaration(node) && node.hasModuleSpecifier() && !node.isModuleSpecifierRelative()) {
|
|
226
|
+
moduleName = node.getModuleSpecifierValue()
|
|
227
|
+
} else if (ts.Node.isCallExpression(node)) {
|
|
228
|
+
const args = node.getArguments()
|
|
229
|
+
const requirePath = args[0]
|
|
230
|
+
if (ts.Node.isStringLiteral(requirePath) && isValidRequireCall(node, requirePath)) {
|
|
231
|
+
moduleName = requirePath.getLiteralValue()
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!moduleName || isIgnoredDependency(moduleName)) {
|
|
236
|
+
return { success: false }
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const cached = DEPENDENCY_CACHE[moduleName]
|
|
240
|
+
if (cached && (!cached.success || !cached.value.getOriginalNode().wasForgotten())) {
|
|
241
|
+
return cached
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return (DEPENDENCY_CACHE[moduleName] = {
|
|
245
|
+
success: true,
|
|
246
|
+
value: new ModuleDependencyShape({ node, moduleName }),
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function generateSBOMContent(context: RecordContext) {
|
|
251
|
+
const { database } = context
|
|
252
|
+
const moduleRecords = database.query('sys_module', { external_source: true })
|
|
253
|
+
const sbomBuilder = new SBOMBuilder()
|
|
254
|
+
for (const mod of moduleRecords) {
|
|
255
|
+
const modulePath = mod.get('path').asString().getValue()
|
|
256
|
+
if (modulePath.endsWith('/package.json')) {
|
|
257
|
+
const moduleContent = mod.get('content').asString().getValue()
|
|
258
|
+
sbomBuilder.addPackageJson(modulePath, moduleContent)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return sbomBuilder.generateSBOM(context)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export const ServerModulePlugin = Plugin.create({
|
|
265
|
+
name: 'ServerModulePlugin',
|
|
266
|
+
files: [
|
|
267
|
+
{
|
|
268
|
+
entryPoint: true,
|
|
269
|
+
matcher: /[/\\]package\.json$/,
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
entryPoint: true,
|
|
273
|
+
matcher: (path, { config, project }) =>
|
|
274
|
+
!path.endsWith('.test.ts') &&
|
|
275
|
+
!path.endsWith('.test.js') &&
|
|
276
|
+
project.isInDir(config.serverModulesDir, path),
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
records: {
|
|
280
|
+
sys_module: {
|
|
281
|
+
toFile(record, context) {
|
|
282
|
+
const { config } = context
|
|
283
|
+
// If the record is not the sbom, we defer to using the record plugin
|
|
284
|
+
if (!record.get('path').ifString()?.endsWith('/bom.json')) {
|
|
285
|
+
return {
|
|
286
|
+
success: false,
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const sbomContent = generateSBOMContent(context)
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
success: true,
|
|
293
|
+
value: {
|
|
294
|
+
name: `sys_module_${record.getId().getValue()}.xml`,
|
|
295
|
+
category: record.getInstallCategory(),
|
|
296
|
+
content: `<?xml version="1.0"?>
|
|
297
|
+
<record_update>
|
|
298
|
+
<sys_module action="INSERT_OR_UPDATE">
|
|
299
|
+
<sys_id>${record.getId().getValue()}</sys_id>
|
|
300
|
+
<sys_scope display_value="${config.scope}">${config.scopeId}</sys_scope>
|
|
301
|
+
<path>${record.get('path').getValue()}</path>
|
|
302
|
+
<external_source>${record.get('external_source').getValue()}</external_source>
|
|
303
|
+
<content><![CDATA[${StringShape.escapeCdataTags(sanitizeModuleContent(sbomContent))}]]></content>
|
|
304
|
+
</sys_module>
|
|
305
|
+
</record_update>`,
|
|
306
|
+
},
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
toShape(record) {
|
|
310
|
+
return { success: true, value: Shape.noOp(record) }
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
shapes: [
|
|
315
|
+
{
|
|
316
|
+
shape: SourceFileShape,
|
|
317
|
+
fileTypes: ['module'],
|
|
318
|
+
toRecord(file, { factory, fs, diagnostics, project, config, packageJson, compiler }) {
|
|
319
|
+
const path = file.getPath()
|
|
320
|
+
if (!path.startsWith(project.resolvePath(config.serverModulesDir))) {
|
|
321
|
+
return { success: false }
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const mappedPath = applyPathMappings(path, config.modulePaths)
|
|
325
|
+
const resolvedPath = project.resolvePath(mappedPath)
|
|
326
|
+
const mappedFile =
|
|
327
|
+
compiler.getSourceFile(resolvedPath) ?? compiler.addSourceFileAtPathIfExists(resolvedPath)
|
|
328
|
+
|
|
329
|
+
if (!mappedFile) {
|
|
330
|
+
diagnostics.error(
|
|
331
|
+
file,
|
|
332
|
+
`Module path was mapped from '${path}' to '${resolvedPath}' but no file exists at the mapped location.`
|
|
333
|
+
)
|
|
334
|
+
return { success: false }
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const content = getEmitOutput(mappedFile, { fs, diagnostics, config, project, compiler })
|
|
338
|
+
if (!content) {
|
|
339
|
+
return { success: false }
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const relativePath = pathModule.relative(project.getRootDir(), resolvedPath)
|
|
343
|
+
const sysModulePath = pathModule.join(config.scope, packageJson.name, packageJson.version, relativePath)
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
success: true,
|
|
347
|
+
value: factory.createRecord({
|
|
348
|
+
source: file,
|
|
349
|
+
table: 'sys_module',
|
|
350
|
+
explicitId: relativePath.replaceAll(/[./\\]/g, '_'),
|
|
351
|
+
properties: {
|
|
352
|
+
// Module resolution at runtime requires this format
|
|
353
|
+
path: sysModulePath,
|
|
354
|
+
content: Shape.from(file, sanitizeModuleContent(content))
|
|
355
|
+
.asString()
|
|
356
|
+
.withContentType('cdata'),
|
|
357
|
+
external_source: false,
|
|
358
|
+
sys_name: sysModulePath,
|
|
359
|
+
},
|
|
360
|
+
}),
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
shape: ModuleDependencyShape,
|
|
366
|
+
fileTypes: ['module'],
|
|
367
|
+
// TODO: When managed cache is provided to plugins, cache dependencies that were already handled to avoid reprocessing
|
|
368
|
+
async toRecord(shape, { packageJson, diagnostics, fs, logger, project, factory, config }) {
|
|
369
|
+
const dependencies = packageJson.dependencies ?? {}
|
|
370
|
+
const { name, entry } = validateAndGetModuleSpecifier(shape.getModuleName())
|
|
371
|
+
|
|
372
|
+
const version = dependencies[name]
|
|
373
|
+
if (!version) {
|
|
374
|
+
diagnostics.error(shape, `Dependency ${name} is not found in package.json`)
|
|
375
|
+
return { success: false }
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const id = `${name}@${version}`
|
|
379
|
+
const repack = await RepackService.create(logger, fs, project.getRootDir())
|
|
380
|
+
const dependencyNodes = await repack.execute({
|
|
381
|
+
id,
|
|
382
|
+
entry: entry ? [entry] : ['.'],
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
if (!dependencyNodes) {
|
|
386
|
+
throw new Error(`Failed to build dependency ${id}`)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const modules: { id: string; path: string; content: string }[] = []
|
|
390
|
+
const { Lint } = await import('../repack/lint/index.js')
|
|
391
|
+
for (const node of dependencyNodes) {
|
|
392
|
+
const { packagePath, files, updatedManifest } = node
|
|
393
|
+
const { name, version } = updatedManifest
|
|
394
|
+
|
|
395
|
+
for (const file of files) {
|
|
396
|
+
const fileContent = fs.readFileSync(pathModule.join(packagePath, file)).toString('utf-8')
|
|
397
|
+
if (/(.js|.cjs|.mjs)$/.test(pathModule.extname(file))) {
|
|
398
|
+
const result = await new Lint().check(fileContent)
|
|
399
|
+
if (result) {
|
|
400
|
+
logger.warn(`Use of unsupported APIs detected in npm dependency ${name}`)
|
|
401
|
+
logger.warn(result)
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
modules.push({
|
|
406
|
+
id: `${name}@${version}/${file}`,
|
|
407
|
+
path: pathModule.join(config.scope, name, version, file),
|
|
408
|
+
content: fileContent,
|
|
409
|
+
})
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const [first, ...rest] = modules.map((m) => {
|
|
414
|
+
return factory.createRecord({
|
|
415
|
+
source: shape,
|
|
416
|
+
table: 'sys_module',
|
|
417
|
+
explicitId: m.id,
|
|
418
|
+
properties: {
|
|
419
|
+
path: m.path,
|
|
420
|
+
content: Shape.from(shape, sanitizeModuleContent(m.content))
|
|
421
|
+
.asString()
|
|
422
|
+
.withContentType('cdata'),
|
|
423
|
+
external_source: true,
|
|
424
|
+
sys_name: m.path,
|
|
425
|
+
},
|
|
426
|
+
})
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
if (!first) {
|
|
430
|
+
return { success: false }
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return {
|
|
434
|
+
success: true,
|
|
435
|
+
value: first.with(...rest),
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
nodes: [
|
|
441
|
+
{ node: 'CallExpression', fileTypes: ['module'], entryPoint: true, toShape: parseModuleDependency },
|
|
442
|
+
{ node: 'ImportDeclaration', fileTypes: ['module'], entryPoint: true, toShape: parseModuleDependency },
|
|
443
|
+
{ node: 'ExportDeclaration', fileTypes: ['module'], entryPoint: true, toShape: parseModuleDependency },
|
|
444
|
+
{ node: 'FunctionDeclaration', fileTypes: ['module'], toShape: parseDeclaration },
|
|
445
|
+
{
|
|
446
|
+
node: 'FunctionExpression',
|
|
447
|
+
fileTypes: ['module'],
|
|
448
|
+
toShape(node, context) {
|
|
449
|
+
const parent = node.getParentIfKind(ts.SyntaxKind.VariableDeclaration)
|
|
450
|
+
if (!parent) {
|
|
451
|
+
return { success: false }
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return parseDeclaration(parent, context)
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
node: 'ArrowFunction',
|
|
459
|
+
fileTypes: ['module'],
|
|
460
|
+
toShape(node, context) {
|
|
461
|
+
const parent = node.getParentIfKind(ts.SyntaxKind.VariableDeclaration)
|
|
462
|
+
if (!parent) {
|
|
463
|
+
return { success: false }
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return parseDeclaration(parent, context)
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
],
|
|
470
|
+
})
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { path, FileSystem } from '@servicenow/sdk-build-core'
|
|
2
|
+
import type { RecordContext } from '@servicenow/sdk-build-core'
|
|
3
|
+
import { JsonSerializer, JSON as CycloneDXJSON } from '@cyclonedx/cyclonedx-library/Serialize'
|
|
4
|
+
import { ExternalReference, type Metadata } from '@cyclonedx/cyclonedx-library/Models'
|
|
5
|
+
import { ExternalReferenceType } from '@cyclonedx/cyclonedx-library/Enums'
|
|
6
|
+
import * as CDXUtils from '@cyclonedx/cyclonedx-library/Utils'
|
|
7
|
+
import * as Builders from '@cyclonedx/cyclonedx-library/Builders'
|
|
8
|
+
import * as Enums from '@cyclonedx/cyclonedx-library/Enums'
|
|
9
|
+
import * as Factories from '@cyclonedx/cyclonedx-library/Factories'
|
|
10
|
+
import * as Models from '@cyclonedx/cyclonedx-library/Models'
|
|
11
|
+
import * as Spec from '@cyclonedx/cyclonedx-library/Spec'
|
|
12
|
+
import { PackageURL } from 'packageurl-js'
|
|
13
|
+
|
|
14
|
+
export class SBOMBuilder {
|
|
15
|
+
/**
|
|
16
|
+
* The set of package.json files that are used to generate the SBOM.
|
|
17
|
+
* @private
|
|
18
|
+
*/
|
|
19
|
+
private packageJsonFiles: Map<string, string> = new Map()
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The package.json of the SDK build plugins. This is used to generate the tool component in the SBOM.
|
|
23
|
+
*/
|
|
24
|
+
private toolPkgJsonDefault = {
|
|
25
|
+
name: '@servicenow/sdk-build-plugins',
|
|
26
|
+
version: '0.0.1',
|
|
27
|
+
author: 'ServiceNow',
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Add a package.json file to the set of package.json files that are used to generate the SBOM for the application.
|
|
32
|
+
* @param file the path to the package.json file
|
|
33
|
+
*/
|
|
34
|
+
public addPackageJson(file: string, content: string) {
|
|
35
|
+
//We don't care about the package.json path but it least keeps the content unique
|
|
36
|
+
this.packageJsonFiles.set(file, content)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Generate a CycloneDX SBOM for the application.
|
|
41
|
+
* @param context the build context
|
|
42
|
+
*/
|
|
43
|
+
public generateSBOM(context: RecordContext): string {
|
|
44
|
+
const cdxExternalReferenceFactory = new Factories.FromNodePackageJson.ExternalReferenceFactory()
|
|
45
|
+
const cdxLicenseFactory = new Factories.LicenseFactory()
|
|
46
|
+
const cdxPurlFactory = new Factories.FromNodePackageJson.PackageUrlFactory('npm')
|
|
47
|
+
const cdxComponentBuilder = new Builders.FromNodePackageJson.ComponentBuilder(
|
|
48
|
+
cdxExternalReferenceFactory,
|
|
49
|
+
cdxLicenseFactory
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const jsonSerializer = new JsonSerializer(new CycloneDXJSON.Normalize.Factory(Spec.Spec1dot6))
|
|
53
|
+
const sdkJson = this.getSdkInfo(context)
|
|
54
|
+
const toolComponent = cdxComponentBuilder.makeComponent(sdkJson, Enums.ComponentType.Library)
|
|
55
|
+
|
|
56
|
+
const rootComponent = cdxComponentBuilder.makeComponent(context.packageJson, Enums.ComponentType.Application)
|
|
57
|
+
|
|
58
|
+
let qualifiers: Record<string, string> | undefined
|
|
59
|
+
if (context.config.scopeId) {
|
|
60
|
+
qualifiers = {
|
|
61
|
+
sys_scope_id: context.config.scopeId,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const purl = new PackageURL(
|
|
65
|
+
'servicenow',
|
|
66
|
+
undefined,
|
|
67
|
+
context.packageJson.name,
|
|
68
|
+
context.packageJson.version,
|
|
69
|
+
qualifiers,
|
|
70
|
+
undefined
|
|
71
|
+
)
|
|
72
|
+
if (rootComponent && purl) {
|
|
73
|
+
rootComponent.purl = purl
|
|
74
|
+
rootComponent.bomRef.value = purl.toString()
|
|
75
|
+
}
|
|
76
|
+
const metadata = new Models.Metadata({
|
|
77
|
+
timestamp: new Date(),
|
|
78
|
+
component: rootComponent,
|
|
79
|
+
})
|
|
80
|
+
toolComponent && metadata.tools.components.add(toolComponent)
|
|
81
|
+
this.addExternalReferences(metadata)
|
|
82
|
+
|
|
83
|
+
const bom = new Models.Bom({
|
|
84
|
+
metadata,
|
|
85
|
+
serialNumber: CDXUtils.BomUtility.randomSerialNumber(),
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
for (const [, content] of this.packageJsonFiles.entries()) {
|
|
89
|
+
const data = content
|
|
90
|
+
const json = JSON.parse(data)
|
|
91
|
+
if (json) {
|
|
92
|
+
const { name, version } = json
|
|
93
|
+
if (!(context.packageJson.name === name && context.packageJson.version === version)) {
|
|
94
|
+
const component = cdxComponentBuilder.makeComponent(json, Enums.ComponentType.Application)
|
|
95
|
+
if (component) {
|
|
96
|
+
this.registerPackageUrlOnComponent(component, cdxPurlFactory)
|
|
97
|
+
bom.components.add(component)
|
|
98
|
+
rootComponent?.dependencies.add(component.bomRef)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const nowDir: string = path.resolve(context.project.getRootDir(), '.now')
|
|
105
|
+
FileSystem.ensureDirSync(context.fs, nowDir)
|
|
106
|
+
const bomPath = path.resolve(nowDir, 'bom.json')
|
|
107
|
+
const bomContent: string = jsonSerializer.serialize(bom, { space: 2, sortLists: true })
|
|
108
|
+
context.fs.writeFileSync(bomPath, bomContent)
|
|
109
|
+
|
|
110
|
+
return bomContent
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Helper method to get the JSON needed to build the tool component for the @servicenow/sdk-build-plugins package.
|
|
115
|
+
* @param context the build context
|
|
116
|
+
* @private
|
|
117
|
+
*/
|
|
118
|
+
public getSdkInfo(context: RecordContext): { name: string; version: string; author: string } {
|
|
119
|
+
try {
|
|
120
|
+
//TODO can we retrieve the package.json's name and version from the context for the sdk-build-plugins package?
|
|
121
|
+
const packageJsonPath = path.join(
|
|
122
|
+
context.project.getRootDir(),
|
|
123
|
+
'node_modules',
|
|
124
|
+
'@servicenow/sdk-build-plugins',
|
|
125
|
+
'package.json'
|
|
126
|
+
)
|
|
127
|
+
const packageJson = JSON.parse(context.fs.readFileSync(packageJsonPath, { encoding: 'utf-8' }))
|
|
128
|
+
return {
|
|
129
|
+
name: packageJson.name,
|
|
130
|
+
version: packageJson.version,
|
|
131
|
+
author: 'ServiceNow',
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
// ignore
|
|
135
|
+
}
|
|
136
|
+
return this.toolPkgJsonDefault
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Helper method to generate a package url from a component and register it on the component. No idea why the base
|
|
141
|
+
* component builder doesn't do this automatically.
|
|
142
|
+
* @param component the component to register the package url on
|
|
143
|
+
* @param purlFactory the package url factory
|
|
144
|
+
*/
|
|
145
|
+
private registerPackageUrlOnComponent(
|
|
146
|
+
component: Models.Component | undefined,
|
|
147
|
+
purlFactory: Factories.FromNodePackageJson.PackageUrlFactory
|
|
148
|
+
) {
|
|
149
|
+
if (component) {
|
|
150
|
+
const purl = purlFactory.makeFromComponent(component)
|
|
151
|
+
if (purl) {
|
|
152
|
+
component.purl = purl
|
|
153
|
+
component.bomRef.value = purl.toString()
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Add external references to the root component that are retrieved from common Jenkins environment variables.
|
|
160
|
+
* @param metadata the bom metadata reference
|
|
161
|
+
* @private
|
|
162
|
+
*/
|
|
163
|
+
private addExternalReferences(metadata: Metadata) {
|
|
164
|
+
const buildUrl = process.env['BUILD_URL']
|
|
165
|
+
if (buildUrl) {
|
|
166
|
+
const buildSystemExRef = new ExternalReference(buildUrl, ExternalReferenceType.BuildSystem, {
|
|
167
|
+
comment: 'As obtained from the environment variable BUILD_URL',
|
|
168
|
+
})
|
|
169
|
+
if (metadata.component && metadata.component.externalReferences) {
|
|
170
|
+
metadata.component.externalReferences.add(buildSystemExRef)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const vcsUrl = process.env['GIT_URL']
|
|
174
|
+
if (vcsUrl) {
|
|
175
|
+
const vcsExRef = new ExternalReference(vcsUrl, ExternalReferenceType.VCS, {
|
|
176
|
+
comment: 'As obtained from the environment variable GIT_URL',
|
|
177
|
+
})
|
|
178
|
+
if (metadata.component && metadata.component.externalReferences) {
|
|
179
|
+
metadata.component.externalReferences.add(vcsExRef)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|