Files
rea/src/artifacts/ArtifactProviderMetadata.ts
T
Kaoru0822andClaude Opus 5.5 11b7f918c2 feat(artifacts): trace Mach-O dylib resolution in bundles
Add trace_dylib_resolution (CLI trace-dylib-resolution) to the artifact
family. For the active Mach-O, or every executable in the active .app
(the main app, XPC services, extensions, login items, helpers), it
reports which file each dylib load reaches and how.

A pure TypeScript reader decodes FAT and Mach-O load commands:
LC_LOAD/WEAK/REEXPORT/LAZY/UPWARD_DYLIB, including the newer
dylib_use_command encoding with weak, re-export, upward and delayed-init
flags, plus LC_ID_DYLIB, LC_RPATH, LC_DYLD_ENVIRONMENT and code-signature
presence. Malformed tables are reported with the failing command instead
of being guessed.

Resolution follows dyld:
- @executable_path comes from each process root;
- @loader_path comes from the loading image, or from the owner of the
  LC_RPATH entry being expanded;
- @rpath searches the loader's rpaths first, then up the load chain;
- dependencies load dependents-first in load-command order, once per
  process, and a matching install name reuses an already loaded image;
- symlinks are followed inside the analyzed root, and escapes stop.

Every candidate path is reported with its outcome. Each edge resolves to
resolved, conditional (an outside-root candidate such as /usr/lib/swift
precedes), unresolved, or undetermined. Absolute and shared-cache system
paths are never checked against the host. Derived findings cover
unresolved required or weak loads, absent earlier @rpath candidates (with
library validation left unknown), and LC_DYLD_ENVIRONMENT.

verify:macos-bundle now also:
- checks every traced image against otool -l;
- asserts each resolution status on a clang-built fixture that adds a
  missing Overrides rpath, an absolute rpath, and a delay-init library;
- compares the predicted load order of two process roots with what dyld
  loads under DYLD_PRINT_LIBRARIES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 10:31:17 +00:00

55 lines
2.1 KiB
TypeScript

import type {
CapabilityDescriptor,
ProviderIdentity,
} from "../application/AnalysisProvider.js";
import { ARTIFACT_GRAPH_PROVIDER } from "../application/InvestigationProviders.js";
import { ARTIFACT_ANALYSIS_OPERATIONS } from "../contracts/artifactToolContracts.js";
/** Identity of artifact inventory and owned extraction observations. */
export const ARTIFACT_PROVIDER_IDENTITY: ProviderIdentity = Object.freeze(
ARTIFACT_GRAPH_PROVIDER,
);
const IDENTITY = ARTIFACT_PROVIDER_IDENTITY;
/** Declare artifact coverage without constructing readers or acquiring resources. */
export const artifactCapabilities = (
platform: NodeJS.Platform = process.platform,
): readonly CapabilityDescriptor[] =>
Object.freeze(
ARTIFACT_ANALYSIS_OPERATIONS.map((operation) => {
const common = {
provider: IDENTITY,
operation,
effects: Object.freeze({
mutatesArtifact: false,
launchesProcess:
operation !== "decode_interface_builder" &&
operation !== "inspect_keyed_archive" &&
operation !== "trace_dylib_resolution",
mayShowUi: false,
mayAccessNetwork: false,
mayWriteFilesystem:
operation === "extract_artifact" ||
operation === "inspect_artifact" ||
operation === "inventory_artifact",
changesPermissions: false,
requiresRoot: false,
}),
limitations: Object.freeze([
"DMG child inventory automatically uses a read-only native macOS mount; PKG remains root-hash-only.",
"ASAR files discovered in filesystem-backed inventories are expanded without bulk extraction; other nested containers remain recorded only.",
]),
};
return Object.freeze(
operation === "inspect_asset_catalog" && platform !== "darwin"
? {
...common,
available: false as const,
availabilityCode: "unsupported_host" as const,
reason: "Apple asset catalogs require macOS assetutil.",
}
: { ...common, available: true as const, reason: null },
);
}),
);