mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
fix(inspector): retain captures through authorization cleanup failures
This commit is contained in:
1 parent
fc404f008d
commit
09b188bb0d
3 files changed
+62
-28
No files matched your search
@@ -147,6 +147,10 @@ Inspector does not authenticate the operating-system PID or distinguish an
|
||||
Electron role, so Evidence records the role authority as
|
||||
`caller-declared-unverified`.
|
||||
|
||||
If final script-location authorization fails, collected script and context facts
|
||||
remain available in the typed failure with explicitly unverified locations.
|
||||
Descriptor cleanup status and its retry owner remain independent of that capture.
|
||||
|
||||
Electron main, preload, and renderer behavior can therefore be observed only
|
||||
as separate Inspector targets. The provider does not infer that two
|
||||
targets belong to the same Electron application.
|
||||
|
||||
@@ -73,6 +73,10 @@ vi.mock("node:fs/promises", async (importOriginal) => {
|
||||
import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js";
|
||||
import { AnalysisCancelledError } from "../domain/analysisErrorCore.js";
|
||||
import { CdpElectronProvider } from "./CdpElectronProvider.js";
|
||||
import { V8InspectorProvider } from "../inspector/V8InspectorProvider.js";
|
||||
import { observeJavaScriptRuntime } from "../application/javascript/JavaScriptRuntimeObservationService.js";
|
||||
import { startFakeV8Inspector } from "../../tests/fixtures/inspector/fakeV8Inspector.js";
|
||||
import { prependAsarJsonWhitespace } from "../../tests/fixtures/asarHeader.js";
|
||||
import {
|
||||
authorizedElectronFile,
|
||||
mapElectronFileAuthorizationFailure,
|
||||
@@ -120,6 +124,50 @@ const asarPage = async (): Promise<string> => {
|
||||
return pathToFileURL(join(archive, "index.html")).href;
|
||||
};
|
||||
|
||||
it("retains Inspector scripts and contexts when final ASAR authorization cannot close its descriptor", async () => {
|
||||
const scriptUrl = await asarPage();
|
||||
const archive = handleState.archive;
|
||||
if (archive === undefined) throw new Error("Missing fixture ASAR");
|
||||
const inspector = await startFakeV8Inspector({
|
||||
targetUrl: "https://example.test/entry.js",
|
||||
scriptUrls: [scriptUrl],
|
||||
});
|
||||
const provider = new V8InspectorProvider();
|
||||
const input = {
|
||||
inspector_endpoint: inspector.endpoint,
|
||||
target_id: inspector.targetId,
|
||||
observation_ms: 0,
|
||||
};
|
||||
try {
|
||||
const control = await observeJavaScriptRuntime(provider, input);
|
||||
expect(control.ok).toBe(true);
|
||||
if (!control.ok) throw control.error;
|
||||
expect(control.value.normalized_result).toMatchObject({
|
||||
scripts: { observed_total: 1 },
|
||||
execution_contexts: [{ name: "main" }],
|
||||
});
|
||||
await prependAsarJsonWhitespace(archive, " ");
|
||||
handleState.failCloseCount = 1;
|
||||
const result = await observeJavaScriptRuntime(provider, input);
|
||||
expect(result.ok).toBe(false);
|
||||
if (result.ok) throw new Error("Expected descriptor close failure");
|
||||
expect(result.error.cleanupIncomplete).toBe(true);
|
||||
expect(result.error.partialObservation).toMatchObject({
|
||||
scripts: {
|
||||
observed_total: 1,
|
||||
items: [{ location: { kind: "unresolved", reported_url: scriptUrl } }],
|
||||
},
|
||||
execution_contexts: [{ name: "main" }],
|
||||
});
|
||||
await provider.close();
|
||||
expect(result.error.cleanupIncomplete).toBe(false);
|
||||
} finally {
|
||||
handleState.failCloseCount = 0;
|
||||
await provider.close();
|
||||
await inspector.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("retains a failed ASAR close through denial and retries before reopening", async () => {
|
||||
const location = await asarPage();
|
||||
const missing = pathToFileURL(
|
||||
|
||||
@@ -410,13 +410,11 @@ export class V8InspectorProvider implements JavaScriptRuntimeObservationPort {
|
||||
});
|
||||
outcome = ok(javascriptRuntimeObservationSchema.parse(result));
|
||||
} catch (cause: unknown) {
|
||||
let error = providerError(cause, "observe_javascript_runtime");
|
||||
const error = providerError(cause, "observe_javascript_runtime");
|
||||
if (
|
||||
state !== undefined &&
|
||||
discovery !== undefined &&
|
||||
target !== undefined &&
|
||||
(cause instanceof BrowserObservationError ||
|
||||
cause instanceof AnalysisCancelledError)
|
||||
target !== undefined
|
||||
) {
|
||||
try {
|
||||
const partialObservation = await finalizeInspectorCapture({
|
||||
@@ -427,35 +425,19 @@ export class V8InspectorProvider implements JavaScriptRuntimeObservationPort {
|
||||
state,
|
||||
locationMode: "reported",
|
||||
});
|
||||
if (cause instanceof AnalysisCancelledError) {
|
||||
cause.retainPartialObservation(partialObservation);
|
||||
error = cause;
|
||||
} else {
|
||||
error = new BrowserObservationError(
|
||||
"observe_javascript_runtime",
|
||||
cause.reason,
|
||||
{
|
||||
cause: error,
|
||||
...(error.userMessage === undefined
|
||||
? {}
|
||||
: { detail: error.userMessage }),
|
||||
partialObservation,
|
||||
},
|
||||
);
|
||||
}
|
||||
error.retainPartialObservation(partialObservation);
|
||||
} catch (projectionFailure: unknown) {
|
||||
if (!(cause instanceof AnalysisCancelledError))
|
||||
error = new ProviderAdapterError(
|
||||
error.retainSecondaryFailure(
|
||||
new ProviderAdapterError(
|
||||
V8_INSPECTOR_PROVIDER_IDENTITY.id,
|
||||
"observe_javascript_runtime",
|
||||
{
|
||||
cause: new AggregateError(
|
||||
[error, projectionFailure],
|
||||
"Inspector failure and partial-observation projection both failed",
|
||||
),
|
||||
userMessage: `${error.message} Partial observations could not be projected.`,
|
||||
cause: projectionFailure,
|
||||
userMessage:
|
||||
"Inspector partial observations could not be projected.",
|
||||
},
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
outcome = err(error);
|
||||
|
||||
Reference in new issue
Block a user