Merge remote-tracking branch 'origin/main' into codex/release-notes-6.9.0-20261111

This commit is contained in:
morluto committed 2026-11-11 17:33:25 -05:00
commit 7f99d80986
9 files changed
+123 -8

No files matched your search

+46
View File
@@ -74,6 +74,52 @@ describe("parseAdbDevicesOutput", () => {
},
]);
});
it("classifies adb mDNS wireless serials as TCP without treating them as USB", () => {
const output = [
"List of devices attached",
"adb-8c4f2a1b4e5d6f70-AbCdEf._adb-tls-connect._tcp device product:cheetah model:Pixel_7_Pro device:cheetah transport_id:4",
"phone._adb._tcp device transport_id:5",
"not-a-service._tcp device transport_id:6",
"adb-wired._adb-tls-connect._tcp device usb:1-1 product:raven model:Pixel_6 device:raven transport_id:7",
"",
].join("\n");
const { devices, unparsedLines } = parseAdbDevicesOutput(output);
expect(unparsedLines).toEqual({ sample: [], total: 0 });
expect(
devices.map(({ serial, transport, kind, kind_basis }) => ({
serial,
transport,
kind,
kind_basis,
})),
).toEqual([
{
serial: "adb-8c4f2a1b4e5d6f70-AbCdEf._adb-tls-connect._tcp",
transport: "tcp",
kind: "unknown",
kind_basis: "none",
},
{
serial: "phone._adb._tcp",
transport: "tcp",
kind: "unknown",
kind_basis: "none",
},
{
serial: "not-a-service._tcp",
transport: "unknown",
kind: "unknown",
kind_basis: "none",
},
{
serial: "adb-wired._adb-tls-connect._tcp",
transport: "usb",
kind: "physical",
kind_basis: "usb_transport",
},
]);
});
});
describe("ADB daemon startup notices", () => {
+9 -1
View File
@@ -23,6 +23,14 @@ import { parseAdbTransportIdentity } from "../domain/adb/adbTransportIdentity.js
const DEVICE_LINE = /^(\S+)\s+(.*)$/u;
/**
* adb prints a wireless-debugging transport's serial as the mDNS instance
* `<instance>._adb._tcp` or `<instance>._adb-tls-connect._tcp`. That serial
* has neither a colon nor a `usb:` field.
*/
const isAdbMdnsTcpSerial = (serial: string): boolean =>
serial.endsWith("._adb._tcp") || serial.endsWith("._adb-tls-connect._tcp");
const normalizeAdbOutputLine = (rawLine: string): string => rawLine.trimEnd();
export const isSuccessfulDaemonStartNotice = (line: string): boolean =>
@@ -122,7 +130,7 @@ export const parseAdbDevicesOutput = (
transport:
usb !== null
? "usb"
: isEmulator || serial.includes(":")
: isEmulator || serial.includes(":") || isAdbMdnsTcpSerial(serial)
? "tcp"
: "unknown",
product: metadataValue(remainder, "product"),
+9 -2
View File
@@ -1259,8 +1259,8 @@ it.each(["typed", "physical"] as const)(
const prepare = vi
.spyOn(ownershipObservation, "prepareProcessOwnershipInspection")
.mockResolvedValue();
const launcher = vi.fn(
async (options: Parameters<typeof spawnOwnedProviderProcess>[0]) => {
const launcher = vi.fn<typeof spawnOwnedProviderProcess>(
async (options) => {
expect(options.signal).toBeDefined();
controller.abort(reason);
expect(options.signal?.reason).toBe(reason);
@@ -1284,6 +1284,13 @@ it.each(["typed", "physical"] as const)(
}
expect(error.cause).toBe(reason);
expect(launcher).toHaveBeenCalledOnce();
launcher.mockImplementationOnce(launcherFor({ kind: "source" }));
const recovered = await worker.analyze(
makeScript("fetch('/after-admission');"),
5_000,
);
expect(recovered.ok).toBe(true);
expect(launcher).toHaveBeenCalledTimes(2);
} finally {
prepare.mockRestore();
await worker.close();
+5 -1
View File
@@ -326,7 +326,11 @@ export class WebBundleAnalysisWorker {
try {
await this.#start(deadline);
} catch (cause: unknown) {
if (processInspectionFailure(cause) !== undefined)
if (
processInspectionFailure(cause) !== undefined ||
(this.#runtime.launchFailure !== undefined &&
Object.is(this.#runtime.launchFailure.cause, cause))
)
ownershipInspectionFailure = this.#inspectionFailure(cause, deadline);
throw cause;
}
@@ -1108,6 +1108,19 @@ it("cites a non-ASCII import name by its source bytes", () => {
expect(bytes.subarray(at, at + 7).toString("latin1")).toBe(name);
});
it("treats the __chk_fail handler import as a FORTIFY sign", () => {
const bytes = elfHardeningFixture({
fortify: true,
fortifyName: "__chk_fail",
});
const image = readElfHardening(bytes);
const row = findRow(image.rows, "fortify");
expect(row).toMatchObject({ state: "enabled", location: { bytes: 10 } });
expect(row.basis).toContain("__chk_fail");
const at = row.location?.offset ?? 0;
expect(bytes.subarray(at, at + 10).toString("latin1")).toBe("__chk_fail");
});
it("keeps malformed name offsets malformed in both formats", async () => {
await expect(
readMachoHardening(machoHardeningFixture({ brokenStrx: true })),
+1
View File
@@ -279,6 +279,7 @@ export const readElfHardening = (
symbols,
(name) =>
name === "__fortify_fail" ||
name === "__chk_fail" ||
(name.startsWith("__") && name.endsWith("_chk")),
"No fortify symbol was found in the dynamic symbol table. A static or stripped image can omit those imports while still using FORTIFY.",
"The dynamic symbol table is absent, so FORTIFY use is unknown.",
+21
View File
@@ -238,6 +238,27 @@ architecture arm64e
]);
});
it("normalizes the CPU names lipo prints for i386 slices", () => {
// cctools misc/lipo.c print_cputype() spells i386 with I386 names.
expect(
parseLipoOutput(`architecture i386
cputype CPU_TYPE_I386
cpusubtype CPU_SUBTYPE_I386_ALL
capabilities 0x0
offset 16384
size 28
align 2^14 (16384)
`).architectures,
).toMatchObject([
{
name: "i386",
cpu_type_code: 7,
cpu_subtype_code: 3,
cpu_subtype_base: 3,
},
]);
});
it("keeps unknown symbolic CPU values as source text without a code", () => {
expect(
parseLipoOutput(
+2
View File
@@ -315,6 +315,7 @@ const integer = (value: string | undefined): number | null => {
const CPU_TYPES: Readonly<Record<string, number>> = {
CPU_TYPE_X86: 7,
CPU_TYPE_I386: 7,
CPU_TYPE_X86_64: 0x01000007,
CPU_TYPE_ARM: 12,
CPU_TYPE_ARM64: 0x0100000c,
@@ -325,6 +326,7 @@ const CPU_TYPES: Readonly<Record<string, number>> = {
const CPU_SUBTYPES: Readonly<Record<string, number>> = {
CPU_SUBTYPE_X86_ALL: 3,
CPU_SUBTYPE_I386_ALL: 3,
CPU_SUBTYPE_X86_64_ALL: 3,
CPU_SUBTYPE_X86_64_H: 8,
CPU_SUBTYPE_ARM64_ALL: 0,
+17 -4
View File
@@ -152,6 +152,7 @@ export class OwnedWorkerRuntime {
#lastRunId: string | undefined;
#terminalCloseRequested = false;
#inputFailure: Error | undefined;
#launchFailure: { readonly cause: unknown } | undefined;
#stdinGeneration = 0;
readonly #stdoutListeners = new Set<{
readonly stream: Readable;
@@ -191,6 +192,11 @@ export class OwnedWorkerRuntime {
return this.#inputFailure;
}
/** Failure observed at the current launcher call, without reclassifying its cause. */
get launchFailure(): { readonly cause: unknown } | undefined {
return this.#launchFailure;
}
/** Keep a returned typed cleanup error live until this exact owner releases. */
bindCleanupFailure(error: OwnedWorkerCleanupFailure | AnalysisError): void {
if (this.#cleanupEpoch === undefined) {
@@ -243,6 +249,7 @@ export class OwnedWorkerRuntime {
const ownRetry = this.#registry.has(this)
? this.#retryPending()
: Promise.resolve();
this.#launchFailure = undefined;
const acquisition = ownRetry.then(async () => {
if (this.#closed) this.#closed = false;
await this.#start(spawnOptions, initialize);
@@ -356,10 +363,16 @@ export class OwnedWorkerRuntime {
this.#cleanupEpoch ??= new OwnedWorkerRuntimeCleanupEpoch(this.#root.path);
if (this.#closing) throw new ProcessAdmissionError("runtime_closing");
this.#lastRunId = spawnOptions.runId;
const spawned = await (this.options.launcher ?? spawnOwnedProviderProcess)({
...spawnOptions,
cwd: this.#root.path,
});
let spawned: Awaited<ReturnType<typeof spawnOwnedProviderProcess>>;
try {
spawned = await (this.options.launcher ?? spawnOwnedProviderProcess)({
...spawnOptions,
cwd: this.#root.path,
});
} catch (cause: unknown) {
this.#launchFailure = { cause };
throw cause;
}
this.#inputFailure = undefined;
this.#observeStdin(spawned.process.stdin);
this.#cleanupEpoch.setRunId(spawned.ownership?.runId ?? spawnOptions.runId);