@jobshimo/browser-link 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,16 +3,32 @@ export interface PeerProcess {
3
3
  binaryName: string;
4
4
  }
5
5
  /**
6
- * Identify which OS process owns the local end of a loopback TCP connection
7
- * at `host:port`. Returns null when the lookup cannot resolve a single owner
6
+ * Identify which OS process owns the LOCAL end of a TCP connection at
7
+ * `host:port`. Returns null when the lookup cannot resolve a single owner
8
8
  * (no result, ambiguous result, command not available, timeout).
9
9
  *
10
10
  * The caller decides how to treat null: this module's only job is to ask the
11
11
  * kernel honestly. Concretely the server treats null as "reject", so users
12
12
  * on systems without lsof / netstat fail closed.
13
+ *
14
+ * Defense in depth: even if the OS lookup somehow points at our own process,
15
+ * we return null. The auth path must never identify the server as its own
16
+ * peer — that would short-circuit the allowlist check.
13
17
  */
14
18
  export declare function lookupPeerProcess(host: string, port: number): Promise<PeerProcess | null>;
15
- export declare function parseLsofOutput(out: string): PeerProcess | null;
19
+ /**
20
+ * Parse `lsof -F pcn` output and return the unique process whose LOCAL
21
+ * endpoint equals `host:port`. Returns null on:
22
+ * - empty output
23
+ * - no entry matching the local endpoint
24
+ * - two or more distinct PIDs claiming the same local endpoint (fail closed)
25
+ * - malformed records (missing pid/command/name)
26
+ *
27
+ * Output shape: every process group starts with `p<pid>`, followed by
28
+ * `c<command>` (one), then one or more `n<local>-><remote>` lines (one per
29
+ * matching socket). Names can contain spaces; lsof escapes them as `\xHH`.
30
+ */
31
+ export declare function parseLsofOutput(out: string, host: string, port: number): PeerProcess | null;
16
32
  /** lsof escapes spaces and tabs in command names as \xHH. Reverse that. */
17
33
  export declare function decodeLsofString(s: string): string;
18
34
  export declare function parseNetstatForLocal(out: string, host: string, port: number): number | null;
@@ -4,53 +4,111 @@ import { promisify } from 'node:util';
4
4
  const execAsync = promisify(exec);
5
5
  const LOOKUP_TIMEOUT_MS = 1500;
6
6
  /**
7
- * Identify which OS process owns the local end of a loopback TCP connection
8
- * at `host:port`. Returns null when the lookup cannot resolve a single owner
7
+ * Identify which OS process owns the LOCAL end of a TCP connection at
8
+ * `host:port`. Returns null when the lookup cannot resolve a single owner
9
9
  * (no result, ambiguous result, command not available, timeout).
10
10
  *
11
11
  * The caller decides how to treat null: this module's only job is to ask the
12
12
  * kernel honestly. Concretely the server treats null as "reject", so users
13
13
  * on systems without lsof / netstat fail closed.
14
+ *
15
+ * Defense in depth: even if the OS lookup somehow points at our own process,
16
+ * we return null. The auth path must never identify the server as its own
17
+ * peer — that would short-circuit the allowlist check.
14
18
  */
