@serve.zone/interfaces 22.0.0 → 23.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +19 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/appstore/types.d.ts +31 -16
- package/dist_ts/appstore/types.js +208 -11
- package/dist_ts/data/coremail.runtime.d.ts +3 -2
- package/dist_ts/data/coremail.runtime.js +10 -5
- package/dist_ts/data/deploymentoperation.js +6 -20
- package/dist_ts/data/deploymentpreflight.d.ts +2 -6
- package/dist_ts/data/deploymentpreflight.js +1 -1
- package/dist_ts/data/hostedapp.d.ts +40 -14
- package/dist_ts/data/hostedapp.js +90 -1
- package/dist_ts/data/immutableimage.d.ts +30 -1
- package/dist_ts/data/immutableimage.js +103 -1
- package/dist_ts/data/index.d.ts +0 -2
- package/dist_ts/data/index.js +1 -3
- package/dist_ts/data/secret.d.ts +186 -13
- package/dist_ts/data/secret.js +543 -45
- package/dist_ts/data/service.d.ts +0 -39
- package/dist_ts/data/service.js +1 -77
- package/dist_ts/data/settings.d.ts +62 -43
- package/dist_ts/data/settings.js +34 -2
- package/dist_ts/platform/storage.d.ts +13 -14
- package/dist_ts/platform/storage.js +1 -1
- package/dist_ts/platform/storagemigration.d.ts +8 -8
- package/dist_ts/platform/storagemigration.golden.d.ts +2 -2
- package/dist_ts/platform/storagemigration.golden.js +19 -88
- package/dist_ts/platform/storagemigration.js +46 -58
- package/dist_ts/platform/types.d.ts +5 -19
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/dist_ts/requests/hostedapp.d.ts +12 -9
- package/dist_ts/requests/hostedapp.js +38 -1
- package/dist_ts/requests/index.d.ts +1 -3
- package/dist_ts/requests/index.js +2 -4
- package/dist_ts/requests/platform.d.ts +0 -1
- package/dist_ts/requests/secret.d.ts +23 -17
- package/dist_ts/requests/secret.js +161 -2
- package/dist_ts/requests/service.d.ts +0 -13
- package/dist_ts/requests/settings.d.ts +3 -3
- package/dist_ts/runtime.d.ts +78 -26
- package/dist_ts/runtime.js +219 -19
- package/package.json +2 -1
- package/readme.md +110 -29
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/appstore/types.ts +251 -25
- package/ts/data/coremail.runtime.ts +18 -4
- package/ts/data/deploymentoperation.ts +7 -31
- package/ts/data/deploymentpreflight.ts +2 -5
- package/ts/data/hostedapp.ts +144 -18
- package/ts/data/immutableimage.ts +150 -1
- package/ts/data/index.ts +0 -2
- package/ts/data/secret.ts +775 -53
- package/ts/data/service.ts +0 -118
- package/ts/data/settings.ts +70 -80
- package/ts/platform/storage.ts +15 -16
- package/ts/platform/storagemigration.golden.ts +18 -95
- package/ts/platform/storagemigration.ts +57 -87
- package/ts/platform/types.ts +6 -19
- package/ts/plugins.ts +2 -0
- package/ts/requests/hostedapp.ts +50 -9
- package/ts/requests/index.ts +0 -4
- package/ts/requests/platform.ts +0 -1
- package/ts/requests/secret.ts +204 -9
- package/ts/requests/service.ts +0 -16
- package/ts/requests/settings.ts +3 -3
- package/ts/runtime.ts +314 -41
- package/dist_ts/data/secretbundle.d.ts +0 -57
- package/dist_ts/data/secretbundle.js +0 -2
- package/dist_ts/data/secretgroup.d.ts +0 -52
- package/dist_ts/data/secretgroup.js +0 -2
- package/dist_ts/requests/secretbundle.d.ts +0 -75
- package/dist_ts/requests/secretbundle.js +0 -4
- package/dist_ts/requests/secretgroup.d.ts +0 -52
- package/dist_ts/requests/secretgroup.js +0 -4
- package/ts/data/secretbundle.ts +0 -65
- package/ts/data/secretgroup.ts +0 -58
- package/ts/requests/secretbundle.ts +0 -103
- package/ts/requests/secretgroup.ts +0 -74
package/ts/runtime.ts
CHANGED
|
@@ -1,80 +1,353 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
2
|
import type {
|
|
3
3
|
IIdentityCredential,
|
|
4
4
|
IResolvedSecretManifest,
|
|
5
5
|
TSha256Digest,
|
|
6
|
+
TSecretRecipientMetadata,
|
|
6
7
|
} from './data/index.js';
|
|
7
8
|
import {
|
|
9
|
+
createResolvedSecretMaterialEnvelopeContext,
|
|
10
|
+
createSecretRecipientEnrollmentChallengeContext,
|
|
8
11
|
validateResolvedSecretManifest,
|
|
12
|
+
validateSecretRecipientMetadata,
|
|
13
|
+
verifySecretEnvelopeContext,
|
|
9
14
|
verifyResolvedSecretManifestDigest,
|
|
10
15
|
} from './data/secret.js';
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
* Runtime-only material. This module is exported only under the Node condition
|
|
14
|
-
* and is intentionally unreachable from the universal/browser-facing barrel.
|
|
15
|
-
*/
|
|
16
|
-
export interface IResolvedSecretMaterialValue {
|
|
17
|
+
export interface ISealedResolvedSecretMaterialEntry {
|
|
17
18
|
secretVersionId: string;
|
|
18
|
-
|
|
19
|
+
envelope: plugins.smartcrypto.IX25519EnvelopeV1;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
/** Runtime-only material. No plaintext value is represented in this DTO. */
|
|
23
|
+
export interface ISealedResolvedSecretMaterial {
|
|
22
24
|
schemaVersion: 1;
|
|
23
|
-
/** Full value-free manifest
|
|
25
|
+
/** Full schema-v2 value-free manifest used for digest and rollout verification. */
|
|
24
26
|
manifest: IResolvedSecretManifest;
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
+
/** Sorted exact coverage of the manifest's pinned versions. */
|
|
28
|
+
entries: ISealedResolvedSecretMaterialEntry[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ISecretMaterialRequestFence {
|
|
32
|
+
serviceId: string;
|
|
33
|
+
imageRolloutId: string;
|
|
34
|
+
imageRolloutGeneration: number;
|
|
35
|
+
requestedImageDigest: TSha256Digest;
|
|
36
|
+
secretRolloutId: string;
|
|
37
|
+
secretRolloutGeneration: number;
|
|
38
|
+
manifestId: string;
|
|
39
|
+
manifestDigest: TSha256Digest;
|
|
40
|
+
expectedRecipientKeyId: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Request fence plus trusted local scope derived from the verified cluster identity. */
|
|
44
|
+
export interface ISecretMaterialExpectation
|
|
45
|
+
extends ISecretMaterialRequestFence {
|
|
46
|
+
organizationId: string;
|
|
47
|
+
clusterId: string;
|
|
27
48
|
}
|
|
28
49
|
|
|
29
50
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
51
|
+
* Parses every envelope with SmartCrypto, verifies its bound context digest,
|
|
52
|
+
* and checks sorted exact version coverage. It never receives a private key
|
|
53
|
+
* and never decrypts material.
|
|
33
54
|
*/
|
|
34
|
-
export const
|
|
55
|
+
export const verifySealedResolvedSecretMaterial = async (
|
|
35
56
|
materialArg: unknown,
|
|
57
|
+
expectedArg: ISecretMaterialExpectation,
|
|
36
58
|
): Promise<boolean> => {
|
|
37
59
|
if (!materialArg || typeof materialArg !== 'object' || Array.isArray(materialArg)) return false;
|
|
38
|
-
const material = materialArg as Partial<
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
60
|
+
const material = materialArg as Partial<ISealedResolvedSecretMaterial>;
|
|
61
|
+
if (JSON.stringify(Object.keys(material).sort()) !== JSON.stringify([
|
|
62
|
+
'entries',
|
|
63
|
+
'manifest',
|
|
64
|
+
'schemaVersion',
|
|
65
|
+
])) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (material.schemaVersion !== 1 || !material.manifest || !Array.isArray(material.entries)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (validateResolvedSecretManifest(material.manifest).length > 0
|
|
72
|
+
|| !await verifyResolvedSecretManifestDigest(material.manifest)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (material.manifest.organizationId !== expectedArg.organizationId
|
|
76
|
+
|| material.manifest.clusterId !== expectedArg.clusterId
|
|
77
|
+
|| material.manifest.serviceId !== expectedArg.serviceId
|
|
78
|
+
|| material.manifest.imageRolloutId !== expectedArg.imageRolloutId
|
|
79
|
+
|| material.manifest.imageRolloutGeneration !== expectedArg.imageRolloutGeneration
|
|
80
|
+
|| material.manifest.requestedImageDigest !== expectedArg.requestedImageDigest
|
|
81
|
+
|| material.manifest.secretRolloutId !== expectedArg.secretRolloutId
|
|
82
|
+
|| material.manifest.secretRolloutGeneration !== expectedArg.secretRolloutGeneration
|
|
83
|
+
|| material.manifest.id !== expectedArg.manifestId
|
|
84
|
+
|| material.manifest.digest !== expectedArg.manifestDigest) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (material.entries.length > 256
|
|
88
|
+
|| material.entries.length !== material.manifest.entries.length) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
49
91
|
const expectedVersionIds = material.manifest.entries
|
|
50
92
|
.map((entryArg) => entryArg.secretVersionId)
|
|
51
93
|
.sort();
|
|
52
|
-
const actualVersionIds = material.
|
|
53
|
-
|
|
54
|
-
.
|
|
55
|
-
|
|
56
|
-
|
|
94
|
+
const actualVersionIds = material.entries.map((entryArg) => entryArg.secretVersionId);
|
|
95
|
+
if (new Set(actualVersionIds).size !== actualVersionIds.length
|
|
96
|
+
|| JSON.stringify(actualVersionIds) !== JSON.stringify(expectedVersionIds)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const manifestEntriesByVersion = new Map(material.manifest.entries.map((entryArg) => [
|
|
100
|
+
entryArg.secretVersionId,
|
|
101
|
+
entryArg,
|
|
102
|
+
]));
|
|
103
|
+
for (const entry of material.entries) {
|
|
104
|
+
if (!entry || typeof entry !== 'object' || typeof entry.secretVersionId !== 'string') return false;
|
|
105
|
+
if (JSON.stringify(Object.keys(entry).sort()) !== JSON.stringify([
|
|
106
|
+
'envelope',
|
|
107
|
+
'secretVersionId',
|
|
108
|
+
])) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const manifestEntry = manifestEntriesByVersion.get(entry.secretVersionId);
|
|
112
|
+
if (!manifestEntry) return false;
|
|
113
|
+
let envelope: plugins.smartcrypto.IX25519EnvelopeV1;
|
|
114
|
+
try {
|
|
115
|
+
envelope = plugins.smartcrypto.parseX25519Envelope(entry.envelope);
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
if (envelope.recipientKeyId !== expectedArg.expectedRecipientKeyId) return false;
|
|
120
|
+
const context = createResolvedSecretMaterialEnvelopeContext({
|
|
121
|
+
recipientKeyId: expectedArg.expectedRecipientKeyId,
|
|
122
|
+
organizationId: material.manifest.organizationId,
|
|
123
|
+
clusterId: material.manifest.clusterId,
|
|
124
|
+
serviceId: material.manifest.serviceId,
|
|
125
|
+
imageRolloutId: material.manifest.imageRolloutId,
|
|
126
|
+
imageRolloutGeneration: material.manifest.imageRolloutGeneration,
|
|
127
|
+
requestedImageDigest: material.manifest.requestedImageDigest,
|
|
128
|
+
secretRolloutId: material.manifest.secretRolloutId,
|
|
129
|
+
secretRolloutGeneration: material.manifest.secretRolloutGeneration,
|
|
130
|
+
manifestId: material.manifest.id,
|
|
131
|
+
manifestDigest: material.manifest.digest,
|
|
132
|
+
secretId: manifestEntry.secretId,
|
|
133
|
+
secretVersionId: manifestEntry.secretVersionId,
|
|
134
|
+
});
|
|
135
|
+
if (!await verifySecretEnvelopeContext(envelope, context)) return false;
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
57
138
|
};
|
|
58
139
|
|
|
59
|
-
/**
|
|
60
|
-
* Cluster and organization are derived from the verified JWT. A caller cannot
|
|
61
|
-
* select either scope through request fields.
|
|
62
|
-
*/
|
|
140
|
+
/** Organization and cluster are derived exclusively from the verified cluster JWT. */
|
|
63
141
|
export interface IReq_GetResolvedSecretMaterial
|
|
64
142
|
extends plugins.typedrequestInterfaces.implementsTR<
|
|
65
143
|
plugins.typedrequestInterfaces.ITypedRequest,
|
|
66
144
|
IReq_GetResolvedSecretMaterial
|
|
67
145
|
> {
|
|
68
146
|
method: 'getResolvedSecretMaterial';
|
|
147
|
+
request: ISecretMaterialRequestFence & {
|
|
148
|
+
identity: IIdentityCredential;
|
|
149
|
+
};
|
|
150
|
+
response: {
|
|
151
|
+
material: ISealedResolvedSecretMaterial;
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface ISecretRecipientEnrollmentChallengeV1 {
|
|
156
|
+
schemaVersion: 1;
|
|
157
|
+
enrollmentId: string;
|
|
158
|
+
clusterId: string;
|
|
159
|
+
recipientKeyId: string;
|
|
160
|
+
publicKey: string;
|
|
161
|
+
generation: number;
|
|
162
|
+
expiresAt: number;
|
|
163
|
+
/** Sealed random 32-byte challenge; plaintext is never persisted or returned. */
|
|
164
|
+
envelope: plugins.smartcrypto.IX25519EnvelopeV1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Cluster scope is derived from the JWT. The server persists only a one-time
|
|
169
|
+
* challenge hash plus expiry and consumes it with generation CAS; reuse,
|
|
170
|
+
* expiry, mismatch, and replay are rejected. Private keys and challenge
|
|
171
|
+
* plaintext have no persistence DTO.
|
|
172
|
+
*/
|
|
173
|
+
export interface IReq_BeginSecretRecipientEnrollment
|
|
174
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
175
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
176
|
+
IReq_BeginSecretRecipientEnrollment
|
|
177
|
+
> {
|
|
178
|
+
method: 'beginSecretRecipientEnrollment';
|
|
179
|
+
request: {
|
|
180
|
+
identity: IIdentityCredential;
|
|
181
|
+
proposedRecipientKeyId: string;
|
|
182
|
+
proposedPublicKey: string;
|
|
183
|
+
expectedGeneration: number;
|
|
184
|
+
};
|
|
185
|
+
response: {
|
|
186
|
+
challenge: ISecretRecipientEnrollmentChallengeV1;
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export interface IReq_CompleteSecretRecipientEnrollment
|
|
191
|
+
extends plugins.typedrequestInterfaces.implementsTR<
|
|
192
|
+
plugins.typedrequestInterfaces.ITypedRequest,
|
|
193
|
+
IReq_CompleteSecretRecipientEnrollment
|
|
194
|
+
> {
|
|
195
|
+
method: 'completeSecretRecipientEnrollment';
|
|
69
196
|
request: {
|
|
70
197
|
identity: IIdentityCredential;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
manifestDigest: TSha256Digest;
|
|
198
|
+
enrollmentId: string;
|
|
199
|
+
expectedGeneration: number;
|
|
200
|
+
/** Canonical unpadded base64url response containing exactly 32 bytes. */
|
|
201
|
+
response: string;
|
|
76
202
|
};
|
|
77
203
|
response: {
|
|
78
|
-
|
|
204
|
+
recipient: TSecretRecipientMetadata;
|
|
79
205
|
};
|
|
80
206
|
}
|
|
207
|
+
|
|
208
|
+
const canonicalIdentifierRegex = /^[A-Za-z0-9][A-Za-z0-9:._-]{0,199}$/;
|
|
209
|
+
const raw32ByteBase64UrlRegex = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
|
|
210
|
+
|
|
211
|
+
export const validateBeginSecretRecipientEnrollmentRequest = (
|
|
212
|
+
requestArg: unknown,
|
|
213
|
+
): string[] => {
|
|
214
|
+
if (!requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg)) {
|
|
215
|
+
return ['enrollment request must be an object'];
|
|
216
|
+
}
|
|
217
|
+
const request = requestArg as Record<string, unknown>;
|
|
218
|
+
const errors: string[] = [];
|
|
219
|
+
if (JSON.stringify(Object.keys(request).sort()) !== JSON.stringify([
|
|
220
|
+
'expectedGeneration',
|
|
221
|
+
'identity',
|
|
222
|
+
'proposedPublicKey',
|
|
223
|
+
'proposedRecipientKeyId',
|
|
224
|
+
])) {
|
|
225
|
+
errors.push('enrollment request must use its exact schema');
|
|
226
|
+
}
|
|
227
|
+
if (!request.identity || typeof request.identity !== 'object'
|
|
228
|
+
|| typeof (request.identity as Record<string, unknown>).jwt !== 'string') {
|
|
229
|
+
errors.push('enrollment identity must be a JWT credential');
|
|
230
|
+
}
|
|
231
|
+
if (typeof request.proposedRecipientKeyId !== 'string'
|
|
232
|
+
|| !canonicalIdentifierRegex.test(request.proposedRecipientKeyId)) {
|
|
233
|
+
errors.push('proposed recipient key id must be canonical');
|
|
234
|
+
}
|
|
235
|
+
if (typeof request.proposedPublicKey !== 'string'
|
|
236
|
+
|| !raw32ByteBase64UrlRegex.test(request.proposedPublicKey)) {
|
|
237
|
+
errors.push('proposed public key must be canonical base64url raw X25519 material');
|
|
238
|
+
}
|
|
239
|
+
if (!Number.isSafeInteger(request.expectedGeneration)
|
|
240
|
+
|| (request.expectedGeneration as number) < 0) {
|
|
241
|
+
errors.push('expected generation must be a non-negative integer');
|
|
242
|
+
}
|
|
243
|
+
if ('clusterId' in request || 'organizationId' in request) {
|
|
244
|
+
errors.push('enrollment request must not select cluster or organization scope');
|
|
245
|
+
}
|
|
246
|
+
return errors;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export const validateSecretRecipientEnrollmentChallenge = async (
|
|
250
|
+
challengeArg: unknown,
|
|
251
|
+
): Promise<string[]> => {
|
|
252
|
+
if (!challengeArg || typeof challengeArg !== 'object' || Array.isArray(challengeArg)) {
|
|
253
|
+
return ['enrollment challenge must be an object'];
|
|
254
|
+
}
|
|
255
|
+
const challenge = challengeArg as Record<string, unknown>;
|
|
256
|
+
const errors: string[] = [];
|
|
257
|
+
if (JSON.stringify(Object.keys(challenge).sort()) !== JSON.stringify([
|
|
258
|
+
'clusterId',
|
|
259
|
+
'enrollmentId',
|
|
260
|
+
'envelope',
|
|
261
|
+
'expiresAt',
|
|
262
|
+
'generation',
|
|
263
|
+
'publicKey',
|
|
264
|
+
'recipientKeyId',
|
|
265
|
+
'schemaVersion',
|
|
266
|
+
])) {
|
|
267
|
+
errors.push('enrollment challenge must use its exact schema');
|
|
268
|
+
}
|
|
269
|
+
for (const field of ['enrollmentId', 'clusterId', 'recipientKeyId'] as const) {
|
|
270
|
+
if (typeof challenge[field] !== 'string'
|
|
271
|
+
|| !canonicalIdentifierRegex.test(challenge[field] as string)) {
|
|
272
|
+
errors.push(`enrollment challenge ${field} must be canonical`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (challenge.schemaVersion !== 1) errors.push('enrollment challenge schemaVersion must be 1');
|
|
276
|
+
if (typeof challenge.publicKey !== 'string'
|
|
277
|
+
|| !raw32ByteBase64UrlRegex.test(challenge.publicKey)) {
|
|
278
|
+
errors.push('enrollment challenge public key must be canonical');
|
|
279
|
+
}
|
|
280
|
+
if (!Number.isSafeInteger(challenge.generation) || (challenge.generation as number) < 1) {
|
|
281
|
+
errors.push('enrollment challenge generation must be positive');
|
|
282
|
+
}
|
|
283
|
+
if (!Number.isSafeInteger(challenge.expiresAt) || (challenge.expiresAt as number) < 1) {
|
|
284
|
+
errors.push('enrollment challenge expiry must be positive');
|
|
285
|
+
}
|
|
286
|
+
let envelope: plugins.smartcrypto.IX25519EnvelopeV1 | undefined;
|
|
287
|
+
try {
|
|
288
|
+
envelope = plugins.smartcrypto.parseX25519Envelope(challenge.envelope);
|
|
289
|
+
} catch {
|
|
290
|
+
errors.push('enrollment challenge envelope must be strict');
|
|
291
|
+
}
|
|
292
|
+
if (envelope) {
|
|
293
|
+
if (envelope.recipientKeyId !== challenge.recipientKeyId) {
|
|
294
|
+
errors.push('enrollment challenge recipient does not match');
|
|
295
|
+
}
|
|
296
|
+
if (!raw32ByteBase64UrlRegex.test(envelope.ciphertext)) {
|
|
297
|
+
errors.push('enrollment challenge ciphertext must contain exactly 32 bytes');
|
|
298
|
+
}
|
|
299
|
+
if (errors.length === 0) {
|
|
300
|
+
const context = createSecretRecipientEnrollmentChallengeContext({
|
|
301
|
+
enrollmentId: challenge.enrollmentId as string,
|
|
302
|
+
clusterId: challenge.clusterId as string,
|
|
303
|
+
recipientKeyId: challenge.recipientKeyId as string,
|
|
304
|
+
publicKey: challenge.publicKey as string,
|
|
305
|
+
generation: challenge.generation as number,
|
|
306
|
+
expiresAt: challenge.expiresAt as number,
|
|
307
|
+
});
|
|
308
|
+
if (!await verifySecretEnvelopeContext(envelope, context)) {
|
|
309
|
+
errors.push('enrollment challenge context does not match');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return errors;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
export const validateCompleteSecretRecipientEnrollmentRequest = (
|
|
317
|
+
requestArg: unknown,
|
|
318
|
+
): string[] => {
|
|
319
|
+
if (!requestArg || typeof requestArg !== 'object' || Array.isArray(requestArg)) {
|
|
320
|
+
return ['enrollment completion must be an object'];
|
|
321
|
+
}
|
|
322
|
+
const request = requestArg as Record<string, unknown>;
|
|
323
|
+
const errors: string[] = [];
|
|
324
|
+
if (JSON.stringify(Object.keys(request).sort()) !== JSON.stringify([
|
|
325
|
+
'enrollmentId',
|
|
326
|
+
'expectedGeneration',
|
|
327
|
+
'identity',
|
|
328
|
+
'response',
|
|
329
|
+
])) {
|
|
330
|
+
errors.push('enrollment completion must use its exact schema');
|
|
331
|
+
}
|
|
332
|
+
if (!request.identity || typeof request.identity !== 'object'
|
|
333
|
+
|| typeof (request.identity as Record<string, unknown>).jwt !== 'string') {
|
|
334
|
+
errors.push('enrollment identity must be a JWT credential');
|
|
335
|
+
}
|
|
336
|
+
if (typeof request.enrollmentId !== 'string'
|
|
337
|
+
|| !canonicalIdentifierRegex.test(request.enrollmentId)) {
|
|
338
|
+
errors.push('enrollment id must be canonical');
|
|
339
|
+
}
|
|
340
|
+
if (!Number.isSafeInteger(request.expectedGeneration)
|
|
341
|
+
|| (request.expectedGeneration as number) < 0) {
|
|
342
|
+
errors.push('expected generation must be a non-negative integer');
|
|
343
|
+
}
|
|
344
|
+
if (typeof request.response !== 'string' || !raw32ByteBase64UrlRegex.test(request.response)) {
|
|
345
|
+
errors.push('enrollment response must be canonical base64url containing exactly 32 bytes');
|
|
346
|
+
}
|
|
347
|
+
if ('clusterId' in request || 'organizationId' in request) {
|
|
348
|
+
errors.push('enrollment completion must not select cluster or organization scope');
|
|
349
|
+
}
|
|
350
|
+
return errors;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
export { validateSecretRecipientMetadata };
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated Compatibility-only persisted shape. Service-owned Secrets and
|
|
3
|
-
* explicit same-organization SecretSets replace this model.
|
|
4
|
-
*/
|
|
5
|
-
export interface ISecretBundle {
|
|
6
|
-
id: string;
|
|
7
|
-
data: {
|
|
8
|
-
name: string;
|
|
9
|
-
description: string;
|
|
10
|
-
/**
|
|
11
|
-
* determines if the secret is a service or an external secret
|
|
12
|
-
* if external secret additional checks are put in place to protect the secret
|
|
13
|
-
*
|
|
14
|
-
* * service:
|
|
15
|
-
* the bundle belongs to a service and can only be used by that service
|
|
16
|
-
* * npmci:
|
|
17
|
-
* the bundle is a secret bundle that is used by an npmci pipeline
|
|
18
|
-
* production secrets will be omitted in any case
|
|
19
|
-
* * gitzone:
|
|
20
|
-
* the bundle is a secret bundle that is used by a gitzone.
|
|
21
|
-
* Only local environment variables are allowed
|
|
22
|
-
* * external:
|
|
23
|
-
* the bundle is a secret bundle that is used by an external service
|
|
24
|
-
*/
|
|
25
|
-
type: 'service' | 'npmci' | 'gitzone' | 'external';
|
|
26
|
-
/**
|
|
27
|
-
* set this if the secretBundle belongs to a service
|
|
28
|
-
*/
|
|
29
|
-
serviceId?: string;
|
|
30
|
-
/**
|
|
31
|
-
* You can add specific secret groups using this
|
|
32
|
-
*/
|
|
33
|
-
includedSecretGroupIds: string[];
|
|
34
|
-
/**
|
|
35
|
-
* access to this secretBundle also grants access to resources with matching tags
|
|
36
|
-
*/
|
|
37
|
-
includedTags: {
|
|
38
|
-
key: string;
|
|
39
|
-
value?: string;
|
|
40
|
-
}[];
|
|
41
|
-
/**
|
|
42
|
-
* access to this secretBundle also grants access to the images
|
|
43
|
-
*/
|
|
44
|
-
imageClaims: {
|
|
45
|
-
imageId: string;
|
|
46
|
-
permissions: ('read' | 'write')[];
|
|
47
|
-
}[];
|
|
48
|
-
/**
|
|
49
|
-
* authrozations select a specific environment of a config bundle
|
|
50
|
-
*/
|
|
51
|
-
authorizations: Array<ISecretBundleAuthorization>;
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
export interface ISecretBundleAuthorization {
|
|
55
|
-
secretAccessKey: string;
|
|
56
|
-
environment: string;
|
|
57
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated Compatibility-only persisted shape. Migrate to ISecretMetadata
|
|
3
|
-
* and immutable SecretVersion records; do not create new SecretGroups.
|
|
4
|
-
*/
|
|
5
|
-
export interface ISecretGroup {
|
|
6
|
-
/**
|
|
7
|
-
* the insatnce id. This should be a random id, except for default
|
|
8
|
-
*/
|
|
9
|
-
id: string;
|
|
10
|
-
data: {
|
|
11
|
-
name: string;
|
|
12
|
-
description: string;
|
|
13
|
-
/**
|
|
14
|
-
* the key of the secretgroup like CI_RUNNER_TOKEN
|
|
15
|
-
*/
|
|
16
|
-
key: string;
|
|
17
|
-
/**
|
|
18
|
-
* the priority of the secretgroup
|
|
19
|
-
* will be used to determine which secretgroup will be used
|
|
20
|
-
* when there are multiple secretgroups with the same key
|
|
21
|
-
*/
|
|
22
|
-
priority?: number;
|
|
23
|
-
/**
|
|
24
|
-
* any tags that can be used to filter the secretgroup
|
|
25
|
-
* can be used for putting secrets into projects
|
|
26
|
-
*/
|
|
27
|
-
tags: {
|
|
28
|
-
key: string;
|
|
29
|
-
value: string;
|
|
30
|
-
}[];
|
|
31
|
-
/**
|
|
32
|
-
* the values for this secretGroup
|
|
33
|
-
*/
|
|
34
|
-
environments: {
|
|
35
|
-
[key: string]: {
|
|
36
|
-
value: string;
|
|
37
|
-
/**
|
|
38
|
-
* can be used to update the value
|
|
39
|
-
*/
|
|
40
|
-
updateToken?: string;
|
|
41
|
-
/**
|
|
42
|
-
* the linux timestamp of the last update
|
|
43
|
-
*/
|
|
44
|
-
lastUpdated: number;
|
|
45
|
-
history: {
|
|
46
|
-
timestamp: string;
|
|
47
|
-
value: string;
|
|
48
|
-
}[];
|
|
49
|
-
};
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../plugins.js';
|
|
2
|
-
import * as data from '../data/index.js';
|
|
3
|
-
import * as userInterfaces from '../data/user.js';
|
|
4
|
-
export interface IReq_GetSecretBundles extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_GetSecretBundles> {
|
|
5
|
-
method: 'getSecretBundles';
|
|
6
|
-
request: {
|
|
7
|
-
identity: userInterfaces.IIdentity;
|
|
8
|
-
};
|
|
9
|
-
response: {
|
|
10
|
-
secretBundles: data.ISecretBundle[];
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
export interface IReq_GetSecretBundleById extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_GetSecretBundleById> {
|
|
14
|
-
method: 'getSecretBundleById';
|
|
15
|
-
request: {
|
|
16
|
-
identity: userInterfaces.IIdentity;
|
|
17
|
-
secretBundleId: string;
|
|
18
|
-
};
|
|
19
|
-
response: {
|
|
20
|
-
secretBundle: data.ISecretBundle;
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
export interface IReq_CreateSecretBundle extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_CreateSecretBundle> {
|
|
24
|
-
method: 'createSecretBundle';
|
|
25
|
-
request: {
|
|
26
|
-
identity: userInterfaces.IIdentity;
|
|
27
|
-
secretBundle: data.ISecretBundle;
|
|
28
|
-
};
|
|
29
|
-
response: {
|
|
30
|
-
resultSecretBundle: data.ISecretBundle;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
export interface IReq_UpdateSecretBundle extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_UpdateSecretBundle> {
|
|
34
|
-
method: 'updateSecretBundle';
|
|
35
|
-
request: {
|
|
36
|
-
identity: userInterfaces.IIdentity;
|
|
37
|
-
secretBundle: data.ISecretBundle;
|
|
38
|
-
};
|
|
39
|
-
response: {
|
|
40
|
-
resultSecretBundle: data.ISecretBundle;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
export interface IReq_DeleteSecretBundleById extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_DeleteSecretBundleById> {
|
|
44
|
-
method: 'deleteSecretBundleById';
|
|
45
|
-
request: {
|
|
46
|
-
identity: userInterfaces.IIdentity;
|
|
47
|
-
secretBundleId: string;
|
|
48
|
-
};
|
|
49
|
-
response: {
|
|
50
|
-
ok: boolean;
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
export interface IReq_GetSecretBundleByAuthorization extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_GetSecretBundleByAuthorization> {
|
|
54
|
-
method: 'getSecretBundleByAuthorization';
|
|
55
|
-
request: {
|
|
56
|
-
identity: userInterfaces.IIdentity;
|
|
57
|
-
secretBundleAuthorization: data.ISecretBundleAuthorization;
|
|
58
|
-
};
|
|
59
|
-
response: {
|
|
60
|
-
secretBundle: data.ISecretBundle;
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
export interface IReq_GetFlatKeyValueObject extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_GetFlatKeyValueObject> {
|
|
64
|
-
method: 'getFlatKeyValueObject';
|
|
65
|
-
request: {
|
|
66
|
-
identity: userInterfaces.IIdentity;
|
|
67
|
-
seccretBundleId: string;
|
|
68
|
-
secretBundleAuthorization: data.ISecretBundleAuthorization;
|
|
69
|
-
};
|
|
70
|
-
response: {
|
|
71
|
-
flatKeyValueObject: {
|
|
72
|
-
[key: string]: string;
|
|
73
|
-
};
|
|
74
|
-
};
|
|
75
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../plugins.js';
|
|
2
|
-
import * as data from '../data/index.js';
|
|
3
|
-
import * as userInterfaces from '../data/user.js';
|
|
4
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjcmV0YnVuZGxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvcmVxdWVzdHMvc2VjcmV0YnVuZGxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDO0FBQ3pDLE9BQU8sS0FBSyxJQUFJLE1BQU0sa0JBQWtCLENBQUM7QUFDekMsT0FBTyxLQUFLLGNBQWMsTUFBTSxpQkFBaUIsQ0FBQyJ9
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../plugins.js';
|
|
2
|
-
import * as data from '../data/index.js';
|
|
3
|
-
import * as userInterfaces from '../data/user.js';
|
|
4
|
-
export interface IReq_GetSecretGroups extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_GetSecretGroups> {
|
|
5
|
-
method: 'getSecretGroups';
|
|
6
|
-
request: {
|
|
7
|
-
identity: userInterfaces.IIdentity;
|
|
8
|
-
};
|
|
9
|
-
response: {
|
|
10
|
-
secretGroups: data.ISecretGroup[];
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
export interface IReq_GetSecretGroupById extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_GetSecretGroupById> {
|
|
14
|
-
method: 'getSecretGroupById';
|
|
15
|
-
request: {
|
|
16
|
-
identity: userInterfaces.IIdentity;
|
|
17
|
-
secretGroupId: string;
|
|
18
|
-
};
|
|
19
|
-
response: {
|
|
20
|
-
secretGroup: data.ISecretGroup;
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
export interface IReq_CreateSecretGroup extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_CreateSecretGroup> {
|
|
24
|
-
method: 'createSecretGroup';
|
|
25
|
-
request: {
|
|
26
|
-
identity: userInterfaces.IIdentity;
|
|
27
|
-
secretGroup: data.ISecretGroup;
|
|
28
|
-
};
|
|
29
|
-
response: {
|
|
30
|
-
resultSecretGroup: data.ISecretGroup;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
export interface IReq_UpdateSecretGroup extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_UpdateSecretGroup> {
|
|
34
|
-
method: 'updateSecretGroup';
|
|
35
|
-
request: {
|
|
36
|
-
identity: userInterfaces.IIdentity;
|
|
37
|
-
secretGroup: data.ISecretGroup;
|
|
38
|
-
};
|
|
39
|
-
response: {
|
|
40
|
-
resultSecretGroup: data.ISecretGroup;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
export interface IReq_DeleteSecretGroupById extends plugins.typedrequestInterfaces.implementsTR<plugins.typedrequestInterfaces.ITypedRequest, IReq_DeleteSecretGroupById> {
|
|
44
|
-
method: 'deleteSecretGroupById';
|
|
45
|
-
request: {
|
|
46
|
-
identity: userInterfaces.IIdentity;
|
|
47
|
-
secretGroupId: string;
|
|
48
|
-
};
|
|
49
|
-
response: {
|
|
50
|
-
ok: boolean;
|
|
51
|
-
};
|
|
52
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../plugins.js';
|
|
2
|
-
import * as data from '../data/index.js';
|
|
3
|
-
import * as userInterfaces from '../data/user.js';
|
|
4
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjcmV0Z3JvdXAuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9yZXF1ZXN0cy9zZWNyZXRncm91cC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGVBQWUsQ0FBQztBQUN6QyxPQUFPLEtBQUssSUFBSSxNQUFNLGtCQUFrQixDQUFDO0FBQ3pDLE9BQU8sS0FBSyxjQUFjLE1BQU0saUJBQWlCLENBQUMifQ==
|