mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-11-06 20:58:14 -04:00
test(proxy): generate TLS test fixtures at runtime (#2389)
Generate ephemeral TLS fixtures once per test file. Preserve transport assertions and add certificate validation coverage.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import { generateKeyPairSync, sign, X509Certificate } from 'node:crypto'
|
||||
|
||||
// Minimal DER encoding for this test certificate, including long-form lengths.
|
||||
function der(tag: number, ...parts: Buffer[]): Buffer {
|
||||
const content = Buffer.concat(parts)
|
||||
const length: number[] = []
|
||||
if (content.length < 128) {
|
||||
length.push(content.length)
|
||||
} else {
|
||||
for (let remaining = content.length; remaining > 0; remaining >>>= 8) {
|
||||
length.unshift(remaining & 0xff)
|
||||
}
|
||||
length.unshift(0x80 | length.length)
|
||||
}
|
||||
return Buffer.concat([Buffer.from([tag, ...length]), content])
|
||||
}
|
||||
|
||||
const sequence = (...parts: Buffer[]) => der(0x30, ...parts)
|
||||
const oid = (hex: string) => der(0x06, Buffer.from(hex, 'hex'))
|
||||
const booleanTrue = der(0x01, Buffer.from([0xff]))
|
||||
|
||||
function certificateTime(date: Date): Buffer {
|
||||
const digits = date.toISOString().replace(/[-:T]/g, '').replace(/\.\d{3}Z$/, 'Z')
|
||||
// X.509 uses UTCTime for 1950–2049 and GeneralizedTime otherwise.
|
||||
const utc = date.getUTCFullYear() >= 1950 && date.getUTCFullYear() < 2050
|
||||
return der(utc ? 0x17 : 0x18, Buffer.from(utc ? digits.slice(2) : digits))
|
||||
}
|
||||
|
||||
function extension(oidHex: string, value: Buffer, critical = false): Buffer {
|
||||
return sequence(
|
||||
oid(oidHex),
|
||||
...(critical ? [booleanTrue] : []),
|
||||
der(0x04, value),
|
||||
)
|
||||
}
|
||||
|
||||
export function createTestTlsFixture() {
|
||||
const { privateKey, publicKey } = generateKeyPairSync('ec', {
|
||||
namedCurve: 'prime256v1',
|
||||
})
|
||||
// AlgorithmIdentifier for ecdsa-with-SHA256 (1.2.840.10045.4.3.2).
|
||||
const algorithm = sequence(oid('2a8648ce3d040302'))
|
||||
const name = sequence(der(0x31, sequence(
|
||||
oid('550403'), // commonName (2.5.4.3)
|
||||
der(0x0c, Buffer.from('localhost')),
|
||||
)))
|
||||
const now = Date.now()
|
||||
const day = 24 * 60 * 60 * 1000
|
||||
const tbsCertificate = sequence(
|
||||
der(0xa0, der(0x02, Buffer.from([2]))), // explicit v3 version
|
||||
der(0x02, Buffer.from([1])), // positive serial, unique for this fresh issuer
|
||||
algorithm,
|
||||
name, // issuer and subject are identical for a self-signed certificate
|
||||
sequence(certificateTime(new Date(now - day)), certificateTime(new Date(now + day))),
|
||||
name,
|
||||
publicKey.export({ type: 'spki', format: 'der' }),
|
||||
der(0xa3, sequence(
|
||||
extension('551d13', sequence(booleanTrue), true), // basicConstraints: CA
|
||||
// keyUsage: digitalSignature + keyCertSign; two unused bits.
|
||||
extension('551d0f', der(0x03, Buffer.from([2, 0x84])), true),
|
||||
extension('551d11', sequence( // subjectAltName
|
||||
der(0x82, Buffer.from('localhost')),
|
||||
der(0x87, Buffer.from([127, 0, 0, 1])),
|
||||
)),
|
||||
)),
|
||||
)
|
||||
const certificate = sequence(
|
||||
tbsCertificate,
|
||||
algorithm,
|
||||
der(0x03, Buffer.from([0]), sign('sha256', tbsCertificate, privateKey)),
|
||||
)
|
||||
return {
|
||||
key: privateKey.export({ type: 'pkcs8', format: 'pem' }),
|
||||
cert: new X509Certificate(certificate).toString(),
|
||||
publicKey,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { afterEach, beforeEach, describe, expect, test } from 'bun:test'
|
||||
import { X509Certificate } from 'node:crypto'
|
||||
import { once } from 'node:events'
|
||||
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { createServer as createHttpServer, type IncomingMessage } from 'node:http'
|
||||
@@ -13,6 +14,7 @@ import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import type { Duplex } from 'node:stream'
|
||||
import { snapshotEnv } from '../test/envSnapshot.js'
|
||||
import { createTestTlsFixture } from '../test/tlsFixture.js'
|
||||
import { clearCACertsCache } from './caCerts.js'
|
||||
import { clearMTLSCache, getMTLSAgent, getWebSocketTLSOptions } from './mtls.js'
|
||||
|
||||
@@ -46,23 +48,19 @@ const { createAxiosInstance, getWebSocketProxyAgent } = await import(
|
||||
`data:text/javascript;base64,${Buffer.from(await proxyBuild.outputs[0].text()).toString('base64')}`
|
||||
) as typeof import('./proxy.js')
|
||||
|
||||
// Self-signed test certificate and key, valid only for the loopback target.
|
||||
const cert = `-----BEGIN CERTIFICATE-----
|
||||
MIIBmzCCAUGgAwIBAgIUAtOYg4xV7C9catkY/bwZc7XywMgwCgYIKoZIzj0EAwIw
|
||||
FDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTI2MTAyNDA4NTUxNloYDzIxMjYwOTMw
|
||||
MDg1NTE2WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjO
|
||||
PQMBBwNCAAS5HiqEES/b1Oe+NOKT+Cg3Gh89Rk0O7bSu0aDJpwdMzOxgOT2jTBmK
|
||||
2H5TUBnDWHjLktPtbmQQ4nMAb1kD4oKWo28wbTAdBgNVHQ4EFgQUu+xqnGfB/gu8
|
||||
DbNNHlVonV69Y64wHwYDVR0jBBgwFoAUu+xqnGfB/gu8DbNNHlVonV69Y64wDwYD
|
||||
VR0TAQH/BAUwAwEB/zAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwCgYIKoZI
|
||||
zj0EAwIDSAAwRQIgPy5jbT1ECH7LFy5r5JrV7xfsOkhwtbuwqVeidDBynFACIQCp
|
||||
sTyTz0llkGsTStOnpDBTzTUQzcDT8z6ZgqqxbY29nw==
|
||||
-----END CERTIFICATE-----`
|
||||
const key = `-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgXc/iw7bBMrZkWHsr
|
||||
tDvMDJ6qPLsspu6IH8lHZ3ccGvihRANCAAS5HiqEES/b1Oe+NOKT+Cg3Gh89Rk0O
|
||||
7bSu0aDJpwdMzOxgOT2jTBmK2H5TUBnDWHjLktPtbmQQ4nMAb1kD4oKW
|
||||
-----END PRIVATE KEY-----`
|
||||
const { key, cert, publicKey } = createTestTlsFixture()
|
||||
|
||||
test('runtime TLS fixture parses, verifies and covers the loopback target', () => {
|
||||
const certificate = new X509Certificate(cert)
|
||||
expect(certificate.verify(publicKey)).toBe(true)
|
||||
expect(certificate.ca).toBe(true)
|
||||
expect(certificate.subject).toBe('CN=localhost')
|
||||
expect(certificate.subjectAltName).toBe('DNS:localhost, IP Address:127.0.0.1')
|
||||
expect(certificate.checkHost('localhost')).toBe('localhost')
|
||||
expect(certificate.checkIP('127.0.0.1')).toBe('127.0.0.1')
|
||||
expect(Date.parse(certificate.validFrom)).toBeLessThan(Date.now())
|
||||
expect(Date.parse(certificate.validTo)).toBeGreaterThan(Date.now())
|
||||
})
|
||||
|
||||
describe('HTTPS_PROXY CONNECT transport', () => {
|
||||
let restoreEnv: () => void
|
||||
|
||||
Reference in New Issue
Block a user