Server-Bibliothek
vor 18 Std. 1f44739ba685cd77dc804adcc58fbf584b21e52b
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
import { execFile } from 'child_process';
import { promisify } from 'util';
 
const execFileAsync = promisify(execFile);
 
export interface Signaturbefund {
  /** Windows-Status: Valid, NotSigned, UnknownError, HashMismatch … bzw. „nicht geprüft". */
  status: string;
  /** Anzeigename des Signaturgebers (leer, wenn unsigniert). */
  herausgeber: string;
}
 
const NICHT_GEPRUEFT: Signaturbefund = { status: 'nicht geprüft', herausgeber: '' };
 
/**
 * Prüft die Windows-Signatur (Authenticode) einer Datei.
 *
 * Bewusst nur erfassend, nicht blockierend: Manche Programme werden legitim
 * unsigniert ausgeliefert (z. B. 7-Zip). Erst ein hinterlegter Erwartungswert
 * macht daraus eine Zusicherung — siehe `weichtHerausgeberAb`.
 *
 * Der Dateipfad geht über eine Umgebungsvariable an PowerShell, damit
 * Anführungszeichen im Pfad nichts zerbrechen können.
 */
export async function pruefeSignatur(dateipfad: string): Promise<Signaturbefund> {
  if (process.platform !== 'win32') {
    return NICHT_GEPRUEFT;
  }
  const skript = [
    '$befund = Get-AuthenticodeSignature -LiteralPath $env:ZU_PRUEFENDE_DATEI;',
    '$name = if ($befund.SignerCertificate) { $befund.SignerCertificate.Subject } else { "" };',
    'ConvertTo-Json -Compress @{ status = $befund.Status.ToString(); herausgeber = $name }',
  ].join(' ');
  try {
    const { stdout } = await execFileAsync('powershell', ['-NoProfile', '-NonInteractive', '-Command', skript], {
      timeout: 60000,
      windowsHide: true,
      env: { ...process.env, ZU_PRUEFENDE_DATEI: dateipfad },
    });
    const roh = JSON.parse(stdout) as { status?: string; herausgeber?: string };
    return {
      status: roh.status ?? 'nicht geprüft',
      herausgeber: lesbarerHerausgeber(roh.herausgeber ?? ''),
    };
  } catch {
    return NICHT_GEPRUEFT; // Prüfung selbst fehlgeschlagen — kein Grund, den Abgleich zu verwerfen
  }
}
 
/** Zieht aus dem Zertifikats-Betreff den sprechenden Teil (CN bzw. erstes Feld). */
function lesbarerHerausgeber(betreff: string): string {
  if (!betreff) {
    return '';
  }
  const felder = betreff.split(',').map((feld) => feld.trim());
  const gemeinsamerName = felder.find((feld) => feld.startsWith('CN='));
  return (gemeinsamerName ?? felder[0]).replace(/^CN=/, '').replace(/^"|"$/g, '');
}
 
/** Weicht der gefundene Herausgeber von der Erwartung ab? Ohne Erwartung nie. */
export function weichtHerausgeberAb(erwartet: string, gefunden: string): boolean {
  if (!erwartet.trim()) {
    return false;
  }
  return !gefunden.toLowerCase().includes(erwartet.trim().toLowerCase());
}