2026-05-17 14:48:52 -04:00
|
|
|
import assert from 'node:assert/strict';
|
|
|
|
|
|
|
|
|
|
globalThis.window = {
|
|
|
|
|
EnhancedSecureCryptoUtils: {},
|
|
|
|
|
DEBUG_MODE: true,
|
|
|
|
|
DEVELOPMENT_MODE: true,
|
|
|
|
|
location: { hostname: 'localhost', search: '?debug' },
|
|
|
|
|
webpackHotUpdate: {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const { EnhancedSecureWebRTCManager } = await import('../src/network/EnhancedSecureWebRTCManager.js');
|
|
|
|
|
|
|
|
|
|
function fake(config = {}) {
|
|
|
|
|
return {
|
|
|
|
|
_config: {
|
|
|
|
|
webrtc: {
|
2026-05-17 17:57:11 -04:00
|
|
|
privacyMode: config.privacyMode ?? (config.relayOnly ? 'relay-only' : 'standard'),
|
2026-05-17 14:48:52 -04:00
|
|
|
relayOnly: config.relayOnly ?? false,
|
|
|
|
|
iceServers: config.iceServers ?? [{ urls: 'stun:stun.example.test:3478' }]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
_ipLeakWarningShown: false,
|
|
|
|
|
delivered: [],
|
|
|
|
|
deliverMessageToUI(message, type) {
|
|
|
|
|
this.delivered.push({ message, type });
|
|
|
|
|
},
|
2026-05-17 17:57:11 -04:00
|
|
|
_hasTurnServer: EnhancedSecureWebRTCManager.prototype._hasTurnServer,
|
|
|
|
|
_isRelayOnlyMode: EnhancedSecureWebRTCManager.prototype._isRelayOnlyMode
|
2026-05-17 14:48:52 -04:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 17:57:11 -04:00
|
|
|
// Standard mode remains usable, but it is not relay-only.
|
2026-05-17 14:48:52 -04:00
|
|
|
{
|
|
|
|
|
const manager = fake();
|
|
|
|
|
const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager);
|
|
|
|
|
assert.equal(config.iceTransportPolicy, undefined);
|
|
|
|
|
assert.equal(config.iceServers[0].urls, 'stun:stun.example.test:3478');
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 17:57:11 -04:00
|
|
|
// Explicit privacy mode uses relay-only transport, suppressing host/srflx usage.
|
|
|
|
|
{
|
|
|
|
|
const manager = fake({ privacyMode: 'relay-only', iceServers: [{ urls: 'turn:turn.example.test:3478' }] });
|
|
|
|
|
const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager);
|
|
|
|
|
assert.equal(config.iceTransportPolicy, 'relay');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Backward-compatible relayOnly alias still enables relay transport.
|
2026-05-17 14:48:52 -04:00
|
|
|
{
|
|
|
|
|
const manager = fake({ relayOnly: true, iceServers: [{ urls: 'turn:turn.example.test:3478' }] });
|
|
|
|
|
const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager);
|
|
|
|
|
assert.equal(config.iceTransportPolicy, 'relay');
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 17:57:11 -04:00
|
|
|
// Missing TURN in standard mode warns clearly and visibly.
|
2026-05-17 14:48:52 -04:00
|
|
|
{
|
|
|
|
|
const manager = fake();
|
|
|
|
|
EnhancedSecureWebRTCManager.prototype._warnIfTurnMissing.call(manager);
|
2026-05-17 17:57:11 -04:00
|
|
|
assert.equal(manager.delivered[0].type, 'system');
|
|
|
|
|
assert.match(manager.delivered[0].message, /relay-only mode is disabled/i);
|
|
|
|
|
assert.match(manager.delivered[0].message, /may expose host or server-reflexive IP addresses/i);
|
2026-05-17 14:48:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// STUN-only config does not claim IP protection, even with privacy mode selected.
|
|
|
|
|
{
|
2026-05-17 17:57:11 -04:00
|
|
|
const manager = fake({ privacyMode: 'relay-only', iceServers: [{ urls: 'stun:stun.example.test:3478' }] });
|
2026-05-17 14:48:52 -04:00
|
|
|
assert.equal(EnhancedSecureWebRTCManager.prototype._hasTurnServer.call(manager), false);
|
|
|
|
|
EnhancedSecureWebRTCManager.prototype._warnIfTurnMissing.call(manager);
|
|
|
|
|
assert.match(manager.delivered[0].message, /STUN alone does not hide IP addresses/i);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 17:57:11 -04:00
|
|
|
// Non-private mode warns even when TURN exists because direct candidates remain allowed.
|
|
|
|
|
{
|
|
|
|
|
const manager = fake({ iceServers: [{ urls: 'turn:turn.example.test:3478' }] });
|
|
|
|
|
EnhancedSecureWebRTCManager.prototype._warnIfTurnMissing.call(manager);
|
|
|
|
|
assert.equal(manager.delivered[0].type, 'system');
|
|
|
|
|
assert.match(manager.delivered[0].message, /relay-only mode is disabled/i);
|
|
|
|
|
assert.match(manager.delivered[0].message, /may expose host or server-reflexive IP addresses/i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ICE defaults are centralized and operator overrides remain untouched.
|
|
|
|
|
{
|
|
|
|
|
assert.equal(Array.isArray(EnhancedSecureWebRTCManager.DEFAULT_ICE_SERVERS), true);
|
|
|
|
|
const overrideServers = [{ urls: ['stun:operator.example.test:3478', 'turn:operator.example.test:3478'] }];
|
|
|
|
|
const manager = fake({ iceServers: overrideServers });
|
|
|
|
|
const config = EnhancedSecureWebRTCManager.prototype._buildPeerConnectionConfig.call(manager);
|
|
|
|
|
assert.equal(config.iceServers, overrideServers);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-17 14:48:52 -04:00
|
|
|
console.log('WebRTC privacy mode tests passed');
|