@floegence/flowersec-core 2.3.6 → 2.3.7

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.
Files changed (58) hide show
  1. package/README.md +31 -42
  2. package/THIRD_PARTY_NOTICES.md +4 -79
  3. package/dist/cli.js +31 -31
  4. package/dist/connector/adapters/rawQuicCandidate.d.ts +5 -0
  5. package/dist/connector/adapters/rawQuicCandidate.js +71 -0
  6. package/dist/connector/sessionAcceptor.d.ts +22 -2
  7. package/dist/connector/sessionAcceptor.js +51 -21
  8. package/dist/connector/sessionConnector.d.ts +2 -0
  9. package/dist/connector/sessionConnector.js +4 -1
  10. package/dist/interop/proxyServerPeer.d.ts +1 -0
  11. package/dist/interop/proxyServerPeer.js +83 -0
  12. package/dist/interop/serverParityPeer.d.ts +1 -0
  13. package/dist/interop/serverParityPeer.js +608 -0
  14. package/dist/node/acceptor.d.ts +31 -19
  15. package/dist/node/acceptor.js +353 -72
  16. package/dist/node/connectSession.d.ts +2 -1
  17. package/dist/node/connectSession.js +22 -12
  18. package/dist/node/controlplane.d.ts +137 -0
  19. package/dist/node/controlplane.js +439 -0
  20. package/dist/node/index.d.ts +8 -2
  21. package/dist/node/index.js +4 -1
  22. package/dist/node/nativeTransportAddon.d.ts +94 -0
  23. package/dist/node/nativeTransportAddon.js +166 -0
  24. package/dist/node/proxyServer.d.ts +31 -0
  25. package/dist/node/proxyServer.js +572 -0
  26. package/dist/node/rawQuicAdapter.d.ts +9 -0
  27. package/dist/node/rawQuicAdapter.js +107 -0
  28. package/dist/node/rawQuicServer.d.ts +24 -0
  29. package/dist/node/rawQuicServer.js +37 -0
  30. package/dist/node/runtimeCapability.d.ts +8 -1
  31. package/dist/node/runtimeCapability.js +20 -6
  32. package/dist/node/tunnelRuntime.d.ts +42 -0
  33. package/dist/node/tunnelRuntime.js +552 -0
  34. package/dist/node/webSocketServer.d.ts +24 -0
  35. package/dist/node/webSocketServer.js +135 -0
  36. package/dist/public/contract.d.ts +4 -3
  37. package/dist/transport/webSocketAdapter.d.ts +7 -0
  38. package/dist/transport/webSocketAdapter.js +136 -0
  39. package/dist/v2/artifact.js +1 -1
  40. package/dist/v2/capability.js +1 -1
  41. package/dist/v2/carrier.d.ts +4 -1
  42. package/dist/v2/carrier.js +2 -2
  43. package/dist/v2/handshake.js +7 -7
  44. package/dist/v2/protocol.js +5 -5
  45. package/dist/v2/publicSession.js +25 -7
  46. package/dist/v2/session.d.ts +2 -2
  47. package/dist/v2/session.js +3 -3
  48. package/dist/v2/unreliableMessage.js +4 -4
  49. package/dist/vendor/tr46.js +11 -6
  50. package/dist/ws-client/binaryTransport.d.ts +3 -0
  51. package/dist/ws-client/binaryTransport.js +14 -0
  52. package/package.json +19 -16
  53. package/sbom/cyclonedx.json +52 -2585
  54. package/sbom/spdx.json +56 -1826
  55. package/dist/node/webTransportClient.d.ts +0 -9
  56. package/dist/node/webTransportClient.js +0 -56
  57. package/dist/node/webTransportServer.d.ts +0 -22
  58. package/dist/node/webTransportServer.js +0 -100
