Files
reverse-engineering-anything/src/domain/artifactFormatMetadata.ts
T

1198 lines
40 KiB
TypeScript

import {
ntfsFileReferenceSchema,
ntfsFileNameSchema,
ntfsAttributeSchema,
ntfsAttributeIssueSchema,
ntfsAttributeListResolutionSchema,
} from "./windows/ntfsMftAttributes.js";
import { z } from "zod";
import { MFT_INLINE_HEADER_LIMIT } from "./windows/ntfsMft.js";
import { jsonRecordSchema } from "./jsonRecord.js";
import { jsonValueSchema } from "./jsonValue.js";
const offset = z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
export const uint32 = z.number().int().min(0).max(0xffff_ffff);
/** Parsed source fields owned by the PCK adapter, independent of content hashes. */
export const godotPckMetadataSchema = z.strictObject({
kind: z.literal("godot-pck"),
format_version: uint32,
engine_major: uint32,
engine_minor: uint32,
engine_patch: uint32,
pack_start: offset,
pack_end: offset,
embedded: z.boolean(),
pack_flags: uint32,
file_base: offset,
directory_start: offset,
declared_file_count: uint32,
directory_status: z.enum(["available", "encrypted", "sparse-bundle"]),
});
export type GodotPckMetadata = z.infer<typeof godotPckMetadataSchema>;
export const godotPckEntryMetadataSchema = z.strictObject({
kind: z.literal("godot-pck-entry"),
index: uint32,
source_path: z.string(),
declared_offset: offset,
flags: uint32,
md5_status: z.enum(["declared", "all-zero"]),
});
/** Parsed source fields owned by the UnityFS adapter, independent of member bytes. */
export const unityFsMetadataSchema = z.strictObject({
kind: z.literal("unityfs"),
signature: z.string(),
format_version: uint32,
player_version: z.string(),
engine_version: z.string(),
declared_size: offset,
flags: uint32,
blocks_info_compressed_size: uint32,
blocks_info_uncompressed_size: uint32,
blocks_info_at_end: z.boolean(),
data_start: offset.nullable(),
block_count: uint32.nullable(),
/** The 16-byte producer hash of the uncompressed block stream, as declared. */
data_hash: z.string().nullable(),
directory_status: z.enum([
"available",
"encrypted",
"unsupported-compression",
"unsupported-layout",
]),
});
export type UnityFsMetadata = z.infer<typeof unityFsMetadataSchema>;
export const unityFsEntryMetadataSchema = z.strictObject({
kind: z.literal("unityfs-entry"),
index: uint32,
source_path: z.string(),
declared_offset: offset,
flags: uint32,
});
/** Parsed source fields owned by the wxapkg adapter, independent of member bytes. */
export const wxapkgMetadataSchema = z.strictObject({
kind: z.literal("wxapkg"),
info: uint32.nullable(),
index_length: uint32.nullable(),
data_length: uint32.nullable(),
file_count: uint32.nullable(),
directory_status: z.enum(["available", "encrypted"]),
});
export type WxapkgMetadata = z.infer<typeof wxapkgMetadataSchema>;
export const wxapkgEntryMetadataSchema = z.strictObject({
kind: z.literal("wxapkg-entry"),
index: uint32,
source_path: z.string(),
declared_offset: offset,
});
/** Source cookie fields and derived directory counts without Python execution. */
export const pyInstallerMetadataSchema = z.strictObject({
kind: z.literal("pyinstaller"),
layout_version: z.enum(["2.0", "2.1"]),
archive_start: offset,
archive_size: offset,
cookie_offset: offset,
toc_offset: offset,
toc_size: offset,
entry_count: offset,
runtime_option_count: offset,
external_dependency_count: offset,
python_version_raw: uint32,
python_major: uint32.nullable(),
python_minor: uint32.nullable(),
python_library: z.string().nullable(),
encryption_marker_present: z.boolean(),
});
export type PyInstallerMetadata = z.infer<typeof pyInstallerMetadataSchema>;
/** Primitive facts preserve source spelling and keep metadata copies independent. */
export const pyInstallerEntryMetadataSchema = z.strictObject({
kind: z.literal("pyinstaller-entry"),
container: z.enum(["carchive", "pyz"]),
container_name: z.string().nullable(),
index: offset,
source_name: z.string(),
type_code: z.union([z.string().length(1), uint32]),
relative_offset: offset,
directory_record_offset: offset.nullable(),
directory_record_size: offset.nullable(),
compression_flag: z.union([z.literal(0), z.literal(1)]),
bytecode_header: z.enum(["not-bytecode", "unknown", "derived"]),
bytecode_magic: z
.string()
.regex(/^[0-9a-f]{8}$/u)
.nullable(),
pyz_status: z.enum(["not-pyz", "decoded", "opaque"]),
pyz_sha256: z
.string()
.regex(/^[0-9a-f]{64}$/u)
.nullable(),
pyz_python_magic: z
.string()
.regex(/^[0-9a-f]{8}$/u)
.nullable(),
pyz_python_version: z.string().nullable(),
pyz_entry_count: offset.nullable(),
encryption_source: z.literal("carchive-marker").nullable(),
dependency_status: z
.enum(["unresolved", "invalid-reference"])
.exactOptional(),
dependency_archive: z.string().nullable().exactOptional(),
dependency_member: z.string().nullable().exactOptional(),
});
export type PyInstallerEntryMetadata = z.infer<
typeof pyInstallerEntryMetadataSchema
>;
/** Ordinary .NET bundle manifest facts, not proof of managed assembly validity. */
export const dotnetBundleEntryMetadataSchema = z.strictObject({
kind: z.literal("dotnet-bundle-entry"),
major_version: uint32,
minor_version: uint32,
file_type: z.number().int().min(0).max(255),
});
/**
* Node.js single-executable blob facts. Lengths follow the Node 24 64-bit
* little-endian `size_t` reader. The fuse sentinel is a separate carrier scan.
*/
export const nodeSeaMetadataSchema = z.strictObject({
kind: z.literal("node-sea"),
magic: z.literal(0x143da20),
flags: uint32,
disable_experimental_sea_warning: z.boolean(),
use_snapshot: z.boolean(),
use_code_cache: z.boolean(),
include_assets: z.boolean(),
include_exec_argv: z.boolean(),
exec_argv_extension: z.enum(["none", "env", "cli"]),
exec_argv: z.array(z.string()),
code_path: z.string(),
carrier: z.enum(["mach-o", "elf", "pe"]),
blob_offset: offset,
blob_length: offset,
fuse_state: z.enum(["set", "unset", "absent", "conflicting"]),
fuse_sentinel: z.string().nullable(),
fuse_contradiction: z.string().nullable(),
});
export type NodeSeaMetadata = z.infer<typeof nodeSeaMetadataSchema>;
/** One injected script, snapshot, code cache, or asset. Bytes stay opaque. */
export const nodeSeaEntryMetadataSchema = z.strictObject({
kind: z.literal("node-sea-entry"),
role: z.enum(["main-script", "snapshot", "code-cache", "asset"]),
source_path: z.string(),
index: uint32,
});
/**
* UF2 facts derived from every 512-byte block. Regions hold only valid
* main-flash payloads; block numbering is grouped by familyID and block count.
*/
export const uf2MetadataSchema = z.strictObject({
kind: z.literal("uf2"),
block_count: offset,
trailing_bytes: offset,
invalid_block_count: offset,
flash_block_count: offset,
not_main_flash_block_count: offset,
file_container_block_count: offset,
md5_block_count: offset,
extension_tag_block_count: offset,
family_count: offset,
/** The registry revision REA's region `family_name` values come from. */
family_name_registry: z.string(),
numbering_group_count: offset,
missing_block_count: offset,
ambiguous_numbering_group_count: offset,
repeated_block_count: offset,
out_of_order_block_count: offset,
block_number_out_of_range_count: offset,
region_count: offset,
data_bytes: offset,
duplicate_bytes: offset,
});
export type Uf2Metadata = z.infer<typeof uf2MetadataSchema>;
/** One family's address-contiguous region; file blocks are zero-based positions. */
export const uf2RegionMetadataSchema = z.strictObject({
kind: z.literal("uf2-region"),
family_id: uint32.nullable(),
/** REA's label for `family_id`, from `family_name_registry`; not file content. */
family_name: z.string().nullable(),
start_address: uint32,
end_address: uint32,
length: offset,
block_count: offset,
first_file_block: offset,
last_file_block: offset,
});
/** DfuSe prefix and DFU suffix facts; both CRCs use the suffix's stored form. */
export const dfuSeMetadataSchema = z.strictObject({
kind: z.literal("dfuse"),
image_size: uint32,
target_count: uint32,
element_count: offset,
empty_element_count: offset,
overlapping_element_count: offset,
data_bytes: offset,
device_version: uint32,
product_id: uint32,
vendor_id: uint32,
suffix_crc: uint32,
computed_crc: uint32,
});
export type DfuSeMetadata = z.infer<typeof dfuSeMetadataSchema>;
/** One DfuSe element with its target's alternate setting and name. */
export const dfuSeElementMetadataSchema = z.strictObject({
kind: z.literal("dfuse-element"),
target_index: uint32,
alternate_setting: uint32,
target_name: z.string().nullable(),
element_index: uint32,
start_address: uint32,
end_address: uint32,
length: offset,
});
/**
* Intel HEX or Motorola S-record facts derived from every record. Start
* addresses are declared values, not proven entry points.
*/
export const hexRecordsMetadataSchema = z.strictObject({
kind: z.literal("hex-records"),
encoding: z.enum(["intel-hex", "srec"]),
variant: z
.enum(["I8HEX", "I16HEX", "I32HEX", "S19", "S28", "S37", "mixed"])
.nullable(),
line_count: offset,
record_count: offset,
data_record_count: offset,
data_bytes: offset,
duplicate_bytes: offset,
region_count: offset,
lowest_address: uint32.nullable(),
highest_address: uint32.nullable(),
termination: z.enum(["present", "absent"]),
start_address: uint32.nullable(),
start_address_record: z.enum(["05", "S7", "S8", "S9"]).nullable(),
start_segment_cs: uint32.nullable(),
start_segment_ip: uint32.nullable(),
header_hex: z
.string()
.regex(/^(?:[0-9a-f]{2}){0,252}$/u)
.nullable(),
header_text: z.string().nullable(),
declared_data_record_count: uint32.nullable(),
});
export type HexRecordsMetadata = z.infer<typeof hexRecordsMetadataSchema>;
/** One address-contiguous region; lines span its contributing data records. */
export const hexRecordRegionMetadataSchema = z.strictObject({
kind: z.literal("hex-record-region"),
start_address: uint32,
end_address: uint32,
length: offset,
record_count: offset,
first_line: offset,
last_line: offset,
});
const ntfsRecordHeaderSchema = z.strictObject({
index: offset,
fileOffset: offset,
sequenceNumber: offset,
hardLinkCount: offset,
flags: z.strictObject({
raw: offset,
names: z.array(z.string()),
unknownMask: offset,
}),
inUse: z.boolean(),
directory: z.boolean(),
usedBytes: offset,
totalBytes: offset,
extentValid: z.boolean(),
baseRecordReference: ntfsFileReferenceSchema.nullable(),
declaredRecordNumber: offset.nullable(),
recordNumberMatchesSlot: z.boolean().nullable(),
logFileSequenceNumber: z.string().regex(/^(?:0|[1-9][0-9]*)$/u),
firstAttributeOffset: offset,
nextAttributeId: offset,
fileNames: z.array(ntfsFileNameSchema).exactOptional(),
attributes: z.array(ntfsAttributeSchema).exactOptional(),
attributeCoverage: z.enum(["complete", "incomplete"]).exactOptional(),
attributeIssue: ntfsAttributeIssueSchema.nullable().exactOptional(),
fileNameCoverage: z.enum(["complete", "incomplete"]).exactOptional(),
attributeListResolution: ntfsAttributeListResolutionSchema.exactOptional(),
fixups: z.strictObject({
sequenceNumber: offset,
entries: offset,
verifiedSectors: offset,
coveredSectors: offset,
mismatchSector: offset.nullable(),
malformed: z.boolean(),
}),
});
/** NTFS $MFT record-header and resident file-name coverage facts. */
export const ntfsMftMetadataSchema = z.strictObject({
kind: z.literal("ntfs-mft"),
record_bytes: offset,
record_count: offset,
in_use_record_count: offset,
directory_record_count: offset,
extension_record_count: offset,
fixup_mismatch_count: offset,
fixup_unverified_record_count: offset,
truncated_tail_bytes: offset,
record_headers: z.array(ntfsRecordHeaderSchema).max(MFT_INLINE_HEADER_LIMIT),
omitted_record_headers: offset,
record_header_start_index: offset.exactOptional(),
next_record_header_index: offset.nullable().exactOptional(),
file_name_count: offset.exactOptional(),
file_name_incomplete_record_count: offset.exactOptional(),
attribute_incomplete_record_count: offset.exactOptional(),
});
/**
* U-Boot FIT root facts. Configuration references are declared names; a
* reference that names no image is reported as a limitation, not removed.
*/
export const fitMetadataSchema = z.strictObject({
kind: z.literal("fit"),
fdt_version: uint32,
fdt_totalsize: offset,
external_data_base: offset,
description: z.string().nullable(),
timestamp: uint32.nullable(),
address_cells: uint32.nullable(),
default_configuration: z.string().nullable(),
configurations: z.array(
z.strictObject({
name: z.string(),
description: z.string().nullable(),
kernel: z.array(z.string()),
firmware: z.array(z.string()),
fdt: z.array(z.string()),
ramdisk: z.array(z.string()),
loadables: z.array(z.string()),
signature_count: uint32,
signatures_unreadable: uint32,
unresolved_reference_count: uint32,
signature_algorithms: z.array(z.string()),
}),
),
});
export type FitMetadata = z.infer<typeof fitMetadataSchema>;
/**
* One /images node's facts. Null fields are absent unless unreadable_fields
* identifies an interpretation failure. Load and entry are declarations;
* stored bytes may be compressed or ciphered.
*/
export const fitImageMetadataSchema = z.strictObject({
kind: z.literal("fit-image"),
index: uint32,
name: z.string(),
description: z.string().nullable(),
type: z.string().nullable(),
arch: z.string().nullable(),
os: z.string().nullable(),
compression: z.string().nullable(),
load: z.string().nullable(),
entry: z.string().nullable(),
data_source: z.enum(["embedded", "data-offset", "data-position"]),
declared_offset: uint32.nullable(),
image_data: z.string().nullable(),
/** Presence is independent of whether the declared algorithm is readable. */
cipher_present: z.boolean(),
cipher_algo: z.string().nullable(),
unreadable_fields: z.array(
z.enum([
"description",
"type",
"arch",
"os",
"compression",
"load",
"entry",
"cipher_algo",
]),
),
hash_algorithms: z.array(z.string()),
hashes_verified: uint32,
hashes_unverified: uint32,
signature_count: uint32,
signatures_unreadable: uint32,
signature_algorithms: z.array(z.string()),
});
export type FitImageMetadata = z.infer<typeof fitImageMetadataSchema>;
export const uint8 = z.number().int().min(0).max(0xff);
const uint16 = z.number().int().min(0).max(0xffff);
const sha256Hex = z.string().regex(/^[0-9a-f]{64}$/u);
const byteRange = z.strictObject({ offset, length: offset });
/** Derived from `chip_id` through the cited table. */
const espChipFactsSchema = z.strictObject({
target: z.string(),
isa: z.enum(["xtensa", "riscv"]),
source: z.string(),
});
/**
* `esp_app_desc_t` fields, as ESP-IDF names them; strings stop at NUL. A field
* the file cuts off is null; `observed_length` says where the descriptor ends.
*/
export const espAppDescriptorSchema = z.strictObject({
kind: z.literal("app"),
offset,
/** Descriptor bytes the file holds, at most the 256 of `esp_app_desc_t`. */
observed_length: z.int().min(4).max(256),
secure_version: uint32.nullable(),
version: z.string().nullable(),
project_name: z.string().nullable(),
time: z.string().nullable(),
date: z.string().nullable(),
idf_ver: z.string().nullable(),
app_elf_sha256: sha256Hex.nullable(),
min_efuse_blk_rev_full: uint16.nullable(),
max_efuse_blk_rev_full: uint16.nullable(),
/**
* ESP-IDF's value: the MMU page size as a base-2 logarithm (16 is 64 KiB);
* 0 in images built before ESP-IDF v5.4.
*/
mmu_page_size: uint8.nullable(),
/**
* The byte ESP-IDF v6.1 names `spi_flash_mode`, as stored; REA does not
* interpret it. Releases that reserve this byte leave it zero.
*/
spi_flash_mode: uint8.nullable(),
});
/** `esp_bootloader_desc_t` fields, as ESP-IDF names them. */
export const espBootloaderDescriptorSchema = z.strictObject({
kind: z.literal("bootloader"),
offset,
secure_version: uint8,
version: uint32,
idf_ver: z.string().nullable(),
date_time: z.string().nullable(),
});
/**
* ESP32-family image facts. `header` and the descriptor fields are observed;
* the descriptor kind, `chip`, the integrity statuses and the trailing-byte
* ranges are derived.
*/
export const espImageMetadataSchema = z.strictObject({
kind: z.literal("esp-image"),
/** `esp_image_header_t` fields, as ESP-IDF names them. */
header: z.strictObject({
segment_count: uint8,
spi_mode: uint8,
spi_speed: uint8,
spi_size: uint8,
entry_addr: uint32,
wp_pin: uint8,
spi_pin_drv: z.string().regex(/^[0-9a-f]{6}$/u),
chip_id: uint16,
min_chip_rev: uint8,
min_chip_rev_full: uint16,
max_chip_rev_full: uint16,
reserved: z.string().regex(/^[0-9a-f]{8}$/u),
hash_appended: z.boolean(),
}),
/** Derived from `chip_id` through the cited table; null for unknown IDs. */
chip: espChipFactsSchema.nullable(),
checksum: z.strictObject({
offset: offset.nullable(),
stored: uint8.nullable(),
computed: uint8.nullable(),
status: z.enum(["valid", "invalid", "unavailable"]),
}),
/** The SHA-256 the header's `hash_appended` adds after the checksum. */
appended_sha256: z.strictObject({
offset: offset.nullable(),
stored: sha256Hex.nullable(),
computed: sha256Hex.nullable(),
status: z.enum(["valid", "invalid", "unavailable", "not-appended"]),
}),
/** End of the checksum block and any digest; null when truncated before it. */
image_end: offset.nullable(),
signature_sector_offset: offset.nullable(),
/** Bytes after the image that no padding, signature or erased flash describes. */
undescribed_trailing: z.array(byteRange),
/** Its kind classifies the image as an app or a bootloader. */
descriptor: z
.discriminatedUnion("kind", [
espAppDescriptorSchema,
espBootloaderDescriptorSchema,
])
.nullable(),
/**
* Set when the file is a flash dump read from a later flash address: this
* image is not an app, its chip's ROM loads the bootloader from
* `flash_offset`, and a partition table sits at `table_file_offset`, where
* that origin puts the default `0x8000`. Reopen the file as
* `esp-flash-image` with that `flash_offset`. Null otherwise, and in the
* nested facts of a flash layout. Absent from Evidence recorded before REA
* reported it; its graph digest covers the record without the field.
*/
flash_dump: z
.strictObject({ flash_offset: uint32, table_file_offset: offset })
.nullable()
.exactOptional(),
});
export type EspImageMetadata = z.infer<typeof espImageMetadataSchema>;
/** One header-declared segment; `data_len` is declared, not available. */
export const espImageSegmentMetadataSchema = z.strictObject({
kind: z.literal("esp-image-segment"),
index: uint8,
load_addr: uint32,
data_len: uint32,
header_offset: offset,
/** ESP-IDF's cold-boot `should_map` and `should_load`; derived. */
classification: z.enum(["mapped", "loaded", "not-loaded", "unknown"]),
});
export type EspImageSegmentMetadata = z.infer<
typeof espImageSegmentMetadataSchema
>;
/** One Secure Boot v2 block with its 0xE7 magic and a matching CRC32. */
export const espImageSignatureBlockMetadataSchema = z.strictObject({
kind: z.literal("esp-image-signature-block"),
/** Slot in the signature sector, 0 to 2. */
index: uint8,
version: uint8,
scheme: z.enum(["rsa", "ecdsa"]).nullable(),
/** Third block byte for ECDSA: 0 selects SHA-256, 1 SHA-384. */
sha_version: uint8.nullable(),
curve_id: uint8.nullable(),
digest_algorithm: z.enum(["sha256", "sha384"]).nullable(),
image_digest: z
.string()
.regex(/^(?:[0-9a-f]{64}|[0-9a-f]{96})$/u)
.nullable(),
/** Whether `image_digest` equals the digest of the bytes before the sector. */
image_digest_matches: z.boolean().nullable(),
/** SHA-256 of the block's public key, the value eFuse key digests hold. */
public_key_sha256: sha256Hex.nullable(),
/** REA checks no key, signature or eFuse. */
signature_status: z.literal("unverified"),
});
export type EspImageSignatureBlockMetadata = z.infer<
typeof espImageSignatureBlockMetadataSchema
>;
/** One partition table entry as declared, with ESP-IDF's type names. */
export const espFlashPartitionMetadataSchema = z.strictObject({
kind: z.literal("esp-flash-partition"),
/** Position among the table's partition entries. */
index: uint8,
/** The label up to its first NUL; null when it is not UTF-8. */
label: z.string().nullable(),
/** The label's bytes up to its first NUL in hex, when it is not UTF-8. */
label_hex: z
.string()
.regex(/^(?:[0-9a-f]{2}){1,16}$/u)
.nullable(),
type: uint8,
subtype: uint8,
/** `gen_esp32part.py` names; null for values it does not name. */
type_name: z.string().nullable(),
subtype_name: z.string().nullable(),
flash_address: uint32,
size: uint32,
encrypted: z.boolean(),
readonly: z.boolean(),
/** Flag bits other than `encrypted` and `readonly`, which ESP-IDF ignores. */
unknown_flags: uint32,
/**
* Observed in the bytes the dump holds, from the partition's start: the
* first byte that is not 0xFF and just past the last, null when every byte
* is erased, and how many bytes are not 0xFF. All null for a partition the
* dump holds none of.
*/
written_offset: uint32.nullable(),
written_end: uint32.nullable(),
written_bytes: uint32.nullable(),
});
/** ESP image facts for a region of a dump; offsets are relative to the region. */
export const espFlashImageFactsSchema = z.strictObject({
/** An ESP image, erased flash, or bytes that are not one, such as ciphertext. */
status: z.enum(["esp-image", "erased", "unrecognized"]),
image: espImageMetadataSchema.nullable(),
segments: z.array(espImageSegmentMetadataSchema),
signature_blocks: z.array(espImageSignatureBlockMetadataSchema),
coverage: z.enum(["complete", "unknown"]).nullable(),
limitations: z.array(z.string()),
});
const espOtaRecordSchema = z.strictObject({
index: z.union([z.literal(0), z.literal(1)]),
ota_seq: uint32,
seq_label: z.string().regex(/^[0-9a-f]{40}$/u),
ota_state: uint32,
state_name: z
.enum(["new", "pending-verify", "valid", "invalid", "aborted", "undefined"])
.nullable(),
crc: uint32,
/** CRC-32 of `ota_seq` seeded with 0xFFFFFFFF, as the bootloader checks it. */
crc_matches: z.boolean(),
valid: z.boolean(),
});
/**
* An ESP flash dump: where it sits in flash, its bootloader, partition table,
* partitions and OTA data. Entries are the table's partitions; app and
* bootloader regions carry their ESP image facts here.
*/
export const espFlashImageMetadataSchema = z.strictObject({
kind: z.literal("esp-flash-image"),
/** Flash address of the file's first byte: 0 unless the caller gave one. */
flash_offset: z.strictObject({
value: uint32,
source: z.enum(["default", "caller"]),
}),
/** From the bootloader's `chip_id`; null without a located bootloader. */
chip: espChipFactsSchema.nullable(),
/**
* The image at its chip's ROM offset, through the partition table; `path`
* is its entry.
*/
bootloader: z
.strictObject({
path: z.string(),
flash_address: uint32,
region_size: offset,
facts: espFlashImageFactsSchema,
})
.nullable(),
partition_table: z.strictObject({
flash_address: uint32,
/** 0x8000, the caller's offset, or a scan for a verified MD5 (derived). */
method: z.enum(["default", "caller", "scan"]),
partition_count: uint8,
md5: z.strictObject({
status: z.enum(["valid", "invalid", "absent"]),
stored: z
.string()
.regex(/^[0-9a-f]{32}$/u)
.nullable(),
computed: z
.string()
.regex(/^[0-9a-f]{32}$/u)
.nullable(),
}),
}),
/**
* Declared partitions that begin before the dump's first byte, which have
* no entry. A partition past the dump's end is an entry with no bytes.
*/
partitions_outside_dump: z.array(espFlashPartitionMetadataSchema),
/** ESP image facts for each app partition the dump holds, by its entry. */
apps: z.array(
z.strictObject({
path: z.string(),
partition_index: uint8,
facts: espFlashImageFactsSchema,
}),
),
/** The otadata partition the bootloader uses: the last one declared. */
otadata: z
.strictObject({
partition_index: uint8,
/** The records the dump holds, at most one per sector. */
records: z.array(espOtaRecordSchema).max(2),
})
.nullable(),
/**
* The app the bootloader would select, derived for every table under the
* stated assumptions. `basis` names the branch: `otadata`,
* `factory-fallback` and `ota_0-fallback` from the otadata records,
* `no-otadata` for a table without otadata (the factory index), `none`
* for the bootloader's INVALID_INDEX, after which it resets, and
* `unknown` when the dump lacks a record the bootloader reads.
* `partition_index` is null when the table declares no partition for the
* selected slot or fallback.
*/
boot_selection: z.strictObject({
basis: z.enum([
"otadata",
"factory-fallback",
"ota_0-fallback",
"no-otadata",
"none",
"unknown",
]),
active_record: z.union([z.literal(0), z.literal(1)]).nullable(),
slot: uint8.nullable(),
partition_index: uint8.nullable(),
assumptions: z.array(z.string()),
/** Rollback, anti-rollback and load fallback decide it at boot. */
running_partition: z.literal("unknown"),
}),
/**
* Ranges outside every described region, each from a byte that is not
* erased flash to the end of its gap.
*/
undescribed: z.array(byteRange),
});
export type EspFlashImageMetadata = z.infer<typeof espFlashImageMetadataSchema>;
/**
* U-Boot legacy uImage header facts. Declared addresses are facts from the
* header, not proven entry points; CRCs are verified before inventory.
*/
export const uImageMetadataSchema = z.strictObject({
kind: z.literal("uimage"),
magic: z.literal(0x27051956),
timestamp: uint32,
timestamp_iso: z.string(),
payload_size: uint32,
load_address: uint32,
entry_point: uint32,
header_crc32: uint32,
data_crc32: uint32,
crc_verified: z.literal(true),
os: z.number().int().min(0).max(255),
os_name: z.string(),
arch: z.number().int().min(0).max(255),
arch_name: z.string(),
type: z.number().int().min(0).max(255),
type_name: z.string(),
compression: z.number().int().min(0).max(255),
compression_name: z.string(),
name: z.string().nullable(),
name_bytes_hex: z.string().regex(/^[0-9a-f]{64}$/u),
sub_image_count: uint32,
});
export type UImageMetadata = z.infer<typeof uImageMetadataSchema>;
/** One stored payload, or one multi-image sub-image, as stored bytes. */
export const uImageEntryMetadataSchema = z.strictObject({
kind: z.literal("uimage-entry"),
role: z.enum(["payload", "sub-image"]),
index: uint32,
});
export type UImageEntryMetadata = z.infer<typeof uImageEntryMetadataSchema>;
const bunHex = z
.string()
.regex(/^[0-9a-f]+$/u)
.nullable();
/**
* Container facts for one Bun standalone module graph. `flags` stays the raw
* u32 because Bun 1.3 and 1.4 reuse the low bits for different meanings.
*/
export const bunStandaloneMetadataSchema = z.strictObject({
kind: z.literal("bun-standalone"),
layout_revision: z.enum(["bun-1.3.3", "bun-1.3.9"]),
carrier: z.enum([
"mach-o-section",
"pe-section",
"elf-section",
"appended-trailer",
]),
payload_offset: offset,
payload_length: offset,
module_count: uint32,
entry_point_id: uint32,
entry_point_source_name: z.string().nullable(),
entry_point_logical_path: z.string().nullable(),
compile_exec_argv: z.string().nullable(),
compile_exec_argv_hex: bunHex,
flags: uint32,
});
export type BunStandaloneMetadata = z.infer<typeof bunStandaloneMetadataSchema>;
/** One stored region of a Bun module. Siblings repeat the module's source facts. */
export const bunStandaloneEntryMetadataSchema = z.strictObject({
kind: z.literal("bun-standalone-entry"),
index: uint32,
region: z.enum(["contents", "sourcemap", "bytecode", "module-info"]),
source_name: z.string().nullable(),
source_name_hex: bunHex,
encoding: uint32,
encoding_name: z.enum(["binary", "latin1", "utf8", "utf16"]).nullable(),
loader: uint32,
loader_name: z.string().nullable(),
module_format: uint32,
module_format_name: z.enum(["none", "esm", "cjs"]).nullable(),
side: uint32,
side_name: z.enum(["server", "client"]).nullable(),
entry_point: z.boolean(),
sourcemap_bytes: offset,
bytecode_bytes: offset,
module_info_bytes: offset,
bytecode_origin: z.string().nullable(),
bytecode_origin_hex: bunHex,
});
export type BunStandaloneEntryMetadata = z.infer<
typeof bunStandaloneEntryMetadataSchema
>;
const denoListingSchema = z.enum(["listed", "unsupported"]);
const denoEnvVarSchema = z.strictObject({
name: z.string(),
value: z.string(),
value_byte_length: offset,
});
/**
* Container facts for one Deno compile section. The metadata JSON has no Deno
* version field. Environment-file values and CA bytes are retained as local evidence.
*/
export const denoStandaloneMetadataSchema = z.strictObject({
kind: z.literal("deno-standalone"),
carrier: z.enum(["mach-o", "elf", "pe"]),
embedding: z.enum([
"mach-o-section",
"mach-o-intel-trailer",
"elf-note",
"pe-resource",
]),
section_offset: offset,
section_length: offset,
entrypoint_key: z.string(),
argv: z.array(z.string()).nullable(),
v8_flags: z.array(z.string()).nullable(),
permissions: jsonRecordSchema(jsonValueSchema).nullable(),
unstable_config: jsonRecordSchema(jsonValueSchema).nullable(),
app_name: z.string().nullable(),
self_extracting: z.string().nullable(),
npm_snapshot_byte_length: offset,
env_var_count: uint32.nullable(),
env_vars: z.array(denoEnvVarSchema).nullable(),
ca_data: z.array(z.number().int().min(0).max(255)).nullable(),
ca_data_byte_length: offset.nullable(),
specifier_count: uint32,
specifier_byte_length: offset,
specifier_listing: denoListingSchema,
redirect_count: uint32,
redirect_byte_length: offset,
redirect_listing: denoListingSchema,
remote_module_count: uint32,
remote_module_byte_length: offset,
remote_module_listing: denoListingSchema,
vfs_header_byte_length: offset,
vfs_file_data_byte_length: offset,
vfs_listing: denoListingSchema,
vfs_case_sensitivity: z.enum(["sensitive", "insensitive"]).nullable(),
});
export type DenoStandaloneMetadata = z.infer<
typeof denoStandaloneMetadataSchema
>;
/** One stored region of a Deno virtual file, symlink, or remote module. */
export const denoStandaloneEntryMetadataSchema = z.strictObject({
kind: z.literal("deno-standalone-entry"),
role: z.enum([
"vfs-file",
"vfs-transpiled",
"vfs-cjs-export-analysis",
"vfs-source-map",
"vfs-symlink",
"remote-module",
"remote-transpiled",
"remote-source-map",
"remote-cjs-export-analysis",
]),
index: uint32,
source_path: z.string().nullable(),
entry_point: z.boolean(),
executable: z.boolean(),
is_valid_utf8: z.boolean().nullable(),
symlink_dest_parts: z.array(z.string()).nullable(),
media_type: z.number().int().min(0).max(255).nullable(),
media_type_name: z.string().nullable(),
});
export type DenoStandaloneEntryMetadata = z.infer<
typeof denoStandaloneEntryMetadataSchema
>;
export const artifactFormatMetadataSchema = z.discriminatedUnion("kind", [
godotPckMetadataSchema,
godotPckEntryMetadataSchema,
unityFsMetadataSchema,
unityFsEntryMetadataSchema,
wxapkgMetadataSchema,
wxapkgEntryMetadataSchema,
pyInstallerMetadataSchema,
pyInstallerEntryMetadataSchema,
dotnetBundleEntryMetadataSchema,
bunStandaloneMetadataSchema,
bunStandaloneEntryMetadataSchema,
denoStandaloneMetadataSchema,
denoStandaloneEntryMetadataSchema,
nodeSeaMetadataSchema,
nodeSeaEntryMetadataSchema,
hexRecordsMetadataSchema,
hexRecordRegionMetadataSchema,
uImageMetadataSchema,
uImageEntryMetadataSchema,
espImageMetadataSchema,
espImageSegmentMetadataSchema,
espImageSignatureBlockMetadataSchema,
uf2MetadataSchema,
uf2RegionMetadataSchema,
ntfsMftMetadataSchema,
dfuSeMetadataSchema,
dfuSeElementMetadataSchema,
espFlashImageMetadataSchema,
espFlashPartitionMetadataSchema,
fitMetadataSchema,
fitImageMetadataSchema,
]);
export type ArtifactFormatMetadata = z.infer<
typeof artifactFormatMetadataSchema
>;
type FormatMetadataByKind = {
readonly [Metadata in ArtifactFormatMetadata as Metadata["kind"]]: Metadata;
};
type FormatMetadataKind = keyof FormatMetadataByKind;
/**
* What each kind declares beyond its own bytes, which artifact comparison
* compares, or why comparison uses none of its facts. Every kind needs an
* entry, so a new kind does not compile without this decision; a new field
* of an existing kind still needs a review of its projection. For a node-less
* metadata record, the projection is all of its format metadata that
* comparison checks.
*/
const declarationsByKind: {
readonly [Kind in FormatMetadataKind]:
| {
readonly declaration: (
metadata: FormatMetadataByKind[Kind],
) => FormatMetadataDeclaration["facts"];
/** Projected fields the producer could not interpret. */
readonly unreadable?: (
metadata: FormatMetadataByKind[Kind],
) => FormatMetadataDeclaration["unreadable"];
}
| { readonly notCompared: string };
} = {
"godot-pck": {
notCompared:
"Its fields come from the pack's own bytes, which content comparison covers.",
},
"godot-pck-entry": {
notCompared:
"index and declared_offset are layout; flags and md5_status repeat occurrence facts; source_path spells Godot's lookup path in a version-dependent way. docs/godot-pck.md gives the evidence.",
},
unityfs: {
notCompared:
"Its fields come from the bundle's own bytes, which content comparison covers.",
},
/** What the directory declares for a member: its source path and raw node flags. Index and offset are layout. */
"unityfs-entry": {
declaration: (metadata) => ({
source_path: metadata.source_path,
flags: metadata.flags,
}),
},
wxapkg: {
notCompared:
"Its fields come from the package's own bytes, which content comparison covers.",
},
"wxapkg-entry": {
notCompared:
"index is layout. source_path keeps the declared name, including a producer root prefix the occurrence path strips.",
},
pyinstaller: {
notCompared:
"Its fields come from the archive's own bytes, which content comparison covers.",
},
/** Declaration identity that is stable across archive layout changes. */
"pyinstaller-entry": {
declaration: (metadata) => ({
container: metadata.container,
container_name: metadata.container_name,
source_name: metadata.source_name,
type_code: metadata.type_code,
}),
},
"dotnet-bundle-entry": {
notCompared:
"Its fields come from the host's bundle manifest, whose bytes the host's content comparison covers.",
},
"bun-standalone": {
notCompared:
"Its fields come from the carrier's own bytes, which content comparison covers.",
},
"bun-standalone-entry": {
notCompared:
"Its fields come from the carrier's module graph, whose bytes the carrier's content comparison covers.",
},
"deno-standalone": {
notCompared:
"Its fields come from the carrier's own bytes, which content comparison covers.",
},
"deno-standalone-entry": {
notCompared:
"Its fields come from the carrier's Deno section, whose bytes the carrier's content comparison covers.",
},
"node-sea": {
notCompared:
"Its fields come from the carrier's own bytes, which content comparison covers.",
},
/** Runtime member identity, independent of blob layout and directory order. */
"node-sea-entry": {
declaration: (metadata) => ({
role: metadata.role,
source_path: metadata.source_path,
}),
},
"hex-records": {
notCompared:
"Its fields come from the file's own records, which content comparison covers.",
},
"hex-record-region": {
notCompared:
"start_address and end_address restate the region's path, and length follows from them; record and line counts are layout.",
},
uimage: {
notCompared:
"Its header facts come from the image's own bytes, which content comparison covers.",
},
"uimage-entry": {
notCompared:
"role and index restate the entry's path; its stored bytes are compared by content.",
},
"esp-image": {
notCompared:
"Its fields come from the image's own bytes, which content comparison covers.",
},
/**
* What a segment header declares: where its bytes load and how many there
* are. Header offsets are layout, and the classification is derived.
*/
"esp-image-segment": {
declaration: (metadata) => ({
load_addr: metadata.load_addr,
data_len: metadata.data_len,
}),
},
"esp-image-signature-block": {
notCompared:
"Its fields come from the block's own bytes, which content comparison covers, and image_digest_matches follows the image's content.",
},
uf2: {
notCompared:
"Its counts come from the file's own blocks, which content comparison covers, and family_name_registry names REA's label registry.",
},
"uf2-region": {
notCompared:
"Its familyID and addresses are in its path, family_name is REA's label for the familyID, and its block counts and positions are layout; content comparison covers its bytes.",
},
"ntfs-mft": {
notCompared:
"Its fields come from the file's own FILE record headers and resident file-name attributes, which content comparison covers.",
},
dfuse: {
notCompared:
"Its fields come from the file's own prefix, target headers and suffix, which content comparison covers.",
},
/** The device memory an element targets, which its path and bytes do not show. */
"dfuse-element": {
declaration: (metadata) => ({
alternate_setting: metadata.alternate_setting,
target_name: metadata.target_name,
}),
},
"esp-flash-image": {
notCompared:
"Its fields come from the dump's bytes and from the caller's flash_offset and partition_table_offset, which are target identity; each partition's range, compared by content, reflects them.",
},
/**
* What a partition entry declares; its index is layout, and the written
* fields are observed from its bytes, which content comparison covers.
*/
"esp-flash-partition": {
declaration: (metadata) => ({
label: metadata.label,
label_hex: metadata.label_hex,
type: metadata.type,
subtype: metadata.subtype,
flash_address: metadata.flash_address,
size: metadata.size,
encrypted: metadata.encrypted,
readonly: metadata.readonly,
unknown_flags: metadata.unknown_flags,
}),
},
fit: {
notCompared:
"Its fields come from the FIT's own bytes, which content comparison covers.",
},
/**
* Payload interpretation and sharing declarations, independent of FIT
* placement: the image's name, index and data position are layout.
*/
"fit-image": {
declaration: (metadata) => ({
type: metadata.type,
arch: metadata.arch,
os: metadata.os,
compression: metadata.compression,
load: metadata.load,
entry: metadata.entry,
image_data: metadata.image_data,
cipher_present: metadata.cipher_present,
cipher_algo: metadata.cipher_algo,
}),
unreadable: (metadata) => metadata.unreadable_fields,
},
};
/** Kinds whose declarations artifact comparison compares, in table order. */
export const COMPARED_FORMAT_METADATA_KINDS = Object.entries(
declarationsByKind,
).flatMap(([kind, entry]) => ("declaration" in entry ? [kind] : []));
/** A declaration that artifact comparison compares within its kind. */
export interface FormatMetadataDeclaration {
readonly kind: FormatMetadataKind;
readonly facts: Readonly<Record<string, unknown>>;
/** Facts whose value is unknown, which compare as neither equal nor changed. */
readonly unreadable: readonly string[];
}
// Generic in the kind, so each projection receives its own metadata type.
const declarationOf = <Kind extends FormatMetadataKind>(
kind: Kind,
metadata: FormatMetadataByKind[Kind],
): FormatMetadataDeclaration | undefined => {
const entry = declarationsByKind[kind];
return "declaration" in entry
? {
kind,
facts: entry.declaration(metadata),
unreadable: entry.unreadable?.(metadata) ?? [],
}
: undefined;
};
/** The compared declaration, or undefined when comparison uses none. */
export const formatMetadataDeclaration = (
metadata: ArtifactFormatMetadata | undefined,
): FormatMetadataDeclaration | undefined =>
metadata === undefined ? undefined : declarationOf(metadata.kind, metadata);