chore(test): подготовить проверку реального продления сертификата

This commit is contained in:
Denozordec committed 2026-10-14 21:31:17 +07:00
1 parent 3d23ae4be4
commit 37a9c828d3
10 files changed
+207 -9

No files matched your search

@@ -0,0 +1,71 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import tls from 'node:tls';
import { X509Certificate } from 'node:crypto';
import { pathToFileURL } from 'node:url';
const fingerprint = value => {
assert.equal(typeof value, 'string', 'Fingerprint is required');
const normalized = value.replaceAll(':', '').toLowerCase();
assert(/^[a-f0-9]{64}$/.test(normalized), 'Expected SHA-256 fingerprint');
return normalized;
};
export function compareRenewal(before, after) {
for (const sample of [before, after]) {
assert(/^mmt253-[a-f0-9]{8}\.pngs\.top$/.test(sample.fqdn), 'Only the prepared exact test domain is permitted');
assert.equal(sample.remoteAddress, '104.165.244.7');
assert.equal(sample.authorized, true);
assert.equal(sample.hostnameMatched, true);
assert.equal(sample.nativePrivateKey, true);
assert.equal(sample.nativeName, sample.certName);
assert.equal(sample.certName, sample.fqdn.replace('.pngs.top', ''));
assert.equal(fingerprint(sample.nativeFingerprint256), fingerprint(sample.fingerprint256), 'Native leaf must match served TLS');
assert(Number.isFinite(Date.parse(sample.validUntil)), 'Expiry is required');
assert(Number.isFinite(Date.parse(sample.sampledAt)), 'Sample time is required');
assert(Date.parse(sample.validUntil) > Date.parse(sample.sampledAt), 'Certificate is expired');
}
assert.equal(after.fqdn, before.fqdn);
assert.equal(after.certName, before.certName);
assert.notEqual(fingerprint(after.fingerprint256), fingerprint(before.fingerprint256), 'Old certificate is still being served');
assert(Date.parse(after.sampledAt) >= Date.parse(before.sampledAt));
assert(Date.parse(after.validUntil) > Date.parse(after.sampledAt), 'New certificate is expired');
// A second issuance on the same day need not extend expiry by a full day.
return { fingerprintChanged: true, nativeAndTlsMatch: true, trustedHostnameMatch: true };
}
async function sample(fixture) {
assert(/^mmt253-[a-f0-9]{8}\.pngs\.top$/.test(fixture.fqdn));
assert.equal(fixture.certName, fixture.fqdn.replace('.pngs.top', ''));
assert.equal(fixture.nativePrivateKey, true);
fingerprint(fixture.nativeFingerprint256);
return await new Promise((resolve, reject) => {
const socket = tls.connect({ host: '104.165.244.7', port: 443, servername: fixture.fqdn, rejectUnauthorized: true });
socket.setTimeout(15000, () => socket.destroy(new Error('TLS acceptance timeout')));
socket.once('error', reject);
socket.once('secureConnect', () => {
try {
assert.equal(socket.authorized, true);
const cert = new X509Certificate(socket.getPeerCertificate().raw);
assert(cert.checkHost(fixture.fqdn), 'Hostname mismatch');
assert(Date.parse(cert.validTo) > Date.now(), 'Certificate is expired');
assert.equal(fingerprint(cert.fingerprint256), fingerprint(fixture.nativeFingerprint256), 'Native leaf does not match TLS');
resolve({ sampledAt: new Date().toISOString(), fqdn: fixture.fqdn, certName: fixture.certName,
remoteAddress: socket.remoteAddress, authorized: socket.authorized, hostnameMatched: true,
protocol: socket.getProtocol(), fingerprint256: cert.fingerprint256,
validFrom: cert.validFrom, validUntil: cert.validTo,
nativeName: fixture.certName, nativePrivateKey: true, nativeFingerprint256: fixture.nativeFingerprint256 });
} catch (error) { reject(error); } finally { socket.end(); }
});
});
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
const [mode, input, output] = process.argv.slice(2);
assert(input && output && ['sample', 'compare'].includes(mode), 'Usage: sample native-summary.json output.json | compare samples.json output.json');
assert(!fs.existsSync(output), 'Never overwrite acceptance evidence');
const data = JSON.parse(fs.readFileSync(input, 'utf8'));
const result = mode === 'sample' ? await sample(data) : compareRenewal(data.before, data.after);
fs.writeFileSync(output, JSON.stringify(result, null, 2) + '\n', { flag: 'wx' });
console.log(mode === 'sample' ? 'Trusted TLS/native fingerprint sample saved' : 'Renewal fingerprint comparison passed');
}
@@ -0,0 +1,13 @@
import assert from 'node:assert/strict';
import { compareRenewal } from './acme-renew-tls.mjs';
const before = { fqdn: 'mmt253-a1b2c3d4.pngs.top', certName: 'mmt253-a1b2c3d4', nativeName: 'mmt253-a1b2c3d4',
remoteAddress: '104.165.244.7', authorized: true, hostnameMatched: true, nativePrivateKey: true,
fingerprint256: 'a'.repeat(64), nativeFingerprint256: 'a'.repeat(64), sampledAt: '2026-10-14T12:00:00Z', validUntil: '2027-01-12T12:00:00Z' };
const after = { ...before, fingerprint256: 'b'.repeat(64), nativeFingerprint256: 'b'.repeat(64), sampledAt: '2026-10-14T12:01:00Z' };
assert.equal(compareRenewal(before, after).fingerprintChanged, true);
for (const patch of [{ fingerprint256: before.fingerprint256, nativeFingerprint256: before.nativeFingerprint256 },
{ nativeFingerprint256: before.nativeFingerprint256 }, { authorized: false }, { hostnameMatched: false },
{ nativePrivateKey: false }, { nativeName: 'another' }, { fqdn: 'other.pngs.top' },
{ remoteAddress: '194.226.121.150' }, { validUntil: 'bad' }, { validUntil: '2026-10-13T12:00:00Z' },
{ sampledAt: '2026-10-14T11:00:00Z' }]) assert.throws(() => compareRenewal(before, { ...after, ...patch }));
console.log('Renewal acceptance comparison: changed leaf passes; 11 false-success cases rejected. No network or CA calls.');