File size: 3,302 Bytes
2b64d42 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import http2 from 'http2';
import { redactProxyUrl, buildLanguageServerEnv, probeLanguageServerPort } from '../src/langserver.js';
async function withHttp2Server(handler, fn) {
const server = http2.createServer();
server.on('stream', handler);
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
const port = server.address().port;
try {
return await fn(port);
} finally {
await new Promise(resolve => server.close(resolve));
}
}
describe('redactProxyUrl', () => {
it('redacts credentials from proxy URLs', () => {
assert.equal(redactProxyUrl('http://user:secret@example.com:8080'), 'example.com:8080 (auth=true)');
});
it('shows host and port for unauthenticated proxies', () => {
assert.equal(redactProxyUrl({ host: 'proxy.example.com', port: 1080 }), 'proxy.example.com:1080');
});
});
describe('buildLanguageServerEnv', () => {
it('keeps allowlisted vars and drops unrelated process env', () => {
const env = buildLanguageServerEnv({
HOME: '/home/dev',
PATH: '/usr/bin',
LANG: 'en_US.UTF-8',
AWS_SECRET_ACCESS_KEY: 'leak-me',
GITHUB_TOKEN: 'leak-me-too',
});
assert.equal(env.HOME, '/home/dev');
assert.equal(env.PATH, '/usr/bin');
assert.equal(env.LANG, 'en_US.UTF-8');
assert.ok(!('AWS_SECRET_ACCESS_KEY' in env));
assert.ok(!('GITHUB_TOKEN' in env));
});
it('applies proxy override across both HTTP and HTTPS forms', () => {
const env = buildLanguageServerEnv(
{ HOME: '/home/dev', HTTP_PROXY: 'http://stale:8080' },
{ proxyUrl: 'http://fresh.example.com:9999' }
);
assert.equal(env.HTTP_PROXY, 'http://fresh.example.com:9999');
assert.equal(env.HTTPS_PROXY, 'http://fresh.example.com:9999');
assert.equal(env.http_proxy, 'http://fresh.example.com:9999');
assert.equal(env.https_proxy, 'http://fresh.example.com:9999');
});
it('falls back to /root for HOME only when source has none', () => {
const env = buildLanguageServerEnv({ PATH: '/usr/bin' });
assert.equal(env.HOME, '/root');
});
it('preserves SSL trust env vars so hardened hosts still verify upstream', () => {
const env = buildLanguageServerEnv({
HOME: '/home/dev',
SSL_CERT_FILE: '/etc/ssl/certs/ca-bundle.crt',
NODE_EXTRA_CA_CERTS: '/etc/ssl/extra.pem',
});
assert.equal(env.SSL_CERT_FILE, '/etc/ssl/certs/ca-bundle.crt');
assert.equal(env.NODE_EXTRA_CA_CERTS, '/etc/ssl/extra.pem');
});
});
describe('probeLanguageServerPort', () => {
it('accepts a port only when the response has a gRPC-like LS signature', async () => {
await withHttp2Server((stream) => {
stream.respond({ ':status': 405, 'content-type': 'application/grpc', 'grpc-status': '12' });
stream.end();
}, async (port) => {
assert.equal(await probeLanguageServerPort(port), true);
});
});
it('rejects unrelated HTTP/2 services on the default LS port shape', async () => {
await withHttp2Server((stream) => {
stream.respond({ ':status': 200, 'content-type': 'text/plain', server: 'not-ls' });
stream.end('ok');
}, async (port) => {
assert.equal(await probeLanguageServerPort(port), false);
});
});
});
|