porthawk-core 0.1.5 → 0.1.6

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,6 +57,10 @@ 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);
@@ -173,6 +178,7 @@ async function killProcess(pid) {
173
178
  0 && (module.exports = {
174
179
  classifyOrigin,
175
180
  getListeningPorts,
181
+ isSystemProcess,
176
182
  killProcess
177
183
  });
178
184
  //# 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\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\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,6 +18,10 @@ 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);
@@ -134,6 +138,7 @@ async function killProcess(pid) {
134
138
  export {
135
139
  classifyOrigin,
136
140
  getListeningPorts,
141
+ isSystemProcess,
137
142
  killProcess
138
143
  };
139
144
  //# 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\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\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.6",
4
4
  "description": "Detection and process control engine for PortHawk",
5
5
  "repository": {
6
6
  "type": "git",