Improve Windows OS metadata parsing

This commit is contained in:
Esdras Renan 2025-10-13 16:34:28 -03:00
parent cf31158a9e
commit 0cd477b8ef

View file

@ -211,28 +211,53 @@ function parseWindowsInstallDate(value: unknown): Date | null {
if (typeof value !== "string") return null if (typeof value !== "string") return null
const trimmed = value.trim() const trimmed = value.trim()
if (!trimmed) return null if (!trimmed) return null
const wmiMatch = trimmed.match(/Date\((\d+)\)/) const wmiMatch = trimmed.match(/Date\((\d+)\)/)
if (wmiMatch) { if (wmiMatch) {
const timestamp = Number(wmiMatch[1]) const timestamp = Number(wmiMatch[1])
if (Number.isFinite(timestamp)) { return Number.isFinite(timestamp) ? new Date(timestamp) : null
return new Date(timestamp)
}
} }
const isoValue = Date.parse(trimmed)
if (!Number.isNaN(isoValue)) return new Date(isoValue)
const digitsOnly = trimmed.replace(/[^0-9]/g, "") const digitsOnly = trimmed.replace(/[^0-9]/g, "")
if (digitsOnly.length >= 8 && digitsOnly.length <= 14) { if (digitsOnly.length >= 8) {
const year = Number(digitsOnly.slice(0, 4)) const yyyy = Number(digitsOnly.slice(0, 4))
const month = Number(digitsOnly.slice(4, 6)) - 1 const mm = Number(digitsOnly.slice(4, 6))
const day = Number(digitsOnly.slice(6, 8)) const dd = Number(digitsOnly.slice(6, 8))
const hours = Number(digitsOnly.slice(8, 10) || "0") const hh = Number(digitsOnly.slice(8, 10) || "0")
const minutes = Number(digitsOnly.slice(10, 12) || "0") const mi = Number(digitsOnly.slice(10, 12) || "0")
const seconds = Number(digitsOnly.slice(12, 14) || "0") const ss = Number(digitsOnly.slice(12, 14) || "0")
const parsed = new Date(Date.UTC(year, month, day, hours, minutes, seconds)) if (yyyy > 1900 && mm >= 1 && mm <= 12 && dd >= 1 && dd <= 31) {
if (!Number.isNaN(parsed.getTime())) { const parsed = new Date(Date.UTC(yyyy, mm - 1, dd, hh, mi, ss))
return parsed if (!Number.isNaN(parsed.getTime())) return parsed
}
const dd2 = Number(digitsOnly.slice(0, 2))
const mm2 = Number(digitsOnly.slice(2, 4))
const yyyy2 = Number(digitsOnly.slice(4, 8))
if (yyyy2 > 1900 && mm2 >= 1 && mm2 <= 12 && dd2 >= 1 && dd2 <= 31) {
const parsed = new Date(Date.UTC(yyyy2, mm2 - 1, dd2))
if (!Number.isNaN(parsed.getTime())) return parsed
} }
} }
const parsed = new Date(trimmed)
return Number.isNaN(parsed.getTime()) ? null : parsed const localMatch = trimmed.match(
/^(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})(?:\s+(\d{1,2}):(\d{2})(?::(\d{2}))?)?$/
)
if (localMatch) {
const dd = Number(localMatch[1])
const mm = Number(localMatch[2])
const yyyy = Number(localMatch[3])
const hh = Number(localMatch[4] || "0")
const mi = Number(localMatch[5] || "0")
const ss = Number(localMatch[6] || "0")
if (yyyy > 1900 && mm >= 1 && mm <= 12 && dd >= 1 && dd <= 31) {
const parsed = new Date(Date.UTC(yyyy, mm - 1, dd, hh, mi, ss))
if (!Number.isNaN(parsed.getTime())) return parsed
}
}
return null
} }
type WindowsOsInfo = { type WindowsOsInfo = {
@ -251,30 +276,51 @@ type WindowsOsInfo = {
function parseWindowsOsInfo(raw: unknown): WindowsOsInfo | null { function parseWindowsOsInfo(raw: unknown): WindowsOsInfo | null {
if (!raw) return null if (!raw) return null
const parseRecord = (value: Record<string, unknown>) => { const parseRecord = (value: Record<string, unknown>) => {
const productName = readString(value, "ProductName", "productName") const read = (k: string, ...alts: string[]) => readString(value, k, ...alts)
const editionId = readString(value, "EditionID", "editionId") const readNum = (...keys: string[]) => readNumber(value, ...keys)
const displayVersion = readString(value, "DisplayVersion", "displayVersion")
const releaseId = readString(value, "ReleaseId", "releaseId") const productName =
const currentBuild = readString(value, "CurrentBuild", "currentBuild") read("ProductName", "productName", "Name", "name") ??
const currentBuildNumber = readString(value, "CurrentBuildNumber", "currentBuildNumber") (read("Family") && read("Edition") ? `${read("Family")} ${read("Edition")}` : undefined)
const licenseStatus = readNumber(value, "LicenseStatus", "licenseStatus")
const isActivatedRaw = value["IsActivated"] const editionId =
read("EditionID", "editionId", "Edition", "edition", "SkuEdition", "skuEdition") ?? undefined
const displayVersion =
read("DisplayVersion", "displayVersion", "Version", "version") ??
read("ReleaseId", "releaseId")
const baseBuild =
read("CurrentBuildNumber", "currentBuildNumber", "CurrentBuild", "currentBuild", "OSBuild", "osBuild") ??
undefined
const ubr = read("UBR") ?? (readNum("UBR") ? String(readNum("UBR")) : undefined)
const currentBuildNumber = baseBuild ? (ubr && /^\d+$/.test(ubr) ? `${baseBuild}.${ubr}` : baseBuild) : undefined
const currentBuild = baseBuild
const licenseStatus = readNum("LicenseStatus", "licenseStatus")
const isActivatedRaw = value["IsActivated"] ?? value["isActivated"]
const isActivated = const isActivated =
typeof isActivatedRaw === "boolean" typeof isActivatedRaw === "boolean"
? isActivatedRaw ? isActivatedRaw
: typeof isActivatedRaw === "string" : typeof isActivatedRaw === "string"
? isActivatedRaw.toLowerCase() === "true" ? isActivatedRaw.toLowerCase() === "true"
: undefined : licenseStatus === 1
const installDate = const installDate =
parseWindowsInstallDate(value["InstallDate"]) ?? parseWindowsInstallDate(value["InstallDate"]) ??
parseWindowsInstallDate(value["InstallationDate"]) ?? parseWindowsInstallDate(value["InstallationDate"]) ??
parseWindowsInstallDate(value["InstallDateTime"]) parseWindowsInstallDate(value["InstallDateTime"]) ??
const experience = readString(value, "Experience", "experience", "UBR") parseWindowsInstallDate(value["InstalledOn"])
const experience =
read("Experience", "experience", "FeatureExperiencePack", "featureExperiencePack") ??
(currentBuildNumber ? `OS Build ${currentBuildNumber}` : undefined)
return { return {
productName, productName,
editionId, editionId,
displayVersion, displayVersion,
releaseId, releaseId: read("ReleaseId", "releaseId"),
currentBuild, currentBuild,
currentBuildNumber, currentBuildNumber,
licenseStatus, licenseStatus,