myaidev-method 0.0.1

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.
@@ -0,0 +1,488 @@
1
+ import { Client } from 'ssh2';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ export class SSHWordPressManager {
6
+ constructor(config) {
7
+ this.host = config.host;
8
+ this.username = config.username;
9
+ this.privateKeyPath = config.privateKeyPath;
10
+ this.password = config.password; // Alternative to key auth
11
+ this.port = config.port || 22;
12
+ this.wordpressPath = config.wordpressPath || '/var/www/html';
13
+ this.connection = null;
14
+ }
15
+
16
+ async connect() {
17
+ return new Promise((resolve, reject) => {
18
+ this.connection = new Client();
19
+
20
+ const authConfig = {
21
+ host: this.host,
22
+ port: this.port,
23
+ username: this.username
24
+ };
25
+
26
+ // Use private key if available, otherwise password
27
+ if (this.privateKeyPath && fs.existsSync(this.privateKeyPath)) {
28
+ authConfig.privateKey = fs.readFileSync(this.privateKeyPath);
29
+ } else if (this.password) {
30
+ authConfig.password = this.password;
31
+ } else {
32
+ reject(new Error('No valid authentication method provided'));
33
+ return;
34
+ }
35
+
36
+ this.connection.on('ready', () => {
37
+ resolve(true);
38
+ });
39
+
40
+ this.connection.on('error', (err) => {
41
+ reject(err);
42
+ });
43
+
44
+ this.connection.connect(authConfig);
45
+ });
46
+ }
47
+
48
+ async executeCommand(command, options = {}) {
49
+ if (!this.connection) {
50
+ throw new Error('SSH connection not established');
51
+ }
52
+
53
+ return new Promise((resolve, reject) => {
54
+ this.connection.exec(command, (err, stream) => {
55
+ if (err) {
56
+ reject(err);
57
+ return;
58
+ }
59
+
60
+ let stdout = '';
61
+ let stderr = '';
62
+
63
+ stream.on('close', (code, signal) => {
64
+ resolve({
65
+ code,
66
+ signal,
67
+ stdout: stdout.trim(),
68
+ stderr: stderr.trim(),
69
+ success: code === 0
70
+ });
71
+ });
72
+
73
+ stream.on('data', (data) => {
74
+ stdout += data.toString();
75
+ });
76
+
77
+ stream.stderr.on('data', (data) => {
78
+ stderr += data.toString();
79
+ });
80
+
81
+ if (options.timeout) {
82
+ setTimeout(() => {
83
+ stream.close();
84
+ reject(new Error(`Command timeout after ${options.timeout}ms`));
85
+ }, options.timeout);
86
+ }
87
+ });
88
+ });
89
+ }
90
+
91
+ async disconnect() {
92
+ if (this.connection) {
93
+ this.connection.end();
94
+ this.connection = null;
95
+ }
96
+ }
97
+
98
+ // WordPress-specific server operations
99
+ async getServerInfo() {
100
+ try {
101
+ const commands = [
102
+ 'uname -a', // OS info
103
+ 'php -v', // PHP version
104
+ 'mysql --version', // MySQL version
105
+ 'df -h', // Disk usage
106
+ 'free -m', // Memory usage
107
+ 'uptime', // Server uptime
108
+ 'nginx -v 2>&1 || apache2 -v', // Web server version
109
+ ];
110
+
111
+ const results = {};
112
+ for (const cmd of commands) {
113
+ const result = await this.executeCommand(cmd);
114
+ results[cmd] = result;
115
+ }
116
+
117
+ return {
118
+ success: true,
119
+ server_info: this.parseServerInfo(results),
120
+ raw_results: results
121
+ };
122
+ } catch (error) {
123
+ return {
124
+ success: false,
125
+ error: error.message
126
+ };
127
+ }
128
+ }
129
+
130
+ async checkFilePermissions() {
131
+ try {
132
+ const commands = [
133
+ `find ${this.wordpressPath} -type f -perm 777`,
134
+ `find ${this.wordpressPath} -type d -perm 777`,
135
+ `ls -la ${this.wordpressPath}/wp-config.php`,
136
+ `ls -la ${this.wordpressPath}/wp-content/uploads/`,
137
+ `find ${this.wordpressPath}/wp-content/ -name "*.php" -perm +200`
138
+ ];
139
+
140
+ const results = {};
141
+ for (const cmd of commands) {
142
+ results[cmd] = await this.executeCommand(cmd);
143
+ }
144
+
145
+ return {
146
+ success: true,
147
+ permission_check: this.analyzePermissions(results),
148
+ recommendations: this.getPermissionRecommendations(results)
149
+ };
150
+ } catch (error) {
151
+ return {
152
+ success: false,
153
+ error: error.message
154
+ };
155
+ }
156
+ }
157
+
158
+ async scanForMalware() {
159
+ try {
160
+ const suspiciousPatterns = [
161
+ 'eval\\(',
162
+ 'base64_decode\\(',
163
+ 'shell_exec\\(',
164
+ 'system\\(',
165
+ 'exec\\(',
166
+ 'passthru\\(',
167
+ 'c99shell',
168
+ 'r57shell'
169
+ ];
170
+
171
+ const scanCommands = suspiciousPatterns.map(pattern =>
172
+ `grep -r "${pattern}" ${this.wordpressPath}/wp-content/ --include="*.php" || true`
173
+ );
174
+
175
+ const results = [];
176
+ for (const cmd of scanCommands) {
177
+ const result = await this.executeCommand(cmd);
178
+ if (result.stdout) {
179
+ results.push({
180
+ pattern: cmd.match(/"(.+?)"/)[1],
181
+ matches: result.stdout.split('\n').filter(line => line.trim())
182
+ });
183
+ }
184
+ }
185
+
186
+ return {
187
+ success: true,
188
+ malware_scan: {
189
+ timestamp: new Date().toISOString(),
190
+ suspicious_files: results,
191
+ total_matches: results.reduce((sum, r) => sum + r.matches.length, 0)
192
+ },
193
+ recommendations: this.getMalwareRecommendations(results)
194
+ };
195
+ } catch (error) {
196
+ return {
197
+ success: false,
198
+ error: error.message
199
+ };
200
+ }
201
+ }
202
+
203
+ async analyzeErrorLogs() {
204
+ try {
205
+ const logPaths = [
206
+ '/var/log/nginx/error.log',
207
+ '/var/log/apache2/error.log',
208
+ '/var/log/php_errors.log',
209
+ `${this.wordpressPath}/wp-content/debug.log`,
210
+ '/var/log/mysql/error.log'
211
+ ];
212
+
213
+ const results = {};
214
+ for (const logPath of logPaths) {
215
+ const cmd = `tail -n 100 ${logPath} 2>/dev/null || echo "Log file not found: ${logPath}"`;
216
+ results[logPath] = await this.executeCommand(cmd);
217
+ }
218
+
219
+ return {
220
+ success: true,
221
+ error_analysis: this.parseErrorLogs(results),
222
+ recommendations: this.getErrorLogRecommendations(results)
223
+ };
224
+ } catch (error) {
225
+ return {
226
+ success: false,
227
+ error: error.message
228
+ };
229
+ }
230
+ }
231
+
232
+ async optimizeDatabase() {
233
+ try {
234
+ // Get WordPress database credentials
235
+ const configCmd = `grep -E "DB_(NAME|USER|PASSWORD|HOST)" ${this.wordpressPath}/wp-config.php`;
236
+ const configResult = await this.executeCommand(configCmd);
237
+
238
+ if (!configResult.success) {
239
+ throw new Error('Could not read WordPress database configuration');
240
+ }
241
+
242
+ const dbConfig = this.parseDbConfig(configResult.stdout);
243
+
244
+ // Database optimization commands
245
+ const optimizationCommands = [
246
+ `mysql -h${dbConfig.host} -u${dbConfig.user} -p${dbConfig.password} ${dbConfig.name} -e "OPTIMIZE TABLE wp_posts, wp_postmeta, wp_options, wp_comments, wp_commentmeta;"`,
247
+ `mysql -h${dbConfig.host} -u${dbConfig.user} -p${dbConfig.password} ${dbConfig.name} -e "DELETE FROM wp_posts WHERE post_status = 'revision' AND post_date < DATE_SUB(NOW(), INTERVAL 30 DAY);"`,
248
+ `mysql -h${dbConfig.host} -u${dbConfig.user} -p${dbConfig.password} ${dbConfig.name} -e "DELETE FROM wp_comments WHERE comment_approved = 'spam' AND comment_date < DATE_SUB(NOW(), INTERVAL 30 DAY);"`
249
+ ];
250
+
251
+ const results = {};
252
+ for (const cmd of optimizationCommands) {
253
+ results[cmd] = await this.executeCommand(cmd);
254
+ }
255
+
256
+ return {
257
+ success: true,
258
+ database_optimization: results,
259
+ summary: this.summarizeDatabaseOptimization(results)
260
+ };
261
+ } catch (error) {
262
+ return {
263
+ success: false,
264
+ error: error.message
265
+ };
266
+ }
267
+ }
268
+
269
+ async createBackup() {
270
+ try {
271
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
272
+ const backupDir = `/tmp/wordpress-backup-${timestamp}`;
273
+
274
+ const commands = [
275
+ `mkdir -p ${backupDir}`,
276
+ `tar -czf ${backupDir}/wordpress-files.tar.gz -C ${path.dirname(this.wordpressPath)} ${path.basename(this.wordpressPath)}`,
277
+ `mysqldump --single-transaction --routines --triggers $(grep DB_NAME ${this.wordpressPath}/wp-config.php | cut -d "'" -f 4) > ${backupDir}/database.sql`
278
+ ];
279
+
280
+ const results = {};
281
+ for (const cmd of commands) {
282
+ results[cmd] = await this.executeCommand(cmd);
283
+ }
284
+
285
+ return {
286
+ success: true,
287
+ backup_location: backupDir,
288
+ backup_files: [`${backupDir}/wordpress-files.tar.gz`, `${backupDir}/database.sql`],
289
+ timestamp: timestamp,
290
+ commands_executed: results
291
+ };
292
+ } catch (error) {
293
+ return {
294
+ success: false,
295
+ error: error.message
296
+ };
297
+ }
298
+ }
299
+
300
+ async updateWordPressFiles() {
301
+ try {
302
+ const commands = [
303
+ `cd ${this.wordpressPath} && wp core update --allow-root`,
304
+ `cd ${this.wordpressPath} && wp plugin update --all --allow-root`,
305
+ `cd ${this.wordpressPath} && wp theme update --all --allow-root`
306
+ ];
307
+
308
+ const results = {};
309
+ for (const cmd of commands) {
310
+ results[cmd] = await this.executeCommand(cmd);
311
+ }
312
+
313
+ return {
314
+ success: true,
315
+ update_results: results,
316
+ summary: this.summarizeUpdates(results)
317
+ };
318
+ } catch (error) {
319
+ return {
320
+ success: false,
321
+ error: error.message
322
+ };
323
+ }
324
+ }
325
+
326
+ // Helper methods
327
+ parseServerInfo(results) {
328
+ return {
329
+ os: results['uname -a']?.stdout || 'Unknown',
330
+ php_version: this.extractPhpVersion(results['php -v']?.stdout),
331
+ mysql_version: this.extractMysqlVersion(results['mysql --version']?.stdout),
332
+ disk_usage: results['df -h']?.stdout,
333
+ memory_usage: results['free -m']?.stdout,
334
+ uptime: results['uptime']?.stdout,
335
+ web_server: results['nginx -v 2>&1 || apache2 -v']?.stdout
336
+ };
337
+ }
338
+
339
+ extractPhpVersion(phpOutput) {
340
+ if (!phpOutput) return 'Unknown';
341
+ const match = phpOutput.match(/PHP (\d+\.\d+\.\d+)/);
342
+ return match ? match[1] : 'Unknown';
343
+ }
344
+
345
+ extractMysqlVersion(mysqlOutput) {
346
+ if (!mysqlOutput) return 'Unknown';
347
+ const match = mysqlOutput.match(/(\d+\.\d+\.\d+)/);
348
+ return match ? match[1] : 'Unknown';
349
+ }
350
+
351
+ analyzePermissions(results) {
352
+ const issues = [];
353
+
354
+ Object.entries(results).forEach(([cmd, result]) => {
355
+ if (result.stdout && result.stdout.trim()) {
356
+ if (cmd.includes('perm 777')) {
357
+ issues.push({
358
+ type: 'overly_permissive',
359
+ files: result.stdout.split('\n'),
360
+ severity: 'high'
361
+ });
362
+ }
363
+ }
364
+ });
365
+
366
+ return {
367
+ total_issues: issues.length,
368
+ issues: issues,
369
+ status: issues.length === 0 ? 'good' : 'needs_attention'
370
+ };
371
+ }
372
+
373
+ getPermissionRecommendations(results) {
374
+ const recommendations = [];
375
+
376
+ // Add specific recommendations based on findings
377
+ recommendations.push({
378
+ action: 'Set proper file permissions',
379
+ command: `find ${this.wordpressPath} -type f -exec chmod 644 {} \\;`,
380
+ description: 'Set all files to 644 permissions'
381
+ });
382
+
383
+ recommendations.push({
384
+ action: 'Set proper directory permissions',
385
+ command: `find ${this.wordpressPath} -type d -exec chmod 755 {} \\;`,
386
+ description: 'Set all directories to 755 permissions'
387
+ });
388
+
389
+ return recommendations;
390
+ }
391
+
392
+ parseDbConfig(configOutput) {
393
+ const config = {};
394
+ const lines = configOutput.split('\n');
395
+
396
+ lines.forEach(line => {
397
+ if (line.includes('DB_NAME')) {
398
+ config.name = line.match(/'([^']+)'/)[1];
399
+ } else if (line.includes('DB_USER')) {
400
+ config.user = line.match(/'([^']+)'/)[1];
401
+ } else if (line.includes('DB_PASSWORD')) {
402
+ config.password = line.match(/'([^']+)'/)[1];
403
+ } else if (line.includes('DB_HOST')) {
404
+ config.host = line.match(/'([^']+)'/)[1];
405
+ }
406
+ });
407
+
408
+ return config;
409
+ }
410
+
411
+ parseErrorLogs(results) {
412
+ const analysis = {
413
+ total_errors: 0,
414
+ error_types: {},
415
+ recent_errors: [],
416
+ recommendations: []
417
+ };
418
+
419
+ Object.entries(results).forEach(([logPath, result]) => {
420
+ if (result.stdout && !result.stdout.includes('Log file not found')) {
421
+ const lines = result.stdout.split('\n');
422
+ analysis.total_errors += lines.length;
423
+
424
+ lines.forEach(line => {
425
+ if (line.trim()) {
426
+ analysis.recent_errors.push({
427
+ log: logPath,
428
+ message: line.trim()
429
+ });
430
+ }
431
+ });
432
+ }
433
+ });
434
+
435
+ return analysis;
436
+ }
437
+
438
+ getMalwareRecommendations(results) {
439
+ const recommendations = [];
440
+
441
+ if (results.length > 0) {
442
+ recommendations.push({
443
+ priority: 'critical',
444
+ action: 'Quarantine suspicious files',
445
+ description: 'Move suspicious files to quarantine for manual review'
446
+ });
447
+
448
+ recommendations.push({
449
+ priority: 'high',
450
+ action: 'Run full malware scan',
451
+ description: 'Use professional malware scanning tools'
452
+ });
453
+ }
454
+
455
+ return recommendations;
456
+ }
457
+
458
+ getErrorLogRecommendations(results) {
459
+ return [
460
+ {
461
+ action: 'Monitor error logs regularly',
462
+ description: 'Set up automated error log monitoring'
463
+ },
464
+ {
465
+ action: 'Configure log rotation',
466
+ description: 'Prevent log files from growing too large'
467
+ }
468
+ ];
469
+ }
470
+
471
+ summarizeDatabaseOptimization(results) {
472
+ return {
473
+ tables_optimized: 'Core WordPress tables',
474
+ revisions_cleaned: 'Posts older than 30 days',
475
+ spam_removed: 'Comments older than 30 days'
476
+ };
477
+ }
478
+
479
+ summarizeUpdates(results) {
480
+ return {
481
+ core_updated: results[Object.keys(results)[0]]?.success || false,
482
+ plugins_updated: results[Object.keys(results)[1]]?.success || false,
483
+ themes_updated: results[Object.keys(results)[2]]?.success || false
484
+ };
485
+ }
486
+ }
487
+
488
+ export default SSHWordPressManager;