Skip to content

Commit

Permalink
feat(suite): connect popup display connecting process and web origin
Browse files Browse the repository at this point in the history
  • Loading branch information
martykan committed Nov 22, 2024
1 parent e07c9dd commit 4fa2a3e
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 3 deletions.
2 changes: 2 additions & 0 deletions packages/suite-desktop-api/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ export type ConnectPopupCall = {
id: number;
method: string;
payload: any;
processName?: string;
origin?: string;
};

export type ConnectPopupResponse = {
Expand Down
18 changes: 17 additions & 1 deletion packages/suite-desktop-core/src/libs/connect-ws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { createDeferred, Deferred } from '@trezor/utils';

import { createHttpReceiver } from './http-receiver';
import { Dependencies } from '../modules';
import { findProcessFromIncomingPort } from './find-process-from-port';

const LOG_PREFIX = 'connect-ws';

Expand All @@ -30,7 +31,20 @@ export const exposeConnectWs = ({
logger.info(LOG_PREFIX, 'Websocket server is listening');
});

wss.on('connection', ws => {
wss.on('connection', async (ws, req) => {
const ip = req.socket.remoteAddress;
const port = req.socket.remotePort;
if ((ip !== '127.0.0.1' && ip !== '::1') || !port) {
logger.error(LOG_PREFIX, `invalid connection attempt from ${ip}:${port}`);
ws.close();

return;
}
logger.info(LOG_PREFIX, `new connection from ${ip}:${port}`);
const processOnPort = await findProcessFromIncomingPort(port);
const { origin } = req.headers;
logger.info(LOG_PREFIX, `origin: ${origin}`);

ws.on('error', err => {
logger.error(LOG_PREFIX, err.message);
});
Expand Down Expand Up @@ -85,6 +99,8 @@ export const exposeConnectWs = ({
id: message.id,
method,
payload: rest,
origin,
processName: processOnPort?.name,
});

// wait for response
Expand Down
69 changes: 69 additions & 0 deletions packages/suite-desktop-core/src/libs/find-process-from-port.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { spawn } from 'child_process';

export function spawnAndCollectStdout(command: string): Promise<string> {
return new Promise((resolve, reject) => {
const child = spawn(command, { shell: true });
let stdout = '';
let stderr = '';
child.stdout.on('data', data => {
stdout += data.toString();
});
child.stderr.on('data', data => {
stderr += data.toString();
});
child.on('close', code => {
if (code !== 0) {
reject(new Error(`Command failed with code ${code}: ${stderr}`));
} else {
resolve(stdout);
}
});
});
}

export async function findProcessFromIncomingPort(port: number) {
switch (process.platform) {
case 'darwin':
case 'linux': {
const command = `lsof -iTCP:${port} -sTCP:ESTABLISHED -n -P +c0`;
const stdout = await spawnAndCollectStdout(command);
const lines = stdout.split('\n');
const process = lines.find(line => line.includes(`:${port}->`));
if (process) {
const name = process.split(/\s+/)[0].replace(/\\x\d{2}/g, ' ');
const pid = process.split(/\s+/)[1];
const user = process.split(/\s+/)[2];

return { name, pid, user };
}

return undefined;
}
case 'win32': {
const command = `netstat -ano | findstr :${port} | findstr ESTABLISHED`;
const stdout = await spawnAndCollectStdout(command);
const lines = stdout.split('\n');
const record = lines
.map(line => {
const parts = line.split(/\s+/);
const pid = parts[parts.length - 1];
const local = parts[2];

return { pid, local };
})
.find(({ local }) => local.endsWith(`:${port}`));
if (record) {
const processInfo = await spawnAndCollectStdout(
`tasklist /FI "PID eq ${record.pid}" | findstr ${record.pid}`,
);
const parts = processInfo.split(/\s+/);
const name = parts[0];
const user = parts[2];

return { name, pid: record.pid, user };
}

return undefined;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ import { Translation } from 'src/components/suite';

interface ConnectPopupModalProps {
method: string;
processName?: string;
origin?: string;
onConfirm: () => void;
onCancel: () => void;
}

export const ConnectPopupModal = ({ method, onConfirm, onCancel }: ConnectPopupModalProps) => {
export const ConnectPopupModal = ({
method,
processName,
origin,
onConfirm,
onCancel,
}: ConnectPopupModalProps) => {
return (
<NewModal
onCancel={onCancel}
Expand All @@ -28,6 +36,18 @@ export const ConnectPopupModal = ({ method, onConfirm, onCancel }: ConnectPopupM
heading={<Translation id="TR_TREZOR_CONNECT" />}
>
<H2>{method}</H2>

{processName && (
<Paragraph margin={{ top: spacings.xs }}>
Process: <strong>{processName}</strong>
</Paragraph>
)}
{origin && (
<Paragraph>
Web Origin: <strong>{origin}</strong>
</Paragraph>
)}

<Paragraph variant="tertiary" margin={{ top: spacings.xs }}>
A 3rd party application is trying to connect to your device. Do you want to allow
this action?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,11 @@ export const UserContextModal = ({
case 'connect-popup':
return (
<ConnectPopupModal
onCancel={onCancel}
onCancel={payload.onCancel}
onConfirm={payload.onConfirm}
method={payload.method}
origin={payload.origin}
processName={payload.processName}
/>
);
default:
Expand Down
7 changes: 7 additions & 0 deletions suite-common/connect-init/src/connectInitThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,14 @@ export const connectPopupCallThunk = createThunk(
id,
method,
payload,
processName,
origin,
}: {
id: number;
method: string;
payload: any;
processName?: string;
origin?: string;
},
{ dispatch, getState, extra },
) => {
Expand Down Expand Up @@ -210,8 +214,11 @@ export const connectPopupCallThunk = createThunk(
dispatch(
extra.actions.openModal({
type: 'connect-popup',
onCancel: () => confirmation.reject(ERRORS.TypedError('Method_Cancel')),
onConfirm: () => confirmation.resolve(),
method: methodInfo.payload.info,
processName,
origin,
}),
);
await confirmation.promise;
Expand Down
3 changes: 3 additions & 0 deletions suite-common/suite-types/src/modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,5 +187,8 @@ export type UserContextPayload =
| {
type: 'connect-popup';
onConfirm: () => void;
onCancel: () => void;
method: string;
processName?: string;
origin?: string;
};

0 comments on commit 4fa2a3e

Please sign in to comment.