mirror of
https://github.com/morluto/rea
synced 2026-10-25 22:08:11 -04:00
fix(frida): preserve session refusal and close execution effects
This commit is contained in:
@@ -244,6 +244,7 @@ export const TOOL_EFFECTS: Readonly<Record<string, ToolEffects>> = {
|
||||
mutatesSession: true,
|
||||
launchesProcess: true,
|
||||
accessesNetwork: true,
|
||||
executesForeignCode: true,
|
||||
idempotent: false,
|
||||
}),
|
||||
instrument_with_frida: effects({
|
||||
|
||||
@@ -588,10 +588,19 @@ export class FridaInstrumentationManager implements FridaInstrumentationPort {
|
||||
return err(unknownFridaSession("resume_frida_session", sessionId));
|
||||
if (managed.mode !== "spawn" || managed.targetResumeObserved)
|
||||
return err(
|
||||
new AnalysisCapabilityUnavailableError(
|
||||
"frida",
|
||||
new AnalysisInputError(
|
||||
"resume_frida_session",
|
||||
"Only a paused process spawned by this session can be resumed.",
|
||||
{
|
||||
userMessage:
|
||||
"Only a paused process spawned by this session can be resumed.",
|
||||
},
|
||||
[
|
||||
{
|
||||
path: ["session_id"],
|
||||
reason: "invalid_value",
|
||||
message: "Select a paused process spawned by this session.",
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
const pendingScripts = [...managed.scripts.values()].filter(
|
||||
|
||||
@@ -298,6 +298,26 @@ it.each(["attached", "during-resume", "after-resume"] as const)(
|
||||
|
||||
it("retains detached state and resume evidence when remote cleanup needs retry", async () => {
|
||||
const fixture = await connectedFixture();
|
||||
expect(
|
||||
(await fixture.client.listTools()).tools.find(
|
||||
(tool) => tool.name === "close_frida_session",
|
||||
)?.annotations?.destructiveHint,
|
||||
).toBe(true);
|
||||
const inventory = await fixture.client.callTool({
|
||||
name: "binary_session",
|
||||
arguments: {},
|
||||
});
|
||||
const availability = expandToolAvailability(
|
||||
toolContract("binary_session").outputSchema.parse(
|
||||
inventory.structuredContent,
|
||||
).result.tool_availability,
|
||||
);
|
||||
expect(
|
||||
availability.find((tool) => tool.name === "close_frida_session"),
|
||||
).toMatchObject({
|
||||
effects: { executesForeignCode: true },
|
||||
annotations: { destructive: true },
|
||||
});
|
||||
const started = await fixture.client.callTool({
|
||||
name: "start_frida_session",
|
||||
arguments: {
|
||||
@@ -1434,3 +1454,35 @@ it.each([0, 1, 2, 3, 4, 8])(
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["attached", "resumed"] as const)(
|
||||
"reports %s Frida resume refusal as selected session input",
|
||||
async (state) => {
|
||||
const fixture = await connectedFixture();
|
||||
const started =
|
||||
state === "attached"
|
||||
? await fixture.manager.startSession({ mode: "attach", pid: 512 })
|
||||
: await fixture.manager.startSession({
|
||||
mode: "spawn",
|
||||
program: "/fixture",
|
||||
});
|
||||
if (!started.ok) throw started.error;
|
||||
const id = started.value.session_id;
|
||||
if (state === "resumed")
|
||||
expect((await fixture.manager.resumeSession(id)).ok).toBe(true);
|
||||
const result = await fixture.client.callTool({
|
||||
name: "resume_frida_session",
|
||||
arguments: { session_id: id },
|
||||
});
|
||||
expect(result.isError).toBe(true);
|
||||
expect(parseMcpToolError(result)).toMatchObject({
|
||||
error: {
|
||||
code: "invalid_request",
|
||||
details: {
|
||||
issues: [{ path: ["session_id"], reason: "invalid_value" }],
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(fixture.controls.resumes).toBe(state === "resumed" ? 1 : 0);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user