porthawk-core 0.1.5 → 0.1.7

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.
package/dist/index.cjs CHANGED
@@ -32,6 +32,7 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  classifyOrigin: () => classifyOrigin,
34
34
  getListeningPorts: () => getListeningPorts,
35
+ isSystemProcess: () => isSystemProcess,
35
36
  killProcess: () => killProcess
36
37
  });
37
38
  module.exports = __toCommonJS(index_exports);
@@ -56,82 +57,102 @@ function classifyOrigin(processInfo) {
56
57
  }
57
58
  return "unknown";
58
59
  }
60
+ var systemProcessPattern = /^(System|System Idle Process|Registry|smss|csrss|wininit|winlogon|services|lsass|lsaiso|svchost|dwm|fontdrvhost|spoolsv|taskhostw|SearchIndexer|MsMpEng|WmiPrvSE|dllhost|conhost|RuntimeBroker|sihost|ctfmon|explorer|systemd|launchd|kernel_task|init|rpcbind|dbus-daemon|cupsd|mDNSResponder|coreaudiod|WindowServer|loginwindow|SystemUIServer|configd|syslogd|logind|cron|rsyslogd|NetworkManager|polkitd|udevd|avahi-daemon)(\.exe)?$/i;
61
+ function isSystemProcess(processName) {
62
+ return systemProcessPattern.test(processName);
63
+ }
59
64
 
60
65
  // src/detect.ts
61
66
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
67
+ var PROCESS_TABLE_MAX_BUFFER = 10 * 1024 * 1024;
62
68
  function isValidPid(pid) {
63
69
  return typeof pid === "number" && Number.isInteger(pid) && pid > 0;
64
70
  }
65
71
  function normalizeProtocol(raw) {
66
72
  return raw.toLowerCase().startsWith("udp") ? "udp" : "tcp";
67
73
  }
68
- async function getUnixProcessInfo(pid) {
69
- const { stdout } = await execFileAsync("ps", ["-p", String(pid), "-o", "ppid=,comm=,args="]);
70
- const line = stdout.trim();
71
- const [ppidToken, commToken, ...rest] = line.split(/\s+/);
72
- const processName = commToken ?? "";
73
- const command = rest.length > 0 ? rest.join(" ") : processName;
74
- const ppid = Number(ppidToken);
75
- let parentName = "";
76
- if (isValidPid(ppid)) {
77
- try {
78
- const { stdout: parentStdout } = await execFileAsync("ps", ["-p", String(ppid), "-o", "comm="]);
79
- parentName = parentStdout.trim();
80
- } catch {
81
- parentName = "";
82
- }
74
+ async function queryAllUnixProcesses() {
75
+ const { stdout } = await execFileAsync("ps", ["-A", "-o", "pid=,ppid=,comm=,args="], {
76
+ maxBuffer: PROCESS_TABLE_MAX_BUFFER
77
+ });
78
+ const table = /* @__PURE__ */ new Map();
79
+ for (const line of stdout.split("\n")) {
80
+ const trimmed = line.trim();
81
+ if (!trimmed) continue;
82
+ const [pidToken, ppidToken, commToken, ...rest] = trimmed.split(/\s+/);
83
+ const pid = Number(pidToken);
84
+ if (!isValidPid(pid)) continue;
85
+ const comm = commToken ?? "";
86
+ table.set(pid, {
87
+ ppid: Number(ppidToken),
88
+ comm,
89
+ args: rest.length > 0 ? rest.join(" ") : comm
90
+ });
83
91
  }
84
- return { pid, processName, command, parentName };
92
+ return table;
93
+ }
94
+ function resolveUnixProcessInfo(pid, table) {
95
+ const entry = table.get(pid);
96
+ if (!entry) {
97
+ throw new Error(`No process found for pid ${pid}`);
98
+ }
99
+ const parentEntry = isValidPid(entry.ppid) ? table.get(entry.ppid) : void 0;
100
+ return {
101
+ pid,
102
+ processName: entry.comm,
103
+ command: entry.args,
104
+ parentName: parentEntry?.comm ?? ""
105
+ };
85
106
  }
86
- async function queryWindowsProcess(pid) {
87
- const script = `Get-CimInstance Win32_Process -Filter "ProcessId=${pid}" | Select-Object Name,CommandLine,ParentProcessId | ConvertTo-Json -Compress`;
88
- const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script]);
107
+ async function queryAllWindowsProcesses() {
108
+ const script = "Get-CimInstance Win32_Process | Select-Object ProcessId,Name,CommandLine,ParentProcessId | ConvertTo-Json -Compress";
109
+ const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], {
110
+ maxBuffer: PROCESS_TABLE_MAX_BUFFER
111
+ });
89
112
  const trimmed = stdout.trim();
90
- if (!trimmed) return void 0;
91
- return JSON.parse(trimmed);
113
+ if (!trimmed) return /* @__PURE__ */ new Map();
114
+ const parsed = JSON.parse(trimmed);
115
+ const records = Array.isArray(parsed) ? parsed : [parsed];
116
+ const table = /* @__PURE__ */ new Map();
117
+ for (const record of records) {
118
+ if (isValidPid(record.ProcessId)) {
119
+ table.set(record.ProcessId, record);
120
+ }
121
+ }
122
+ return table;
92
123
  }
