@hsuite/smart-engines-cli 1.7.0 → 1.8.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/README.md +56 -7
- package/dist/__tests__/helpers/fixtures-image-hygiene.d.ts +18 -0
- package/dist/__tests__/helpers/fixtures-image-hygiene.d.ts.map +1 -0
- package/dist/__tests__/helpers/fixtures-image-hygiene.js +42 -0
- package/dist/__tests__/helpers/fixtures-image-hygiene.js.map +1 -0
- package/dist/_lib/baas.d.ts +12 -17
- package/dist/_lib/baas.d.ts.map +1 -1
- package/dist/_lib/baas.js +13 -1
- package/dist/_lib/baas.js.map +1 -1
- package/dist/_lib/framework.js +2 -2
- package/dist/_lib/image-hygiene.d.ts +36 -0
- package/dist/_lib/image-hygiene.d.ts.map +1 -0
- package/dist/_lib/image-hygiene.js +233 -0
- package/dist/_lib/image-hygiene.js.map +1 -0
- package/dist/_lib/run-fn.d.ts +34 -0
- package/dist/_lib/run-fn.d.ts.map +1 -0
- package/dist/_lib/run-fn.js +15 -0
- package/dist/_lib/run-fn.js.map +1 -0
- package/dist/_lib/subscription-client.d.ts +20 -0
- package/dist/_lib/subscription-client.d.ts.map +1 -1
- package/dist/_lib/subscription-client.js +23 -1
- package/dist/_lib/subscription-client.js.map +1 -1
- package/dist/_lib/transport.d.ts +62 -0
- package/dist/_lib/transport.d.ts.map +1 -0
- package/dist/_lib/transport.js +120 -0
- package/dist/_lib/transport.js.map +1 -0
- package/dist/commands/customer-scope.d.ts +48 -0
- package/dist/commands/customer-scope.d.ts.map +1 -0
- package/dist/commands/customer-scope.js +89 -0
- package/dist/commands/customer-scope.js.map +1 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +3 -2
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/governance.d.ts.map +1 -1
- package/dist/commands/governance.js +13 -2
- package/dist/commands/governance.js.map +1 -1
- package/dist/commands/personhood.d.ts.map +1 -1
- package/dist/commands/personhood.js +9 -4
- package/dist/commands/personhood.js.map +1 -1
- package/dist/commands/redeploy.d.ts.map +1 -1
- package/dist/commands/redeploy.js +3 -2
- package/dist/commands/redeploy.js.map +1 -1
- package/dist/commands/transactions.d.ts.map +1 -1
- package/dist/commands/transactions.js +2 -1
- package/dist/commands/transactions.js.map +1 -1
- package/dist/commands/verify.d.ts +4 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +37 -22
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,6 +99,42 @@ After this, your smart-app boots in cluster-mode automatically.
|
|
|
99
99
|
|
|
100
100
|
---
|
|
101
101
|
|
|
102
|
+
## Plaintext transport: HTTPS by default, one explicit opt-in
|
|
103
|
+
|
|
104
|
+
The CLI mints and carries credentials (`hsuite init`, `hsuite subscribe`), so it
|
|
105
|
+
does not talk plaintext `http://` to a public host. Every command that takes a
|
|
106
|
+
`--server` / `--gateway` — or reaches the validator through
|
|
107
|
+
`_lib/subscription-client.ts` — routes through ONE resolver
|
|
108
|
+
(`src/_lib/transport.ts`); no call site derives the flag from the URL scheme.
|
|
109
|
+
|
|
110
|
+
| Target | Behaviour |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `https://…` | Accepted |
|
|
113
|
+
| `http://localhost`, `http://127.0.0.1`, `http://[::1]` | Accepted — the local-dev flow needs no flag |
|
|
114
|
+
| `http://<cluster-service>.svc[.cluster.local]` | Deferred to the SDK transport policy (which admits it) |
|
|
115
|
+
| any other `http://…` | **Refused** before the client is built |
|
|
116
|
+
|
|
117
|
+
A refusal names the opt-in:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
✗ refusing to send credentials over plaintext http:// to gateway.example.com: the
|
|
121
|
+
smart-engines transport policy admits plain http:// only toward an in-cluster
|
|
122
|
+
service name (*.svc, *.cluster.local). Use https://, or re-run with --insecure
|
|
123
|
+
(or HSUITE_ALLOW_INSECURE=1) to allow it — local development only.
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The opt-in is one mechanism, either spelling:
|
|
127
|
+
|
|
128
|
+
```sh
|
|
129
|
+
hsuite --insecure deploy --manifest ./app.json # global flag, before or after the subcommand
|
|
130
|
+
HSUITE_ALLOW_INSECURE=1 hsuite deploy --manifest ./app.json
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
It is global (`--insecure` is registered once on the root program, not per
|
|
134
|
+
command) and applies to every command in the same invocation. The flag sets
|
|
135
|
+
`HSUITE_ALLOW_INSECURE=1`; only the exact value `1` counts. Use it only against
|
|
136
|
+
a host you operate yourself.
|
|
137
|
+
|
|
102
138
|
## Deployment commands
|
|
103
139
|
|
|
104
140
|
Eight commands take a smart-app from a manifest to a running, manageable
|
|
@@ -111,10 +147,11 @@ deployment on the cluster. They all share base flags:
|
|
|
111
147
|
| `--app-id <id>` | `DEPLOYED_APP_ID` → `SUBSCRIPTION_APP_ID` | App to act on |
|
|
112
148
|
|
|
113
149
|
Every command constructs exactly **one** `BaasClient`
|
|
114
|
-
(`{ hostUrl, appId, pathPrefix: '/host', allowInsecure, timeout }
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
150
|
+
(`{ hostUrl, appId, pathPrefix: '/host', allowInsecure, timeout }`, where
|
|
151
|
+
`allowInsecure` comes from the shared resolver above — `undefined` unless the
|
|
152
|
+
target is loopback or opted in) and authenticates it with the env XRPL signer —
|
|
153
|
+
connecting straight to the public round-robin gateway (no cluster-discovery
|
|
154
|
+
factory, whose per-cluster hostnames aren't reachable from a dev machine).
|
|
118
155
|
|
|
119
156
|
### The manifest (one canonical shape)
|
|
120
157
|
|
|
@@ -166,8 +203,8 @@ same set the smart-app's env schema reads (`XRPL_*`, `SUBSCRIPTION_APP_ID`,
|
|
|
166
203
|
deploy-time / CLI-only knobs (e.g. `SUBSCRIPTION_NFT_SERIAL`, `VALIDATOR_API_KEY`)
|
|
167
204
|
are **not** forwarded. The list lives in
|
|
168
205
|
`packages/hsuite-cli/src/_lib/runtime-env.ts` (`RUNTIME_ENV_KEYS`), mirroring the
|
|
169
|
-
smart-app schema (`
|
|
170
|
-
|
|
206
|
+
smart-app schema (`smart-app/src/config/env-validation.ts` in the
|
|
207
|
+
`HSuiteNetwork/showcase-smart-app` repo).
|
|
171
208
|
|
|
172
209
|
**Precedence:** the forwarded `.env.local` values are the base;
|
|
173
210
|
**`manifest.backend.env` is overlaid last and WINS** for any key it sets. Use
|
|
@@ -264,6 +301,17 @@ The `strategy` field on the manifest/deploy request is **advisory**: a
|
|
|
264
301
|
`recreate` request is coerced to `RollingUpdate` for multi-replica apps (with a
|
|
265
302
|
warning).
|
|
266
303
|
|
|
304
|
+
### Image-hygiene gate (HSUITEPT-5)
|
|
305
|
+
|
|
306
|
+
Right after `docker build` — and before any tag / login / push, also with
|
|
307
|
+
`--skip-build` — `hsuite deploy` scans the built image's compiled output
|
|
308
|
+
(`dist/`, `build/`, `lib/` under the image `WorkingDir`) and **aborts** if it
|
|
309
|
+
ships `.map`, `.d.ts` or `.tsbuildinfo` files: a pentest pulled a customer image
|
|
310
|
+
whose `dist/` read like source. Not skippable. Build the backend with
|
|
311
|
+
`sourceMap` / `declaration` off and `removeComments` on (a production tsconfig),
|
|
312
|
+
or keep those files out of the image (`.dockerignore`). `node_modules/` is never
|
|
313
|
+
scanned.
|
|
314
|
+
|
|
267
315
|
### `hsuite deploy` — THE canonical deploy path
|
|
268
316
|
|
|
269
317
|
`hsuite deploy --manifest <file>` is the **single, canonical path** to deploy a
|
|
@@ -404,7 +452,8 @@ hsuite subscribe --env .env.local --tier builder
|
|
|
404
452
|
## Companion packages
|
|
405
453
|
|
|
406
454
|
- **[`@hsuite/smart-engines-sdk`](https://www.npmjs.com/package/@hsuite/smart-engines-sdk)** — the underlying SDK that `hsuite` wraps. Use it directly when you need finer-grained control over auth, BaaS calls, or chain operations.
|
|
407
|
-
- **[
|
|
455
|
+
- **[showcase-smart-app](https://github.com/HSuiteNetwork/showcase-smart-app)** — a reference NestJS backend + Ionic dApp that demonstrate the full integration.
|
|
456
|
+
- **[boilerplate-smart-app](https://github.com/HSuiteNetwork/boilerplate-smart-app)** — the fork-and-go starting point for a new smart-app.
|
|
408
457
|
|
|
409
458
|
---
|
|
410
459
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canned answers for the image-hygiene gate legs every `buildAndPush` runs
|
|
3
|
+
* (HSUITEPT-5): `docker inspect` → WorkingDir, `docker create` → container id,
|
|
4
|
+
* `docker cp` → the current {@link hygieneTar}, `docker rm` → nothing.
|
|
5
|
+
*
|
|
6
|
+
* Fold into any fake `execFileSync` / `runFn` as its fallthrough
|
|
7
|
+
* (`return hygieneLeg(file, args)`), so the gate sees a CLEAN image by default
|
|
8
|
+
* and the suite under test keeps asserting only its own legs. Plant an
|
|
9
|
+
* offending file with `hygieneTar = tarWith('dist/main.js.map')`.
|
|
10
|
+
*/
|
|
11
|
+
export declare const HYGIENE_CID: string;
|
|
12
|
+
/** Empty archive = clean image. Reassign per test; reset in `beforeEach` where it matters. */
|
|
13
|
+
export declare let hygieneTar: Buffer;
|
|
14
|
+
export declare function setHygieneTar(tar: Buffer): void;
|
|
15
|
+
export declare function hygieneLeg(file: string, args: string[]): unknown;
|
|
16
|
+
/** One-entry ustar archive (enough to plant an offending file). */
|
|
17
|
+
export declare function tarWith(name: string): Buffer;
|
|
18
|
+
//# sourceMappingURL=fixtures-image-hygiene.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures-image-hygiene.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/fixtures-image-hygiene.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,QAAiB,CAAC;AAE1C,8FAA8F;AAC9F,eAAO,IAAI,UAAU,EAAE,MAA2B,CAAC;AAEnD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAMhE;AAED,mEAAmE;AACnE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM5C"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hygieneTar = exports.HYGIENE_CID = void 0;
|
|
4
|
+
exports.setHygieneTar = setHygieneTar;
|
|
5
|
+
exports.hygieneLeg = hygieneLeg;
|
|
6
|
+
exports.tarWith = tarWith;
|
|
7
|
+
/**
|
|
8
|
+
* Canned answers for the image-hygiene gate legs every `buildAndPush` runs
|
|
9
|
+
* (HSUITEPT-5): `docker inspect` → WorkingDir, `docker create` → container id,
|
|
10
|
+
* `docker cp` → the current {@link hygieneTar}, `docker rm` → nothing.
|
|
11
|
+
*
|
|
12
|
+
* Fold into any fake `execFileSync` / `runFn` as its fallthrough
|
|
13
|
+
* (`return hygieneLeg(file, args)`), so the gate sees a CLEAN image by default
|
|
14
|
+
* and the suite under test keeps asserting only its own legs. Plant an
|
|
15
|
+
* offending file with `hygieneTar = tarWith('dist/main.js.map')`.
|
|
16
|
+
*/
|
|
17
|
+
exports.HYGIENE_CID = 'c'.repeat(64);
|
|
18
|
+
/** Empty archive = clean image. Reassign per test; reset in `beforeEach` where it matters. */
|
|
19
|
+
exports.hygieneTar = Buffer.alloc(1024);
|
|
20
|
+
function setHygieneTar(tar) {
|
|
21
|
+
exports.hygieneTar = tar;
|
|
22
|
+
}
|
|
23
|
+
function hygieneLeg(file, args) {
|
|
24
|
+
if (file !== 'docker')
|
|
25
|
+
return undefined;
|
|
26
|
+
if (args[0] === 'inspect')
|
|
27
|
+
return '/app\n';
|
|
28
|
+
if (args[0] === 'create')
|
|
29
|
+
return `${exports.HYGIENE_CID}\n`;
|
|
30
|
+
if (args[0] === 'cp')
|
|
31
|
+
return exports.hygieneTar;
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
/** One-entry ustar archive (enough to plant an offending file). */
|
|
35
|
+
function tarWith(name) {
|
|
36
|
+
const header = Buffer.alloc(512);
|
|
37
|
+
header.write(name, 0, 100, 'utf8');
|
|
38
|
+
header.write('00000000000\0', 124, 12, 'ascii');
|
|
39
|
+
header.write('0', 156, 1, 'ascii');
|
|
40
|
+
return Buffer.concat([header, Buffer.alloc(1024)]);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=fixtures-image-hygiene.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures-image-hygiene.js","sourceRoot":"","sources":["../../../src/__tests__/helpers/fixtures-image-hygiene.ts"],"names":[],"mappings":";;;AAeA,sCAEC;AAED,gCAMC;AAGD,0BAMC;AAlCD;;;;;;;;;GASG;AACU,QAAA,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAE1C,8FAA8F;AACnF,QAAA,UAAU,GAAW,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAEnD,SAAgB,aAAa,CAAC,GAAW;IACvC,kBAAU,GAAG,GAAG,CAAC;AACnB,CAAC;AAED,SAAgB,UAAU,CAAC,IAAY,EAAE,IAAc;IACrD,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC3C,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;QAAE,OAAO,GAAG,mBAAW,IAAI,CAAC;IACpD,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;QAAE,OAAO,kBAAU,CAAC;IACxC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,mEAAmE;AACnE,SAAgB,OAAO,CAAC,IAAY;IAClC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"}
|
package/dist/_lib/baas.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BaasClient } from '@hsuite/smart-engines-sdk';
|
|
2
2
|
import type { EnvMap } from './env';
|
|
3
|
+
import type { RunFn } from './run-fn';
|
|
3
4
|
/** Default per-app deploy timeout — `init` runs the smart-app entity DKG ceremony. */
|
|
4
5
|
export declare const DEFAULT_DEPLOY_TIMEOUT_MS = 240000;
|
|
5
6
|
/**
|
|
@@ -18,23 +19,11 @@ export type RegistryCreds = {
|
|
|
18
19
|
repository: string;
|
|
19
20
|
};
|
|
20
21
|
/**
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
24
|
-
* The return value MATTERS for stdout-capturing invocations (the `docker push`
|
|
25
|
-
* and `docker inspect` legs in {@link buildAndPush}): `execFileSync` returns the
|
|
26
|
-
* child's stdout, and with `encoding: 'utf8'` that is a `string`. We parse the
|
|
27
|
-
* push summary / `RepoDigests` out of it to recover the pushed manifest digest.
|
|
28
|
-
* Legs run with inherited stdio (build/tag/login) return nothing meaningful.
|
|
29
|
-
* Unit tests supply a fake that returns canned push/inspect stdout.
|
|
22
|
+
* The shell-out seam. Declared in `./run-fn` so `image-hygiene.ts` can take it
|
|
23
|
+
* without importing this module back (dependency-cruiser `no-circular`), and
|
|
24
|
+
* re-exported here because every existing caller imports it from `baas`.
|
|
30
25
|
*/
|
|
31
|
-
export type RunFn
|
|
32
|
-
input?: string;
|
|
33
|
-
stdio?: unknown;
|
|
34
|
-
env?: NodeJS.ProcessEnv;
|
|
35
|
-
cwd?: string;
|
|
36
|
-
encoding?: BufferEncoding;
|
|
37
|
-
}) => unknown;
|
|
26
|
+
export type { RunFn } from './run-fn';
|
|
38
27
|
export type ConnectAndAuthOpts = {
|
|
39
28
|
/** Public gateway/host URL. */
|
|
40
29
|
gateway: string;
|
|
@@ -51,7 +40,7 @@ export type ConnectAndAuthOpts = {
|
|
|
51
40
|
clientFactory?: (config: {
|
|
52
41
|
hostUrl: string;
|
|
53
42
|
appId: string;
|
|
54
|
-
allowInsecure: boolean;
|
|
43
|
+
allowInsecure: boolean | undefined;
|
|
55
44
|
pathPrefix: string;
|
|
56
45
|
timeout: number;
|
|
57
46
|
}) => BaasClient;
|
|
@@ -88,6 +77,8 @@ export type BuildAndPushOpts = {
|
|
|
88
77
|
platform?: string;
|
|
89
78
|
/** Test seam — defaults to `execFileSync`. */
|
|
90
79
|
runFn?: RunFn;
|
|
80
|
+
/** Progress sink for the image-hygiene gate (defaults to silent). */
|
|
81
|
+
log?: (message: string) => void;
|
|
91
82
|
/** Push retry attempts (round-robin robot-auth can need a couple). Default 6. */
|
|
92
83
|
pushAttempts?: number;
|
|
93
84
|
/**
|
|
@@ -125,6 +116,10 @@ export declare function imageTarget(registry: RegistryCreds, tag: string): strin
|
|
|
125
116
|
* - optional `<build>` (shell command) in `context`
|
|
126
117
|
* - `docker build --platform linux/amd64 --provenance=false --sbom=false
|
|
127
118
|
* -t hsuite-local/<name>:<tag> -f <dockerfile> <context>`
|
|
119
|
+
* - image-hygiene gate ({@link assertImageHygiene}, HSUITEPT-5): the built
|
|
120
|
+
* image's compiled output must ship no source maps / declarations — runs
|
|
121
|
+
* even with `--skip-build` (the image is about to be deployed either way)
|
|
122
|
+
* and ABORTS before any tag / login / push
|
|
128
123
|
* - `docker tag <local> <server>/<repo>:<tag>`
|
|
129
124
|
* - `docker login <server> -u <username> --password-stdin` ← username a
|
|
130
125
|
* DISCRETE argv element; password on stdin
|
package/dist/_lib/baas.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baas.d.ts","sourceRoot":"","sources":["../../src/_lib/baas.ts"],"names":[],"mappings":"AA+CA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"baas.d.ts","sourceRoot":"","sources":["../../src/_lib/baas.ts"],"names":[],"mappings":"AA+CA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAGpC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAItC,sFAAsF;AACtF,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,gBAAgB,CAAC;AAEpD,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,YAAY,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEtC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE;QACvB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,OAAO,GAAG,SAAS,CAAC;QACnC,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,KAAK,UAAU,CAAC;CAClB,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiBlF;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,aAAa,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,qEAAqE;IACrE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;;;;;;;;OAkBG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAExE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAmI3D;AAwDD,MAAM,MAAM,eAAe,GAAG;IAC5B,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,2BAA2B;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAIzD"}
|
package/dist/_lib/baas.js
CHANGED
|
@@ -55,6 +55,8 @@ const child_process_1 = require("child_process");
|
|
|
55
55
|
const smart_engines_sdk_1 = require("@hsuite/smart-engines-sdk");
|
|
56
56
|
const env_1 = require("./env");
|
|
57
57
|
const docker_1 = require("./docker");
|
|
58
|
+
const image_hygiene_1 = require("./image-hygiene");
|
|
59
|
+
const transport_1 = require("./transport");
|
|
58
60
|
/** Default per-app deploy timeout — `init` runs the smart-app entity DKG ceremony. */
|
|
59
61
|
exports.DEFAULT_DEPLOY_TIMEOUT_MS = 240_000;
|
|
60
62
|
/**
|
|
@@ -75,7 +77,7 @@ async function connectAndAuth(opts) {
|
|
|
75
77
|
const config = {
|
|
76
78
|
hostUrl: opts.gateway,
|
|
77
79
|
appId: opts.appId ?? 'pending',
|
|
78
|
-
allowInsecure: opts.gateway
|
|
80
|
+
allowInsecure: (0, transport_1.resolveAllowInsecure)(opts.gateway),
|
|
79
81
|
pathPrefix: '/host',
|
|
80
82
|
timeout: opts.timeout ?? exports.DEFAULT_DEPLOY_TIMEOUT_MS,
|
|
81
83
|
};
|
|
@@ -104,6 +106,10 @@ function imageTarget(registry, tag) {
|
|
|
104
106
|
* - optional `<build>` (shell command) in `context`
|
|
105
107
|
* - `docker build --platform linux/amd64 --provenance=false --sbom=false
|
|
106
108
|
* -t hsuite-local/<name>:<tag> -f <dockerfile> <context>`
|
|
109
|
+
* - image-hygiene gate ({@link assertImageHygiene}, HSUITEPT-5): the built
|
|
110
|
+
* image's compiled output must ship no source maps / declarations — runs
|
|
111
|
+
* even with `--skip-build` (the image is about to be deployed either way)
|
|
112
|
+
* and ABORTS before any tag / login / push
|
|
107
113
|
* - `docker tag <local> <server>/<repo>:<tag>`
|
|
108
114
|
* - `docker login <server> -u <username> --password-stdin` ← username a
|
|
109
115
|
* DISCRETE argv element; password on stdin
|
|
@@ -187,6 +193,12 @@ function buildAndPush(opts) {
|
|
|
187
193
|
env: dockerEnv,
|
|
188
194
|
});
|
|
189
195
|
}
|
|
196
|
+
// HSUITEPT-5: never push (or deploy an already-pushed) image whose compiled
|
|
197
|
+
// output reads like source. Throws `ImageHygieneError` with the offending
|
|
198
|
+
// entries; nothing below runs. Not skippable — this is the platform-owned
|
|
199
|
+
// choke point for customer image hygiene, and app-side build config is
|
|
200
|
+
// never relied on.
|
|
201
|
+
(0, image_hygiene_1.assertImageHygiene)({ image: localTag, run, env: dockerEnv, log: opts.log });
|
|
190
202
|
// #1679: aim the whole docker sequence at ONE origin when the caller resolved
|
|
191
203
|
// a pin. `opts.registry` itself is left alone — see `pushHost`'s docblock for
|
|
192
204
|
// why collapsing it breaks the downstream fan-out.
|
package/dist/_lib/baas.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baas.js","sourceRoot":"","sources":["../../src/_lib/baas.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"baas.js","sourceRoot":"","sources":["../../src/_lib/baas.ts"],"names":[],"mappings":";;;AA6GA,wCAiBC;AAyDD,kCAEC;AAwCD,oCAmIC;AAyED,kCAIC;AAjbD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,iDAA6C;AAC7C,iEAAuD;AAEvD,+BAA2C;AAC3C,qCAAkE;AAElE,mDAAqD;AACrD,2CAAmD;AAEnD,sFAAsF;AACzE,QAAA,yBAAyB,GAAG,OAAO,CAAC;AAEjD;;;;;;GAMG;AACU,QAAA,sBAAsB,GAAG,aAAa,CAAC;AAuCpD;;;;GAIG;AACI,KAAK,UAAU,cAAc,CAAC,IAAwB;IAC3D,MAAM,MAAM,GAAG,IAAA,wBAAkB,EAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,SAAS;QAC9B,aAAa,EAAE,IAAA,gCAAoB,EAAC,IAAI,CAAC,OAAO,CAAC;QACjD,UAAU,EAAE,OAAO;QACnB,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,iCAAyB;KACnD,CAAC;IACF,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,8BAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC1E,MAAM,MAAM,CAAC,YAAY,CAAC;QACxB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAsDD;;GAEG;AACH,SAAgB,WAAW,CAAC,QAAuB,EAAE,GAAW;IAC9D,OAAO,GAAG,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,SAAgB,YAAY,CAAC,IAAsB;IACjD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,IAAK,4BAAiC,CAAC;IAC7D,MAAM,SAAS,GAAsB;QACnC,GAAG,OAAO,CAAC,GAAG;QACd,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7D,CAAC;IACF,6EAA6E;IAC7E,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAClD,+EAA+E;IAC/E,yEAAyE;IACzE,MAAM,YAAY,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAEnD,+EAA+E;IAC/E,8EAA8E;IAC9E,8EAA8E;IAC9E,2EAA2E;IAC3E,+EAA+E;IAC/E,4EAA4E;IAC5E,8EAA8E;IAC9E,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QAClC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE;YAC5B,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,IAAI,CAAC,OAAO;YACjB,GAAG,EAAE,EAAE,GAAG,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,EAAuB;SAC9D,CAAC,CAAC;IACL,CAAC;IAED,MAAM,QAAQ,GAAG,gBAAgB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;IACzD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACpB,2EAA2E;QAC3E,sEAAsE;QACtE,EAAE;QACF,yEAAyE;QACzE,4EAA4E;QAC5E,iEAAiE;QACjE,2EAA2E;QAC3E,qEAAqE;QACrE,4EAA4E;QAC5E,oEAAoE;QACpE,6DAA6D;QAC7D,sEAAsE;QACtE,yEAAyE;QACzE,oEAAoE;QACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,8BAAsB,CAAC;QACzD,GAAG,CACD,QAAQ,EACR;YACE,OAAO;YACP,YAAY;YACZ,QAAQ;YACR,oBAAoB;YACpB,cAAc;YACd,IAAI;YACJ,QAAQ;YACR,IAAI;YACJ,IAAI,CAAC,UAAU;YACf,IAAI,CAAC,OAAO;SACb,EACD;YACE,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,IAAI,CAAC,OAAO;YACjB,GAAG,EAAE,SAAS;SACf,CACF,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,0EAA0E;IAC1E,0EAA0E;IAC1E,uEAAuE;IACvE,mBAAmB;IACnB,IAAA,kCAAkB,EAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAE5E,8EAA8E;IAC9E,8EAA8E;IAC9E,mDAAmD;IACnD,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAC1E,MAAM,MAAM,GAAG,UAAU;QACvB,CAAC,CAAC,GAAG,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,IAAI,CAAC,GAAG,EAAE;QACzD,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACzC,8EAA8E;IAC9E,wEAAwE;IACxE,MAAM,SAAS,GAAG,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IACrD,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;IAE/E,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,wEAAwE;QACxE,sEAAsE;QACtE,wEAAwE;QACxE,OAAO,4BAA4B,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IACpE,CAAC;IAED,8EAA8E;IAC9E,6CAA6C;IAC7C,GAAG,CACD,QAAQ,EACR,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC,EACtE,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,SAAS,EAAE,CACrE,CAAC;IAEF,4EAA4E;IAC5E,4EAA4E;IAC5E,8EAA8E;IAC9E,kEAAkE;IAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC;IACxC,IAAI,OAAgB,CAAC;IACrB,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,uEAAuE;YACvE,qEAAqE;YACrE,wBAAwB;YACxB,OAAO,GAAG,QAAQ,CAChB,GAAG,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAC3F,CAAC;YACF,MAAM,GAAG,IAAI,CAAC;YACd,MAAM;QACR,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,GAAG,GAAG,CAAC;QAChB,CAAC;IACH,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,4BAA4B,QAAQ,cAAe,OAAiB,EAAE,OAAO,IAAI,SAAS,EAAE,CAC7F,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,6EAA6E;IAC7E,OAAO,4BAA4B,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,IAAA,wBAAe,EAAC,OAAO,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,gGAAgG;AAChG,SAAS,QAAQ,CAAC,GAAY;IAC5B,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,IAAI,GAAG,IAAI,OAAQ,GAAc,CAAC,QAAQ,KAAK,UAAU;QAAE,OAAQ,GAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnG,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,4BAA4B,CACnC,GAAU,EACV,MAAc,EACd,GAAsB,EACtB,UAAyB;IAEzB,yEAAyE;IACzE,8EAA8E;IAC9E,6EAA6E;IAC7E,wBAAwB;IACxB,MAAM,GAAG,GAAG,MAAM,CAAC;IACnB,MAAM,UAAU,GAAG,QAAQ,CACzB,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE;QACzD,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;QACnC,QAAQ,EAAE,MAAM;QAChB,GAAG;KACJ,CAAC,CACH,CAAC;IACF,MAAM,cAAc,GAAG,IAAA,8BAAqB,EAAC,UAAU,CAAC,CAAC;IACzD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,UAAU,GAAG,0DAA0D;YACrE,mFAAmF;YACnF,wDAAwD,CAC3D,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,IAAI,UAAU,KAAK,cAAc,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CACb,uBAAuB,GAAG,2BAA2B,UAAU,oBAAoB;YACjF,UAAU,cAAc,2DAA2D;YACnF,0DAA0D,CAC7D,CAAC;IACJ,CAAC;IACD,OAAO,cAAc,CAAC;AACxB,CAAC;AAWD;;;;;;;GAOG;AACH,SAAgB,WAAW,CAAC,IAAqB;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,IAAK,4BAAiC,CAAC;IAC7D,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACzE,OAAO,IAAI,CAAC,GAAG,CAAC;AAClB,CAAC"}
|
package/dist/_lib/framework.js
CHANGED
|
@@ -15,8 +15,8 @@ exports.resolveSdkVersion = resolveSdkVersion;
|
|
|
15
15
|
*
|
|
16
16
|
* Detection is dependency-driven (reads the project's `package.json`), matching
|
|
17
17
|
* the real showcase apps:
|
|
18
|
-
* - backend (`
|
|
19
|
-
* - frontend (`
|
|
18
|
+
* - backend (`smart-app/` in showcase-smart-app): `@hsuite/smart-engines-sdk` + `@nestjs/core`
|
|
19
|
+
* - frontend (`ionic-dapp/` in showcase-smart-app): `@hsuite/native-connect-*` + `@ionic/angular`
|
|
20
20
|
*
|
|
21
21
|
* This module is PURE I/O-at-the-edge: it reads one file and classifies. No
|
|
22
22
|
* network, no clock.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { RunFn } from './run-fn';
|
|
2
|
+
/** Compiled-output directories a smart-app image conventionally ships under its `WorkingDir`. */
|
|
3
|
+
export declare const IMAGE_HYGIENE_SCAN_DIRS: readonly ["dist", "build", "lib"];
|
|
4
|
+
/** Entry suffixes that must never ship in a customer image. */
|
|
5
|
+
export declare const IMAGE_HYGIENE_FORBIDDEN_SUFFIXES: readonly [".map", ".d.ts", ".tsbuildinfo"];
|
|
6
|
+
/** Thrown when the built image ships source maps / declarations. `violations` lists every entry. */
|
|
7
|
+
export declare class ImageHygieneError extends Error {
|
|
8
|
+
readonly image: string;
|
|
9
|
+
readonly violations: string[];
|
|
10
|
+
constructor(image: string, violations: string[]);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Entry names of a (ustar / PAX / GNU) tar archive. Contents are skipped by
|
|
14
|
+
* size; only names are returned. Tolerant: stops at the first zero block or a
|
|
15
|
+
* truncated header, so a non-archive buffer yields `[]`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function listTarEntries(tar: Buffer): string[];
|
|
18
|
+
/** Entries that must never ship in a customer image. */
|
|
19
|
+
export declare function findImageHygieneViolations(entries: readonly string[]): string[];
|
|
20
|
+
export type AssertImageHygieneOpts = {
|
|
21
|
+
/** Local image ref to scan (the freshly built `hsuite-local/<name>:<tag>`). */
|
|
22
|
+
image: string;
|
|
23
|
+
/** `execFileSync`-shaped seam (see {@link RunFn}). */
|
|
24
|
+
run: RunFn;
|
|
25
|
+
/** Env threaded to every docker leg (carries `DOCKER_HOST`). */
|
|
26
|
+
env: NodeJS.ProcessEnv;
|
|
27
|
+
/** Progress / warning sink. */
|
|
28
|
+
log?: (message: string) => void;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Scan the built image's compiled output and THROW {@link ImageHygieneError}
|
|
32
|
+
* if it ships source maps, declaration files or tsbuildinfo. See the module
|
|
33
|
+
* header for the mechanism and the fail-closed contract.
|
|
34
|
+
*/
|
|
35
|
+
export declare function assertImageHygiene(opts: AssertImageHygieneOpts): void;
|
|
36
|
+
//# sourceMappingURL=image-hygiene.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-hygiene.d.ts","sourceRoot":"","sources":["../../src/_lib/image-hygiene.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAEtC,iGAAiG;AACjG,eAAO,MAAM,uBAAuB,mCAAoC,CAAC;AAEzE,+DAA+D;AAC/D,eAAO,MAAM,gCAAgC,4CAA6C,CAAC;AAqB3F,oGAAoG;AACpG,qBAAa,iBAAkB,SAAQ,KAAK;IAExC,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE;gBADpB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAAE;CAWhC;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BpD;AAED,wDAAwD;AACxD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAE/E;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,GAAG,EAAE,KAAK,CAAC;IACX,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,+BAA+B;IAC/B,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI,CAyCrE"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ImageHygieneError = exports.IMAGE_HYGIENE_FORBIDDEN_SUFFIXES = exports.IMAGE_HYGIENE_SCAN_DIRS = void 0;
|
|
4
|
+
exports.listTarEntries = listTarEntries;
|
|
5
|
+
exports.findImageHygieneViolations = findImageHygieneViolations;
|
|
6
|
+
exports.assertImageHygiene = assertImageHygiene;
|
|
7
|
+
/**
|
|
8
|
+
* `hsuite deploy` image-hygiene gate — HSUITEPT-5 (#3245).
|
|
9
|
+
*
|
|
10
|
+
* The pentest pulled a customer image whose compiled output shipped 98
|
|
11
|
+
* `.js.map`, 49 `.d.ts` and JSDoc-preserved `.js` files: the backend read like
|
|
12
|
+
* source, handing an attacker every guard condition and fallback path before
|
|
13
|
+
* touching the live app. The registry side (tenant Harbor projects served to
|
|
14
|
+
* anonymous pullers) is closed in the provisioner; this closes the image side
|
|
15
|
+
* at the one choke point the platform owns for a customer build — the CLI,
|
|
16
|
+
* right after `docker build` and BEFORE anything is tagged, logged in, or
|
|
17
|
+
* pushed. App-side tsconfig/Dockerfile hygiene is never relied on.
|
|
18
|
+
*
|
|
19
|
+
* Mechanism (no shell inside the image — a distroless runtime has none):
|
|
20
|
+
* docker inspect -f '{{.Config.WorkingDir}}' <image> → where the app lives
|
|
21
|
+
* docker create <image> → throwaway container
|
|
22
|
+
* docker cp <id>:<workdir>/<dir> - → tar of each compiled
|
|
23
|
+
* output dir (dist/…)
|
|
24
|
+
* docker rm -f <id> → always, even on throw
|
|
25
|
+
* The tar is parsed for ENTRY NAMES only; contents are never inspected. Only
|
|
26
|
+
* the conventional compiled-output dirs are scanned, so `node_modules/` (whose
|
|
27
|
+
* published packages legitimately ship `.d.ts` / `.map`) is never a false hit.
|
|
28
|
+
*
|
|
29
|
+
* Fail closed: any offending entry aborts the deploy with the full list and
|
|
30
|
+
* the fix. An absent output dir is "nothing to scan" (a warning when none of
|
|
31
|
+
* the conventional dirs exists at all — the gate could not see the app code).
|
|
32
|
+
* A `docker cp` that fails for any OTHER reason is also fatal — only docker's
|
|
33
|
+
* own not-found signature means "absent"; anything else is a scan that did not
|
|
34
|
+
* happen, and the gate must never report one it did not perform.
|
|
35
|
+
*/
|
|
36
|
+
const path_1 = require("path");
|
|
37
|
+
/** Compiled-output directories a smart-app image conventionally ships under its `WorkingDir`. */
|
|
38
|
+
exports.IMAGE_HYGIENE_SCAN_DIRS = ['dist', 'build', 'lib'];
|
|
39
|
+
/** Entry suffixes that must never ship in a customer image. */
|
|
40
|
+
exports.IMAGE_HYGIENE_FORBIDDEN_SUFFIXES = ['.map', '.d.ts', '.tsbuildinfo'];
|
|
41
|
+
/** `docker cp` of a compiled dist/ is a few MB; the default 1 MiB `execFileSync` buffer is not enough. */
|
|
42
|
+
const TAR_MAX_BUFFER_BYTES = 256 * 1024 * 1024;
|
|
43
|
+
/**
|
|
44
|
+
* Docker's "this path is not in the image" signature — the ONE `docker cp`
|
|
45
|
+
* failure that legitimately means "nothing to scan". Matched against the
|
|
46
|
+
* child's stderr, which docker populates for this wording.
|
|
47
|
+
*
|
|
48
|
+
* Deliberately NOT also matching a bare `no such file or directory`: that is
|
|
49
|
+
* the daemon's OS-level errno text, which appears when the READ failed (a
|
|
50
|
+
* pruned or corrupt overlay2 graph) rather than when the path is absent. The
|
|
51
|
+
* two must not collapse — a real not-found read as fatal only aborts a deploy
|
|
52
|
+
* the operator can re-run, while a broken read taken as "absent" silently
|
|
53
|
+
* disables the gate.
|
|
54
|
+
*/
|
|
55
|
+
const DOCKER_CP_MISSING_RE = /could not find the file/i;
|
|
56
|
+
const TAR_BLOCK = 512;
|
|
57
|
+
/** Thrown when the built image ships source maps / declarations. `violations` lists every entry. */
|
|
58
|
+
class ImageHygieneError extends Error {
|
|
59
|
+
image;
|
|
60
|
+
violations;
|
|
61
|
+
constructor(image, violations) {
|
|
62
|
+
super(`Image hygiene gate (HSUITEPT-5): ${image} ships ${violations.length} file(s) that expose ` +
|
|
63
|
+
`the app's source and must not be deployed:\n` +
|
|
64
|
+
violations.map((v) => ` - ${v}`).join('\n') +
|
|
65
|
+
`\nBuild the backend with sourceMap/declaration OFF and removeComments ON (a production ` +
|
|
66
|
+
`tsconfig), or exclude them from the image (.dockerignore), then re-run \`hsuite deploy\`.`);
|
|
67
|
+
this.image = image;
|
|
68
|
+
this.violations = violations;
|
|
69
|
+
this.name = 'ImageHygieneError';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.ImageHygieneError = ImageHygieneError;
|
|
73
|
+
/**
|
|
74
|
+
* Entry names of a (ustar / PAX / GNU) tar archive. Contents are skipped by
|
|
75
|
+
* size; only names are returned. Tolerant: stops at the first zero block or a
|
|
76
|
+
* truncated header, so a non-archive buffer yields `[]`.
|
|
77
|
+
*/
|
|
78
|
+
function listTarEntries(tar) {
|
|
79
|
+
const names = [];
|
|
80
|
+
let pendingLongName = null;
|
|
81
|
+
for (let off = 0; off + TAR_BLOCK <= tar.length;) {
|
|
82
|
+
const header = tar.subarray(off, off + TAR_BLOCK);
|
|
83
|
+
if (header.every((b) => b === 0))
|
|
84
|
+
break;
|
|
85
|
+
const size = parseInt(cstr(header, 124, 12) || '0', 8) || 0;
|
|
86
|
+
const type = String.fromCharCode(header[156] || 0x30);
|
|
87
|
+
const data = tar.subarray(off + TAR_BLOCK, off + TAR_BLOCK + size);
|
|
88
|
+
off += TAR_BLOCK + Math.ceil(size / TAR_BLOCK) * TAR_BLOCK;
|
|
89
|
+
if (type === 'L') {
|
|
90
|
+
pendingLongName = cstr(data, 0, data.length); // GNU long name
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (type === 'x') {
|
|
94
|
+
pendingLongName = paxPath(data.toString('utf8')) ?? pendingLongName; // PAX extended header
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (type === 'g')
|
|
98
|
+
continue; // PAX global header
|
|
99
|
+
const prefix = cstr(header, 345, 155);
|
|
100
|
+
const name = cstr(header, 0, 100);
|
|
101
|
+
names.push(pendingLongName ?? (prefix ? `${prefix}/${name}` : name));
|
|
102
|
+
pendingLongName = null;
|
|
103
|
+
}
|
|
104
|
+
return names;
|
|
105
|
+
}
|
|
106
|
+
/** Entries that must never ship in a customer image. */
|
|
107
|
+
function findImageHygieneViolations(entries) {
|
|
108
|
+
return entries.filter((e) => exports.IMAGE_HYGIENE_FORBIDDEN_SUFFIXES.some((s) => e.endsWith(s)));
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Scan the built image's compiled output and THROW {@link ImageHygieneError}
|
|
112
|
+
* if it ships source maps, declaration files or tsbuildinfo. See the module
|
|
113
|
+
* header for the mechanism and the fail-closed contract.
|
|
114
|
+
*/
|
|
115
|
+
function assertImageHygiene(opts) {
|
|
116
|
+
const { image, run, env } = opts;
|
|
117
|
+
const log = opts.log ?? (() => undefined);
|
|
118
|
+
const utf8 = { stdio: ['ignore', 'pipe', 'inherit'], encoding: 'utf8', env };
|
|
119
|
+
const workdir = asText(run('docker', ['inspect', '-f', '{{.Config.WorkingDir}}', image], utf8)) || '/';
|
|
120
|
+
const cid = asText(run('docker', ['create', image], utf8));
|
|
121
|
+
if (!cid)
|
|
122
|
+
throw new Error(`image hygiene gate: \`docker create ${image}\` returned no container id`);
|
|
123
|
+
try {
|
|
124
|
+
const violations = [];
|
|
125
|
+
let scanned = 0;
|
|
126
|
+
for (const dir of exports.IMAGE_HYGIENE_SCAN_DIRS) {
|
|
127
|
+
const entries = copyOut(run, `${cid}:${path_1.posix.join(workdir, dir)}`, env);
|
|
128
|
+
if (entries === null)
|
|
129
|
+
continue;
|
|
130
|
+
scanned++;
|
|
131
|
+
const files = entries.filter((e) => !e.endsWith('/'));
|
|
132
|
+
const bad = findImageHygieneViolations(files);
|
|
133
|
+
violations.push(...bad);
|
|
134
|
+
log(` image hygiene: ${dir}/ — ${files.length} file(s), ${bad.length} offending`);
|
|
135
|
+
}
|
|
136
|
+
if (scanned === 0) {
|
|
137
|
+
log(` ⚠ image hygiene: no compiled output found under ${workdir} (${exports.IMAGE_HYGIENE_SCAN_DIRS.join('/')}) — ` +
|
|
138
|
+
`nothing to scan`);
|
|
139
|
+
}
|
|
140
|
+
if (violations.length > 0)
|
|
141
|
+
throw new ImageHygieneError(image, violations);
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
// Best-effort reaping. A throw from a `finally` REPLACES the in-flight
|
|
145
|
+
// exception, so an unchecked `docker rm` would mask an `ImageHygieneError`
|
|
146
|
+
// verdict with a docker error — but a leaked container is still worth a
|
|
147
|
+
// line rather than being silent.
|
|
148
|
+
try {
|
|
149
|
+
run('docker', ['rm', '-f', cid], { stdio: 'ignore', env });
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
log(` ⚠ image hygiene: could not remove scan container ${cid}: ${errorText(err)}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* `docker cp <src> -` → entry names, or `null` when the path genuinely does not
|
|
158
|
+
* exist in the image.
|
|
159
|
+
*
|
|
160
|
+
* FAIL CLOSED: only docker's own not-found signature maps to `null`. Every
|
|
161
|
+
* other failure — a `maxBuffer` overflow on an unexpectedly huge directory, a
|
|
162
|
+
* dead container, a dropped daemon — is rethrown, because the gate cannot
|
|
163
|
+
* distinguish a broken read from an absent directory and must not report a scan
|
|
164
|
+
* it did not perform. stderr is PIPED rather than discarded so that signature
|
|
165
|
+
* is actually visible; with stderr `ignore`d, `execFileSync`'s error carries no
|
|
166
|
+
* docker text and EVERY failure reads as "not found".
|
|
167
|
+
*/
|
|
168
|
+
function copyOut(run, src, env) {
|
|
169
|
+
let out;
|
|
170
|
+
try {
|
|
171
|
+
out = run('docker', ['cp', src, '-'], {
|
|
172
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
173
|
+
env,
|
|
174
|
+
maxBuffer: TAR_MAX_BUFFER_BYTES,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
const text = errorText(err);
|
|
179
|
+
if (DOCKER_CP_MISSING_RE.test(text))
|
|
180
|
+
return null;
|
|
181
|
+
throw new Error(`image hygiene gate: \`docker cp ${src} -\` failed, so the built image could not be ` +
|
|
182
|
+
`scanned — refusing to deploy an unscanned image: ${text}`);
|
|
183
|
+
}
|
|
184
|
+
// A non-Buffer means the seam decoded the archive, and a utf8 round-trip
|
|
185
|
+
// corrupts tar bytes into a buffer that parses as empty — a contract
|
|
186
|
+
// violation that would have read as "clean scan". Fail closed on it.
|
|
187
|
+
if (!Buffer.isBuffer(out)) {
|
|
188
|
+
throw new Error(`image hygiene gate: \`docker cp ${src} -\` returned ${typeof out}, not a tar buffer — ` +
|
|
189
|
+
`the run seam must not set \`encoding\` on this leg`);
|
|
190
|
+
}
|
|
191
|
+
return listTarEntries(out);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Message plus captured stderr of a thrown child-process error, for signature
|
|
195
|
+
* matching.
|
|
196
|
+
*
|
|
197
|
+
* TOTAL by construction: this runs inside `catch` blocks — including the
|
|
198
|
+
* `finally` that reaps the scan container — and `String()` throws on a
|
|
199
|
+
* null-prototype object or a hostile `Symbol.toPrimitive`. A throw from here
|
|
200
|
+
* would REPLACE the in-flight `ImageHygieneError`, i.e. the gate's verdict
|
|
201
|
+
* would be masked by a logging failure. Unstringifiable input therefore
|
|
202
|
+
* degrades to a literal rather than propagating.
|
|
203
|
+
*/
|
|
204
|
+
function errorText(err) {
|
|
205
|
+
try {
|
|
206
|
+
const stderr = err.stderr;
|
|
207
|
+
const tail = Buffer.isBuffer(stderr) ? stderr.toString('utf8') : String(stderr ?? '');
|
|
208
|
+
const head = err instanceof Error ? err.message : String(err);
|
|
209
|
+
return `${head}\n${tail}`.trim();
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return '[unstringifiable error]';
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function asText(out) {
|
|
216
|
+
return typeof out === 'string'
|
|
217
|
+
? out.trim()
|
|
218
|
+
: Buffer.isBuffer(out)
|
|
219
|
+
? out.toString('utf8').trim()
|
|
220
|
+
: '';
|
|
221
|
+
}
|
|
222
|
+
/** NUL-terminated ASCII field of a tar header / GNU long-name block. */
|
|
223
|
+
function cstr(buf, start, len) {
|
|
224
|
+
const slice = buf.subarray(start, start + len);
|
|
225
|
+
const nul = slice.indexOf(0);
|
|
226
|
+
return slice.subarray(0, nul === -1 ? slice.length : nul).toString('utf8');
|
|
227
|
+
}
|
|
228
|
+
/** The `path=` record of a PAX extended header (`"<len> path=<value>\n"` lines). */
|
|
229
|
+
function paxPath(pax) {
|
|
230
|
+
const m = /(?:^|\n)\d+ path=([^\n]*)\n/.exec(pax);
|
|
231
|
+
return m ? m[1] : null;
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=image-hygiene.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-hygiene.js","sourceRoot":"","sources":["../../src/_lib/image-hygiene.ts"],"names":[],"mappings":"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"}
|