mirror of
https://github.com/fosrl/pangolin.git
synced 2026-01-28 22:00:51 +00:00
225 lines
8.4 KiB
TypeScript
225 lines
8.4 KiB
TypeScript
import { sha256 } from "@oslojs/crypto/sha2";
|
|
import { encodeHexLowerCase } from "@oslojs/encoding";
|
|
import { currentFingerprint, db, fingerprintSnapshots, Olm } from "@server/db";
|
|
import { calculateCutoffTimestamp } from "@server/lib/cleanupLogs";
|
|
import { desc, eq, lt } from "drizzle-orm";
|
|
|
|
function fingerprintSnapshotHash(fingerprint: any, postures: any): string {
|
|
const canonical = {
|
|
username: fingerprint.username ?? null,
|
|
hostname: fingerprint.hostname ?? null,
|
|
platform: fingerprint.platform ?? null,
|
|
osVersion: fingerprint.osVersion ?? null,
|
|
kernelVersion: fingerprint.kernelVersion ?? null,
|
|
arch: fingerprint.arch ?? null,
|
|
deviceModel: fingerprint.deviceModel ?? null,
|
|
serialNumber: fingerprint.serialNumber ?? null,
|
|
platformFingerprint: fingerprint.platformFingerprint ?? null,
|
|
|
|
biometricsEnabled: postures.biometricsEnabled ?? false,
|
|
diskEncrypted: postures.diskEncrypted ?? false,
|
|
firewallEnabled: postures.firewallEnabled ?? false,
|
|
autoUpdatesEnabled: postures.autoUpdatesEnabled ?? false,
|
|
tpmAvailable: postures.tpmAvailable ?? false,
|
|
|
|
windowsAntivirusEnabled: postures.windowsAntivirusEnabled ?? false,
|
|
|
|
macosSipEnabled: postures.macosSipEnabled ?? false,
|
|
macosGatekeeperEnabled: postures.macosGatekeeperEnabled ?? false,
|
|
macosFirewallStealthMode: postures.macosFirewallStealthMode ?? false,
|
|
|
|
linuxAppArmorEnabled: postures.linuxAppArmorEnabled ?? false,
|
|
linuxSELinuxEnabled: postures.linuxSELinuxEnabled ?? false
|
|
};
|
|
|
|
return encodeHexLowerCase(
|
|
sha256(new TextEncoder().encode(JSON.stringify(canonical)))
|
|
);
|
|
}
|
|
|
|
export async function handleFingerprintInsertion(
|
|
olm: Olm,
|
|
fingerprint: any,
|
|
postures: any
|
|
) {
|
|
if (
|
|
!olm?.olmId ||
|
|
!fingerprint ||
|
|
!postures ||
|
|
Object.keys(fingerprint).length === 0 ||
|
|
Object.keys(postures).length === 0
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const now = Math.floor(Date.now() / 1000);
|
|
const hash = fingerprintSnapshotHash(fingerprint, postures);
|
|
|
|
const [current] = await db
|
|
.select()
|
|
.from(currentFingerprint)
|
|
.where(eq(currentFingerprint.olmId, olm.olmId))
|
|
.limit(1);
|
|
|
|
if (!current) {
|
|
const [inserted] = await db
|
|
.insert(currentFingerprint)
|
|
.values({
|
|
olmId: olm.olmId,
|
|
firstSeen: now,
|
|
lastSeen: now,
|
|
lastCollectedAt: now,
|
|
|
|
// fingerprint
|
|
username: fingerprint.username,
|
|
hostname: fingerprint.hostname,
|
|
platform: fingerprint.platform,
|
|
osVersion: fingerprint.osVersion,
|
|
kernelVersion: fingerprint.kernelVersion,
|
|
arch: fingerprint.arch,
|
|
deviceModel: fingerprint.deviceModel,
|
|
serialNumber: fingerprint.serialNumber,
|
|
platformFingerprint: fingerprint.platformFingerprint,
|
|
|
|
biometricsEnabled: postures.biometricsEnabled,
|
|
diskEncrypted: postures.diskEncrypted,
|
|
firewallEnabled: postures.firewallEnabled,
|
|
autoUpdatesEnabled: postures.autoUpdatesEnabled,
|
|
tpmAvailable: postures.tpmAvailable,
|
|
|
|
windowsAntivirusEnabled: postures.windowsAntivirusEnabled,
|
|
|
|
macosSipEnabled: postures.macosSipEnabled,
|
|
macosGatekeeperEnabled: postures.macosGatekeeperEnabled,
|
|
macosFirewallStealthMode: postures.macosFirewallStealthMode,
|
|
|
|
linuxAppArmorEnabled: postures.linuxAppArmorEnabled,
|
|
linuxSELinuxEnabled: postures.linuxSELinuxEnabled
|
|
})
|
|
.returning();
|
|
|
|
await db.insert(fingerprintSnapshots).values({
|
|
fingerprintId: inserted.fingerprintId,
|
|
|
|
username: fingerprint.username,
|
|
hostname: fingerprint.hostname,
|
|
platform: fingerprint.platform,
|
|
osVersion: fingerprint.osVersion,
|
|
kernelVersion: fingerprint.kernelVersion,
|
|
arch: fingerprint.arch,
|
|
deviceModel: fingerprint.deviceModel,
|
|
serialNumber: fingerprint.serialNumber,
|
|
platformFingerprint: fingerprint.platformFingerprint,
|
|
|
|
biometricsEnabled: postures.biometricsEnabled,
|
|
diskEncrypted: postures.diskEncrypted,
|
|
firewallEnabled: postures.firewallEnabled,
|
|
autoUpdatesEnabled: postures.autoUpdatesEnabled,
|
|
tpmAvailable: postures.tpmAvailable,
|
|
|
|
windowsAntivirusEnabled: postures.windowsAntivirusEnabled,
|
|
|
|
macosSipEnabled: postures.macosSipEnabled,
|
|
macosGatekeeperEnabled: postures.macosGatekeeperEnabled,
|
|
macosFirewallStealthMode: postures.macosFirewallStealthMode,
|
|
|
|
linuxAppArmorEnabled: postures.linuxAppArmorEnabled,
|
|
linuxSELinuxEnabled: postures.linuxSELinuxEnabled,
|
|
|
|
hash,
|
|
collectedAt: now
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
const [latestSnapshot] = await db
|
|
.select({ hash: fingerprintSnapshots.hash })
|
|
.from(fingerprintSnapshots)
|
|
.where(eq(fingerprintSnapshots.fingerprintId, current.fingerprintId))
|
|
.orderBy(desc(fingerprintSnapshots.collectedAt))
|
|
.limit(1);
|
|
|
|
const changed = !latestSnapshot || latestSnapshot.hash !== hash;
|
|
|
|
if (changed) {
|
|
await db.insert(fingerprintSnapshots).values({
|
|
fingerprintId: current.fingerprintId,
|
|
|
|
username: fingerprint.username,
|
|
hostname: fingerprint.hostname,
|
|
platform: fingerprint.platform,
|
|
osVersion: fingerprint.osVersion,
|
|
kernelVersion: fingerprint.kernelVersion,
|
|
arch: fingerprint.arch,
|
|
deviceModel: fingerprint.deviceModel,
|
|
serialNumber: fingerprint.serialNumber,
|
|
platformFingerprint: fingerprint.platformFingerprint,
|
|
|
|
biometricsEnabled: postures.biometricsEnabled,
|
|
diskEncrypted: postures.diskEncrypted,
|
|
firewallEnabled: postures.firewallEnabled,
|
|
autoUpdatesEnabled: postures.autoUpdatesEnabled,
|
|
tpmAvailable: postures.tpmAvailable,
|
|
|
|
windowsAntivirusEnabled: postures.windowsAntivirusEnabled,
|
|
|
|
macosSipEnabled: postures.macosSipEnabled,
|
|
macosGatekeeperEnabled: postures.macosGatekeeperEnabled,
|
|
macosFirewallStealthMode: postures.macosFirewallStealthMode,
|
|
|
|
linuxAppArmorEnabled: postures.linuxAppArmorEnabled,
|
|
linuxSELinuxEnabled: postures.linuxSELinuxEnabled,
|
|
|
|
hash,
|
|
collectedAt: now
|
|
});
|
|
|
|
await db
|
|
.update(currentFingerprint)
|
|
.set({
|
|
lastSeen: now,
|
|
lastCollectedAt: now,
|
|
|
|
username: fingerprint.username,
|
|
hostname: fingerprint.hostname,
|
|
platform: fingerprint.platform,
|
|
osVersion: fingerprint.osVersion,
|
|
kernelVersion: fingerprint.kernelVersion,
|
|
arch: fingerprint.arch,
|
|
deviceModel: fingerprint.deviceModel,
|
|
serialNumber: fingerprint.serialNumber,
|
|
platformFingerprint: fingerprint.platformFingerprint,
|
|
|
|
biometricsEnabled: postures.biometricsEnabled,
|
|
diskEncrypted: postures.diskEncrypted,
|
|
firewallEnabled: postures.firewallEnabled,
|
|
autoUpdatesEnabled: postures.autoUpdatesEnabled,
|
|
tpmAvailable: postures.tpmAvailable,
|
|
|
|
windowsAntivirusEnabled: postures.windowsAntivirusEnabled,
|
|
|
|
macosSipEnabled: postures.macosSipEnabled,
|
|
macosGatekeeperEnabled: postures.macosGatekeeperEnabled,
|
|
macosFirewallStealthMode: postures.macosFirewallStealthMode,
|
|
|
|
linuxAppArmorEnabled: postures.linuxAppArmorEnabled,
|
|
linuxSELinuxEnabled: postures.linuxSELinuxEnabled
|
|
})
|
|
.where(eq(currentFingerprint.fingerprintId, current.fingerprintId));
|
|
} else {
|
|
await db
|
|
.update(currentFingerprint)
|
|
.set({ lastSeen: now })
|
|
.where(eq(currentFingerprint.fingerprintId, current.fingerprintId));
|
|
}
|
|
}
|
|
|
|
export async function cleanUpOldFingerprintSnapshots(retentionDays: number) {
|
|
const cutoff = calculateCutoffTimestamp(retentionDays);
|
|
|
|
await db
|
|
.delete(fingerprintSnapshots)
|
|
.where(lt(fingerprintSnapshots.collectedAt, cutoff));
|
|
}
|