@judaharagao/opencode-ssh 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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -0
  3. package/dist/config/defaults.d.ts +11 -0
  4. package/dist/config/defaults.d.ts.map +1 -0
  5. package/dist/config/defaults.js +23 -0
  6. package/dist/config/defaults.js.map +1 -0
  7. package/dist/config/schema.d.ts +51 -0
  8. package/dist/config/schema.d.ts.map +1 -0
  9. package/dist/config/schema.js +41 -0
  10. package/dist/config/schema.js.map +1 -0
  11. package/dist/display/formatter.d.ts +41 -0
  12. package/dist/display/formatter.d.ts.map +1 -0
  13. package/dist/display/formatter.js +179 -0
  14. package/dist/display/formatter.js.map +1 -0
  15. package/dist/display/stream.d.ts +38 -0
  16. package/dist/display/stream.d.ts.map +1 -0
  17. package/dist/display/stream.js +102 -0
  18. package/dist/display/stream.js.map +1 -0
  19. package/dist/hooks.d.ts +13 -0
  20. package/dist/hooks.d.ts.map +1 -0
  21. package/dist/hooks.js +151 -0
  22. package/dist/hooks.js.map +1 -0
  23. package/dist/index.d.ts +7 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +19 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/security/allowlist.d.ts +23 -0
  28. package/dist/security/allowlist.d.ts.map +1 -0
  29. package/dist/security/allowlist.js +105 -0
  30. package/dist/security/allowlist.js.map +1 -0
  31. package/dist/security/blocklist.d.ts +35 -0
  32. package/dist/security/blocklist.d.ts.map +1 -0
  33. package/dist/security/blocklist.js +351 -0
  34. package/dist/security/blocklist.js.map +1 -0
  35. package/dist/security/policy.d.ts +40 -0
  36. package/dist/security/policy.d.ts.map +1 -0
  37. package/dist/security/policy.js +133 -0
  38. package/dist/security/policy.js.map +1 -0
  39. package/dist/security/validator.d.ts +32 -0
  40. package/dist/security/validator.d.ts.map +1 -0
  41. package/dist/security/validator.js +152 -0
  42. package/dist/security/validator.js.map +1 -0
  43. package/dist/ssh/audit.d.ts +47 -0
  44. package/dist/ssh/audit.d.ts.map +1 -0
  45. package/dist/ssh/audit.js +126 -0
  46. package/dist/ssh/audit.js.map +1 -0
  47. package/dist/ssh/connection.d.ts +73 -0
  48. package/dist/ssh/connection.d.ts.map +1 -0
  49. package/dist/ssh/connection.js +210 -0
  50. package/dist/ssh/connection.js.map +1 -0
  51. package/dist/ssh/executor.d.ts +49 -0
  52. package/dist/ssh/executor.d.ts.map +1 -0
  53. package/dist/ssh/executor.js +275 -0
  54. package/dist/ssh/executor.js.map +1 -0
  55. package/dist/ssh/manager.d.ts +43 -0
  56. package/dist/ssh/manager.d.ts.map +1 -0
  57. package/dist/ssh/manager.js +98 -0
  58. package/dist/ssh/manager.js.map +1 -0
  59. package/dist/ssh/sanitizer.d.ts +41 -0
  60. package/dist/ssh/sanitizer.d.ts.map +1 -0
  61. package/dist/ssh/sanitizer.js +108 -0
  62. package/dist/ssh/sanitizer.js.map +1 -0
  63. package/dist/tools.d.ts +7 -0
  64. package/dist/tools.d.ts.map +1 -0
  65. package/dist/tools.js +584 -0
  66. package/dist/tools.js.map +1 -0
  67. package/package.json +64 -0
