From 9dbe9b5f179444ab667da67f24a8d2e725c96924 Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Thu, 4 Jun 2026 10:20:11 +0000 Subject: [PATCH 1/2] feat(core): add tlsRejectUnauthorized option to Context for dev environments Allows bypassing TLS certificate verification for oscaidev workspace instances that serve self-signed certs. Defaults to true (no change for existing users). Co-Authored-By: Claude --- packages/core/src/context.ts | 8 ++++++++ packages/core/src/core.test.ts | 23 +++++++++++++++++++++++ packages/core/src/core.ts | 17 +++++++++++------ packages/core/src/fetch.ts | 17 +++++++++++++++-- 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/packages/core/src/context.ts b/packages/core/src/context.ts index db970ec..fabbfa0 100644 --- a/packages/core/src/context.ts +++ b/packages/core/src/context.ts @@ -3,6 +3,7 @@ import { createFetch } from './fetch'; export type ContextConfig = { personalAccessToken?: string; environment?: string; + tlsRejectUnauthorized?: boolean; }; export type ServiceAccessToken = { @@ -25,6 +26,7 @@ export type Subscriptions = { export class Context { private personalAccessToken?: string; private environment: string; + private _tlsRejectUnauthorized: boolean; constructor(config?: ContextConfig) { if (!config?.personalAccessToken && !process.env.OSC_ACCESS_TOKEN) { @@ -37,6 +39,8 @@ export class Context { ? config.personalAccessToken : process.env.OSC_ACCESS_TOKEN; this.environment = config?.environment ? config.environment : 'prod'; + this._tlsRejectUnauthorized = + config?.tlsRejectUnauthorized !== false ? true : false; } getPersonalAccessToken() { @@ -47,6 +51,10 @@ export class Context { return this.environment; } + get tlsRejectUnauthorized() { + return this._tlsRejectUnauthorized; + } + async getServiceAccessToken(serviceId: string): Promise { const serviceUrl = new URL( `https://catalog.svc.${this.environment}.osaas.io/mysubscriptions` diff --git a/packages/core/src/core.test.ts b/packages/core/src/core.test.ts index 52e53d4..5940dae 100644 --- a/packages/core/src/core.test.ts +++ b/packages/core/src/core.test.ts @@ -8,6 +8,29 @@ jest.mock('./fetch', () => ({ createFetch: jest.fn() })); +describe('Context tlsRejectUnauthorized', () => { + test('defaults to true when not specified', () => { + const ctx = new Context({ personalAccessToken: 'dummy' }); + expect(ctx.tlsRejectUnauthorized).toBe(true); + }); + + test('stores false when explicitly set to false', () => { + const ctx = new Context({ + personalAccessToken: 'dummy', + tlsRejectUnauthorized: false + }); + expect(ctx.tlsRejectUnauthorized).toBe(false); + }); + + test('stores true when explicitly set to true', () => { + const ctx = new Context({ + personalAccessToken: 'dummy', + tlsRejectUnauthorized: true + }); + expect(ctx.tlsRejectUnauthorized).toBe(true); + }); +}); + describe('Core functionalities', () => { afterAll(() => { jest.clearAllMocks(); diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 7597ba1..10bd2a2 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -298,12 +298,17 @@ export async function getInstanceHealth( 'health' ); - const { status } = await createFetch<{ status: string }>(healthUrl, { - method: 'GET', - headers: { - 'x-jwt': `Bearer ${token}` - } - }); + const { status } = await createFetch<{ status: string }>( + healthUrl, + { + method: 'GET', + headers: { + 'x-jwt': `Bearer ${token}` + } + }, + undefined, + context.tlsRejectUnauthorized + ); return status; } diff --git a/packages/core/src/fetch.ts b/packages/core/src/fetch.ts index 9a2d88d..5a6a9e1 100644 --- a/packages/core/src/fetch.ts +++ b/packages/core/src/fetch.ts @@ -28,11 +28,24 @@ const defaultErrorFactory: ErrorFactory = async (response) => { export const createFetch = async ( url: string | URL, options?: RequestInit, - errorFactory: ErrorFactory = defaultErrorFactory + errorFactory: ErrorFactory = defaultErrorFactory, + tlsRejectUnauthorized = true ): Promise => { try { Log().debug(`${options?.method}: ${url}`); - const response = await fetch(url, { ...options }); + const fetchOptions: RequestInit = { ...options }; + if (!tlsRejectUnauthorized) { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { Agent } = require('undici') as { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Agent: new (opts: { connect: { rejectUnauthorized: boolean } }) => any; + }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (fetchOptions as any).dispatcher = new Agent({ + connect: { rejectUnauthorized: false } + }); + } + const response = await fetch(url, fetchOptions); Log().debug( response.status + ': ' + response.statusText + ': ' + response.ok From 1b42d235103ad2392b635c7f762d458c6ff60ecd Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Thu, 4 Jun 2026 10:30:23 +0000 Subject: [PATCH 2/2] fix(core): add undici to runtime dependencies for TLS dispatcher require('undici') is used at runtime when tlsRejectUnauthorized: false. Without this, published @osaas/client-core would throw MODULE_NOT_FOUND in consumer environments where undici is not a transitive dev dep. Co-Authored-By: Claude --- package-lock.json | 5 ++--- packages/core/package.json | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8d95da9..0a5c77f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2829,7 +2829,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dev": true, "engines": { "node": ">=14" } @@ -14486,7 +14485,6 @@ "version": "5.28.4", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", - "dev": true, "dependencies": { "@fastify/busboy": "^2.0.0" }, @@ -15021,7 +15019,8 @@ "version": "0.24.0", "license": "MIT", "dependencies": { - "chalk": "4.1.2" + "chalk": "4.1.2", + "undici": "^5.4.0" } }, "packages/db": { diff --git a/packages/core/package.json b/packages/core/package.json index eee15ec..6dc9707 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -25,7 +25,8 @@ "test": "jest" }, "dependencies": { - "chalk": "4.1.2" + "chalk": "4.1.2", + "undici": "^5.4.0" }, "publishConfig": { "access": "public"