mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-04 00:51:03 +02:00
Add files via upload
This commit is contained in:
+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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user