93
- async function getWindowsProcessInfo(pid) {
94
- const record = await queryWindowsProcess(pid);
124
+ function resolveWindowsProcessInfo(pid, table) {
125
+ const record = table.get(pid);
95
126
  if (!record) {
96
127
  throw new Error(`No process found for pid ${pid}`);
97
128
  }
98
- let parentName = "";
99
- if (isValidPid(record.ParentProcessId)) {
100
- try {
101
- const parentRecord = await queryWindowsProcess(record.ParentProcessId);
102
- parentName = parentRecord?.Name ?? "";
103
- } catch {
104
- parentName = "";
105
- }
106
- }
129
+ const parentRecord = isValidPid(record.ParentProcessId) ? table.get(record.ParentProcessId) : void 0;
107
130
  return {
108
131
  pid,
109
132
  processName: record.Name ?? "",
110
133
  command: record.CommandLine ?? record.Name ?? "",
111
- parentName
134
+ parentName: parentRecord?.Name ?? ""
112
135
  };
113
136
  }
114
- async function getProcessInfo(pid) {
115
- return process.platform === "win32" ? getWindowsProcessInfo(pid) : getUnixProcessInfo(pid);
116
- }
117
137
  async function getListeningPorts() {
118
138
  const connections = await si.networkConnections();
119
139
  const listening = connections.filter(
120
140
  (conn) => conn.state?.toUpperCase() === "LISTEN" && isValidPid(conn.pid)
121
141
  );
122
- const processInfoByPid = /* @__PURE__ */ new Map();
142
+ if (listening.length === 0) {
143
+ return [];
144
+ }
145
+ const isWindows = process.platform === "win32";
146
+ const processTable = isWindows ? await queryAllWindowsProcesses() : await queryAllUnixProcesses();
123
147
  const result = [];
124
148
  for (const conn of listening) {
125
149
  const pid = conn.pid;
126
- if (!processInfoByPid.has(pid)) {
127
- try {
128
- processInfoByPid.set(pid, await getProcessInfo(pid));
129
- } catch {
130
- processInfoByPid.set(pid, null);
131
- }
150
+ let processInfo;
151
+ try {
152
+ processInfo = isWindows ? resolveWindowsProcessInfo(pid, processTable) : resolveUnixProcessInfo(pid, processTable);
153
+ } catch {
154
+ continue;
132
155
  }
133
- const processInfo = processInfoByPid.get(pid);
134
- if (!processInfo) continue;
135
156
  const port = Number(conn.localPort);
136
157
  if (!Number.isInteger(port)) continue;
137
158
  result.push({
@@ -173,6 +194,7 @@ async function killProcess(pid) {
173
194
  0 && (module.exports = {
174
195
  classifyOrigin,
175
196
  getListeningPorts,
197
+ isSystemProcess,
176
198
  killProcess
177
199
  });
178
200
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/detect.ts","../src/classify.ts","../src/kill.ts"],"sourcesContent":["export { getListeningPorts } from './detect.js';\nexport { killProcess } from './kill.js';\nexport { classifyOrigin } from './classify.js';\nexport type { PortInfo, ProcessInfo, Protocol, Origin, ClassifyRule } from './types.js';\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport * as si from 'systeminformation';\nimport { classifyOrigin } from './classify.js';\nimport type { PortInfo, Protocol, ProcessInfo } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\nfunction isValidPid(pid: unknown): pid is number {\n return typeof pid === 'number' && Number.isInteger(pid) && pid > 0;\n}\n\nfunction normalizeProtocol(raw: string): Protocol {\n return raw.toLowerCase().startsWith('udp') ? 'udp' : 'tcp';\n}\n\nasync function getUnixProcessInfo(pid: number): Promise<ProcessInfo> {\n const { stdout } = await execFileAsync('ps', ['-p', String(pid), '-o', 'ppid=,comm=,args=']);\n const line = stdout.trim();\n const [ppidToken, commToken, ...rest] = line.split(/\\s+/);\n const processName = commToken ?? '';\n const command = rest.length > 0 ? rest.join(' ') : processName;\n const ppid = Number(ppidToken);\n\n let parentName = '';\n if (isValidPid(ppid)) {\n try {\n const { stdout: parentStdout } = await execFileAsync('ps', ['-p', String(ppid), '-o', 'comm=']);\n parentName = parentStdout.trim();\n } catch {\n parentName = '';\n }\n }\n\n return { pid, processName, command, parentName };\n}\n\ninterface WindowsProcessRecord {\n Name?: string;\n CommandLine?: string;\n ParentProcessId?: number;\n}\n\nasync function queryWindowsProcess(pid: number): Promise<WindowsProcessRecord | undefined> {\n const script = `Get-CimInstance Win32_Process -Filter \"ProcessId=${pid}\" | Select-Object Name,CommandLine,ParentProcessId | ConvertTo-Json -Compress`;\n const { stdout } = await execFileAsync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script]);\n const trimmed = stdout.trim();\n if (!trimmed) return undefined;\n return JSON.parse(trimmed) as WindowsProcessRecord;\n}\n\nasync function getWindowsProcessInfo(pid: number): Promise<ProcessInfo> {\n const record = await queryWindowsProcess(pid);\n if (!record) {\n throw new Error(`No process found for pid ${pid}`);\n }\n\n let parentName = '';\n if (isValidPid(record.ParentProcessId)) {\n try {\n const parentRecord = await queryWindowsProcess(record.ParentProcessId);\n parentName = parentRecord?.Name ?? '';\n } catch {\n parentName = '';\n }\n }\n\n return {\n pid,\n processName: record.Name ?? '',\n command: record.CommandLine ?? record.Name ?? '',\n parentName,\n };\n}\n\nasync function getProcessInfo(pid: number): Promise<ProcessInfo> {\n return process.platform === 'win32' ? getWindowsProcessInfo(pid) : getUnixProcessInfo(pid);\n}\n\nexport async function getListeningPorts(): Promise<PortInfo[]> {\n const connections = await si.networkConnections();\n const listening = connections.filter(\n (conn) => conn.state?.toUpperCase() === 'LISTEN' && isValidPid(conn.pid),\n );\n\n const processInfoByPid = new Map<number, ProcessInfo | null>();\n\n const result: PortInfo[] = [];\n\n for (const conn of listening) {\n const pid = conn.pid;\n\n if (!processInfoByPid.has(pid)) {\n try {\n processInfoByPid.set(pid, await getProcessInfo(pid));\n } catch {\n processInfoByPid.set(pid, null);\n }\n }\n\n const processInfo = processInfoByPid.get(pid);\n if (!processInfo) continue;\n\n const port = Number(conn.localPort);\n if (!Number.isInteger(port)) continue;\n\n result.push({\n port,\n pid,\n protocol: normalizeProtocol(conn.protocol),\n processName: processInfo.processName,\n command: processInfo.command,\n origin: classifyOrigin(processInfo),\n });\n }\n\n const seen = new Set<string>();\n return result.filter((entry) => {\n const key = `${entry.pid}:${entry.port}:${entry.protocol}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n","import type { ClassifyRule, Origin, ProcessInfo } from './types.js';\n\nconst agentHostPattern = /^(code|code-oss|cursor|windsurf)(\\.exe|\\s.*)?$/i;\n\nconst rules: ClassifyRule[] = [\n { namePattern: /claude/i, origin: 'agent' },\n { namePattern: /^(node|python|python3|deno|bun)(\\.exe)?$/i, parentPattern: agentHostPattern, origin: 'agent' },\n { namePattern: /^(bash|zsh|sh|fish|powershell|pwsh|cmd)(\\.exe)?$/i, parentPattern: agentHostPattern, origin: 'agent' },\n];\n\nexport function classifyOrigin(processInfo: ProcessInfo): Origin {\n for (const rule of rules) {\n if (!rule.namePattern.test(processInfo.processName)) continue;\n if (rule.parentPattern && !rule.parentPattern.test(processInfo.parentName)) continue;\n return rule.origin;\n }\n return 'unknown';\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nfunction assertValidPid(pid: number): void {\n if (!Number.isInteger(pid) || pid <= 0) {\n throw new Error(`Invalid pid: ${pid}`);\n }\n}\n\nexport async function killProcess(pid: number): Promise<void> {\n assertValidPid(pid);\n\n if (process.platform === 'win32') {\n await execFileAsync('taskkill', ['/PID', String(pid), '/F']);\n return;\n }\n\n process.kill(pid);\n}\n"],"mappings":"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","names":["import_node_child_process","import_node_util","execFileAsync"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/detect.ts","../src/classify.ts","../src/kill.ts"],"sourcesContent":["export { getListeningPorts } from './detect.js';\nexport { killProcess } from './kill.js';\nexport { classifyOrigin, isSystemProcess } from './classify.js';\nexport type { PortInfo, ProcessInfo, Protocol, Origin, ClassifyRule } from './types.js';\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport * as si from 'systeminformation';\nimport { classifyOrigin } from './classify.js';\nimport type { PortInfo, Protocol, ProcessInfo } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\n// Full process tables can be a few hundred KB of JSON/text on a busy\n// machine — well past Node's 1MB default maxBuffer for exec/execFile.\nconst PROCESS_TABLE_MAX_BUFFER = 10 * 1024 * 1024;\n\nfunction isValidPid(pid: unknown): pid is number {\n return typeof pid === 'number' && Number.isInteger(pid) && pid > 0;\n}\n\nfunction normalizeProtocol(raw: string): Protocol {\n return raw.toLowerCase().startsWith('udp') ? 'udp' : 'tcp';\n}\n\ninterface UnixProcessRecord {\n ppid: number;\n comm: string;\n args: string;\n}\n\n// One \"ps\" call for the whole process table, instead of one call per pid\n// (plus one more per parent pid) — the earlier per-pid version re-shelled\n// out for every unique pid on every refresh, which is the dominant cost\n// on a machine with many listening ports.\nasync function queryAllUnixProcesses(): Promise<Map<number, UnixProcessRecord>> {\n const { stdout } = await execFileAsync('ps', ['-A', '-o', 'pid=,ppid=,comm=,args='], {\n maxBuffer: PROCESS_TABLE_MAX_BUFFER,\n });\n\n const table = new Map<number, UnixProcessRecord>();\n for (const line of stdout.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n const [pidToken, ppidToken, commToken, ...rest] = trimmed.split(/\\s+/);\n const pid = Number(pidToken);\n if (!isValidPid(pid)) continue;\n\n const comm = commToken ?? '';\n table.set(pid, {\n ppid: Number(ppidToken),\n comm,\n args: rest.length > 0 ? rest.join(' ') : comm,\n });\n }\n return table;\n}\n\nfunction resolveUnixProcessInfo(pid: number, table: Map<number, UnixProcessRecord>): ProcessInfo {\n const entry = table.get(pid);\n if (!entry) {\n throw new Error(`No process found for pid ${pid}`);\n }\n\n const parentEntry = isValidPid(entry.ppid) ? table.get(entry.ppid) : undefined;\n\n return {\n pid,\n processName: entry.comm,\n command: entry.args,\n parentName: parentEntry?.comm ?? '',\n };\n}\n\ninterface WindowsProcessRecord {\n ProcessId?: number;\n Name?: string;\n CommandLine?: string;\n ParentProcessId?: number;\n}\n\n// Same batching principle as the Unix path: one Get-CimInstance call for\n// every process, instead of a fresh powershell.exe spawn per pid (and\n// another per parent pid) — powershell.exe's own startup overhead, not\n// the WMI query itself, was what made refreshes slow with many ports open.\nasync function queryAllWindowsProcesses(): Promise<Map<number, WindowsProcessRecord>> {\n const script =\n 'Get-CimInstance Win32_Process | ' +\n 'Select-Object ProcessId,Name,CommandLine,ParentProcessId | ' +\n 'ConvertTo-Json -Compress';\n const { stdout } = await execFileAsync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], {\n maxBuffer: PROCESS_TABLE_MAX_BUFFER,\n });\n\n const trimmed = stdout.trim();\n if (!trimmed) return new Map();\n\n const parsed: unknown = JSON.parse(trimmed);\n // ConvertTo-Json emits a bare object instead of a one-element array\n // when the pipeline only produced a single result.\n const records = (Array.isArray(parsed) ? parsed : [parsed]) as WindowsProcessRecord[];\n\n const table = new Map<number, WindowsProcessRecord>();\n for (const record of records) {\n if (isValidPid(record.ProcessId)) {\n table.set(record.ProcessId, record);\n }\n }\n return table;\n}\n\nfunction resolveWindowsProcessInfo(pid: number, table: Map<number, WindowsProcessRecord>): ProcessInfo {\n const record = table.get(pid);\n if (!record) {\n throw new Error(`No process found for pid ${pid}`);\n }\n\n const parentRecord = isValidPid(record.ParentProcessId) ? table.get(record.ParentProcessId) : undefined;\n\n return {\n pid,\n processName: record.Name ?? '',\n command: record.CommandLine ?? record.Name ?? '',\n parentName: parentRecord?.Name ?? '',\n };\n}\n\nexport async function getListeningPorts(): Promise<PortInfo[]> {\n const connections = await si.networkConnections();\n const listening = connections.filter(\n (conn) => conn.state?.toUpperCase() === 'LISTEN' && isValidPid(conn.pid),\n );\n\n if (listening.length === 0) {\n return [];\n }\n\n const isWindows = process.platform === 'win32';\n const processTable = isWindows ? await queryAllWindowsProcesses() : await queryAllUnixProcesses();\n\n const result: PortInfo[] = [];\n\n for (const conn of listening) {\n const pid = conn.pid;\n\n let processInfo: ProcessInfo;\n try {\n processInfo = isWindows\n ? resolveWindowsProcessInfo(pid, processTable as Map<number, WindowsProcessRecord>)\n : resolveUnixProcessInfo(pid, processTable as Map<number, UnixProcessRecord>);\n } catch {\n // Process exited between the port scan and building the process\n // table — it isn't meaningfully \"listening\" anymore, so drop it\n // rather than surface it as unknown.\n continue;\n }\n\n const port = Number(conn.localPort);\n if (!Number.isInteger(port)) continue;\n\n result.push({\n port,\n pid,\n protocol: normalizeProtocol(conn.protocol),\n processName: processInfo.processName,\n command: processInfo.command,\n origin: classifyOrigin(processInfo),\n });\n }\n\n const seen = new Set<string>();\n return result.filter((entry) => {\n const key = `${entry.pid}:${entry.port}:${entry.protocol}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n","import type { ClassifyRule, Origin, ProcessInfo } from './types.js';\n\nconst agentHostPattern = /^(code|code-oss|cursor|windsurf)(\\.exe|\\s.*)?$/i;\n\nconst rules: ClassifyRule[] = [\n { namePattern: /claude/i, origin: 'agent' },\n { namePattern: /^(node|python|python3|deno|bun)(\\.exe)?$/i, parentPattern: agentHostPattern, origin: 'agent' },\n { namePattern: /^(bash|zsh|sh|fish|powershell|pwsh|cmd)(\\.exe)?$/i, parentPattern: agentHostPattern, origin: 'agent' },\n];\n\nexport function classifyOrigin(processInfo: ProcessInfo): Origin {\n for (const rule of rules) {\n if (!rule.namePattern.test(processInfo.processName)) continue;\n if (rule.parentPattern && !rule.parentPattern.test(processInfo.parentName)) continue;\n return rule.origin;\n }\n return 'unknown';\n}\n\n// Well-known OS/service process names — used only to declutter a display\n// (e.g. the VS Code sidebar's \"hide system processes\" setting), never to\n// change what getListeningPorts() actually detects. A process spoofing one\n// of these names would be just as invisible to a plain \"netstat\" read, so\n// this is a display convenience, not a security boundary.\nconst systemProcessPattern =\n /^(System|System Idle Process|Registry|smss|csrss|wininit|winlogon|services|lsass|lsaiso|svchost|dwm|fontdrvhost|spoolsv|taskhostw|SearchIndexer|MsMpEng|WmiPrvSE|dllhost|conhost|RuntimeBroker|sihost|ctfmon|explorer|systemd|launchd|kernel_task|init|rpcbind|dbus-daemon|cupsd|mDNSResponder|coreaudiod|WindowServer|loginwindow|SystemUIServer|configd|syslogd|logind|cron|rsyslogd|NetworkManager|polkitd|udevd|avahi-daemon)(\\.exe)?$/i;\n\nexport function isSystemProcess(processName: string): boolean {\n return systemProcessPattern.test(processName);\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nfunction assertValidPid(pid: number): void {\n if (!Number.isInteger(pid) || pid <= 0) {\n throw new Error(`Invalid pid: ${pid}`);\n }\n}\n\nexport async function killProcess(pid: number): Promise<void> {\n assertValidPid(pid);\n\n if (process.platform === 'win32') {\n await execFileAsync('taskkill', ['/PID', String(pid), '/F']);\n return;\n }\n\n process.kill(pid);\n}\n"],"mappings":"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","names":["import_node_child_process","import_node_util","execFileAsync"]}
package/dist/index.d.cts CHANGED
@@ -25,5 +25,6 @@ declare function getListeningPorts(): Promise<PortInfo[]>;
25
25
  declare function killProcess(pid: number): Promise<void>;
26
26
 
27
27
  declare function classifyOrigin(processInfo: ProcessInfo): Origin;
28
+ declare function isSystemProcess(processName: string): boolean;
28
29
 
29
- export { type ClassifyRule, type Origin, type PortInfo, type ProcessInfo, type Protocol, classifyOrigin, getListeningPorts, killProcess };
30
+ export { type ClassifyRule, type Origin, type PortInfo, type ProcessInfo, type Protocol, classifyOrigin, getListeningPorts, isSystemProcess, killProcess };
package/dist/index.d.ts CHANGED
@@ -25,5 +25,6 @@ declare function getListeningPorts(): Promise<PortInfo[]>;
25
25
  declare function killProcess(pid: number): Promise<void>;
26
26
 
27
27
  declare function classifyOrigin(processInfo: ProcessInfo): Origin;
28
+ declare function isSystemProcess(processName: string): boolean;
28
29
 
29
- export { type ClassifyRule, type Origin, type PortInfo, type ProcessInfo, type Protocol, classifyOrigin, getListeningPorts, killProcess };
30
+ export { type ClassifyRule, type Origin, type PortInfo, type ProcessInfo, type Protocol, classifyOrigin, getListeningPorts, isSystemProcess, killProcess };
package/dist/index.js CHANGED
@@ -18,82 +18,102 @@ function classifyOrigin(processInfo) {
18
18
  }
19
19
  return "unknown";
20
20
  }
21
+ var systemProcessPattern = /^(System|System Idle Process|Registry|smss|csrss|wininit|winlogon|services|lsass|lsaiso|svchost|dwm|fontdrvhost|spoolsv|taskhostw|SearchIndexer|MsMpEng|WmiPrvSE|dllhost|conhost|RuntimeBroker|sihost|ctfmon|explorer|systemd|launchd|kernel_task|init|rpcbind|dbus-daemon|cupsd|mDNSResponder|coreaudiod|WindowServer|loginwindow|SystemUIServer|configd|syslogd|logind|cron|rsyslogd|NetworkManager|polkitd|udevd|avahi-daemon)(\.exe)?$/i;
22
+ function isSystemProcess(processName) {
23
+ return systemProcessPattern.test(processName);
24
+ }
21
25
 
22
26
  // src/detect.ts
23
27
  var execFileAsync = promisify(execFile);
28
+ var PROCESS_TABLE_MAX_BUFFER = 10 * 1024 * 1024;
24
29
  function isValidPid(pid) {
25
30
  return typeof pid === "number" && Number.isInteger(pid) && pid > 0;
26
31
  }
27
32
  function normalizeProtocol(raw) {
28
33
  return raw.toLowerCase().startsWith("udp") ? "udp" : "tcp";
29
34
  }
30
- async function getUnixProcessInfo(pid) {
31
- const { stdout } = await execFileAsync("ps", ["-p", String(pid), "-o", "ppid=,comm=,args="]);
32
- const line = stdout.trim();
33
- const [ppidToken, commToken, ...rest] = line.split(/\s+/);
34
- const processName = commToken ?? "";
35
- const command = rest.length > 0 ? rest.join(" ") : processName;
36
- const ppid = Number(ppidToken);
37
- let parentName = "";
38
- if (isValidPid(ppid)) {
39
- try {
40
- const { stdout: parentStdout } = await execFileAsync("ps", ["-p", String(ppid), "-o", "comm="]);
41
- parentName = parentStdout.trim();
42
- } catch {
43
- parentName = "";
44
- }
35
+ async function queryAllUnixProcesses() {
36
+ const { stdout } = await execFileAsync("ps", ["-A", "-o", "pid=,ppid=,comm=,args="], {
37
+ maxBuffer: PROCESS_TABLE_MAX_BUFFER
38
+ });
39
+ const table = /* @__PURE__ */ new Map();
40
+ for (const line of stdout.split("\n")) {
41
+ const trimmed = line.trim();
42
+ if (!trimmed) continue;
43
+ const [pidToken, ppidToken, commToken, ...rest] = trimmed.split(/\s+/);
44
+ const pid = Number(pidToken);
45
+ if (!isValidPid(pid)) continue;
46
+ const comm = commToken ?? "";
47
+ table.set(pid, {
48
+ ppid: Number(ppidToken),
49
+ comm,
50
+ args: rest.length > 0 ? rest.join(" ") : comm
51
+ });
45
52
  }
46
- return { pid, processName, command, parentName };
53
+ return table;
54
+ }
55
+ function resolveUnixProcessInfo(pid, table) {
56
+ const entry = table.get(pid);
57
+ if (!entry) {
58
+ throw new Error(`No process found for pid ${pid}`);
59
+ }
60
+ const parentEntry = isValidPid(entry.ppid) ? table.get(entry.ppid) : void 0;
61
+ return {
62
+ pid,
63
+ processName: entry.comm,
64
+ command: entry.args,
65
+ parentName: parentEntry?.comm ?? ""
66
+ };
47
67
  }
48
- async function queryWindowsProcess(pid) {
49
- const script = `Get-CimInstance Win32_Process -Filter "ProcessId=${pid}" | Select-Object Name,CommandLine,ParentProcessId | ConvertTo-Json -Compress`;
50
- const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script]);
68
+ async function queryAllWindowsProcesses() {
69
+ const script = "Get-CimInstance Win32_Process | Select-Object ProcessId,Name,CommandLine,ParentProcessId | ConvertTo-Json -Compress";
70
+ const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], {
71
+ maxBuffer: PROCESS_TABLE_MAX_BUFFER
72
+ });
51
73
  const trimmed = stdout.trim();
52
- if (!trimmed) return void 0;
53
- return JSON.parse(trimmed);
74
+ if (!trimmed) return /* @__PURE__ */ new Map();
75
+ const parsed = JSON.parse(trimmed);
76
+ const records = Array.isArray(parsed) ? parsed : [parsed];
77
+ const table = /* @__PURE__ */ new Map();
78
+ for (const record of records) {
79
+ if (isValidPid(record.ProcessId)) {
80
+ table.set(record.ProcessId, record);
81
+ }
82
+ }
83
+ return table;
54
84
  }
55
- async function getWindowsProcessInfo(pid) {
56
- const record = await queryWindowsProcess(pid);
85
+ function resolveWindowsProcessInfo(pid, table) {
86
+ const record = table.get(pid);
57
87
  if (!record) {
58
88
  throw new Error(`No process found for pid ${pid}`);
59
89
  }
60
- let parentName = "";
61
- if (isValidPid(record.ParentProcessId)) {
62
- try {
63
- const parentRecord = await queryWindowsProcess(record.ParentProcessId);
64
- parentName = parentRecord?.Name ?? "";
65
- } catch {
66
- parentName = "";
67
- }
68
- }
90
+ const parentRecord = isValidPid(record.ParentProcessId) ? table.get(record.ParentProcessId) : void 0;
69
91
  return {
70
92
  pid,
71
93
  processName: record.Name ?? "",
72
94
  command: record.CommandLine ?? record.Name ?? "",
73
- parentName
95
+ parentName: parentRecord?.Name ?? ""
74
96
  };
75
97
  }
76
- async function getProcessInfo(pid) {
77
- return process.platform === "win32" ? getWindowsProcessInfo(pid) : getUnixProcessInfo(pid);
78
- }
79
98
  async function getListeningPorts() {
80
99
  const connections = await si.networkConnections();
81
100
  const listening = connections.filter(
82
101
  (conn) => conn.state?.toUpperCase() === "LISTEN" && isValidPid(conn.pid)
83
102
  );
84
- const processInfoByPid = /* @__PURE__ */ new Map();
103
+ if (listening.length === 0) {
104
+ return [];
105
+ }
106
+ const isWindows = process.platform === "win32";
107
+ const processTable = isWindows ? await queryAllWindowsProcesses() : await queryAllUnixProcesses();
85
108
  const result = [];
86
109
  for (const conn of listening) {
87
110
  const pid = conn.pid;
88
- if (!processInfoByPid.has(pid)) {
89
- try {
90
- processInfoByPid.set(pid, await getProcessInfo(pid));
91
- } catch {
92
- processInfoByPid.set(pid, null);
93
- }
111
+ let processInfo;
112
+ try {
113
+ processInfo = isWindows ? resolveWindowsProcessInfo(pid, processTable) : resolveUnixProcessInfo(pid, processTable);
114
+ } catch {
115
+ continue;
94
116
  }
95
- const processInfo = processInfoByPid.get(pid);
96
- if (!processInfo) continue;
97
117
  const port = Number(conn.localPort);
98
118
  if (!Number.isInteger(port)) continue;
99
119
  result.push({
@@ -134,6 +154,7 @@ async function killProcess(pid) {
134
154
  export {
135
155
  classifyOrigin,
136
156
  getListeningPorts,
157
+ isSystemProcess,
137
158
  killProcess
138
159
  };
139
160
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/detect.ts","../src/classify.ts","../src/kill.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport * as si from 'systeminformation';\nimport { classifyOrigin } from './classify.js';\nimport type { PortInfo, Protocol, ProcessInfo } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\nfunction isValidPid(pid: unknown): pid is number {\n return typeof pid === 'number' && Number.isInteger(pid) && pid > 0;\n}\n\nfunction normalizeProtocol(raw: string): Protocol {\n return raw.toLowerCase().startsWith('udp') ? 'udp' : 'tcp';\n}\n\nasync function getUnixProcessInfo(pid: number): Promise<ProcessInfo> {\n const { stdout } = await execFileAsync('ps', ['-p', String(pid), '-o', 'ppid=,comm=,args=']);\n const line = stdout.trim();\n const [ppidToken, commToken, ...rest] = line.split(/\\s+/);\n const processName = commToken ?? '';\n const command = rest.length > 0 ? rest.join(' ') : processName;\n const ppid = Number(ppidToken);\n\n let parentName = '';\n if (isValidPid(ppid)) {\n try {\n const { stdout: parentStdout } = await execFileAsync('ps', ['-p', String(ppid), '-o', 'comm=']);\n parentName = parentStdout.trim();\n } catch {\n parentName = '';\n }\n }\n\n return { pid, processName, command, parentName };\n}\n\ninterface WindowsProcessRecord {\n Name?: string;\n CommandLine?: string;\n ParentProcessId?: number;\n}\n\nasync function queryWindowsProcess(pid: number): Promise<WindowsProcessRecord | undefined> {\n const script = `Get-CimInstance Win32_Process -Filter \"ProcessId=${pid}\" | Select-Object Name,CommandLine,ParentProcessId | ConvertTo-Json -Compress`;\n const { stdout } = await execFileAsync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script]);\n const trimmed = stdout.trim();\n if (!trimmed) return undefined;\n return JSON.parse(trimmed) as WindowsProcessRecord;\n}\n\nasync function getWindowsProcessInfo(pid: number): Promise<ProcessInfo> {\n const record = await queryWindowsProcess(pid);\n if (!record) {\n throw new Error(`No process found for pid ${pid}`);\n }\n\n let parentName = '';\n if (isValidPid(record.ParentProcessId)) {\n try {\n const parentRecord = await queryWindowsProcess(record.ParentProcessId);\n parentName = parentRecord?.Name ?? '';\n } catch {\n parentName = '';\n }\n }\n\n return {\n pid,\n processName: record.Name ?? '',\n command: record.CommandLine ?? record.Name ?? '',\n parentName,\n };\n}\n\nasync function getProcessInfo(pid: number): Promise<ProcessInfo> {\n return process.platform === 'win32' ? getWindowsProcessInfo(pid) : getUnixProcessInfo(pid);\n}\n\nexport async function getListeningPorts(): Promise<PortInfo[]> {\n const connections = await si.networkConnections();\n const listening = connections.filter(\n (conn) => conn.state?.toUpperCase() === 'LISTEN' && isValidPid(conn.pid),\n );\n\n const processInfoByPid = new Map<number, ProcessInfo | null>();\n\n const result: PortInfo[] = [];\n\n for (const conn of listening) {\n const pid = conn.pid;\n\n if (!processInfoByPid.has(pid)) {\n try {\n processInfoByPid.set(pid, await getProcessInfo(pid));\n } catch {\n processInfoByPid.set(pid, null);\n }\n }\n\n const processInfo = processInfoByPid.get(pid);\n if (!processInfo) continue;\n\n const port = Number(conn.localPort);\n if (!Number.isInteger(port)) continue;\n\n result.push({\n port,\n pid,\n protocol: normalizeProtocol(conn.protocol),\n processName: processInfo.processName,\n command: processInfo.command,\n origin: classifyOrigin(processInfo),\n });\n }\n\n const seen = new Set<string>();\n return result.filter((entry) => {\n const key = `${entry.pid}:${entry.port}:${entry.protocol}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n","import type { ClassifyRule, Origin, ProcessInfo } from './types.js';\n\nconst agentHostPattern = /^(code|code-oss|cursor|windsurf)(\\.exe|\\s.*)?$/i;\n\nconst rules: ClassifyRule[] = [\n { namePattern: /claude/i, origin: 'agent' },\n { namePattern: /^(node|python|python3|deno|bun)(\\.exe)?$/i, parentPattern: agentHostPattern, origin: 'agent' },\n { namePattern: /^(bash|zsh|sh|fish|powershell|pwsh|cmd)(\\.exe)?$/i, parentPattern: agentHostPattern, origin: 'agent' },\n];\n\nexport function classifyOrigin(processInfo: ProcessInfo): Origin {\n for (const rule of rules) {\n if (!rule.namePattern.test(processInfo.processName)) continue;\n if (rule.parentPattern && !rule.parentPattern.test(processInfo.parentName)) continue;\n return rule.origin;\n }\n return 'unknown';\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nfunction assertValidPid(pid: number): void {\n if (!Number.isInteger(pid) || pid <= 0) {\n throw new Error(`Invalid pid: ${pid}`);\n }\n}\n\nexport async function killProcess(pid: number): Promise<void> {\n assertValidPid(pid);\n\n if (process.platform === 'win32') {\n await execFileAsync('taskkill', ['/PID', String(pid), '/F']);\n return;\n }\n\n process.kill(pid);\n}\n"],"mappings":"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","names":["execFile","promisify","execFileAsync"]}
1
+ {"version":3,"sources":["../src/detect.ts","../src/classify.ts","../src/kill.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport * as si from 'systeminformation';\nimport { classifyOrigin } from './classify.js';\nimport type { PortInfo, Protocol, ProcessInfo } from './types.js';\n\nconst execFileAsync = promisify(execFile);\n\n// Full process tables can be a few hundred KB of JSON/text on a busy\n// machine — well past Node's 1MB default maxBuffer for exec/execFile.\nconst PROCESS_TABLE_MAX_BUFFER = 10 * 1024 * 1024;\n\nfunction isValidPid(pid: unknown): pid is number {\n return typeof pid === 'number' && Number.isInteger(pid) && pid > 0;\n}\n\nfunction normalizeProtocol(raw: string): Protocol {\n return raw.toLowerCase().startsWith('udp') ? 'udp' : 'tcp';\n}\n\ninterface UnixProcessRecord {\n ppid: number;\n comm: string;\n args: string;\n}\n\n// One \"ps\" call for the whole process table, instead of one call per pid\n// (plus one more per parent pid) — the earlier per-pid version re-shelled\n// out for every unique pid on every refresh, which is the dominant cost\n// on a machine with many listening ports.\nasync function queryAllUnixProcesses(): Promise<Map<number, UnixProcessRecord>> {\n const { stdout } = await execFileAsync('ps', ['-A', '-o', 'pid=,ppid=,comm=,args='], {\n maxBuffer: PROCESS_TABLE_MAX_BUFFER,\n });\n\n const table = new Map<number, UnixProcessRecord>();\n for (const line of stdout.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n const [pidToken, ppidToken, commToken, ...rest] = trimmed.split(/\\s+/);\n const pid = Number(pidToken);\n if (!isValidPid(pid)) continue;\n\n const comm = commToken ?? '';\n table.set(pid, {\n ppid: Number(ppidToken),\n comm,\n args: rest.length > 0 ? rest.join(' ') : comm,\n });\n }\n return table;\n}\n\nfunction resolveUnixProcessInfo(pid: number, table: Map<number, UnixProcessRecord>): ProcessInfo {\n const entry = table.get(pid);\n if (!entry) {\n throw new Error(`No process found for pid ${pid}`);\n }\n\n const parentEntry = isValidPid(entry.ppid) ? table.get(entry.ppid) : undefined;\n\n return {\n pid,\n processName: entry.comm,\n command: entry.args,\n parentName: parentEntry?.comm ?? '',\n };\n}\n\ninterface WindowsProcessRecord {\n ProcessId?: number;\n Name?: string;\n CommandLine?: string;\n ParentProcessId?: number;\n}\n\n// Same batching principle as the Unix path: one Get-CimInstance call for\n// every process, instead of a fresh powershell.exe spawn per pid (and\n// another per parent pid) — powershell.exe's own startup overhead, not\n// the WMI query itself, was what made refreshes slow with many ports open.\nasync function queryAllWindowsProcesses(): Promise<Map<number, WindowsProcessRecord>> {\n const script =\n 'Get-CimInstance Win32_Process | ' +\n 'Select-Object ProcessId,Name,CommandLine,ParentProcessId | ' +\n 'ConvertTo-Json -Compress';\n const { stdout } = await execFileAsync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], {\n maxBuffer: PROCESS_TABLE_MAX_BUFFER,\n });\n\n const trimmed = stdout.trim();\n if (!trimmed) return new Map();\n\n const parsed: unknown = JSON.parse(trimmed);\n // ConvertTo-Json emits a bare object instead of a one-element array\n // when the pipeline only produced a single result.\n const records = (Array.isArray(parsed) ? parsed : [parsed]) as WindowsProcessRecord[];\n\n const table = new Map<number, WindowsProcessRecord>();\n for (const record of records) {\n if (isValidPid(record.ProcessId)) {\n table.set(record.ProcessId, record);\n }\n }\n return table;\n}\n\nfunction resolveWindowsProcessInfo(pid: number, table: Map<number, WindowsProcessRecord>): ProcessInfo {\n const record = table.get(pid);\n if (!record) {\n throw new Error(`No process found for pid ${pid}`);\n }\n\n const parentRecord = isValidPid(record.ParentProcessId) ? table.get(record.ParentProcessId) : undefined;\n\n return {\n pid,\n processName: record.Name ?? '',\n command: record.CommandLine ?? record.Name ?? '',\n parentName: parentRecord?.Name ?? '',\n };\n}\n\nexport async function getListeningPorts(): Promise<PortInfo[]> {\n const connections = await si.networkConnections();\n const listening = connections.filter(\n (conn) => conn.state?.toUpperCase() === 'LISTEN' && isValidPid(conn.pid),\n );\n\n if (listening.length === 0) {\n return [];\n }\n\n const isWindows = process.platform === 'win32';\n const processTable = isWindows ? await queryAllWindowsProcesses() : await queryAllUnixProcesses();\n\n const result: PortInfo[] = [];\n\n for (const conn of listening) {\n const pid = conn.pid;\n\n let processInfo: ProcessInfo;\n try {\n processInfo = isWindows\n ? resolveWindowsProcessInfo(pid, processTable as Map<number, WindowsProcessRecord>)\n : resolveUnixProcessInfo(pid, processTable as Map<number, UnixProcessRecord>);\n } catch {\n // Process exited between the port scan and building the process\n // table — it isn't meaningfully \"listening\" anymore, so drop it\n // rather than surface it as unknown.\n continue;\n }\n\n const port = Number(conn.localPort);\n if (!Number.isInteger(port)) continue;\n\n result.push({\n port,\n pid,\n protocol: normalizeProtocol(conn.protocol),\n processName: processInfo.processName,\n command: processInfo.command,\n origin: classifyOrigin(processInfo),\n });\n }\n\n const seen = new Set<string>();\n return result.filter((entry) => {\n const key = `${entry.pid}:${entry.port}:${entry.protocol}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n}\n","import type { ClassifyRule, Origin, ProcessInfo } from './types.js';\n\nconst agentHostPattern = /^(code|code-oss|cursor|windsurf)(\\.exe|\\s.*)?$/i;\n\nconst rules: ClassifyRule[] = [\n { namePattern: /claude/i, origin: 'agent' },\n { namePattern: /^(node|python|python3|deno|bun)(\\.exe)?$/i, parentPattern: agentHostPattern, origin: 'agent' },\n { namePattern: /^(bash|zsh|sh|fish|powershell|pwsh|cmd)(\\.exe)?$/i, parentPattern: agentHostPattern, origin: 'agent' },\n];\n\nexport function classifyOrigin(processInfo: ProcessInfo): Origin {\n for (const rule of rules) {\n if (!rule.namePattern.test(processInfo.processName)) continue;\n if (rule.parentPattern && !rule.parentPattern.test(processInfo.parentName)) continue;\n return rule.origin;\n }\n return 'unknown';\n}\n\n// Well-known OS/service process names — used only to declutter a display\n// (e.g. the VS Code sidebar's \"hide system processes\" setting), never to\n// change what getListeningPorts() actually detects. A process spoofing one\n// of these names would be just as invisible to a plain \"netstat\" read, so\n// this is a display convenience, not a security boundary.\nconst systemProcessPattern =\n /^(System|System Idle Process|Registry|smss|csrss|wininit|winlogon|services|lsass|lsaiso|svchost|dwm|fontdrvhost|spoolsv|taskhostw|SearchIndexer|MsMpEng|WmiPrvSE|dllhost|conhost|RuntimeBroker|sihost|ctfmon|explorer|systemd|launchd|kernel_task|init|rpcbind|dbus-daemon|cupsd|mDNSResponder|coreaudiod|WindowServer|loginwindow|SystemUIServer|configd|syslogd|logind|cron|rsyslogd|NetworkManager|polkitd|udevd|avahi-daemon)(\\.exe)?$/i;\n\nexport function isSystemProcess(processName: string): boolean {\n return systemProcessPattern.test(processName);\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nfunction assertValidPid(pid: number): void {\n if (!Number.isInteger(pid) || pid <= 0) {\n throw new Error(`Invalid pid: ${pid}`);\n }\n}\n\nexport async function killProcess(pid: number): Promise<void> {\n assertValidPid(pid);\n\n if (process.platform === 'win32') {\n await execFileAsync('taskkill', ['/PID', String(pid), '/F']);\n return;\n }\n\n process.kill(pid);\n}\n"],"mappings":"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","names":["execFile","promisify","execFileAsync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "porthawk-core",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Detection and process control engine for PortHawk",
5
5
  "repository": {
6
6
  "type": "git",