Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/java/ch/csnc/burp/CopyRequestResponseConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,21 @@ public class CopyRequestResponseConfiguration {
private static final String COPY_FULL_HEADER_DEFAULT = "Ctrl+Shift+Alt+C";
private static final String USE_NBSP_LABEL = "Use Non-Breakable Spaces";
private static final boolean USE_NBSP_DEFAULT = false;
private static final String USE_MARKDOWN_FORMAT_LABEL = "Use Markdown Format";
private static final boolean USE_MARKDOWN_FORMAT_DEFAULT = false;
private static final String TEMPLATE_LABEL = "Template";
private static final String TEMPLATE_DEFAULT = "{request}\\n\\n{response}";
private static final String HIDE_REQUEST_HEADERS_LABEL = "Hide Request Headers";
private static final String HIDE_REQUEST_HEADERS_DEFAULT = "";
private static final String HIDE_RESPONSE_HEADERS_LABEL = "Hide Response Headers";
private static final String HIDE_RESPONSE_HEADERS_DEFAULT = "";
private static final String MARKDOWN_CODE_BLOCK_HEADER_LABEL = "Markdown Code Block Header";
private static final String MARKDOWN_CODE_BLOCK_HEADER_DEFAULT = "http";

private final static SettingsPanelSetting[] settings = new SettingsPanelSetting[] {
SettingsPanelSetting.stringSetting(CUT_TEXT_LABEL, CUT_TEXT_DEFAULT),
SettingsPanelSetting.booleanSetting(USE_NBSP_LABEL, USE_NBSP_DEFAULT),
SettingsPanelSetting.booleanSetting(USE_MARKDOWN_FORMAT_LABEL, USE_MARKDOWN_FORMAT_DEFAULT),
SettingsPanelSetting.stringSetting(COPY_FULL_FULL_OR_SELECTION_HOT_KEY_LABEL,
COPY_FULL_FULL_OR_SELECTION_HOT_KEY_DEFAULT),
SettingsPanelSetting.stringSetting(COPY_FULL_HEADER_LABEL, COPY_FULL_HEADER_DEFAULT),
Expand All @@ -36,6 +41,7 @@ public class CopyRequestResponseConfiguration {
"Comma-separated, case-insensitive regexes to match header names, e.g., \"sec-.*,accept.*\"",
HIDE_REQUEST_HEADERS_LABEL, HIDE_REQUEST_HEADERS_DEFAULT),
SettingsPanelSetting.stringSetting(HIDE_RESPONSE_HEADERS_LABEL, HIDE_RESPONSE_HEADERS_DEFAULT),
SettingsPanelSetting.stringSetting(MARKDOWN_CODE_BLOCK_HEADER_LABEL, MARKDOWN_CODE_BLOCK_HEADER_DEFAULT),
};

private final static SettingsPanelWithData panel = SettingsPanelBuilder.settingsPanel()
Expand All @@ -58,6 +64,10 @@ public static boolean useNonBreakableSpace() {
return panel.getBoolean(USE_NBSP_LABEL);
}

public static boolean useMarkdownFormat() {
return panel.getBoolean(USE_MARKDOWN_FORMAT_LABEL);
}

public static String copyFullFullOrSelectionHotKey() {
return panel.getString(COPY_FULL_FULL_OR_SELECTION_HOT_KEY_LABEL);
}
Expand All @@ -84,6 +94,10 @@ public static List<Pattern> hideResponseHeaders() {
.toList();
}

public static String markdownCodeBlockHeader() {
return panel.getString(MARKDOWN_CODE_BLOCK_HEADER_LABEL);
}

public static void register() {
CopyRequestResponseExtension.api().userInterface().registerSettingsPanel(panel);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ public List<Component> provideMenuItems(ContextMenuEvent event) {
var menuItems = new ArrayList<Component>();

var copyFullFull = new JMenuItem("Copy HTTP Request & Response (Full/Full)");
copyFullFull.addActionListener(actionEvent -> CopyRequestResponseCopyActions.copyFullFull(requestResponses));
copyFullFull.addActionListener(actionEvent -> CopyRequestResponseCopyActions.copyFullFullConfigured(requestResponses));
menuItems.add(copyFullFull);

var copyFullHeader = new JMenuItem("Copy HTTP Request & Response (Full/Header)");
copyFullHeader.addActionListener(actionEvent -> CopyRequestResponseCopyActions.copyFullHeader(requestResponses));
copyFullHeader.addActionListener(actionEvent -> CopyRequestResponseCopyActions.copyFullHeaderConfigured(requestResponses));
menuItems.add(copyFullHeader);

if (event.messageEditorRequestResponse().map(MessageEditorHttpRequestResponse::selectionContext).orElse(null) == MessageEditorHttpRequestResponse.SelectionContext.RESPONSE) {
var copyFullHeaderPlusSelectedData = new JMenuItem("Copy HTTP Request & Response (Full/Header + Selected Data)");
copyFullHeaderPlusSelectedData.addActionListener(actionEvent -> CopyRequestResponseCopyActions.copyFullHeaderPlusSelectedData(event.messageEditorRequestResponse().orElseThrow()));
copyFullHeaderPlusSelectedData.addActionListener(actionEvent -> CopyRequestResponseCopyActions.copyFullHeaderPlusSelectedDataConfigured(event.messageEditorRequestResponse().orElseThrow()));
menuItems.add(copyFullHeaderPlusSelectedData);
}

Expand Down
88 changes: 85 additions & 3 deletions src/main/java/ch/csnc/burp/CopyRequestResponseCopyActions.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@

public class CopyRequestResponseCopyActions {

public static void copyFullFullConfigured(List<HttpRequestResponse> requestResponses) {
if (CopyRequestResponseConfiguration.useMarkdownFormat()) {
copyFullFullMarkdown(requestResponses);
} else {
copyFullFull(requestResponses);
}
}

public static void copyFullHeaderConfigured(List<HttpRequestResponse> requestResponses) {
if (CopyRequestResponseConfiguration.useMarkdownFormat()) {
copyFullHeaderMarkdown(requestResponses);
} else {
copyFullHeader(requestResponses);
}
}

public static void copyFullHeaderPlusSelectedDataConfigured(MessageEditorHttpRequestResponse editor) {
if (CopyRequestResponseConfiguration.useMarkdownFormat()) {
copyFullHeaderPlusSelectedDataMarkdown(editor);
} else {
copyFullHeaderPlusSelectedData(editor);
}
}

public static void copyFullFull(List<HttpRequestResponse> requestResponses) {
requestResponses = requestResponses.stream().map(CopyRequestResponseCopyActions::hideHeaders).toList();

Expand Down Expand Up @@ -50,11 +74,63 @@ public static void copyFullHeader(List<HttpRequestResponse> requestResponses) {
toClipboard(text);
}

public static void copyFullFullMarkdown(List<HttpRequestResponse> requestResponses) {
requestResponses = requestResponses.stream().map(CopyRequestResponseCopyActions::hideHeaders).toList();

var text = requestResponses.stream()
.map(requestResponse -> {
var requestBlock = toMarkdownCodeBlock(requestResponse.request().toString().strip());
var responseBlock = Optional.ofNullable(requestResponse.response())
.map(HttpResponse::toString)
.map(String::strip)
.map(CopyRequestResponseCopyActions::toMarkdownCodeBlock)
.orElse("");
return format(requestBlock, responseBlock);
})
.collect(Collectors.joining("\n\n"));

toClipboard(text);
}

public static void copyFullHeaderMarkdown(List<HttpRequestResponse> requestResponses) {
requestResponses = requestResponses.stream().map(CopyRequestResponseCopyActions::hideHeaders).toList();

var text = requestResponses.stream()
.map(requestResponse -> {
var requestBlock = toMarkdownCodeBlock(requestResponse.request().toString().strip());
var responseString = "";
if (requestResponse.hasResponse()) {
var response = requestResponse.response();
responseString = response.toString().substring(0, response.bodyOffset()).strip();
responseString += "\n\n";
responseString += CopyRequestResponseConfiguration.cutText();
}
var responseBlock = responseString.isEmpty() ? "" : toMarkdownCodeBlock(responseString);
return format(requestBlock, responseBlock);
})
.collect(Collectors.joining("\n\n"));

toClipboard(text);
}

public static void copyFullHeaderPlusSelectedData(MessageEditorHttpRequestResponse editor) {
var requestResponse = hideHeaders(editor.requestResponse());

var requestString = requestResponse.request().toString().strip();
var text = format(requestString, selectedResponseString(editor, requestResponse));
copyWithDelay(text);
}

public static void copyFullHeaderPlusSelectedDataMarkdown(MessageEditorHttpRequestResponse editor) {
var requestResponse = hideHeaders(editor.requestResponse());
var requestBlock = toMarkdownCodeBlock(requestResponse.request().toString().strip());
var responseString = selectedResponseString(editor, requestResponse);
var responseBlock = responseString.isEmpty() ? "" : toMarkdownCodeBlock(responseString);
var text = format(requestBlock, responseBlock);
copyWithDelay(text);
}

private static String selectedResponseString(MessageEditorHttpRequestResponse editor,
HttpRequestResponse requestResponse) {
Supplier<String> responseStringSupplier = () -> {
if (!requestResponse.hasResponse()) {
return "";
Expand All @@ -78,7 +154,6 @@ public static void copyFullHeaderPlusSelectedData(MessageEditorHttpRequestRespon
}

var endIndex = selectionOffsets.endIndexExclusive();
CopyRequestResponseExtension.api().logging().logToError("start: %d, end %d".formatted(startIndex, endIndex));
if (endIndex <= startIndex) {
responseString += CopyRequestResponseConfiguration.cutText();
return responseString;
Expand Down Expand Up @@ -109,8 +184,10 @@ public static void copyFullHeaderPlusSelectedData(MessageEditorHttpRequestRespon
return responseString;
};

var text = format(requestString, responseStringSupplier.get());
return responseStringSupplier.get();
}

private static void copyWithDelay(String text) {
// Ugly hack because VMware is messing up the clipboard if a text is still
// selected, the function
// has to be run in a separate thread which sleeps for 0.2 seconds.
Expand Down Expand Up @@ -159,6 +236,11 @@ private static String format(String request, String response) {
.replace("{response}", response);
}

private static String toMarkdownCodeBlock(String content) {
var header = CopyRequestResponseConfiguration.markdownCodeBlockHeader().strip();
return "```%s\n%s\n```".formatted(header, content);
}

private static void toClipboard(String text0) {
var text1 = text0.replaceAll("\r\n", "\n");
var systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ public static void register() {
event -> {
event.messageEditorRequestResponse().ifPresent(messageEditorHttpRequestResponse -> {
if (messageEditorHttpRequestResponse.selectionContext() == MessageEditorHttpRequestResponse.SelectionContext.RESPONSE && messageEditorHttpRequestResponse.selectionOffsets().isPresent()) {
CopyRequestResponseCopyActions.copyFullHeaderPlusSelectedData(messageEditorHttpRequestResponse);
CopyRequestResponseCopyActions.copyFullHeaderPlusSelectedDataConfigured(messageEditorHttpRequestResponse);
} else {
CopyRequestResponseCopyActions.copyFullFull(List.of(messageEditorHttpRequestResponse.requestResponse()));
CopyRequestResponseCopyActions.copyFullFullConfigured(List.of(messageEditorHttpRequestResponse.requestResponse()));
}
});
});
Expand All @@ -32,7 +32,7 @@ public static void register() {
event -> event.messageEditorRequestResponse()
.map(MessageEditorHttpRequestResponse::requestResponse)
.map(List::of)
.ifPresent(CopyRequestResponseCopyActions::copyFullHeader));
.ifPresent(CopyRequestResponseCopyActions::copyFullHeaderConfigured));
}

private CopyRequestResponseHotKeyHandler() {
Expand Down