@floegence/flowersec-core 2.5.1 → 2.5.3
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/dist/node/acceptor.js +25 -6
- package/dist/proxy/windowBridge.d.ts +1 -0
- package/dist/proxy/windowBridge.js +50 -9
- package/dist/transport/webTransportAdapter.js +17 -2
- package/dist/v2/session.d.ts +1 -1
- package/dist/v2/session.js +34 -11
- package/package.json +3 -3
- package/sbom/cyclonedx.json +6 -6
- package/sbom/spdx.json +11 -11
package/dist/node/acceptor.js
CHANGED
|
@@ -443,12 +443,31 @@ export async function createAcceptor(options) {
|
|
|
443
443
|
const abort = () => controller.abort(operation.signal?.reason);
|
|
444
444
|
operation.signal?.addEventListener("abort", abort, { once: true });
|
|
445
445
|
try {
|
|
446
|
-
return await Promise
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
446
|
+
return await new Promise((resolve, reject) => {
|
|
447
|
+
let settled = false;
|
|
448
|
+
let remaining = listeners.length;
|
|
449
|
+
const errors = [];
|
|
450
|
+
listeners.forEach((listener, index) => {
|
|
451
|
+
const selected = listeners[(index + cursor) % listeners.length];
|
|
452
|
+
void selected.accept({ signal: controller.signal }).then((carrier) => {
|
|
453
|
+
if (!settled) {
|
|
454
|
+
settled = true;
|
|
455
|
+
cursor = (cursor + 1) % listeners.length;
|
|
456
|
+
controller.abort();
|
|
457
|
+
resolve(carrier);
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
carrier.abort({ code: 1000, reason: "accept race lost" });
|
|
461
|
+
}
|
|
462
|
+
}, (error) => {
|
|
463
|
+
errors.push(error);
|
|
464
|
+
remaining -= 1;
|
|
465
|
+
if (!settled && remaining === 0) {
|
|
466
|
+
settled = true;
|
|
467
|
+
reject(new AggregateError(errors, "all listeners failed to accept"));
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
});
|
|
452
471
|
});
|
|
453
472
|
}
|
|
454
473
|
finally {
|
|
@@ -12,6 +12,7 @@ export declare class MessagePortByteStream implements ByteStream {
|
|
|
12
12
|
private buffered;
|
|
13
13
|
private readBuffered;
|
|
14
14
|
private ended;
|
|
15
|
+
private writeClosed;
|
|
15
16
|
private closed;
|
|
16
17
|
constructor(port: MessagePort);
|
|
17
18
|
read(options?: OperationOptions): Promise<Uint8Array | null>;
|
|
@@ -18,6 +18,7 @@ export class MessagePortByteStream {
|
|
|
18
18
|
buffered = 0;
|
|
19
19
|
readBuffered = 0;
|
|
20
20
|
ended = false;
|
|
21
|
+
writeClosed = false;
|
|
21
22
|
closed = false;
|
|
22
23
|
constructor(port) {
|
|
23
24
|
this.port = port;
|
|
@@ -52,6 +53,8 @@ export class MessagePortByteStream {
|
|
|
52
53
|
async write(data, options = {}) {
|
|
53
54
|
if (this.closed || this.terminalError !== undefined)
|
|
54
55
|
throw this.terminalError ?? new SessionError("closed");
|
|
56
|
+
if (this.writeClosed)
|
|
57
|
+
throw new SessionError("operation_failed");
|
|
55
58
|
if (options.signal?.aborted === true)
|
|
56
59
|
throw new SessionError("canceled");
|
|
57
60
|
if (data.length === 0)
|
|
@@ -79,8 +82,9 @@ export class MessagePortByteStream {
|
|
|
79
82
|
return data.length;
|
|
80
83
|
}
|
|
81
84
|
async closeWrite() {
|
|
82
|
-
if (this.closed)
|
|
85
|
+
if (this.closed || this.writeClosed)
|
|
83
86
|
return;
|
|
87
|
+
this.writeClosed = true;
|
|
84
88
|
this.port.postMessage({ type: "end" });
|
|
85
89
|
}
|
|
86
90
|
async reset() {
|
|
@@ -100,6 +104,10 @@ export class MessagePortByteStream {
|
|
|
100
104
|
return;
|
|
101
105
|
const message = value;
|
|
102
106
|
if (message.type === "chunk") {
|
|
107
|
+
if (this.ended || this.closed) {
|
|
108
|
+
this.fail(new SessionError("operation_failed"));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
103
111
|
if (!Number.isSafeInteger(message.id) || !(message.data instanceof ArrayBuffer) || message.data.byteLength > MAX_BRIDGE_CHUNK_BYTES) {
|
|
104
112
|
this.fail(new SessionError("operation_failed"));
|
|
105
113
|
return;
|
|
@@ -130,6 +138,8 @@ export class MessagePortByteStream {
|
|
|
130
138
|
return;
|
|
131
139
|
}
|
|
132
140
|
if (message.type === "end") {
|
|
141
|
+
if (this.ended)
|
|
142
|
+
return;
|
|
133
143
|
this.ended = true;
|
|
134
144
|
for (const waiter of this.readWaiters.splice(0))
|
|
135
145
|
waiter.resolve(null);
|
|
@@ -219,20 +229,39 @@ export function registerProxyAppWindow(options) {
|
|
|
219
229
|
openWebSocketStream: async (path, openOptions = {}) => {
|
|
220
230
|
if (disposed)
|
|
221
231
|
throw new SessionError("closed");
|
|
232
|
+
if (openOptions.signal?.aborted === true)
|
|
233
|
+
throw new SessionError("canceled");
|
|
222
234
|
const channel = new MessageChannel();
|
|
223
235
|
const response = new Promise((resolve, reject) => {
|
|
224
|
-
|
|
236
|
+
let settled = false;
|
|
237
|
+
const finish = (error, opened) => {
|
|
238
|
+
if (settled)
|
|
239
|
+
return;
|
|
240
|
+
settled = true;
|
|
241
|
+
clearTimeout(timer);
|
|
242
|
+
openOptions.signal?.removeEventListener("abort", abort);
|
|
243
|
+
if (error !== undefined) {
|
|
244
|
+
channel.port1.close();
|
|
245
|
+
reject(error);
|
|
246
|
+
}
|
|
247
|
+
else
|
|
248
|
+
resolve(opened);
|
|
249
|
+
};
|
|
250
|
+
const abort = () => {
|
|
251
|
+
channel.port1.postMessage({ type: "reset" });
|
|
252
|
+
finish(new SessionError("canceled"));
|
|
253
|
+
};
|
|
254
|
+
const timer = setTimeout(() => finish(new SessionError("timeout")), 10_000);
|
|
225
255
|
channel.port1.onmessage = (event) => {
|
|
226
256
|
if (event.data?.type !== WEBSOCKET_ACK_MESSAGE)
|
|
227
257
|
return;
|
|
228
|
-
clearTimeout(timer);
|
|
229
258
|
if (event.data.ok !== true) {
|
|
230
|
-
|
|
231
|
-
reject(new SessionError("operation_failed"));
|
|
259
|
+
finish(new SessionError("operation_failed"));
|
|
232
260
|
return;
|
|
233
261
|
}
|
|
234
|
-
|
|
262
|
+
finish(undefined, Object.freeze({ stream: new MessagePortByteStream(channel.port1), protocol: typeof event.data.protocol === "string" ? event.data.protocol : "" }));
|
|
235
263
|
};
|
|
264
|
+
openOptions.signal?.addEventListener("abort", abort, { once: true });
|
|
236
265
|
});
|
|
237
266
|
controller.postMessage({
|
|
238
267
|
type: WEBSOCKET_OPEN_MESSAGE,
|
|
@@ -241,9 +270,6 @@ export function registerProxyAppWindow(options) {
|
|
|
241
270
|
protocols: openOptions.protocols ?? [],
|
|
242
271
|
...(nonce === undefined ? {} : { capabilityNonce: nonce }),
|
|
243
272
|
}, origin, [channel.port2]);
|
|
244
|
-
if (openOptions.signal !== undefined) {
|
|
245
|
-
openOptions.signal.addEventListener("abort", () => channel.port1.close(), { once: true });
|
|
246
|
-
}
|
|
247
273
|
return await response;
|
|
248
274
|
},
|
|
249
275
|
dispose: () => { disposed = true; },
|
|
@@ -303,10 +329,25 @@ export function registerProxyControllerWindow(options) {
|
|
|
303
329
|
}
|
|
304
330
|
if (event.data?.type === WEBSOCKET_OPEN_MESSAGE && event.data.version === 2) {
|
|
305
331
|
void (async () => {
|
|
332
|
+
let canceled = false;
|
|
333
|
+
const openController = new AbortController();
|
|
334
|
+
port.onmessage = (message) => {
|
|
335
|
+
if (message.data?.type === "reset" ||
|
|
336
|
+
message.data?.type === "close") {
|
|
337
|
+
canceled = true;
|
|
338
|
+
openController.abort();
|
|
339
|
+
port.close();
|
|
340
|
+
}
|
|
341
|
+
};
|
|
306
342
|
try {
|
|
307
343
|
const opened = await options.runtime.openWebSocketStream(String(event.data.path ?? ""), {
|
|
308
344
|
protocols: Array.isArray(event.data.protocols) ? event.data.protocols.map(String) : [],
|
|
345
|
+
signal: openController.signal,
|
|
309
346
|
});
|
|
347
|
+
if (canceled) {
|
|
348
|
+
await opened.stream.reset();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
310
351
|
port.postMessage({ type: WEBSOCKET_ACK_MESSAGE, ok: true, protocol: opened.protocol });
|
|
311
352
|
await bridgeStreams(opened.stream, port);
|
|
312
353
|
}
|
|
@@ -327,6 +327,7 @@ class WebTransportDatagramAdapter {
|
|
|
327
327
|
this.terminalError = error;
|
|
328
328
|
this.incoming.fail(error);
|
|
329
329
|
void this.writable.abort(error).catch(() => undefined);
|
|
330
|
+
void this.readable.cancel(error).catch(() => undefined);
|
|
330
331
|
}
|
|
331
332
|
assertOpen() {
|
|
332
333
|
if (this.terminalError !== undefined)
|
|
@@ -416,15 +417,26 @@ class IncomingWebTransportStreamQueue {
|
|
|
416
417
|
}
|
|
417
418
|
}
|
|
418
419
|
class IncomingDatagramQueue {
|
|
420
|
+
static MAX_VALUES = 256;
|
|
421
|
+
static MAX_BYTES = 4 * 1024 * 1024;
|
|
419
422
|
values = [];
|
|
423
|
+
bufferedBytes = 0;
|
|
420
424
|
waiters = new Set();
|
|
421
425
|
terminalError;
|
|
422
426
|
push(value) {
|
|
423
427
|
if (this.terminalError !== undefined)
|
|
424
428
|
return;
|
|
425
429
|
const waiter = this.waiters.values().next().value;
|
|
426
|
-
if (waiter === undefined)
|
|
430
|
+
if (waiter === undefined) {
|
|
431
|
+
// DATAGRAMs are lossy by contract. Drop an incoming value once the
|
|
432
|
+
// bounded queue budget is exhausted, while keeping the native reader
|
|
433
|
+
// drained so an unconsumed peer cannot grow memory without bound.
|
|
434
|
+
if (this.values.length >= IncomingDatagramQueue.MAX_VALUES ||
|
|
435
|
+
this.bufferedBytes + value.byteLength > IncomingDatagramQueue.MAX_BYTES)
|
|
436
|
+
return;
|
|
427
437
|
this.values.push(value);
|
|
438
|
+
this.bufferedBytes += value.byteLength;
|
|
439
|
+
}
|
|
428
440
|
else
|
|
429
441
|
waiter.deliver(value);
|
|
430
442
|
}
|
|
@@ -434,8 +446,10 @@ class IncomingDatagramQueue {
|
|
|
434
446
|
if (this.terminalError !== undefined)
|
|
435
447
|
return Promise.reject(this.terminalError);
|
|
436
448
|
const value = this.values.shift();
|
|
437
|
-
if (value !== undefined)
|
|
449
|
+
if (value !== undefined) {
|
|
450
|
+
this.bufferedBytes -= value.byteLength;
|
|
438
451
|
return Promise.resolve(value);
|
|
452
|
+
}
|
|
439
453
|
return new Promise((resolve, reject) => {
|
|
440
454
|
let settled = false;
|
|
441
455
|
const cleanup = () => {
|
|
@@ -470,6 +484,7 @@ class IncomingDatagramQueue {
|
|
|
470
484
|
return;
|
|
471
485
|
this.terminalError = error;
|
|
472
486
|
this.values.length = 0;
|
|
487
|
+
this.bufferedBytes = 0;
|
|
473
488
|
for (const waiter of [...this.waiters])
|
|
474
489
|
waiter.fail(error);
|
|
475
490
|
}
|
package/dist/v2/session.d.ts
CHANGED
|
@@ -132,7 +132,7 @@ export declare class SessionV2 implements SessionV2Contract {
|
|
|
132
132
|
private peerResponderFrozen;
|
|
133
133
|
private responderChanged;
|
|
134
134
|
private idleWatchdogStarted;
|
|
135
|
-
private
|
|
135
|
+
private idleTimerCancel;
|
|
136
136
|
private readonly terminationState;
|
|
137
137
|
private readonly rpcRouter;
|
|
138
138
|
constructor(carrier: CarrierSessionV2, control: CarrierStreamV2, controlReader: ExactReader, config: SessionConfigV2, material: HandshakeMaterial);
|
package/dist/v2/session.js
CHANGED
|
@@ -137,7 +137,7 @@ export class SessionV2 {
|
|
|
137
137
|
peerResponderFrozen = false;
|
|
138
138
|
responderChanged = deferred();
|
|
139
139
|
idleWatchdogStarted = false;
|
|
140
|
-
|
|
140
|
+
idleTimerCancel;
|
|
141
141
|
terminationState = deferred();
|
|
142
142
|
rpcRouter;
|
|
143
143
|
constructor(carrier, control, controlReader, config, material) {
|
|
@@ -1017,10 +1017,9 @@ export class SessionV2 {
|
|
|
1017
1017
|
const timeoutMs = sessionIdleTimeoutMs(this.config);
|
|
1018
1018
|
if (timeoutMs === 0)
|
|
1019
1019
|
return;
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
this.idleTimer = undefined;
|
|
1020
|
+
this.idleTimerCancel?.();
|
|
1021
|
+
this.idleTimerCancel = scheduleLongTimeout(() => {
|
|
1022
|
+
this.idleTimerCancel = undefined;
|
|
1024
1023
|
this.fail(new SessionV2Error("timeout", "Flowersec v2 session idle timeout exceeded"));
|
|
1025
1024
|
}, timeoutMs);
|
|
1026
1025
|
}
|
|
@@ -1034,10 +1033,8 @@ export class SessionV2 {
|
|
|
1034
1033
|
this.terminalError = error;
|
|
1035
1034
|
this.lifecycle = "closed";
|
|
1036
1035
|
this.terminationState.resolve({ error });
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
this.idleTimer = undefined;
|
|
1040
|
-
}
|
|
1036
|
+
this.idleTimerCancel?.();
|
|
1037
|
+
this.idleTimerCancel = undefined;
|
|
1041
1038
|
this.incoming.fail(error);
|
|
1042
1039
|
this.outboundPermits.fail(error);
|
|
1043
1040
|
this.inboundPermits.fail(error);
|
|
@@ -1988,12 +1985,38 @@ function createSessionDeadline(config, phase) {
|
|
|
1988
1985
|
}
|
|
1989
1986
|
function defaultDeadlineFactory(timeoutMs, phase) {
|
|
1990
1987
|
const controller = new AbortController();
|
|
1991
|
-
const
|
|
1988
|
+
const cancelTimer = scheduleLongTimeout(() => {
|
|
1992
1989
|
controller.abort(new SessionV2Error("timeout", `${phase} deadline exceeded`));
|
|
1993
1990
|
}, timeoutMs);
|
|
1994
1991
|
return {
|
|
1995
1992
|
signal: controller.signal,
|
|
1996
|
-
cancel:
|
|
1993
|
+
cancel: cancelTimer,
|
|
1994
|
+
};
|
|
1995
|
+
}
|
|
1996
|
+
const MAX_NATIVE_TIMEOUT_MS = 2_147_483_647;
|
|
1997
|
+
// Node and browsers clamp setTimeout delays above a signed 32-bit integer.
|
|
1998
|
+
// Keep the public timeout range intact by scheduling the remaining duration in
|
|
1999
|
+
// bounded segments against a monotonic wall-clock deadline.
|
|
2000
|
+
function scheduleLongTimeout(callback, timeoutMs) {
|
|
2001
|
+
const clock = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
2002
|
+
const deadline = clock() + timeoutMs;
|
|
2003
|
+
let timer;
|
|
2004
|
+
let canceled = false;
|
|
2005
|
+
const schedule = () => {
|
|
2006
|
+
if (canceled)
|
|
2007
|
+
return;
|
|
2008
|
+
const remaining = deadline - clock();
|
|
2009
|
+
if (remaining <= 0) {
|
|
2010
|
+
callback();
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
timer = setTimeout(schedule, Math.min(remaining, MAX_NATIVE_TIMEOUT_MS));
|
|
2014
|
+
};
|
|
2015
|
+
schedule();
|
|
2016
|
+
return () => {
|
|
2017
|
+
canceled = true;
|
|
2018
|
+
if (timer !== undefined)
|
|
2019
|
+
clearTimeout(timer);
|
|
1997
2020
|
};
|
|
1998
2021
|
}
|
|
1999
2022
|
function combineSignals(...signals) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floegence/flowersec-core",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.3",
|
|
4
4
|
"description": "Flowersec core TypeScript library for carrier-neutral encrypted sessions and multiplexed streams.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"ws": "^8.21.2"
|
|
78
78
|
},
|
|
79
79
|
"optionalDependencies": {
|
|
80
|
-
"@floegence/flowersec-node-native": "2.5.
|
|
80
|
+
"@floegence/flowersec-node-native": "2.5.3"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@playwright/test": "1.62.1",
|
|
@@ -95,5 +95,5 @@
|
|
|
95
95
|
"vite": "^8.2.1",
|
|
96
96
|
"vitest": "4.1.10"
|
|
97
97
|
},
|
|
98
|
-
"flowersecSourceCommit": "
|
|
98
|
+
"flowersecSourceCommit": "aa50aab509e3519b2b9c8e49abccc407a831a741"
|
|
99
99
|
}
|
package/sbom/cyclonedx.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.5",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:ec0f2db1-aea5-5835-89bb-52b08c4a4a1c",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
7
|
"component": {
|
|
8
8
|
"type": "library",
|
|
9
9
|
"name": "@floegence/flowersec-core",
|
|
10
|
-
"version": "2.5.
|
|
11
|
-
"purl": "pkg:npm/%40floegence/flowersec-core@2.5.
|
|
12
|
-
"bom-ref": "pkg:npm/%40floegence/flowersec-core@2.5.
|
|
10
|
+
"version": "2.5.3",
|
|
11
|
+
"purl": "pkg:npm/%40floegence/flowersec-core@2.5.3",
|
|
12
|
+
"bom-ref": "pkg:npm/%40floegence/flowersec-core@2.5.3"
|
|
13
13
|
},
|
|
14
14
|
"properties": [
|
|
15
15
|
{
|
|
16
16
|
"name": "flowersec:source-inventory-sha256",
|
|
17
|
-
"value": "
|
|
17
|
+
"value": "1a8ba3adc252c3c72e7a5129a172b0c767a075161ac1d0379f0b257f091816fb"
|
|
18
18
|
}
|
|
19
19
|
]
|
|
20
20
|
},
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
],
|
|
191
191
|
"dependencies": [
|
|
192
192
|
{
|
|
193
|
-
"ref": "pkg:npm/%40floegence/flowersec-core@2.5.
|
|
193
|
+
"ref": "pkg:npm/%40floegence/flowersec-core@2.5.3",
|
|
194
194
|
"dependsOn": [
|
|
195
195
|
"pkg:npm/%40noble/ciphers@2.3.0",
|
|
196
196
|
"pkg:npm/%40noble/curves@2.3.0",
|
package/sbom/spdx.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"dataLicense": "CC0-1.0",
|
|
4
4
|
"SPDXID": "SPDXRef-DOCUMENT",
|
|
5
5
|
"name": "flowersec-ts",
|
|
6
|
-
"documentNamespace": "https://github.com/floegence/flowersec/sbom/flowersec-ts/
|
|
6
|
+
"documentNamespace": "https://github.com/floegence/flowersec/sbom/flowersec-ts/1a8ba3adc252c3c72e7a5129a172b0c767a075161ac1d0379f0b257f091816fb",
|
|
7
7
|
"creationInfo": {
|
|
8
8
|
"created": "1970-01-01T00:00:00Z",
|
|
9
9
|
"creators": [
|
|
@@ -13,19 +13,19 @@
|
|
|
13
13
|
"packages": [
|
|
14
14
|
{
|
|
15
15
|
"name": "@floegence/flowersec-core",
|
|
16
|
-
"SPDXID": "SPDXRef-Package-
|
|
17
|
-
"versionInfo": "2.5.
|
|
16
|
+
"SPDXID": "SPDXRef-Package-290762592d648256929a",
|
|
17
|
+
"versionInfo": "2.5.3",
|
|
18
18
|
"downloadLocation": "NOASSERTION",
|
|
19
19
|
"filesAnalyzed": false,
|
|
20
20
|
"licenseConcluded": "NOASSERTION",
|
|
21
21
|
"licenseDeclared": "NOASSERTION",
|
|
22
22
|
"copyrightText": "NOASSERTION",
|
|
23
|
-
"comment": "Flowersec source inventory SHA-256:
|
|
23
|
+
"comment": "Flowersec source inventory SHA-256: 1a8ba3adc252c3c72e7a5129a172b0c767a075161ac1d0379f0b257f091816fb",
|
|
24
24
|
"externalRefs": [
|
|
25
25
|
{
|
|
26
26
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
27
27
|
"referenceType": "purl",
|
|
28
|
-
"referenceLocator": "pkg:npm/%40floegence/flowersec-core@2.5.
|
|
28
|
+
"referenceLocator": "pkg:npm/%40floegence/flowersec-core@2.5.3"
|
|
29
29
|
}
|
|
30
30
|
]
|
|
31
31
|
},
|
|
@@ -136,30 +136,30 @@
|
|
|
136
136
|
{
|
|
137
137
|
"spdxElementId": "SPDXRef-DOCUMENT",
|
|
138
138
|
"relationshipType": "DESCRIBES",
|
|
139
|
-
"relatedSpdxElement": "SPDXRef-Package-
|
|
139
|
+
"relatedSpdxElement": "SPDXRef-Package-290762592d648256929a"
|
|
140
140
|
},
|
|
141
141
|
{
|
|
142
|
-
"spdxElementId": "SPDXRef-Package-
|
|
142
|
+
"spdxElementId": "SPDXRef-Package-290762592d648256929a",
|
|
143
143
|
"relationshipType": "DEPENDS_ON",
|
|
144
144
|
"relatedSpdxElement": "SPDXRef-Package-5ce913a03239b02770fb"
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
|
-
"spdxElementId": "SPDXRef-Package-
|
|
147
|
+
"spdxElementId": "SPDXRef-Package-290762592d648256929a",
|
|
148
148
|
"relationshipType": "DEPENDS_ON",
|
|
149
149
|
"relatedSpdxElement": "SPDXRef-Package-01009adf60db02c13634"
|
|
150
150
|
},
|
|
151
151
|
{
|
|
152
|
-
"spdxElementId": "SPDXRef-Package-
|
|
152
|
+
"spdxElementId": "SPDXRef-Package-290762592d648256929a",
|
|
153
153
|
"relationshipType": "DEPENDS_ON",
|
|
154
154
|
"relatedSpdxElement": "SPDXRef-Package-8815b117c8a1d5f7eeb0"
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
|
-
"spdxElementId": "SPDXRef-Package-
|
|
157
|
+
"spdxElementId": "SPDXRef-Package-290762592d648256929a",
|
|
158
158
|
"relationshipType": "DEPENDS_ON",
|
|
159
159
|
"relatedSpdxElement": "SPDXRef-Package-f8b45a289df643042b94"
|
|
160
160
|
},
|
|
161
161
|
{
|
|
162
|
-
"spdxElementId": "SPDXRef-Package-
|
|
162
|
+
"spdxElementId": "SPDXRef-Package-290762592d648256929a",
|
|
163
163
|
"relationshipType": "DEPENDS_ON",
|
|
164
164
|
"relatedSpdxElement": "SPDXRef-Package-b779412f685822663496"
|
|
165
165
|
},
|