@virtru/dsp-sdk 0.3.1 → 0.3.2
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/.rush/temp/chunked-rush-logs/dsp-sdk._phase_build.chunks.jsonl +3 -3
- package/.rush/temp/chunked-rush-logs/dsp-sdk._phase_test.chunks.jsonl +9 -38
- package/.rush/temp/package-deps__phase_build.json +28 -16
- package/.rush/temp/shrinkwrap-deps.json +174 -85
- package/CHANGELOG.json +14 -0
- package/CHANGELOG.md +8 -1
- package/README.md +10 -0
- package/dist/src/gen/config/v1/tagging_pb.d.ts +88 -4
- package/dist/src/gen/config/v1/tagging_pb.js +60 -5
- package/dist/src/gen/policyimportexport/v1/policy_import_export_pb.d.ts +6 -0
- package/dist/src/gen/policyimportexport/v1/policy_import_export_pb.js +2 -1
- package/dist/src/gen/virtru/common/common_pb.d.ts +10 -10
- package/dist/src/gen/virtru/common/common_pb.js +29 -13
- package/dist/src/gen/virtru/policy/certificates/v1/certificates_pb.d.ts +14 -14
- package/dist/src/gen/virtru/policy/certificates/v1/certificates_pb.js +40 -16
- package/dist/src/gen/virtru/policy/objects_pb.d.ts +5 -5
- package/dist/src/gen/virtru/policy/objects_pb.js +26 -12
- package/dist/src/lib/client.js +1 -1
- package/dist/src/lib/utils.js +9 -6
- package/dist/tests/dsp.test.js +1 -4
- package/package.json +8 -3
- package/src/gen/config/formatters/testdata/v1/test_pb.ts +1 -0
- package/src/gen/config/v1/config_connect.ts +111 -0
- package/src/gen/config/v1/tagging_pb.ts +106 -5
- package/src/gen/helloworld/v1/helloworld_connect.ts +49 -0
- package/src/gen/helloworld/v1/helloworld_pb.ts +6 -0
- package/src/gen/kas/nanotdf/v1/nanotdf_rewrap_connect.ts +39 -0
- package/src/gen/kas/nanotdf/v1/nanotdf_rewrap_pb.ts +207 -0
- package/src/gen/policyimportexport/v1/policy_import_export_connect.ts +56 -0
- package/src/gen/policyimportexport/v1/policy_import_export_pb.ts +10 -2
- package/src/gen/shared/v1/shared_connect.ts +61 -0
- package/src/gen/shared/v2/shared_connect.ts +39 -0
- package/src/gen/tagging/pdp/v2/externalprocessors/processor_connect.ts +156 -0
- package/src/gen/tagging/pdp/v2/tagging_connect.ts +73 -0
- package/src/gen/version/v1/version_connect.ts +31 -0
- package/src/gen/virtru/common/common_pb.ts +55 -33
- package/src/gen/virtru/policy/certificates/v1/certificates_pb.ts +188 -143
- package/src/gen/virtru/policy/objects_pb.ts +34 -16
- package/src/gen/web-admin/v1/config_connect.ts +52 -0
- package/src/lib/client.ts +5 -1
- package/src/lib/utils.test.ts +76 -40
- package/src/lib/utils.ts +54 -29
- package/tests/dsp.test.ts +2 -6
- package/update-protos.sh +63 -0
package/src/lib/utils.test.ts
CHANGED
|
@@ -9,14 +9,14 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
|
9
9
|
// Mock all @opentdf imports BEFORE importing utils
|
|
10
10
|
vi.mock('@opentdf/sdk', () => ({
|
|
11
11
|
AuthProviders: {
|
|
12
|
-
refreshAuthProvider: vi.fn()
|
|
12
|
+
refreshAuthProvider: vi.fn(),
|
|
13
13
|
},
|
|
14
14
|
OpenTDF: vi.fn(),
|
|
15
|
-
PermissionDeniedError: class PermissionDeniedError extends Error {}
|
|
15
|
+
PermissionDeniedError: class PermissionDeniedError extends Error {},
|
|
16
16
|
}));
|
|
17
17
|
|
|
18
18
|
vi.mock('@opentdf/sdk/platform', () => ({
|
|
19
|
-
PlatformClient: vi.fn()
|
|
19
|
+
PlatformClient: vi.fn(),
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
22
|
vi.mock('@opentdf/sdk/platform/policy/objects_pb.js', () => ({
|
|
@@ -25,18 +25,19 @@ vi.mock('@opentdf/sdk/platform/policy/objects_pb.js', () => ({
|
|
|
25
25
|
constructor(data?: any) {
|
|
26
26
|
if (data) Object.assign(this, data);
|
|
27
27
|
}
|
|
28
|
-
}
|
|
28
|
+
},
|
|
29
29
|
}));
|
|
30
30
|
|
|
31
31
|
vi.mock('@opentdf/sdk/platform/common/common_pb.js', () => ({
|
|
32
32
|
ActiveStateEnum: {
|
|
33
|
-
ACTIVE: 1
|
|
34
|
-
}
|
|
33
|
+
ACTIVE: 1,
|
|
34
|
+
},
|
|
35
35
|
}));
|
|
36
36
|
|
|
37
37
|
// Polyfill atob if needed
|
|
38
38
|
if (typeof (globalThis as any).atob === 'undefined') {
|
|
39
|
-
(globalThis as any).atob = (b64: string) =>
|
|
39
|
+
(globalThis as any).atob = (b64: string) =>
|
|
40
|
+
Buffer.from(b64, 'base64').toString('binary');
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
// Mock pkijs/asn1js with a way to control them
|
|
@@ -48,7 +49,13 @@ const mockState = {
|
|
|
48
49
|
vi.mock('asn1js', () => ({
|
|
49
50
|
fromBER: vi.fn((_: ArrayBuffer) => {
|
|
50
51
|
if (mockState.asn1Mode === 'bad-der') return { offset: -1 };
|
|
51
|
-
return {
|
|
52
|
+
return {
|
|
53
|
+
offset: 0,
|
|
54
|
+
result: {
|
|
55
|
+
subject: { typesAndValues: [] },
|
|
56
|
+
issuer: { typesAndValues: [] },
|
|
57
|
+
},
|
|
58
|
+
};
|
|
52
59
|
}),
|
|
53
60
|
}));
|
|
54
61
|
|
|
@@ -65,13 +72,13 @@ vi.mock('pkijs', () => {
|
|
|
65
72
|
async verify() {
|
|
66
73
|
return mockState.verifyResult;
|
|
67
74
|
}
|
|
68
|
-
}
|
|
75
|
+
},
|
|
69
76
|
};
|
|
70
77
|
});
|
|
71
78
|
|
|
72
79
|
// Mock DSPClient
|
|
73
80
|
vi.mock('./client', () => ({
|
|
74
|
-
DSPClient: vi.fn()
|
|
81
|
+
DSPClient: vi.fn(),
|
|
75
82
|
}));
|
|
76
83
|
|
|
77
84
|
// NOW import utils
|
|
@@ -87,30 +94,35 @@ describe('validateJWSCertificateChain', () => {
|
|
|
87
94
|
});
|
|
88
95
|
|
|
89
96
|
it('throws error when x5c is empty', async () => {
|
|
90
|
-
await expect(
|
|
91
|
-
|
|
97
|
+
await expect(
|
|
98
|
+
validateJWSCertificateChain([], [{ pem: '---' }] as any)
|
|
99
|
+
).rejects.toThrow(/No certificates provided in x5c array/);
|
|
92
100
|
});
|
|
93
101
|
|
|
94
102
|
it('throws error when too many certificates in x5c', async () => {
|
|
95
103
|
const tooMany = new Array(11).fill('AQI=');
|
|
96
|
-
await expect(
|
|
97
|
-
|
|
104
|
+
await expect(
|
|
105
|
+
validateJWSCertificateChain(tooMany, [{ pem: '---' }] as any)
|
|
106
|
+
).rejects.toThrow(/Too many certificates in x5c/);
|
|
98
107
|
});
|
|
99
108
|
|
|
100
109
|
it('throws error when no trusted roots provided', async () => {
|
|
101
|
-
await expect(
|
|
102
|
-
|
|
110
|
+
await expect(
|
|
111
|
+
validateJWSCertificateChain(['AQI='], [] as any)
|
|
112
|
+
).rejects.toThrow(/No trusted root certificates provided/);
|
|
103
113
|
});
|
|
104
114
|
|
|
105
115
|
it('fails on invalid base64 in x5c', async () => {
|
|
106
|
-
await expect(
|
|
107
|
-
|
|
116
|
+
await expect(
|
|
117
|
+
validateJWSCertificateChain(['***not-base64***'], [{ pem: '---' }] as any)
|
|
118
|
+
).rejects.toThrow(/Failed to parse certificate at x5c\[0]/);
|
|
108
119
|
});
|
|
109
120
|
|
|
110
121
|
it('fails on ASN.1 parse error', async () => {
|
|
111
122
|
mockState.asn1Mode = 'bad-der';
|
|
112
|
-
await expect(
|
|
113
|
-
|
|
123
|
+
await expect(
|
|
124
|
+
validateJWSCertificateChain(['AQI='], [{ pem: '---' }] as any)
|
|
125
|
+
).rejects.toThrow(/Invalid DER encoding/);
|
|
114
126
|
});
|
|
115
127
|
|
|
116
128
|
it('returns valid true on successful verification', async () => {
|
|
@@ -123,7 +135,10 @@ describe('validateJWSCertificateChain', () => {
|
|
|
123
135
|
|
|
124
136
|
it('returns warnings when some trusted roots fail to parse', async () => {
|
|
125
137
|
const pem = '-----BEGIN CERTIFICATE-----\nAQI=\n-----END CERTIFICATE-----';
|
|
126
|
-
const res = await validateJWSCertificateChain(['AQI='], [
|
|
138
|
+
const res = await validateJWSCertificateChain(['AQI='], [
|
|
139
|
+
{ pem: '' },
|
|
140
|
+
{ pem },
|
|
141
|
+
] as any);
|
|
127
142
|
expect(res.valid).toBe(true);
|
|
128
143
|
expect(res.validationResult.warnings).toHaveLength(1);
|
|
129
144
|
expect(res.validationResult.warnings[0]).toMatch(/has no PEM data/);
|
|
@@ -166,7 +181,9 @@ describe('getTrustedCertificates', () => {
|
|
|
166
181
|
updateClientPublicKey: vi.fn().mockResolvedValue(undefined),
|
|
167
182
|
};
|
|
168
183
|
|
|
169
|
-
(AuthProviders.refreshAuthProvider as any).mockResolvedValue(
|
|
184
|
+
(AuthProviders.refreshAuthProvider as any).mockResolvedValue(
|
|
185
|
+
mockAuthProvider
|
|
186
|
+
);
|
|
170
187
|
|
|
171
188
|
// Mock certificate service
|
|
172
189
|
mockCertificateService = {
|
|
@@ -211,9 +228,11 @@ describe('getTrustedCertificates', () => {
|
|
|
211
228
|
|
|
212
229
|
expect(result).toHaveLength(2);
|
|
213
230
|
expect(result).toEqual([mockCertificate1, mockCertificate2]);
|
|
214
|
-
expect(
|
|
231
|
+
expect(
|
|
232
|
+
mockCertificateService.listCertificatesByNamespace
|
|
233
|
+
).toHaveBeenCalledWith({
|
|
215
234
|
namespaceId: 'namespace-1',
|
|
216
|
-
pagination: { limit:
|
|
235
|
+
pagination: { limit: 100, offset: 0 },
|
|
217
236
|
});
|
|
218
237
|
});
|
|
219
238
|
|
|
@@ -229,18 +248,20 @@ describe('getTrustedCertificates', () => {
|
|
|
229
248
|
mockCertificateService.listCertificatesByNamespace
|
|
230
249
|
.mockResolvedValueOnce({
|
|
231
250
|
certificates: [mockCert1, mockCert2],
|
|
232
|
-
pagination: { nextOffset:
|
|
251
|
+
pagination: { nextOffset: 0 },
|
|
233
252
|
})
|
|
234
253
|
.mockResolvedValueOnce({
|
|
235
254
|
certificates: [mockCert3],
|
|
236
|
-
pagination: { nextOffset:
|
|
255
|
+
pagination: { nextOffset: 0 },
|
|
237
256
|
});
|
|
238
257
|
|
|
239
258
|
const result = await getTrustedCertificates(mockOptions);
|
|
240
259
|
|
|
241
260
|
expect(result).toHaveLength(3);
|
|
242
261
|
expect(result).toEqual([mockCert1, mockCert2, mockCert3]);
|
|
243
|
-
expect(
|
|
262
|
+
expect(
|
|
263
|
+
mockCertificateService.listCertificatesByNamespace
|
|
264
|
+
).toHaveBeenCalledTimes(2);
|
|
244
265
|
});
|
|
245
266
|
|
|
246
267
|
it('handles pagination correctly (multiple pages)', async () => {
|
|
@@ -256,26 +277,32 @@ describe('getTrustedCertificates', () => {
|
|
|
256
277
|
mockCertificateService.listCertificatesByNamespace
|
|
257
278
|
.mockResolvedValueOnce({
|
|
258
279
|
certificates: [mockCert1, mockCert2],
|
|
259
|
-
pagination: { nextOffset:
|
|
280
|
+
pagination: { nextOffset: 100 },
|
|
260
281
|
})
|
|
261
282
|
// Second page
|
|
262
283
|
.mockResolvedValueOnce({
|
|
263
284
|
certificates: [mockCert3],
|
|
264
|
-
pagination: { nextOffset:
|
|
285
|
+
pagination: { nextOffset: 0 },
|
|
265
286
|
});
|
|
266
287
|
|
|
267
288
|
const result = await getTrustedCertificates(mockOptions);
|
|
268
289
|
|
|
269
290
|
expect(result).toHaveLength(3);
|
|
270
291
|
expect(result).toEqual([mockCert1, mockCert2, mockCert3]);
|
|
271
|
-
expect(
|
|
272
|
-
|
|
292
|
+
expect(
|
|
293
|
+
mockCertificateService.listCertificatesByNamespace
|
|
294
|
+
).toHaveBeenCalledTimes(2);
|
|
295
|
+
expect(
|
|
296
|
+
mockCertificateService.listCertificatesByNamespace
|
|
297
|
+
).toHaveBeenNthCalledWith(1, {
|
|
273
298
|
namespaceId: 'namespace-1',
|
|
274
|
-
pagination: { limit:
|
|
299
|
+
pagination: { limit: 100, offset: 0 },
|
|
275
300
|
});
|
|
276
|
-
expect(
|
|
301
|
+
expect(
|
|
302
|
+
mockCertificateService.listCertificatesByNamespace
|
|
303
|
+
).toHaveBeenNthCalledWith(2, {
|
|
277
304
|
namespaceId: 'namespace-1',
|
|
278
|
-
pagination: { limit:
|
|
305
|
+
pagination: { limit: 100, offset: 100 },
|
|
279
306
|
});
|
|
280
307
|
});
|
|
281
308
|
|
|
@@ -286,7 +313,7 @@ describe('getTrustedCertificates', () => {
|
|
|
286
313
|
|
|
287
314
|
mockCertificateService.listCertificatesByNamespace.mockResolvedValue({
|
|
288
315
|
certificates: [],
|
|
289
|
-
pagination: { nextOffset:
|
|
316
|
+
pagination: { nextOffset: 0 },
|
|
290
317
|
});
|
|
291
318
|
|
|
292
319
|
const result = await getTrustedCertificates(mockOptions);
|
|
@@ -304,7 +331,9 @@ describe('getTrustedCertificates', () => {
|
|
|
304
331
|
new Error('Certificate service error')
|
|
305
332
|
);
|
|
306
333
|
|
|
307
|
-
await expect(getTrustedCertificates(mockOptions)).rejects.toThrow(
|
|
334
|
+
await expect(getTrustedCertificates(mockOptions)).rejects.toThrow(
|
|
335
|
+
'Certificate service error'
|
|
336
|
+
);
|
|
308
337
|
});
|
|
309
338
|
|
|
310
339
|
it('handles errors from namespace listing', async () => {
|
|
@@ -312,7 +341,9 @@ describe('getTrustedCertificates', () => {
|
|
|
312
341
|
new Error('Namespace listing error')
|
|
313
342
|
);
|
|
314
343
|
|
|
315
|
-
await expect(getTrustedCertificates(mockOptions)).rejects.toThrow(
|
|
344
|
+
await expect(getTrustedCertificates(mockOptions)).rejects.toThrow(
|
|
345
|
+
'Namespace listing error'
|
|
346
|
+
);
|
|
316
347
|
});
|
|
317
348
|
|
|
318
349
|
it('initializes auth provider correctly', async () => {
|
|
@@ -353,7 +384,9 @@ describe('getTrustedCertificates', () => {
|
|
|
353
384
|
});
|
|
354
385
|
|
|
355
386
|
it('returns same Certificate type compatible with validateJWSCertificateChain', async () => {
|
|
356
|
-
const mockCert = {
|
|
387
|
+
const mockCert = {
|
|
388
|
+
pem: '-----BEGIN CERTIFICATE-----\nAQI=\n-----END CERTIFICATE-----',
|
|
389
|
+
};
|
|
357
390
|
|
|
358
391
|
mockPlatformClient.v1.namespace.listNamespaces.mockResolvedValue({
|
|
359
392
|
namespaces: [{ id: 'namespace-1' }],
|
|
@@ -361,7 +394,7 @@ describe('getTrustedCertificates', () => {
|
|
|
361
394
|
|
|
362
395
|
mockCertificateService.listCertificatesByNamespace.mockResolvedValue({
|
|
363
396
|
certificates: [mockCert],
|
|
364
|
-
pagination: { nextOffset:
|
|
397
|
+
pagination: { nextOffset: 0 },
|
|
365
398
|
});
|
|
366
399
|
|
|
367
400
|
const result = await getTrustedCertificates(mockOptions);
|
|
@@ -370,7 +403,10 @@ describe('getTrustedCertificates', () => {
|
|
|
370
403
|
expect(result[0].pem).toBeDefined();
|
|
371
404
|
|
|
372
405
|
// Integration test: verify compatibility with validateJWSCertificateChain
|
|
373
|
-
const validationResult = await validateJWSCertificateChain(
|
|
406
|
+
const validationResult = await validateJWSCertificateChain(
|
|
407
|
+
['AQI='],
|
|
408
|
+
result
|
|
409
|
+
);
|
|
374
410
|
expect(validationResult.valid).toBe(true);
|
|
375
411
|
});
|
|
376
412
|
});
|
package/src/lib/utils.ts
CHANGED
|
@@ -4,16 +4,21 @@
|
|
|
4
4
|
* Your use of this file is governed by the Virtu Terms of Service <https://www.virtru.com/terms-of-service/>.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type {AuthProvider} from '@opentdf/sdk';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
import type { AuthProvider } from '@opentdf/sdk';
|
|
8
|
+
import {
|
|
9
|
+
AuthProviders,
|
|
10
|
+
OpenTDF,
|
|
11
|
+
PermissionDeniedError,
|
|
12
|
+
type Source,
|
|
13
|
+
} from '@opentdf/sdk';
|
|
14
|
+
import { PlatformClient } from '@opentdf/sdk/platform';
|
|
15
|
+
import { ActiveStateEnum } from '@opentdf/sdk/platform/common/common_pb.js';
|
|
16
|
+
import type { Certificate } from '../gen/virtru/policy/objects_pb';
|
|
17
|
+
import type { Interceptor as Inter } from '@connectrpc/connect';
|
|
13
18
|
import * as asn1js from 'asn1js';
|
|
14
19
|
import * as pkijs from 'pkijs';
|
|
15
|
-
import {MAX_CERT_BYTES, MAX_CERTS} from
|
|
16
|
-
import {DSPClient} from './client';
|
|
20
|
+
import { MAX_CERT_BYTES, MAX_CERTS } from './consts';
|
|
21
|
+
import { DSPClient } from './client';
|
|
17
22
|
|
|
18
23
|
export type Interceptor = Inter;
|
|
19
24
|
|
|
@@ -160,20 +165,27 @@ export async function getUserEntitlements(options: {
|
|
|
160
165
|
refreshToken: options.refreshToken,
|
|
161
166
|
});
|
|
162
167
|
const accessToken = await authProvider.oidcAuth.get();
|
|
163
|
-
const userEntitlementsResponse = await fetch(
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
168
|
+
const userEntitlementsResponse = await fetch(
|
|
169
|
+
`${options.platformEndpoint}/shared/entitlements`,
|
|
170
|
+
{
|
|
171
|
+
body: JSON.stringify({}),
|
|
172
|
+
headers: {
|
|
173
|
+
Authorization: `Bearer ${accessToken}`,
|
|
174
|
+
'Content-Type': 'application/json',
|
|
175
|
+
},
|
|
176
|
+
method: 'POST',
|
|
177
|
+
}
|
|
178
|
+
);
|
|
171
179
|
|
|
172
180
|
return (await userEntitlementsResponse.json()) as GetUserEntitlementsResponse;
|
|
173
181
|
}
|
|
174
182
|
|
|
175
|
-
export const flattenObligations = (
|
|
176
|
-
|
|
183
|
+
export const flattenObligations = (
|
|
184
|
+
obligations: SupportedObligations
|
|
185
|
+
): string[] =>
|
|
186
|
+
Object.values(obligations).flatMap(
|
|
187
|
+
(feature: ObligationFeatureMap) => feature?.fully_qualified_names || []
|
|
188
|
+
);
|
|
177
189
|
|
|
178
190
|
/**
|
|
179
191
|
* Helper function to retrieve certificates for a specific namespace with pagination handling.
|
|
@@ -195,10 +207,11 @@ async function getCertificatesForNamespace(
|
|
|
195
207
|
const limit = 100;
|
|
196
208
|
|
|
197
209
|
while (true) {
|
|
198
|
-
const response =
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
210
|
+
const response =
|
|
211
|
+
await dspClient.v1.certificateService.listCertificatesByNamespace({
|
|
212
|
+
namespaceId,
|
|
213
|
+
pagination: { limit, offset },
|
|
214
|
+
});
|
|
202
215
|
|
|
203
216
|
if (response.certificates?.length > 0) {
|
|
204
217
|
certificates.push(...response.certificates);
|
|
@@ -304,7 +317,6 @@ export async function getTrustedCertificates(options: {
|
|
|
304
317
|
certificates.push(...namespaceCerts);
|
|
305
318
|
}
|
|
306
319
|
} catch (error) {
|
|
307
|
-
console.error('Error retrieving namespace certificates:', error);
|
|
308
320
|
throw error;
|
|
309
321
|
}
|
|
310
322
|
|
|
@@ -314,7 +326,9 @@ export async function getTrustedCertificates(options: {
|
|
|
314
326
|
/**
|
|
315
327
|
* Helper to get a string representation of a pkijs Name.
|
|
316
328
|
*/
|
|
317
|
-
function getNameStringSafe(
|
|
329
|
+
function getNameStringSafe(
|
|
330
|
+
name: pkijs.RelativeDistinguishedNames | undefined
|
|
331
|
+
): string {
|
|
318
332
|
if (!name) return '';
|
|
319
333
|
try {
|
|
320
334
|
// pkijs Name to string can be derived from typesAndValues
|
|
@@ -354,7 +368,9 @@ function base64ToArrayBuffer(b64: string): ArrayBuffer {
|
|
|
354
368
|
throw new Error('Empty base64 data');
|
|
355
369
|
}
|
|
356
370
|
if (approxBytes > MAX_CERT_BYTES) {
|
|
357
|
-
throw new Error(
|
|
371
|
+
throw new Error(
|
|
372
|
+
`Certificate exceeds maximum allowed size of ${MAX_CERT_BYTES} bytes`
|
|
373
|
+
);
|
|
358
374
|
}
|
|
359
375
|
const binaryString = atob(normalized);
|
|
360
376
|
const bytes = new Uint8Array(binaryString.length);
|
|
@@ -419,7 +435,10 @@ export async function validateJWSCertificateChain(
|
|
|
419
435
|
if (x5c.length > MAX_CERTS) {
|
|
420
436
|
throw new Error(`Too many certificates in x5c (max ${MAX_CERTS})`);
|
|
421
437
|
}
|
|
422
|
-
if (
|
|
438
|
+
if (
|
|
439
|
+
!Array.isArray(trustedRootCertificates) ||
|
|
440
|
+
trustedRootCertificates.length === 0
|
|
441
|
+
) {
|
|
423
442
|
throw new Error('No trusted root certificates provided');
|
|
424
443
|
}
|
|
425
444
|
|
|
@@ -480,8 +499,12 @@ export async function validateJWSCertificateChain(
|
|
|
480
499
|
const leaf = chainCertificates[0];
|
|
481
500
|
const leafSubject = getNameStringSafe(leaf.subject);
|
|
482
501
|
const leafIssuer = getNameStringSafe(leaf.issuer);
|
|
483
|
-
const baseMsg =
|
|
484
|
-
|
|
502
|
+
const baseMsg =
|
|
503
|
+
verificationResult.resultMessage ||
|
|
504
|
+
'Certificate chain validation failed';
|
|
505
|
+
throw new Error(
|
|
506
|
+
`${baseMsg} (leaf subject='${leafSubject}', issuer='${leafIssuer}')`
|
|
507
|
+
);
|
|
485
508
|
}
|
|
486
509
|
|
|
487
510
|
return {
|
|
@@ -493,7 +516,9 @@ export async function validateJWSCertificateChain(
|
|
|
493
516
|
};
|
|
494
517
|
} catch (error) {
|
|
495
518
|
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
496
|
-
const warnSuffix = warnings.length
|
|
519
|
+
const warnSuffix = warnings.length
|
|
520
|
+
? `; warnings: ${warnings.join(' | ')}`
|
|
521
|
+
: '';
|
|
497
522
|
throw new Error(`Validation error: ${msg}${warnSuffix}`);
|
|
498
523
|
}
|
|
499
524
|
}
|
package/tests/dsp.test.ts
CHANGED
|
@@ -70,8 +70,8 @@ test('should create a new DSP instance with platformUrl', () => {
|
|
|
70
70
|
});
|
|
71
71
|
|
|
72
72
|
test('should throw an error if platformUrl is not provided', () => {
|
|
73
|
-
expect(() => new DSP({} as never)).toThrowError(
|
|
74
|
-
'
|
|
73
|
+
expect(() => new DSP({ authProvider } as never)).toThrowError(
|
|
74
|
+
'platformUrl is required for DSP client'
|
|
75
75
|
);
|
|
76
76
|
});
|
|
77
77
|
|
|
@@ -117,7 +117,3 @@ test('should throw if authProvider is missing', () => {
|
|
|
117
117
|
'Client ID or custom AuthProvider must be defined'
|
|
118
118
|
);
|
|
119
119
|
});
|
|
120
|
-
|
|
121
|
-
test('should throw an error if platformUrl is not provided', async () => {
|
|
122
|
-
expect(() => new DSP({} as never)).toThrowError();
|
|
123
|
-
});
|
package/update-protos.sh
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
REPO_URL="git@github.com:virtru-corp/data-security-platform.git"
|
|
6
|
+
CLONE_DIR=".dsp"
|
|
7
|
+
DEST_DIR="src/gen/"
|
|
8
|
+
FILES_TO_COPY="sdk_web/src/gen/"
|
|
9
|
+
|
|
10
|
+
echo "Cloning repository..."
|
|
11
|
+
if test -d "$CLONE_DIR"; then
|
|
12
|
+
echo "Repo exists? Try: rm -rf '$CLONE_DIR'"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
git clone --depth=1 "$REPO_URL" "$CLONE_DIR"
|
|
16
|
+
|
|
17
|
+
echo "Updating repo buf.gen.yaml to generate for dsp-sdk..."
|
|
18
|
+
echo >>"$CLONE_DIR/buf.gen.yaml"
|
|
19
|
+
# Copy buf.gen.yaml over to our cloned reposiroty, modifying it to output to our src/gen/ directory
|
|
20
|
+
sed '1,/plugins:/d' buf.gen.yaml | sed 's# src/gen# ../src/gen#' >>"$CLONE_DIR/buf.gen.yaml"
|
|
21
|
+
|
|
22
|
+
pushd "$CLONE_DIR"
|
|
23
|
+
|
|
24
|
+
echo "Generating protos..."
|
|
25
|
+
make proto-generate
|
|
26
|
+
|
|
27
|
+
popd
|
|
28
|
+
|
|
29
|
+
echo "Removing repo..."
|
|
30
|
+
rm -rf "$CLONE_DIR"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
echo "Prepending copyright headers to TypeScript files..."
|
|
34
|
+
VIRTRU_LICENSE=$(cat <<EOF
|
|
35
|
+
/*
|
|
36
|
+
* Copyright (c) Virtru Corporation. All rights reserved.
|
|
37
|
+
*
|
|
38
|
+
* Your use of this file is governed by the Virtu Terms of Service <https://www.virtru.com/terms-of-service/>.
|
|
39
|
+
*/
|
|
40
|
+
EOF
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Find all .ts files and loop through them
|
|
44
|
+
git status --porcelain | grep -E '(M|A)' | cut -b 4- | sed "s#^#$(git rev-parse --show-toplevel)/#" | sed "s#^$(pwd)/##" | sort | uniq | grep "$DEST_DIR" | while read -r ts_file; do
|
|
45
|
+
echo "Processing $ts_file..."
|
|
46
|
+
TMPFILE="${ts_file}.tmp"
|
|
47
|
+
|
|
48
|
+
# Prepend header: Concatenate header + original file -> temp file, then move back
|
|
49
|
+
rm -f "$TMPFILE"
|
|
50
|
+
touch "$TMPFILE"
|
|
51
|
+
echo "$VIRTRU_LICENSE" >$TMPFILE
|
|
52
|
+
echo >>$TMPFILE
|
|
53
|
+
cat "$ts_file" >>"$TMPFILE"
|
|
54
|
+
mv "$TMPFILE" "$ts_file"
|
|
55
|
+
done
|
|
56
|
+
|
|
57
|
+
echo "Re-adding to stage, for prettier..."
|
|
58
|
+
git add src/gen
|
|
59
|
+
|
|
60
|
+
echo "Running prettier..."
|
|
61
|
+
rush prettier
|
|
62
|
+
|
|
63
|
+
echo "Protos updated successfully!"
|