From 97fd4ff06939b643c48a0d9046bf46c20549071d Mon Sep 17 00:00:00 2001 From: logan Date: Wed, 3 Jun 2026 00:48:09 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=95=B0=E6=8D=AE=E5=8F=98=E6=9B=B4?= =?UTF-8?q?=E5=B7=A5=E5=8D=95=E6=8B=A6=E6=88=AA=E5=8F=AA=E8=AF=BBSQL=20?= =?UTF-8?q?=E2=80=94=20=E5=89=8D=E7=AB=AF=E6=8F=90=E4=BA=A4/=E6=A3=80?= =?UTF-8?q?=E6=B5=8B+=E5=90=8E=E7=AB=AF=E5=88=9B=E5=BB=BA=E4=B8=89?= =?UTF-8?q?=E5=B1=82=E6=8B=A6=E6=88=AASELECT/SHOW/DESC/EXPLAIN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/handler/ticket.go | 10 ++++++++++ frontend/src/pages/TicketCreatePage.tsx | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/backend/internal/handler/ticket.go b/backend/internal/handler/ticket.go index a2f9343..41f7f12 100644 --- a/backend/internal/handler/ticket.go +++ b/backend/internal/handler/ticket.go @@ -219,6 +219,16 @@ func CreateTicket(c *gin.Context) { return } + // Reject read-only SQL statements (SELECT/SHOW/DESC/EXPLAIN) + upperSQL := strings.ToUpper(strings.TrimSpace(input.SQLContent)) + readOnlyPrefixes := []string{"SELECT", "SHOW", "DESC", "DESCRIBE", "EXPLAIN"} + for _, prefix := range readOnlyPrefixes { + if strings.HasPrefix(upperSQL, prefix) { + c.JSON(http.StatusBadRequest, gin.H{"detail": "数据变更工单不支持 SELECT/SHOW/DESC 等只读语句,请使用查询数据功能"}) + return + } + } + // Auto-fill workflow_id from instance if not provided if input.WorkflowID == nil || *input.WorkflowID == 0 { var inst model.Instance diff --git a/frontend/src/pages/TicketCreatePage.tsx b/frontend/src/pages/TicketCreatePage.tsx index 9e8487a..0df818b 100644 --- a/frontend/src/pages/TicketCreatePage.tsx +++ b/frontend/src/pages/TicketCreatePage.tsx @@ -180,6 +180,15 @@ export default function TicketCreatePage() { if (!instanceId) { setError('请选择目标实例'); return; } if (!database) { setError('请选择数据库'); return; } + // 前端拦截只读语句 + const upperSQL = sqlContent.trim().toUpperCase(); + const readOnlyPrefixes = ['SELECT', 'SHOW', 'DESC', 'DESCRIBE', 'EXPLAIN']; + if (readOnlyPrefixes.some(p => upperSQL.startsWith(p))) { + setError('数据变更工单不支持 SELECT/SHOW/DESC 等只读语句,请使用查询数据功能'); + setValidated(false); + return; + } + setError(''); setAuditResult(null); setValidating(true); try { const result = await ticketApi.audit({ @@ -212,6 +221,14 @@ export default function TicketCreatePage() { if (!sqlContent.trim()) { setError('请填写 SQL 语句'); return; } if (!reason.trim()) { setError('请填写变更原因'); return; } + // 前端拦截只读语句 + const upperSQL = sqlContent.trim().toUpperCase(); + const readOnlyPrefixes = ['SELECT', 'SHOW', 'DESC', 'DESCRIBE', 'EXPLAIN']; + if (readOnlyPrefixes.some(p => upperSQL.startsWith(p))) { + setError('数据变更工单不支持 SELECT/SHOW/DESC 等只读语句,请使用查询数据功能'); + return; + } + setError(''); setSubmitting(true); try {