mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-05-19 14:28:16 +02:00
Add files via upload
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
### What it does
|
||||
|
||||
- Configure **Host / Port / Password** and choose **Single-Agent** or **Multi-Agent**
|
||||
- Configure **Host / Port / HTTPS / Password** and choose an agent mode
|
||||
- Click **Validate** to login (`POST /api/auth/login`) and verify token (`GET /api/auth/validate`)
|
||||
- Right-click any HTTP message in Burp and send it to CyberStrikeAI for **streaming web pentest**
|
||||
- Keep a **test history sidebar** (searchable) so you can revisit previous runs
|
||||
@@ -63,6 +63,7 @@ If you already have Gradle available, you can still use `build.gradle` to build.
|
||||
|
||||
### Notes
|
||||
|
||||
- This extension connects to your CyberStrikeAI server (default is `http://127.0.0.1:8080`).
|
||||
- Default connection is `https://127.0.0.1:8080` (**HTTPS** checked). Self-signed / local certs are trusted automatically (no import).
|
||||
- Uncheck **HTTPS** only if your server runs plain HTTP.
|
||||
- It uses **Bearer Token** authentication obtained from the configured password.
|
||||
|
||||
|
||||
@@ -81,7 +81,8 @@ cd plugins/burp-suite/cyberstrikeai-burp-extension
|
||||
2) 填写:
|
||||
- **Host**:例如 `127.0.0.1`
|
||||
- **Port**:例如 `8080`
|
||||
- **Password**:你的 CyberStrikeAI 登录密码(对应服务端 `config.yaml` 的 `auth.password`)
|
||||
- **HTTPS**:默认勾选(对接 `config.yaml` 中 `tls_enabled` / 自签证书);插件会自动信任本地自签证书,无需导入
|
||||
- **Password**:你的 CyberStrikeAI 登录密码(对应服务端 `auth.password`)
|
||||
- **Agent mode**:选择 `Single Agent` 或 `Multi Agent`
|
||||
3) 点击 **Validate**
|
||||
- 成功:状态显示 `OK (token saved)`
|
||||
@@ -94,8 +95,9 @@ cd plugins/burp-suite/cyberstrikeai-burp-extension
|
||||
|
||||
- **Validate 失败 / 401**
|
||||
- 确认密码是否正确(服务端 `auth.password`)
|
||||
- 确认 IP/端口是否能访问(例如浏览器能打开 `http://IP:PORT/`)
|
||||
- 若服务器启用了反向代理/HTTPS,需要把插件里 baseUrl 改成对应协议与端口(当前插件默认使用 `http://`)
|
||||
- 确认 IP/端口是否能访问(例如浏览器能打开 `https://IP:PORT/`)
|
||||
- 服务端启用 TLS 时勾选 **HTTPS**(默认已勾选);自签证书无需手动导入
|
||||
- 若仍为纯 HTTP 部署,取消勾选 **HTTPS**
|
||||
|
||||
- **选择 Multi Agent 后提示“多代理未启用”**
|
||||
- 服务端需要开启:`config.yaml` 中 `multi_agent.enabled: true`
|
||||
|
||||
BIN
Binary file not shown.
+52
-11
@@ -73,15 +73,34 @@ public class BurpExtender implements IBurpExtender, IContextMenuFactory {
|
||||
public void onEvent(String type, String message, String rawJson) {
|
||||
if (type == null) type = "";
|
||||
switch (type) {
|
||||
case "response_start":
|
||||
tab.appendProgressToRun(runId, "\n\n[主回复]\n");
|
||||
break;
|
||||
case "response_delta":
|
||||
case "eino_agent_reply_stream_delta":
|
||||
tab.appendFinalToRun(runId, message);
|
||||
if (message != null && !message.isEmpty()) {
|
||||
tab.appendFinalToRun(runId, message);
|
||||
}
|
||||
break;
|
||||
case "response":
|
||||
tab.appendFinalToRun(runId, "\n\n--- Final Response ---\n");
|
||||
tab.appendFinalToRun(runId, message);
|
||||
tab.setFinalResponse(runId, message);
|
||||
break;
|
||||
case "eino_agent_reply_stream_start":
|
||||
tab.appendProgressToRun(runId, "\n\n[子代理回复]\n");
|
||||
break;
|
||||
case "eino_agent_reply_stream_delta":
|
||||
if (message != null && !message.isEmpty()) {
|
||||
tab.appendProgressToRun(runId, message);
|
||||
}
|
||||
break;
|
||||
case "eino_agent_reply_stream_end":
|
||||
tab.appendProgressToRun(runId, "\n");
|
||||
break;
|
||||
case "eino_agent_reply":
|
||||
if (message != null && !message.isEmpty()) {
|
||||
tab.appendProgressToRun(runId, "\n\n[子代理回复]\n" + message + "\n");
|
||||
}
|
||||
break;
|
||||
case "progress":
|
||||
tab.appendProgressToRun(runId, "\n[progress] " + message + "\n");
|
||||
tab.setRunStatus(runId, "running");
|
||||
@@ -94,21 +113,40 @@ public class BurpExtender implements IBurpExtender, IContextMenuFactory {
|
||||
tab.appendProgressToRun(runId, "\n[error] " + message + "\n");
|
||||
tab.setRunStatus(runId, "error");
|
||||
break;
|
||||
case "reasoning_chain_stream_start":
|
||||
tab.appendProgressToRun(runId, "\n\n[推理过程]\n");
|
||||
break;
|
||||
case "reasoning_chain_stream_delta":
|
||||
if (message != null && !message.isEmpty()) {
|
||||
tab.appendProgressToRun(runId, message);
|
||||
}
|
||||
break;
|
||||
case "reasoning_chain_stream_end":
|
||||
tab.appendProgressToRun(runId, "\n");
|
||||
break;
|
||||
case "reasoning_chain":
|
||||
if (message != null && !message.isEmpty()) {
|
||||
String streamId = rawJson != null ? SimpleJson.extractStringField(rawJson, "streamId") : "";
|
||||
if (streamId == null || streamId.isEmpty()) {
|
||||
tab.appendProgressToRun(runId, "\n\n[推理过程]\n" + message + "\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "thinking_stream_start":
|
||||
if (tab.isShowDebugEvents()) {
|
||||
tab.resetThinkingStream(runId);
|
||||
}
|
||||
break;
|
||||
case "thinking_stream_delta":
|
||||
if (tab.isShowDebugEvents() && message != null && !message.isEmpty()) {
|
||||
tab.appendProgressToRun(runId, message);
|
||||
}
|
||||
break;
|
||||
case "tool_call":
|
||||
case "tool_result":
|
||||
case "tool_result_delta":
|
||||
if (tab.isShowDebugEvents() && message != null && !message.isEmpty()) {
|
||||
if ("thinking_stream_delta".equals(type)) {
|
||||
tab.appendThinkingDelta(runId, message);
|
||||
} else {
|
||||
tab.appendProgressToRun(runId, "\n[" + type + "] " + message + "\n");
|
||||
}
|
||||
tab.appendProgressToRun(runId, "\n[" + type + "] " + message + "\n");
|
||||
}
|
||||
break;
|
||||
case "conversation":
|
||||
@@ -125,7 +163,9 @@ public class BurpExtender implements IBurpExtender, IContextMenuFactory {
|
||||
case "done":
|
||||
break;
|
||||
default:
|
||||
if (tab.isShowDebugEvents() && message != null && !message.isEmpty()) {
|
||||
if (tab.isShowDebugEvents() && message != null && !message.isEmpty()
|
||||
&& !type.endsWith("_stream_delta") && !type.endsWith("_stream_start")
|
||||
&& !type.endsWith("_stream_end")) {
|
||||
tab.appendProgressToRun(runId, "\n[" + type + "] " + message + "\n");
|
||||
}
|
||||
break;
|
||||
@@ -134,8 +174,9 @@ public class BurpExtender implements IBurpExtender, IContextMenuFactory {
|
||||
|
||||
@Override
|
||||
public void onError(String message, Exception e) {
|
||||
tab.appendProgressToRun(runId, "\n[error] " + message + "\n");
|
||||
tab.setRunStatus(runId, "error");
|
||||
boolean cancelled = message != null && message.toLowerCase().contains("cancel");
|
||||
tab.appendProgressToRun(runId, cancelled ? "\n[info] " + message + "\n" : "\n[error] " + message + "\n");
|
||||
tab.setRunStatus(runId, cancelled ? "cancelled" : "error");
|
||||
callbacks.printError("CyberStrikeAI stream error: " + message);
|
||||
if (e != null) {
|
||||
callbacks.printError(e.toString());
|
||||
|
||||
+127
-11
@@ -2,17 +2,29 @@ package burp;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
final class CyberStrikeAIClient {
|
||||
|
||||
private static final int AUTH_CONNECT_TIMEOUT_MS = 4_000;
|
||||
private static final int AUTH_READ_TIMEOUT_MS = 5_000;
|
||||
/** login + validate 整段上限,避免两次读超时叠加拖到半分钟 */
|
||||
private static final int AUTH_OVERALL_TIMEOUT_MS = 10_000;
|
||||
private static final int DEFAULT_READ_TIMEOUT_MS = 15_000;
|
||||
|
||||
private final AtomicReference<HttpURLConnection> activeConnection = new AtomicReference<>();
|
||||
private final AtomicReference<Thread> activeThread = new AtomicReference<>();
|
||||
|
||||
static final class Config {
|
||||
final String baseUrl; // e.g. http://127.0.0.1:8080
|
||||
final String password;
|
||||
@@ -49,15 +61,97 @@ final class CyberStrikeAIClient {
|
||||
void onDone();
|
||||
}
|
||||
|
||||
boolean hasActiveRequest() {
|
||||
return activeConnection.get() != null;
|
||||
}
|
||||
|
||||
void cancelActiveRequest() {
|
||||
HttpURLConnection conn = activeConnection.getAndSet(null);
|
||||
if (conn != null) {
|
||||
try {
|
||||
conn.disconnect();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
Thread t = activeThread.getAndSet(null);
|
||||
if (t != null) {
|
||||
t.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
String loginAndValidate(Config cfg) throws IOException {
|
||||
String token = login(cfg.baseUrl, cfg.password);
|
||||
validate(cfg.baseUrl, token);
|
||||
return token;
|
||||
Thread worker = Thread.currentThread();
|
||||
java.util.Timer deadline = new java.util.Timer("CyberStrikeAI-AuthDeadline", true);
|
||||
deadline.schedule(new java.util.TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
worker.interrupt();
|
||||
cancelActiveRequest();
|
||||
}
|
||||
}, AUTH_OVERALL_TIMEOUT_MS);
|
||||
try {
|
||||
String token = login(cfg.baseUrl, cfg.password);
|
||||
if (Thread.interrupted()) {
|
||||
throw timeoutIOException();
|
||||
}
|
||||
validate(cfg.baseUrl, token);
|
||||
if (Thread.interrupted()) {
|
||||
throw timeoutIOException();
|
||||
}
|
||||
return token;
|
||||
} catch (SocketTimeoutException e) {
|
||||
throw timeoutIOException();
|
||||
} finally {
|
||||
deadline.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private static IOException timeoutIOException() {
|
||||
return new IOException("Connection timed out (~" + (AUTH_OVERALL_TIMEOUT_MS / 1000)
|
||||
+ "s). Check host/port and HTTPS checkbox.");
|
||||
}
|
||||
|
||||
private void trackConnection(HttpURLConnection conn) {
|
||||
activeThread.set(Thread.currentThread());
|
||||
activeConnection.set(conn);
|
||||
}
|
||||
|
||||
private void releaseConnection(HttpURLConnection conn) {
|
||||
if (activeConnection.compareAndSet(conn, null)) {
|
||||
activeThread.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isCancelled(Throwable e) {
|
||||
if (e == null) {
|
||||
return Thread.currentThread().isInterrupted();
|
||||
}
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
return true;
|
||||
}
|
||||
if (e instanceof InterruptedIOException) {
|
||||
return true;
|
||||
}
|
||||
if (e instanceof SocketTimeoutException) {
|
||||
return false;
|
||||
}
|
||||
Throwable cause = e.getCause();
|
||||
if (cause != null && cause != e) {
|
||||
return isCancelled(cause);
|
||||
}
|
||||
String msg = e.getMessage();
|
||||
return msg != null && (
|
||||
msg.toLowerCase().contains("cancel")
|
||||
|| msg.toLowerCase().contains("abort")
|
||||
|| msg.toLowerCase().contains("closed")
|
||||
);
|
||||
}
|
||||
|
||||
private String login(String baseUrl, String password) throws IOException {
|
||||
URL url = new URL(baseUrl + "/api/auth/login");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
HttpURLConnection conn = SslTrustAll.open(url, AUTH_CONNECT_TIMEOUT_MS, AUTH_READ_TIMEOUT_MS);
|
||||
trackConnection(conn);
|
||||
try {
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
@@ -92,11 +186,16 @@ final class CyberStrikeAIClient {
|
||||
throw new IOException("Login response missing token. Check backend address and credentials.");
|
||||
}
|
||||
return token;
|
||||
} finally {
|
||||
releaseConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(String baseUrl, String token) throws IOException {
|
||||
URL url = new URL(baseUrl + "/api/auth/validate");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
HttpURLConnection conn = SslTrustAll.open(url, AUTH_CONNECT_TIMEOUT_MS, AUTH_READ_TIMEOUT_MS);
|
||||
trackConnection(conn);
|
||||
try {
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setRequestProperty("Authorization", "Bearer " + token);
|
||||
int code = conn.getResponseCode();
|
||||
@@ -104,6 +203,9 @@ final class CyberStrikeAIClient {
|
||||
if (code < 200 || code >= 300) {
|
||||
throw new IOException("Validate failed (" + code + "): " + resp);
|
||||
}
|
||||
} finally {
|
||||
releaseConnection(conn);
|
||||
}
|
||||
}
|
||||
|
||||
void streamTest(Config cfg, String token, String message, StreamListener listener) {
|
||||
@@ -117,11 +219,12 @@ final class CyberStrikeAIClient {
|
||||
payload.put("orchestration", cfg.agentMode.orchestration);
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
Thread worker = new Thread(() -> {
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
URL url = new URL(urlStr);
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
conn = SslTrustAll.open(url, AUTH_CONNECT_TIMEOUT_MS, 0);
|
||||
trackConnection(conn);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
@@ -142,6 +245,9 @@ final class CyberStrikeAIClient {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
break;
|
||||
}
|
||||
// SSE format: "data: {json}"
|
||||
if (line.startsWith("data:")) {
|
||||
String json = line.substring("data:".length()).trim();
|
||||
@@ -156,15 +262,25 @@ final class CyberStrikeAIClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
listener.onDone();
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
listener.onError("Cancelled.", null);
|
||||
} else {
|
||||
listener.onDone();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
listener.onError(e.getMessage(), e);
|
||||
if (isCancelled(e)) {
|
||||
listener.onError("Cancelled.", e);
|
||||
} else {
|
||||
listener.onError(e.getMessage(), e);
|
||||
}
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
releaseConnection(conn);
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
}, "CyberStrikeAI-Stream").start();
|
||||
}, "CyberStrikeAI-Stream");
|
||||
worker.start();
|
||||
}
|
||||
|
||||
void cancelByConversationId(String baseUrl, String token, String conversationId) throws IOException {
|
||||
@@ -172,7 +288,7 @@ final class CyberStrikeAIClient {
|
||||
throw new IOException("Missing conversationId.");
|
||||
}
|
||||
URL url = new URL(baseUrl + "/api/agent-loop/cancel");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
HttpURLConnection conn = SslTrustAll.open(url, AUTH_CONNECT_TIMEOUT_MS, AUTH_READ_TIMEOUT_MS);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
|
||||
+130
-34
@@ -14,6 +14,7 @@ final class CyberStrikeAITab implements ITab {
|
||||
|
||||
private final JTextField hostField = new JTextField("127.0.0.1");
|
||||
private final JTextField portField = new JTextField("8080");
|
||||
private final JCheckBox useHttpsBox = new JCheckBox("HTTPS", true);
|
||||
private final JPasswordField passwordField = new JPasswordField();
|
||||
private final JComboBox<String> agentModeBox = new JComboBox<>(new String[]{
|
||||
"Native ReAct", "Eino Single (ADK)", "Deep (DeepAgent)", "Plan-Execute", "Supervisor"
|
||||
@@ -29,6 +30,10 @@ final class CyberStrikeAITab implements ITab {
|
||||
|
||||
private final JTextArea progressArea = new JTextArea();
|
||||
private final JTextArea finalRawArea = new JTextArea(); // raw final stream / final response
|
||||
private JScrollPane progressScrollPane;
|
||||
private JScrollPane finalRawScrollPane;
|
||||
/** 距底部在此像素内视为「跟随滚动」,否则用户上拉阅读时不抢滚动条 */
|
||||
private static final int SCROLL_FOLLOW_THRESHOLD_PX = 48;
|
||||
private final JEditorPane markdownPane = new JEditorPane("text/html", "");
|
||||
private final CardLayout outputCardsLayout = new CardLayout();
|
||||
private final JPanel outputCards = new JPanel(outputCardsLayout);
|
||||
@@ -41,6 +46,7 @@ final class CyberStrikeAITab implements ITab {
|
||||
|
||||
private final CyberStrikeAIClient client = new CyberStrikeAIClient();
|
||||
private final AtomicReference<String> tokenRef = new AtomicReference<>("");
|
||||
private final AtomicReference<Thread> validateThreadRef = new AtomicReference<>();
|
||||
|
||||
private final DefaultListModel<TestRun> testListModel = new DefaultListModel<>();
|
||||
private final JList<TestRun> testList = new JList<>(testListModel);
|
||||
@@ -107,6 +113,8 @@ final class CyberStrikeAITab implements ITab {
|
||||
row1.add(hostField);
|
||||
row1.add(new JLabel("Port"));
|
||||
row1.add(portField);
|
||||
useHttpsBox.setToolTipText("Use https:// for CyberStrikeAI (self-signed certs are trusted automatically)");
|
||||
row1.add(useHttpsBox);
|
||||
row1.add(new JLabel("Password"));
|
||||
row1.add(passwordField);
|
||||
row1.add(validateButton);
|
||||
@@ -186,15 +194,22 @@ final class CyberStrikeAITab implements ITab {
|
||||
configureTextArea(requestArea, false);
|
||||
configureTextArea(responseArea, false);
|
||||
|
||||
outputCards.add(new JScrollPane(finalRawArea), "raw");
|
||||
finalRawScrollPane = new JScrollPane(finalRawArea);
|
||||
finalRawScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
finalRawScrollPane.getVerticalScrollBar().setUnitIncrement(16);
|
||||
outputCards.add(finalRawScrollPane, "raw");
|
||||
outputCards.add(new JScrollPane(markdownPane), "md");
|
||||
|
||||
outputRoot.add(buildOutputHeader(), BorderLayout.NORTH);
|
||||
outputRoot.add(buildOutputBody(), BorderLayout.CENTER);
|
||||
|
||||
rightTabs.addTab("Output", outputRoot);
|
||||
rightTabs.addTab("Request", new JScrollPane(requestArea));
|
||||
rightTabs.addTab("Response", new JScrollPane(responseArea));
|
||||
JScrollPane requestScroll = new JScrollPane(requestArea);
|
||||
requestScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
rightTabs.addTab("Request", requestScroll);
|
||||
JScrollPane responseScroll = new JScrollPane(responseArea);
|
||||
responseScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
rightTabs.addTab("Response", responseScroll);
|
||||
return rightTabs;
|
||||
}
|
||||
|
||||
@@ -210,12 +225,13 @@ final class CyberStrikeAITab implements ITab {
|
||||
}
|
||||
|
||||
private JComponent buildOutputBody() {
|
||||
JScrollPane progressScroll = new JScrollPane(progressArea);
|
||||
progressScroll.setBorder(BorderFactory.createTitledBorder("Progress"));
|
||||
progressScroll.getVerticalScrollBar().setUnitIncrement(16);
|
||||
progressScrollPane = new JScrollPane(progressArea);
|
||||
progressScrollPane.setBorder(BorderFactory.createTitledBorder("Progress"));
|
||||
progressScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
progressScrollPane.getVerticalScrollBar().setUnitIncrement(16);
|
||||
|
||||
JPanel empty = new JPanel();
|
||||
progressContainer.add(progressScroll, "show");
|
||||
progressContainer.add(progressScrollPane, "show");
|
||||
progressContainer.add(empty, "hide");
|
||||
((CardLayout) progressContainer.getLayout()).show(progressContainer, "show");
|
||||
|
||||
@@ -259,10 +275,27 @@ final class CyberStrikeAITab implements ITab {
|
||||
return split;
|
||||
}
|
||||
|
||||
private static boolean isScrollNearBottom(JScrollPane scrollPane) {
|
||||
if (scrollPane == null) {
|
||||
return true;
|
||||
}
|
||||
JScrollBar bar = scrollPane.getVerticalScrollBar();
|
||||
int max = Math.max(0, bar.getMaximum() - bar.getVisibleAmount());
|
||||
return bar.getValue() >= max - SCROLL_FOLLOW_THRESHOLD_PX;
|
||||
}
|
||||
|
||||
private static void scrollPaneToBottom(JScrollPane scrollPane) {
|
||||
if (scrollPane == null) {
|
||||
return;
|
||||
}
|
||||
JScrollBar bar = scrollPane.getVerticalScrollBar();
|
||||
bar.setValue(bar.getMaximum());
|
||||
}
|
||||
|
||||
private static void configureTextArea(JTextArea area, boolean monospaced) {
|
||||
area.setEditable(false);
|
||||
area.setLineWrap(false);
|
||||
area.setWrapStyleWord(false);
|
||||
area.setLineWrap(true);
|
||||
area.setWrapStyleWord(true);
|
||||
if (monospaced) {
|
||||
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
||||
} else {
|
||||
@@ -381,24 +414,44 @@ final class CyberStrikeAITab implements ITab {
|
||||
|
||||
private void wireActions() {
|
||||
validateButton.addActionListener(e -> {
|
||||
validateButton.setEnabled(false);
|
||||
if ("Cancel".equals(validateButton.getText())) {
|
||||
cancelValidateInProgress();
|
||||
return;
|
||||
}
|
||||
validateButton.setText("Cancel");
|
||||
validateButton.setEnabled(true);
|
||||
stopButton.setEnabled(true);
|
||||
statusLabel.setText("Validating...");
|
||||
log("Validating connection...");
|
||||
new Thread(() -> {
|
||||
log("Validating connection... (max ~10s; click Cancel or Stop to abort)");
|
||||
Thread worker = new Thread(() -> {
|
||||
try {
|
||||
CyberStrikeAIClient.Config cfg = currentConfig();
|
||||
String token = client.loginAndValidate(cfg);
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
return;
|
||||
}
|
||||
tokenRef.set(token);
|
||||
SwingUtilities.invokeLater(() -> statusLabel.setText("OK (token saved)"));
|
||||
log("Validation OK.");
|
||||
} catch (Exception ex) {
|
||||
tokenRef.set("");
|
||||
SwingUtilities.invokeLater(() -> statusLabel.setText("Failed: " + ex.getMessage()));
|
||||
log("Validation failed: " + ex.getMessage());
|
||||
if (Thread.currentThread().isInterrupted()) {
|
||||
SwingUtilities.invokeLater(() -> statusLabel.setText("Cancelled"));
|
||||
log("Validation cancelled.");
|
||||
} else {
|
||||
SwingUtilities.invokeLater(() -> statusLabel.setText("Failed: " + ex.getMessage()));
|
||||
log("Validation failed: " + ex.getMessage());
|
||||
}
|
||||
} finally {
|
||||
SwingUtilities.invokeLater(() -> validateButton.setEnabled(true));
|
||||
validateThreadRef.set(null);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
validateButton.setText("Validate");
|
||||
validateButton.setEnabled(true);
|
||||
});
|
||||
}
|
||||
}, "CyberStrikeAI-Validate").start();
|
||||
}, "CyberStrikeAI-Validate");
|
||||
validateThreadRef.set(worker);
|
||||
worker.start();
|
||||
});
|
||||
|
||||
clearButton.addActionListener(e -> {
|
||||
@@ -435,10 +488,23 @@ final class CyberStrikeAITab implements ITab {
|
||||
});
|
||||
|
||||
stopButton.addActionListener(e -> {
|
||||
if ("Cancel".equals(validateButton.getText())) {
|
||||
cancelValidateInProgress();
|
||||
return;
|
||||
}
|
||||
|
||||
String runId = selectedRunId;
|
||||
if (runId != null && client.hasActiveRequest()) {
|
||||
client.cancelActiveRequest();
|
||||
appendProgressToRun(runId, "\n[info] Stream stopped.\n");
|
||||
setRunStatus(runId, "cancelled");
|
||||
return;
|
||||
}
|
||||
|
||||
if (runId == null) return;
|
||||
TestRun run = runs.get(runId);
|
||||
if (run == null) return;
|
||||
|
||||
String token = getToken();
|
||||
if (token == null || token.trim().isEmpty()) {
|
||||
appendProgressToRun(runId, "\n[error] Not validated.\n");
|
||||
@@ -483,7 +549,8 @@ final class CyberStrikeAITab implements ITab {
|
||||
String host = hostField.getText().trim();
|
||||
String port = portField.getText().trim();
|
||||
String password = new String(passwordField.getPassword());
|
||||
String baseUrl = "http://" + host + ":" + port;
|
||||
String scheme = useHttpsBox.isSelected() ? "https" : "http";
|
||||
String baseUrl = scheme + "://" + host + ":" + port;
|
||||
int idx = agentModeBox.getSelectedIndex();
|
||||
CyberStrikeAIClient.AgentMode mode = (idx >= 0 && idx < AGENT_MODES.length)
|
||||
? AGENT_MODES[idx]
|
||||
@@ -567,10 +634,31 @@ final class CyberStrikeAITab implements ITab {
|
||||
run.progressBuffer.append(s);
|
||||
}
|
||||
if (runId.equals(selectedRunId)) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
progressArea.append(s);
|
||||
progressArea.setCaretPosition(progressArea.getDocument().getLength());
|
||||
});
|
||||
SwingUtilities.invokeLater(() -> appendProgressUi(s, false));
|
||||
}
|
||||
}
|
||||
|
||||
private void appendProgressUi(String s, boolean forceFollow) {
|
||||
JScrollBar bar = progressScrollPane != null ? progressScrollPane.getVerticalScrollBar() : null;
|
||||
int scrollBefore = bar != null ? bar.getValue() : 0;
|
||||
boolean follow = forceFollow || isScrollNearBottom(progressScrollPane);
|
||||
progressArea.append(s);
|
||||
if (follow) {
|
||||
scrollPaneToBottom(progressScrollPane);
|
||||
} else if (bar != null) {
|
||||
bar.setValue(scrollBefore);
|
||||
}
|
||||
}
|
||||
|
||||
private void appendFinalUi(String s, boolean forceFollow) {
|
||||
JScrollBar bar = finalRawScrollPane != null ? finalRawScrollPane.getVerticalScrollBar() : null;
|
||||
int scrollBefore = bar != null ? bar.getValue() : 0;
|
||||
boolean follow = forceFollow || isScrollNearBottom(finalRawScrollPane);
|
||||
finalRawArea.append(s);
|
||||
if (follow) {
|
||||
scrollPaneToBottom(finalRawScrollPane);
|
||||
} else if (bar != null) {
|
||||
bar.setValue(scrollBefore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,10 +708,7 @@ final class CyberStrikeAITab implements ITab {
|
||||
run.finalBuffer.append(s);
|
||||
}
|
||||
if (runId.equals(selectedRunId)) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
finalRawArea.append(s);
|
||||
finalRawArea.setCaretPosition(finalRawArea.getDocument().getLength());
|
||||
});
|
||||
SwingUtilities.invokeLater(() -> appendFinalUi(s, false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,9 +741,9 @@ final class CyberStrikeAITab implements ITab {
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
progressArea.setText(progress);
|
||||
progressArea.setCaretPosition(progressArea.getDocument().getLength());
|
||||
scrollPaneToBottom(progressScrollPane);
|
||||
finalRawArea.setText(fin);
|
||||
finalRawArea.setCaretPosition(finalRawArea.getDocument().getLength());
|
||||
scrollPaneToBottom(finalRawScrollPane);
|
||||
requestArea.setText(run.requestRaw == null ? "" : run.requestRaw);
|
||||
responseArea.setText(run.responseRaw == null ? "" : run.responseRaw);
|
||||
refreshOutputView();
|
||||
@@ -682,25 +767,36 @@ final class CyberStrikeAITab implements ITab {
|
||||
|
||||
void clearAndShowStreamHeader(String title) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
progressArea.setText("");
|
||||
finalRawArea.setText(title + "\n\n");
|
||||
progressArea.setText("[*] " + title + "\n\n");
|
||||
finalRawArea.setText("");
|
||||
markdownPane.setText("");
|
||||
});
|
||||
}
|
||||
|
||||
// Legacy helpers kept for Validate logging
|
||||
void appendStreamLine(String s) {
|
||||
if (s == null) return;
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
progressArea.append(s);
|
||||
progressArea.append("\n");
|
||||
progressArea.setCaretPosition(progressArea.getDocument().getLength());
|
||||
});
|
||||
SwingUtilities.invokeLater(() -> appendProgressUi(s + "\n", false));
|
||||
}
|
||||
|
||||
private void log(String s) {
|
||||
appendStreamLine("[*] " + s);
|
||||
}
|
||||
|
||||
private void cancelValidateInProgress() {
|
||||
client.cancelActiveRequest();
|
||||
Thread t = validateThreadRef.getAndSet(null);
|
||||
if (t != null) {
|
||||
t.interrupt();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusLabel.setText("Cancelled");
|
||||
validateButton.setText("Validate");
|
||||
validateButton.setEnabled(true);
|
||||
});
|
||||
log("Validation cancelled.");
|
||||
}
|
||||
|
||||
private void applyFilter() {
|
||||
String q = searchField.getText();
|
||||
if (q == null) q = "";
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
package burp;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* Opens HTTPS connections without validating server certificates (self-signed / local dev).
|
||||
* Applied per-connection only; does not change JVM-wide defaults for other Burp components.
|
||||
*/
|
||||
final class SslTrustAll {
|
||||
|
||||
private static volatile SSLSocketFactory socketFactory;
|
||||
private static final HostnameVerifier TRUST_ALL_HOSTS = (hostname, session) -> true;
|
||||
|
||||
private SslTrustAll() {
|
||||
}
|
||||
|
||||
static HttpURLConnection open(URL url) throws IOException {
|
||||
return open(url, 5_000, 30_000);
|
||||
}
|
||||
|
||||
static HttpURLConnection open(URL url, int connectTimeoutMs, int readTimeoutMs) throws IOException {
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setConnectTimeout(connectTimeoutMs);
|
||||
conn.setReadTimeout(readTimeoutMs);
|
||||
if (conn instanceof HttpsURLConnection) {
|
||||
HttpsURLConnection https = (HttpsURLConnection) conn;
|
||||
https.setSSLSocketFactory(new TimeoutSslSocketFactory(socketFactory(), connectTimeoutMs, readTimeoutMs));
|
||||
https.setHostnameVerifier(TRUST_ALL_HOSTS);
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
private static SSLSocketFactory socketFactory() {
|
||||
SSLSocketFactory sf = socketFactory;
|
||||
if (sf != null) {
|
||||
return sf;
|
||||
}
|
||||
synchronized (SslTrustAll.class) {
|
||||
sf = socketFactory;
|
||||
if (sf != null) {
|
||||
return sf;
|
||||
}
|
||||
try {
|
||||
TrustManager[] trustAll = new TrustManager[]{
|
||||
new X509TrustManager() {
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) {
|
||||
}
|
||||
}
|
||||
};
|
||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||
ctx.init(null, trustAll, new java.security.SecureRandom());
|
||||
sf = ctx.getSocketFactory();
|
||||
socketFactory = sf;
|
||||
return sf;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to initialize trust-all TLS", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensures TCP connect + socket read respect timeouts (plain HttpURLConnection SSL can hang longer). */
|
||||
private static final class TimeoutSslSocketFactory extends SSLSocketFactory {
|
||||
private final SSLSocketFactory delegate;
|
||||
private final int connectTimeoutMs;
|
||||
private final int readTimeoutMs;
|
||||
|
||||
TimeoutSslSocketFactory(SSLSocketFactory delegate, int connectTimeoutMs, int readTimeoutMs) {
|
||||
this.delegate = delegate;
|
||||
this.connectTimeoutMs = connectTimeoutMs;
|
||||
this.readTimeoutMs = readTimeoutMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDefaultCipherSuites() {
|
||||
return delegate.getDefaultCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSupportedCipherSuites() {
|
||||
return delegate.getSupportedCipherSuites();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() throws IOException {
|
||||
return tune(delegate.createSocket());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
|
||||
return tune(delegate.createSocket(s, host, port, autoClose));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
Socket plain = new Socket();
|
||||
plain.connect(new InetSocketAddress(host, port), connectTimeoutMs);
|
||||
return tune(delegate.createSocket(plain, host, port, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, java.net.InetAddress localHost, int localPort) throws IOException {
|
||||
Socket plain = new Socket();
|
||||
plain.bind(new InetSocketAddress(localHost, localPort));
|
||||
plain.connect(new InetSocketAddress(host, port), connectTimeoutMs);
|
||||
return tune(delegate.createSocket(plain, host, port, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(java.net.InetAddress host, int port) throws IOException {
|
||||
Socket plain = new Socket();
|
||||
plain.connect(new InetSocketAddress(host, port), connectTimeoutMs);
|
||||
return tune(delegate.createSocket(plain, host.getHostName(), port, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(java.net.InetAddress address, int port, java.net.InetAddress localAddress, int localPort) throws IOException {
|
||||
Socket plain = new Socket();
|
||||
plain.bind(new InetSocketAddress(localAddress, localPort));
|
||||
plain.connect(new InetSocketAddress(address, port), connectTimeoutMs);
|
||||
return tune(delegate.createSocket(plain, address.getHostName(), port, true));
|
||||
}
|
||||
|
||||
private Socket tune(Socket socket) throws IOException {
|
||||
socket.setSoTimeout(readTimeoutMs);
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
+4
@@ -1,12 +1,16 @@
|
||||
burp/SslTrustAll.class
|
||||
burp/SslTrustAll$TimeoutSslSocketFactory.class
|
||||
burp/CyberStrikeAIClient$StreamListener.class
|
||||
burp/CyberStrikeAIClient$Config.class
|
||||
burp/CyberStrikeAIClient$AgentMode.class
|
||||
burp/MarkdownRenderer.class
|
||||
burp/SimpleJson.class
|
||||
burp/CyberStrikeAIClient.class
|
||||
burp/CyberStrikeAIClient$1.class
|
||||
burp/CyberStrikeAITab$DotIcon.class
|
||||
burp/CyberStrikeAITab.class
|
||||
burp/CyberStrikeAITab$1.class
|
||||
burp/SslTrustAll$1.class
|
||||
burp/BurpExtender$1.class
|
||||
burp/BurpExtender.class
|
||||
burp/CyberStrikeAITab$TestRun.class
|
||||
|
||||
+1
@@ -4,3 +4,4 @@
|
||||
/Users/temp/Downloads/CyberStrikeAI-main/plugins/burp-suite/cyberstrikeai-burp-extension/src/main/java/burp/HttpMessageFormatter.java
|
||||
/Users/temp/Downloads/CyberStrikeAI-main/plugins/burp-suite/cyberstrikeai-burp-extension/src/main/java/burp/MarkdownRenderer.java
|
||||
/Users/temp/Downloads/CyberStrikeAI-main/plugins/burp-suite/cyberstrikeai-burp-extension/src/main/java/burp/SimpleJson.java
|
||||
/Users/temp/Downloads/CyberStrikeAI-main/plugins/burp-suite/cyberstrikeai-burp-extension/src/main/java/burp/SslTrustAll.java
|
||||
|
||||
Reference in New Issue
Block a user