mirror of
https://github.com/PortSwigger/mcp-server
synced 2026-06-21 13:45:21 +00:00
* refactor: rename GetOrganizerRequests to GetOrganizerItems to match Montoya terminology * feat: include id and status in Organizer item serialization * feat: dedicated Organizer permission separate from HTTP history * refactor: rename HistoryAccess to DataAccess to cover Organizer items * fix: keep config change listener alive so checkbox sync survives GC * fix: declare data access listener before init so it isn't null at registration * chore: stop tracking .idea/ and update gitignore * fix: serialize Organizer item status using displayName() instead of enum constant name
This commit is contained in:
+1
-4
@@ -5,10 +5,7 @@ build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
.idea/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
Generated
-8
@@ -1,8 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
Generated
-1
@@ -1 +0,0 @@
|
||||
burp-mcp
|
||||
Generated
Generated
-16
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
-6
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.2.21" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
-1
@@ -1 +0,0 @@
|
||||
<template />
|
||||
Generated
-5
@@ -1,5 +0,0 @@
|
||||
<template unencoded="false">
|
||||
<roots>
|
||||
<root index="0" path="" />
|
||||
</roots>
|
||||
</template>
|
||||
Generated
-6
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -53,6 +53,12 @@ class ConfigUi(private val config: McpConfig, private val providers: List<Provid
|
||||
private var toggleListener: ((Boolean) -> Unit)? = null
|
||||
private var suppressToggleEvents: Boolean = false
|
||||
|
||||
private val dataAccessRefreshListener: () -> Unit = {
|
||||
SwingUtilities.invokeLater {
|
||||
serverConfigurationPanel.updateDataAccessCheckboxes()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
enabledToggle.setState(config.enabled, animate = false)
|
||||
hostField.text = config.host
|
||||
@@ -81,12 +87,7 @@ class ConfigUi(private val config: McpConfig, private val providers: List<Provid
|
||||
}
|
||||
|
||||
private fun setupConfigListeners() {
|
||||
val historyAccessRefreshListener = {
|
||||
SwingUtilities.invokeLater {
|
||||
serverConfigurationPanel.updateHistoryAccessCheckboxes()
|
||||
}
|
||||
}
|
||||
val handle = config.addHistoryAccessChangeListener(historyAccessRefreshListener)
|
||||
val handle = config.addDataAccessChangeListener(dataAccessRefreshListener)
|
||||
listenerHandles.add(handle)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class McpConfig(storage: PersistedObject, private val logging: Logging) {
|
||||
var host by storage.string("127.0.0.1")
|
||||
var port by storage.int(9876)
|
||||
var requireHttpRequestApproval by storage.boolean(true)
|
||||
var requireHistoryAccessApproval by storage.boolean(true)
|
||||
var requireDataAccessApproval by storage.boolean(true)
|
||||
|
||||
private var _alwaysAllowHttpHistory by storage.boolean(false)
|
||||
var alwaysAllowHttpHistory: Boolean
|
||||
@@ -24,7 +24,7 @@ class McpConfig(storage: PersistedObject, private val logging: Logging) {
|
||||
set(value) {
|
||||
if (_alwaysAllowHttpHistory != value) {
|
||||
_alwaysAllowHttpHistory = value
|
||||
notifyHistoryAccessChanged()
|
||||
notifyDataAccessChanged()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,13 +34,23 @@ class McpConfig(storage: PersistedObject, private val logging: Logging) {
|
||||
set(value) {
|
||||
if (_alwaysAllowWebSocketHistory != value) {
|
||||
_alwaysAllowWebSocketHistory = value
|
||||
notifyHistoryAccessChanged()
|
||||
notifyDataAccessChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private var _alwaysAllowOrganizer by storage.boolean(false)
|
||||
var alwaysAllowOrganizer: Boolean
|
||||
get() = _alwaysAllowOrganizer
|
||||
set(value) {
|
||||
if (_alwaysAllowOrganizer != value) {
|
||||
_alwaysAllowOrganizer = value
|
||||
notifyDataAccessChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private var _autoApproveTargets by storage.stringList("")
|
||||
private val targetsChangeListeners = CopyOnWriteArrayList<ListenerRegistration>()
|
||||
private val historyAccessChangeListeners = CopyOnWriteArrayList<ListenerRegistration>()
|
||||
private val dataAccessChangeListeners = CopyOnWriteArrayList<ListenerRegistration>()
|
||||
|
||||
var autoApproveTargets: String
|
||||
get() = _autoApproveTargets
|
||||
@@ -113,24 +123,24 @@ class McpConfig(storage: PersistedObject, private val logging: Logging) {
|
||||
}
|
||||
}
|
||||
|
||||
fun addHistoryAccessChangeListener(listener: () -> Unit): ListenerHandle {
|
||||
fun addDataAccessChangeListener(listener: () -> Unit): ListenerHandle {
|
||||
val registration = ListenerRegistration(listener)
|
||||
historyAccessChangeListeners.add(registration)
|
||||
return ListenerHandle { removeHistoryAccessChangeListener(registration) }
|
||||
dataAccessChangeListeners.add(registration)
|
||||
return ListenerHandle { removeDataAccessChangeListener(registration) }
|
||||
}
|
||||
|
||||
private fun removeHistoryAccessChangeListener(registration: ListenerRegistration) {
|
||||
historyAccessChangeListeners.remove(registration)
|
||||
private fun removeDataAccessChangeListener(registration: ListenerRegistration) {
|
||||
dataAccessChangeListeners.remove(registration)
|
||||
}
|
||||
|
||||
private fun notifyHistoryAccessChanged() {
|
||||
cleanupStaleListeners(historyAccessChangeListeners)
|
||||
val listeners = historyAccessChangeListeners.mapNotNull { it.listener.get() }
|
||||
private fun notifyDataAccessChanged() {
|
||||
cleanupStaleListeners(dataAccessChangeListeners)
|
||||
val listeners = dataAccessChangeListeners.mapNotNull { it.listener.get() }
|
||||
listeners.forEach { listener ->
|
||||
try {
|
||||
listener()
|
||||
} catch (e: Exception) {
|
||||
logging.logToError("History access change listener failed: ${e.message}")
|
||||
logging.logToError("Data access change listener failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,7 +152,7 @@ class McpConfig(storage: PersistedObject, private val logging: Logging) {
|
||||
|
||||
fun cleanup() {
|
||||
targetsChangeListeners.clear()
|
||||
historyAccessChangeListeners.clear()
|
||||
dataAccessChangeListeners.clear()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ class ServerConfigurationPanel(
|
||||
|
||||
private lateinit var alwaysAllowHttpHistoryCheckBox: JCheckBox
|
||||
private lateinit var alwaysAllowWebSocketHistoryCheckBox: JCheckBox
|
||||
private lateinit var alwaysAllowOrganizerCheckBox: JCheckBox
|
||||
|
||||
init {
|
||||
layout = BoxLayout(this, BoxLayout.Y_AXIS)
|
||||
@@ -61,12 +62,12 @@ class ServerConfigurationPanel(
|
||||
add(httpRequestApprovalCheckBox)
|
||||
add(createVerticalStrut(Design.Spacing.MD))
|
||||
|
||||
val historyAccessApprovalCheckBox = createHistoryAccessApprovalCheckBox()
|
||||
add(historyAccessApprovalCheckBox)
|
||||
val dataAccessApprovalCheckBox = createDataAccessApprovalCheckBox()
|
||||
add(dataAccessApprovalCheckBox)
|
||||
add(createVerticalStrut(Design.Spacing.SM))
|
||||
|
||||
alwaysAllowHttpHistoryCheckBox = createIndentedCheckBox(
|
||||
"Always allow HTTP history access", config.alwaysAllowHttpHistory, config.requireHistoryAccessApproval
|
||||
"Always allow HTTP history access", config.alwaysAllowHttpHistory, config.requireDataAccessApproval
|
||||
) { config.alwaysAllowHttpHistory = it }
|
||||
add(alwaysAllowHttpHistoryCheckBox)
|
||||
add(createVerticalStrut(Design.Spacing.SM))
|
||||
@@ -74,9 +75,17 @@ class ServerConfigurationPanel(
|
||||
alwaysAllowWebSocketHistoryCheckBox = createIndentedCheckBox(
|
||||
"Always allow WebSocket history access",
|
||||
config.alwaysAllowWebSocketHistory,
|
||||
config.requireHistoryAccessApproval
|
||||
config.requireDataAccessApproval
|
||||
) { config.alwaysAllowWebSocketHistory = it }
|
||||
add(alwaysAllowWebSocketHistoryCheckBox)
|
||||
add(createVerticalStrut(Design.Spacing.SM))
|
||||
|
||||
alwaysAllowOrganizerCheckBox = createIndentedCheckBox(
|
||||
"Always allow Organizer access",
|
||||
config.alwaysAllowOrganizer,
|
||||
config.requireDataAccessApproval
|
||||
) { config.alwaysAllowOrganizer = it }
|
||||
add(alwaysAllowOrganizerCheckBox)
|
||||
|
||||
add(validationErrorLabel)
|
||||
}
|
||||
@@ -95,26 +104,30 @@ class ServerConfigurationPanel(
|
||||
return enabledPanel
|
||||
}
|
||||
|
||||
private fun createHistoryAccessApprovalCheckBox(): JCheckBox {
|
||||
private fun createDataAccessApprovalCheckBox(): JCheckBox {
|
||||
return createStandardCheckBox(
|
||||
"Require approval for history access", config.requireHistoryAccessApproval
|
||||
"Require approval for project data access", config.requireDataAccessApproval
|
||||
) { enabled ->
|
||||
config.requireHistoryAccessApproval = enabled
|
||||
config.requireDataAccessApproval = enabled
|
||||
if (!enabled) {
|
||||
config.alwaysAllowHttpHistory = false
|
||||
config.alwaysAllowWebSocketHistory = false
|
||||
config.alwaysAllowOrganizer = false
|
||||
alwaysAllowHttpHistoryCheckBox.isSelected = false
|
||||
alwaysAllowWebSocketHistoryCheckBox.isSelected = false
|
||||
alwaysAllowOrganizerCheckBox.isSelected = false
|
||||
}
|
||||
alwaysAllowHttpHistoryCheckBox.isEnabled = enabled
|
||||
alwaysAllowWebSocketHistoryCheckBox.isEnabled = enabled
|
||||
alwaysAllowOrganizerCheckBox.isEnabled = enabled
|
||||
}
|
||||
}
|
||||
|
||||
fun updateHistoryAccessCheckboxes() {
|
||||
fun updateDataAccessCheckboxes() {
|
||||
SwingUtilities.invokeLater {
|
||||
alwaysAllowHttpHistoryCheckBox.isSelected = config.alwaysAllowHttpHistory
|
||||
alwaysAllowWebSocketHistoryCheckBox.isSelected = config.alwaysAllowWebSocketHistory
|
||||
alwaysAllowOrganizerCheckBox.isSelected = config.alwaysAllowOrganizer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.portswigger.mcp.schema
|
||||
|
||||
import burp.api.montoya.collaborator.Interaction as CollaboratorInteraction
|
||||
import burp.api.montoya.organizer.OrganizerItem
|
||||
import burp.api.montoya.proxy.ProxyHttpRequestResponse
|
||||
import burp.api.montoya.proxy.ProxyWebSocketMessage
|
||||
import burp.api.montoya.scanner.audit.issues.AuditIssue
|
||||
@@ -52,6 +53,16 @@ fun ProxyHttpRequestResponse.toSerializableForm(): HttpRequestResponse {
|
||||
)
|
||||
}
|
||||
|
||||
fun OrganizerItem.toSerializableForm(): OrganizerItemDetails {
|
||||
return OrganizerItemDetails(
|
||||
id = id(),
|
||||
status = status().displayName(),
|
||||
request = request()?.toString() ?: "<no request>",
|
||||
response = response()?.toString() ?: "<no response>",
|
||||
notes = annotations().notes()
|
||||
)
|
||||
}
|
||||
|
||||
fun ProxyWebSocketMessage.toSerializableForm(): WebSocketMessage {
|
||||
return WebSocketMessage(
|
||||
payload = payload()?.toString() ?: "<no payload>",
|
||||
@@ -108,6 +119,15 @@ data class HttpRequestResponse(
|
||||
val notes: String?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class OrganizerItemDetails(
|
||||
val id: Int,
|
||||
val status: String,
|
||||
val request: String?,
|
||||
val response: String?,
|
||||
val notes: String?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Interaction(
|
||||
val interactionId: String,
|
||||
|
||||
+25
-22
@@ -6,34 +6,35 @@ import javax.swing.SwingUtilities
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
enum class HistoryAccessType() {
|
||||
HTTP_HISTORY(), WEBSOCKET_HISTORY();
|
||||
enum class DataAccessType() {
|
||||
HTTP_HISTORY(), WEBSOCKET_HISTORY(), ORGANIZER();
|
||||
}
|
||||
|
||||
interface HistoryAccessApprovalHandler {
|
||||
suspend fun requestHistoryAccess(accessType: HistoryAccessType, config: McpConfig): Boolean
|
||||
interface DataAccessApprovalHandler {
|
||||
suspend fun requestDataAccess(accessType: DataAccessType, config: McpConfig): Boolean
|
||||
}
|
||||
|
||||
class SwingHistoryAccessApprovalHandler : HistoryAccessApprovalHandler {
|
||||
override suspend fun requestHistoryAccess(
|
||||
accessType: HistoryAccessType, config: McpConfig
|
||||
class SwingDataAccessApprovalHandler : DataAccessApprovalHandler {
|
||||
override suspend fun requestDataAccess(
|
||||
accessType: DataAccessType, config: McpConfig
|
||||
): Boolean {
|
||||
return suspendCoroutine { continuation ->
|
||||
SwingUtilities.invokeLater {
|
||||
val historyTypeName = when (accessType) {
|
||||
HistoryAccessType.HTTP_HISTORY -> "HTTP history"
|
||||
HistoryAccessType.WEBSOCKET_HISTORY -> "WebSocket history"
|
||||
val accessTypeName = when (accessType) {
|
||||
DataAccessType.HTTP_HISTORY -> "HTTP history"
|
||||
DataAccessType.WEBSOCKET_HISTORY -> "WebSocket history"
|
||||
DataAccessType.ORGANIZER -> "Organizer items"
|
||||
}
|
||||
|
||||
val message = buildString {
|
||||
appendLine("An MCP client is requesting access to your Burp Suite $historyTypeName.")
|
||||
appendLine("An MCP client is requesting access to your Burp Suite $accessTypeName.")
|
||||
appendLine()
|
||||
appendLine("This may include sensitive data from previous web sessions.")
|
||||
appendLine("Choose how you would like to respond:")
|
||||
}
|
||||
|
||||
val options = arrayOf(
|
||||
"Allow Once", "Always Allow $historyTypeName", "Deny"
|
||||
"Allow Once", "Always Allow $accessTypeName", "Deny"
|
||||
)
|
||||
|
||||
val burpFrame = findBurpFrame()
|
||||
@@ -49,8 +50,9 @@ class SwingHistoryAccessApprovalHandler : HistoryAccessApprovalHandler {
|
||||
|
||||
1 -> {
|
||||
when (accessType) {
|
||||
HistoryAccessType.HTTP_HISTORY -> config.alwaysAllowHttpHistory = true
|
||||
HistoryAccessType.WEBSOCKET_HISTORY -> config.alwaysAllowWebSocketHistory = true
|
||||
DataAccessType.HTTP_HISTORY -> config.alwaysAllowHttpHistory = true
|
||||
DataAccessType.WEBSOCKET_HISTORY -> config.alwaysAllowWebSocketHistory = true
|
||||
DataAccessType.ORGANIZER -> config.alwaysAllowOrganizer = true
|
||||
}
|
||||
continuation.resume(true)
|
||||
}
|
||||
@@ -64,26 +66,27 @@ class SwingHistoryAccessApprovalHandler : HistoryAccessApprovalHandler {
|
||||
}
|
||||
}
|
||||
|
||||
object HistoryAccessSecurity {
|
||||
object DataAccessSecurity {
|
||||
|
||||
var approvalHandler: HistoryAccessApprovalHandler = SwingHistoryAccessApprovalHandler()
|
||||
var approvalHandler: DataAccessApprovalHandler = SwingDataAccessApprovalHandler()
|
||||
|
||||
suspend fun checkHistoryAccessPermission(
|
||||
accessType: HistoryAccessType, config: McpConfig
|
||||
suspend fun checkDataAccessPermission(
|
||||
accessType: DataAccessType, config: McpConfig
|
||||
): Boolean {
|
||||
if (!config.requireHistoryAccessApproval) {
|
||||
if (!config.requireDataAccessApproval) {
|
||||
return true
|
||||
}
|
||||
|
||||
val isAlwaysAllowed = when (accessType) {
|
||||
HistoryAccessType.HTTP_HISTORY -> config.alwaysAllowHttpHistory
|
||||
HistoryAccessType.WEBSOCKET_HISTORY -> config.alwaysAllowWebSocketHistory
|
||||
DataAccessType.HTTP_HISTORY -> config.alwaysAllowHttpHistory
|
||||
DataAccessType.WEBSOCKET_HISTORY -> config.alwaysAllowWebSocketHistory
|
||||
DataAccessType.ORGANIZER -> config.alwaysAllowOrganizer
|
||||
}
|
||||
|
||||
if (isAlwaysAllowed) {
|
||||
return true
|
||||
}
|
||||
|
||||
return approvalHandler.requestHistoryAccess(accessType, config)
|
||||
return approvalHandler.requestDataAccess(accessType, config)
|
||||
}
|
||||
}
|
||||
@@ -15,17 +15,17 @@ import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.portswigger.mcp.config.McpConfig
|
||||
import net.portswigger.mcp.schema.toSerializableForm
|
||||
import net.portswigger.mcp.security.HistoryAccessSecurity
|
||||
import net.portswigger.mcp.security.HistoryAccessType
|
||||
import net.portswigger.mcp.security.DataAccessSecurity
|
||||
import net.portswigger.mcp.security.DataAccessType
|
||||
import net.portswigger.mcp.security.HttpRequestSecurity
|
||||
import java.awt.KeyboardFocusManager
|
||||
import java.util.regex.Pattern
|
||||
import javax.swing.JTextArea
|
||||
|
||||
private suspend fun checkHistoryPermissionOrDeny(
|
||||
accessType: HistoryAccessType, config: McpConfig, api: MontoyaApi, logMessage: String
|
||||
private suspend fun checkDataAccessOrDeny(
|
||||
accessType: DataAccessType, config: McpConfig, api: MontoyaApi, logMessage: String
|
||||
): Boolean {
|
||||
val allowed = HistoryAccessSecurity.checkHistoryAccessPermission(accessType, config)
|
||||
val allowed = DataAccessSecurity.checkDataAccessPermission(accessType, config)
|
||||
if (!allowed) {
|
||||
api.logging().logToOutput("MCP $logMessage access denied")
|
||||
return false
|
||||
@@ -283,7 +283,7 @@ fun Server.registerTools(api: MontoyaApi, config: McpConfig) {
|
||||
|
||||
mcpPaginatedTool<GetProxyHttpHistory>("Displays items within the proxy HTTP history") {
|
||||
val allowed = runBlocking {
|
||||
checkHistoryPermissionOrDeny(HistoryAccessType.HTTP_HISTORY, config, api, "HTTP history")
|
||||
checkDataAccessOrDeny(DataAccessType.HTTP_HISTORY, config, api, "HTTP history")
|
||||
}
|
||||
if (!allowed) {
|
||||
return@mcpPaginatedTool sequenceOf("HTTP history access denied by Burp Suite")
|
||||
@@ -294,7 +294,7 @@ fun Server.registerTools(api: MontoyaApi, config: McpConfig) {
|
||||
|
||||
mcpPaginatedTool<GetProxyHttpHistoryRegex>("Displays items matching a specified regex within the proxy HTTP history") {
|
||||
val allowed = runBlocking {
|
||||
checkHistoryPermissionOrDeny(HistoryAccessType.HTTP_HISTORY, config, api, "HTTP history")
|
||||
checkDataAccessOrDeny(DataAccessType.HTTP_HISTORY, config, api, "HTTP history")
|
||||
}
|
||||
if (!allowed) {
|
||||
return@mcpPaginatedTool sequenceOf("HTTP history access denied by Burp Suite")
|
||||
@@ -305,23 +305,23 @@ fun Server.registerTools(api: MontoyaApi, config: McpConfig) {
|
||||
.map { truncateIfNeeded(Json.encodeToString(it.toSerializableForm())) }
|
||||
}
|
||||
|
||||
mcpPaginatedTool<GetOrganizerRequests>("Displays items within the Organizer tab") {
|
||||
mcpPaginatedTool<GetOrganizerItems>("Displays items within the Organizer tab") {
|
||||
val allowed = runBlocking {
|
||||
checkHistoryPermissionOrDeny(HistoryAccessType.HTTP_HISTORY, config, api, "HTTP history")
|
||||
checkDataAccessOrDeny(DataAccessType.ORGANIZER, config, api, "Organizer")
|
||||
}
|
||||
if (!allowed) {
|
||||
return@mcpPaginatedTool sequenceOf("HTTP history access denied by Burp Suite")
|
||||
return@mcpPaginatedTool sequenceOf("Organizer access denied by Burp Suite")
|
||||
}
|
||||
|
||||
api.organizer().items().asSequence().map { truncateIfNeeded(Json.encodeToString(it.toSerializableForm())) }
|
||||
}
|
||||
|
||||
mcpPaginatedTool<GetOrganizerRequestsRegex>("Displays items matching a specified regex within the Organizer tab") {
|
||||
mcpPaginatedTool<GetOrganizerItemsRegex>("Displays items matching a specified regex within the Organizer tab") {
|
||||
val allowed = runBlocking {
|
||||
checkHistoryPermissionOrDeny(HistoryAccessType.HTTP_HISTORY, config, api, "HTTP history")
|
||||
checkDataAccessOrDeny(DataAccessType.ORGANIZER, config, api, "Organizer")
|
||||
}
|
||||
if (!allowed) {
|
||||
return@mcpPaginatedTool sequenceOf("HTTP history access denied by Burp Suite")
|
||||
return@mcpPaginatedTool sequenceOf("Organizer access denied by Burp Suite")
|
||||
}
|
||||
|
||||
val compiledRegex = Pattern.compile(regex)
|
||||
@@ -331,7 +331,7 @@ fun Server.registerTools(api: MontoyaApi, config: McpConfig) {
|
||||
|
||||
mcpPaginatedTool<GetProxyWebsocketHistory>("Displays items within the proxy WebSocket history") {
|
||||
val allowed = runBlocking {
|
||||
checkHistoryPermissionOrDeny(HistoryAccessType.WEBSOCKET_HISTORY, config, api, "WebSocket history")
|
||||
checkDataAccessOrDeny(DataAccessType.WEBSOCKET_HISTORY, config, api, "WebSocket history")
|
||||
}
|
||||
if (!allowed) {
|
||||
return@mcpPaginatedTool sequenceOf("WebSocket history access denied by Burp Suite")
|
||||
@@ -343,7 +343,7 @@ fun Server.registerTools(api: MontoyaApi, config: McpConfig) {
|
||||
|
||||
mcpPaginatedTool<GetProxyWebsocketHistoryRegex>("Displays items matching a specified regex within the proxy WebSocket history") {
|
||||
val allowed = runBlocking {
|
||||
checkHistoryPermissionOrDeny(HistoryAccessType.WEBSOCKET_HISTORY, config, api, "WebSocket history")
|
||||
checkDataAccessOrDeny(DataAccessType.WEBSOCKET_HISTORY, config, api, "WebSocket history")
|
||||
}
|
||||
if (!allowed) {
|
||||
return@mcpPaginatedTool sequenceOf("WebSocket history access denied by Burp Suite")
|
||||
@@ -486,10 +486,10 @@ data class GetProxyHttpHistory(override val count: Int, override val offset: Int
|
||||
data class GetProxyHttpHistoryRegex(val regex: String, override val count: Int, override val offset: Int) : Paginated
|
||||
|
||||
@Serializable
|
||||
data class GetOrganizerRequests(override val count: Int, override val offset: Int) : Paginated
|
||||
data class GetOrganizerItems(override val count: Int, override val offset: Int) : Paginated
|
||||
|
||||
@Serializable
|
||||
data class GetOrganizerRequestsRegex(val regex: String, override val count: Int, override val offset: Int) : Paginated
|
||||
data class GetOrganizerItemsRegex(val regex: String, override val count: Int, override val offset: Int) : Paginated
|
||||
|
||||
@Serializable
|
||||
data class GetProxyWebsocketHistory(override val count: Int, override val offset: Int) : Paginated
|
||||
|
||||
@@ -58,9 +58,10 @@ class ToolsKtTest {
|
||||
every { getBoolean("enabled") } returns true
|
||||
every { getBoolean("configEditingTooling") } returns true
|
||||
every { getBoolean("requireHttpRequestApproval") } returns false
|
||||
every { getBoolean("requireHistoryAccessApproval") } returns false
|
||||
every { getBoolean("requireDataAccessApproval") } returns false
|
||||
every { getBoolean("_alwaysAllowHttpHistory") } returns false
|
||||
every { getBoolean("_alwaysAllowWebSocketHistory") } returns false
|
||||
every { getBoolean("_alwaysAllowOrganizer") } returns false
|
||||
every { getString("host") } returns "127.0.0.1"
|
||||
every { getString("_autoApproveTargets") } returns ""
|
||||
every { getInteger("port") } returns testPort
|
||||
|
||||
Reference in New Issue
Block a user