mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 11:58:34 +08:00
fix(security): root-cause guards against blank tool guard rule rows
This commit is contained in:
parent
4567bd6019
commit
ae4d432ee2
@ -138,6 +138,17 @@ public class SecurityController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "按主键 ID 删除自定义规则(兜底,rule_id 异常时使用)")
|
||||||
|
@DeleteMapping("/guard/rules/by-id/{id}")
|
||||||
|
public R<String> deleteRuleByPk(@PathVariable Long id) {
|
||||||
|
try {
|
||||||
|
ruleService.deleteRuleByPk(id);
|
||||||
|
return R.ok("删除成功");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return R.fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== Audit ====================
|
// ==================== Audit ====================
|
||||||
|
|
||||||
@Operation(summary = "审计日志")
|
@Operation(summary = "审计日志")
|
||||||
|
|||||||
@ -64,10 +64,15 @@ public class ToolGuardRuleService {
|
|||||||
* 新增自定义规则
|
* 新增自定义规则
|
||||||
*/
|
*/
|
||||||
public ToolGuardRuleEntity createRule(ToolGuardRuleEntity rule) {
|
public ToolGuardRuleEntity createRule(ToolGuardRuleEntity rule) {
|
||||||
if (rule == null || rule.getRuleId() == null || rule.getRuleId().isBlank()) {
|
if (rule == null) {
|
||||||
throw new IllegalArgumentException("Rule ID is required");
|
throw new IllegalArgumentException("Rule body is required");
|
||||||
}
|
}
|
||||||
|
requireNonBlank(rule.getRuleId(), "Rule ID");
|
||||||
|
requireNonBlank(rule.getName(), "Rule name");
|
||||||
|
requireNonBlank(rule.getPattern(), "Rule pattern");
|
||||||
rule.setRuleId(rule.getRuleId().trim());
|
rule.setRuleId(rule.getRuleId().trim());
|
||||||
|
rule.setName(rule.getName().trim());
|
||||||
|
rule.setPattern(rule.getPattern().trim());
|
||||||
rule.setBuiltin(false);
|
rule.setBuiltin(false);
|
||||||
ruleMapper.insert(rule);
|
ruleMapper.insert(rule);
|
||||||
ruleRegistry.reload();
|
ruleRegistry.reload();
|
||||||
@ -75,7 +80,8 @@ public class ToolGuardRuleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新规则
|
* 更新规则。仅覆盖请求里显式提供的字段;显式传入的关键字段(name / pattern)
|
||||||
|
* 不允许置为空白,避免回写出无意义的"空名空模式"行。
|
||||||
*/
|
*/
|
||||||
public ToolGuardRuleEntity updateRule(String ruleId, ToolGuardRuleEntity update) {
|
public ToolGuardRuleEntity updateRule(String ruleId, ToolGuardRuleEntity update) {
|
||||||
ToolGuardRuleEntity existing = getByRuleId(ruleId);
|
ToolGuardRuleEntity existing = getByRuleId(ruleId);
|
||||||
@ -83,14 +89,20 @@ public class ToolGuardRuleService {
|
|||||||
throw new IllegalArgumentException("Rule not found: " + ruleId);
|
throw new IllegalArgumentException("Rule not found: " + ruleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update.getName() != null) existing.setName(update.getName());
|
if (update.getName() != null) {
|
||||||
|
requireNonBlank(update.getName(), "Rule name");
|
||||||
|
existing.setName(update.getName().trim());
|
||||||
|
}
|
||||||
if (update.getDescription() != null) existing.setDescription(update.getDescription());
|
if (update.getDescription() != null) existing.setDescription(update.getDescription());
|
||||||
if (update.getToolName() != null) existing.setToolName(update.getToolName());
|
if (update.getToolName() != null) existing.setToolName(update.getToolName());
|
||||||
if (update.getParamName() != null) existing.setParamName(update.getParamName());
|
if (update.getParamName() != null) existing.setParamName(update.getParamName());
|
||||||
if (update.getCategory() != null) existing.setCategory(update.getCategory());
|
if (update.getCategory() != null) existing.setCategory(update.getCategory());
|
||||||
if (update.getSeverity() != null) existing.setSeverity(update.getSeverity());
|
if (update.getSeverity() != null) existing.setSeverity(update.getSeverity());
|
||||||
if (update.getDecision() != null) existing.setDecision(update.getDecision());
|
if (update.getDecision() != null) existing.setDecision(update.getDecision());
|
||||||
if (update.getPattern() != null) existing.setPattern(update.getPattern());
|
if (update.getPattern() != null) {
|
||||||
|
requireNonBlank(update.getPattern(), "Rule pattern");
|
||||||
|
existing.setPattern(update.getPattern().trim());
|
||||||
|
}
|
||||||
if (update.getExcludePattern() != null) existing.setExcludePattern(update.getExcludePattern());
|
if (update.getExcludePattern() != null) existing.setExcludePattern(update.getExcludePattern());
|
||||||
if (update.getRemediation() != null) existing.setRemediation(update.getRemediation());
|
if (update.getRemediation() != null) existing.setRemediation(update.getRemediation());
|
||||||
if (update.getEnabled() != null) existing.setEnabled(update.getEnabled());
|
if (update.getEnabled() != null) existing.setEnabled(update.getEnabled());
|
||||||
@ -101,6 +113,12 @@ public class ToolGuardRuleService {
|
|||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void requireNonBlank(String value, String fieldLabel) {
|
||||||
|
if (value == null || value.isBlank()) {
|
||||||
|
throw new IllegalArgumentException(fieldLabel + " is required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启用/禁用规则
|
* 启用/禁用规则
|
||||||
*/
|
*/
|
||||||
@ -128,4 +146,24 @@ public class ToolGuardRuleService {
|
|||||||
ruleMapper.deleteById(existing.getId());
|
ruleMapper.deleteById(existing.getId());
|
||||||
ruleRegistry.reload();
|
ruleRegistry.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按主键 ID 删除自定义规则。兜底通道:当 rule_id 因历史脏数据为空或无法走
|
||||||
|
* /guard/rules/{ruleId} 路径变量时,UI 仍可通过主键删除。
|
||||||
|
*/
|
||||||
|
public void deleteRuleByPk(Long id) {
|
||||||
|
if (id == null) {
|
||||||
|
throw new IllegalArgumentException("Rule primary key is required");
|
||||||
|
}
|
||||||
|
ToolGuardRuleEntity existing = ruleMapper.selectById(id);
|
||||||
|
if (existing == null) {
|
||||||
|
throw new IllegalArgumentException("Rule not found: id=" + id);
|
||||||
|
}
|
||||||
|
if (Boolean.TRUE.equals(existing.getBuiltin())) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Cannot delete builtin rule: " + existing.getRuleId());
|
||||||
|
}
|
||||||
|
ruleMapper.deleteById(id);
|
||||||
|
ruleRegistry.reload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
-- Earlier releases let the rule-create API persist a custom guard rule
|
||||||
|
-- with a blank rule_id because the service skipped the not-blank check.
|
||||||
|
-- The resulting row was undeletable from the UI: the delete endpoint is
|
||||||
|
-- /guard/rules/{ruleId}, and a blank path variable produces a 404 instead
|
||||||
|
-- of resolving to the row. This migration does two things:
|
||||||
|
--
|
||||||
|
-- 1. Purge any orphan rows already persisted on existing installations
|
||||||
|
-- so users who hit the bug on v1.2.0 can recover without direct DB
|
||||||
|
-- surgery. NULL, empty, and whitespace-only rule_id are all swept;
|
||||||
|
-- built-in rules are excluded defensively because they are seeded
|
||||||
|
-- with stable IDs and should never appear here.
|
||||||
|
--
|
||||||
|
-- 2. Add a CHECK constraint so the database itself rejects blank
|
||||||
|
-- rule_id going forward. The service-layer guard already prevents
|
||||||
|
-- this from the UI, but the DB constraint defends against any
|
||||||
|
-- future code path that bypasses the service (batch import, direct
|
||||||
|
-- SQL, future endpoints) and makes the invariant explicit at the
|
||||||
|
-- schema level.
|
||||||
|
|
||||||
|
DELETE FROM mate_tool_guard_rule
|
||||||
|
WHERE (rule_id IS NULL OR LENGTH(TRIM(rule_id)) = 0)
|
||||||
|
AND (builtin IS NULL OR builtin = FALSE);
|
||||||
|
|
||||||
|
ALTER TABLE mate_tool_guard_rule
|
||||||
|
ADD CONSTRAINT ck_tool_guard_rule_id_nonblank
|
||||||
|
CHECK (rule_id IS NOT NULL AND LENGTH(TRIM(rule_id)) > 0);
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
-- See the matching H2 file for context. This migration purges any
|
||||||
|
-- orphan rows that earlier releases persisted with a blank rule_id and
|
||||||
|
-- then installs a CHECK constraint so the schema itself rejects blank
|
||||||
|
-- rule_id, defending against any future code path that bypasses the
|
||||||
|
-- service-layer guard. CHECK constraints are enforced from MySQL 8.0.16
|
||||||
|
-- onward; this project targets MySQL 8.0+ so the constraint is live.
|
||||||
|
|
||||||
|
DELETE FROM mate_tool_guard_rule
|
||||||
|
WHERE (rule_id IS NULL OR LENGTH(TRIM(rule_id)) = 0)
|
||||||
|
AND (builtin IS NULL OR builtin = FALSE);
|
||||||
|
|
||||||
|
ALTER TABLE mate_tool_guard_rule
|
||||||
|
ADD CONSTRAINT ck_tool_guard_rule_id_nonblank
|
||||||
|
CHECK (rule_id IS NOT NULL AND LENGTH(TRIM(rule_id)) > 0);
|
||||||
Loading…
Reference in New Issue
Block a user