Files
reverse-engineering-anything/src/artifacts/ArtifactReader.ts
T
d7ca93cc2c fix(artifacts): name the ASAR header admission budget (#6172)
Keep the 16 MiB ASAR header/index ceiling fixed and report that budget,
the lack of a runtime override, and that source selection does not bypass
header admission.

Refs #5910

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
2026-10-22 06:33:09 +08:00

323 lines
12 KiB
TypeScript

import type { ArtifactAdmissionLimit } from "../domain/artifactOperationError.js";
import type {
ArtifactIntegrityClaim,
ArtifactIntegrityDetails,
} from "../domain/artifactIntegrity.js";
import type { ArtifactFormatMetadata } from "../domain/artifactFormatMetadata.js";
import { AnalysisError } from "../domain/analysisErrorBase.js";
import type { AnalysisCleanupLease } from "./AnalysisCleanupLease.js";
import type { Readable } from "node:stream";
import type {
ArtifactCommand,
ArtifactInventoryCoverage,
ArtifactTraversalOmissionKind,
} from "../domain/artifactGraph.js";
import type { ZipPackageFormat } from "../domain/zipPackageFormat.js";
import type {
AnalysisCleanupObservation,
AnalysisPartialObservation,
} from "../domain/analysisErrorBase.js";
/** Which bound a `limit` failure hit, so callers need not infer it. */
export type ArtifactReaderBoundary = "file-size" | "capacity" | "memory";
export interface ArtifactReaderFailureOptions extends ErrorOptions {
readonly cancellation?: { readonly reason: unknown } | undefined;
readonly cleanup?: AnalysisCleanupObservation | undefined;
/** Exact authority for all reported cleanup; undefined leaves ownership unknown. */
readonly cleanupLeases?: readonly AnalysisCleanupLease[] | undefined;
readonly partialObservation?: AnalysisPartialObservation | undefined;
/** The constrained resource bound for a limit failure. */
readonly boundary?: ArtifactReaderBoundary;
/** Named admission whose recovery must not be inferred from other limit text. */
readonly admission?: ArtifactAdmissionLimit;
}
/** Archive-neutral entry metadata. Reader adapters never choose output paths. */
export interface ArtifactEntry {
readonly path: string;
readonly kind: "file" | "directory" | "symlink" | "slice" | "metadata";
readonly declaredSize: number | null;
readonly compressedSize: number | null;
readonly executable: boolean;
readonly encrypted: boolean;
readonly byteOffset: number | null;
readonly declaredIntegrity: ArtifactIntegrityClaim | null;
readonly formatMetadata?: ArtifactFormatMetadata;
readonly unpacked: boolean;
readonly limitations: readonly string[];
readonly adapterKey: string;
/** Filesystem identity captured during traversal, when supplied by an adapter. */
readonly sourceIdentity?: {
readonly device: bigint;
readonly inode: bigint;
readonly size: bigint;
readonly mode: bigint;
readonly mtimeNs: bigint;
readonly ctimeNs: bigint;
};
}
/** Compare captured filesystem identity and mutable metadata at a read boundary. */
export const sameArtifactSourceIdentity = (
expected: ArtifactEntry["sourceIdentity"],
observed: {
readonly dev: bigint;
readonly ino: bigint;
readonly size: bigint;
readonly mode: bigint;
readonly mtimeNs: bigint;
readonly ctimeNs: bigint;
},
): boolean =>
expected !== undefined &&
observed.dev === expected.device &&
observed.ino === expected.inode &&
observed.size === expected.size &&
observed.mode === expected.mode &&
observed.mtimeNs === expected.mtimeNs &&
observed.ctimeNs === expected.ctimeNs;
/** Snapshot mutable entry metadata before the producer later validates open(). */
export const copyArtifactEntry = (entry: ArtifactEntry): ArtifactEntry => ({
...entry,
limitations: [...entry.limitations],
declaredIntegrity:
entry.declaredIntegrity === null ? null : { ...entry.declaredIntegrity },
...(entry.formatMetadata === undefined
? {}
: { formatMetadata: { ...entry.formatMetadata } }),
...(entry.sourceIdentity === undefined
? {}
: { sourceIdentity: { ...entry.sourceIdentity } }),
});
/** Compare every portable and adapter-owned fact supplied with an entry. */
export const sameArtifactEntry = (
entry: ArtifactEntry,
produced: ArtifactEntry,
): boolean =>
entry.path === produced.path &&
entry.kind === produced.kind &&
entry.declaredSize === produced.declaredSize &&
entry.compressedSize === produced.compressedSize &&
entry.executable === produced.executable &&
entry.encrypted === produced.encrypted &&
entry.byteOffset === produced.byteOffset &&
sameDeclaredIntegrity(entry.declaredIntegrity, produced.declaredIntegrity) &&
sameFormatMetadata(entry.formatMetadata, produced.formatMetadata) &&
entry.unpacked === produced.unpacked &&
entry.adapterKey === produced.adapterKey &&
entry.limitations.length === produced.limitations.length &&
entry.limitations.every(
(limitation, index) => limitation === produced.limitations[index],
) &&
entry.sourceIdentity?.device === produced.sourceIdentity?.device &&
entry.sourceIdentity?.inode === produced.sourceIdentity?.inode &&
entry.sourceIdentity?.size === produced.sourceIdentity?.size &&
entry.sourceIdentity?.mode === produced.sourceIdentity?.mode &&
entry.sourceIdentity?.mtimeNs === produced.sourceIdentity?.mtimeNs &&
entry.sourceIdentity?.ctimeNs === produced.sourceIdentity?.ctimeNs;
const sameDeclaredIntegrity = (
left: ArtifactEntry["declaredIntegrity"],
right: ArtifactEntry["declaredIntegrity"],
): boolean =>
left === right ||
(left !== null &&
left !== undefined &&
right !== null &&
right !== undefined &&
left.algorithm === right.algorithm &&
left.digest === right.digest);
/** Format metadata contracts contain only primitive source fields. */
const sameFormatMetadata = (
left: ArtifactFormatMetadata | undefined,
right: ArtifactFormatMetadata | undefined,
): boolean =>
left === right ||
(left !== undefined &&
right !== undefined &&
Object.keys(left).length === Object.keys(right).length &&
Object.entries(left).every(
([key, value]) =>
Object.hasOwn(right, key) && Reflect.get(right, key) === value,
));
/** One entry or subtree deliberately excluded from a traversal. */
export interface ArtifactTraversalOmission {
readonly path: string;
readonly kind: ArtifactTraversalOmissionKind;
}
/** Facts from an exhausted traversal, separate from entry content certainty. */
export interface ArtifactReaderTraversal {
readonly status: ArtifactInventoryCoverage["status"];
readonly limitations: readonly string[];
}
/** Per-iterator observation sink; adapters report intentional omissions without shared state. */
export interface ArtifactTraversalOptions {
readonly onOmission?: (omission: ArtifactTraversalOmission) => void;
readonly onCoverage?: (coverage: ArtifactReaderTraversal) => void;
}
/** Read-only adapter over one directory, archive, or virtual container. */
export interface ArtifactReader {
readonly format:
| "directory"
| ZipPackageFormat
| "asar"
| "godot-pck"
| "pyinstaller"
| "file";
entries(
signal?: AbortSignal,
options?: ArtifactTraversalOptions,
): AsyncIterable<ArtifactEntry>;
open(entry: ArtifactEntry, signal?: AbortSignal): Promise<Readable>;
provenance(): readonly ArtifactCommand[];
formatMetadata?(): ArtifactFormatMetadata | undefined;
/** Bind materialization to the exact inventoried carrier before creating outputs. */
prepareContainer?(
expectedSha256: string,
signal?: AbortSignal,
): Promise<void>;
close(): Promise<void>;
}
/** Typed adapter failure translated at provider boundary. */
export class ArtifactReaderFailure extends Error {
readonly cancellation: { readonly reason: unknown } | undefined;
readonly cleanup: AnalysisCleanupObservation | undefined;
readonly cleanupLeases: readonly AnalysisCleanupLease[] | undefined;
readonly partialObservation: AnalysisPartialObservation | undefined;
/** The constrained bound for a `limit` reason, when the reader knows it. */
readonly boundary: ArtifactReaderBoundary | undefined;
/** Named admission for a limit whose public recovery is owned by the producer. */
readonly admission: ArtifactAdmissionLimit | undefined;
constructor(
readonly reason:
| "cancelled"
| "format"
| "integrity"
| "io"
| "limit"
| "path"
| "unavailable",
message: string,
options?: ArtifactReaderFailureOptions,
readonly details?: ArtifactIntegrityDetails,
) {
super(message, options);
this.name = "ArtifactReaderFailure";
this.cancellation =
reason === "cancelled"
? (options?.cancellation ?? { reason: options?.cause })
: undefined;
this.cleanup = options?.cleanup;
this.cleanupLeases =
options?.cleanupLeases ?? (this.cleanup === undefined ? [] : undefined);
this.partialObservation = options?.partialObservation;
this.boundary = options?.boundary;
this.admission = options?.admission;
}
/** Preserve the primary failure and observation. Supplied leases must cover all merged cleanup. */
static withCleanup(
cause: unknown,
cleanup: AnalysisCleanupObservation,
partialObservation?: AnalysisPartialObservation,
cleanupLeases?: readonly AnalysisCleanupLease[],
): ArtifactReaderFailure {
const primary =
cause instanceof ArtifactReaderFailure
? cause
: new ArtifactReaderFailure("io", errorMessage(cause), {
cause,
...(cause instanceof AnalysisError
? {
partialObservation: cause.partialObservation,
...(cause.cleanupIncomplete
? {
cleanup: ArtifactReaderFailure.cleanupObservation(
cause,
[],
),
}
: {}),
}
: {}),
});
const mergedObservation = partialObservation ?? primary.partialObservation;
return new ArtifactReaderFailure(
primary.reason,
primary.message,
{
cause: primary,
cancellation: primary.cancellation,
cleanup: combineCleanup(primary.cleanup, cleanup),
cleanupLeases:
cleanupLeases ??
(cleanup === primary.cleanup ? primary.cleanupLeases : undefined),
// The constrained bound survives cleanup wrapping, or a file-size
// refusal would be reported as a memory constraint once a close fails.
...(primary.boundary === undefined
? {}
: { boundary: primary.boundary }),
...(primary.admission === undefined
? {}
: { admission: primary.admission }),
...(mergedObservation === undefined
? {}
: { partialObservation: mergedObservation }),
},
primary.details,
);
}
/** Give otherwise opaque cleanup failures a concrete owned resource. */
static cleanupObservation(
cause: unknown,
resource: string | readonly string[],
): AnalysisCleanupObservation {
if (cause instanceof ArtifactReaderFailure && cause.cleanup !== undefined)
return cause.cleanup;
if (cause instanceof AnalysisError && cause.cleanupIncomplete)
return {
reason: cause.cleanup?.reason ?? cause.message,
resources: [...cause.cleanupResources],
};
return {
reason: errorMessage(cause),
resources: typeof resource === "string" ? [resource] : [...resource],
};
}
}
const combineCleanup = (
primary: AnalysisCleanupObservation | undefined,
cleanup: AnalysisCleanupObservation,
): AnalysisCleanupObservation =>
primary === undefined || primary === cleanup
? cleanup
: {
reason: `${primary.reason}; ${cleanup.reason}`,
resources: [...new Set([...primary.resources, ...cleanup.resources])],
};
const errorMessage = (cause: unknown): string =>
cause instanceof Error ? cause.message : String(cause);
/** Read explicit source authority; unrelated domain cleanup remains unresolved. */
export const artifactPrimaryCleanupLeases = (
cause: unknown,
): readonly AnalysisCleanupLease[] | undefined =>
cause instanceof ArtifactReaderFailure
? cause.cleanupLeases
: cause instanceof AnalysisError && cause.cleanupIncomplete
? undefined
: [];