Skip to content
Merged
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
38 changes: 28 additions & 10 deletions src/burp/gui/BinTab.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import burp.api.montoya.ui.editor.HttpResponseEditor;
import burp.gui.ToastNotification.MessageType;
import burp.models.RequestBin;
import burp.util.StorageFileUtils;
import interactsh.InteractshEntry;

import javax.swing.*;
Expand Down Expand Up @@ -630,7 +631,7 @@ private List<InteractshEntry> getPersistedInteractionsFromStorage() {

String userHome = System.getProperty("user.home");
java.io.File storageDir = new java.io.File(userHome, ".requestbin-collaborator");
java.io.File dataFile = new java.io.File(storageDir, "interactions-" + bin.getUniqueId() + ".json");
java.io.File dataFile = StorageFileUtils.interactionsFile(storageDir, bin.getUniqueId());

if (!dataFile.exists()) {
api.logging().logToOutput("[BinTab] No persisted interactions file for bin: " + bin.getName());
Expand Down Expand Up @@ -824,7 +825,7 @@ private void updateViewedStatusInStorage(java.util.Set<String> entryKeys) {

String userHome = System.getProperty("user.home");
java.io.File storageDir = new java.io.File(userHome, ".requestbin-collaborator");
java.io.File dataFile = new java.io.File(storageDir, "interactions-" + bin.getUniqueId() + ".json");
java.io.File dataFile = StorageFileUtils.interactionsFile(storageDir, bin.getUniqueId());

if (!dataFile.exists()) {
return;
Expand Down Expand Up @@ -999,40 +1000,57 @@ private void clearLog() {
// Update empty state (switch to empty view)
updateEmptyState();

// Delete persistence file
deletePersistenceFile();

ToastNotification.showToast(this, "✓ Log cleared and persistence file deleted", MessageType.SUCCESS);
// Delete persistence file in background to avoid blocking EDT
deletePersistenceFileAsync();
}
}

/**
* Delete the persistence file for this bin
*/
private void deletePersistenceFile() {
private void deletePersistenceFileAsync() {
Thread deletionThread = new Thread(() -> {
boolean deleted = deletePersistenceFile();
SwingUtilities.invokeLater(() -> {
if (deleted) {
ToastNotification.showToast(this, "✓ Log cleared and persistence file deleted", MessageType.SUCCESS);
} else {
ToastNotification.showToast(this, "✓ Log cleared (persistence file was unchanged)", MessageType.WARNING);
}
});
}, "requestbin-delete-persistence");
deletionThread.setDaemon(true);
deletionThread.start();
}

private boolean deletePersistenceFile() {
try {
if (bin.getUniqueId() == null || bin.getUniqueId().isEmpty()) {
api.logging().logToError("Cannot delete persistence file: bin has no unique ID");
return;
return false;
}

// Use same path as updateViewedStatusInStorage method
String userHome = System.getProperty("user.home");
File storageDir = new File(userHome, ".requestbin-collaborator");
File persistenceFile = new File(storageDir, "interactions-" + bin.getUniqueId() + ".json");
File persistenceFile = StorageFileUtils.interactionsFile(storageDir, bin.getUniqueId());

if (persistenceFile.exists()) {
boolean deleted = persistenceFile.delete();
if (deleted) {
api.logging().logToOutput("Deleted persistence file: " + persistenceFile.getAbsolutePath());
return true;
} else {
api.logging().logToError("Failed to delete persistence file: " + persistenceFile.getAbsolutePath());
return false;
}
} else {
api.logging().logToOutput("Persistence file does not exist: " + persistenceFile.getAbsolutePath());
return false;
}
} catch (Exception e) {
api.logging().logToError("Error deleting persistence file: " + e.getMessage());
return false;
}
}

Expand Down Expand Up @@ -1268,4 +1286,4 @@ public void addInteraction(Object interaction) {
});
}
}
}
}
9 changes: 5 additions & 4 deletions src/burp/services/BinService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import burp.models.RequestBin;
import burp.models.BinServer;
import burp.models.Correlation;
import burp.util.StorageFileUtils;

