diff --git a/src/dispatch/database/revisions/tenant/versions/2025-08-06_f2bce475e71b.py b/src/dispatch/database/revisions/tenant/versions/2025-08-06_f2bce475e71b.py new file mode 100644 index 000000000000..6e04bd650e4d --- /dev/null +++ b/src/dispatch/database/revisions/tenant/versions/2025-08-06_f2bce475e71b.py @@ -0,0 +1,23 @@ +"""Adds support for configuring security event suggestions in the project model. + +Revision ID: f2bce475e71b +Revises: 408118048599 +Create Date: 2025-08-06 09:48:16.045362 + +""" +from alembic import op +import sqlalchemy as sa + + +revision = 'f2bce475e71b' +down_revision = '4649b11b683f' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('project', sa.Column('suggest_security_event_over_incident', sa.Boolean(), server_default='t', nullable=True)) + + +def downgrade(): + op.drop_column('project', 'suggest_security_event_over_incident') diff --git a/src/dispatch/project/models.py b/src/dispatch/project/models.py index 4adcece43fc7..4278858d2a46 100644 --- a/src/dispatch/project/models.py +++ b/src/dispatch/project/models.py @@ -67,6 +67,9 @@ class Project(Base): report_incident_title_hint = Column(String, nullable=True) report_incident_description_hint = Column(String, nullable=True) + # controls whether to suggest security events over incidents + suggest_security_event_over_incident = Column(Boolean, default=False, server_default="f") + snooze_extension_oncall_service_id = Column(Integer, nullable=True) snooze_extension_oncall_service = relationship( "Service", @@ -116,6 +119,7 @@ class ProjectBase(DispatchBase): report_incident_instructions: str | None = None report_incident_title_hint: str | None = None report_incident_description_hint: str | None = None + suggest_security_event_over_incident: bool | None = Field(True) snooze_extension_oncall_service: Service | None = None @@ -127,8 +131,8 @@ class ProjectUpdate(ProjectBase): send_daily_reports: bool | None = Field(True) send_weekly_reports: bool | None = Field(False) weekly_report_notification_id: int | None = None - stable_priority_id: int | None - snooze_extension_oncall_service_id: int | None + stable_priority_id: int | None = None + snooze_extension_oncall_service_id: int | None = None class ProjectRead(ProjectBase): diff --git a/src/dispatch/static/dispatch/src/components/AppToolbar.vue b/src/dispatch/static/dispatch/src/components/AppToolbar.vue index 5825541b9a3b..4375941768c8 100644 --- a/src/dispatch/static/dispatch/src/components/AppToolbar.vue +++ b/src/dispatch/static/dispatch/src/components/AppToolbar.vue @@ -23,6 +23,17 @@ /> + + mdi-shield-search + Report Security Event + @@ -153,6 +164,7 @@ import OrganizationApi from "@/organization/api" import OrganizationCreateEditDialog from "@/organization/CreateEditDialog.vue" import UserApi from "@/auth/api" import CurrentUserAvatar from "@/atomics/CurrentUserAvatar.vue" +import { mapFields } from "vuex-map-fields" export default { name: "AppToolbar", @@ -169,6 +181,7 @@ export default { CurrentUserAvatar, }, computed: { + ...mapFields("auth", ["currentUser.projects"]), queryString: { set(query) { this.$store.dispatch("search/setQuery", query) @@ -198,6 +211,18 @@ export default { }) }, }, + defaultUserProjects() { + if (!this.projects || this.projects.length === 0) { + return [] + } + return this.projects.filter((v) => v.default === true).map((v) => v.project) + }, + shouldShowSecurityEventButton() { + // Check if any of the default projects have the security event suggestion enabled + return this.defaultUserProjects.some( + (project) => project.suggest_security_event_over_incident === true + ) + }, }, methods: { updateExperimentalFeatures() { @@ -230,6 +255,9 @@ export default { localStorage.setItem("dark_theme", this.$vuetify.theme.global.current.dark.toString()) this.dark_theme = !this.dark_theme }, + navigateToEventReport() { + this.$router.push({ name: "eventReport" }) + }, switchOrganizations(slug) { this.$router.push({ params: { organization: slug } }).then(() => { this.$router.go() @@ -237,7 +265,7 @@ export default { }, ...mapState("auth", ["currentUser"]), ...mapState("app", ["currentVersion", "currentVersionDate"]), - ...mapActions("auth", ["logout", "getExperimentalFeatures"]), + ...mapActions("auth", ["logout", "getExperimentalFeatures", "refreshCurrentUser"]), ...mapActions("search", ["setQuery"]), ...mapActions("organization", ["showCreateEditDialog"]), ...mapActions("app", ["showCommitMessage"]), @@ -272,6 +300,13 @@ export default { this.loading = false }) + // Fetch user projects if they're not loaded + if (!this.projects || this.projects.length === 0) { + this.refreshCurrentUser().catch((error) => { + console.error("Failed to refresh user projects:", error) + }) + } + this.getExperimentalFeatures() }, } diff --git a/src/dispatch/static/dispatch/src/incident/ReportSubmissionCard.vue b/src/dispatch/static/dispatch/src/incident/ReportSubmissionCard.vue index 96b12e21edec..adfaa57099fd 100644 --- a/src/dispatch/static/dispatch/src/incident/ReportSubmissionCard.vue +++ b/src/dispatch/static/dispatch/src/incident/ReportSubmissionCard.vue @@ -101,19 +101,41 @@ - - Submit - - +
+ + +
+ Consider reporting as a Security Event instead +
+ Please consider using the + + Security Event reporting page + + for better handling and response. +
+
+ + Submit + + +
diff --git a/src/dispatch/static/dispatch/src/project/NewEditSheet.vue b/src/dispatch/static/dispatch/src/project/NewEditSheet.vue index 30a93c5a9f15..b1f9053da88d 100644 --- a/src/dispatch/static/dispatch/src/project/NewEditSheet.vue +++ b/src/dispatch/static/dispatch/src/project/NewEditSheet.vue @@ -182,6 +182,13 @@ /> Report incident card settings + + + { storage_use_title: false, allow_self_join: null, select_commander_visibility: null, + suggest_security_event_over_incident: null, + stable_priority_id: null, + snooze_extension_oncall_service_id: null, + report_incident_instructions: null, + report_incident_title_hint: null, + report_incident_description_hint: null, + send_daily_reports: null, + send_weekly_reports: null, + weekly_report_notification_id: null, } }