@underpostnet/cyberia 3.3.76 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -0
- package/.github/workflows/coverall.cyberia.ci.yml +17 -3
- package/.github/workflows/cyberia-client.cd.yml +2 -1
- package/.github/workflows/cyberia-server.cd.yml +2 -1
- package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
- package/.github/workflows/engine-cyberia.ci.yml +7 -1
- package/.github/workflows/ghpkg.ci.yml +19 -2
- package/.github/workflows/gitlab.ci.yml +8 -6
- package/.github/workflows/hardhat.ci.yml +4 -1
- package/.github/workflows/npmpkg.ci.yml +9 -5
- package/.github/workflows/publish.ci.yml +21 -12
- package/.github/workflows/publish.cyberia.ci.yml +25 -14
- package/.github/workflows/pwa-microservices-template-page.cd.yml +4 -2
- package/CHANGELOG.md +110 -262
- package/CLI-HELP.md +29 -2
- package/Dockerfile +1 -1
- package/Dockerfile.dev +1 -1
- package/Dockerfile.test +1 -1
- package/README.md +1 -1
- package/bin/build.js +67 -14
- package/bin/cyberia.js +4 -4
- package/bin/index.js +4 -4
- package/compose.env +1 -0
- package/conf.js +1 -1043
- package/deploy/cyberia-client/deploy.sh +13 -11
- package/deploy/cyberia-server/deploy.sh +13 -11
- package/deploy/dd-cyberia/sync-deploy.sh +7 -28
- package/deploy/lib/config.sh +1 -1
- package/deploy/lib/github-actions-logging.sh +3 -2
- package/deploy/lib/host.sh +1 -1
- package/deploy/pwa-microservices-template/deploy.sh +7 -1
- package/deployment.yaml +1 -1
- package/docker-compose.yml +7 -7
- package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
- package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
- package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
- package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
- package/docs/coverage/cyberia/base.css +362 -0
- package/docs/coverage/cyberia/block-navigation.js +82 -0
- package/docs/coverage/cyberia/favicon.png +0 -0
- package/docs/coverage/cyberia/index.html +161 -0
- package/docs/coverage/cyberia/prettify.css +101 -0
- package/docs/coverage/cyberia/prettify.js +937 -0
- package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
- package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
- package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
- package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
- package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
- package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
- package/docs/coverage/cyberia/sorter.js +205 -0
- package/hardhat/package-lock.json +6 -6
- package/hardhat/package.json +2 -2
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/package.json +19 -17
- package/scripts/test-monitor.sh +1 -1
- package/src/api/file/file.router.js +7 -1
- package/src/cli/baremetal.js +1 -1
- package/src/cli/cluster.js +73 -18
- package/src/cli/deploy.js +211 -30
- package/src/cli/docker-compose.js +1 -1
- package/src/cli/index.js +36 -0
- package/src/cli/kubectl.js +4 -3
- package/src/cli/release.js +5 -1
- package/src/cli/repository.js +14 -11
- package/src/cli/run.js +10 -3
- package/src/cli/secrets.js +393 -200
- package/src/cli/socketsecurity.js +260 -0
- package/src/client/components/core/Badge.js +0 -1
- package/src/client/components/core/CssCore.js +11 -1
- package/src/client/components/core/Modal.js +189 -66
- package/src/client/components/core/SearchBox.js +0 -1
- package/src/client/components/core/ToolTip.js +15 -0
- package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
- package/src/client/components/default/AppShellDefault.js +1 -4
- package/src/client/components/itemledger/AppShellItemledger.js +0 -2
- package/src/client/components/underpost/AppShellUnderpost.js +12 -0
- package/src/client/components/underpost/CssUnderpost.js +15 -2
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +8 -0
- package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
- package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +54 -56
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +30 -29
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +54 -25
- package/src/client/ssr/head/Css.js +12 -10
- package/src/client/ssr/views/Cyberia404.js +9 -13
- package/src/client/ssr/views/NotFound.js +75 -0
- package/src/client-builder/client-bundle.js +2 -2
- package/src/index.js +13 -1
- package/src/runtime/engine-cyberia/Dockerfile +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
- package/src/runtime/engine-cyberia/Dockerfile.test +1 -1
- package/src/runtime/engine-cyberia/compose.env +1 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +7 -7
- package/src/server/build/catalog.js +1 -0
- package/src/server/build/execution.js +19 -5
- package/src/server/build/package.js +166 -3
- package/src/server/build/testing.js +2 -1
- package/src/server/network/underpost-gateway.js +113 -14
- package/src/server/runtime/conf.js +57 -7
- package/src/server/runtime/config-scope.js +2 -0
- package/src/server/runtime/process.js +4 -3
- package/src/server/runtime/start.js +11 -2
- package/src/server/security/socketsecurity.js +1735 -0
- package/test/integration/infra/1-security/config-scope.test.js +3 -0
- package/test/integration/infra/1-security/socketsecurity.test.js +1041 -0
- package/test/integration/infra/1-security/sops-secret-store.test.js +201 -71
- package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
- package/test/unit/build-template.test.js +24 -0
- package/test/unit/catalog.test.js +13 -2
- package/test/unit/conf-resolution.test.js +41 -7
- package/test/unit/cyberia/instance-data.test.js +314 -0
- package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
- package/test/unit/deploy-log-table.test.js +38 -0
- package/test/unit/execution-profiles.test.js +1 -0
- package/test/unit/gateway-pod-fetch.test.js +95 -0
- package/test/unit/letsencrypt-issuer.test.js +101 -0
- package/test/unit/package.test.js +118 -0
- package/test/unit/release-bump.test.js +3 -1
- package/test/unit/start-options.test.js +12 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"main": "src/index.js",
|
|
4
4
|
"name": "@underpostnet/cyberia",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.4.0",
|
|
6
6
|
"description": "Cyberia CLI — toolchain for the Cyberia MMO data layer, content pipeline, persistence, gRPC services, and ERC-1155 lifecycle on Hyperledger Besu.",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"start": "node --max-old-space-size=8192 src/server",
|
|
@@ -18,14 +18,17 @@
|
|
|
18
18
|
"docs": "typedoc --options typedoc.json",
|
|
19
19
|
"install:global": "npm install -g prettier",
|
|
20
20
|
"install:test": "npm install -g coveralls-next",
|
|
21
|
-
"install": "npm run install:global && npm run install:test",
|
|
21
|
+
"install:tools": "npm run install:global && npm run install:test",
|
|
22
|
+
"prepare": "node bin socketsecurity --patch-apply",
|
|
22
23
|
"prettier": "prettier --write .",
|
|
23
24
|
"fix": "npm audit fix --force && npm audit",
|
|
24
25
|
"baremetal": "node bin baremetal --dev --commission --ls --create-machine",
|
|
25
26
|
"security:secrets": "gitleaks detect --source . --config .gitleaks.toml --report-path ./gitleaks-report.json --report-format json --redact=0",
|
|
26
27
|
"security:secrets:ci": "gitleaks detect --source . --config .gitleaks.toml --redact=1",
|
|
27
28
|
"security:deps": "npm audit",
|
|
28
|
-
"security": "
|
|
29
|
+
"security:socket": "node bin socketsecurity --audit",
|
|
30
|
+
"security:socket:ci": "node bin socketsecurity --ci",
|
|
31
|
+
"security": "npm run security:secrets:ci && npm run security:deps && npm run security:socket",
|
|
29
32
|
"docker:generate": "node bin docker-compose --generate --deploy-id dd-cyberia --docker-compose-id cyberia",
|
|
30
33
|
"docker:up": "node bin docker-compose --up --deploy-id dd-cyberia --docker-compose-id cyberia",
|
|
31
34
|
"docker:up:build": "node bin docker-compose --up --build --deploy-id dd-cyberia --docker-compose-id cyberia",
|
|
@@ -72,7 +75,7 @@
|
|
|
72
75
|
},
|
|
73
76
|
"homepage": "https://github.com/underpostnet/engine-cyberia#readme",
|
|
74
77
|
"dependencies": {
|
|
75
|
-
"underpost": "^3.
|
|
78
|
+
"@underpostnet/underpost": "^3.4.0",
|
|
76
79
|
"maxrects-packer": "^2.7.3",
|
|
77
80
|
"pngjs": "^7.0.0",
|
|
78
81
|
"jimp": "^1.6.0",
|
|
@@ -89,7 +92,7 @@
|
|
|
89
92
|
"minimatch": "^10.2.2",
|
|
90
93
|
"glob": "^11.0.0",
|
|
91
94
|
"diff": ">=8.0.3",
|
|
92
|
-
"js-yaml": "
|
|
95
|
+
"js-yaml": "^5.4.1",
|
|
93
96
|
"debug": ">=4.3.6",
|
|
94
97
|
"serialize-javascript": ">=7.0.5",
|
|
95
98
|
"router": {
|
|
@@ -105,12 +108,8 @@
|
|
|
105
108
|
"@grpc/grpc-js": "^1.14.4",
|
|
106
109
|
"@grpc/proto-loader": "^0.8.1",
|
|
107
110
|
"@neodrag/vanilla": "^2.3.1",
|
|
108
|
-
"@vitest/coverage-v8": "5.0.0",
|
|
109
111
|
"ag-grid-community": "^36.1.0",
|
|
110
|
-
"allure-vitest": "3.12.0",
|
|
111
112
|
"axios": "^1.20.0",
|
|
112
|
-
"bumpp": "^12.3.0",
|
|
113
|
-
"chai": "^6.2.2",
|
|
114
113
|
"clipboardy": "^5.3.2",
|
|
115
114
|
"cloudinary": "^2.11.0",
|
|
116
115
|
"colors": "^1.4.0",
|
|
@@ -119,11 +118,10 @@
|
|
|
119
118
|
"cookie-parser": "^1.4.7",
|
|
120
119
|
"cors": "^2.8.6",
|
|
121
120
|
"d3": "^7.9.0",
|
|
122
|
-
"dexie": "^4.4.
|
|
121
|
+
"dexie": "^4.4.6",
|
|
123
122
|
"dotenv": "^17.4.2",
|
|
124
123
|
"easymde": "^2.21.0",
|
|
125
124
|
"esbuild": "^0.28.2",
|
|
126
|
-
"escape-string-regexp": "^5.0.0",
|
|
127
125
|
"express": "^5.2.1",
|
|
128
126
|
"express-fileupload": "^1.4.3",
|
|
129
127
|
"express-rate-limit": "^8.7.0",
|
|
@@ -142,15 +140,14 @@
|
|
|
142
140
|
"jsonwebtoken": "^9.0.3",
|
|
143
141
|
"jszip": "^3.10.2",
|
|
144
142
|
"mariadb": "^3.5.4",
|
|
145
|
-
"marked": "^18.0.
|
|
146
|
-
"mongoose": "^9.
|
|
143
|
+
"marked": "^18.0.12",
|
|
144
|
+
"mongoose": "^9.10.0",
|
|
147
145
|
"morgan": "^1.12.0",
|
|
148
|
-
"nodemailer": "^10.0.
|
|
146
|
+
"nodemailer": "^10.0.3",
|
|
149
147
|
"nodemon": "^3.0.1",
|
|
150
148
|
"peer": "^1.0.2",
|
|
151
149
|
"peerjs": "^1.5.5",
|
|
152
150
|
"prom-client": "^15.1.2",
|
|
153
|
-
"read": "^6.0.0",
|
|
154
151
|
"rrule": "^2.8.1",
|
|
155
152
|
"shelljs": "^0.10.0",
|
|
156
153
|
"sitemap": "^9.0.1",
|
|
@@ -163,7 +160,6 @@
|
|
|
163
160
|
"typescript": "6.0.3",
|
|
164
161
|
"validator": "^13.15.35",
|
|
165
162
|
"vanilla-jsoneditor": "^3.13.0",
|
|
166
|
-
"vitest": "5.0.0",
|
|
167
163
|
"winston": "^3.19.0",
|
|
168
164
|
"workbox-background-sync": "^7.4.1",
|
|
169
165
|
"workbox-cacheable-response": "^7.4.1",
|
|
@@ -171,6 +167,12 @@
|
|
|
171
167
|
"workbox-expiration": "^7.4.1",
|
|
172
168
|
"workbox-precaching": "^7.4.1",
|
|
173
169
|
"workbox-routing": "^7.4.1",
|
|
174
|
-
"workbox-strategies": "^7.4.1"
|
|
170
|
+
"workbox-strategies": "^7.4.1",
|
|
171
|
+
"@socketsecurity/cli": "1.1.171",
|
|
172
|
+
"@vitest/coverage-v8": "5.0.0",
|
|
173
|
+
"allure-vitest": "3.12.1",
|
|
174
|
+
"bumpp": "^12.3.0",
|
|
175
|
+
"chai": "^6.2.2",
|
|
176
|
+
"vitest": "5.0.0"
|
|
175
177
|
}
|
|
176
178
|
}
|
package/scripts/test-monitor.sh
CHANGED
|
@@ -24,7 +24,7 @@ MODE=runtime # runtime | instance
|
|
|
24
24
|
ENV=development # development | production
|
|
25
25
|
DEPLOY_ID=dd-test # deploy id (instance mode: parent of conf.instances.json)
|
|
26
26
|
INSTANCE_IDS= # instance mode: csv of ids (default: all in conf.instances.json)
|
|
27
|
-
IMAGE=underpost/wp:v3.
|
|
27
|
+
IMAGE=underpost/wp:v3.4.0 # runtime mode image (instance mode reads image from conf)
|
|
28
28
|
VERSIONS=green # csv of blue/green versions
|
|
29
29
|
REPLICAS=1 # replicas per deployment
|
|
30
30
|
NAMESPACE=default # k8s namespace
|
|
@@ -4,7 +4,7 @@ import { FileController } from './file.controller.js';
|
|
|
4
4
|
|
|
5
5
|
class FileRouter {
|
|
6
6
|
/**
|
|
7
|
-
* authenticated writes, admin-only deletes.
|
|
7
|
+
* authenticated writes, admin-only deletes and full listing.
|
|
8
8
|
* @param {import('../types.js').RouterOptions} options
|
|
9
9
|
* @returns {import('express').Router}
|
|
10
10
|
*/
|
|
@@ -13,6 +13,12 @@ class FileRouter {
|
|
|
13
13
|
router.post(`/:id`, options.authMiddleware, async (req, res) => await FileController.post(req, res, options));
|
|
14
14
|
router.post(`/`, options.authMiddleware, async (req, res) => await FileController.post(req, res, options));
|
|
15
15
|
router.get(`/blob/:id`, async (req, res) => await FileController.get(req, res, options));
|
|
16
|
+
router.get(
|
|
17
|
+
`/all`,
|
|
18
|
+
options.authMiddleware,
|
|
19
|
+
adminGuard,
|
|
20
|
+
async (req, res) => await FileController.get(req, res, options),
|
|
21
|
+
);
|
|
16
22
|
router.get(`/:id`, async (req, res) => await FileController.get(req, res, options));
|
|
17
23
|
router.get(`/`, async (req, res) => await FileController.get(req, res, options));
|
|
18
24
|
router.delete(
|
package/src/cli/baremetal.js
CHANGED
|
@@ -3389,7 +3389,7 @@ fi
|
|
|
3389
3389
|
throw new Error('Invalid token format');
|
|
3390
3390
|
}
|
|
3391
3391
|
|
|
3392
|
-
logger.info('Maas api token generated', { consumer_key,
|
|
3392
|
+
logger.info('Maas api token generated', { consumer_key, token_key });
|
|
3393
3393
|
return { consumer_key, consumer_secret, token_key, token_secret };
|
|
3394
3394
|
},
|
|
3395
3395
|
|
package/src/cli/cluster.js
CHANGED
|
@@ -36,6 +36,13 @@ import os from 'os';
|
|
|
36
36
|
import fs from 'fs-extra';
|
|
37
37
|
import Underpost from '../index.js';
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* @constant CERT_MANAGER_VERSION
|
|
41
|
+
* @description Pinned cert-manager chart version.
|
|
42
|
+
* @memberof UnderpostCluster
|
|
43
|
+
*/
|
|
44
|
+
const CERT_MANAGER_VERSION = 'v1.17.0';
|
|
45
|
+
|
|
39
46
|
const logger = loggerFactory(import.meta);
|
|
40
47
|
|
|
41
48
|
const GATEWAY_API_RELEASE = 'v1.5.1';
|
|
@@ -670,22 +677,12 @@ EOF
|
|
|
670
677
|
}
|
|
671
678
|
}
|
|
672
679
|
|
|
673
|
-
if (options.certManager)
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
--create-namespace \
|
|
680
|
-
--version v1.17.0 \
|
|
681
|
-
--set crds.enabled=true`,
|
|
682
|
-
);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
const letsEncName = 'letsencrypt-prod';
|
|
686
|
-
shellExec(`sudo kubectl delete ClusterIssuer ${letsEncName} --ignore-not-found`);
|
|
687
|
-
shellExec(`sudo kubectl apply -f ${underpostRoot}/manifests/${letsEncName}.yaml -n ${options.namespace}`);
|
|
688
|
-
}
|
|
680
|
+
if (options.certManager)
|
|
681
|
+
Underpost.cluster.installCertManager({
|
|
682
|
+
namespace: options.namespace,
|
|
683
|
+
env: options.dev ? 'development' : 'production',
|
|
684
|
+
solver: Underpost.cluster.acmeSolverFactory({ options, presence: Underpost.cluster.ingressStackPresence() }),
|
|
685
|
+
});
|
|
689
686
|
},
|
|
690
687
|
|
|
691
688
|
/**
|
|
@@ -1100,6 +1097,57 @@ EOF
|
|
|
1100
1097
|
return true;
|
|
1101
1098
|
},
|
|
1102
1099
|
|
|
1100
|
+
/**
|
|
1101
|
+
* @method acmeSolverFactory
|
|
1102
|
+
* @description Which ingress stack answers the HTTP-01 challenge.
|
|
1103
|
+
*
|
|
1104
|
+
* Read from the cluster, like {@link UnderpostCluster.ingressStackPresence}: the
|
|
1105
|
+
* `cluster` runner installs the issuer in a separate invocation that carries no stack
|
|
1106
|
+
* flag, so the flags alone would always name the Contour solver. `--contour` is the
|
|
1107
|
+
* only explicit override; a cluster with the Gateway API data plane installed routes
|
|
1108
|
+
* every hostname through it, so an Ingress solver there is unreachable.
|
|
1109
|
+
* @param {object} params
|
|
1110
|
+
* @param {object} [params.options] - Cluster init options.
|
|
1111
|
+
* @param {{contour: boolean, gateway: boolean}} params.presence - Installed stacks.
|
|
1112
|
+
* @returns {'gateway'|'ingress'} Solver kind.
|
|
1113
|
+
* @memberof UnderpostCluster
|
|
1114
|
+
*/
|
|
1115
|
+
acmeSolverFactory({ options = {}, presence }) {
|
|
1116
|
+
if (options.contour === true && options.gatewayApi !== true) return 'ingress';
|
|
1117
|
+
return options.gatewayApi === true || presence.gateway ? 'gateway' : 'ingress';
|
|
1118
|
+
},
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* @method installCertManager
|
|
1122
|
+
* @description Installs or upgrades cert-manager and applies the Let's Encrypt
|
|
1123
|
+
* ClusterIssuer for the stack in use.
|
|
1124
|
+
*
|
|
1125
|
+
* `upgrade --install` rather than a one-time install: the Gateway API solver is
|
|
1126
|
+
* ignored by a controller that was not started with `enableGatewayAPI`, so a cluster
|
|
1127
|
+
* whose cert-manager predates the Gateway API migration must be re-configured, not
|
|
1128
|
+
* skipped because a pod is Running. The version is pinned, so on a cluster already at
|
|
1129
|
+
* it the upgrade is a no-op.
|
|
1130
|
+
* @param {object} params
|
|
1131
|
+
* @param {string} [params.namespace] - Namespace the deploys' Gateways live in.
|
|
1132
|
+
* @param {string} [params.env] - Environment the routed deploys publish under.
|
|
1133
|
+
* @param {'gateway'|'ingress'} params.solver - Which stack answers the challenge.
|
|
1134
|
+
* @returns {{solver: string, parentRefs: Array<{name: string, namespace: string}>}} What was applied.
|
|
1135
|
+
* @memberof UnderpostCluster
|
|
1136
|
+
*/
|
|
1137
|
+
installCertManager({ namespace = 'default', env = 'production', solver }) {
|
|
1138
|
+
const gatewayValues =
|
|
1139
|
+
solver === 'gateway'
|
|
1140
|
+
? ' --set config.apiVersion=controller.config.cert-manager.io/v1alpha1' +
|
|
1141
|
+
' --set config.kind=ControllerConfiguration --set config.enableGatewayAPI=true'
|
|
1142
|
+
: '';
|
|
1143
|
+
shellExec(`helm repo add jetstack https://charts.jetstack.io --force-update`);
|
|
1144
|
+
shellExec(
|
|
1145
|
+
`helm upgrade --install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace` +
|
|
1146
|
+
` --version ${CERT_MANAGER_VERSION} --set crds.enabled=true${gatewayValues} --wait --timeout 5m`,
|
|
1147
|
+
);
|
|
1148
|
+
return Underpost.deploy.applyLetsEncryptIssuer({ solver, namespace, env });
|
|
1149
|
+
},
|
|
1150
|
+
|
|
1103
1151
|
/**
|
|
1104
1152
|
* @method ingressStackPresence
|
|
1105
1153
|
* @description Which ingress data planes are installed right now.
|
|
@@ -1358,14 +1406,21 @@ EOF
|
|
|
1358
1406
|
}
|
|
1359
1407
|
|
|
1360
1408
|
try {
|
|
1361
|
-
|
|
1409
|
+
// The manifest carries the whole nginx.conf: echoing it as the command line
|
|
1410
|
+
// buries the lines that follow it — the fallback probes above all — under
|
|
1411
|
+
// a page of routing tables on every sync.
|
|
1412
|
+
logger.info('Applying the underpost ingress manifests', { namespace, node: ingressNode });
|
|
1413
|
+
shellExec(
|
|
1414
|
+
`kubectl apply -f - -n ${namespace} <<'EOF'
|
|
1362
1415
|
${underpostIngressManifestsFactory({
|
|
1363
1416
|
namespace,
|
|
1364
1417
|
conf,
|
|
1365
1418
|
nodeName: ingressNode,
|
|
1366
1419
|
})}
|
|
1367
1420
|
EOF
|
|
1368
|
-
|
|
1421
|
+
`,
|
|
1422
|
+
{ disableLog: true },
|
|
1423
|
+
);
|
|
1369
1424
|
} catch (error) {
|
|
1370
1425
|
if (shouldHotReload)
|
|
1371
1426
|
execIngress(`sh -c 'cp /tmp/nginx.previous.conf /tmp/nginx.conf && nginx -s reload -c /tmp/nginx.conf'`, {
|
package/src/cli/deploy.js
CHANGED
|
@@ -50,6 +50,8 @@ import nodePath from 'node:path';
|
|
|
50
50
|
import dotenv from 'dotenv';
|
|
51
51
|
import os from 'node:os';
|
|
52
52
|
import crypto from 'node:crypto';
|
|
53
|
+
import { fileURLToPath } from 'node:url';
|
|
54
|
+
import { dump as yamlDump, load as yamlLoad } from 'js-yaml';
|
|
53
55
|
import { appSecretName } from './app.js';
|
|
54
56
|
import { domainContextFactory } from './domains.js';
|
|
55
57
|
import Underpost from '../index.js';
|
|
@@ -93,6 +95,23 @@ const UPSTREAM_FAILURE_STATUSES = [502, 503, 504];
|
|
|
93
95
|
|
|
94
96
|
const CONTAINER_ENGINE_ROOT = '/home/dd/engine';
|
|
95
97
|
|
|
98
|
+
/**
|
|
99
|
+
* @constant LETSENCRYPT_ISSUER_NAME
|
|
100
|
+
* @description The ClusterIssuer every generated Certificate references.
|
|
101
|
+
* @memberof UnderpostDeploy
|
|
102
|
+
*/
|
|
103
|
+
const LETSENCRYPT_ISSUER_NAME = 'letsencrypt-prod';
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @constant LETSENCRYPT_ISSUER_MANIFEST
|
|
107
|
+
* @description Base ClusterIssuer manifest: account, ACME server and key Secret. Resolved
|
|
108
|
+
* from this module so an installed CLI and a checkout read the same file.
|
|
109
|
+
* @memberof UnderpostDeploy
|
|
110
|
+
*/
|
|
111
|
+
const LETSENCRYPT_ISSUER_MANIFEST = fileURLToPath(
|
|
112
|
+
new URL(`../../manifests/${LETSENCRYPT_ISSUER_NAME}.yaml`, import.meta.url),
|
|
113
|
+
);
|
|
114
|
+
|
|
96
115
|
// The manifest set `deploy --build-manifest` produces for one environment.
|
|
97
116
|
const DEFAULT_MANIFEST_FILES = [
|
|
98
117
|
'deployment.yaml',
|
|
@@ -255,12 +274,68 @@ ${buildKindPorts(fromPort, toPort)}`;
|
|
|
255
274
|
: Underpost.deploy.trafficServiceYamlFactory({ deployId, env, traffic, namespace, fromPort, toPort });
|
|
256
275
|
const escaped = `${deployId}-${env}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
257
276
|
manifest = manifest.replace(new RegExp(`app: ${escaped}-(?:blue|green)`), `app: ${deployId}-${env}-${traffic}`);
|
|
258
|
-
|
|
277
|
+
logger.info('Applying the traffic Service', { deployId, env, traffic, namespace });
|
|
278
|
+
shellExec(
|
|
279
|
+
`kubectl apply -f - -n ${namespace} <<'EOF'
|
|
259
280
|
${manifest}
|
|
260
281
|
EOF
|
|
261
|
-
|
|
282
|
+
`,
|
|
283
|
+
{ disableLog: true },
|
|
284
|
+
);
|
|
262
285
|
return Underpost.deploy.trafficServiceNameFactory({ deployId, env });
|
|
263
286
|
},
|
|
287
|
+
/**
|
|
288
|
+
* Removes the Gateway API objects an earlier model of this deploy left behind, so
|
|
289
|
+
* they stop competing with the consolidated `<deployId>-<env>` Gateway.
|
|
290
|
+
*
|
|
291
|
+
* The per-host model emitted one Gateway and one `<host>-http3` ClientTrafficPolicy
|
|
292
|
+
* per hostname. Left in place they duplicate that hostname's listeners in the same
|
|
293
|
+
* merged set, and the oldest resource retains traffic. `undefined` / `undefined-http3`
|
|
294
|
+
* is the same problem under a different name: the consolidated objects were briefly
|
|
295
|
+
* emitted with an unresolved host in their metadata, and merged listeners are
|
|
296
|
+
* configured by the oldest policy that targets them.
|
|
297
|
+
*
|
|
298
|
+
* The namespace is listed once and only objects that exist are deleted: a sweep that
|
|
299
|
+
* issued a blind delete per host per kind spent two dozen `kubectl` round-trips per
|
|
300
|
+
* sync on names that were gone after the first migration, and buried the one line
|
|
301
|
+
* that mattered when something was actually removed.
|
|
302
|
+
* @param {object} params
|
|
303
|
+
* @param {string[]} params.hosts - Hostnames the deploy's `conf.server.json` declares.
|
|
304
|
+
* @param {string} params.namespace - Namespace the deploy's Gateway objects live in.
|
|
305
|
+
* @returns {{gateways: string[], policies: string[]}} Names removed, per kind.
|
|
306
|
+
* @memberof UnderpostDeploy
|
|
307
|
+
*/
|
|
308
|
+
sweepLegacyGatewayObjects({ hosts = [], namespace = 'default' }) {
|
|
309
|
+
const existing = (kind) =>
|
|
310
|
+
new Set(
|
|
311
|
+
`${
|
|
312
|
+
shellExec(`kubectl get ${kind} -n ${namespace} -o name --ignore-not-found`, {
|
|
313
|
+
stdout: true,
|
|
314
|
+
silent: true,
|
|
315
|
+
silentOnError: true,
|
|
316
|
+
disableLog: true,
|
|
317
|
+
}) ?? ''
|
|
318
|
+
}`
|
|
319
|
+
.split('\n')
|
|
320
|
+
.map((line) => line.trim().split('/').pop())
|
|
321
|
+
.filter(Boolean),
|
|
322
|
+
);
|
|
323
|
+
const legacy = [...new Set(hosts.filter(Boolean)), 'undefined'];
|
|
324
|
+
const gateways = existing('Gateway');
|
|
325
|
+
const policies = existing('ClientTrafficPolicy');
|
|
326
|
+
const removed = {
|
|
327
|
+
gateways: legacy.filter((name) => gateways.has(name)),
|
|
328
|
+
policies: legacy.map((name) => `${name}-http3`).filter((name) => policies.has(name)),
|
|
329
|
+
};
|
|
330
|
+
for (const name of removed.gateways)
|
|
331
|
+
shellExec(`sudo kubectl delete Gateway ${name} -n ${namespace} --ignore-not-found`);
|
|
332
|
+
for (const name of removed.policies)
|
|
333
|
+
shellExec(`sudo kubectl delete ClientTrafficPolicy ${name} -n ${namespace} --ignore-not-found`);
|
|
334
|
+
if (removed.gateways.length > 0 || removed.policies.length > 0)
|
|
335
|
+
logger.warn('Legacy per-host Gateway API objects removed', { namespace, ...removed });
|
|
336
|
+
return removed;
|
|
337
|
+
},
|
|
338
|
+
|
|
264
339
|
/**
|
|
265
340
|
* Removes the route kind owned by the inactive ingress stack. The active
|
|
266
341
|
* route is already published before this runs, so migration never creates a
|
|
@@ -1112,6 +1187,125 @@ spec:
|
|
|
1112
1187
|
kind: ClusterIssuer
|
|
1113
1188
|
secretName: ${host}`;
|
|
1114
1189
|
},
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* The Gateways an ACME HTTP-01 challenge route must attach to: every Gateway the
|
|
1193
|
+
* namespace holds now, plus the one each routed deploy publishes under, whether or
|
|
1194
|
+
* not it exists yet. A challenge HTTPRoute lists them all as parents; the data plane
|
|
1195
|
+
* attaches it to whichever one carries a listener for the challenged hostname, and
|
|
1196
|
+
* reports the rest as unmatched, which is harmless.
|
|
1197
|
+
*
|
|
1198
|
+
* The union rather than either side alone: the live list covers Gateways a deploy
|
|
1199
|
+
* outside `dd.routes` created, the route table covers a deploy that has not been
|
|
1200
|
+
* applied yet — an issuer that only knew the live list would have to be re-applied
|
|
1201
|
+
* after every first deploy before that deploy's certificate could issue.
|
|
1202
|
+
* @param {object} params
|
|
1203
|
+
* @param {string} [params.namespace] - Namespace the Gateways live in.
|
|
1204
|
+
* @param {string} [params.env] - Environment the routed deploys publish under.
|
|
1205
|
+
* @returns {Array<{name: string, namespace: string}>} Parent references, sorted by name.
|
|
1206
|
+
* @memberof UnderpostDeploy
|
|
1207
|
+
*/
|
|
1208
|
+
acmeGatewayParentRefsFactory({ namespace = 'default', env = 'production' } = {}) {
|
|
1209
|
+
const live = `${
|
|
1210
|
+
shellExec(`kubectl get gateway -n ${namespace} -o name --ignore-not-found`, {
|
|
1211
|
+
stdout: true,
|
|
1212
|
+
silent: true,
|
|
1213
|
+
silentOnError: true,
|
|
1214
|
+
disableLog: true,
|
|
1215
|
+
}) ?? ''
|
|
1216
|
+
}`
|
|
1217
|
+
.split('\n')
|
|
1218
|
+
.map((line) => line.trim().split('/').pop())
|
|
1219
|
+
.filter(Boolean);
|
|
1220
|
+
const routed = readDeployRoutes().map((deployId) => Underpost.deploy.gatewayNameFactory({ deployId, env }));
|
|
1221
|
+
return [...new Set([...live, ...routed])].sort().map((name) => ({ name, namespace }));
|
|
1222
|
+
},
|
|
1223
|
+
|
|
1224
|
+
/**
|
|
1225
|
+
* Renders the Let's Encrypt ClusterIssuer for the ingress stack that actually
|
|
1226
|
+
* carries the HTTP-01 challenge.
|
|
1227
|
+
*
|
|
1228
|
+
* The base manifest fixes the account (email, ACME server, key Secret); only the
|
|
1229
|
+
* solver is decided here. With the Gateway API stack, the challenge must be answered
|
|
1230
|
+
* through an HTTPRoute on the deploy's Gateway: an `ingress`-class solver creates an
|
|
1231
|
+
* Ingress nothing on the request path reads, so `/.well-known/acme-challenge/*` is
|
|
1232
|
+
* routed by the application's own HTTPRoute to the workload — a 404 — or, with no
|
|
1233
|
+
* workload behind it, to the maintenance fallback. Every renewal fails that way while
|
|
1234
|
+
* the solver pods sit there ready, and the failure only surfaces when the certificate
|
|
1235
|
+
* expires.
|
|
1236
|
+
* @param {object} params
|
|
1237
|
+
* @param {'gateway'|'ingress'} params.solver - Which stack answers the challenge.
|
|
1238
|
+
* @param {Array<{name: string, namespace: string}>} [params.parentRefs] - Gateways for the `gateway` solver.
|
|
1239
|
+
* @param {string} [params.basePath] - Base ClusterIssuer manifest.
|
|
1240
|
+
* @returns {string} ClusterIssuer YAML.
|
|
1241
|
+
* @throws {Error} When the Gateway solver is requested with no Gateway to attach to.
|
|
1242
|
+
* @memberof UnderpostDeploy
|
|
1243
|
+
*/
|
|
1244
|
+
letsEncryptIssuerYamlFactory({ solver, parentRefs = [], basePath = LETSENCRYPT_ISSUER_MANIFEST }) {
|
|
1245
|
+
const issuer = yamlLoad(fs.readFileSync(basePath, 'utf8'));
|
|
1246
|
+
if (solver === 'gateway') {
|
|
1247
|
+
if (parentRefs.length === 0)
|
|
1248
|
+
throw new Error('[cert-manager] the Gateway API HTTP-01 solver needs at least one Gateway parentRef');
|
|
1249
|
+
issuer.spec.acme.solvers = [
|
|
1250
|
+
{
|
|
1251
|
+
http01: {
|
|
1252
|
+
gatewayHTTPRoute: {
|
|
1253
|
+
parentRefs: parentRefs.map(({ name, namespace }) => ({ name, namespace, kind: 'Gateway' })),
|
|
1254
|
+
},
|
|
1255
|
+
},
|
|
1256
|
+
},
|
|
1257
|
+
];
|
|
1258
|
+
} else issuer.spec.acme.solvers = [{ http01: { ingress: { class: 'contour' } } }];
|
|
1259
|
+
return yamlDump(issuer, { lineWidth: -1, noRefs: true });
|
|
1260
|
+
},
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* Whether the Let's Encrypt ClusterIssuer is installed. False both when it was never
|
|
1264
|
+
* applied and when cert-manager's CRDs are absent, which is the same answer here.
|
|
1265
|
+
* @returns {boolean}
|
|
1266
|
+
* @memberof UnderpostDeploy
|
|
1267
|
+
*/
|
|
1268
|
+
letsEncryptIssuerExists() {
|
|
1269
|
+
return (
|
|
1270
|
+
`${
|
|
1271
|
+
shellExec(`kubectl get clusterissuer ${LETSENCRYPT_ISSUER_NAME} -o name --ignore-not-found`, {
|
|
1272
|
+
stdout: true,
|
|
1273
|
+
silent: true,
|
|
1274
|
+
silentOnError: true,
|
|
1275
|
+
disableLog: true,
|
|
1276
|
+
}) ?? ''
|
|
1277
|
+
}`.trim().length > 0
|
|
1278
|
+
);
|
|
1279
|
+
},
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* Applies the Let's Encrypt ClusterIssuer for the stack in use, so certificates keep
|
|
1283
|
+
* renewing through whichever data plane the deploy's hostnames are served by.
|
|
1284
|
+
* @param {object} params
|
|
1285
|
+
* @param {'gateway'|'ingress'} params.solver - Which stack answers the challenge.
|
|
1286
|
+
* @param {string} [params.namespace] - Namespace the Gateways live in.
|
|
1287
|
+
* @param {string} [params.env] - Environment the routed deploys publish under.
|
|
1288
|
+
* @param {string} [params.basePath] - Base ClusterIssuer manifest.
|
|
1289
|
+
* @returns {{solver: string, parentRefs: Array<{name: string, namespace: string}>}} What was applied.
|
|
1290
|
+
* @memberof UnderpostDeploy
|
|
1291
|
+
*/
|
|
1292
|
+
applyLetsEncryptIssuer({ solver, namespace = 'default', env = 'production', basePath } = {}) {
|
|
1293
|
+
const parentRefs = solver === 'gateway' ? Underpost.deploy.acmeGatewayParentRefsFactory({ namespace, env }) : [];
|
|
1294
|
+
const manifest = Underpost.deploy.letsEncryptIssuerYamlFactory({ solver, parentRefs, basePath });
|
|
1295
|
+
const stagePath = `/dev/shm/underpost-${LETSENCRYPT_ISSUER_NAME}.yaml`;
|
|
1296
|
+
fs.writeFileSync(stagePath, manifest, 'utf8');
|
|
1297
|
+
try {
|
|
1298
|
+
shellExec(`sudo kubectl apply -f ${stagePath}`);
|
|
1299
|
+
} finally {
|
|
1300
|
+
fs.removeSync(stagePath);
|
|
1301
|
+
}
|
|
1302
|
+
logger.info("Let's Encrypt ClusterIssuer applied", {
|
|
1303
|
+
issuer: LETSENCRYPT_ISSUER_NAME,
|
|
1304
|
+
solver,
|
|
1305
|
+
gateways: parentRefs.map(({ name }) => name),
|
|
1306
|
+
});
|
|
1307
|
+
return { solver, parentRefs };
|
|
1308
|
+
},
|
|
1115
1309
|
/**
|
|
1116
1310
|
* Mirrors each deploy's built development manifests into the project tree, and refreshes the
|
|
1117
1311
|
* public default configuration they belong to.
|
|
@@ -2341,34 +2535,15 @@ EOF`);
|
|
|
2341
2535
|
});
|
|
2342
2536
|
}
|
|
2343
2537
|
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
// one, and leaving them behind duplicates that hostname's listeners
|
|
2354
|
-
// in the same merged set. The oldest resource would retain traffic.
|
|
2355
|
-
//
|
|
2356
|
-
// `undefined-http3` is the same problem under a different name: the
|
|
2357
|
-
// consolidated policy was briefly emitted with an unresolved host in
|
|
2358
|
-
// its metadata. Merged listeners are configured by the oldest policy
|
|
2359
|
-
// that targets them, so that object outranks the correctly named one
|
|
2360
|
-
// for as long as it exists.
|
|
2361
|
-
if (options.gatewayApi)
|
|
2362
|
-
for (const name of [host, 'undefined']) {
|
|
2363
|
-
shellExec(`sudo kubectl delete Gateway ${name} -n ${namespace} --ignore-not-found`, { silent: true });
|
|
2364
|
-
shellExec(`sudo kubectl delete ClientTrafficPolicy ${name}-http3 -n ${namespace} --ignore-not-found`, {
|
|
2365
|
-
silent: true,
|
|
2366
|
-
});
|
|
2367
|
-
}
|
|
2368
|
-
if (Underpost.deploy.isCertManagerContext({ host, env, options }))
|
|
2369
|
-
shellExec(`sudo kubectl delete Certificate ${host} -n ${namespace} --ignore-not-found`);
|
|
2370
|
-
}
|
|
2371
|
-
}
|
|
2538
|
+
// The host's route object is left in place and replaced by the `apply`
|
|
2539
|
+
// below. Deleting it first unpublished the hostname for the whole
|
|
2540
|
+
// reconciliation window, so every promote dropped live requests before
|
|
2541
|
+
// the new colour was ever the question.
|
|
2542
|
+
if (!options.disableUpdateProxy && options.gatewayApi)
|
|
2543
|
+
Underpost.deploy.sweepLegacyGatewayObjects({ hosts: Object.keys(confServer), namespace });
|
|
2544
|
+
for (const host of Object.keys(confServer))
|
|
2545
|
+
if (!options.disableUpdateProxy && Underpost.deploy.isCertManagerContext({ host, env, options }))
|
|
2546
|
+
shellExec(`sudo kubectl delete Certificate ${host} -n ${namespace} --ignore-not-found`);
|
|
2372
2547
|
|
|
2373
2548
|
const manifestsPath =
|
|
2374
2549
|
env === 'production'
|
|
@@ -2480,6 +2655,11 @@ EOF`);
|
|
|
2480
2655
|
}
|
|
2481
2656
|
|
|
2482
2657
|
if (Underpost.deploy.isCertManagerContext({ host: Object.keys(confServer)[0], env, options })) {
|
|
2658
|
+
// The issuer's challenge route must list this deploy's Gateway as a parent, or
|
|
2659
|
+
// the certificates applied below can never issue. Re-rendered from the live
|
|
2660
|
+
// Gateway list, so a deploy created after `cluster --cert-manager` is covered.
|
|
2661
|
+
if (options.gatewayApi && Underpost.deploy.letsEncryptIssuerExists())
|
|
2662
|
+
Underpost.deploy.applyLetsEncryptIssuer({ solver: 'gateway', namespace, env });
|
|
2483
2663
|
const secretPath = `./${manifestsPath}/secret.yaml`;
|
|
2484
2664
|
if (fs.existsSync(secretPath) && fs.readFileSync(secretPath, 'utf8').trim()) {
|
|
2485
2665
|
shellExec(`sudo kubectl apply -f ${secretPath} -n ${namespace}`);
|
|
@@ -3143,3 +3323,4 @@ spec:
|
|
|
3143
3323
|
}
|
|
3144
3324
|
|
|
3145
3325
|
export default UnderpostDeploy;
|
|
3326
|
+
export { LETSENCRYPT_ISSUER_MANIFEST, LETSENCRYPT_ISSUER_NAME };
|
|
@@ -192,7 +192,7 @@ services:
|
|
|
192
192
|
MONGO_IMAGE=mongo:latest
|
|
193
193
|
VALKEY_IMAGE=valkey/valkey:latest
|
|
194
194
|
APP_IMAGE=underpost/underpost-engine
|
|
195
|
-
APP_TAG=v3.
|
|
195
|
+
APP_TAG=v3.4.0
|
|
196
196
|
PROXY_IMAGE=nginx:stable-alpine
|
|
197
197
|
PROMETHEUS_IMAGE=prom/prometheus:latest
|
|
198
198
|
GRAFANA_IMAGE=grafana/grafana:latest
|
package/src/cli/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { commitData } from '../client/components/core/CommonJs.js';
|
|
|
7
7
|
import { registerDomainCommand } from './domains.js';
|
|
8
8
|
import { TEST_TIERS, testSuiteNames } from '../server/build/testing.js';
|
|
9
9
|
import { EXECUTION_PROFILES, profileFromOptionsFactory, setExecutionProfile } from '../server/build/execution.js';
|
|
10
|
+
import { SEVERITIES } from '../server/security/socketsecurity.js';
|
|
10
11
|
|
|
11
12
|
import Underpost from '../index.js';
|
|
12
13
|
|
|
@@ -1403,6 +1404,41 @@ program
|
|
|
1403
1404
|
)
|
|
1404
1405
|
.action(Underpost.package.callback);
|
|
1405
1406
|
|
|
1407
|
+
program
|
|
1408
|
+
.command('socketsecurity')
|
|
1409
|
+
.option(
|
|
1410
|
+
'--audit',
|
|
1411
|
+
"Audits the checkout (default). Dependency security: segregation, npm audit, the Socket patch scan and, with SOCKET_CLI_API_TOKEN set, the Socket full scan of the dependency manifests. Source code risk: with SOCKET_CLI_API_TOKEN set, the alerts Socket's registry analysis raises on this project's own published package, as refactoring tasks. Writes the JSON and Markdown reports.",
|
|
1412
|
+
)
|
|
1413
|
+
.option(
|
|
1414
|
+
'--reach',
|
|
1415
|
+
'Runs the full application reachability analysis over the local working tree with the Socket scan (tier 1 evidence); no pushed commit is needed.',
|
|
1416
|
+
)
|
|
1417
|
+
.option(
|
|
1418
|
+
'--ci',
|
|
1419
|
+
'Audits, then exits non-zero when the Socket scan violates the organization policy or a finding remains at --fail-on.',
|
|
1420
|
+
)
|
|
1421
|
+
.option('--fail-on <severity>', `Severity --ci fails at. One of: ${SEVERITIES.join(', ')}. Defaults to high.`)
|
|
1422
|
+
.option(
|
|
1423
|
+
'--fix',
|
|
1424
|
+
'Runs the remediation pass before the audit: npm audit fix, every available Socket patch, socket fix without major upgrades, socket patch apply.',
|
|
1425
|
+
)
|
|
1426
|
+
.option('--major', 'With --fix: lets socket fix apply major upgrades.')
|
|
1427
|
+
.option('--patch-scan', 'Lists the Socket patches available for the installed packages.')
|
|
1428
|
+
.option(
|
|
1429
|
+
'--patch-get <identifier>',
|
|
1430
|
+
'Downloads one patch (UUID, CVE, GHSA or PURL) into .socket/manifest.json and applies it.',
|
|
1431
|
+
)
|
|
1432
|
+
.option('--patch-apply', 'Applies the patch manifest; the prepare hook. A host without the Socket CLI skips it.')
|
|
1433
|
+
.option('--patch-setup', 'Installs the prepare hook in package.json.')
|
|
1434
|
+
.option('--out <dir>', 'Report directory (default: security-reports).')
|
|
1435
|
+
.option('--dry-run', 'Previews --patch-setup; verifies --patch-apply without writing.')
|
|
1436
|
+
.description(
|
|
1437
|
+
'Security audit through Socket.dev: dependency security (advisories, supply chain alerts, reachability, ' +
|
|
1438
|
+
"security patches in .socket/manifest.json) and source code risk (alerts on this project's own code).",
|
|
1439
|
+
)
|
|
1440
|
+
.action(Underpost.socketSecurity.callback);
|
|
1441
|
+
|
|
1406
1442
|
program
|
|
1407
1443
|
.command('release')
|
|
1408
1444
|
.argument('[version]', 'The new version string to set (e.g., "3.1.4"). Defaults to current version.')
|
package/src/cli/kubectl.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { loggerFactory, redactSensitiveText } from '../server/ops/logger.js';
|
|
10
|
-
import { shellExec, sleepSync } from '../server/runtime/process.js';
|
|
10
|
+
import { shellArgumentFactory, shellExec, sleepSync } from '../server/runtime/process.js';
|
|
11
11
|
import { timer } from '../client/components/core/CommonJs.js';
|
|
12
12
|
import Underpost from '../index.js';
|
|
13
13
|
|
|
@@ -275,7 +275,8 @@ class UnderpostKubectl {
|
|
|
275
275
|
},
|
|
276
276
|
|
|
277
277
|
/**
|
|
278
|
-
* Runs a shell command inside a pod container via `kubectl exec`.
|
|
278
|
+
* Runs a shell command inside a pod container via `kubectl exec`. The command is quoted as one
|
|
279
|
+
* argument, so the pod's shell parses it and the host shell never expands it.
|
|
279
280
|
* @param {object} params
|
|
280
281
|
* @param {string} params.podName - Target pod name.
|
|
281
282
|
* @param {string} params.namespace - Pod namespace.
|
|
@@ -287,7 +288,7 @@ class UnderpostKubectl {
|
|
|
287
288
|
* @memberof UnderpostKubectl
|
|
288
289
|
*/
|
|
289
290
|
exec({ podName, namespace, command, retries = DEFAULT_POD_RETRIES, timeoutSeconds = 0 }) {
|
|
290
|
-
const kubectlCmd = `sudo kubectl exec -n ${namespace} -i ${podName} -- sh -c
|
|
291
|
+
const kubectlCmd = `sudo kubectl exec -n ${shellArgumentFactory(namespace)} -i ${shellArgumentFactory(podName)} -- sh -c ${shellArgumentFactory(command)}`;
|
|
291
292
|
return Underpost.kubectl.run(kubectlCmd, {
|
|
292
293
|
context: `exec in pod ${podName}`,
|
|
293
294
|
retries,
|