@@ -5,6 +5,14 @@ vi.mock('./electron-runtime', () => ({
55 ipcMain : { handle : vi . fn ( ) } ,
66} ) ) ;
77
8+ // Mock the TLS-bypass wrapper so tests can assert the enabled flag passed to
9+ // it without actually swapping the global undici dispatcher mid-test. The
10+ // pass-through impl preserves the existing fetch path so all other suites in
11+ // this file (which rely on installFakeFetch) keep working unchanged.
12+ vi . mock ( './tls-override' , ( ) => ( {
13+ withTlsBypass : vi . fn ( async ( _enabled : boolean , fn : ( ) => Promise < unknown > ) => fn ( ) ) ,
14+ } ) ) ;
15+
816import { createHash } from 'node:crypto' ;
917import {
1018 _clearModelsCache ,
@@ -23,6 +31,7 @@ import {
2331 normalizeOllamaBaseUrl ,
2432 runProviderTest ,
2533} from './connection-ipc' ;
34+ import { withTlsBypass } from './tls-override' ;
2635
2736// ---------------------------------------------------------------------------
2837// Thin test-only handler that exercises the same fetch/parse/cache path
@@ -1386,3 +1395,156 @@ describe('config:v1:test-endpoint response parsing', () => {
13861395 }
13871396 } ) ;
13881397} ) ;
1398+
1399+ // ---------------------------------------------------------------------------
1400+ // TLS bypass plumbing — every outbound HTTP entrypoint must (a) consult
1401+ // withTlsBypass exactly once and (b) gate the `enabled` arg by the
1402+ // `builtin !== true && tlsRejectUnauthorized === true` rule so a tampered
1403+ // config can never weaken TLS for built-in providers.
1404+ // ---------------------------------------------------------------------------
1405+
1406+ describe ( 'TLS bypass routing' , ( ) => {
1407+ beforeEach ( ( ) => {
1408+ vi . useRealTimers ( ) ;
1409+ vi . mocked ( withTlsBypass ) . mockClear ( ) ;
1410+ } ) ;
1411+
1412+ function lastBypassEnabled ( ) : boolean | undefined {
1413+ const calls = vi . mocked ( withTlsBypass ) . mock . calls ;
1414+ const last = calls . at ( - 1 ) ;
1415+ return last ?. [ 0 ] ;
1416+ }
1417+
1418+ it ( 'runProviderTest: built-in provider with tlsRejectUnauthorized=true is forced to enabled=false' , async ( ) => {
1419+ const { restore } = installFakeFetch ( ( ) => ( { status : 200 , body : { data : [ ] } } ) ) ;
1420+ try {
1421+ const res = await runProviderTest ( {
1422+ provider : 'anthropic' ,
1423+ wire : 'anthropic' ,
1424+ apiKey : 'sk-ant-test' ,
1425+ baseUrl : 'https://api.anthropic.com' ,
1426+ builtin : true ,
1427+ tlsRejectUnauthorized : true ,
1428+ } ) ;
1429+ expect ( res . ok ) . toBe ( true ) ;
1430+ expect ( vi . mocked ( withTlsBypass ) ) . toHaveBeenCalledTimes ( 1 ) ;
1431+ expect ( lastBypassEnabled ( ) ) . toBe ( false ) ;
1432+ } finally {
1433+ restore ( ) ;
1434+ }
1435+ } ) ;
1436+
1437+ it ( 'runProviderTest: non-built-in provider with tlsRejectUnauthorized=true → enabled=true' , async ( ) => {
1438+ const { restore } = installFakeFetch ( ( ) => ( { status : 200 , body : { data : [ ] } } ) ) ;
1439+ try {
1440+ const res = await runProviderTest ( {
1441+ provider : 'internal-gateway' ,
1442+ wire : 'openai-chat' ,
1443+ apiKey : 'sk-test' ,
1444+ baseUrl : 'https://internal.example.corp/v1' ,
1445+ builtin : false ,
1446+ tlsRejectUnauthorized : true ,
1447+ } ) ;
1448+ expect ( res . ok ) . toBe ( true ) ;
1449+ expect ( vi . mocked ( withTlsBypass ) ) . toHaveBeenCalledTimes ( 1 ) ;
1450+ expect ( lastBypassEnabled ( ) ) . toBe ( true ) ;
1451+ } finally {
1452+ restore ( ) ;
1453+ }
1454+ } ) ;
1455+
1456+ it ( 'runProviderTest: non-built-in with tlsRejectUnauthorized=false → enabled=false' , async ( ) => {
1457+ const { restore } = installFakeFetch ( ( ) => ( { status : 200 , body : { data : [ ] } } ) ) ;
1458+ try {
1459+ const res = await runProviderTest ( {
1460+ provider : 'internal-gateway' ,
1461+ wire : 'openai-chat' ,
1462+ apiKey : 'sk-test' ,
1463+ baseUrl : 'https://internal.example.corp/v1' ,
1464+ builtin : false ,
1465+ tlsRejectUnauthorized : false ,
1466+ } ) ;
1467+ expect ( res . ok ) . toBe ( true ) ;
1468+ expect ( lastBypassEnabled ( ) ) . toBe ( false ) ;
1469+ } finally {
1470+ restore ( ) ;
1471+ }
1472+ } ) ;
1473+
1474+ it ( 'runProviderTest: non-built-in with tlsRejectUnauthorized=undefined → enabled=false' , async ( ) => {
1475+ const { restore } = installFakeFetch ( ( ) => ( { status : 200 , body : { data : [ ] } } ) ) ;
1476+ try {
1477+ const res = await runProviderTest ( {
1478+ provider : 'internal-gateway' ,
1479+ wire : 'openai-chat' ,
1480+ apiKey : 'sk-test' ,
1481+ baseUrl : 'https://internal.example.corp/v1' ,
1482+ builtin : false ,
1483+ } ) ;
1484+ expect ( res . ok ) . toBe ( true ) ;
1485+ expect ( lastBypassEnabled ( ) ) . toBe ( false ) ;
1486+ } finally {
1487+ restore ( ) ;
1488+ }
1489+ } ) ;
1490+
1491+ it ( 'handleConfigV1TestEndpoint: payload with tlsRejectUnauthorized=true → enabled=true' , async ( ) => {
1492+ const { restore } = installFakeFetch ( ( ) => ( {
1493+ status : 200 ,
1494+ body : { data : [ { id : 'gpt-x' } ] } ,
1495+ } ) ) ;
1496+ try {
1497+ const res = await handleConfigV1TestEndpoint ( {
1498+ wire : 'openai-chat' ,
1499+ baseUrl : 'https://internal.example.corp/v1' ,
1500+ apiKey : 'sk-test' ,
1501+ tlsRejectUnauthorized : true ,
1502+ } ) ;
1503+ expect ( res ) . toEqual ( { ok : true , modelCount : 1 , models : [ 'gpt-x' ] } ) ;
1504+ expect ( vi . mocked ( withTlsBypass ) ) . toHaveBeenCalledTimes ( 1 ) ;
1505+ expect ( lastBypassEnabled ( ) ) . toBe ( true ) ;
1506+ } finally {
1507+ restore ( ) ;
1508+ }
1509+ } ) ;
1510+
1511+ it ( 'handleConfigV1TestEndpoint: missing tlsRejectUnauthorized → enabled=false' , async ( ) => {
1512+ const { restore } = installFakeFetch ( ( ) => ( {
1513+ status : 200 ,
1514+ body : { data : [ { id : 'gpt-x' } ] } ,
1515+ } ) ) ;
1516+ try {
1517+ await handleConfigV1TestEndpoint ( {
1518+ wire : 'openai-chat' ,
1519+ baseUrl : 'https://internal.example.corp/v1' ,
1520+ apiKey : 'sk-test' ,
1521+ } ) ;
1522+ expect ( lastBypassEnabled ( ) ) . toBe ( false ) ;
1523+ } finally {
1524+ restore ( ) ;
1525+ }
1526+ } ) ;
1527+
1528+ it ( 'handleConfigV1TestEndpoint: rejects non-boolean tlsRejectUnauthorized before fetch' , async ( ) => {
1529+ const { restore } = installFakeFetch ( ( ) => {
1530+ throw new Error ( 'fetch should not be called' ) ;
1531+ } ) ;
1532+ try {
1533+ await expect (
1534+ handleConfigV1TestEndpoint ( {
1535+ wire : 'openai-chat' ,
1536+ baseUrl : 'https://provider.example/v1' ,
1537+ apiKey : 'sk-test' ,
1538+ tlsRejectUnauthorized : 'yes' ,
1539+ } ) ,
1540+ ) . resolves . toEqual ( {
1541+ ok : false ,
1542+ error : 'bad-input' ,
1543+ message : 'tlsRejectUnauthorized must be a boolean' ,
1544+ } ) ;
1545+ expect ( vi . mocked ( withTlsBypass ) ) . not . toHaveBeenCalled ( ) ;
1546+ } finally {
1547+ restore ( ) ;
1548+ }
1549+ } ) ;
1550+ } ) ;
0 commit comments