15
19
  export async function lookupPeerProcess(host, port) {
16
20
  const os = platform();
21
+ let result;
17
22
  try {
18
23
  if (os === 'darwin' || os === 'linux') {
19
- return await lookupUnix(host, port);
24
+ result = await lookupUnix(host, port);
20
25
  }
21
- if (os === 'win32') {
22
- return await lookupWindows(host, port);
26
+ else if (os === 'win32') {
27
+ result = await lookupWindows(host, port);
28
+ }
29
+ else {
30
+ return null;
23
31
  }
24
- return null;
25
32
  }
26
33
  catch {
27
34
  return null;
28
35
  }
36
+ if (result && result.pid === process.pid)
37
+ return null;
38
+ return result;
29
39
  }
30
40
  /**
31
- * macOS / Linux path: `lsof` is the broadest tool available. The -F flag
32
- * gives us a stable, field-tagged output (one field per line, prefixed with
33
- * the field letter) that survives binary names containing spaces common
34
- * on macOS ("Google Chrome Helper").
41
+ * macOS / Linux path: `lsof` is the broadest tool available.
42
+ *
43
+ * The `-F pcn` flag asks for a stable, field-tagged output (one field per
44
+ * line, prefixed with the field letter): p=PID, c=command, n=NAME (which for
45
+ * TCP sockets is "local->remote"). We need the NAME because `-i @host:port`
46
+ * matches BOTH ends of any socket that touches host:port — and on loopback
47
+ * (peer and server both on 127.0.0.1) that means lsof returns both ends of
48
+ * the same connection. The parser then keeps only entries whose LOCAL side
49
+ * is host:port — the unambiguous peer.
50
+ *
51
+ * The Windows path (`parseNetstatForLocal`) applies the same local-endpoint
52
+ * filter; keep these two branches in sync.
35
53
  */
36
54
  async function lookupUnix(host, port) {
37
- const cmd = `lsof -nP -F pc -iTCP@${host}:${port} -sTCP:ESTABLISHED`;
55
+ const cmd = `lsof -nP -F pcn -iTCP@${host}:${port} -sTCP:ESTABLISHED`;
38
56
  const { stdout } = await execAsync(cmd, { timeout: LOOKUP_TIMEOUT_MS });
39
- return parseLsofOutput(stdout);
57
+ return parseLsofOutput(stdout, host, port);
40
58
  }
41
- export function parseLsofOutput(out) {
42
- let pid = null;
59
+ /**
60
+ * Parse `lsof -F pcn` output and return the unique process whose LOCAL
61
+ * endpoint equals `host:port`. Returns null on:
62
+ * - empty output
63
+ * - no entry matching the local endpoint
64
+ * - two or more distinct PIDs claiming the same local endpoint (fail closed)
65
+ * - malformed records (missing pid/command/name)
66
+ *
67
+ * Output shape: every process group starts with `p<pid>`, followed by
68
+ * `c<command>` (one), then one or more `n<local>-><remote>` lines (one per
69
+ * matching socket). Names can contain spaces; lsof escapes them as `\xHH`.
70
+ */
71
+ export function parseLsofOutput(out, host, port) {
72
+ const target = `${host}:${port}`;
73
+ const owners = new Map();
74
+ let curPid = null;
75
+ let curName = null;
43
76
  for (const line of out.split('\n')) {
44
- if (line.startsWith('p')) {
45
- const n = Number.parseInt(line.slice(1), 10);
46
- pid = Number.isFinite(n) ? n : null;
77
+ if (line.length === 0)
78
+ continue;
79
+ const tag = line[0];
80
+ const rest = line.slice(1);
81
+ if (tag === 'p') {
82
+ const n = Number.parseInt(rest, 10);
83
+ curPid = Number.isFinite(n) ? n : null;
84
+ curName = null;
85
+ continue;
86
+ }
87
+ if (tag === 'c') {
88
+ curName = decodeLsofString(rest);
47
89
  continue;
48
90
  }
49
- if (line.startsWith('c') && pid !== null) {
50
- return { pid, binaryName: decodeLsofString(line.slice(1)) };
91
+ if (tag === 'n' && curPid !== null && curName !== null) {
92
+ const arrowIdx = rest.indexOf('->');
93
+ if (arrowIdx < 0)
94
+ continue;
95
+ const local = rest.slice(0, arrowIdx);
96
+ if (local !== target)
97
+ continue;
98
+ const existing = owners.get(curPid);
99
+ if (existing === undefined)
100
+ owners.set(curPid, curName);
101
+ else if (existing !== curName) {
102
+ // Same pid reporting two different names within one dump is
103
+ // contradictory — bail rather than guess.
104
+ return null;
105
+ }
51
106
  }
52
107
  }
53
- return null;
108
+ if (owners.size !== 1)
109
+ return null;
110
+ const [[pid, binaryName]] = owners.entries();
111
+ return { pid, binaryName };
54
112
  }
55
113
  /** lsof escapes spaces and tabs in command names as \xHH. Reverse that. */
56
114
  export function decodeLsofString(s) {
@@ -60,6 +118,9 @@ export function decodeLsofString(s) {
60
118
  * Windows path: `netstat -ano` lists every TCP connection with its owning
61
119
  * PID. We then ask `tasklist` for the image name of that PID. Both ship by
62
120
  * default with Windows; no extra tooling needed.
121
+ *
122
+ * Like the UNIX path, this filters by LOCAL endpoint match — see
123
+ * `parseNetstatForLocal`. Keep the two branches in sync.
63
124
  */
64
125
  async function lookupWindows(host, port) {
65
126
  const { stdout: netstatOut } = await execAsync('netstat -ano -p TCP', {
@@ -1 +1 @@
1
- {"version":3,"file":"process-identity.js","sourceRoot":"","sources":["../../src/auth/process-identity.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process-identity.js","sourceRoot":"","sources":["../../src/auth/process-identity.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobshimo/browser-link",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "MCP server that bridges Claude Code, OpenCode, GitHub Copilot CLI and other MCP clients to a Chrome tab. Per-tool permissions, multi-agent mode (multiple MCP clients sharing one bridge), persistent UI map across sessions.",
5
5
  "keywords": [
6
6
  "mcp",