fix: integrate 24 reviewed browser/native/semantics corrections (#815)

* fix(reference): resolve emitted extensions to TypeScript source

Signed-off-by: Rudy Celekli <rudy@gradiahq.com>

* test(reference): retain legitimate declaration import counterparts

Signed-off-by: Rudy Celekli <rudy@gradiahq.com>

* fix(mcp): recognize implicit form elicitation capability

Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com>

* fix(ghidra): reject contradictory NativeAOT reports

* fix(native): preserve thin architectures across echoed filename lines

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>

* fix(native): preserve app bundle executable filename whitespace

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>

* fix(doctor): validate Node-wrapped client policy

Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com>

* fix(reference): preserve observed entry failure reasons

Signed-off-by: Rudy Celekli <rudy@gradiahq.com>

* fix(artifacts): verify opaque bytes before reusing inventory identities

Signed-off-by: Rudy Celekli <rudy@gradiahq.com>

* fix(browser): resolve DOM destinations against document base URL

Signed-off-by: Rudy Celekli <rudy@gradiahq.com>

* fix: recover serialized native NIB view hierarchy

Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com>

* ci: exercise native compiled NIB hierarchy regressions

Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com>

* fix(deps): remediate reachable archive-parsing advisories

Two high-severity advisories are reachable from untrusted target content:

- @xmldom/xmldom 0.9.10 (via plist) parses XML plist bytes from analyzed
  app bundles in the keyed-archive reader. Refreshed to 0.9.12.
- brace-expansion 5.0.7 (via @electron/asar -> minimatch) is reached while
  analyzing untrusted ASAR archives. Refreshed to 5.0.12.

Both are in-range patch updates (plist declares ^0.9.10, minimatch declares
^5.0.5), so no source or API change is required and no direct dependency
declaration moves. Lockfile-only change.

The remaining smol-toml advisory is not included: it parses operator-owned
client configuration rather than untrusted target content, and its fix moves
outside the declared range and changes leading-BOM handling.

* fix(semantics): link wrapped arrow Promise returns

* fix(reconstruction): preserve unknown graph limitations

* fix(native): preserve exact code signing identifiers

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>

* fix(native): recognize only whole otool command headers

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>

* fix(native): retain nm-proven literal export names

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>

* fix(javascript): project decoded file URL paths

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>

* fix: preserve explicit source-relative URL observation spelling

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>

* fix(native): project Mach-O evidence into container file offsets

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>

* fix(browser): normalize referrer policy fallback lists

Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>

---------

Signed-off-by: Rudy Celekli <rudy@gradiahq.com>
Signed-off-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com>
Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com>
Co-authored-by: Rudy Celekli <rudy@gradiahq.com>
Co-authored-by: RudyCelekli <47457359+rudycelekli@users.noreply.github.com>
Co-authored-by: Kaoru0822 <aisakamimaki@gmail.com>
Co-authored-by: DeryFerd <deryferdikao125@gmail.com>
This commit is contained in:
authored and GitHub committed 2026-10-07 10:33:10 +08:00
1 parent 2572e1eb2f
commit 3af3811ab0
43 files changed
+2032 -82

No files matched your search

+46 -1
View File
@@ -14,6 +14,7 @@ import {
type ArtifactReader,
} from "../artifacts/ArtifactReader.js";
import { streamChunkToBuffer } from "../artifacts/StreamBytes.js";
import { hashReadable } from "./ArtifactInventory/hash.js";
import type { ArtifactInventorySnapshot } from "./ArtifactInventory.js";
/** Relevant file categories projected from the complete artifact inventory. */
@@ -89,6 +90,21 @@ export const readJavaScriptArtifactFiles = async (
snapshot: ArtifactInventorySnapshot,
signal?: AbortSignal,
): Promise<JavaScriptArtifactFileSet> => {
if (reader instanceof AsarArtifactReader) {
const root = snapshot.nodes.find(
({ artifact_id }) => artifact_id === snapshot.manifest.root_artifact_id,
);
if (root === undefined)
throw new ArtifactReaderFailure(
"integrity",
"Root artifact node is missing",
);
await verifyEntryBytes(
reader.openContainer(signal),
{ path: ".", sha256: root.sha256, bytes: root.size },
signal,
);
}
const inventory = expectedInventory(snapshot);
const expected = inventory.files;
const context: ReadContext = {
@@ -156,6 +172,11 @@ const visitReader = async (
context.registry.add(path, nestedAsar ? "directory" : entry.kind);
if (nestedAsar) {
const inventory = expectedContainer(path, context);
await verifyEntryBytes(
await frame.reader.open(entry, context.signal),
inventory,
context.signal,
);
context.containers.push(inventory);
const nested = new AsarArtifactReader(entry.adapterKey);
stack.push({
@@ -214,8 +235,14 @@ const readText = async (
context: ReadContext,
input: ReadTextInput,
): Promise<JavaScriptArtifactFile["text"]> => {
if (input.expected.kind === "native-addon")
if (input.expected.kind === "native-addon") {
await verifyEntryBytes(
await input.reader.open(input.entry, context.signal),
input.expected,
context.signal,
);
return { included: false, reason: "not-applicable" };
}
const bytes = await readAll(
await input.reader.open(input.entry, context.signal),
context.signal,
@@ -240,6 +267,24 @@ const readText = async (
}
};
const verifyEntryBytes = async (
stream: Readable,
expected: Pick<ExpectedFile, "path" | "sha256" | "bytes">,
signal?: AbortSignal,
): Promise<void> => {
try {
const digest = await hashReadable(stream, signal);
if (digest.sha256 !== expected.sha256 || digest.bytes !== expected.bytes)
throw new ArtifactReaderFailure(
"integrity",
`Artifact entry changed after inventory: ${expected.path}`,
);
} catch (cause: unknown) {
abortIfNeeded(signal);
throw cause;
}
};
const expectedInventory = (
snapshot: ArtifactInventorySnapshot,
): {