Skip to content
This repository was archived by the owner on Sep 3, 2025. It is now read-only.
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
73 changes: 32 additions & 41 deletions src/dispatch/plugins/dispatch_slack/case/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
from dispatch.participant_role import service as participant_role_service
from dispatch.participant_role.models import ParticipantRoleType
from dispatch.plugin import service as plugin_service
from dispatch.plugins.dispatch_duo.enums import PushResponseResult
from dispatch.plugins.dispatch_slack import service as dispatch_slack_service
from dispatch.plugins.dispatch_slack.bolt import app
from dispatch.plugins.dispatch_slack.case.enums import (
Expand Down Expand Up @@ -860,8 +859,7 @@ def _create_snooze_filter(
db_session.commit()

# Check if last_mfa_time was within the last hour
last_hour = datetime.now() - timedelta(hours=1)
if (user.last_mfa_time and user.last_mfa_time > last_hour) or mfa_enabled is False:
if not mfa_enabled:
_create_snooze_filter(
db_session=db_session,
user=user,
Expand All @@ -874,12 +872,23 @@ def _create_snooze_filter(
message="Snooze Filter added successfully.",
)
else:
# Send the MFA push notification
response = mfa_plugin.instance.send_push_notification(
username=context["user"].email,
type="Are you creating a snooze filter in Dispatch?",
challenge, challenge_url = mfa_plugin.instance.create_mfa_challenge(
action="signal-snooze",
current_user=user,
db_session=db_session,
project_id=context["subject"].project_id,
)
ack_engagement_submission_event(
ack=ack, mfa_enabled=mfa_enabled, challenge_url=challenge_url
)

# wait for the mfa challenge
response = mfa_plugin.instance.wait_for_challenge(
challenge_id=challenge.challenge_id,
db_session=db_session,
)
if response == PushResponseResult.allow:

if response == MfaChallengeStatus.APPROVED:
# Get the existing filters for the signal
_create_snooze_filter(
db_session=db_session,
Expand All @@ -895,10 +904,10 @@ def _create_snooze_filter(
user.last_mfa_time = datetime.now()
db_session.commit()
else:
if response == PushResponseResult.timeout:
if response == MfaChallengeStatus.EXPIRED:
text = "Adding Snooze failed, the MFA request timed out."
elif response == PushResponseResult.user_not_found:
text = "Adding Snooze failed, user not found in MFA provider."
elif response == MfaChallengeStatus.DENIED:
text = "Adding Snooze failed, challenge did not complete succsfully."
else:
text = "Adding Snooze failed, you must accept the MFA prompt."

Expand Down Expand Up @@ -1735,7 +1744,7 @@ def report_issue(
Context(
elements=[
MarkdownText(
text="Cases are meant to triage events that do not raise to the level of incidents, but can be escalated to incidents if necessary. If you suspect a security issue and need help, please fill out this form to the best of your abilities.."
text="Cases are meant for triaging events that do not raise to the level of incidents, but can be escalated to incidents if necessary."
)
]
),
Expand Down Expand Up @@ -1780,13 +1789,6 @@ def handle_report_project_select_action(
)

blocks = [
Context(
elements=[
MarkdownText(
text="Cases are meant to triage events that do not raise to the level of incidents, but can be escalated to incidents if necessary. If you suspect a security issue and need help, please fill out this form to the best of your abilities.."
)
]
),
title_input(),
description_input(),
project_select(
Expand All @@ -1809,13 +1811,6 @@ def handle_report_project_select_action(
)
]
),
case_priority_select(
db_session=db_session,
project_id=project.id,
initial_option=None,
optional=True,
block_id=None, # ensures state is reset
),
]

modal = Modal(
Expand Down Expand Up @@ -1899,13 +1894,6 @@ def handle_report_case_type_select_action(
assignee_slack_id = None

blocks = [
Context(
elements=[
MarkdownText(
text="Cases are meant to triage events that do not raise to the level of incidents, but can be escalated to incidents if necessary. If you suspect a security issue and need help, please fill out this form to the best of your abilities."
)
]
),
title_input(),
description_input(),
project_select(
Expand All @@ -1928,13 +1916,6 @@ def handle_report_case_type_select_action(
)
]
),
case_priority_select(
db_session=db_session,
project_id=project.id,
initial_option=None,
optional=True,
block_id=None, # ensures state is reset
),
]

# Create a new assignee_select block with a unique block_id
Expand All @@ -1944,7 +1925,7 @@ def handle_report_case_type_select_action(
initial_user=assignee_slack_id if assignee_slack_id else None,
action_id=CaseReportActions.assignee_select,
block_id=new_block_id,
)
),
)

# Conditionally add context blocks
Expand Down Expand Up @@ -1983,6 +1964,16 @@ def handle_report_case_type_select_action(
]
)

blocks.append(
case_priority_select(
db_session=db_session,
project_id=project.id,
initial_option=None,
optional=True,
block_id=None, # ensures state is reset
),
)

modal = Modal(
title="Open a Case",
blocks=blocks,
Expand Down
36 changes: 23 additions & 13 deletions src/dispatch/plugins/dispatch_slack/case/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,17 @@ def create_case_message(case: Case, channel_id: str) -> list[Block]:
Button(
text="Edit",
action_id=CaseNotificationActions.edit,
style="primary",
value=button_metadata,
),
Button(
text="Escalate",
action_id=CaseNotificationActions.escalate,
style="danger",
text=":slack: Create Channel",
action_id=CaseNotificationActions.migrate,
value=button_metadata,
),
Button(
text="Create Channel",
action_id=CaseNotificationActions.migrate,
style="primary",
text="🔥 Escalate",
action_id=CaseNotificationActions.escalate,
style="danger",
value=button_metadata,
),
]
Expand Down Expand Up @@ -208,7 +206,7 @@ def create_signal_messages(case_id: int, channel_id: str, db_session: Session) -
# Define the initial elements with "Raw Data" and "Snooze" buttons
elements = [
Button(
text="Snooze",
text="💤 Snooze",
action_id=SignalNotificationActions.snooze,
value=button_metadata,
),
Expand All @@ -219,7 +217,7 @@ def create_signal_messages(case_id: int, channel_id: str, db_session: Session) -
# If `first_instance_signal.external_url` is not empty, add the "Response Plan" button
elements.append(
Button(
text="Response Plan",
text="🔖 Response Plan",
action_id="button-link",
url=first_instance_signal.external_url,
)
Expand Down Expand Up @@ -308,9 +306,13 @@ def create_genai_signal_summary(
instances = signal_service.get_instances_in_case(db_session=db_session, case_id=case.id)
(first_instance_id, first_instance_signal) = instances.first()

related_cases = signal_service.get_cases_for_signal(
db_session=db_session, signal_id=first_instance_signal.id
).filter(Case.id != case.id)
related_cases = (
signal_service.get_cases_for_signal(
db_session=db_session, signal_id=first_instance_signal.id
)
.from_self()
.filter(Case.id != case.id)
)

# Prepare historical context
historical_context = []
Expand Down Expand Up @@ -345,11 +347,19 @@ def create_genai_signal_summary(
genai_plugin = plugin_service.get_active_instance(
db_session=db_session, project_id=case.project.id, plugin_type="artificial-intelligence"
)

if not genai_plugin:
log.warning("artificial-intelligence plugin not enabled, will not generate signal summary")
return signal_metadata_blocks

if not signal_instance.signal.genai_prompt:
log.warning(
f"artificial-intelligence plugin enabled but no prompt defined for {signal_instance.signal.name}"
)
return signal_metadata_blocks

response = genai_plugin.instance.chat_completion(
prompt=f"""{first_instance_signal.prompt}
prompt=f"""{signal_instance.signal.genai_prompt}

Current Event:
{str(signal_instance.raw)}
Expand Down
26 changes: 14 additions & 12 deletions src/dispatch/plugins/dispatch_slack/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,18 +122,20 @@ def create_threaded(self, case: Case, conversation_id: str, db_session: Session)
except Exception as e:
logger.exception(f"Error uploading alert JSON to the Case thread: {e}")

message = create_genai_signal_summary(
case=case,
channel_id=conversation_id,
db_session=db_session,
client=client,
)
send_message(
client=client,
conversation_id=conversation_id,
ts=case.signal_thread_ts,
blocks=message,
)
try:
send_message(
client=client,
conversation_id=conversation_id,
ts=case.signal_thread_ts,
blocks=create_genai_signal_summary(
case=case,
channel_id=conversation_id,
db_session=db_session,
client=client,
),
)
except Exception as e:
logger.exception(f"Error generating Gen AI response to case: {e}")
db_session.commit()
return response

Expand Down
3 changes: 0 additions & 3 deletions src/dispatch/static/dispatch/src/signal/NewEditDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,6 @@
hint="The model to use for processing."
persistent-hint
name="model"
readonly
/>
</v-col>
<v-col cols="12">
Expand All @@ -254,7 +253,6 @@
hint="The system message to set the behavior of the assistant"
persistent-hint
name="systemMessage"
readonly
/>
</v-col>
<v-col cols="12">
Expand All @@ -266,7 +264,6 @@
hint="The prompt to use for the assistant."
persistent-hint
name="Prompt"
readonly
/>
</v-col>
</v-row>
Expand Down