harper 5.1.20 → 5.2.0-alpha.5
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/agent/operations.ts +13 -16
- package/components/Application.ts +188 -7
- package/components/ApplicationScope.ts +3 -0
- package/components/Scope.ts +12 -0
- package/components/anthropic/index.ts +9 -2
- package/components/bedrock/index.ts +25 -2
- package/components/componentLoader.ts +56 -7
- package/components/componentSecrets.ts +451 -0
- package/components/deploymentRecorder.ts +7 -1
- package/components/mcp/adapters/fastify.ts +3 -0
- package/components/mcp/adapters/harperHttp.ts +3 -0
- package/components/mcp/audit.ts +21 -6
- package/components/mcp/customResourceRegistry.ts +17 -38
- package/components/mcp/listChanged.ts +37 -20
- package/components/mcp/quota.ts +145 -0
- package/components/mcp/rateLimit.ts +152 -14
- package/components/mcp/resources.ts +2 -4
- package/components/mcp/toolRegistry.ts +73 -4
- package/components/mcp/tools/application.ts +77 -8
- package/components/mcp/tools/operations.ts +106 -36
- package/components/mcp/tools/schemas/operationDescriptions.ts +4 -4
- package/components/mcp/tools/schemas/operations.ts +5 -0
- package/components/mcp/transport.ts +55 -4
- package/components/ollama/index.ts +6 -2
- package/components/openai/index.ts +9 -2
- package/components/operations.js +204 -10
- package/components/operationsValidation.js +173 -0
- package/components/secretOperations.ts +498 -0
- package/config/configUtils.ts +25 -1
- package/config-app.schema.json +81 -0
- package/config-root.schema.json +8 -1
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
- package/dataLayer/readAuditLog.ts +14 -0
- package/dataLayer/schemaDescribe.ts +1 -0
- package/dist/agent/operations.d.ts +7 -4
- package/dist/agent/operations.js +13 -15
- package/dist/agent/operations.js.map +1 -1
- package/dist/components/Application.d.ts +25 -2
- package/dist/components/Application.js +166 -7
- package/dist/components/Application.js.map +1 -1
- package/dist/components/ApplicationScope.d.ts +2 -0
- package/dist/components/ApplicationScope.js +3 -0
- package/dist/components/ApplicationScope.js.map +1 -1
- package/dist/components/Scope.d.ts +8 -0
- package/dist/components/Scope.js +11 -0
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/anthropic/index.d.ts +4 -1
- package/dist/components/anthropic/index.js +4 -2
- package/dist/components/anthropic/index.js.map +1 -1
- package/dist/components/bedrock/index.d.ts +4 -1
- package/dist/components/bedrock/index.js +22 -2
- package/dist/components/bedrock/index.js.map +1 -1
- package/dist/components/componentLoader.d.ts +2 -0
- package/dist/components/componentLoader.js +52 -10
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/componentSecrets.d.ts +63 -0
- package/dist/components/componentSecrets.js +405 -0
- package/dist/components/componentSecrets.js.map +1 -0
- package/dist/components/deploymentRecorder.d.ts +3 -0
- package/dist/components/deploymentRecorder.js +4 -3
- package/dist/components/deploymentRecorder.js.map +1 -1
- package/dist/components/mcp/adapters/fastify.d.ts +2 -0
- package/dist/components/mcp/adapters/fastify.js +1 -0
- package/dist/components/mcp/adapters/fastify.js.map +1 -1
- package/dist/components/mcp/adapters/harperHttp.d.ts +2 -0
- package/dist/components/mcp/adapters/harperHttp.js +1 -0
- package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
- package/dist/components/mcp/audit.d.ts +2 -2
- package/dist/components/mcp/audit.js +20 -5
- package/dist/components/mcp/audit.js.map +1 -1
- package/dist/components/mcp/customResourceRegistry.d.ts +5 -12
- package/dist/components/mcp/customResourceRegistry.js +15 -30
- package/dist/components/mcp/customResourceRegistry.js.map +1 -1
- package/dist/components/mcp/listChanged.d.ts +7 -2
- package/dist/components/mcp/listChanged.js +28 -0
- package/dist/components/mcp/listChanged.js.map +1 -1
- package/dist/components/mcp/quota.d.ts +31 -0
- package/dist/components/mcp/quota.js +155 -0
- package/dist/components/mcp/quota.js.map +1 -0
- package/dist/components/mcp/rateLimit.d.ts +19 -3
- package/dist/components/mcp/rateLimit.js +129 -13
- package/dist/components/mcp/rateLimit.js.map +1 -1
- package/dist/components/mcp/resources.js.map +1 -1
- package/dist/components/mcp/toolRegistry.d.ts +39 -1
- package/dist/components/mcp/toolRegistry.js +60 -5
- package/dist/components/mcp/toolRegistry.js.map +1 -1
- package/dist/components/mcp/tools/application.d.ts +8 -1
- package/dist/components/mcp/tools/application.js +65 -5
- package/dist/components/mcp/tools/application.js.map +1 -1
- package/dist/components/mcp/tools/operations.d.ts +15 -3
- package/dist/components/mcp/tools/operations.js +99 -38
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/mcp/tools/schemas/operationDescriptions.js +4 -4
- package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
- package/dist/components/mcp/tools/schemas/operations.js +4 -0
- package/dist/components/mcp/tools/schemas/operations.js.map +1 -1
- package/dist/components/mcp/transport.d.ts +6 -0
- package/dist/components/mcp/transport.js +47 -3
- package/dist/components/mcp/transport.js.map +1 -1
- package/dist/components/ollama/index.d.ts +4 -1
- package/dist/components/ollama/index.js +4 -2
- package/dist/components/ollama/index.js.map +1 -1
- package/dist/components/openai/index.d.ts +4 -1
- package/dist/components/openai/index.js +4 -2
- package/dist/components/openai/index.js.map +1 -1
- package/dist/components/operations.d.ts +39 -0
- package/dist/components/operations.js +194 -2
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.d.ts +38 -0
- package/dist/components/operationsValidation.js +155 -0
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/secretOperations.d.ts +113 -0
- package/dist/components/secretOperations.js +475 -0
- package/dist/components/secretOperations.js.map +1 -0
- package/dist/config/configUtils.d.ts +4 -1
- package/dist/config/configUtils.js +18 -1
- package/dist/config/configUtils.js.map +1 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
- package/dist/dataLayer/readAuditLog.js +11 -0
- package/dist/dataLayer/readAuditLog.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +2 -0
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/globals.d.ts +1 -1
- package/dist/globals.js +1 -0
- package/dist/globals.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +38 -0
- package/dist/resources/DatabaseTransaction.d.ts +31 -3
- package/dist/resources/DatabaseTransaction.js +190 -29
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.d.ts +1 -1
- package/dist/resources/LMDBTransaction.js +43 -5
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.d.ts +51 -0
- package/dist/resources/PrimaryRocksDatabase.js +194 -0
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -0
- package/dist/resources/RecordEncoder.d.ts +1 -0
- package/dist/resources/RecordEncoder.js +81 -75
- package/dist/resources/RecordEncoder.js.map +1 -1
- package/dist/resources/RequestTarget.d.ts +19 -0
- package/dist/resources/RequestTarget.js.map +1 -1
- package/dist/resources/Resource.d.ts +0 -1
- package/dist/resources/Resource.js +103 -16
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +6 -2
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/Resources.js.map +1 -1
- package/dist/resources/Table.d.ts +6 -4
- package/dist/resources/Table.js +247 -24
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/read.js +25 -28
- package/dist/resources/analytics/read.js.map +1 -1
- package/dist/resources/blob.js +165 -22
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/crdt.d.ts +2 -0
- package/dist/resources/crdt.js +45 -36
- package/dist/resources/crdt.js.map +1 -1
- package/dist/resources/databases.d.ts +1 -0
- package/dist/resources/databases.js +134 -7
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/graphql.js +8 -0
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +35 -5
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +239 -14
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/jsResource.js +6 -0
- package/dist/resources/jsResource.js.map +1 -1
- package/dist/resources/loadEnv.js +30 -3
- package/dist/resources/loadEnv.js.map +1 -1
- package/dist/resources/login.js +2 -2
- package/dist/resources/login.js.map +1 -1
- package/dist/resources/models/Models.js +14 -3
- package/dist/resources/models/Models.js.map +1 -1
- package/dist/resources/models/embedHook.js +10 -2
- package/dist/resources/models/embedHook.js.map +1 -1
- package/dist/resources/models/openaiStream.d.ts +56 -0
- package/dist/resources/models/openaiStream.js +94 -0
- package/dist/resources/models/openaiStream.js.map +1 -0
- package/dist/resources/replayLogs.js +4 -3
- package/dist/resources/replayLogs.js.map +1 -1
- package/dist/resources/search.d.ts +1 -1
- package/dist/resources/search.js +104 -11
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/secretDecryptor.d.ts +54 -0
- package/dist/resources/secretDecryptor.js +131 -0
- package/dist/resources/secretDecryptor.js.map +1 -0
- package/dist/resources/tracked.js +15 -5
- package/dist/resources/tracked.js.map +1 -1
- package/dist/security/auth.js +71 -8
- package/dist/security/auth.js.map +1 -1
- package/dist/security/jsLoader.js +11 -0
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/tokenAuthentication.d.ts +9 -1
- package/dist/security/tokenAuthentication.js +31 -1
- package/dist/security/tokenAuthentication.js.map +1 -1
- package/dist/server/DurableSubscriptionsSession.d.ts +1 -1
- package/dist/server/DurableSubscriptionsSession.js +8 -4
- package/dist/server/DurableSubscriptionsSession.js.map +1 -1
- package/dist/server/REST.js +28 -9
- package/dist/server/REST.js.map +1 -1
- package/dist/server/fastifyRoutes.js +11 -1
- package/dist/server/fastifyRoutes.js.map +1 -1
- package/dist/server/graphqlQuerying.js +4 -3
- package/dist/server/graphqlQuerying.js.map +1 -1
- package/dist/server/http.d.ts +40 -2
- package/dist/server/http.js +560 -77
- package/dist/server/http.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +64 -0
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/liveSubscriptionAuth.d.ts +12 -0
- package/dist/server/liveSubscriptionAuth.js +140 -0
- package/dist/server/liveSubscriptionAuth.js.map +1 -0
- package/dist/server/loadRootComponents.js +2 -1
- package/dist/server/loadRootComponents.js.map +1 -1
- package/dist/server/middlewareChain.d.ts +44 -1
- package/dist/server/middlewareChain.js +86 -10
- package/dist/server/middlewareChain.js.map +1 -1
- package/dist/server/mqtt.js +2 -1
- package/dist/server/mqtt.js.map +1 -1
- package/dist/server/operationsServer.js +1 -1
- package/dist/server/operationsServer.js.map +1 -1
- package/dist/server/serverHelpers/Headers.d.ts +9 -0
- package/dist/server/serverHelpers/Headers.js +22 -0
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/JSONStream.js +3 -3
- package/dist/server/serverHelpers/JSONStream.js.map +1 -1
- package/dist/server/serverHelpers/Request.d.ts +52 -1
- package/dist/server/serverHelpers/Request.js +122 -1
- package/dist/server/serverHelpers/Request.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.js +3 -8
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/progressEmitter.d.ts +19 -0
- package/dist/server/serverHelpers/progressEmitter.js +52 -4
- package/dist/server/serverHelpers/progressEmitter.js.map +1 -1
- package/dist/server/serverHelpers/registeredOperations.d.ts +40 -0
- package/dist/server/serverHelpers/registeredOperations.js +286 -0
- package/dist/server/serverHelpers/registeredOperations.js.map +1 -0
- package/dist/server/serverHelpers/serverHandlers.js +11 -5
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +1 -0
- package/dist/server/serverHelpers/serverUtilities.js +67 -5
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/serverHelpers/uwsServer.d.ts +80 -0
- package/dist/server/serverHelpers/uwsServer.js +433 -0
- package/dist/server/serverHelpers/uwsServer.js.map +1 -0
- package/dist/server/static.d.ts +0 -15
- package/dist/server/static.js +161 -2
- package/dist/server/static.js.map +1 -1
- package/dist/server/status/index.d.ts +4 -1
- package/dist/server/status/index.js +64 -4
- package/dist/server/status/index.js.map +1 -1
- package/dist/server/threads/manageThreads.d.ts +10 -0
- package/dist/server/threads/manageThreads.js +107 -4
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.js +5 -1
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadServer.js +92 -14
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/upgrade/directives/5-2-0.d.ts +7 -0
- package/dist/upgrade/directives/5-2-0.js +108 -0
- package/dist/upgrade/directives/5-2-0.js.map +1 -0
- package/dist/upgrade/directives/directivesController.js +2 -1
- package/dist/upgrade/directives/directivesController.js.map +1 -1
- package/dist/utility/common_utils.js +5 -0
- package/dist/utility/common_utils.js.map +1 -1
- package/dist/utility/envFile.d.ts +41 -0
- package/dist/utility/envFile.js +221 -0
- package/dist/utility/envFile.js.map +1 -0
- package/dist/utility/globalSchema.d.ts +9 -0
- package/dist/utility/hdbTerms.d.ts +25 -0
- package/dist/utility/hdbTerms.js +31 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/logging/harper_logger.d.ts +21 -0
- package/dist/utility/logging/harper_logger.js +160 -8
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logRotator.js +29 -17
- package/dist/utility/logging/logRotator.js.map +1 -1
- package/dist/utility/logging/readLog.d.ts +1 -1
- package/dist/utility/logging/readLog.js +305 -2
- package/dist/utility/logging/readLog.js.map +1 -1
- package/dist/utility/operationPermissions.d.ts +18 -0
- package/dist/utility/operationPermissions.js +35 -1
- package/dist/utility/operationPermissions.js.map +1 -1
- package/dist/utility/operation_authorization.d.ts +17 -0
- package/dist/utility/operation_authorization.js +38 -0
- package/dist/utility/operation_authorization.js.map +1 -1
- package/dist/utility/secretEnvelope.d.ts +30 -0
- package/dist/utility/secretEnvelope.js +94 -0
- package/dist/utility/secretEnvelope.js.map +1 -0
- package/dist/utility/signalling.d.ts +7 -0
- package/dist/utility/signalling.js +16 -0
- package/dist/utility/signalling.js.map +1 -1
- package/dist/validation/configValidator.js +70 -24
- package/dist/validation/configValidator.js.map +1 -1
- package/index.ts +4 -0
- package/json/systemSchema.json +38 -0
- package/npm-shrinkwrap.json +15066 -9347
- package/package.json +13 -8
- package/resources/DESIGN.md +15 -12
- package/resources/DatabaseTransaction.ts +206 -37
- package/resources/LMDBTransaction.ts +43 -9
- package/resources/PrimaryRocksDatabase.ts +201 -0
- package/resources/RecordEncoder.ts +81 -69
- package/resources/RequestTarget.ts +21 -0
- package/resources/Resource.ts +109 -25
- package/resources/ResourceInterface.ts +6 -2
- package/resources/Resources.ts +1 -3
- package/resources/Table.ts +238 -29
- package/resources/analytics/read.ts +25 -30
- package/resources/blob.ts +159 -23
- package/resources/crdt.ts +37 -31
- package/resources/databases.ts +143 -6
- package/resources/graphql.ts +8 -0
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +251 -17
- package/resources/jsResource.ts +6 -0
- package/resources/loadEnv.ts +34 -3
- package/resources/login.ts +2 -2
- package/resources/models/Models.ts +19 -3
- package/resources/models/embedHook.ts +13 -2
- package/resources/models/openaiStream.ts +133 -0
- package/resources/replayLogs.ts +4 -3
- package/resources/search.ts +113 -11
- package/resources/secretDecryptor.ts +159 -0
- package/resources/tracked.ts +24 -17
- package/security/auth.ts +70 -10
- package/security/jsLoader.ts +12 -0
- package/security/tokenAuthentication.ts +42 -1
- package/server/DESIGN.md +10 -0
- package/server/DurableSubscriptionsSession.ts +8 -4
- package/server/REST.ts +26 -9
- package/server/fastifyRoutes.ts +10 -1
- package/server/graphqlQuerying.ts +4 -3
- package/server/http.ts +563 -77
- package/server/itc/serverHandlers.js +70 -0
- package/server/liveSubscriptionAuth.ts +152 -0
- package/server/loadRootComponents.js +2 -1
- package/server/middlewareChain.ts +107 -17
- package/server/mqtt.ts +2 -1
- package/server/operationsServer.ts +2 -2
- package/server/serverHelpers/Headers.ts +24 -0
- package/server/serverHelpers/JSONStream.ts +3 -3
- package/server/serverHelpers/Request.ts +127 -0
- package/server/serverHelpers/contentTypes.ts +3 -8
- package/server/serverHelpers/progressEmitter.ts +55 -5
- package/server/serverHelpers/registeredOperations.ts +259 -0
- package/server/serverHelpers/serverHandlers.js +11 -5
- package/server/serverHelpers/serverUtilities.ts +88 -5
- package/server/serverHelpers/uwsServer.ts +488 -0
- package/server/static.ts +180 -2
- package/server/status/index.ts +72 -4
- package/server/threads/manageThreads.js +102 -4
- package/server/threads/socketRouter.ts +5 -1
- package/server/threads/threadServer.js +93 -10
- package/studio/web/assets/{Chat-SdD1EZca.js → Chat-Cv_2paZE.js} +2 -2
- package/studio/web/assets/{Chat-SdD1EZca.js.map → Chat-Cv_2paZE.js.map} +1 -1
- package/studio/web/assets/{FloatingChat-GMq6tHK9.js → FloatingChat-CPyPIcVR.js} +4 -4
- package/studio/web/assets/{FloatingChat-GMq6tHK9.js.map → FloatingChat-CPyPIcVR.js.map} +1 -1
- package/studio/web/assets/{applications-B5hhv7uA.js → applications-C0jT2vdZ.js} +2 -2
- package/studio/web/assets/{applications-B5hhv7uA.js.map → applications-C0jT2vdZ.js.map} +1 -1
- package/studio/web/assets/{index-VFcpNWgq.js → index-D_GKOkhn.js} +6 -6
- package/studio/web/assets/index-D_GKOkhn.js.map +1 -0
- package/studio/web/assets/{index.lazy-DgcJojIA.js → index.lazy-CAmCIA65.js} +4 -4
- package/studio/web/assets/{index.lazy-DgcJojIA.js.map → index.lazy-CAmCIA65.js.map} +1 -1
- package/studio/web/assets/{profile-BqN-8vzm.js → profile-CaF-4aZe.js} +2 -2
- package/studio/web/assets/{profile-BqN-8vzm.js.map → profile-CaF-4aZe.js.map} +1 -1
- package/studio/web/assets/{setComponentFile-CHFvFq9u.js → setComponentFile-DLW1DHCt.js} +2 -2
- package/studio/web/assets/{setComponentFile-CHFvFq9u.js.map → setComponentFile-DLW1DHCt.js.map} +1 -1
- package/studio/web/assets/{setup-CQ3vWJSr.js → setup-TGCErIhq.js} +2 -2
- package/studio/web/assets/{setup-CQ3vWJSr.js.map → setup-TGCErIhq.js.map} +1 -1
- package/studio/web/assets/{status-BsW5fjuh.js → status-DG0Maoao.js} +2 -2
- package/studio/web/assets/{status-BsW5fjuh.js.map → status-DG0Maoao.js.map} +1 -1
- package/studio/web/assets/{swagger-ui-react-DITdeB9-.js → swagger-ui-react-8T4SgQ1m.js} +2 -2
- package/studio/web/assets/{swagger-ui-react-DITdeB9-.js.map → swagger-ui-react-8T4SgQ1m.js.map} +1 -1
- package/studio/web/assets/{tsMode-Dyph-OUs.js → tsMode-DJ6Sl24Q.js} +2 -2
- package/studio/web/assets/{tsMode-Dyph-OUs.js.map → tsMode-DJ6Sl24Q.js.map} +1 -1
- package/studio/web/assets/{useEntityRestURL-D0QTUqex.js → useEntityRestURL-JO2mfWTQ.js} +2 -2
- package/studio/web/assets/{useEntityRestURL-D0QTUqex.js.map → useEntityRestURL-JO2mfWTQ.js.map} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/directives/5-2-0.ts +82 -0
- package/upgrade/directives/directivesController.ts +2 -1
- package/utility/common_utils.ts +5 -0
- package/utility/envFile.ts +218 -0
- package/utility/hdbTerms.ts +31 -0
- package/utility/logging/harper_logger.ts +156 -8
- package/utility/logging/logRotator.ts +24 -14
- package/utility/logging/readLog.ts +323 -2
- package/utility/operationPermissions.ts +35 -1
- package/utility/operation_authorization.ts +74 -1
- package/utility/secretEnvelope.ts +113 -0
- package/utility/signalling.ts +15 -0
- package/validation/configValidator.ts +78 -25
- package/studio/web/assets/index-VFcpNWgq.js.map +0 -1
package/components/operations.js
CHANGED
|
@@ -11,6 +11,14 @@ const hdbTerms = require('../utility/hdbTerms.ts');
|
|
|
11
11
|
const env = require('../utility/environment/environmentManager.ts');
|
|
12
12
|
const configUtils = require('../config/configUtils.ts');
|
|
13
13
|
const hdbUtils = require('../utility/common_utils.ts');
|
|
14
|
+
const {
|
|
15
|
+
isEnvFile,
|
|
16
|
+
isProtectedEnvFile,
|
|
17
|
+
parseEnvKeys,
|
|
18
|
+
renderMaskedEnv,
|
|
19
|
+
upsertEnvValues,
|
|
20
|
+
removeEnvKeys,
|
|
21
|
+
} = require('../utility/envFile.ts');
|
|
14
22
|
const { handleHDBError, ServerError, hdbErrors } = require('../utility/errors/hdbError.ts');
|
|
15
23
|
const { HDB_ERROR_MSGS, HTTP_STATUS_CODES } = hdbErrors;
|
|
16
24
|
const manageThreads = require('../server/threads/manageThreads.js');
|
|
@@ -18,7 +26,7 @@ const { packageDirectory } = require('../components/packageComponent.ts');
|
|
|
18
26
|
const { Resources } = require('../resources/Resources.ts');
|
|
19
27
|
const { Application, prepareApplication, ASIDE_STAGING_DIR } = require('./Application.ts');
|
|
20
28
|
const { server } = require('../server/Server.ts');
|
|
21
|
-
const { DeploymentRecorder, awaitDeploymentRow } = require('./deploymentRecorder.ts');
|
|
29
|
+
const { DeploymentRecorder, awaitDeploymentRow, DEFAULT_AWAIT_ROW_TIMEOUT_MS } = require('./deploymentRecorder.ts');
|
|
22
30
|
const { ProgressEmitter } = require('../server/serverHelpers/progressEmitter.ts');
|
|
23
31
|
|
|
24
32
|
/**
|
|
@@ -362,6 +370,16 @@ async function deployComponent(req) {
|
|
|
362
370
|
throw handleHDBError(validation, validation.message, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
363
371
|
}
|
|
364
372
|
|
|
373
|
+
// Ingest any provided registry token into the secrets store so the credential lives as
|
|
374
|
+
// replicated ciphertext (reference, not embed); already-reference entries pass through, and with
|
|
375
|
+
// no custody a literal token stays as a transient, this-node-only fallback (#1158). Peers
|
|
376
|
+
// re-running a replicated deploy already carry references and never re-ingest.
|
|
377
|
+
const { ingestRegistryAuth, resolveRegistryAuth } = require('./secretOperations.ts');
|
|
378
|
+
req.registryAuth = await ingestRegistryAuth(req, req.registryAuth, req.project);
|
|
379
|
+
// References are safe to persist (config + deployment row) and replicate; a no-custody literal
|
|
380
|
+
// token is not — it is used only for this node's install below, then stripped before replication.
|
|
381
|
+
const registryAuthReferences = (req.registryAuth ?? []).filter((entry) => entry && entry.secret !== undefined);
|
|
382
|
+
|
|
365
383
|
// Write to root config if the request contains a package identifier
|
|
366
384
|
if (req.package) {
|
|
367
385
|
// Check if trying to overwrite a core component (requires force)
|
|
@@ -385,6 +403,9 @@ async function deployComponent(req) {
|
|
|
385
403
|
};
|
|
386
404
|
}
|
|
387
405
|
if (req.urlPath !== undefined) applicationConfig.urlPath = req.urlPath;
|
|
406
|
+
// Persist registry-auth references (never tokens) so every cold install of this component —
|
|
407
|
+
// reboot, new peer, rollback — re-resolves the credential from the store.
|
|
408
|
+
if (registryAuthReferences.length) applicationConfig.registryAuth = registryAuthReferences;
|
|
388
409
|
await configUtils.addConfig(req.project, applicationConfig);
|
|
389
410
|
}
|
|
390
411
|
|
|
@@ -409,6 +430,8 @@ async function deployComponent(req) {
|
|
|
409
430
|
package_identifier: req.package ?? null,
|
|
410
431
|
user: req.hdb_user?.username,
|
|
411
432
|
restart_mode: req.restart === 'rolling' ? 'rolling' : req.restart ? 'immediate' : null,
|
|
433
|
+
// Reference form only — the rollback source for re-resolving registry auth.
|
|
434
|
+
registry_auth: registryAuthReferences.length ? registryAuthReferences : null,
|
|
412
435
|
emitter,
|
|
413
436
|
});
|
|
414
437
|
if (recorder) req._deploymentId = recorder.deploymentId;
|
|
@@ -446,6 +469,19 @@ async function deployComponent(req) {
|
|
|
446
469
|
extractionPayload = row.payload_blob.stream();
|
|
447
470
|
}
|
|
448
471
|
|
|
472
|
+
// Resolve registryAuth references into concrete tokens for this node's npm pack/install
|
|
473
|
+
// (a no-custody literal-token fallback passes through unchanged). On a peer running a
|
|
474
|
+
// replicated deploy, the referenced hdb_secret row may arrive just behind the deploy op, so
|
|
475
|
+
// allow a bounded grace period (same budget as the payload-row wait) for it to replicate in.
|
|
476
|
+
let registryAuthWaitMs = 0;
|
|
477
|
+
if (isReplicatedExecution) {
|
|
478
|
+
const requested = Number(req.deployment_timeout);
|
|
479
|
+
registryAuthWaitMs = Number.isFinite(requested) && requested >= 0 ? requested : DEFAULT_AWAIT_ROW_TIMEOUT_MS;
|
|
480
|
+
}
|
|
481
|
+
const resolvedRegistryAuth = await resolveRegistryAuth(req.registryAuth, req.project, {
|
|
482
|
+
waitMs: registryAuthWaitMs,
|
|
483
|
+
});
|
|
484
|
+
|
|
449
485
|
const application = new Application({
|
|
450
486
|
name: req.project,
|
|
451
487
|
payload: extractionPayload,
|
|
@@ -462,7 +498,16 @@ async function deployComponent(req) {
|
|
|
462
498
|
installCapture.push(manager, stream, line);
|
|
463
499
|
if (emitter) emit('install', { manager, stream, line });
|
|
464
500
|
},
|
|
501
|
+
// Private-registry auth (already resolved above), used here for this node's npm pack/install.
|
|
502
|
+
registryAuth: resolvedRegistryAuth,
|
|
465
503
|
});
|
|
504
|
+
// Reduce req.registryAuth to references only (never a token) before it can reach an error/log
|
|
505
|
+
// path or replication: references are what peers resolve from their own replicated hdb_secret
|
|
506
|
+
// copy; a no-custody literal token is dropped entirely (peers fall back to their fabric-injected
|
|
507
|
+
// NPM_CONFIG_USERCONFIG, as before). This also fixes the prior success-only strip that leaked a
|
|
508
|
+
// literal token on a prepare/load failure.
|
|
509
|
+
if (registryAuthReferences.length) req.registryAuth = registryAuthReferences;
|
|
510
|
+
else delete req.registryAuth;
|
|
466
511
|
|
|
467
512
|
emit('phase', { phase: 'prepare', status: 'start' });
|
|
468
513
|
await prepareApplication(application);
|
|
@@ -475,18 +520,32 @@ async function deployComponent(req) {
|
|
|
475
520
|
pseudoResources.isWorker = true;
|
|
476
521
|
|
|
477
522
|
const componentLoader = require('./componentLoader.ts').default || require('./componentLoader.ts');
|
|
523
|
+
const { trackScopeClose } = require('./scopeShutdown.ts');
|
|
478
524
|
let lastError;
|
|
479
525
|
componentLoader.setErrorReporter((error) => (lastError = error));
|
|
480
526
|
emit('phase', { phase: 'load', status: 'start' });
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
527
|
+
// This load exists only to surface load-time errors early; the Scopes it creates are
|
|
528
|
+
// throwaway. They are collected (instead of registered for worker-shutdown auto-close) so we
|
|
529
|
+
// can close them here once validation completes — otherwise each deploy leaks the Scope's
|
|
530
|
+
// deploy-lifecycle listeners on this worker, eventually tripping MaxListenersExceededWarning
|
|
531
|
+
// (#1462).
|
|
532
|
+
const validationScopes = new Set();
|
|
533
|
+
const validation = (async () => {
|
|
534
|
+
try {
|
|
535
|
+
await componentLoader.loadComponent(application.dirPath, pseudoResources, undefined, {
|
|
536
|
+
collectScopes: validationScopes,
|
|
537
|
+
});
|
|
538
|
+
} finally {
|
|
539
|
+
const closeResults = await Promise.allSettled(Array.from(validationScopes, (scope) => scope.close()));
|
|
540
|
+
for (const result of closeResults) {
|
|
541
|
+
if (result.status === 'rejected') log.warn('Failed to close a deploy-validation Scope', result.reason);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
})();
|
|
545
|
+
// Track the load+close so a concurrent worker shutdown waits for these scopes to finish
|
|
546
|
+
// disposing — a plugin may start a native runtime in handleApplication — before realExit.
|
|
547
|
+
trackScopeClose(validation);
|
|
548
|
+
await validation;
|
|
490
549
|
emit('phase', { phase: 'load', status: 'done' });
|
|
491
550
|
|
|
492
551
|
if (lastError) throw lastError;
|
|
@@ -497,6 +556,9 @@ async function deployComponent(req) {
|
|
|
497
556
|
// ProgressEmitter holds function listeners that can't survive the replication
|
|
498
557
|
// channel's serialization; strip it unconditionally.
|
|
499
558
|
delete req.progress;
|
|
559
|
+
// req.registryAuth was already deleted immediately after the Application ctor (above) so the
|
|
560
|
+
// token never reaches the replication channel or a peer's operation log; peers authenticate
|
|
561
|
+
// against the private registry via their own fabric-injected NPM_CONFIG_USERCONFIG on reinstall.
|
|
500
562
|
if (systemReplicated && recorder) {
|
|
501
563
|
// The hdb_deployment row + payload_blob will reach peers via table replication,
|
|
502
564
|
// so peers can look up the payload by deployment_id. Drop req.payload to keep
|
|
@@ -748,6 +810,10 @@ async function getComponents() {
|
|
|
748
810
|
mtime: stats.mtime,
|
|
749
811
|
size: stats.size,
|
|
750
812
|
};
|
|
813
|
+
// Flag protected .env files so editors know their contents are masked by
|
|
814
|
+
// get_component_file and only editable via set_env_value. Template files
|
|
815
|
+
// (.env.example / .sample / .template) are not secret, so they are left alone.
|
|
816
|
+
if (isProtectedEnvFile(itemName)) res.protected = true;
|
|
751
817
|
result.entries.push(res);
|
|
752
818
|
}
|
|
753
819
|
}
|
|
@@ -762,7 +828,12 @@ async function getComponents() {
|
|
|
762
828
|
name: configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT).split(path.sep).slice(-1).pop(),
|
|
763
829
|
entries: [],
|
|
764
830
|
});
|
|
831
|
+
const { getUnsatisfiedEnv } = require('./componentSecrets.ts');
|
|
765
832
|
for (let entry of results.entries) {
|
|
833
|
+
// Declared-but-unsatisfied `env:` expectations (#1550) — metadata only (name, description,
|
|
834
|
+
// required, reason, tier), never values — so Studio/deploy output can render configure-me.
|
|
835
|
+
const unsatisfiedEnv = getUnsatisfiedEnv(entry.name);
|
|
836
|
+
if (unsatisfiedEnv.length > 0) entry.unsatisfiedEnv = unsatisfiedEnv;
|
|
766
837
|
const componentConfig = rootConfig?.[entry.name];
|
|
767
838
|
if (!componentConfig || typeof componentConfig !== 'object') continue;
|
|
768
839
|
if (componentConfig.package) entry.package = componentConfig.package;
|
|
@@ -846,6 +917,19 @@ async function getComponentFile(req) {
|
|
|
846
917
|
HTTP_STATUS_CODES.CONTENT_TOO_LARGE
|
|
847
918
|
);
|
|
848
919
|
}
|
|
920
|
+
// Protected .env files expose the key names (and a value-free masked rendering) but never
|
|
921
|
+
// the secret values. Template files (.env.example etc.) fall through and are read verbatim.
|
|
922
|
+
if (isProtectedEnvFile(req.file)) {
|
|
923
|
+
const keys = parseEnvKeys(await fs.readFile(filePath, 'utf8'));
|
|
924
|
+
return {
|
|
925
|
+
protected: true,
|
|
926
|
+
keys,
|
|
927
|
+
message: renderMaskedEnv(keys),
|
|
928
|
+
size: stats.size,
|
|
929
|
+
birthtime: stats.birthtime,
|
|
930
|
+
mtime: stats.mtime,
|
|
931
|
+
};
|
|
932
|
+
}
|
|
849
933
|
return {
|
|
850
934
|
message: await fs.readFile(filePath, options),
|
|
851
935
|
size: stats.size,
|
|
@@ -884,6 +968,113 @@ async function setComponentFile(req) {
|
|
|
884
968
|
return response;
|
|
885
969
|
}
|
|
886
970
|
|
|
971
|
+
/**
|
|
972
|
+
* Resolve the absolute path of a project's env file (defaulting to `.env`) and confirm it is one.
|
|
973
|
+
* @param req
|
|
974
|
+
* @returns {{file:string, filePath:string}}
|
|
975
|
+
*/
|
|
976
|
+
function resolveEnvFilePath(req) {
|
|
977
|
+
const file = req.file || '.env';
|
|
978
|
+
if (!isEnvFile(file)) {
|
|
979
|
+
const msg = `'${file}' is not a .env file`;
|
|
980
|
+
throw handleHDBError(new Error(msg), msg, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
981
|
+
}
|
|
982
|
+
const compRoot = configUtils.getConfigPath(hdbTerms.CONFIG_PARAMS.COMPONENTSROOT);
|
|
983
|
+
return { file, filePath: path.join(compRoot, req.project, file) };
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* List the key names of a project's .env file. Never returns the secret values.
|
|
988
|
+
* @param req
|
|
989
|
+
* @returns {Promise<{file:string, keys:string[], size:number, mtime:Date}>}
|
|
990
|
+
*/
|
|
991
|
+
async function getEnvKeys(req) {
|
|
992
|
+
const validation = validator.getEnvKeysValidator(req);
|
|
993
|
+
if (validation) {
|
|
994
|
+
throw handleHDBError(validation, validation.message, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
const { file, filePath } = resolveEnvFilePath(req);
|
|
998
|
+
try {
|
|
999
|
+
const [contents, stats] = await Promise.all([fs.readFile(filePath, 'utf8'), fs.stat(filePath)]);
|
|
1000
|
+
return { file, keys: parseEnvKeys(contents), size: stats.size, mtime: stats.mtime };
|
|
1001
|
+
} catch (err) {
|
|
1002
|
+
if (err.code === hdbTerms.NODE_ERROR_CODES.ENOENT) {
|
|
1003
|
+
throw new Error(`Component file not found '${path.join(req.project, file)}'`);
|
|
1004
|
+
}
|
|
1005
|
+
throw err;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Set one (`key` + `value`) or many (`values`) entries in a project's .env file, preserving all
|
|
1011
|
+
* other keys, comments and formatting. Creates the file if it does not exist. Never echoes values.
|
|
1012
|
+
* @param req
|
|
1013
|
+
* @returns {Promise<{message:string, keys:string[]}>}
|
|
1014
|
+
*/
|
|
1015
|
+
async function setEnvValue(req) {
|
|
1016
|
+
const validation = validator.setEnvValueValidator(req);
|
|
1017
|
+
if (validation) {
|
|
1018
|
+
throw handleHDBError(validation, validation.message, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
const { file, filePath } = resolveEnvFilePath(req);
|
|
1022
|
+
const updates = req.values ?? { [req.key]: req.value };
|
|
1023
|
+
|
|
1024
|
+
let existing = '';
|
|
1025
|
+
try {
|
|
1026
|
+
existing = await fs.readFile(filePath, 'utf8');
|
|
1027
|
+
} catch (err) {
|
|
1028
|
+
if (err.code !== hdbTerms.NODE_ERROR_CODES.ENOENT) throw err;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
let updated;
|
|
1032
|
+
try {
|
|
1033
|
+
updated = upsertEnvValues(existing, updates);
|
|
1034
|
+
} catch (err) {
|
|
1035
|
+
throw handleHDBError(err, err.message, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
1036
|
+
}
|
|
1037
|
+
await fs.outputFile(filePath, updated, 'utf8');
|
|
1038
|
+
|
|
1039
|
+
const response = await server.replication.replicateOperation(req);
|
|
1040
|
+
response.message = `Successfully set env value(s) in ${file}`;
|
|
1041
|
+
response.keys = parseEnvKeys(updated);
|
|
1042
|
+
return response;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* Remove one (`key`) or many (`keys`) entries from a project's .env file, leaving the rest intact.
|
|
1047
|
+
* @param req
|
|
1048
|
+
* @returns {Promise<{message:string, keys:string[]}>}
|
|
1049
|
+
*/
|
|
1050
|
+
async function deleteEnvValue(req) {
|
|
1051
|
+
const validation = validator.deleteEnvValueValidator(req);
|
|
1052
|
+
if (validation) {
|
|
1053
|
+
throw handleHDBError(validation, validation.message, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
const { file, filePath } = resolveEnvFilePath(req);
|
|
1057
|
+
const keysToRemove = req.keys ?? [req.key];
|
|
1058
|
+
|
|
1059
|
+
let existing;
|
|
1060
|
+
try {
|
|
1061
|
+
existing = await fs.readFile(filePath, 'utf8');
|
|
1062
|
+
} catch (err) {
|
|
1063
|
+
if (err.code === hdbTerms.NODE_ERROR_CODES.ENOENT) {
|
|
1064
|
+
throw new Error(`Component file not found '${path.join(req.project, file)}'`);
|
|
1065
|
+
}
|
|
1066
|
+
throw err;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
const updated = removeEnvKeys(existing, keysToRemove);
|
|
1070
|
+
await fs.outputFile(filePath, updated, 'utf8');
|
|
1071
|
+
|
|
1072
|
+
const response = await server.replication.replicateOperation(req);
|
|
1073
|
+
response.message = `Successfully deleted env value(s) from ${file}`;
|
|
1074
|
+
response.keys = parseEnvKeys(updated);
|
|
1075
|
+
return response;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
887
1078
|
/**
|
|
888
1079
|
* Deletes a component dir/file
|
|
889
1080
|
* @param req
|
|
@@ -939,4 +1130,7 @@ exports.deployComponent = deployComponent;
|
|
|
939
1130
|
exports.getComponents = getComponents;
|
|
940
1131
|
exports.getComponentFile = getComponentFile;
|
|
941
1132
|
exports.setComponentFile = setComponentFile;
|
|
1133
|
+
exports.getEnvKeys = getEnvKeys;
|
|
1134
|
+
exports.setEnvValue = setEnvValue;
|
|
1135
|
+
exports.deleteEnvValue = deleteEnvValue;
|
|
942
1136
|
exports.dropComponent = dropComponent;
|
|
@@ -9,10 +9,15 @@ const hdbLogger = require('../utility/logging/harper_logger.ts');
|
|
|
9
9
|
const configUtils = require('../config/configUtils.ts');
|
|
10
10
|
const { hdbErrors } = require('../utility/errors/hdbError.ts');
|
|
11
11
|
const { HDB_ERROR_MSGS } = hdbErrors;
|
|
12
|
+
const { ENV_ENCRYPTED_PREFIX } = require('../utility/envFile.ts');
|
|
12
13
|
|
|
13
14
|
// File name can only be alphanumeric, dash and underscores
|
|
14
15
|
const PROJECT_FILE_NAME_REGEX = /^[a-zA-Z0-9-_]+$/;
|
|
15
16
|
|
|
17
|
+
// dotenv's accepted key character set. Restricting keys to this prevents a crafted key (e.g. one
|
|
18
|
+
// containing `=` or a newline) from injecting extra assignments into a .env file.
|
|
19
|
+
const ENV_KEY_REGEX = /^[\w.-]+$/;
|
|
20
|
+
|
|
16
21
|
module.exports = {
|
|
17
22
|
getDropCustomFunctionValidator,
|
|
18
23
|
setCustomFunctionValidator,
|
|
@@ -23,6 +28,12 @@ module.exports = {
|
|
|
23
28
|
setComponentFileValidator,
|
|
24
29
|
getComponentFileValidator,
|
|
25
30
|
dropComponentFileValidator,
|
|
31
|
+
getEnvKeysValidator,
|
|
32
|
+
setEnvValueValidator,
|
|
33
|
+
deleteEnvValueValidator,
|
|
34
|
+
setSecretValidator,
|
|
35
|
+
grantSecretValidator,
|
|
36
|
+
deleteSecretValidator,
|
|
26
37
|
};
|
|
27
38
|
|
|
28
39
|
/**
|
|
@@ -169,6 +180,141 @@ function getComponentFileValidator(req) {
|
|
|
169
180
|
return validator.validateBySchema(req, getCompSchema);
|
|
170
181
|
}
|
|
171
182
|
|
|
183
|
+
/**
|
|
184
|
+
* Validate get_env_keys requests. `file` is optional and defaults to `.env` in the handler.
|
|
185
|
+
* @param req
|
|
186
|
+
* @returns {*}
|
|
187
|
+
*/
|
|
188
|
+
function getEnvKeysValidator(req) {
|
|
189
|
+
const schema = Joi.object({
|
|
190
|
+
// Patterned like the env writers (not the looser getComponentFile) so a `project` containing
|
|
191
|
+
// `..` can't traverse out of the components root when joined in resolveEnvFilePath.
|
|
192
|
+
project: Joi.string()
|
|
193
|
+
.pattern(PROJECT_FILE_NAME_REGEX)
|
|
194
|
+
.required()
|
|
195
|
+
.messages({ 'string.pattern.base': HDB_ERROR_MSGS.BAD_PROJECT_NAME }),
|
|
196
|
+
file: Joi.string().custom(checkFilePath).optional(),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return validator.validateBySchema(req, schema);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Validate set_env_value requests: exactly one of (`key` + `value`) or `values` (a key→value map).
|
|
204
|
+
* @param req
|
|
205
|
+
* @returns {*}
|
|
206
|
+
*/
|
|
207
|
+
function setEnvValueValidator(req) {
|
|
208
|
+
const schema = Joi.object({
|
|
209
|
+
project: Joi.string()
|
|
210
|
+
.pattern(PROJECT_FILE_NAME_REGEX)
|
|
211
|
+
.required()
|
|
212
|
+
.messages({ 'string.pattern.base': HDB_ERROR_MSGS.BAD_PROJECT_NAME }),
|
|
213
|
+
file: Joi.string().custom(checkFilePath).optional(),
|
|
214
|
+
key: Joi.string().pattern(ENV_KEY_REGEX),
|
|
215
|
+
value: Joi.string().allow(''),
|
|
216
|
+
// `.unknown(false)` rejects keys that don't match ENV_KEY_REGEX. Without it the schema-wide
|
|
217
|
+
// `allowUnknown: true` (see validateBySchema) would let an invalid key (e.g. with a space or
|
|
218
|
+
// newline) pass through unvalidated and corrupt the file.
|
|
219
|
+
values: Joi.object().pattern(ENV_KEY_REGEX, Joi.string().allow('')).unknown(false),
|
|
220
|
+
})
|
|
221
|
+
.with('key', 'value')
|
|
222
|
+
.with('value', 'key')
|
|
223
|
+
.xor('key', 'values');
|
|
224
|
+
|
|
225
|
+
return validator.validateBySchema(req, schema);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Validate delete_env_value requests: exactly one of `key` or `keys` (an array of key names).
|
|
230
|
+
* @param req
|
|
231
|
+
* @returns {*}
|
|
232
|
+
*/
|
|
233
|
+
function deleteEnvValueValidator(req) {
|
|
234
|
+
const schema = Joi.object({
|
|
235
|
+
project: Joi.string()
|
|
236
|
+
.pattern(PROJECT_FILE_NAME_REGEX)
|
|
237
|
+
.required()
|
|
238
|
+
.messages({ 'string.pattern.base': HDB_ERROR_MSGS.BAD_PROJECT_NAME }),
|
|
239
|
+
file: Joi.string().custom(checkFilePath).optional(),
|
|
240
|
+
key: Joi.string().pattern(ENV_KEY_REGEX),
|
|
241
|
+
keys: Joi.array().items(Joi.string().pattern(ENV_KEY_REGEX)).min(1),
|
|
242
|
+
}).xor('key', 'keys');
|
|
243
|
+
|
|
244
|
+
return validator.validateBySchema(req, schema);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// A secret name doubles as an env key when materialized, so it is held to the same character set.
|
|
248
|
+
const SECRET_NAME = Joi.string()
|
|
249
|
+
.pattern(ENV_KEY_REGEX)
|
|
250
|
+
.required()
|
|
251
|
+
.messages({ 'string.pattern.base': `'name' must only contain word characters, dots and dashes` });
|
|
252
|
+
|
|
253
|
+
// The encrypted-value marker followed by a base64url envelope body (structural validation of the
|
|
254
|
+
// decoded JSON happens in the handler via parseEnvelopeFields). Derived from the shared prefix
|
|
255
|
+
// constant so validator and handler can't drift; the prefix contains no regex metacharacters.
|
|
256
|
+
// Trailing `=` padding is tolerated — some browser encoders emit padded base64url, and Node's
|
|
257
|
+
// base64url decoder accepts either form.
|
|
258
|
+
const SECRET_ENVELOPE_REGEX = new RegExp(`^${ENV_ENCRYPTED_PREFIX}[A-Za-z0-9_-]+={0,2}$`);
|
|
259
|
+
|
|
260
|
+
// Size cap for secret values and envelopes: rows live forever in a replicated, audited system
|
|
261
|
+
// table, so unbounded payloads are a storage/replication hazard, not a feature.
|
|
262
|
+
const SECRET_MAX_LENGTH = 256 * 1024;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Validate set_secret requests: `name` plus exactly one of `value` (plaintext) or `envelope`
|
|
266
|
+
* (`enc:v1:` ciphertext), with optional `metadata`, and a tier of either `processEnv` or `grants`
|
|
267
|
+
* (the handler rejects the two together — a processEnv secret is global, so scoping it is meaningless).
|
|
268
|
+
* @param req
|
|
269
|
+
* @returns {*}
|
|
270
|
+
*/
|
|
271
|
+
function setSecretValidator(req) {
|
|
272
|
+
const schema = Joi.object({
|
|
273
|
+
name: SECRET_NAME,
|
|
274
|
+
value: Joi.string().allow('').max(SECRET_MAX_LENGTH),
|
|
275
|
+
envelope: Joi.string()
|
|
276
|
+
.max(SECRET_MAX_LENGTH)
|
|
277
|
+
.pattern(SECRET_ENVELOPE_REGEX)
|
|
278
|
+
.messages({ 'string.pattern.base': `'envelope' must be an '${ENV_ENCRYPTED_PREFIX}' base64url envelope` }),
|
|
279
|
+
// Modest structural caps: metadata is a small free-form label object, not a payload store,
|
|
280
|
+
// and grants is a set (explicit duplicates rejected here; write paths also dedupe dirty state).
|
|
281
|
+
metadata: Joi.object().max(100),
|
|
282
|
+
grants: Joi.array().items(Joi.string().min(1)).max(100).unique(),
|
|
283
|
+
// process.env delivery tier; mutually exclusive with grants (enforced in the handler so the
|
|
284
|
+
// check also covers a grants add against an already-processEnv stored row).
|
|
285
|
+
processEnv: Joi.boolean(),
|
|
286
|
+
}).xor('value', 'envelope');
|
|
287
|
+
|
|
288
|
+
return validator.validateBySchema(req, schema);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Validate grant_secret / revoke_secret requests (same shape: `name` + `component`).
|
|
293
|
+
* @param req
|
|
294
|
+
* @returns {*}
|
|
295
|
+
*/
|
|
296
|
+
function grantSecretValidator(req) {
|
|
297
|
+
const schema = Joi.object({
|
|
298
|
+
name: SECRET_NAME,
|
|
299
|
+
component: Joi.string().min(1).required(),
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
return validator.validateBySchema(req, schema);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Validate delete_secret requests.
|
|
307
|
+
* @param req
|
|
308
|
+
* @returns {*}
|
|
309
|
+
*/
|
|
310
|
+
function deleteSecretValidator(req) {
|
|
311
|
+
const schema = Joi.object({
|
|
312
|
+
name: SECRET_NAME,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
return validator.validateBySchema(req, schema);
|
|
316
|
+
}
|
|
317
|
+
|
|
172
318
|
/**
|
|
173
319
|
* Validate addCustomFunctionProject requests.
|
|
174
320
|
* @param req
|
|
@@ -252,6 +398,33 @@ function deployComponentValidator(req) {
|
|
|
252
398
|
})
|
|
253
399
|
.optional()
|
|
254
400
|
.messages({ 'any.invalid': 'urlPath must not contain ".."' }),
|
|
401
|
+
// Private-registry auth. Each entry supplies its credential exactly one of two ways:
|
|
402
|
+
// - `token`: a literal token, used only for this node's npm pack/install and never
|
|
403
|
+
// persisted or replicated (stripped from req before replicateOperation).
|
|
404
|
+
// - `secret`: the name of an hdb_secret row (#1550); the token is resolved by decrypting
|
|
405
|
+
// that row on this node at deploy time, so the credential lives in the secrets store
|
|
406
|
+
// (reference, not embed) instead of travelling in the operation body.
|
|
407
|
+
registryAuth: Joi.array()
|
|
408
|
+
.items(
|
|
409
|
+
Joi.object({
|
|
410
|
+
// registry and token are written verbatim into the transient .npmrc, which is
|
|
411
|
+
// line-based; forbid CR/LF so a super_user can't inject extra npm config lines.
|
|
412
|
+
// (registry also accepts bare hosts and //host/ forms, so a strict URI validator
|
|
413
|
+
// would reject supported inputs — the newline guard is the right scope here.)
|
|
414
|
+
registry: Joi.string()
|
|
415
|
+
.pattern(/^[^\r\n]+$/)
|
|
416
|
+
.required(),
|
|
417
|
+
token: Joi.string().pattern(/^[^\r\n]+$/),
|
|
418
|
+
// A reference into the hdb_secret store; same name grammar as set_secret's `name`.
|
|
419
|
+
secret: Joi.string()
|
|
420
|
+
.pattern(ENV_KEY_REGEX)
|
|
421
|
+
.messages({ 'string.pattern.base': `'secret' must only contain word characters, dots and dashes` }),
|
|
422
|
+
scope: Joi.string()
|
|
423
|
+
.pattern(/^@[a-z0-9-_.]+$/)
|
|
424
|
+
.optional(),
|
|
425
|
+
}).xor('token', 'secret')
|
|
426
|
+
)
|
|
427
|
+
.optional(),
|
|
255
428
|
}).with('urlPath', 'package');
|
|
256
429
|
|
|
257
430
|
return validator.validateBySchema(req, deployProjSchema);
|