@@ -0,0 +1,107 @@
1
+ import { X509Certificate, createPrivateKey } from "node:crypto";
2
+ const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;
3
+ const CERTIFICATE_PEM_BEGIN = "-----BEGIN CERTIFICATE-----";
4
+ const CERTIFICATE_PEM_END = "-----END CERTIFICATE-----";
5
+ const MAX_CERTIFICATE_PEM_BYTES = 256 * 1024;
6
+ const MAX_CERTIFICATE_CHAIN_LENGTH = 32;
7
+ export async function createNodeRawQuicClientV2(driver, candidate, artifact, tls, signal, handshakeTimeoutMs = DEFAULT_HANDSHAKE_TIMEOUT_MS) {
8
+ const url = new URL(candidate.normalized_url);
9
+ if (candidate.carrier !== "raw_quic" || url.protocol !== "quic:" || url.port === "") {
10
+ throw new TypeError("invalid raw QUIC candidate");
11
+ }
12
+ const options = {
13
+ host: unbracket(url.hostname),
14
+ port: Number(url.port),
15
+ serverName: unbracket(url.hostname),
16
+ path: artifact.path.kind,
17
+ trustRootsDer: normalizeCertificateChain(tls.ca),
18
+ inboundBidirectionalStreamCapacity: artifact.session.max_inbound_streams + 2,
19
+ handshakeTimeoutMs,
20
+ };
21
+ return await driver.connectRawQuic(options, { signal });
22
+ }
23
+ export function normalizeCertificateChain(input) {
24
+ const values = Array.isArray(input) ? input : [input];
25
+ const certificates = [];
26
+ let totalInputBytes = 0;
27
+ for (const value of values) {
28
+ if (typeof value !== "string" && !(value instanceof Uint8Array)) {
29
+ throw new TypeError("invalid raw QUIC certificate");
30
+ }
31
+ totalInputBytes += typeof value === "string" ? Buffer.byteLength(value, "utf8") : value.byteLength;
32
+ if (totalInputBytes > MAX_CERTIFICATE_PEM_BYTES)
33
+ throw new TypeError("invalid raw QUIC certificate");
34
+ if (typeof value === "string") {
35
+ const blocks = splitCertificatePEM(value);
36
+ if (blocks.length === 0)
37
+ throw new TypeError("invalid raw QUIC certificate");
38
+ if (certificates.length + blocks.length > MAX_CERTIFICATE_CHAIN_LENGTH) {
39
+ throw new TypeError("invalid raw QUIC certificate");
40
+ }
41
+ for (const block of blocks)
42
+ certificates.push(parseCertificate(block));
43
+ }
44
+ else if (value instanceof Uint8Array && value.length > 0) {
45
+ if (certificates.length >= MAX_CERTIFICATE_CHAIN_LENGTH)
46
+ throw new TypeError("invalid raw QUIC certificate");
47
+ certificates.push(parseCertificate(value));
48
+ }
49
+ else {
50
+ throw new TypeError("invalid raw QUIC certificate");
51
+ }
52
+ }
53
+ if (certificates.length === 0)
54
+ throw new TypeError("raw QUIC requires explicit trust roots");
55
+ return certificates;
56
+ }
57
+ function splitCertificatePEM(value) {
58
+ if (Buffer.byteLength(value, "utf8") > MAX_CERTIFICATE_PEM_BYTES) {
59
+ throw new TypeError("invalid raw QUIC certificate");
60
+ }
61
+ const blocks = [];
62
+ let cursor = 0;
63
+ while (cursor < value.length) {
64
+ const begin = value.indexOf(CERTIFICATE_PEM_BEGIN, cursor);
65
+ if (begin < 0) {
66
+ if (value.slice(cursor).trim().length !== 0)
67
+ throw new TypeError("invalid raw QUIC certificate");
68
+ break;
69
+ }
70
+ if (value.slice(cursor, begin).trim().length !== 0)
71
+ throw new TypeError("invalid raw QUIC certificate");
72
+ const contentStart = begin + CERTIFICATE_PEM_BEGIN.length;
73
+ const end = value.indexOf(CERTIFICATE_PEM_END, contentStart);
74
+ if (end < 0 || value.indexOf(CERTIFICATE_PEM_BEGIN, contentStart) >= 0 && value.indexOf(CERTIFICATE_PEM_BEGIN, contentStart) < end) {
75
+ throw new TypeError("invalid raw QUIC certificate");
76
+ }
77
+ blocks.push(value.slice(begin, end + CERTIFICATE_PEM_END.length));
78
+ if (blocks.length > MAX_CERTIFICATE_CHAIN_LENGTH)
79
+ throw new TypeError("invalid raw QUIC certificate");
80
+ cursor = end + CERTIFICATE_PEM_END.length;
81
+ }
82
+ return blocks;
83
+ }
84
+ export function normalizePrivateKey(input) {
85
+ if ((typeof input === "string" && input.length === 0) ||
86
+ (input instanceof Uint8Array && input.length === 0)) {
87
+ throw new TypeError("invalid raw QUIC private key");
88
+ }
89
+ try {
90
+ const key = createPrivateKey(input);
91
+ return new Uint8Array(key.export({ format: "der", type: "pkcs8" }));
92
+ }
93
+ catch {
94
+ throw new TypeError("invalid raw QUIC private key");
95
+ }
96
+ }
97
+ function parseCertificate(input) {
98
+ try {
99
+ return new Uint8Array(new X509Certificate(input).raw);
100
+ }
101
+ catch {
102
+ throw new TypeError("invalid raw QUIC certificate");
103
+ }
104
+ }
105
+ function unbracket(host) {
106
+ return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
107
+ }
@@ -0,0 +1,24 @@
1
+ import type { CarrierSessionV2 } from "../v2/carrier.js";
2
+ import type { PathKind } from "../v2/contract.js";
3
+ import type { NativeRawQuicDriver } from "./nativeTransportAddon.js";
4
+ export type NodeRawQuicServerOptions = Readonly<{
5
+ host: string;
6
+ port: number;
7
+ path: PathKind;
8
+ tls: Readonly<{
9
+ certificate: string | Uint8Array;
10
+ privateKey: string | Uint8Array;
11
+ }>;
12
+ inboundBidirectionalStreamCapacity: number;
13
+ }>;
14
+ export type NodeRawQuicServer = Readonly<{
15
+ address(): Readonly<{
16
+ host: string;
17
+ port: number;
18
+ }>;
19
+ accept(options?: Readonly<{
20
+ signal?: AbortSignal;
21
+ }>): Promise<CarrierSessionV2>;
22
+ close(): Promise<void>;
23
+ }>;
24
+ export declare function startNodeRawQuicServer(driver: NativeRawQuicDriver, options: NodeRawQuicServerOptions): Promise<NodeRawQuicServer>;
@@ -0,0 +1,37 @@
1
+ import { adaptNativeCarrierSessionV2 } from "../v2/carrier.js";
2
+ import { normalizeCertificateChain, normalizePrivateKey } from "./rawQuicAdapter.js";
3
+ export async function startNodeRawQuicServer(driver, options) {
4
+ validateOptions(options);
5
+ let listener;
6
+ try {
7
+ listener = await driver.bindRawQuic({
8
+ host: options.host,
9
+ port: options.port,
10
+ path: options.path,
11
+ certificateChainDer: normalizeCertificateChain(options.tls.certificate),
12
+ privateKeyDer: normalizePrivateKey(options.tls.privateKey),
13
+ inboundBidirectionalStreamCapacity: options.inboundBidirectionalStreamCapacity,
14
+ });
15
+ }
16
+ catch (error) {
17
+ if (error instanceof TypeError)
18
+ throw error;
19
+ throw new Error("Flowersec raw QUIC listener failed to bind");
20
+ }
21
+ return {
22
+ address: () => listener.address(),
23
+ accept: async (operation = {}) => adaptNativeCarrierSessionV2(await listener.accept(operation)),
24
+ close: async () => await listener.close(),
25
+ };
26
+ }
27
+ function validateOptions(options) {
28
+ if (!Number.isInteger(options.port) || options.port < 0 || options.port > 65_535 ||
29
+ !Number.isInteger(options.inboundBidirectionalStreamCapacity) ||
30
+ options.inboundBidirectionalStreamCapacity < 3 ||
31
+ options.inboundBidirectionalStreamCapacity > 130) {
32
+ throw new TypeError("invalid Node raw QUIC listener options");
33
+ }
34
+ if (options.tls.certificate.length === 0 || options.tls.privateKey.length === 0) {
35
+ throw new TypeError("raw QUIC listener requires explicit TLS material");
36
+ }
37
+ }
@@ -1,4 +1,11 @@
1
- export declare const NODE_RUNTIME_CAPABILITY_V2: Readonly<{
1
+ export declare const NODE_RUNTIME_PROFILE_V2: Readonly<{
2
+ language: string;
3
+ runtime: string;
4
+ schemaVersion: 2;
5
+ tuples: readonly import("../v2/capability.js").RuntimeCapabilityTupleV2[];
6
+ unsupported: readonly import("../v2/capability.js").UnsupportedRuntimeCarrierV2[];
7
+ }>;
8
+ export declare function detectNodeRuntimeCapabilityV2(rawQuicAvailable: boolean): Readonly<{
2
9
  language: string;
3
10
  runtime: string;
4
11
  schemaVersion: 2;
@@ -1,10 +1,24 @@
1
1
  import { defineRuntimeCapabilityDescriptorV2 } from "../v2/capability.js";
2
- export const NODE_RUNTIME_CAPABILITY_V2 = defineRuntimeCapabilityDescriptorV2("node", [
2
+ const nodeWebSocketTuples = [
3
3
  { carrier: "websocket", datagrams: false, migration: false, networkMode: "dial", path: "direct", reliableStreams: true, sessionRole: "client" },
4
4
  { carrier: "websocket", datagrams: false, migration: false, networkMode: "dial", path: "tunnel", reliableStreams: true, sessionRole: "client" },
5
5
  { carrier: "websocket", datagrams: false, migration: false, networkMode: "dial", path: "tunnel", reliableStreams: true, sessionRole: "server" },
6
- { carrier: "webtransport", datagrams: true, migration: false, networkMode: "dial", path: "direct", reliableStreams: true, sessionRole: "client" },
7
- { carrier: "webtransport", datagrams: true, migration: false, networkMode: "dial", path: "tunnel", reliableStreams: true, sessionRole: "client" },
8
- { carrier: "webtransport", datagrams: true, migration: false, networkMode: "dial", path: "tunnel", reliableStreams: true, sessionRole: "server" },
9
- { carrier: "webtransport", datagrams: true, migration: false, networkMode: "listen", path: "direct", reliableStreams: true, sessionRole: "server" },
10
- ], [{ carrier: "raw_quic", reason: "raw_quic_adapter_not_implemented" }]);
6
+ { carrier: "websocket", datagrams: false, migration: false, networkMode: "listen", path: "direct", reliableStreams: true, sessionRole: "server" },
7
+ ];
8
+ export const NODE_RUNTIME_PROFILE_V2 = defineRuntimeCapabilityDescriptorV2("node", [
9
+ { carrier: "raw_quic", datagrams: true, migration: false, networkMode: "dial", path: "direct", reliableStreams: true, sessionRole: "client" },
10
+ { carrier: "raw_quic", datagrams: true, migration: false, networkMode: "dial", path: "tunnel", reliableStreams: true, sessionRole: "client" },
11
+ { carrier: "raw_quic", datagrams: true, migration: false, networkMode: "dial", path: "tunnel", reliableStreams: true, sessionRole: "server" },
12
+ { carrier: "raw_quic", datagrams: true, migration: false, networkMode: "listen", path: "direct", reliableStreams: true, sessionRole: "server" },
13
+ ...nodeWebSocketTuples,
14
+ ], [
15
+ { carrier: "webtransport", reason: "node_webtransport_driver_unavailable" },
16
+ ]);
17
+ export function detectNodeRuntimeCapabilityV2(rawQuicAvailable) {
18
+ if (rawQuicAvailable)
19
+ return NODE_RUNTIME_PROFILE_V2;
20
+ return defineRuntimeCapabilityDescriptorV2("node", nodeWebSocketTuples, [
21
+ { carrier: "raw_quic", reason: "node_native_transport_unavailable" },
22
+ { carrier: "webtransport", reason: "node_webtransport_driver_unavailable" },
23
+ ]);
24
+ }
@@ -0,0 +1,42 @@
1
+ import type { OperationOptions } from "../public/contract.js";
2
+ import { TunnelAuthorizationResponse, type TunnelAuthorizationDecision as ControlPlaneTunnelAuthorizationDecision, type RuntimeAuthorizationRequest } from "./controlplane.js";
3
+ export type TunnelAuthorizationDecision = ControlPlaneTunnelAuthorizationDecision;
4
+ export type TunnelRuntimeListener = Readonly<{
5
+ carrier: "websocket";
6
+ host: string;
7
+ port: number;
8
+ tls: Readonly<{
9
+ certificate: string;
10
+ privateKey: string;
11
+ }>;
12
+ allowedOrigins: readonly string[];
13
+ }> | Readonly<{
14
+ carrier: "raw_quic";
15
+ host: string;
16
+ port: number;
17
+ tls: Readonly<{
18
+ certificate: string;
19
+ privateKey: string;
20
+ }>;
21
+ }>;
22
+ export type TunnelRuntimeOptions = Readonly<{
23
+ listeners: readonly TunnelRuntimeListener[];
24
+ maxInboundStreams: number;
25
+ maxPendingLegs?: number;
26
+ maxActivePairs?: number;
27
+ maxConcurrentStreams?: number;
28
+ pairTimeoutMs?: number;
29
+ cleanupTimeoutMs?: number;
30
+ authorize(request: RuntimeAuthorizationRequest, options: OperationOptions): Promise<TunnelAuthorizationDecision | TunnelAuthorizationResponse>;
31
+ release?(leaseId: string): Promise<void> | void;
32
+ }>;
33
+ export declare class TunnelRuntime {
34
+ private constructor();
35
+ start(): Promise<void>;
36
+ addresses(): readonly Readonly<{
37
+ host: string;
38
+ port: number;
39
+ }>[];
40
+ close(): Promise<void>;
41
+ }
42
+ export declare function createTunnelRuntime(options: TunnelRuntimeOptions): TunnelRuntime;