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
11 changes: 6 additions & 5 deletions C2Client/C2Client/ArtifactPanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
)

from .grpcClient import TeamServerApi_pb2
from .operations import TeamServerOperations
from .panel_style import apply_dark_panel_style, style_dark_combo_popup
from .ui_status import StatusKind, apply_status, compact_message

Expand Down Expand Up @@ -97,7 +98,7 @@ class Artifacts(QWidget):

def __init__(self, parent: QWidget | None, grpcClient: Any) -> None:
super().__init__(parent)
self.grpcClient = grpcClient
self.operations = TeamServerOperations(grpcClient)
self.artifacts: list[Any] = []
apply_dark_panel_style(self)

Expand Down Expand Up @@ -229,7 +230,7 @@ def buildQuery(self) -> Any:

def refreshArtifacts(self) -> None:
try:
self.artifacts = list(self.grpcClient.listArtifacts(self.buildQuery()))
self.artifacts = self.operations.list_artifacts(self.buildQuery())
except Exception as exc:
self.artifacts = []
self.printArtifacts()
Expand Down Expand Up @@ -352,7 +353,7 @@ def downloadSelectedArtifactToClient(self) -> None:
return

try:
response = self.grpcClient.downloadArtifact(artifact_id)
response = self.operations.download_artifact(artifact_id)
except Exception as exc:
apply_status(self.statusLabel, f"Artifacts: {compact_message(exc, limit=120)}", StatusKind.ERROR)
return
Expand Down Expand Up @@ -389,7 +390,7 @@ def uploadArtifactFromClient(self) -> None:

platform, arch = self.selectedUploadTarget()
try:
response = self.grpcClient.uploadArtifact(source_path.name, payload, platform, arch)
response = self.operations.upload_artifact(source_path.name, payload, platform, arch)
except Exception as exc:
apply_status(self.statusLabel, f"Artifacts: {compact_message(exc, limit=120)}", StatusKind.ERROR)
return
Expand Down Expand Up @@ -425,7 +426,7 @@ def deleteSelectedArtifact(self) -> None:
return

try:
response = self.grpcClient.deleteArtifact(artifact_id)
response = self.operations.delete_artifact(artifact_id)
except Exception as exc:
apply_status(
self.statusLabel,
Expand Down
18 changes: 15 additions & 3 deletions C2Client/C2Client/AssistantPanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
QWidget,
QTextEdit
)
import markdown