import interactsh.InteractshEntry;
import org.json.JSONArray;
Expand Down Expand Up @@ -186,7 +187,7 @@ public boolean deleteBin(String binId) {
}
// delete persistence file
File storageDir = new File(System.getProperty("user.home"), ".requestbin-collaborator");
File persistenceFile = new File(storageDir, "interactions-" + toRemove.getUniqueId() + ".json");
File persistenceFile = StorageFileUtils.interactionsFile(storageDir, toRemove.getUniqueId());
if (persistenceFile.exists()) {
persistenceFile.delete();
}
Expand Down Expand Up @@ -456,7 +457,7 @@ private void loadPersistedInteractions() {
*/
private int loadInteractionsForBin(RequestBin bin, java.io.File storageDir) {
try {
java.io.File dataFile = new java.io.File(storageDir, "interactions-" + bin.getCorrelationId() + ".json");
java.io.File dataFile = StorageFileUtils.interactionsFile(storageDir, bin.getCorrelationId());

if (!dataFile.exists()) {
api.logging().logToOutput("[BinService] No persisted interactions for bin: " + bin.getName());
Expand Down Expand Up @@ -530,7 +531,7 @@ public List<InteractshEntry> getPersistedInteractions(RequestBin bin) {

String userHome = System.getProperty("user.home");
java.io.File storageDir = new java.io.File(userHome, ".requestbin-collaborator");
java.io.File dataFile = new java.io.File(storageDir, "interactions-" + bin.getCorrelationId() + ".json");
java.io.File dataFile = StorageFileUtils.interactionsFile(storageDir, bin.getCorrelationId());

if (!dataFile.exists()) {
return interactions;
Expand Down Expand Up @@ -627,4 +628,4 @@ public void clearAllBins() {
}
api.logging().logToOutput("Cleared all bins");
}
}
}
7 changes: 4 additions & 3 deletions src/burp/services/PollingService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import burp.api.montoya.MontoyaApi;
import burp.models.BinServer;
import burp.models.Correlation;
import burp.util.StorageFileUtils;
import burp.utils.CryptoUtils;
import interactsh.InteractshEntry;
import org.json.JSONArray;
Expand Down Expand Up @@ -333,8 +334,8 @@ private void saveInteractionToStorage(InteractshEntry entry, Correlation correla
api.logging().logToOutput("[PollingService] Created storage directory: " + storageDir.getAbsolutePath() + " (success: " + created + ")");
}

// File for this bin unique ID
File dataFile = new File(storageDir, "interactions-" + binUniqueId + ".json");
// File for this bin unique ID (sanitized to prevent path traversal)
File dataFile = StorageFileUtils.interactionsFile(storageDir, binUniqueId);

JSONArray storedInteractions;
if (dataFile.exists()) {
Expand Down Expand Up @@ -529,4 +530,4 @@ public String getBinUniqueId() {
return binUniqueId;
}
}
}
}
55 changes: 55 additions & 0 deletions src/burp/util/StorageFileUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package burp.util;

import java.io.File;
import java.util.Locale;

/**
* Utilities for safely constructing local storage file paths.
*/
public final class StorageFileUtils {
private static final int MAX_IDENTIFIER_LENGTH = 128;
private static final String DEFAULT_IDENTIFIER = "unknown";

private StorageFileUtils() {
// Utility class
}

/**
* Sanitizes an identifier so it is safe to use as part of a filename.
*/
public static String sanitizeIdentifierForFilename(String identifier) {
if (identifier == null) {
return DEFAULT_IDENTIFIER;
}

String sanitized = identifier
.trim()
.replace('\\', '_')
.replace('/', '_')
.replaceAll("[^a-zA-Z0-9._-]", "_");

// Avoid hidden-file style names and parent-directory patterns.
while (sanitized.startsWith(".")) {
sanitized = sanitized.substring(1);
}
sanitized = sanitized.replace("..", "_");

if (sanitized.isEmpty()) {
sanitized = DEFAULT_IDENTIFIER;
}

if (sanitized.length() > MAX_IDENTIFIER_LENGTH) {
sanitized = sanitized.substring(0, MAX_IDENTIFIER_LENGTH);
}

return sanitized.toLowerCase(Locale.ROOT);
}

/**
* Builds the storage file path for a bin/interaction identifier.
*/
public static File interactionsFile(File storageDir, String identifier) {
String safeIdentifier = sanitizeIdentifierForFilename(identifier);
return new File(storageDir, "interactions-" + safeIdentifier + ".json");
}
}
Loading