Skip to content

Commit 375e7c2

Browse files
committed
Expand API error handling and alert error details in Frida interception
1 parent b803f56 commit 375e7c2

File tree

5 files changed

+74
-21
lines changed

5 files changed

+74
-21
lines changed

src/components/intercept/config/frida-config.tsx

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { computed, observable, action, autorun, flow, runInAction } from 'mobx';
44
import { observer, inject, disposeOnUnmount } from 'mobx-react';
55

66
import { delay } from '../../../util/promise';
7-
import { UnreachableCheck } from '../../../util/error';
7+
import { ErrorLike, UnreachableCheck } from '../../../util/error';
88
import { styled } from '../../../styles';
99
import { Icon } from '../../../icons';
1010

@@ -16,6 +16,7 @@ import { RulesStore } from '../../../model/rules/rules-store';
1616
import { FridaActivationOptions, FridaHost, FridaTarget } from '../../../model/interception/frida';
1717

1818
import { getDetailedInterceptorMetadata } from '../../../services/server-api';
19+
import { ActivationFailure } from '../../../services/server-api-types';
1920

2021
import { TextInput } from '../../common/inputs';
2122
import { InterceptionTargetList } from './intercept-target-list';
@@ -162,6 +163,16 @@ const INCOMPATIBLE_APP_IDS: string[] = [
162163
"com.google.android.googlequicksearchbox"
163164
];
164165

166+
const alertActivationError = (action: string, error: ErrorLike) => {
167+
if (error instanceof ActivationFailure) {
168+
alert(`Failed to ${action}: ${error.failureMessage} (${error.errorCode})`);
169+
} else {
170+
alert(`Failed to ${action}: ${error.message ?? error}`);
171+
}
172+
173+
throw error;
174+
};
175+
165176
@inject('proxyStore')
166177
@inject('rulesStore')
167178
@inject('eventsStore')
@@ -319,7 +330,7 @@ class FridaConfig extends React.Component<{
319330
await this.props.activateInterceptor({
320331
action: 'setup',
321332
hostId
322-
});
333+
}).catch((e) => alertActivationError('setup Frida', e));
323334

324335
this.setHostProgress(hostId, 75);
325336
await this.launchInterceptor(hostId);
@@ -343,7 +354,7 @@ class FridaConfig extends React.Component<{
343354
await this.props.activateInterceptor({
344355
action: 'launch',
345356
hostId
346-
});
357+
}).catch((e) => alertActivationError('launch Frida', e));
347358

348359
this.setHostProgress(hostId, 100);
349360
await delay(10); // Tiny delay, purely for nice UI purposes
@@ -374,7 +385,9 @@ class FridaConfig extends React.Component<{
374385
action: 'intercept',
375386
hostId: host.id,
376387
targetId
377-
}).then(() => {
388+
})
389+
.catch((e) => alertActivationError(`intercept ${targetId}`, e))
390+
.then(() => {
378391
this.props.reportSuccess();
379392
}).finally(action(() => {
380393
_.pull(this.inProgressTargetIds, targetId);

src/components/send/send-page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ class SendPage extends React.Component<{
6969

7070
sendRequest(selectedRequest).catch(e => {
7171
console.log(e);
72-
const errorMessage = (e instanceof ApiError && e.apiErrorMessage)
73-
? e.apiErrorMessage
72+
const errorMessage = (e instanceof ApiError && e.apiError?.message)
73+
? e.apiError?.message
7474
: e.message ?? e;
7575
alert(errorMessage);
7676
});

src/services/server-api-types.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { NetworkInterfaceInfo } from 'os';
22
import { ProxySetting } from 'mockttp';
3+
import { ErrorLike } from '../util/error';
34

45
export interface ServerInterceptor {
56
id: string;
@@ -29,9 +30,39 @@ export class ApiError extends Error {
2930
message: string,
3031
readonly operationName: string,
3132
readonly errorCode?: string | number,
32-
public apiErrorMessage?: string
33+
public apiError?: {
34+
message?: string,
35+
code?: string
36+
}
3337
) {
3438
super(`API error during ${operationName}: ${message}`);
39+
if (apiError) {
40+
this.cause = new Error(apiError?.message ?? '[Unknown API error]');
41+
this.cause.code = apiError?.code ?? 'unknown';
42+
this.cause.stack = '(From server API)';
43+
}
3544
}
3645

46+
private cause?: ErrorLike;
47+
48+
}
49+
50+
export class ActivationFailure extends Error {
51+
constructor(
52+
readonly interceptorId: string,
53+
readonly failureMessage: string,
54+
readonly errorCode?: string,
55+
readonly cause?: ErrorLike
56+
) {
57+
super(`Failed to activate interceptor ${interceptorId}: ${failureMessage}`);
58+
}
59+
}
60+
61+
export class ActivationNonSuccess extends Error {
62+
constructor(
63+
readonly interceptorId: string,
64+
readonly metadata: unknown
65+
) {
66+
super(`Interceptor ${interceptorId} activation ran unsuccessfully`);
67+
}
3768
}

src/services/server-api.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
SERVER_REST_API_SUPPORTED
88
} from './service-versions';
99

10-
import { type ServerConfig, type NetworkInterfaces, type ServerInterceptor, ApiError } from './server-api-types';
10+
import { type ServerConfig, type NetworkInterfaces, type ServerInterceptor, ApiError, ActivationFailure, ActivationNonSuccess } from './server-api-types';
1111
export type { ServerConfig, NetworkInterfaces, ServerInterceptor };
1212

1313
import { GraphQLApiClient } from './server-graphql-api';
@@ -97,20 +97,29 @@ export async function getDetailedInterceptorMetadata<M extends unknown>(
9797
}
9898

9999
export async function activateInterceptor(id: string, proxyPort: number, options?: any): Promise<unknown> {
100-
const result = await (await apiClient).activateInterceptor(id, proxyPort, options);
100+
try {
101+
const result = await (await apiClient).activateInterceptor(id, proxyPort, options);
101102

102-
if (result.success) {
103-
return result.metadata;
104-
} else {
105-
// Some kind of failure:
106-
console.log('Activation result', JSON.stringify(result));
107-
108-
const error = Object.assign(
109-
new ApiError(`failed to activate interceptor ${id}`, `activate-interceptor-${id}`),
110-
result
111-
);
103+
if (result.success) {
104+
return result.metadata;
105+
} else {
106+
// Some kind of failure (either non-critical, or old server that returned all errors
107+
// like this without a 500):
108+
console.log('Activation result', JSON.stringify(result));
112109

113-
throw error;
110+
throw new ActivationNonSuccess(id, result.metadata);
111+
}
112+
} catch (e: any) {
113+
if (e instanceof ApiError) {
114+
throw new ActivationFailure(
115+
id,
116+
e.apiError?.message ?? `Failed to activate interceptor ${id}`,
117+
e.apiError?.code,
118+
e
119+
)
120+
} else {
121+
throw e;
122+
}
114123
}
115124
}
116125

src/services/server-rest-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export class RestApiClient {
8181
}`,
8282
operationName,
8383
response.status,
84-
errorMessage
84+
{ message: errorMessage, code: errorCode }
8585
);
8686
}
8787

0 commit comments

Comments
 (0)