diff --git a/docs/development/build-and-test/schema-codegen.md b/docs/development/build-and-test/schema-codegen.md index 66eef64cb..20cded92f 100644 --- a/docs/development/build-and-test/schema-codegen.md +++ b/docs/development/build-and-test/schema-codegen.md @@ -135,8 +135,8 @@ contract-valid, and no relaxed schema twin is generated. history and published registry identities. - `validate-configs.js` validates the repository config corpus against exact registered schemas discovered from the same registry command. -- `wire-conformance.test.ts` checks the Node one-shot public-to-raw v1.1 - mapping against the exact generated v1.1 oracle. +- `wire-conformance.test.ts` checks the Node one-shot public-to-raw v1.0 + mapping against the exact generated published v1.0 oracle. - `wire-conformance-state-aware.test.ts` checks backend-specific state-aware public types against the exact v0.9 and v1.1 oracles. - `check-schema-versions.js` and `check-version-sync.js` enforce schema and diff --git a/sdk/node/src/bindings/one-shot.ts b/sdk/node/src/bindings/one-shot.ts index 1ea1f64b8..afbb3bbe4 100644 --- a/sdk/node/src/bindings/one-shot.ts +++ b/sdk/node/src/bindings/one-shot.ts @@ -22,7 +22,7 @@ import { MxcError } from '../v1/errors.js'; import { SDK_CONTRACT_VERSION } from '../v1/contract-version.js'; import type { ContainerConfig, - NetworkConfig, + DirectionalNetworkConfig, PortMapping, ProcessContainerConfig, WslcConfig, @@ -114,7 +114,7 @@ function assertIntegerRange( } } -function validateNetworkPorts(network: NetworkConfig | undefined): void { +function validateNetworkPorts(network: DirectionalNetworkConfig | undefined): void { const egress = network?.egress; const rules = [...(egress?.allow ?? []), ...(egress?.deny ?? [])]; for (const [ruleIndex, rule] of rules.entries()) { diff --git a/sdk/node/src/helper.ts b/sdk/node/src/helper.ts index 4d7993fe4..5c6f45a18 100644 --- a/sdk/node/src/helper.ts +++ b/sdk/node/src/helper.ts @@ -102,67 +102,6 @@ export function makeLogFilePath(dir: string): string { return path.join(dir, `mxc-diag-${ts}-${suffix}.log`); } -/** - * Apply Linux network-policy defaults to a `ContainerConfig`. - * - * Linux enforces per-host filtering in one of two ways: - * 1. **iptables firewall** (`enforcementMode: 'firewall'`) — LXC's - * privileged enforcement path. Requires root / CAP_NET_ADMIN. - * 2. **Cooperative HTTP proxy** (`network.proxy` set) — Bubblewrap's - * unprivileged enforcement path. The proxy applies the host policy - * for cooperating HTTP clients; raw-socket clients bypass it. - * - * Legacy LXC network blocks with no explicit enforcement mode use `'firewall'` - * because capabilities are Windows-only. Bubblewrap uses firewall only for - * host lists without a proxy. - * - * If the caller explicitly passes `enforcementMode: 'capabilities'` we - * warn: `'capabilities'` is a Windows/AppContainer concept (a token - * capability mask) and has no Linux equivalent. The explicit value is left - * unchanged so native backend validation can report unsupported combinations. - * - * Shared between the explicit `'bubblewrap'` / `'lxc'` builders and the - * abstract `'process'` branch on Linux (which resolves to Bubblewrap - * server-side). - * - * NOTE: when `network.proxy` is configured on Bubblewrap, host filtering - * is enforced at the proxy layer (unprivileged, no CAP_NET_ADMIN). The - * Rust config parser explicitly rejects `bubblewrap + proxy + firewall` - * since the iptables path requires privilege the bwrap backend - * deliberately avoids. Callers in that mode must leave enforcementMode - * at its default. - */ -export function applyLinuxNetworkPolicy(config: ContainerConfig): void { - if (!config.network) { - return; - } - if (config.network.enforcementMode === 'capabilities') { - console.warn( - "mxc-sdk: network.enforcementMode='capabilities' has no effect on Linux " + - "(it is a Windows AppContainer / ProcessContainer concept). The Linux " + - "runner will not enforce host filtering via capabilities. Use the " + - "default mode (auto-promotes to 'firewall' for LXC, or use network.proxy " + - "for unprivileged Bubblewrap enforcement)." - ); - } - // LXC cannot enforce Windows AppContainer capabilities, so omitted legacy modes use firewall. - const isLegacyNetwork = - config.network.egress === undefined && config.network.ingress === undefined; - if ( - config.containment === 'lxc' && - isLegacyNetwork && - config.network.enforcementMode === undefined - ) { - config.network.enforcementMode = 'firewall'; - } - const hasProxy = !!config.network.proxy; - const hasHostRules = - !!(config.network.allowedHosts?.length || config.network.blockedHosts?.length); - if (hasHostRules && !hasProxy) { - config.network.enforcementMode = 'firewall'; - } -} - /** * Resolves the executor binary and builds the common CLI arguments for any * MXC request envelope (one-shot or state-aware). Performs platform support diff --git a/sdk/node/src/v1/container.ts b/sdk/node/src/v1/container.ts index 865bd2bfe..b9d9c18da 100644 --- a/sdk/node/src/v1/container.ts +++ b/sdk/node/src/v1/container.ts @@ -10,7 +10,6 @@ import { ExecutionResult, UnsupportedV1NetworkFields, } from './types.js'; -import { applyLinuxNetworkPolicy } from '../helper.js'; import { diagLog } from '../diagnostic.js'; import { MxcError } from './errors.js'; import { prepareOneShotRequest } from '../bindings/one-shot.js'; @@ -129,14 +128,13 @@ function buildWslcContainerConfig( /** * Builds the Bubblewrap (bwrap) portion of a ContainerConfig. * Bubblewrap is Linux-only and uses shared cross-backend fields only — - * no backend-specific config block. Network enforcement via iptables - * reuses the same approach as LXC. + * no backend-specific config block. Network policy uses shared directional + * fields enforced by the native backend. */ function buildBubblewrapConfig( config: ContainerConfig, ): ContainerConfig { config.containment = 'bubblewrap'; - applyLinuxNetworkPolicy(config); return config; } @@ -150,7 +148,6 @@ function buildLinuxProcessConfig( distribution: 'alpine', release: '3.23', }; - applyLinuxNetworkPolicy(config); return config; } diff --git a/sdk/node/src/v1/types.ts b/sdk/node/src/v1/types.ts index d8ba22dc6..b19af0127 100644 --- a/sdk/node/src/v1/types.ts +++ b/sdk/node/src/v1/types.ts @@ -251,34 +251,6 @@ export interface FilesystemConfig { clearPolicyOnExit?: boolean; } -/** - * Network access configuration. Legacy fields remain for TypeScript source - * compatibility only; every registered exact contract rejects them. Use - * directional fields and runtimeConfig.networkProxy instead. - */ -export interface NetworkConfig extends DirectionalNetworkConfig { - /** - * Retired enforcement selector; registered contracts reject this field. - * @deprecated Use directional network policy instead. - */ - enforcementMode?: 'capabilities' | 'firewall' | 'both'; - /** @deprecated Use egress.default instead; registered contracts reject this field. */ - defaultPolicy?: 'allow' | 'block'; - /** - * @deprecated Use ingress.default instead; registered contracts reject this field. - */ - allowLocalNetwork?: boolean; - /** @deprecated Use numeric CIDR egress rules or proxy-side hostname filtering. */ - allowedHosts?: string[]; - /** @deprecated Use numeric CIDR egress rules or proxy-side hostname filtering. */ - blockedHosts?: string[]; - /** @deprecated Registered contracts reject network.proxy. Use - * runtimeConfig.networkProxy with the backend's supported policy posture. */ - proxy?: { builtinTestServer: true } | { localhost: number } | { url: string }; - /** @deprecated Use lifecycle.preservePolicy; registered contracts reject this field. */ - removeRulesOnExit?: boolean; -} - /** Directional network policy used by provisioning. */ export interface DirectionalNetworkConfig { /** Outbound network policy. */ @@ -450,7 +422,7 @@ export interface ContainerConfig { /** Filesystem access configuration */ filesystem?: FilesystemConfig; /** Network access configuration */ - network?: NetworkConfig; + network?: DirectionalNetworkConfig; /** Runtime values supplied separately from sandbox policy. */ runtimeConfig?: NetworkRuntimeConfig; /** Telemetry configuration for TraceLogging ETW support */ diff --git a/sdk/node/tests/unit/wire-conformance.test.ts b/sdk/node/tests/unit/wire-conformance.test.ts index bc90c38f5..b53b27e47 100644 --- a/sdk/node/tests/unit/wire-conformance.test.ts +++ b/sdk/node/tests/unit/wire-conformance.test.ts @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -// Exact v1.1 one-shot type conformance oracle. +// Exact v1.0 one-shot type conformance oracle. // -// The generated module `../../src/generated/v1_1_0_alpha/wire.ts` is emitted -// from the exact v1.1 contract. It is the source of truth for the raw one-shot -// shape produced by the current high-level SDK builder. +// The generated module `../../src/generated/v1_0_0/wire.ts` is emitted +// from the published v1.0 contract. It is the source of truth for the raw +// one-shot shape produced by the high-level V1 SDK builder. // // This file asserts — at COMPILE TIME — that the hand-written public SDK types -// in `../../src/types.ts` still conform to that generated shape. If the Rust +// in `../../src/v1/types.ts` still conform to that generated shape. If the Rust // exact contract changes (a field renamed/removed, an enum value added/dropped, // a type narrowed) the regenerated oracle shifts and these assertions stop // compiling, so `npm run build:test-unit` fails. The runtime body is a no-op; @@ -17,12 +17,11 @@ // Mapping rationale: // * The exact contract rejects null and requires one-shot `process`. The // high-level builder supplies the exact version, process, and LXC defaults; -// `PublicV1OneShotConfig` models that emitted raw shape. -// * `NetworkConfig` intentionally spans historical public versions. Its -// public-only key assertion documents the legacy fields omitted from v1.1 -// raw JSON while keeping directional field conformance exact. +// `PublicV1OneShotConfig` models that emitted raw shape after containment +// validation excludes development-only choices. +// * Network comparisons use the directional fields emitted by the V1 builder. // * Stable WSLC is additionally checked against the published v0.9 one-shot -// contract because this stack exposes WSLC in both v0.9 and v1.1. +// contract because this stack exposes WSLC in both v0.9 and v1.0. // * `OnlyInPublic` additionally catches a public field whose wire counterpart // was renamed or removed (width subtyping alone would not), and is asserted // to equal a documented, explicit set of SDK-only fields — so a NEW @@ -61,6 +60,7 @@ import type { import type { Containment as InternalContainment, } from '../../src/v1/types.js'; +import type { SDK_CONTRACT_VERSION } from '../../src/v1/contract-version.js'; import type { ContainerId as InternalContainerId, } from '../../src/v1/lifecycle-types.js'; @@ -85,7 +85,6 @@ import type { ProcessConfig, LifecycleConfig, FilesystemConfig, - NetworkConfig, DirectionalNetworkConfig, NetworkEgressConfig, NetworkIngressConfig, @@ -103,8 +102,6 @@ import type { TelemetryConfig, ContainerConfig, ClipboardPolicy as PublicClipboardPolicy, - ContainmentType, - ContainmentBackend, } from '../../src/v1/types.js'; import type { @@ -134,7 +131,7 @@ import type { ProcessContainerUiIsolation as WireUiIsolation, TransportProtocol as WireTransportProtocol, Version as WireVersion, -} from '../../src/generated/v1_1_0_alpha/wire.js'; +} from '../../src/generated/v1_0_0/wire.js'; import type { OneShotWslc as WireV09Wslc, @@ -151,6 +148,7 @@ import type { // --- enum / union conformance --------------------------------------------- +type _SdkContractVersion = AssertTrue>; type _V1BackendConfigExport = AssertTrue< Equivalent >; @@ -203,10 +201,9 @@ type _ExecutionResultExitCode = AssertTrue>; -// The SDK splits containment into abstract intents + concrete backends; their -// union must cover exactly the wire `Containment` enum. +// V1 creation supports exactly the containment choices in its published wire contract. type _Containment = AssertTrue< - Equivalent + Equivalent >; // Enum-backed object fields are checked bidirectionally so an exact-contract @@ -226,6 +223,9 @@ type _NetworkPortProtocol = AssertTrue< type _BaseProcessUiIsolation = AssertTrue< Equivalent, WireUiIsolation> >; +type _BaseProcessUiSystemSettings = AssertTrue< + Equivalent> +>; type _PortProtocol = AssertTrue< Equivalent, WireTransportProtocol> >; @@ -237,7 +237,7 @@ type _PortProtocol = AssertTrue< type _ProcessVals = AssertTrue>; type _LifecycleVals = AssertTrue>; type _FilesystemVals = AssertTrue>; -type _NetworkVals = AssertTrue>; +type _NetworkVals = AssertTrue>; type _NetworkEgressVals = AssertTrue>; type _NetworkIngressVals = AssertTrue>; type _NetworkPeerVals = AssertTrue>; @@ -280,21 +280,7 @@ type _TelemetryKeys = AssertTrue, 'clearPolicyOnExit'>>; -// The public network type spans historical contracts. Exact v1.1 emits only -// directional fields; the listed legacy and SDK-only fields are deliberately -// absent from its raw JSON mapping. -type _NetworkKeys = AssertTrue< - Equivalent< - OnlyInPublic, - | 'enforcementMode' - | 'defaultPolicy' - | 'allowLocalNetwork' - | 'allowedHosts' - | 'blockedHosts' - | 'proxy' - | 'removeRulesOnExit' - > ->; +type _NetworkKeys = AssertTrue, never>>; // `ProcessContainerConfig.name` is the deprecated AppContainer profile name // (superseded by top-level `containerId`); not a wire `processContainer` field. @@ -307,18 +293,20 @@ type _LxcKeys = AssertTrue, nev // --- ROOT conformance (review finding F1) --------------------------------- // Without these, a top-level wire field rename/removal regenerates wire.ts but // no assertion notices, so the leaf-only checks above are not enough. -// `PublicV1OneShotConfig` models the current high-level builder output: -// * its value shape must be assignable to exact v1.1 `OneShotRequest`; and +// `PublicV1OneShotConfig` models the current high-level builder output, +// excluding the development-only Hyperlight section of the public raw config: +// * its value shape must be assignable to exact v1.0 `OneShotRequest`; and // * it must have no public-only root keys. // A new root divergence fails the build. type PublicV1OneShotConfig = Omit< ContainerConfig, - 'version' | 'process' | 'network' | 'lxc' | 'appContainer' + 'version' | 'process' | 'network' | 'lxc' | 'appContainer' | 'containment' | 'hyperlight' > & { version: WireVersion; process: ProcessConfig; network?: DirectionalNetworkConfig; lxc?: RawV1LxcConfig; + containment?: V1Containment['type']; }; type _RootVals = AssertTrue>; type _RootKeys = AssertTrue< @@ -334,7 +322,7 @@ type _RootKeys = AssertTrue< type _ProcessWireKeys = AssertTrue, never>>; type _LifecycleWireKeys = AssertTrue, never>>; type _FilesystemWireKeys = AssertTrue, never>>; -type _NetworkWireKeys = AssertTrue, never>>; +type _NetworkWireKeys = AssertTrue, never>>; type _NetworkEgressWireKeys = AssertTrue, never>>; type _NetworkIngressWireKeys = AssertTrue, never>>; type _NetworkPeerWireKeys = AssertTrue, never>>; @@ -358,26 +346,24 @@ type _SeatbeltWireKeys = AssertTrue< >; type _TelemetryWireKeys = AssertTrue, never>>; -// Root: the high-level builder intentionally omits schema metadata, fallback, -// development-only test and Windows Sandbox sections. +// Root: the high-level builder intentionally omits schema metadata and fallback. type _RootWireKeys = AssertTrue< Equivalent< OnlyInWire, | '$schema' | '_comment' | 'fallback' - | 'test' - | 'windowsSandbox' > >; // Reference the assertion aliases so they read as intentionally load-bearing. export type WireConformanceAssertions = [ - _V1BackendConfigExport, _V1IdExport, + _SdkContractVersion, _V1BackendConfigExport, _V1IdExport, _ContainerRequestFilesystem, _ContainerRequestNetwork, _ContainerRequestContainment, _Clipboard, _Containment, _NetworkEgressDefault, _NetworkIngressDefault, _NetworkIngressHostLoopback, - _NetworkPortProtocol, _BaseProcessUiIsolation, _PortProtocol, + _NetworkPortProtocol, _BaseProcessUiIsolation, _BaseProcessUiSystemSettings, + _PortProtocol, _ProcessVals, _LifecycleVals, _FilesystemVals, _NetworkVals, _UiVals, _NetworkEgressVals, _NetworkIngressVals, _NetworkPeerVals, _NetworkPortVals, _NetworkRuleVals, _RuntimeConfigVals,