from .assistant_agent import C2AssistantAgent
from .assistant_agent.domain.command_outcome import CommandOutcome
from .assistant_agent.skills.panel import SkillsPanel
from .assistant_agent.presentation import render_assistant_answer
from .assistant_agent.tools.module_state_tool import has_loaded_module, is_module_already_loaded_response
from .command_event_hub import CommandEventHub, CommandResultEvent
from .grpcClient import TeamServerApi_pb2
Expand Down Expand Up @@ -115,7 +115,7 @@ def __init__(self, parent, grpcClient, *, command_event_hub: CommandEventHub | N
else:
self.printInTerminal(
"System",
"Assistant ready. Open a session console from the Sessions tab to provide session context and command output to the assistant. Use /help to list local assistant commands.",
"Assistant ready. TeamServer actions are available without a beacon session. Open a session console for beacon commands. Use /bundles to inspect available tools or /help for local commands.",
)

def sessionAssistantMethod(self, action, beaconHash, listenerHash, hostname, username, arch, privilege, os, lastProofOfLife, killed):
Expand Down Expand Up @@ -389,6 +389,10 @@ def runCommand(self):
self._show_local_help()
return

if local_command == "/bundles":
self._show_bundles()
return

if local_command == "/status":
self._show_pending_status()
return
Expand Down Expand Up @@ -448,6 +452,7 @@ def _show_local_help(self):
"\n".join(
[
"/help - Show AssistantPanel local commands.",
"/bundles - Show conversation bundles and available tools.",
"/status - Show the current assistant pending command state.",
"/stop - Stop this assistant wait; the remote command continues.",
"/cancel and /reset - Aliases for /stop.",
Expand All @@ -457,6 +462,13 @@ def _show_local_help(self):
)


def _show_bundles(self):
capabilities = self.agent.conversation_capabilities()
lines = [f"{name}: {', '.join(tools) if tools else 'unavailable'}"
for name, tools in capabilities.items()]
self.printInTerminal("Conversation bundles:", "\n".join(lines))


def _show_pending_status(self):
self.printInTerminal("Analysis:", self._format_pending_status())

Expand Down Expand Up @@ -552,7 +564,7 @@ def _process_assistant_response(self, message):
if assistant_reply and not is_pending:
self.printInTerminal(
"Analysis:",
markdown.markdown(assistant_reply, extensions=["fenced_code", "tables"]),
render_assistant_answer(assistant_reply),
rich_message=True,
)

Expand Down
4 changes: 3 additions & 1 deletion C2Client/C2Client/CommandPanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from .console_style import CONSOLE_COLORS, apply_console_output_style
from .grpcClient import TeamServerApi_pb2
from .operations import TeamServerOperations
from .panel_style import apply_dark_panel_style, style_dark_combo_popup
from .ui_status import StatusKind, apply_status, compact_message

Expand Down Expand Up @@ -87,6 +88,7 @@ class Commands(QWidget):
def __init__(self, parent: QWidget | None, grpcClient: Any) -> None:
super().__init__(parent)
self.grpcClient = grpcClient
self.operations = TeamServerOperations(grpcClient)
self.commands: list[Any] = []
apply_dark_panel_style(self)

Expand Down Expand Up @@ -188,7 +190,7 @@ def buildQuery(self) -> Any:

def refreshCommands(self) -> None:
try:
self.commands = list(self.grpcClient.listCommands(self.buildQuery()))
self.commands = self.operations.list_commands(self.buildQuery())
except Exception as exc:
self.commands = []
self.printCommands()
Expand Down
14 changes: 8 additions & 6 deletions C2Client/C2Client/CredentialVaultPanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

from .console_style import apply_console_output_style
from .grpcClient import TeamServerApi_pb2
from .operations import TeamServerOperations
from .grpc_status import is_response_ok, operation_ack_text, response_message
from .panel_style import apply_dark_panel_style, style_dark_combo_popup
from .ui_status import StatusKind, apply_status, compact_message
Expand Down Expand Up @@ -214,6 +215,7 @@ class CredentialVault(QWidget):
def __init__(self, parent: QWidget | None, grpcClient: Any) -> None:
super().__init__(parent)
self.grpcClient = grpcClient
self.operations = TeamServerOperations(grpcClient)
self.credentials: list[Any] = []
self.selectedCredentialId = ""
self.detailSecretRevealed = False
Expand Down Expand Up @@ -373,7 +375,7 @@ def buildQuery(self) -> Any:

def refreshCredentials(self) -> None:
try:
self.credentials = list(self.grpcClient.listCredentials(self.buildQuery()))
self.credentials = self.operations.list_credentials(self.buildQuery())
except Exception as exc:
self.credentials = []
self.printCredentials()
Expand Down Expand Up @@ -535,7 +537,7 @@ def addCredential(self) -> None:
if values is None:
return
try:
ack = self.grpcClient.addCredential(self.buildRequestFromValues(values))
ack = self.operations.add_credential(self.buildRequestFromValues(values))
except Exception as exc:
apply_status(self.statusLabel, f"Add credential: {compact_message(exc, limit=120)}", StatusKind.ERROR)
return
Expand All @@ -547,7 +549,7 @@ def editSelectedCredential(self) -> None:
apply_status(self.statusLabel, "Edit credential: select a credential first.", StatusKind.WARNING)
return
try:
detail = self.grpcClient.getCredential(self.selectedCredentialId, reveal_secret=True)
detail = self.operations.get_credential(self.selectedCredentialId, reveal_secret=True)
except Exception as exc:
apply_status(self.statusLabel, f"Edit credential: {compact_message(exc, limit=120)}", StatusKind.ERROR)
return
Expand All @@ -571,7 +573,7 @@ def updateCredential(self, values: dict[str, str]) -> None:
return
try:
request = self.buildRequestFromValues(values, credential_id=self.selectedCredentialId)
ack = self.grpcClient.updateCredential(request)
ack = self.operations.update_credential(request)
except Exception as exc:
apply_status(self.statusLabel, f"Update credential: {compact_message(exc, limit=120)}", StatusKind.ERROR)
return
Expand All @@ -591,7 +593,7 @@ def revealDetailSecret(self) -> None:
apply_status(self.statusLabel, "Credential secret hidden.", StatusKind.SUCCESS)
return
try:
detail = self.grpcClient.getCredential(self.selectedCredentialId, reveal_secret=True)
detail = self.operations.get_credential(self.selectedCredentialId, reveal_secret=True)
except Exception as exc:
apply_status(self.statusLabel, f"Reveal credential: {compact_message(exc, limit=120)}", StatusKind.ERROR)
return
Expand Down Expand Up @@ -628,7 +630,7 @@ def deleteSelectedCredential(self) -> None:
if answer != QMessageBox.StandardButton.Yes:
return
try:
ack = self.grpcClient.deleteCredential(self.selectedCredentialId)
ack = self.operations.delete_credential(self.selectedCredentialId)
except Exception as exc:
apply_status(self.statusLabel, f"Delete credential: {compact_message(exc, limit=120)}", StatusKind.ERROR)
return
Expand Down
4 changes: 3 additions & 1 deletion C2Client/C2Client/ListenerPanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
)

from .grpcClient import TeamServerApi_pb2
from .operations import TeamServerOperations
from .env import env_int
from .grpc_status import is_response_ok, operation_ack_text
from .console_style import CONSOLE_COLORS
Expand Down Expand Up @@ -254,7 +255,8 @@ def __init__(self, grpc_client, action, request, signals):

def run(self):
try:
operation = self.grpc_client.addListener if self.action == "add" else self.grpc_client.stopListener
operations = TeamServerOperations(self.grpc_client)
operation = operations.add_listener if self.action == "add" else operations.stop_listener
ack = operation(self.request)
self.signals.finished.emit(self.action, ack, "")
except Exception as exc:
Expand Down
13 changes: 8 additions & 5 deletions C2Client/C2Client/SessionPanel.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
)