@@ -0,0 +1,152 @@
1
+ import { getBuiltinBlocklist, createCustomEntries } from "./blocklist.js";
2
+ import { getAllowedCommands } from "./allowlist.js";
3
+ /**
4
+ * Validate a command against the security policy.
5
+ *
6
+ * Validation order:
7
+ * 1. Check against destructive blocklist → ALWAYS BLOCKED (no exceptions)
8
+ * 2. Check against risky blocklist → requires permission prompt
9
+ * 3. In restricted/read_only mode, check against allowlist
10
+ */
11
+ export function validateCommand(command, mode, extraBlocklist = []) {
12
+ const trimmed = command.trim();
13
+ if (!trimmed) {
14
+ return { safe: true, level: "safe" };
15
+ }
16
+ // ── Step 1: Check against ALL blocklist entries ──
17
+ const allBlocklistEntries = [
18
+ ...getBuiltinBlocklist(),
19
+ ...createCustomEntries(extraBlocklist),
20
+ ];
21
+ for (const entry of allBlocklistEntries) {
22
+ if (entry.pattern.test(trimmed)) {
23
+ // Destructive: always blocked, no override
24
+ if (entry.level === "destructive") {
25
+ return {
26
+ safe: false,
27
+ level: "destructive",
28
+ reason: `🚫 BLOCKED [${entry.id}]: ${entry.reason}`,
29
+ matched_rule: entry.id,
30
+ suggestions: getSuggestions(entry),
31
+ };
32
+ }
33
+ // Risky: requires permission prompt
34
+ if (entry.level === "risky") {
35
+ return {
36
+ safe: false,
37
+ level: "risky",
38
+ reason: `⚠️ REQUIRES APPROVAL [${entry.id}]: ${entry.reason}`,
39
+ matched_rule: entry.id,
40
+ suggestions: getSuggestions(entry),
41
+ };
42
+ }
43
+ }
44
+ }
45
+ // ── Step 2: In restricted/read_only mode, check allowlist ──
46
+ if (mode === "restricted" || mode === "read_only") {
47
+ const allowedCommands = getAllowedCommands(mode);
48
+ const isAllowed = allowedCommands.some((entry) => entry.pattern.test(trimmed));
49
+ if (!isAllowed) {
50
+ return {
51
+ safe: false,
52
+ level: "not_in_allowlist",
53
+ reason: `🔒 NOT ALLOWED in "${mode}" mode. Command not in the allowlist.`,
54
+ suggestions: [
55
+ `Switch to "full" mode to allow all non-blocked commands`,
56
+ `Add this command pattern to the allowlist via ssh.security_policy`,
57
+ ],
58
+ };
59
+ }
60
+ }
61
+ // ── Command is safe ──
62
+ return { safe: true, level: "safe" };
63
+ }
64
+ /**
65
+ * Check if a command is purely read-only (no side effects).
66
+ */
67
+ export function isReadOnlyCommand(command) {
68
+ const readOnlyPatterns = [
69
+ /^ls\b/,
70
+ /^ll\b/,
71
+ /^la\b/,
72
+ /^cat\b/,
73
+ /^head\b/,
74
+ /^tail\b/,
75
+ /^less\b/,
76
+ /^more\b/,
77
+ /^grep\b/,
78
+ /^rg\b/,
79
+ /^find\b/,
80
+ /^which\b/,
81
+ /^whereis\b/,
82
+ /^whoami\b/,
83
+ /^id\b/,
84
+ /^pwd\b/,
85
+ /^echo\b/,
86
+ /^date\b/,
87
+ /^uptime\b/,
88
+ /^uname\b/,
89
+ /^hostname\b/,
90
+ /^df\b/,
91
+ /^du\b/,
92
+ /^free\b/,
93
+ /^ps\b/,
94
+ /^wc\b/,
95
+ /^file\b/,
96
+ /^stat\b/,
97
+ /^tree\b/,
98
+ /^diff\b/,
99
+ /^md5sum\b/,
100
+ /^sha256sum\b/,
101
+ /^env\b/,
102
+ /^printenv\b/,
103
+ /^man\b/,
104
+ /^ping\b/,
105
+ /^dig\b/,
106
+ /^nslookup\b/,
107
+ /^docker\s+ps\b/,
108
+ /^docker\s+images\b/,
109
+ /^docker\s+logs\b/,
110
+ /^docker\s+inspect\b/,
111
+ /^kubectl\s+get\b/,
112
+ /^kubectl\s+describe\b/,
113
+ /^kubectl\s+logs\b/,
114
+ /^kubectl\s+top\b/,
115
+ /^systemctl\s+status\b/,
116
+ /^systemctl\s+list\b/,
117
+ /^journalctl\b/,
118
+ /^git\s+(status|log|diff|show|branch|remote)\b/,
119
+ ];
120
+ return readOnlyPatterns.some((p) => p.test(command.trim()));
121
+ }
122
+ /**
123
+ * Get safer alternatives for a blocked/risky command.
124
+ */
125
+ function getSuggestions(entry) {
126
+ const suggestions = {
127
+ DESTR: ["This command is permanently blocked and cannot be overridden."],
128
+ RISK: ["Consider running this command manually via a direct SSH session."],
129
+ };
130
+ const prefix = entry.id.split("-")[0];
131
+ return suggestions[prefix] || ["Consider a safer alternative or run manually."];
132
+ }
133
+ /**
134
+ * Format validation result for display.
135
+ */
136
+ export function formatValidationResult(result) {
137
+ if (result.safe) {
138
+ return "✅ Command is safe to execute.";
139
+ }
140
+ const lines = [result.reason || "Command validation failed."];
141
+ if (result.matched_rule) {
142
+ lines.push(`Rule: ${result.matched_rule}`);
143
+ }
144
+ if (result.suggestions && result.suggestions.length > 0) {
145
+ lines.push("\nSuggestions:");
146
+ for (const s of result.suggestions) {
147
+ lines.push(` • ${s}`);
148
+ }
149
+ }
150
+ return lines.join("\n");
151
+ }
152
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../src/security/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAA0C,MAAM,gBAAgB,CAAA;AACjH,OAAO,EAAE,kBAAkB,EAAuB,MAAM,gBAAgB,CAAA;AAgBxE;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,EACf,IAAkB,EAClB,iBAA2B,EAAE;IAE7B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;IACtC,CAAC;IAED,oDAAoD;IACpD,MAAM,mBAAmB,GAAG;QAC1B,GAAG,mBAAmB,EAAE;QACxB,GAAG,mBAAmB,CAAC,cAAc,CAAC;KACvC,CAAA;IAED,KAAK,MAAM,KAAK,IAAI,mBAAmB,EAAE,CAAC;QACxC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAChC,2CAA2C;YAC3C,IAAI,KAAK,CAAC,KAAK,KAAK,aAAa,EAAE,CAAC;gBAClC,OAAO;oBACL,IAAI,EAAE,KAAK;oBACX,KAAK,EAAE,aAAa;oBACpB,MAAM,EAAE,eAAe,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE;oBACnD,YAAY,EAAE,KAAK,CAAC,EAAE;oBACtB,WAAW,EAAE,cAAc,CAAC,KAAK,CAAC;iBACnC,CAAA;YACH,CAAC;YAED,oCAAoC;YACpC,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;gBAC5B,OAAO;oBACL,IAAI,EAAE,KAAK;oBACX,KAAK,EAAE,OAAO;oBACd,MAAM,EAAE,yBAAyB,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC,MAAM,EAAE;oBAC7D,YAAY,EAAE,KAAK,CAAC,EAAE;oBACtB,WAAW,EAAE,cAAc,CAAC,KAAK,CAAC;iBACnC,CAAA;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,8DAA8D;IAC9D,IAAI,IAAI,KAAK,YAAY,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QAClD,MAAM,eAAe,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAA;QAChD,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;QAE9E,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO;gBACL,IAAI,EAAE,KAAK;gBACX,KAAK,EAAE,kBAAkB;gBACzB,MAAM,EAAE,sBAAsB,IAAI,uCAAuC;gBACzE,WAAW,EAAE;oBACX,yDAAyD;oBACzD,mEAAmE;iBACpE;aACF,CAAA;QACH,CAAC;IACH,CAAC;IAED,wBAAwB;IACxB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;AACtC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,gBAAgB,GAAG;QACvB,OAAO;QACP,OAAO;QACP,OAAO;QACP,QAAQ;QACR,SAAS;QACT,SAAS;QACT,SAAS;QACT,SAAS;QACT,SAAS;QACT,OAAO;QACP,SAAS;QACT,UAAU;QACV,YAAY;QACZ,WAAW;QACX,OAAO;QACP,QAAQ;QACR,SAAS;QACT,SAAS;QACT,WAAW;QACX,UAAU;QACV,aAAa;QACb,OAAO;QACP,OAAO;QACP,SAAS;QACT,OAAO;QACP,OAAO;QACP,SAAS;QACT,SAAS;QACT,SAAS;QACT,SAAS;QACT,WAAW;QACX,cAAc;QACd,QAAQ;QACR,aAAa;QACb,QAAQ;QACR,SAAS;QACT,QAAQ;QACR,aAAa;QACb,gBAAgB;QAChB,oBAAoB;QACpB,kBAAkB;QAClB,qBAAqB;QACrB,kBAAkB;QAClB,uBAAuB;QACvB,mBAAmB;QACnB,kBAAkB;QAClB,uBAAuB;QACvB,qBAAqB;QACrB,eAAe;QACf,+CAA+C;KAChD,CAAA;IAED,OAAO,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;AAC7D,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,KAAqB;IAC3C,MAAM,WAAW,GAA6B;QAC5C,KAAK,EAAE,CAAC,+DAA+D,CAAC;QACxE,IAAI,EAAE,CAAC,kEAAkE,CAAC;KAC3E,CAAA;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IACrC,OAAO,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,+CAA+C,CAAC,CAAA;AACjF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAwB;IAC7D,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,OAAO,+BAA+B,CAAA;IACxC,CAAC;IAED,MAAM,KAAK,GAAa,CAAC,MAAM,CAAC,MAAM,IAAI,4BAA4B,CAAC,CAAA;IAEvE,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,YAAY,EAAE,CAAC,CAAA;IAC5C,CAAC;IAED,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxD,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;QAC5B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACxB,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
@@ -0,0 +1,47 @@
1
+ export interface AuditEntry {
2
+ /** ISO timestamp */
3
+ timestamp: string;
4
+ /** Session identifier (host@user) */
5
+ sessionId: string;
6
+ /** Command executed */
7
+ command: string;
8
+ /** Result of the command */
9
+ result: "success" | "blocked" | "approved" | "error";
10
+ /** Exit code */
11
+ exitCode: number;
12
+ /** Duration in milliseconds */
13
+ duration: number;
14
+ /** Reason for blocking/error */
15
+ reason?: string;
16
+ /** Matched blocklist rule ID */
17
+ matchedRule?: string;
18
+ }
19
+ /**
20
+ * Log a command execution to the audit trail.
21
+ * Uses JSONL format (append-only).
22
+ */
23
+ export declare function logCommand(projectDir: string, entry: Omit<AuditEntry, "timestamp">): void;
24
+ /**
25
+ * Read the audit log entries.
26
+ */
27
+ export declare function readAuditLog(projectDir: string, options?: {
28
+ limit?: number;
29
+ sessionId?: string;
30
+ commandFilter?: string;
31
+ }): AuditEntry[];
32
+ /**
33
+ * Format audit log entries for display.
34
+ */
35
+ export declare function formatAuditLog(entries: AuditEntry[]): string;
36
+ /**
37
+ * Get audit statistics.
38
+ */
39
+ export declare function getAuditStats(projectDir: string): {
40
+ total: number;
41
+ success: number;
42
+ blocked: number;
43
+ approved: number;
44
+ error: number;
45
+ blockedCommands: string[];
46
+ };
47
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/ssh/audit.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,oBAAoB;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,4BAA4B;IAC5B,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,CAAA;IACpD,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAmBD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,GAAG,IAAI,CAYzF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CAClB,GACL,UAAU,EAAE,CAmCd;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAuC5D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,eAAe,EAAE,MAAM,EAAE,CAAA;CAC1B,CAiBA"}
@@ -0,0 +1,126 @@
1
+ import { appendFileSync, readFileSync, existsSync, mkdirSync } from "fs";
2
+ import { join } from "path";
3
+ import { getSshDataDir } from "../config/defaults.js";
4
+ const AUDIT_FILE = "audit.jsonl";
5
+ /**
6
+ * Get the audit log file path.
7
+ */
8
+ function getAuditPath(projectDir) {
9
+ const dataDir = getSshDataDir(projectDir);
10
+ try {
11
+ if (!existsSync(dataDir)) {
12
+ mkdirSync(dataDir, { recursive: true });
13
+ }
14
+ }
15
+ catch {
16
+ // If we can't create the directory, use a fallback
17
+ }
18
+ return join(dataDir, AUDIT_FILE);
19
+ }
20
+ /**
21
+ * Log a command execution to the audit trail.
22
+ * Uses JSONL format (append-only).
23
+ */
24
+ export function logCommand(projectDir, entry) {
25
+ const auditEntry = {
26
+ timestamp: new Date().toISOString(),
27
+ ...entry,
28
+ };
29
+ try {
30
+ const auditPath = getAuditPath(projectDir);
31
+ appendFileSync(auditPath, JSON.stringify(auditEntry) + "\n", "utf-8");
32
+ }
33
+ catch {
34
+ // Audit logging should never crash the main flow
35
+ }
36
+ }
37
+ /**
38
+ * Read the audit log entries.
39
+ */
40
+ export function readAuditLog(projectDir, options = {}) {
41
+ const auditPath = getAuditPath(projectDir);
42
+ if (!existsSync(auditPath))
43
+ return [];
44
+ try {
45
+ const content = readFileSync(auditPath, "utf-8");
46
+ const lines = content.trim().split("\n").filter(Boolean);
47
+ let entries = lines.map((line) => {
48
+ try {
49
+ return JSON.parse(line);
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }).filter((e) => e !== null);
55
+ // Apply filters
56
+ if (options.sessionId) {
57
+ entries = entries.filter((e) => e.sessionId === options.sessionId);
58
+ }
59
+ if (options.commandFilter) {
60
+ const filter = options.commandFilter.toLowerCase();
61
+ entries = entries.filter((e) => e.command.toLowerCase().includes(filter));
62
+ }
63
+ // Apply limit (return most recent entries)
64
+ if (options.limit && options.limit > 0) {
65
+ entries = entries.slice(-options.limit);
66
+ }
67
+ return entries;
68
+ }
69
+ catch {
70
+ return [];
71
+ }
72
+ }
73
+ /**
74
+ * Format audit log entries for display.
75
+ */
76
+ export function formatAuditLog(entries) {
77
+ if (entries.length === 0) {
78
+ return "📋 No audit entries found.";
79
+ }
80
+ const lines = [`## SSH Audit Log (${entries.length} entries)\n`];
81
+ for (const entry of entries) {
82
+ const icon = entry.result === "success"
83
+ ? "✅"
84
+ : entry.result === "approved"
85
+ ? "⚠️"
86
+ : entry.result === "blocked"
87
+ ? "🚫"
88
+ : "❌";
89
+ const duration = entry.duration < 1000
90
+ ? `${entry.duration}ms`
91
+ : `${(entry.duration / 1000).toFixed(1)}s`;
92
+ lines.push(`${icon} **${entry.timestamp}** | ${entry.sessionId} | \`${entry.command}\``);
93
+ const details = [];
94
+ if (entry.exitCode !== 0)
95
+ details.push(`exit: ${entry.exitCode}`);
96
+ details.push(`duration: ${duration}`);
97
+ if (entry.matchedRule)
98
+ details.push(`rule: ${entry.matchedRule}`);
99
+ if (entry.reason)
100
+ details.push(`reason: ${entry.reason}`);
101
+ if (details.length > 0) {
102
+ lines.push(` ${details.join(" | ")}`);
103
+ }
104
+ lines.push("");
105
+ }
106
+ return lines.join("\n");
107
+ }
108
+ /**
109
+ * Get audit statistics.
110
+ */
111
+ export function getAuditStats(projectDir) {
112
+ const entries = readAuditLog(projectDir);
113
+ return {
114
+ total: entries.length,
115
+ success: entries.filter((e) => e.result === "success").length,
116
+ blocked: entries.filter((e) => e.result === "blocked").length,
117
+ approved: entries.filter((e) => e.result === "approved").length,
118
+ error: entries.filter((e) => e.result === "error").length,
119
+ blockedCommands: [
120
+ ...new Set(entries
121
+ .filter((e) => e.result === "blocked")
122
+ .map((e) => e.command)),
123
+ ],
124
+ };
125
+ }
126
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/ssh/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ export interface SshConnectionConfig {
2
+ host: string;
3
+ port: number;
4
+ username: string;
5
+ authMethod: "password" | "key";
6
+ password?: string;
7
+ keyPath?: string;
8
+ passphrase?: string;
9
+ /** Timeout in milliseconds */
10
+ timeout?: number;
11
+ /** Connection alias for display */
12
+ alias?: string;
13
+ }
14
+ export interface SshConnectionInfo {
15
+ id: string;
16
+ config: SshConnectionConfig;
17
+ connected: boolean;
18
+ connectedAt?: string;
19
+ lastActivity?: string;
20
+ }
21
+ export declare class SshConnection {
22
+ private client;
23
+ private _config;
24
+ private _id;
25
+ private _connected;
26
+ private _connectedAt?;
27
+ private _lastActivity?;
28
+ private _reconnectAttempts;
29
+ private _maxReconnectAttempts;
30
+ constructor(id: string, config: SshConnectionConfig);
31
+ get id(): string;
32
+ get config(): SshConnectionConfig;
33
+ get connected(): boolean;
34
+ get connectedAt(): Date | undefined;
35
+ get lastActivity(): Date | undefined;
36
+ /**
37
+ * Connect to the SSH server.
38
+ */
39
+ connect(): Promise<void>;
40
+ /**
41
+ * Execute a command on the remote server.
42
+ * Returns a stream for real-time output.
43
+ */
44
+ exec(command: string, options?: {
45
+ cwd?: string;
46
+ timeout?: number;
47
+ }): Promise<{
48
+ stdout: string;
49
+ stderr: string;
50
+ code: number | null;
51
+ signal: string | undefined;
52
+ }>;
53
+ /**
54
+ * Execute a command and return it as a readable stream (for real-time output).
55
+ */
56
+ execStream(command: string, options?: {
57
+ cwd?: string;
58
+ timeout?: number;
59
+ }): NodeJS.ReadableStream | null;
60
+ /**
61
+ * Get an SFTP session for file transfers.
62
+ */
63
+ sftp(): Promise<any>;
64
+ /**
65
+ * Disconnect from the SSH server.
66
+ */
67
+ disconnect(): void;
68
+ /**
69
+ * Get connection info for display.
70
+ */
71
+ getInfo(): SshConnectionInfo;
72
+ }
73
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/ssh/connection.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,UAAU,GAAG,KAAK,CAAA;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,mBAAmB,CAAA;IAC3B,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,GAAG,CAAQ;IACnB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,YAAY,CAAC,CAAM;IAC3B,OAAO,CAAC,aAAa,CAAC,CAAM;IAC5B,OAAO,CAAC,kBAAkB,CAAI;IAC9B,OAAO,CAAC,qBAAqB,CAAI;gBAErB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB;IAMnD,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,IAAI,MAAM,IAAI,mBAAmB,CAEhC;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,WAAW,IAAI,IAAI,GAAG,SAAS,CAElC;IAED,IAAI,YAAY,IAAI,IAAI,GAAG,SAAS,CAEnC;IAED;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA+D9B;;;OAGG;IACG,IAAI,CACR,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IA8D/F;;OAEG;IACH,UAAU,CACR,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3C,MAAM,CAAC,cAAc,GAAG,IAAI;IAgB/B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC;IAgB1B;;OAEG;IACH,UAAU,IAAI,IAAI;IAOlB;;OAEG;IACH,OAAO,IAAI,iBAAiB;CAgB7B"}
@@ -0,0 +1,210 @@
1
+ import { Client } from "ssh2";
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { resolve as pathResolve } from "path";
4
+ import { escapeShellArg } from "./sanitizer.js";
5
+ export class SshConnection {
6
+ client;
7
+ _config;
8
+ _id;
9
+ _connected = false;
10
+ _connectedAt;
11
+ _lastActivity;
12
+ _reconnectAttempts = 0;
13
+ _maxReconnectAttempts = 3;
14
+ constructor(id, config) {
15
+ this._id = id;
16
+ this._config = config;
17
+ this.client = new Client();
18
+ }
19
+ get id() {
20
+ return this._id;
21
+ }
22
+ get config() {
23
+ return this._config;
24
+ }
25
+ get connected() {
26
+ return this._connected;
27
+ }
28
+ get connectedAt() {
29
+ return this._connectedAt;
30
+ }
31
+ get lastActivity() {
32
+ return this._lastActivity;
33
+ }
34
+ /**
35
+ * Connect to the SSH server.
36
+ */
37
+ async connect() {
38
+ return new Promise((resolve, reject) => {
39
+ const connectConfig = {
40
+ host: this._config.host,
41
+ port: this._config.port,
42
+ username: this._config.username,
43
+ readyTimeout: this._config.timeout || 10000,
44
+ keepaliveInterval: 10000,
45
+ keepaliveCountMax: 3,
46
+ };
47
+ // Configure authentication
48
+ if (this._config.authMethod === "password") {
49
+ connectConfig.password = this._config.password;
50
+ }
51
+ else {
52
+ // Key-based authentication
53
+ const keyPath = this._config.keyPath
54
+ ? pathResolve(this._config.keyPath.replace("~", process.env.HOME || ""))
55
+ : pathResolve(process.env.HOME || "", ".ssh", "id_rsa");
56
+ if (!existsSync(keyPath)) {
57
+ reject(new Error(`SSH key not found: ${keyPath}`));
58
+ return;
59
+ }
60
+ connectConfig.privateKey = readFileSync(keyPath, "utf-8");
61
+ if (this._config.passphrase) {
62
+ connectConfig.passphrase = this._config.passphrase;
63
+ }
64
+ }
65
+ this.client.on("ready", () => {
66
+ this._connected = true;
67
+ this._connectedAt = new Date();
68
+ this._lastActivity = new Date();
69
+ this._reconnectAttempts = 0;
70
+ resolve();
71
+ });
72
+ this.client.on("error", (err) => {
73
+ this._connected = false;
74
+ if (this._reconnectAttempts < this._maxReconnectAttempts) {
75
+ this._reconnectAttempts++;
76
+ // Attempt reconnect after delay
77
+ setTimeout(() => {
78
+ this.connect().catch(() => { });
79
+ }, 1000 * this._reconnectAttempts);
80
+ }
81
+ reject(err);
82
+ });
83
+ this.client.on("close", () => {
84
+ this._connected = false;
85
+ });
86
+ this.client.on("end", () => {
87
+ this._connected = false;
88
+ });
89
+ this.client.connect(connectConfig);
90
+ });
91
+ }
92
+ /**
93
+ * Execute a command on the remote server.
94
+ * Returns a stream for real-time output.
95
+ */
96
+ async exec(command, options) {
97
+ if (!this._connected) {
98
+ throw new Error("SSH connection is not active");
99
+ }
100
+ this._lastActivity = new Date();
101
+ const wrappedCommand = options?.cwd
102
+ ? `cd ${escapeShellArg(options.cwd)} && ${command}`
103
+ : command;
104
+ return new Promise((resolve, reject) => {
105
+ const timeoutMs = (options?.timeout || 30) * 1000;
106
+ let timeoutId;
107
+ this.client.exec(wrappedCommand, (err, stream) => {
108
+ if (err) {
109
+ reject(err);
110
+ return;
111
+ }
112
+ let stdout = "";
113
+ let stderr = "";
114
+ let killed = false;
115
+ // Set timeout
116
+ if (timeoutMs > 0) {
117
+ timeoutId = setTimeout(() => {
118
+ killed = true;
119
+ stream.close();
120
+ reject(new Error(`Command timed out after ${timeoutMs / 1000}s`));
121
+ }, timeoutMs);
122
+ }
123
+ stream.on("data", (data) => {
124
+ stdout += data.toString("utf-8");
125
+ });
126
+ stream.stderr.on("data", (data) => {
127
+ stderr += data.toString("utf-8");
128
+ });
129
+ stream.on("close", (code, signal) => {
130
+ if (timeoutId)
131
+ clearTimeout(timeoutId);
132
+ if (killed)
133
+ return;
134
+ resolve({
135
+ stdout,
136
+ stderr,
137
+ code: code ?? 1,
138
+ signal: signal ?? undefined,
139
+ });
140
+ });
141
+ stream.on("error", (streamErr) => {
142
+ if (timeoutId)
143
+ clearTimeout(timeoutId);
144
+ reject(streamErr);
145
+ });
146
+ });
147
+ });
148
+ }
149
+ /**
150
+ * Execute a command and return it as a readable stream (for real-time output).
151
+ */
152
+ execStream(command, options) {
153
+ if (!this._connected) {
154
+ return null;
155
+ }
156
+ this._lastActivity = new Date();
157
+ const wrappedCommand = options?.cwd
158
+ ? `cd ${escapeShellArg(options.cwd)} && ${command}`
159
+ : command;
160
+ // This returns a stream that can be consumed externally
161
+ // The actual streaming is handled by the executor
162
+ return null; // Placeholder - actual streaming handled in executor.ts
163
+ }
164
+ /**
165
+ * Get an SFTP session for file transfers.
166
+ */
167
+ async sftp() {
168
+ if (!this._connected) {
169
+ throw new Error("SSH connection is not active");
170
+ }
171
+ return new Promise((resolve, reject) => {
172
+ this.client.sftp((err, sftp) => {
173
+ if (err) {
174
+ reject(err);
175
+ return;
176
+ }
177
+ resolve(sftp);
178
+ });
179
+ });
180
+ }
181
+ /**
182
+ * Disconnect from the SSH server.
183
+ */
184
+ disconnect() {
185
+ if (this._connected) {
186
+ this.client.end();
187
+ this._connected = false;
188
+ }
189
+ }
190
+ /**
191
+ * Get connection info for display.
192
+ */
193
+ getInfo() {
194
+ return {
195
+ id: this._id,
196
+ config: {
197
+ host: this._config.host,
198
+ port: this._config.port,
199
+ username: this._config.username,
200
+ authMethod: this._config.authMethod,
201
+ alias: this._config.alias,
202
+ // NEVER expose password or key in info
203
+ },
204
+ connected: this._connected,
205
+ connectedAt: this._connectedAt?.toISOString(),
206
+ lastActivity: this._lastActivity?.toISOString(),
207
+ };
208
+ }
209
+ }
210
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/ssh/connection.ts"],"names":[],"mappings":"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"}