feat(cert-manager): Hersteller-ZIP, PKCS#7, eingefügter Text, Analysieren und Aufteilen
- ZIP wird an den Anfangsbytes erkannt und mit Grenzen geöffnet (eine Ebene, Verhältnis, Gesamtgröße, verschlüsselte Einträge) - PKCS#7 als PEM und DER, auch für EC, über den ASN.1-Lauf - Eingefügter PEM-Text als eigener Eintrag im Reiter Dateien - Neue Reiter Analysieren und Aufteilen auf dem gemeinsamen Arbeitsbereich Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { X509Certificate } from 'node:crypto';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import AdmZip from 'adm-zip';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { certItemFromDer, detectBlob } from './cert-model';
|
||||
import type { CertItem } from './cert-types';
|
||||
@@ -129,3 +130,82 @@ describe('detectBlob: Zertifikate', () => {
|
||||
expect(item.id).toMatch(/^c-[0-9a-f]{16}$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectBlob: PKCS#7', () => {
|
||||
it.each([
|
||||
'rsa-chain.p7b',
|
||||
'rsa-chain.p7c',
|
||||
'ec-chain.p7b',
|
||||
])('%s liefert drei Zertifikate mit unveraenderten Fingerabdruecken', (name) => {
|
||||
const r = detectBlob(fx(name), ctx(name));
|
||||
expect(r.ignored).toEqual([]);
|
||||
expect(r.items).toHaveLength(3);
|
||||
const prefix = name.startsWith('rsa') ? 'rsa' : 'ec';
|
||||
const expected = ['leaf', 'inter', 'root'].map((p) => certs(`${prefix}-${p}.pem`)[0].id);
|
||||
expect(r.items.map((i) => i.id).sort()).toEqual([...expected].sort());
|
||||
expect((r.items[0] as CertItem).sources).toEqual([{ file: 0, path: name }]);
|
||||
});
|
||||
|
||||
it('PKCS#7 als DER ohne Endung wird erkannt (Inhalt, nicht Name)', () => {
|
||||
const r = detectBlob(fx('rsa-chain.p7c'), ctx('irgendwas.dat'));
|
||||
expect(r.items).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('abgeschnittenes PKCS#7 ergibt unbekannt, keinen Fehler', () => {
|
||||
const r = detectBlob(fx('rsa-chain.p7c').subarray(0, 400), ctx('halb.p7c'));
|
||||
expect(r.items).toEqual([]);
|
||||
expect(r.ignored).toEqual([{ file: 0, path: 'halb.p7c', reason: 'unknown' }]);
|
||||
});
|
||||
|
||||
it('PKCS#7-Block neben einem Zertifikat im selben Text', () => {
|
||||
const both = Buffer.concat([fx('rsa-chain.p7b'), Buffer.from('\n'), fx('ec-leaf.pem')]);
|
||||
const r = detectBlob(both, ctx('beides.pem'));
|
||||
expect(r.items).toHaveLength(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectBlob: ZIP', () => {
|
||||
function zip(entries: Record<string, Buffer>): Buffer {
|
||||
const z = new AdmZip();
|
||||
for (const [name, data] of Object.entries(entries)) z.addFile(name, data);
|
||||
return z.toBuffer();
|
||||
}
|
||||
|
||||
it('oeffnet ein ZIP an den Anfangsbytes und gibt jedem Teil den Pfad "zip/eintrag"', () => {
|
||||
const blob = zip({
|
||||
'ServerCertificate.crt': fx('ec-leaf.pem'),
|
||||
'Intermediate/CA.crt': fx('ec-inter.pem'),
|
||||
'chain.p7b': fx('rsa-chain.p7b'),
|
||||
'readme.txt': Buffer.from('Bitte lesen'),
|
||||
'__MACOSX/._x': Buffer.from('mac'),
|
||||
});
|
||||
const r = detectBlob(blob, { file: 2, path: 'bundle.dat', passwords: [] });
|
||||
const ec = r.items.find((i) => (i as CertItem).cn === 'ec.example.test');
|
||||
expect(ec?.sources).toEqual([{ file: 2, path: 'bundle.dat/ServerCertificate.crt' }]);
|
||||
expect(r.items).toHaveLength(5);
|
||||
expect(r.ignored).toEqual([{ file: 2, path: 'bundle.dat/readme.txt', reason: 'unknown' }]);
|
||||
});
|
||||
|
||||
it('ein ZIP im ZIP: nestedZip mit Pfad, der Rest wird gelesen', () => {
|
||||
const inner = zip({ 'x.pem': fx('rsa-root.pem') });
|
||||
const blob = zip({ 'a.pem': fx('rsa-leaf.pem'), 'inner.zip': inner });
|
||||
const r = detectBlob(blob, ctx('v.zip'));
|
||||
expect(r.items).toHaveLength(1);
|
||||
expect(r.ignored).toEqual([{ file: 0, path: 'v.zip/inner.zip', reason: 'nestedZip' }]);
|
||||
});
|
||||
|
||||
it('kaputtes ZIP und verschluesseltes ZIP werden gemeldet', () => {
|
||||
const broken = Buffer.concat([Buffer.from('PK\x03\x04', 'binary'), Buffer.alloc(100, 9)]);
|
||||
expect(detectBlob(broken, ctx('b.zip')).ignored).toEqual([
|
||||
{ file: 0, path: 'b.zip', reason: 'brokenZip' },
|
||||
]);
|
||||
expect(detectBlob(fx('encrypted-entry.zip'), ctx('e.zip')).ignored).toEqual([
|
||||
{ file: 0, path: 'e.zip/rsa-leaf.pem', reason: 'encryptedZip' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('ein ZIP ganz ohne lesbare Teile ergibt einen Eintrag unbekannt fuer das ZIP', () => {
|
||||
const r = detectBlob(new AdmZip().toBuffer(), ctx('leer.zip'));
|
||||
expect(r.ignored).toEqual([{ file: 0, path: 'leer.zip', reason: 'unknown' }]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user