from .grpcClient import TeamServerApi_pb2
from .operations import TeamServerOperations
from .env import env_int, env_path
from .grpc_status import is_response_ok, operation_ack_text
from .panel_style import apply_dark_panel_style, create_dark_context_menu
Expand Down Expand Up @@ -245,7 +246,7 @@ def __init__(self, grpc_client, beacon_hash, listener_hash, signals):

def run(self):
try:
listeners = list(self.grpc_client.listListeners())
listeners = TeamServerOperations(self.grpc_client).list_listeners()
self.signals.finished.emit(self.beacon_hash, self.listener_hash, listeners, "")
except Exception as exc:
self.signals.finished.emit(self.beacon_hash, self.listener_hash, [], str(exc))
Expand All @@ -260,7 +261,7 @@ def __init__(self, grpc_client, request, signals):

def run(self):
try:
self.signals.finished.emit(self.grpc_client.stopSession(self.request), "")
self.signals.finished.emit(TeamServerOperations(self.grpc_client).stop_session(self.request), "")
except Exception as exc:
self.signals.finished.emit(None, str(exc))

Expand Down Expand Up @@ -808,8 +809,9 @@ def _eventFields(item):

def listSessions(self):
try:
fetch = getattr(self.grpcClient, "listSessionHistory", self.grpcClient.listSessions)
sessions = list(fetch())
operations = TeamServerOperations(self.grpcClient)
sessions = (operations.list_session_history() if hasattr(self.grpcClient, "listSessionHistory")
else operations.list_sessions())
except Exception as exc:
self._onRefreshFailure(str(exc))
return
Expand Down Expand Up @@ -1008,5 +1010,6 @@ def printSessions(self):

class GetSessionsWorker(RefreshWorker):
def __init__(self, grpc_client):
fetch = getattr(grpc_client, "listSessionHistory", grpc_client.listSessions)
operations = TeamServerOperations(grpc_client)
fetch = operations.list_session_history if hasattr(grpc_client, "listSessionHistory") else operations.list_sessions
super().__init__(fetch, env_int("C2_SESSION_REFRESH_MS", 2000, minimum=100))
Loading
Loading