safecrab 0.1.0
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/LICENSE +21 -0
- package/README.md +220 -0
- package/dist/cli/commands/scan.d.ts +5 -0
- package/dist/cli/commands/scan.d.ts.map +1 -0
- package/dist/cli/commands/scan.js +47 -0
- package/dist/cli/commands/scan.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +23 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/engine/context.d.ts +6 -0
- package/dist/engine/context.d.ts.map +1 -0
- package/dist/engine/context.js +33 -0
- package/dist/engine/context.js.map +1 -0
- package/dist/engine/exposure.d.ts +22 -0
- package/dist/engine/exposure.d.ts.map +1 -0
- package/dist/engine/exposure.js +100 -0
- package/dist/engine/exposure.js.map +1 -0
- package/dist/engine/heuristics.d.ts +11 -0
- package/dist/engine/heuristics.d.ts.map +1 -0
- package/dist/engine/heuristics.js +219 -0
- package/dist/engine/heuristics.js.map +1 -0
- package/dist/engine/types.d.ts +46 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +5 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/system/collectors/cloudflare.d.ts +8 -0
- package/dist/system/collectors/cloudflare.d.ts.map +1 -0
- package/dist/system/collectors/cloudflare.js +32 -0
- package/dist/system/collectors/cloudflare.js.map +1 -0
- package/dist/system/collectors/firewall.d.ts +10 -0
- package/dist/system/collectors/firewall.d.ts.map +1 -0
- package/dist/system/collectors/firewall.js +69 -0
- package/dist/system/collectors/firewall.js.map +1 -0
- package/dist/system/collectors/network.d.ts +10 -0
- package/dist/system/collectors/network.d.ts.map +1 -0
- package/dist/system/collectors/network.js +18 -0
- package/dist/system/collectors/network.js.map +1 -0
- package/dist/system/collectors/services.d.ts +6 -0
- package/dist/system/collectors/services.d.ts.map +1 -0
- package/dist/system/collectors/services.js +31 -0
- package/dist/system/collectors/services.js.map +1 -0
- package/dist/system/collectors/tailscale.d.ts +10 -0
- package/dist/system/collectors/tailscale.d.ts.map +1 -0
- package/dist/system/collectors/tailscale.js +39 -0
- package/dist/system/collectors/tailscale.js.map +1 -0
- package/dist/system/parsers/ip-parser.d.ts +25 -0
- package/dist/system/parsers/ip-parser.d.ts.map +1 -0
- package/dist/system/parsers/ip-parser.js +100 -0
- package/dist/system/parsers/ip-parser.js.map +1 -0
- package/dist/system/parsers/ss-parser.d.ts +15 -0
- package/dist/system/parsers/ss-parser.d.ts.map +1 -0
- package/dist/system/parsers/ss-parser.js +153 -0
- package/dist/system/parsers/ss-parser.js.map +1 -0
- package/dist/system/shell.d.ts +27 -0
- package/dist/system/shell.d.ts.map +1 -0
- package/dist/system/shell.js +56 -0
- package/dist/system/shell.js.map +1 -0
- package/dist/ui/findings.d.ts +6 -0
- package/dist/ui/findings.d.ts.map +1 -0
- package/dist/ui/findings.js +91 -0
- package/dist/ui/findings.js.map +1 -0
- package/dist/ui/renderer.d.ts +18 -0
- package/dist/ui/renderer.d.ts.map +1 -0
- package/dist/ui/renderer.js +33 -0
- package/dist/ui/renderer.js.map +1 -0
- package/dist/ui/summary.d.ts +17 -0
- package/dist/ui/summary.d.ts.map +1 -0
- package/dist/ui/summary.js +39 -0
- package/dist/ui/summary.js.map +1 -0
- package/dist/ui/theme.d.ts +39 -0
- package/dist/ui/theme.d.ts.map +1 -0
- package/dist/ui/theme.js +47 -0
- package/dist/ui/theme.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for ip addr output
|
|
3
|
+
* Maps IP addresses to network interface names
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Parse ip addr output to create a mapping of IP addresses to interface names
|
|
7
|
+
*
|
|
8
|
+
* Example output:
|
|
9
|
+
* 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
|
|
10
|
+
* inet 127.0.0.1/8 scope host lo
|
|
11
|
+
* 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP
|
|
12
|
+
* inet 192.168.1.100/24 brd 192.168.1.255 scope global eth0
|
|
13
|
+
* 3: tailscale0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1280 qdisc fq_codel state UNKNOWN
|
|
14
|
+
* inet 100.64.0.1/32 scope global tailscale0
|
|
15
|
+
*/
|
|
16
|
+
export function parseIPAddr(output) {
|
|
17
|
+
const ipToInterfaces = new Map();
|
|
18
|
+
const lines = output.split("\n");
|
|
19
|
+
let currentInterface = null;
|
|
20
|
+
for (const line of lines) {
|
|
21
|
+
const trimmed = line.trim();
|
|
22
|
+
// Interface line: "2: eth0: <BROADCAST,..."
|
|
23
|
+
const ifaceMatch = trimmed.match(/^\d+:\s+([^:]+):/);
|
|
24
|
+
if (ifaceMatch) {
|
|
25
|
+
currentInterface = ifaceMatch[1]?.trim() ?? null;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
// IPv4 address line: "inet 192.168.1.100/24 ..."
|
|
29
|
+
const inet4Match = trimmed.match(/^inet\s+([0-9.]+)/);
|
|
30
|
+
if (inet4Match && currentInterface) {
|
|
31
|
+
const ip = inet4Match[1];
|
|
32
|
+
if (ip) {
|
|
33
|
+
const interfaces = ipToInterfaces.get(ip) ?? [];
|
|
34
|
+
interfaces.push(currentInterface);
|
|
35
|
+
ipToInterfaces.set(ip, interfaces);
|
|
36
|
+
}
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// IPv6 address line: "inet6 fe80::1/64 ..."
|
|
40
|
+
const inet6Match = trimmed.match(/^inet6\s+([0-9a-f:]+)/);
|
|
41
|
+
if (inet6Match && currentInterface) {
|
|
42
|
+
const ip = inet6Match[1];
|
|
43
|
+
if (ip) {
|
|
44
|
+
const interfaces = ipToInterfaces.get(ip) ?? [];
|
|
45
|
+
interfaces.push(currentInterface);
|
|
46
|
+
ipToInterfaces.set(ip, interfaces);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return ipToInterfaces;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get list of all network interfaces from ip addr output
|
|
54
|
+
*/
|
|
55
|
+
export function getInterfaces(output) {
|
|
56
|
+
const interfaces = [];
|
|
57
|
+
const ipMap = parseIPAddr(output);
|
|
58
|
+
// Group by interface name
|
|
59
|
+
const interfaceMap = new Map();
|
|
60
|
+
for (const [ip, ifaces] of ipMap.entries()) {
|
|
61
|
+
for (const iface of ifaces) {
|
|
62
|
+
const ips = interfaceMap.get(iface) ?? [];
|
|
63
|
+
ips.push(ip);
|
|
64
|
+
interfaceMap.set(iface, ips);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const [name, ips] of interfaceMap.entries()) {
|
|
68
|
+
interfaces.push({
|
|
69
|
+
name,
|
|
70
|
+
ips,
|
|
71
|
+
isPublic: isPublicInterface(name, ips),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return interfaces;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Determine if an interface is public-facing
|
|
78
|
+
* Private interfaces: lo, docker*, tailscale*, cloudflare*
|
|
79
|
+
*/
|
|
80
|
+
function isPublicInterface(name, ips) {
|
|
81
|
+
// Loopback
|
|
82
|
+
if (name === "lo" || name.startsWith("lo:")) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
// Virtual/tunnel interfaces
|
|
86
|
+
if (name.startsWith("docker") ||
|
|
87
|
+
name.startsWith("tailscale") ||
|
|
88
|
+
name.startsWith("cloudflare") ||
|
|
89
|
+
name.startsWith("veth") ||
|
|
90
|
+
name.startsWith("br-")) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// Check if all IPs are localhost
|
|
94
|
+
const allLocalhost = ips.every((ip) => ip.startsWith("127.") || ip === "::1");
|
|
95
|
+
if (allLocalhost) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=ip-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ip-parser.js","sourceRoot":"","sources":["../../../src/system/parsers/ip-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for ss -tulnp output
|
|
3
|
+
* Extracts listening services with port, protocol, process info
|
|
4
|
+
*/
|
|
5
|
+
import type { ListeningService } from "../../engine/types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Parse ss -tulnp output into structured ListeningService objects
|
|
8
|
+
*
|
|
9
|
+
* Example ss output:
|
|
10
|
+
* Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
|
|
11
|
+
* tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3))
|
|
12
|
+
* udp UNCONN 0 0 127.0.0.1:53 0.0.0.0:* users:(("systemd-resolve",pid=567,fd=12))
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseSSOutput(output: string, interfaceMap: Map<string, string[]>): ListeningService[];
|
|
15
|
+
//# sourceMappingURL=ss-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ss-parser.d.ts","sourceRoot":"","sources":["../../../src/system/parsers/ss-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAW9D;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAClC,gBAAgB,EAAE,CAiBpB"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser for ss -tulnp output
|
|
3
|
+
* Extracts listening services with port, protocol, process info
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
const ListeningServiceSchema = z.object({
|
|
7
|
+
port: z.number(),
|
|
8
|
+
protocol: z.enum(["tcp", "udp"]),
|
|
9
|
+
process: z.string(),
|
|
10
|
+
pid: z.number(),
|
|
11
|
+
interfaces: z.array(z.string()),
|
|
12
|
+
boundIp: z.string(),
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Parse ss -tulnp output into structured ListeningService objects
|
|
16
|
+
*
|
|
17
|
+
* Example ss output:
|
|
18
|
+
* Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
|
|
19
|
+
* tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1234,fd=3))
|
|
20
|
+
* udp UNCONN 0 0 127.0.0.1:53 0.0.0.0:* users:(("systemd-resolve",pid=567,fd=12))
|
|
21
|
+
*/
|
|
22
|
+
export function parseSSOutput(output, interfaceMap) {
|
|
23
|
+
const services = [];
|
|
24
|
+
const lines = output.split("\n");
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
// Skip header and empty lines
|
|
27
|
+
if (!line.trim() || line.startsWith("Netid") || line.startsWith("State")) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const service = parseSSLine(line, interfaceMap);
|
|
31
|
+
if (service) {
|
|
32
|
+
services.push(service);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return services;
|
|
36
|
+
}
|
|
37
|
+
function parseSSLine(line, interfaceMap) {
|
|
38
|
+
// Split by whitespace
|
|
39
|
+
const parts = line.trim().split(/\s+/);
|
|
40
|
+
if (parts.length < 5) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
// Extract protocol (tcp or udp)
|
|
44
|
+
const protocol = parts[0]?.toLowerCase();
|
|
45
|
+
if (protocol !== "tcp" && protocol !== "udp") {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
// Extract local address:port (usually index 4 for ss -tulnp)
|
|
49
|
+
// Format: "0.0.0.0:22" or "[::]:80" or "127.0.0.1:8080"
|
|
50
|
+
let localAddrPart = parts[4];
|
|
51
|
+
// Handle different formats in ss output
|
|
52
|
+
if (!localAddrPart?.includes(":")) {
|
|
53
|
+
// Try to find the Local Address:Port part
|
|
54
|
+
for (let i = 3; i < parts.length; i++) {
|
|
55
|
+
if (parts[i]?.includes(":")) {
|
|
56
|
+
localAddrPart = parts[i];
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!localAddrPart) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const { ip, port } = parseAddress(localAddrPart);
|
|
65
|
+
if (port === null) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
// Extract process info from the Process column
|
|
69
|
+
// Format: users:(("sshd",pid=1234,fd=3))
|
|
70
|
+
const processMatch = line.match(/users:\(\("([^"]+)",pid=(\d+)/);
|
|
71
|
+
const processName = processMatch?.[1] ?? "unknown";
|
|
72
|
+
const pid = processMatch?.[2] ? Number.parseInt(processMatch[2], 10) : 0;
|
|
73
|
+
// Map IP to interfaces
|
|
74
|
+
const interfaces = mapIpToInterfaces(ip, interfaceMap);
|
|
75
|
+
const service = {
|
|
76
|
+
port,
|
|
77
|
+
protocol: protocol,
|
|
78
|
+
process: processName,
|
|
79
|
+
pid,
|
|
80
|
+
interfaces,
|
|
81
|
+
boundIp: ip,
|
|
82
|
+
};
|
|
83
|
+
// Validate with zod
|
|
84
|
+
const result = ListeningServiceSchema.safeParse(service);
|
|
85
|
+
if (!result.success) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return service;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Parse address:port string
|
|
92
|
+
* Handles IPv4, IPv6, and wildcard addresses
|
|
93
|
+
*/
|
|
94
|
+
function parseAddress(addr) {
|
|
95
|
+
// IPv6 format: [::]:80 or [::1]:8080
|
|
96
|
+
const ipv6Match = addr.match(/^\[([^\]]+)\]:(\d+)$/);
|
|
97
|
+
if (ipv6Match) {
|
|
98
|
+
const port = Number.parseInt(ipv6Match[2] ?? "", 10);
|
|
99
|
+
return {
|
|
100
|
+
ip: ipv6Match[1] ?? "::",
|
|
101
|
+
port: Number.isNaN(port) ? null : port,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// IPv4 format: 0.0.0.0:22 or 127.0.0.1:8080
|
|
105
|
+
const ipv4Match = addr.match(/^([^:]+):(\d+)$/);
|
|
106
|
+
if (ipv4Match) {
|
|
107
|
+
const port = Number.parseInt(ipv4Match[2] ?? "", 10);
|
|
108
|
+
return {
|
|
109
|
+
ip: ipv4Match[1] ?? "0.0.0.0",
|
|
110
|
+
port: Number.isNaN(port) ? null : port,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// Handle asterisk wildcard (some versions of ss)
|
|
114
|
+
if (addr.startsWith("*:")) {
|
|
115
|
+
const portStr = addr.slice(2);
|
|
116
|
+
const port = Number.parseInt(portStr, 10);
|
|
117
|
+
return {
|
|
118
|
+
ip: "0.0.0.0",
|
|
119
|
+
port: Number.isNaN(port) ? null : port,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return { ip: "0.0.0.0", port: null };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Map bound IP to network interface names
|
|
126
|
+
*
|
|
127
|
+
* Rules:
|
|
128
|
+
* - 0.0.0.0 or :: → all public interfaces (eth0, ens3, etc.) + lo
|
|
129
|
+
* - 127.0.0.1 or ::1 → localhost only
|
|
130
|
+
* - Specific IP → lookup in interface map
|
|
131
|
+
*/
|
|
132
|
+
function mapIpToInterfaces(ip, interfaceMap) {
|
|
133
|
+
// Localhost
|
|
134
|
+
if (ip === "127.0.0.1" || ip === "::1" || ip === "localhost") {
|
|
135
|
+
return ["lo"];
|
|
136
|
+
}
|
|
137
|
+
// Wildcard - bind to all interfaces
|
|
138
|
+
if (ip === "0.0.0.0" || ip === "::" || ip === "*") {
|
|
139
|
+
const allInterfaces = [];
|
|
140
|
+
for (const ifaces of interfaceMap.values()) {
|
|
141
|
+
allInterfaces.push(...ifaces);
|
|
142
|
+
}
|
|
143
|
+
return allInterfaces.length > 0 ? allInterfaces : ["lo"];
|
|
144
|
+
}
|
|
145
|
+
// Specific IP - look up which interface(s) have this IP
|
|
146
|
+
const interfaces = interfaceMap.get(ip);
|
|
147
|
+
if (interfaces && interfaces.length > 0) {
|
|
148
|
+
return interfaces;
|
|
149
|
+
}
|
|
150
|
+
// Unknown - assume localhost for safety
|
|
151
|
+
return ["lo"];
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=ss-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ss-parser.js","sourceRoot":"","sources":["../../../src/system/parsers/ss-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAChC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,YAAmC;IAEnC,MAAM,QAAQ,GAAuB,EAAE,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,8BAA8B;QAC9B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACzE,SAAS;QACX,CAAC;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QAChD,IAAI,OAAO,EAAE,CAAC;YACZ,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,YAAmC;IACpE,sBAAsB;IACtB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEvC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gCAAgC;IAChC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;IACzC,IAAI,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6DAA6D;IAC7D,wDAAwD;IACxD,IAAI,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAE7B,wCAAwC;IACxC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,0CAA0C;QAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC5B,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;gBACzB,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;IACjD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,+CAA+C;IAC/C,yCAAyC;IACzC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACjE,MAAM,WAAW,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC;IACnD,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEzE,uBAAuB;IACvB,MAAM,UAAU,GAAG,iBAAiB,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;IAEvD,MAAM,OAAO,GAAqB;QAChC,IAAI;QACJ,QAAQ,EAAE,QAAyB;QACnC,OAAO,EAAE,WAAW;QACpB,GAAG;QACH,UAAU;QACV,OAAO,EAAE,EAAE;KACZ,CAAC;IAEF,oBAAoB;IACpB,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACzD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,IAAY;IAChC,qCAAqC;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IACrD,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACrD,OAAO;YACL,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI;YACxB,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;SACvC,CAAC;IACJ,CAAC;IAED,4CAA4C;IAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAChD,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACrD,OAAO;YACL,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS;YAC7B,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;SACvC,CAAC;IACJ,CAAC;IAED,iDAAiD;IACjD,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC1C,OAAO;YACL,EAAE,EAAE,SAAS;YACb,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;SACvC,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACvC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,EAAU,EAAE,YAAmC;IACxE,YAAY;IACZ,IAAI,EAAE,KAAK,WAAW,IAAI,EAAE,KAAK,KAAK,IAAI,EAAE,KAAK,WAAW,EAAE,CAAC;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,oCAAoC;IACpC,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;QAClD,MAAM,aAAa,GAAa,EAAE,CAAC;QACnC,KAAK,MAAM,MAAM,IAAI,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,aAAa,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;IAED,wDAAwD;IACxD,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACxC,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,wCAAwC;IACxC,OAAO,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized shell execution wrapper
|
|
3
|
+
* This is the ONLY place raw shell calls happen
|
|
4
|
+
*/
|
|
5
|
+
export interface CommandResult {
|
|
6
|
+
stdout: string;
|
|
7
|
+
stderr: string;
|
|
8
|
+
exitCode: number;
|
|
9
|
+
success: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ShellOptions {
|
|
12
|
+
timeout?: number;
|
|
13
|
+
ignoreErrors?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Execute a shell command with error handling
|
|
17
|
+
*/
|
|
18
|
+
export declare function execCommand(command: string, args?: string[], options?: ShellOptions): Promise<CommandResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Check if running with root privileges
|
|
21
|
+
*/
|
|
22
|
+
export declare function isRoot(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Check if a command exists in PATH
|
|
25
|
+
*/
|
|
26
|
+
export declare function commandExists(command: string): Promise<boolean>;
|
|
27
|
+
//# sourceMappingURL=shell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/system/shell.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,MAAM,EAAO,EACnB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,aAAa,CAAC,CA4BxB;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,OAAO,CAOhC;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGrE"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized shell execution wrapper
|
|
3
|
+
* This is the ONLY place raw shell calls happen
|
|
4
|
+
*/
|
|
5
|
+
import { execa } from "execa";
|
|
6
|
+
/**
|
|
7
|
+
* Execute a shell command with error handling
|
|
8
|
+
*/
|
|
9
|
+
export async function execCommand(command, args = [], options = {}) {
|
|
10
|
+
const timeout = options.timeout ?? 5000;
|
|
11
|
+
const ignoreErrors = options.ignoreErrors ?? false;
|
|
12
|
+
try {
|
|
13
|
+
const result = await execa(command, args, {
|
|
14
|
+
timeout,
|
|
15
|
+
reject: false, // Don't throw on non-zero exit codes
|
|
16
|
+
shell: false,
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
stdout: result.stdout,
|
|
20
|
+
stderr: result.stderr,
|
|
21
|
+
exitCode: result.exitCode ?? 0,
|
|
22
|
+
success: result.exitCode === 0,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (ignoreErrors) {
|
|
27
|
+
return {
|
|
28
|
+
stdout: "",
|
|
29
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
30
|
+
exitCode: 1,
|
|
31
|
+
success: false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if running with root privileges
|
|
39
|
+
*/
|
|
40
|
+
export function isRoot() {
|
|
41
|
+
try {
|
|
42
|
+
return process.getuid?.() === 0;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// getuid not available (e.g., on Windows)
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if a command exists in PATH
|
|
51
|
+
*/
|
|
52
|
+
export async function commandExists(command) {
|
|
53
|
+
const result = await execCommand("which", [command], { ignoreErrors: true });
|
|
54
|
+
return result.success;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/system/shell.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAc9B;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAe,EACf,OAAiB,EAAE,EACnB,UAAwB,EAAE;IAE1B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC;IACxC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,KAAK,CAAC;IAEnD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YACxC,OAAO;YACP,MAAM,EAAE,KAAK,EAAE,qCAAqC;YACpD,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;QAEH,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,CAAC;YAC9B,OAAO,EAAE,MAAM,CAAC,QAAQ,KAAK,CAAC;SAC/B,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO;gBACL,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;gBAC9D,QAAQ,EAAE,CAAC;gBACX,OAAO,EAAE,KAAK;aACf,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,MAAM;IACpB,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,0CAA0C;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe;IACjD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7E,OAAO,MAAM,CAAC,OAAO,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings.d.ts","sourceRoot":"","sources":["../../src/ui/findings.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAGlD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAwC1D"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Findings rendering - grouped by severity
|
|
3
|
+
*/
|
|
4
|
+
import { colors, icons, spacing } from "./theme.js";
|
|
5
|
+
export function renderFindings(findings) {
|
|
6
|
+
if (findings.length === 0) {
|
|
7
|
+
return `${spacing.section}${colors.success(`${icons.tick} No security issues detected.`)}`;
|
|
8
|
+
}
|
|
9
|
+
const lines = [];
|
|
10
|
+
// Group by severity
|
|
11
|
+
const critical = findings.filter((f) => f.severity === "critical");
|
|
12
|
+
const warnings = findings.filter((f) => f.severity === "warning");
|
|
13
|
+
const info = findings.filter((f) => f.severity === "info");
|
|
14
|
+
// Render critical findings
|
|
15
|
+
if (critical.length > 0) {
|
|
16
|
+
lines.push(spacing.section);
|
|
17
|
+
lines.push(colors.critical.bold("CRITICAL"));
|
|
18
|
+
for (const finding of critical) {
|
|
19
|
+
lines.push(renderFinding(finding));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Render warnings
|
|
23
|
+
if (warnings.length > 0) {
|
|
24
|
+
lines.push(spacing.section);
|
|
25
|
+
lines.push(colors.warning.bold("WARNINGS"));
|
|
26
|
+
for (const finding of warnings) {
|
|
27
|
+
lines.push(renderFinding(finding));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Render info
|
|
31
|
+
if (info.length > 0) {
|
|
32
|
+
lines.push(spacing.section);
|
|
33
|
+
lines.push(colors.info.bold("INFO"));
|
|
34
|
+
for (const finding of info) {
|
|
35
|
+
lines.push(renderFinding(finding));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return lines.join(spacing.line);
|
|
39
|
+
}
|
|
40
|
+
function renderFinding(finding) {
|
|
41
|
+
const lines = [];
|
|
42
|
+
// Icon + title
|
|
43
|
+
const icon = getIcon(finding);
|
|
44
|
+
const colorFn = getColor(finding.severity);
|
|
45
|
+
lines.push(colorFn(`${icon} ${finding.title}`));
|
|
46
|
+
// Description with indentation
|
|
47
|
+
const descLines = finding.description.split("\n");
|
|
48
|
+
for (const line of descLines) {
|
|
49
|
+
lines.push(`${spacing.indent}${line}`);
|
|
50
|
+
}
|
|
51
|
+
// Recommendation if present
|
|
52
|
+
if (finding.recommendation) {
|
|
53
|
+
lines.push("");
|
|
54
|
+
lines.push(colors.dim(`${spacing.indent}Recommendation:`));
|
|
55
|
+
lines.push(`${spacing.doubleIndent}${finding.recommendation}`);
|
|
56
|
+
}
|
|
57
|
+
return lines.join(spacing.line);
|
|
58
|
+
}
|
|
59
|
+
function getIcon(finding) {
|
|
60
|
+
// Use icon override if specified
|
|
61
|
+
if (finding.icon === "warning") {
|
|
62
|
+
return icons.warning;
|
|
63
|
+
}
|
|
64
|
+
if (finding.icon === "tick") {
|
|
65
|
+
return icons.tick;
|
|
66
|
+
}
|
|
67
|
+
// Default icon based on severity
|
|
68
|
+
switch (finding.severity) {
|
|
69
|
+
case "critical":
|
|
70
|
+
return icons.cross;
|
|
71
|
+
case "warning":
|
|
72
|
+
return icons.warning;
|
|
73
|
+
case "info":
|
|
74
|
+
return icons.tick;
|
|
75
|
+
default:
|
|
76
|
+
return icons.info;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function getColor(severity) {
|
|
80
|
+
switch (severity) {
|
|
81
|
+
case "critical":
|
|
82
|
+
return colors.critical;
|
|
83
|
+
case "warning":
|
|
84
|
+
return colors.warning;
|
|
85
|
+
case "info":
|
|
86
|
+
return colors.info;
|
|
87
|
+
default:
|
|
88
|
+
return (text) => text;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=findings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings.js","sourceRoot":"","sources":["../../src/ui/findings.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEpD,MAAM,UAAU,cAAc,CAAC,QAAmB;IAChD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,GAAG,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,IAAI,+BAA+B,CAAC,EAAE,CAAC;IAC7F,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,oBAAoB;IACpB,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC;IAE3D,2BAA2B;IAC3B,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAC7C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,kBAAkB;IAClB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAC5C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,cAAc;IACd,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC5B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACrC,KAAK,MAAM,OAAO,IAAI,IAAI,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,aAAa,CAAC,OAAgB;IACrC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,eAAe;IACf,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9B,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAEhD,+BAA+B;IAC/B,MAAM,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,4BAA4B;IAC5B,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,iBAAiB,CAAC,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,YAAY,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,OAAO,CAAC,OAAgB;IAC/B,iCAAiC;IACjC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IAED,iCAAiC;IACjC,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,UAAU;YACb,OAAO,KAAK,CAAC,KAAK,CAAC;QACrB,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC,OAAO,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB;YACE,OAAO,KAAK,CAAC,IAAI,CAAC;IACtB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,QAAgB;IAChC,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,UAAU;YACb,OAAO,MAAM,CAAC,QAAQ,CAAC;QACzB,KAAK,SAAS;YACZ,OAAO,MAAM,CAAC,OAAO,CAAC;QACxB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB;YACE,OAAO,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC;IAClC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main report renderer
|
|
3
|
+
*/
|
|
4
|
+
import type { Finding, ListeningService } from "../engine/types.js";
|
|
5
|
+
export interface ScanReport {
|
|
6
|
+
services: ListeningService[];
|
|
7
|
+
findings: Finding[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Render the complete security scan report
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderReport(report: ScanReport): string;
|
|
13
|
+
/**
|
|
14
|
+
* Determine exit code based on findings
|
|
15
|
+
* Returns 1 if critical findings exist, 0 otherwise
|
|
16
|
+
*/
|
|
17
|
+
export declare function getExitCode(findings: Finding[]): number;
|
|
18
|
+
//# sourceMappingURL=renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/ui/renderer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAMpE,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAkBvD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAGvD"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main report renderer
|
|
3
|
+
*/
|
|
4
|
+
import { isRoot } from "../system/shell.js";
|
|
5
|
+
import { renderFindings } from "./findings.js";
|
|
6
|
+
import { calculateStats, renderSummary } from "./summary.js";
|
|
7
|
+
import { dim, spacing } from "./theme.js";
|
|
8
|
+
/**
|
|
9
|
+
* Render the complete security scan report
|
|
10
|
+
*/
|
|
11
|
+
export function renderReport(report) {
|
|
12
|
+
const { services, findings } = report;
|
|
13
|
+
const root = isRoot();
|
|
14
|
+
const lines = [];
|
|
15
|
+
// Summary section
|
|
16
|
+
const stats = calculateStats(services, findings, root);
|
|
17
|
+
lines.push(renderSummary(stats));
|
|
18
|
+
// Findings sections
|
|
19
|
+
lines.push(renderFindings(findings));
|
|
20
|
+
// Footer - trust-building message
|
|
21
|
+
lines.push(spacing.section);
|
|
22
|
+
lines.push(dim("No changes were made to your system."));
|
|
23
|
+
return lines.join(spacing.line);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Determine exit code based on findings
|
|
27
|
+
* Returns 1 if critical findings exist, 0 otherwise
|
|
28
|
+
*/
|
|
29
|
+
export function getExitCode(findings) {
|
|
30
|
+
const hasCritical = findings.some((f) => f.severity === "critical");
|
|
31
|
+
return hasCritical ? 1 : 0;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../src/ui/renderer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAO1C;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAkB;IAC7C,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IACtC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC;IAEtB,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,kBAAkB;IAClB,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IACvD,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IAEjC,oBAAoB;IACpB,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;IAErC,kCAAkC;IAClC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5B,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC,CAAC;IAExD,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,QAAmB;IAC7C,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC;IACpE,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summary section rendering
|
|
3
|
+
*/
|
|
4
|
+
import type { Finding, ListeningService } from "../engine/types.js";
|
|
5
|
+
export interface SummaryStats {
|
|
6
|
+
totalServices: number;
|
|
7
|
+
publiclyReachable: number;
|
|
8
|
+
criticalFindings: number;
|
|
9
|
+
warningFindings: number;
|
|
10
|
+
isRoot: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function renderSummary(stats: SummaryStats): string;
|
|
13
|
+
/**
|
|
14
|
+
* Calculate summary statistics from services and findings
|
|
15
|
+
*/
|
|
16
|
+
export declare function calculateStats(services: ListeningService[], findings: Finding[], isRoot: boolean): SummaryStats;
|
|
17
|
+
//# sourceMappingURL=summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../../src/ui/summary.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGpE,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CA6BzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,QAAQ,EAAE,OAAO,EAAE,EACnB,MAAM,EAAE,OAAO,GACd,YAAY,CAYd"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summary section rendering
|
|
3
|
+
*/
|
|
4
|
+
import { bullet, colors, header, icons, spacing } from "./theme.js";
|
|
5
|
+
export function renderSummary(stats) {
|
|
6
|
+
const lines = [];
|
|
7
|
+
// Title
|
|
8
|
+
lines.push(`${icons.crab} ${colors.crab.bold("Safecrab Security Scan")}`);
|
|
9
|
+
lines.push("");
|
|
10
|
+
// Root warning if not running as root
|
|
11
|
+
if (!stats.isRoot) {
|
|
12
|
+
lines.push(colors.warning(`${icons.warning} Running without root may hide some services and can show incorrect firewall or process info.`));
|
|
13
|
+
lines.push(colors.dim(" For full visibility, run Safecrab with sudo."));
|
|
14
|
+
lines.push("");
|
|
15
|
+
}
|
|
16
|
+
// Summary statistics
|
|
17
|
+
lines.push(header("Summary:"));
|
|
18
|
+
lines.push(bullet(`${stats.totalServices} services detected`));
|
|
19
|
+
lines.push(bullet(`${stats.publiclyReachable} publicly reachable`));
|
|
20
|
+
lines.push(bullet(`${stats.criticalFindings} critical issues`));
|
|
21
|
+
if (stats.warningFindings > 0) {
|
|
22
|
+
lines.push(bullet(`${stats.warningFindings} warnings`));
|
|
23
|
+
}
|
|
24
|
+
return lines.join(spacing.line);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Calculate summary statistics from services and findings
|
|
28
|
+
*/
|
|
29
|
+
export function calculateStats(services, findings, isRoot) {
|
|
30
|
+
const publiclyReachable = new Set(findings.filter((f) => f.service && f.severity !== "info").map((f) => f.service?.port)).size;
|
|
31
|
+
return {
|
|
32
|
+
totalServices: services.length,
|
|
33
|
+
publiclyReachable,
|
|
34
|
+
criticalFindings: findings.filter((f) => f.severity === "critical").length,
|
|
35
|
+
warningFindings: findings.filter((f) => f.severity === "warning").length,
|
|
36
|
+
isRoot,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/ui/summary.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAUpE,MAAM,UAAU,aAAa,CAAC,KAAmB;IAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,QAAQ;IACR,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC;IAC1E,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,sCAAsC;IACtC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,KAAK,CAAC,IAAI,CACR,MAAM,CAAC,OAAO,CACZ,GAAG,KAAK,CAAC,OAAO,+FAA+F,CAChH,CACF,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC,CAAC;QAC1E,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,qBAAqB;IACrB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,aAAa,oBAAoB,CAAC,CAAC,CAAC;IAC/D,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,iBAAiB,qBAAqB,CAAC,CAAC,CAAC;IACpE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,gBAAgB,kBAAkB,CAAC,CAAC,CAAC;IAEhE,IAAI,KAAK,CAAC,eAAe,GAAG,CAAC,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,eAAe,WAAW,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,QAA4B,EAC5B,QAAmB,EACnB,MAAe;IAEf,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAC/B,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CACvF,CAAC,IAAI,CAAC;IAEP,OAAO;QACL,aAAa,EAAE,QAAQ,CAAC,MAAM;QAC9B,iBAAiB;QACjB,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM;QAC1E,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM;QACxE,MAAM;KACP,CAAC;AACJ,CAAC"}
|