const assert = require('node:assert/strict'); const { spawnSync } = require('node:child_process'); const { chmodSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync, } = require('node:fs'); const { tmpdir } = require('node:os'); const { join, resolve } = require('node:path'); const { afterEach, test } = require('node:test'); const { executeClusterWorkerCredentialManagementClient, validateClusterWorkerCredentialManagementClientResult, } = require('@qinglong/cluster-admin/worker-credential-management-client'); const CLIENT_KEY = resolve( __dirname, '../../ql3-cluster-control/test/fixtures/mtls/client-key.pem', ); const CLIENT_CERT = resolve( __dirname, '../../ql3-cluster-control/test/fixtures/mtls/client-cert.pem', ); const SERVER_KEY = resolve( __dirname, '../../ql3-cluster-control/test/fixtures/mtls/server-key.pem', ); const CA_CERT = resolve( __dirname, '../../ql3-cluster-control/test/fixtures/mtls/ca-cert.pem', ); const temporaryDirectories = []; const CLI = resolve( __dirname, '../dist/worker-credential/workerCredentialManagementClientCli.js', ); const subject = Object.freeze({ type: 'user', id: 'operator-a' }); const plan = Object.freeze({ actionRef: 'worker-credential:worker-1:rotate:1', authorityProjectId: 'cluster-authority', action: 'rotate', target: Object.freeze({ deliveryId: 'delivery-1', workerId: 'worker-1', credentialId: 'credential-2', previousCredentialId: 'credential-1', credentialNotBeforeAtMs: 1_000, credentialExpiresAtMs: 2_000, deploymentTargetDigest: 'a'.repeat(64), deploymentGeneration: 'generation-2', }), requestedBy: subject, plannedAtMs: 900, expiresAtMs: 1_800, previewDigest: 'b'.repeat(64), planDigest: 'c'.repeat(64), }); const approval = Object.freeze({ id: 'approval-1', projectId: 'cluster-authority', version: 2, state: 'approved', risk: 'high', decisionMode: 'four_eyes', requestedBy: subject, requestedAtMs: 910, expiresAtMs: 1_800, decision: 'approved', decisionReasonCode: 'reviewed', decidedBy: Object.freeze({ type: 'user', id: 'reviewer-b' }), decidedAtMs: 920, dispatchId: null, consumedAtMs: null, actionType: 'worker_credential.delivery.rotate', actionRef: plan.actionRef, actionDigest: 'd'.repeat(64), previewDigest: plan.previewDigest, }); const session = Object.freeze({ workerId: 'worker-1', sessionId: '018f0f5d-7b6a-7a11-8f4d-2f7b4f477001', generation: 2, sessionVersion: 5, lifecycle: 'online', compatibility: 'default_placement', architecture: 'arm64', supportTier: 'tier1', protocolVersion: '1.0.0', operatingSystem: 'linux', maxConcurrentRuns: 2, availableSlots: 1, registeredAtMs: 900, lastHeartbeatAtMs: 1_050, leaseExpiresAtMs: 2_000, updatedAtMs: 1_050, observedAtMs: 1_100, runtimes: Object.freeze([{ name: 'node', version: '24.18.0' }]), declaredCapacity: Object.freeze({ cpuCores: 1, memoryBytes: 268_435_456, diskBytes: 1_073_741_824, gpuCount: 0, }), }); const sessionSummary = Object.freeze( Object.fromEntries( Object.entries(session).filter( ([key]) => key !== 'runtimes' && key !== 'declaredCapacity', ), ), ); function command(operation) { return { operation, request: {}, }; } function privateWrite(filePath, value) { writeFileSync(filePath, value, { mode: 0o600 }); chmodSync(filePath, 0o600); } function clientFiles(options = {}) { const directory = realpathSync( mkdtempSync(join(tmpdir(), 'ql3-worker-client-')), ); temporaryDirectories.push(directory); const paths = { configFile: join(directory, 'client.json'), commandFile: join(directory, 'command.json'), assertionFile: join(directory, 'assertion.jwt'), }; const caFile = join(directory, 'ca.crt'); const clientCertificateFile = join(directory, 'client.crt'); const clientPrivateKeyFile = join(directory, 'client.key'); privateWrite(caFile, readFileSync(CA_CERT)); privateWrite(clientCertificateFile, readFileSync(CLIENT_CERT)); privateWrite( clientPrivateKeyFile, readFileSync(options.mismatchedKey ? SERVER_KEY : CLIENT_KEY), ); const config = { schemaVersion: 1, endpoint: 'https://manager.example.test:8444/api/v3/worker-credentials/management', servername: 'manager.example.test', caFile, ...(options.omitClientIdentity ? {} : { clientCertificateFile, clientPrivateKeyFile }), requestTimeoutMs: 1_000, }; privateWrite(paths.configFile, `${JSON.stringify(config)}\n`); privateWrite( paths.commandFile, `${JSON.stringify({ schemaVersion: 1, operation: 'worker-credential.inspect', request: { actionRef: 'worker-credential:worker-1:rotate:1', authorityProjectId: 'cluster-authority', approvalRequestId: 'approval-worker-1', inspectionId: 'inspection-worker-1', }, })}\n`, ); privateWrite( paths.assertionFile, 'eyJhbGciOiJFZERTQSJ9.eyJzdWIiOiJ1In0.c2lnbmF0dXJl', ); return { paths, clientPrivateKeyFile }; } afterEach(() => { for (const directory of temporaryDirectories.splice(0)) { rmSync(directory, { recursive: true, force: true }); } }); test('validates all six low-sensitive Worker management results', () => { const fixtures = [ [ 'worker-credential.plan', { schemaVersion: 1, operation: 'worker-credential.plan', status: 'created', plan }, ], [ 'worker-credential.propose', { schemaVersion: 1, operation: 'worker-credential.propose', approvalStatus: 'created', plan, approval, }, ], [ 'worker-credential.decide', { schemaVersion: 1, operation: 'worker-credential.decide', status: 'decided', approval, }, ], [ 'worker-credential.inspect', { schemaVersion: 1, operation: 'worker-credential.inspect', plan, approval, stale: false, }, ], [ 'worker-session.inspect', { schemaVersion: 1, operation: 'worker-session.inspect', observedAtMs: 1_100, worker: session, }, ], [ 'worker-session.list', { schemaVersion: 1, operation: 'worker-session.list', observedAtMs: 1_100, workers: [sessionSummary], nextCursor: null, }, ], ]; for (const [operation, result] of fixtures) { assert.equal( validateClusterWorkerCredentialManagementClientResult( result, command(operation), ).operation, operation, ); assert.doesNotMatch(JSON.stringify(result), /authenticationId|token|secret/i); } }); test('rejects widened and secret-bearing response shapes', () => { assert.throws(() => validateClusterWorkerCredentialManagementClientResult( { schemaVersion: 1, operation: 'worker-credential.inspect', plan: null, approval: { ...approval, authenticationId: 'must-not-leak' }, stale: false, }, command('worker-credential.inspect'), ), ); assert.throws(() => validateClusterWorkerCredentialManagementClientResult( { schemaVersion: 1, operation: 'worker-credential.execute', status: 'completed', }, command('worker-credential.inspect'), ), ); assert.throws(() => validateClusterWorkerCredentialManagementClientResult( { schemaVersion: 1, operation: 'worker-session.inspect', observedAtMs: 1_100, worker: { ...session, labels: { secret: 'value' } }, }, command('worker-session.inspect'), ), ); assert.throws(() => validateClusterWorkerCredentialManagementClientResult( { schemaVersion: 1, operation: 'worker-session.list', observedAtMs: 1_100, workers: [ { ...sessionSummary, workerId: 'worker-b' }, { ...sessionSummary, workerId: 'worker-a' }, ], nextCursor: null, }, command('worker-session.list'), ), ); for (const worker of [ { ...session, workerId: null }, { ...session, generation: -1 }, { ...session, availableSlots: 3 }, { ...session, observedAtMs: 1_000 }, { ...session, declaredCapacity: { ...session.declaredCapacity, gpuCount: -1 } }, ]) { assert.throws(() => validateClusterWorkerCredentialManagementClientResult( { schemaVersion: 1, operation: 'worker-session.inspect', observedAtMs: worker.observedAtMs, worker, }, command('worker-session.inspect'), ), ); } }); test('requires one matching private client certificate identity before connect', async () => { { const files = clientFiles(); let connects = 0; await assert.rejects( executeClusterWorkerCredentialManagementClient(files.paths, { async connect(target) { connects += 1; assert.deepEqual(target, { hostname: 'manager.example.test', port: 8444, }); throw new Error('expected-connect-stop'); }, }), { code: 'QL3_PLUGIN_PACKAGE_MANAGEMENT_CLIENT_REQUEST_FAILED' }, ); assert.equal(connects, 1); } for (const options of [ { omitClientIdentity: true }, { mismatchedKey: true }, ]) { const files = clientFiles(options); let connects = 0; await assert.rejects( executeClusterWorkerCredentialManagementClient(files.paths, { async connect() { connects += 1; throw new Error('must not connect'); }, }), { code: 'QL3_PLUGIN_PACKAGE_MANAGEMENT_CLIENT_CONFIG_INVALID' }, ); assert.equal(connects, 0); } }); test('CLI exposes path-only usage and no credential material', () => { const help = spawnSync(process.execPath, [CLI, '--help'], { encoding: 'utf8', }); assert.equal(help.status, 0); assert.match(help.stdout, /^Usage: ql3-worker-credential-client /); assert.doesNotMatch(help.stdout, /token|secret|credential-value/i); const invalid = spawnSync(process.execPath, [CLI, '--assertion=value'], { encoding: 'utf8', }); assert.equal(invalid.status, 64); assert.match(invalid.stderr, /USAGE_INVALID/); assert.doesNotMatch(invalid.stderr, /assertion=value/); });