vh-dev-helper 0.0.0-stage → 1.0.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/dist/bin/index.d.ts +2 -0
- package/dist/bin/index.js +5 -0
- package/dist/src/commands/dashbaord/index.d.ts +2 -0
- package/dist/src/commands/dashbaord/index.js +38 -0
- package/dist/src/commands/dashbaord/project.command.d.ts +2 -0
- package/dist/src/commands/dashbaord/project.command.js +17 -0
- package/dist/src/commands/git/bg.command.d.ts +2 -0
- package/dist/src/commands/git/bg.command.js +17 -0
- package/dist/src/commands/git/cleanup.command.d.ts +2 -0
- package/dist/src/commands/git/cleanup.command.js +12 -0
- package/dist/src/commands/git/cp.command.d.ts +2 -0
- package/dist/src/commands/git/cp.command.js +68 -0
- package/dist/src/commands/git/del.command.d.ts +2 -0
- package/dist/src/commands/git/del.command.js +19 -0
- package/dist/src/commands/git/fetch.command.d.ts +2 -0
- package/dist/src/commands/git/fetch.command.js +12 -0
- package/dist/src/commands/git/final.command.d.ts +2 -0
- package/dist/src/commands/git/final.command.js +23 -0
- package/dist/src/commands/git/ga.command.d.ts +2 -0
- package/dist/src/commands/git/ga.command.js +22 -0
- package/dist/src/commands/git/gcm.command.d.ts +2 -0
- package/dist/src/commands/git/gcm.command.js +23 -0
- package/dist/src/commands/git/gco.command.d.ts +2 -0
- package/dist/src/commands/git/gco.command.js +27 -0
- package/dist/src/commands/git/gcob.command.d.ts +2 -0
- package/dist/src/commands/git/gcob.command.js +23 -0
- package/dist/src/commands/git/gph.command.d.ts +2 -0
- package/dist/src/commands/git/gph.command.js +29 -0
- package/dist/src/commands/git/gpho.command.d.ts +2 -0
- package/dist/src/commands/git/gpho.command.js +20 -0
- package/dist/src/commands/git/gpl.command.d.ts +2 -0
- package/dist/src/commands/git/gpl.command.js +33 -0
- package/dist/src/commands/git/gplo.command.d.ts +2 -0
- package/dist/src/commands/git/gplo.command.js +20 -0
- package/dist/src/commands/git/gpu.command.d.ts +2 -0
- package/dist/src/commands/git/gpu.command.js +23 -0
- package/dist/src/commands/git/gr.command.d.ts +2 -0
- package/dist/src/commands/git/gr.command.js +20 -0
- package/dist/src/commands/git/graph.command.d.ts +2 -0
- package/dist/src/commands/git/graph.command.js +12 -0
- package/dist/src/commands/git/gs.command.d.ts +2 -0
- package/dist/src/commands/git/gs.command.js +23 -0
- package/dist/src/commands/git/index.d.ts +2 -0
- package/dist/src/commands/git/index.js +52 -0
- package/dist/src/commands/git/init.command.d.ts +2 -0
- package/dist/src/commands/git/init.command.js +12 -0
- package/dist/src/commands/git/merge.command.d.ts +2 -0
- package/dist/src/commands/git/merge.command.js +13 -0
- package/dist/src/commands/git/sync.command.d.ts +2 -0
- package/dist/src/commands/git/sync.command.js +12 -0
- package/dist/src/commands/git/undo.command.d.ts +2 -0
- package/dist/src/commands/git/undo.command.js +21 -0
- package/dist/src/commands/git/untrack.command.d.ts +2 -0
- package/dist/src/commands/git/untrack.command.js +27 -0
- package/dist/src/commands/index.d.ts +1 -0
- package/dist/src/commands/index.js +15 -0
- package/dist/src/commands/network/discover.command.d.ts +2 -0
- package/dist/src/commands/network/discover.command.js +24 -0
- package/dist/src/commands/network/index.d.ts +2 -0
- package/dist/src/commands/network/index.js +47 -0
- package/dist/src/commands/network/inspect.command.d.ts +2 -0
- package/dist/src/commands/network/inspect.command.js +34 -0
- package/dist/src/commands/network/ports.command.d.ts +2 -0
- package/dist/src/commands/network/ports.command.js +27 -0
- package/dist/src/commands/network/ssl.command.d.ts +2 -0
- package/dist/src/commands/network/ssl.command.js +32 -0
- package/dist/src/commands/network/trace.command.d.ts +2 -0
- package/dist/src/commands/network/trace.command.js +30 -0
- package/dist/src/core/helper/command.helper.d.ts +1 -0
- package/dist/src/core/helper/command.helper.js +28 -0
- package/dist/src/core/helper/git.helper.d.ts +2 -0
- package/dist/src/core/helper/git.helper.js +58 -0
- package/dist/src/core/helper/prompts.helper.d.ts +5 -0
- package/dist/src/core/helper/prompts.helper.js +83 -0
- package/dist/src/core/shell/exec.d.ts +1 -0
- package/dist/src/core/shell/exec.js +38 -0
- package/dist/src/dashboards/collectors/project/project.collector.d.ts +9 -0
- package/dist/src/dashboards/collectors/project/project.collector.js +29 -0
- package/dist/src/dashboards/project/backend/server/dashboard.bootstrap.d.ts +6 -0
- package/dist/src/dashboards/project/backend/server/dashboard.bootstrap.js +45 -0
- package/dist/src/dashboards/project/backend/server/dashboard.server.d.ts +6 -0
- package/dist/src/dashboards/project/backend/server/dashboard.server.js +25 -0
- package/dist/src/dashboards/project/backend/server/websocket.server.d.ts +8 -0
- package/dist/src/dashboards/project/backend/server/websocket.server.js +28 -0
- package/dist/src/dashboards/project/backend/services/git.service.d.ts +9 -0
- package/dist/src/dashboards/project/backend/services/git.service.js +18 -0
- package/dist/src/dashboards/project/backend/services/metrics.service.d.ts +7 -0
- package/dist/src/dashboards/project/backend/services/metrics.service.js +54 -0
- package/dist/src/dashboards/project/backend/services/project.service.d.ts +10 -0
- package/dist/src/dashboards/project/backend/services/project.service.js +19 -0
- package/dist/src/dashboards/project/backend/services/system.service.d.ts +15 -0
- package/dist/src/dashboards/project/backend/services/system.service.js +42 -0
- package/dist/src/dashboards/project/backend/wrapper/process.wrapper.d.ts +13 -0
- package/dist/src/dashboards/project/backend/wrapper/process.wrapper.js +56 -0
- package/dist/src/services/git/git.service.d.ts +37 -0
- package/dist/src/services/git/git.service.js +303 -0
- package/dist/src/services/network/network.service.d.ts +23 -0
- package/dist/src/services/network/network.service.js +524 -0
- package/package.json +38 -4
- package/README.md +0 -3
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.networkService = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const exec_1 = require("../../core/shell/exec");
|
|
9
|
+
class NetworkService {
|
|
10
|
+
async discover(target) {
|
|
11
|
+
try {
|
|
12
|
+
console.log(chalk_1.default.cyan.bold("\n📡 NETWORK DISCOVERY\n"));
|
|
13
|
+
if (target === "localhost") {
|
|
14
|
+
await this.discoverLocal();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (target.includes("/")) {
|
|
18
|
+
await this.discoverSubnet(target);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const privateIp = /^(10\.|172\.|192\.168)/.test(target);
|
|
22
|
+
if (privateIp) {
|
|
23
|
+
await this.inspectDevice(target);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
await this.discoverHost(target);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error(error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async discoverLocal() {
|
|
33
|
+
console.log(chalk_1.default.yellow("🌐 Discovering Local Devices\n"));
|
|
34
|
+
const subnet = (await (0, exec_1.exec)(`
|
|
35
|
+
ipconfig getifaddr en0 |
|
|
36
|
+
awk -F. '{print $1"."$2"."$3".0/24"}'
|
|
37
|
+
`, true)).trim();
|
|
38
|
+
console.log(`Subnet : ${subnet}\n`);
|
|
39
|
+
await this.discoverSubnet(subnet);
|
|
40
|
+
}
|
|
41
|
+
async discoverSubnet(subnet) {
|
|
42
|
+
console.log(chalk_1.default.yellow("🔍 Scanning subnet...\n"));
|
|
43
|
+
await (0, exec_1.exec)(`
|
|
44
|
+
for ip in $(seq 1 254)
|
|
45
|
+
do
|
|
46
|
+
ping -c 1 -W 1 ${subnet.replace("/24", "")}.$ip >/dev/null 2>&1 &
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
wait
|
|
50
|
+
`, true);
|
|
51
|
+
const arp = await (0, exec_1.exec)(`arp -a`, true);
|
|
52
|
+
const devices = arp.split("\n").filter(Boolean);
|
|
53
|
+
console.log(chalk_1.default.yellow("📱 Devices"));
|
|
54
|
+
for (const line of devices) {
|
|
55
|
+
const ip = line.match(/\((.*?)\)/)?.[1];
|
|
56
|
+
const mac = line.match(/at (.*?) /)?.[1];
|
|
57
|
+
if (!ip) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
console.log(`${chalk_1.default.green(ip)} | ${mac || "Unknown MAC"}`);
|
|
61
|
+
}
|
|
62
|
+
console.log(chalk_1.default.green("\n✅ Discovery Complete\n"));
|
|
63
|
+
}
|
|
64
|
+
async inspectDevice(ip) {
|
|
65
|
+
console.log(chalk_1.default.yellow("🖥 Device Inspection\n"));
|
|
66
|
+
const hostname = (await (0, exec_1.exec)(`nslookup ${ip}`, true))
|
|
67
|
+
.split("\n")
|
|
68
|
+
.find((x) => x.includes("name"));
|
|
69
|
+
console.log(`IP : ${ip}`);
|
|
70
|
+
console.log(`Hostname : ${hostname || "Unknown"}`);
|
|
71
|
+
console.log("\nScanning ports...\n");
|
|
72
|
+
await this.ports(ip, {});
|
|
73
|
+
}
|
|
74
|
+
async discoverHost(host) {
|
|
75
|
+
console.log(chalk_1.default.yellow("🌐 Host Discovery\n"));
|
|
76
|
+
const ip = (await (0, exec_1.exec)(`dig +short ${host} | head -n1`, true)).trim();
|
|
77
|
+
const info = JSON.parse(await (0, exec_1.exec)(`curl -s ipinfo.io/${ip}`, true));
|
|
78
|
+
console.log(`Domain : ${host}`);
|
|
79
|
+
console.log(`IP : ${ip}`);
|
|
80
|
+
console.log(`Provider : ${info.org}`);
|
|
81
|
+
console.log(`Country : ${info.country}`);
|
|
82
|
+
console.log(`Region : ${info.region}`);
|
|
83
|
+
console.log("\nPort Scan\n");
|
|
84
|
+
await this.ports(host, {});
|
|
85
|
+
}
|
|
86
|
+
async ports(host, options) {
|
|
87
|
+
try {
|
|
88
|
+
console.log(chalk_1.default.cyan.bold("\n🔌 PORT SCAN\n"));
|
|
89
|
+
console.log(chalk_1.default.yellow("🎯 Target"));
|
|
90
|
+
console.log(`Host : ${host}\n`);
|
|
91
|
+
const DEFAULT = [
|
|
92
|
+
[22, "SSH"],
|
|
93
|
+
[80, "HTTP"],
|
|
94
|
+
[443, "HTTPS"],
|
|
95
|
+
[3306, "MySQL"],
|
|
96
|
+
[5432, "Postgres"],
|
|
97
|
+
[6379, "Redis"],
|
|
98
|
+
[27017, "MongoDB"],
|
|
99
|
+
[3000, "Node"],
|
|
100
|
+
[8080, "HTTP Alt"],
|
|
101
|
+
[21, "FTP"],
|
|
102
|
+
[25, "SMTP"],
|
|
103
|
+
[53, "DNS"],
|
|
104
|
+
[110, "POP3"],
|
|
105
|
+
[143, "IMAP"],
|
|
106
|
+
];
|
|
107
|
+
const EXTENDED = [
|
|
108
|
+
...DEFAULT,
|
|
109
|
+
[23, "TELNET"],
|
|
110
|
+
[445, "SMB"],
|
|
111
|
+
[587, "SMTP"],
|
|
112
|
+
[993, "IMAPS"],
|
|
113
|
+
[995, "POP3S"],
|
|
114
|
+
[3389, "RDP"],
|
|
115
|
+
[5900, "VNC"],
|
|
116
|
+
[9200, "Elastic"],
|
|
117
|
+
[8443, "HTTPS Alt"],
|
|
118
|
+
[9092, "Kafka"],
|
|
119
|
+
[2375, "Docker"],
|
|
120
|
+
[27018, "Mongo Replica"],
|
|
121
|
+
[5000, "Flask"],
|
|
122
|
+
[4000, "Dev"],
|
|
123
|
+
];
|
|
124
|
+
let ports;
|
|
125
|
+
if (options.ports) {
|
|
126
|
+
ports = options.ports
|
|
127
|
+
.split(",")
|
|
128
|
+
.map((x) => Number(x.trim()))
|
|
129
|
+
.filter(Boolean)
|
|
130
|
+
.map((p) => [p, "Custom"]);
|
|
131
|
+
}
|
|
132
|
+
else if (options.full) {
|
|
133
|
+
ports = Array.from({
|
|
134
|
+
length: 65535,
|
|
135
|
+
}, (_, i) => [i + 1, "Unknown"]);
|
|
136
|
+
}
|
|
137
|
+
else if (options.extended) {
|
|
138
|
+
ports = EXTENDED;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
ports = DEFAULT;
|
|
142
|
+
}
|
|
143
|
+
console.log(chalk_1.default.yellow("🔎 Scanning"));
|
|
144
|
+
console.log(`Ports : ${ports.length}\n`);
|
|
145
|
+
const start = Date.now();
|
|
146
|
+
const BATCH = options.full ? 100 : 50;
|
|
147
|
+
const openPorts = [];
|
|
148
|
+
for (let i = 0; i < ports.length; i += BATCH) {
|
|
149
|
+
const chunk = ports.slice(i, i + BATCH);
|
|
150
|
+
const results = await Promise.all(chunk.map(async ([port, service]) => {
|
|
151
|
+
try {
|
|
152
|
+
await (0, exec_1.exec)(`
|
|
153
|
+
nc \
|
|
154
|
+
-z \
|
|
155
|
+
-G 1 \
|
|
156
|
+
${host} \
|
|
157
|
+
${port}
|
|
158
|
+
`, true);
|
|
159
|
+
return {
|
|
160
|
+
port,
|
|
161
|
+
service,
|
|
162
|
+
open: true,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}));
|
|
169
|
+
openPorts.push(...results.filter(Boolean));
|
|
170
|
+
process.stdout.write(`Progress : ${Math.min(i + BATCH, ports.length)}/${ports.length}\r`);
|
|
171
|
+
}
|
|
172
|
+
console.log("\n");
|
|
173
|
+
console.log(chalk_1.default.yellow("📂 Open Ports"));
|
|
174
|
+
if (!openPorts.length) {
|
|
175
|
+
console.log(chalk_1.default.red("No open ports found"));
|
|
176
|
+
}
|
|
177
|
+
openPorts.forEach((port) => {
|
|
178
|
+
console.log(`${chalk_1.default.green(port.port)} → ${port.service}`);
|
|
179
|
+
});
|
|
180
|
+
console.log("\n");
|
|
181
|
+
console.log(chalk_1.default.yellow("📊 Summary"));
|
|
182
|
+
console.log(`Ports Checked : ${ports.length}`);
|
|
183
|
+
console.log(`Open Ports : ${openPorts.length}`);
|
|
184
|
+
console.log(`Scan Time : ${Date.now() - start} ms`);
|
|
185
|
+
console.log("\n");
|
|
186
|
+
console.log(chalk_1.default.yellow("🚨 Analysis"));
|
|
187
|
+
if (openPorts.some((x) => x.port === 22)) {
|
|
188
|
+
console.log(chalk_1.default.yellow("SSH reachable from internet"));
|
|
189
|
+
}
|
|
190
|
+
const databases = openPorts.filter((x) => [3306, 5432, 6379, 27017].includes(x.port));
|
|
191
|
+
if (databases.length) {
|
|
192
|
+
console.log(chalk_1.default.red("Database services exposed"));
|
|
193
|
+
}
|
|
194
|
+
console.log(chalk_1.default.green("\n✅ Scan complete\n"));
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
console.log(chalk_1.default.red("❌ Scan failed"));
|
|
198
|
+
console.error(error);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async trace(host) {
|
|
202
|
+
try {
|
|
203
|
+
console.log(chalk_1.default.cyan.bold("\n🛰 NETWORK TRACE\n"));
|
|
204
|
+
console.log(chalk_1.default.yellow("🎯 Target"));
|
|
205
|
+
console.log(`Host : ${host}\n`);
|
|
206
|
+
const resolvedIp = (await (0, exec_1.exec)(`dig +short ${host} | head -n1`, true)).trim();
|
|
207
|
+
console.log(chalk_1.default.yellow("🌍 DNS Resolution"));
|
|
208
|
+
console.log(`Resolved IP : ${resolvedIp}\n`);
|
|
209
|
+
console.log(chalk_1.default.gray("Fast tracing...\n"));
|
|
210
|
+
const raw = await (0, exec_1.exec)(`
|
|
211
|
+
traceroute \
|
|
212
|
+
-m 8 \
|
|
213
|
+
-q 1 \
|
|
214
|
+
-w 200 \
|
|
215
|
+
-n \
|
|
216
|
+
${host}
|
|
217
|
+
`, true);
|
|
218
|
+
const hops = raw.split("\n").slice(1).filter(Boolean);
|
|
219
|
+
const uniqueIps = new Set();
|
|
220
|
+
let timeoutCount = 0;
|
|
221
|
+
let latencyTotal = 0;
|
|
222
|
+
let latencyCount = 0;
|
|
223
|
+
for (const hop of hops) {
|
|
224
|
+
const ip = hop.match(/(\d+\.\d+\.\d+\.\d+)/)?.[1];
|
|
225
|
+
if (ip && !ip.match(/^(10\.|172\.|192\.168|127\.|169\.254)/)) {
|
|
226
|
+
uniqueIps.add(ip);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
console.log(chalk_1.default.gray(`Enriching ${uniqueIps.size} public hops...\n`));
|
|
230
|
+
const ipCache = new Map();
|
|
231
|
+
await Promise.all([...uniqueIps].map(async (ip) => {
|
|
232
|
+
try {
|
|
233
|
+
const result = await (0, exec_1.exec)(`curl -s --max-time 1 ipinfo.io/${ip}`, true);
|
|
234
|
+
ipCache.set(ip, JSON.parse(result));
|
|
235
|
+
}
|
|
236
|
+
catch { }
|
|
237
|
+
}));
|
|
238
|
+
console.log(chalk_1.default.yellow("📍 Hop Details"));
|
|
239
|
+
for (const hop of hops) {
|
|
240
|
+
const cleaned = hop.trim();
|
|
241
|
+
if (cleaned.includes("*")) {
|
|
242
|
+
timeoutCount++;
|
|
243
|
+
console.log(chalk_1.default.red(cleaned));
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const ip = cleaned.match(/(\d+\.\d+\.\d+\.\d+)/)?.[1];
|
|
247
|
+
const latency = cleaned.match(/([0-9.]+)\s*ms/)?.[1];
|
|
248
|
+
if (latency) {
|
|
249
|
+
latencyTotal += Number(latency);
|
|
250
|
+
latencyCount++;
|
|
251
|
+
}
|
|
252
|
+
let extra = "";
|
|
253
|
+
if (ipCache.has(ip)) {
|
|
254
|
+
const info = ipCache.get(ip);
|
|
255
|
+
extra = chalk_1.default.gray(` | ${info.org || "Unknown"} | ${info.country || ""}`);
|
|
256
|
+
}
|
|
257
|
+
else if (ip) {
|
|
258
|
+
extra = chalk_1.default.gray(" | Private");
|
|
259
|
+
}
|
|
260
|
+
console.log(`${cleaned}${extra}`);
|
|
261
|
+
}
|
|
262
|
+
console.log("");
|
|
263
|
+
console.log(chalk_1.default.yellow("📊 Statistics"));
|
|
264
|
+
console.log(`Hop Count : ${hops.length}`);
|
|
265
|
+
console.log(`Timeout Hops : ${timeoutCount}`);
|
|
266
|
+
console.log(`Public Hops : ${uniqueIps.size}`);
|
|
267
|
+
console.log(`Avg Latency : ${latencyCount ? (latencyTotal / latencyCount).toFixed(2) : "N/A"} ms`);
|
|
268
|
+
console.log("");
|
|
269
|
+
console.log(chalk_1.default.yellow("🚨 Analysis"));
|
|
270
|
+
if (timeoutCount > 2) {
|
|
271
|
+
console.log(chalk_1.default.yellow("Packet filtering detected"));
|
|
272
|
+
}
|
|
273
|
+
console.log(chalk_1.default.green("\n✅ Trace complete\n"));
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
console.log(chalk_1.default.red("❌ Trace failed"));
|
|
277
|
+
console.error(error);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async ssl(domain) {
|
|
281
|
+
try {
|
|
282
|
+
console.log(chalk_1.default.cyan.bold("\n🔐 SSL INSPECT\n"));
|
|
283
|
+
console.log(`Domain : ${domain}\n`);
|
|
284
|
+
const cert = await (0, exec_1.exec)(`
|
|
285
|
+
echo | openssl s_client \
|
|
286
|
+
-connect ${domain}:443 \
|
|
287
|
+
-servername ${domain} \
|
|
288
|
+
-status \
|
|
289
|
+
2>/dev/null |
|
|
290
|
+
openssl x509 -text -fingerprint -noout
|
|
291
|
+
`, true);
|
|
292
|
+
const subject = cert.match(/Subject:\s*(.*)/)?.[1] || "Unknown";
|
|
293
|
+
const issuer = cert.match(/Issuer:\s*(.*)/)?.[1] || "Unknown";
|
|
294
|
+
const issued = cert.match(/Not Before:\s*(.*)/)?.[1] || "Unknown";
|
|
295
|
+
const expiry = cert.match(/Not After :\s*(.*)/)?.[1] || "Unknown";
|
|
296
|
+
const serial = cert.match(/Serial Number:\s*([A-Fa-f0-9:\s]+)/)?.[1]?.trim() ||
|
|
297
|
+
"Unknown";
|
|
298
|
+
const signature = cert.match(/Signature Algorithm:\s*(.*)/)?.[1] || "Unknown";
|
|
299
|
+
const keySize = cert.match(/Public-Key: \((.*?) bit\)/)?.[1] || "Unknown";
|
|
300
|
+
const fingerprint = cert.match(/SHA1 Fingerprint=(.*)/)?.[1] || "Unknown";
|
|
301
|
+
const org = subject.match(/O\s*=\s*([^,]+)/)?.[1];
|
|
302
|
+
const country = subject.match(/C\s*=\s*([^,]+)/)?.[1];
|
|
303
|
+
const sans = [...cert.matchAll(/DNS:([^,\n]+)/g)].map((m) => m[1]);
|
|
304
|
+
const protocol = await (0, exec_1.exec)(`
|
|
305
|
+
echo | openssl s_client \
|
|
306
|
+
-connect ${domain}:443 \
|
|
307
|
+
-servername ${domain} \
|
|
308
|
+
2>/dev/null |
|
|
309
|
+
grep "Protocol"
|
|
310
|
+
`, true);
|
|
311
|
+
const cipher = await (0, exec_1.exec)(`
|
|
312
|
+
echo | openssl s_client \
|
|
313
|
+
-connect ${domain}:443 \
|
|
314
|
+
-servername ${domain} \
|
|
315
|
+
2>/dev/null |
|
|
316
|
+
grep "Cipher is"
|
|
317
|
+
`, true);
|
|
318
|
+
const verify = await (0, exec_1.exec)(`
|
|
319
|
+
echo | openssl s_client \
|
|
320
|
+
-connect ${domain}:443 \
|
|
321
|
+
-servername ${domain} \
|
|
322
|
+
2>/dev/null |
|
|
323
|
+
grep "Verify return code"
|
|
324
|
+
`, true);
|
|
325
|
+
const headers = await (0, exec_1.exec)(`curl -I -s https://${domain}`, true);
|
|
326
|
+
const expiryDate = new Date(expiry);
|
|
327
|
+
const issuedDate = new Date(issued);
|
|
328
|
+
const daysLeft = Math.ceil((expiryDate.getTime() - Date.now()) / 86400000);
|
|
329
|
+
const certAge = Math.floor((Date.now() - issuedDate.getTime()) / 86400000);
|
|
330
|
+
const wildcard = sans.some((s) => s.includes("*"));
|
|
331
|
+
const selfSigned = issuer === subject;
|
|
332
|
+
const hsts = headers.toLowerCase().includes("strict-transport-security");
|
|
333
|
+
const ocsp = headers.toLowerCase().includes("ocsp");
|
|
334
|
+
console.log(chalk_1.default.yellow("🏢 Ownership"));
|
|
335
|
+
console.log(`Subject : ${subject}`);
|
|
336
|
+
console.log(`Organization : ${org || "Not Available"}`);
|
|
337
|
+
console.log(`Country : ${country || "Not Available"}`);
|
|
338
|
+
console.log(`Issued By : ${issuer}`);
|
|
339
|
+
console.log("");
|
|
340
|
+
console.log(chalk_1.default.yellow("📅 Validity"));
|
|
341
|
+
console.log(`Issued On : ${issued}`);
|
|
342
|
+
console.log(`Expires On : ${expiry}`);
|
|
343
|
+
console.log(`Cert Age : ${certAge} days`);
|
|
344
|
+
console.log(`Days Left : ${daysLeft}`);
|
|
345
|
+
console.log("");
|
|
346
|
+
console.log(chalk_1.default.yellow("🔒 Security"));
|
|
347
|
+
console.log(protocol.trim());
|
|
348
|
+
console.log(cipher.trim());
|
|
349
|
+
console.log(`Public Key : ${keySize} bit`);
|
|
350
|
+
console.log(`Signature : ${signature}`);
|
|
351
|
+
console.log(`Fingerprint : ${fingerprint}`);
|
|
352
|
+
console.log(`Serial : ${serial}`);
|
|
353
|
+
console.log("");
|
|
354
|
+
console.log(chalk_1.default.yellow("🛡 Trust"));
|
|
355
|
+
console.log(`Self Signed : ${selfSigned ? "Yes" : "No"}`);
|
|
356
|
+
console.log(`Trust Status : ${verify.trim()}`);
|
|
357
|
+
console.log("");
|
|
358
|
+
console.log(chalk_1.default.yellow("🌍 Domains"));
|
|
359
|
+
console.log(`Wildcard : ${wildcard ? "Yes" : "No"}`);
|
|
360
|
+
console.log(`SAN Count : ${sans.length}`);
|
|
361
|
+
sans.slice(0, 10).forEach((s) => console.log(` • ${s}`));
|
|
362
|
+
if (sans.length > 10) {
|
|
363
|
+
console.log(` ... +${sans.length - 10} more`);
|
|
364
|
+
}
|
|
365
|
+
console.log("");
|
|
366
|
+
console.log(chalk_1.default.yellow("⚙ Transport"));
|
|
367
|
+
console.log(`HSTS : ${hsts ? "Enabled" : "Disabled"}`);
|
|
368
|
+
console.log(`OCSP : ${ocsp ? "Present" : "Not Found"}`);
|
|
369
|
+
console.log("");
|
|
370
|
+
console.log(chalk_1.default.yellow("🚨 Status"));
|
|
371
|
+
if (daysLeft < 15) {
|
|
372
|
+
console.log(chalk_1.default.red("Certificate expiring soon"));
|
|
373
|
+
}
|
|
374
|
+
else if (daysLeft < 45) {
|
|
375
|
+
console.log(chalk_1.default.yellow("Monitor renewal"));
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
console.log(chalk_1.default.green("Healthy certificate"));
|
|
379
|
+
}
|
|
380
|
+
console.log(chalk_1.default.green("\n✅ SSL inspection complete\n"));
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
console.log(chalk_1.default.red("❌ SSL inspection failed"));
|
|
384
|
+
console.error(error);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async inspect(input) {
|
|
388
|
+
try {
|
|
389
|
+
const domain = input.replace(/^https?:\/\//, "").split("/")[0];
|
|
390
|
+
const random = Date.now();
|
|
391
|
+
const testUrl = `${input}${input.includes("?") ? "&" : "?"}nocache=${random}`;
|
|
392
|
+
console.log(chalk_1.default.cyan.bold("\n🌐 NETWORK INSPECT"));
|
|
393
|
+
console.log(chalk_1.default.gray(`Target : ${input}`));
|
|
394
|
+
console.log(chalk_1.default.gray(`Domain : ${domain}`));
|
|
395
|
+
console.log("");
|
|
396
|
+
await this.showDns(domain);
|
|
397
|
+
await this.showIp(domain);
|
|
398
|
+
await this.showRedirects(input);
|
|
399
|
+
await this.showHeaders(input);
|
|
400
|
+
await this.showStats(testUrl, false);
|
|
401
|
+
await this.showStats(testUrl, true);
|
|
402
|
+
console.log(chalk_1.default.green("\n✅ Done\n"));
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
console.log(chalk_1.default.red("\n❌ Inspection failed"));
|
|
406
|
+
console.error(error);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
formatTime(value) {
|
|
410
|
+
const num = Number(value);
|
|
411
|
+
if (num < 1) {
|
|
412
|
+
return `${(num * 1000).toFixed(3)} ms`;
|
|
413
|
+
}
|
|
414
|
+
return `${num.toFixed(3)} s`;
|
|
415
|
+
}
|
|
416
|
+
async showDns(domain) {
|
|
417
|
+
console.log(chalk_1.default.yellow("📡 DNS"));
|
|
418
|
+
const resolver = "8.8.8.8";
|
|
419
|
+
console.log(chalk_1.default.gray("\nResolver Used:"));
|
|
420
|
+
console.log(` ${resolver} (Google DNS)`);
|
|
421
|
+
const ns = await (0, exec_1.exec)(`dig NS +short ${domain}`, true);
|
|
422
|
+
const nameservers = ns.split("\n").filter(Boolean);
|
|
423
|
+
console.log(chalk_1.default.gray("\nNameservers:"));
|
|
424
|
+
if (nameservers.length) {
|
|
425
|
+
nameservers.forEach((server) => console.log(` ${server}`));
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
console.log(" none");
|
|
429
|
+
}
|
|
430
|
+
const cname = await (0, exec_1.exec)(`dig CNAME +short ${domain}`, true);
|
|
431
|
+
console.log(chalk_1.default.gray("\nCNAME:"));
|
|
432
|
+
console.log(cname.trim() || " none");
|
|
433
|
+
const records = await (0, exec_1.exec)(`dig A +short ${domain}`, true);
|
|
434
|
+
console.log(chalk_1.default.gray("\nA Records:"));
|
|
435
|
+
records
|
|
436
|
+
.split("\n")
|
|
437
|
+
.filter(Boolean)
|
|
438
|
+
.forEach((ip) => {
|
|
439
|
+
console.log(` ${domain} → ${chalk_1.default.green(ip)}`);
|
|
440
|
+
});
|
|
441
|
+
console.log("");
|
|
442
|
+
}
|
|
443
|
+
async showIp(domain) {
|
|
444
|
+
const ip = (await (0, exec_1.exec)(`dig +short ${domain} | head -n1`, true)).trim();
|
|
445
|
+
console.log(chalk_1.default.yellow("🌍 IP INFO"));
|
|
446
|
+
console.log(`Resolved IP : ${chalk_1.default.green(ip)}`);
|
|
447
|
+
if (!ip) {
|
|
448
|
+
console.log("");
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const raw = await (0, exec_1.exec)(`curl -s ipinfo.io/${ip}`, true);
|
|
452
|
+
const info = JSON.parse(raw);
|
|
453
|
+
console.log(`Country : ${info.country}`);
|
|
454
|
+
console.log(`City : ${info.city}`);
|
|
455
|
+
console.log(`Region : ${info.region}`);
|
|
456
|
+
console.log(`Provider : ${info.org}`);
|
|
457
|
+
console.log(`Timezone : ${info.timezone}`);
|
|
458
|
+
console.log("");
|
|
459
|
+
}
|
|
460
|
+
async showRedirects(url) {
|
|
461
|
+
console.log(chalk_1.default.yellow("↪ Redirect Chain"));
|
|
462
|
+
const chain = await (0, exec_1.exec)(`
|
|
463
|
+
curl -sIL "${url}" |
|
|
464
|
+
grep -Ei "HTTP/|location:"
|
|
465
|
+
`, true);
|
|
466
|
+
console.log(chain.trim() || "No redirects");
|
|
467
|
+
console.log("");
|
|
468
|
+
}
|
|
469
|
+
async showHeaders(url) {
|
|
470
|
+
console.log(chalk_1.default.yellow("🖥 Headers"));
|
|
471
|
+
const headers = await (0, exec_1.exec)(`curl -I -s "${url}"`, true);
|
|
472
|
+
headers
|
|
473
|
+
.split("\n")
|
|
474
|
+
.filter((line) => {
|
|
475
|
+
const lower = line.toLowerCase();
|
|
476
|
+
return (lower.startsWith("server") ||
|
|
477
|
+
lower.startsWith("cache-control") ||
|
|
478
|
+
lower.startsWith("content-type"));
|
|
479
|
+
})
|
|
480
|
+
.forEach((line) => console.log(line.trim()));
|
|
481
|
+
console.log("");
|
|
482
|
+
}
|
|
483
|
+
async showStats(url, follow) {
|
|
484
|
+
console.log(chalk_1.default.yellow(follow ? "📊 With Redirects" : "📊 Without Redirects"));
|
|
485
|
+
const raw = await (0, exec_1.exec)(`
|
|
486
|
+
curl ${follow ? "-L" : ""} \
|
|
487
|
+
-o /dev/null \
|
|
488
|
+
-s \
|
|
489
|
+
-w "
|
|
490
|
+
HTTP:%{http_code}
|
|
491
|
+
DNS:%{time_namelookup}
|
|
492
|
+
TCP:%{time_connect}
|
|
493
|
+
TLS:%{time_appconnect}
|
|
494
|
+
TTFB:%{time_starttransfer}
|
|
495
|
+
TOTAL:%{time_total}
|
|
496
|
+
REDIRECT:%{time_redirect}
|
|
497
|
+
COUNT:%{num_redirects}
|
|
498
|
+
REMOTE:%{remote_ip}
|
|
499
|
+
TYPE:%{content_type}
|
|
500
|
+
" \
|
|
501
|
+
"${url}"
|
|
502
|
+
`, true);
|
|
503
|
+
const stats = Object.fromEntries(raw
|
|
504
|
+
.trim()
|
|
505
|
+
.split("\n")
|
|
506
|
+
.map((line) => line.split(":")));
|
|
507
|
+
console.log(`HTTP : ${stats.HTTP}`);
|
|
508
|
+
console.log(`DNS : ${this.formatTime(stats.DNS)}`);
|
|
509
|
+
console.log(`TCP : ${this.formatTime(stats.TCP)}`);
|
|
510
|
+
console.log(`TLS : ${this.formatTime(stats.TLS)}`);
|
|
511
|
+
console.log(`TTFB : ${this.formatTime(stats.TTFB)}`);
|
|
512
|
+
console.log(`TOTAL : ${this.formatTime(stats.TOTAL)}`);
|
|
513
|
+
console.log(`REDIRECT : ${this.formatTime(stats.REDIRECT)}`);
|
|
514
|
+
console.log(`COUNT : ${stats.COUNT}`);
|
|
515
|
+
console.log(`REMOTE IP : ${stats.REMOTE}`);
|
|
516
|
+
console.log(`TYPE : ${stats.TYPE}`);
|
|
517
|
+
const total = Number(stats.TOTAL);
|
|
518
|
+
if (total > 1) {
|
|
519
|
+
console.log(chalk_1.default.red("⚠ Slow response detected"));
|
|
520
|
+
}
|
|
521
|
+
console.log("");
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
exports.networkService = new NetworkService();
|
package/package.json
CHANGED
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vh-dev-helper",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "commonjs",
|
|
5
|
+
"bin": {
|
|
6
|
+
"vh": "dist/bin/index.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsx bin/index.ts",
|
|
14
|
+
"prepack": "npm run build",
|
|
15
|
+
"completion": "tabtab install vh"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [],
|
|
18
|
+
"author": "",
|
|
19
|
+
"license": "ISC",
|
|
20
|
+
"description": "",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@inquirer/prompts": "^8.5.2",
|
|
23
|
+
"@types/express": "^5.0.6",
|
|
24
|
+
"@types/pidusage": "^2.0.5",
|
|
25
|
+
"@types/ws": "^8.18.1",
|
|
26
|
+
"chalk": "^5.6.2",
|
|
27
|
+
"commander": "^15.0.0",
|
|
28
|
+
"express": "^5.2.1",
|
|
29
|
+
"open": "^11.0.0",
|
|
30
|
+
"pidusage": "^4.0.1",
|
|
31
|
+
"systeminformation": "^5.31.7",
|
|
32
|
+
"tabtab": "^3.0.2",
|
|
33
|
+
"ws": "^8.21.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^25.9.1",
|
|
37
|
+
"tsx": "^4.22.4",
|
|
38
|
+
"typescript": "^6.0.3"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/README.md
DELETED