fix:修复内容管理的
This commit is contained in:
parent
d97d513598
commit
c3f9029a28
|
|
@ -2,6 +2,7 @@ package com.llm.guard.biz.controller;
|
||||||
|
|
||||||
import com.llm.guard.biz.domain.param.ContentDlpRuleParam;
|
import com.llm.guard.biz.domain.param.ContentDlpRuleParam;
|
||||||
import com.llm.guard.biz.domain.param.ContentAuditCorpusParam;
|
import com.llm.guard.biz.domain.param.ContentAuditCorpusParam;
|
||||||
|
import com.llm.guard.biz.domain.param.ContentAuditCorpusQueryParam;
|
||||||
import com.llm.guard.biz.domain.param.ContentAuditPageQueryParam;
|
import com.llm.guard.biz.domain.param.ContentAuditPageQueryParam;
|
||||||
import com.llm.guard.biz.domain.param.ContentAuditPolicyCreateParam;
|
import com.llm.guard.biz.domain.param.ContentAuditPolicyCreateParam;
|
||||||
import com.llm.guard.biz.domain.param.ContentAuditRecallConfigParam;
|
import com.llm.guard.biz.domain.param.ContentAuditRecallConfigParam;
|
||||||
|
|
@ -11,6 +12,7 @@ import com.llm.guard.biz.domain.param.ContentMaskPolicyParam;
|
||||||
import com.llm.guard.biz.domain.param.ContentPolicyParam;
|
import com.llm.guard.biz.domain.param.ContentPolicyParam;
|
||||||
import com.llm.guard.biz.domain.param.StatusUpdateParam;
|
import com.llm.guard.biz.domain.param.StatusUpdateParam;
|
||||||
import com.llm.guard.biz.domain.resp.ContentAuditPageResp;
|
import com.llm.guard.biz.domain.resp.ContentAuditPageResp;
|
||||||
|
import com.llm.guard.biz.domain.resp.ContentAuditCorpusResp;
|
||||||
import com.llm.guard.biz.domain.resp.ContentDlpRuleResp;
|
import com.llm.guard.biz.domain.resp.ContentDlpRuleResp;
|
||||||
import com.llm.guard.biz.domain.resp.ContentMaskPolicyResp;
|
import com.llm.guard.biz.domain.resp.ContentMaskPolicyResp;
|
||||||
import com.llm.guard.biz.domain.resp.ContentPolicyResp;
|
import com.llm.guard.biz.domain.resp.ContentPolicyResp;
|
||||||
|
|
@ -161,6 +163,13 @@ public class ConfigContentConfigController {
|
||||||
return AjaxResult.success(resp);
|
return AjaxResult.success(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "词库分页查询")
|
||||||
|
@GetMapping("/audit/corpus/list")
|
||||||
|
public AjaxResult listAuditCorpus(@Validated @ParameterObject ContentAuditCorpusQueryParam query) {
|
||||||
|
PageResp<ContentAuditCorpusResp> resp = contentAuditPageService.corpusPage(query);
|
||||||
|
return AjaxResult.success(resp);
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "更新合规检测范围")
|
@Operation(summary = "更新合规检测范围")
|
||||||
@PutMapping("/audit/scope")
|
@PutMapping("/audit/scope")
|
||||||
public AjaxResult updateAuditScope(@RequestBody ContentAuditScopeConfigParam param) {
|
public AjaxResult updateAuditScope(@RequestBody ContentAuditScopeConfigParam param) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package com.llm.guard.biz.controller;
|
package com.llm.guard.biz.controller;
|
||||||
|
|
||||||
import com.llm.guard.biz.domain.param.SecurityPostureQueryParam;
|
import com.llm.guard.biz.domain.param.SecurityPostureQueryParam;
|
||||||
|
import com.llm.guard.biz.domain.resp.IntelligentRiskAnalysisResp;
|
||||||
|
import com.llm.guard.biz.domain.resp.SecurityGovernanceLoopResp;
|
||||||
import com.llm.guard.biz.domain.resp.SecurityPostureResp;
|
import com.llm.guard.biz.domain.resp.SecurityPostureResp;
|
||||||
import com.llm.guard.biz.service.SecurityPostureService;
|
import com.llm.guard.biz.service.SecurityPostureService;
|
||||||
import com.llm.guard.common.core.web.domain.AjaxResult;
|
import com.llm.guard.common.core.web.domain.AjaxResult;
|
||||||
|
|
@ -27,4 +29,18 @@ public class SecurityPostureController {
|
||||||
SecurityPostureResp resp = securityPostureService.queryPosture(query);
|
SecurityPostureResp resp = securityPostureService.queryPosture(query);
|
||||||
return AjaxResult.success(resp);
|
return AjaxResult.success(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取智能风险分析中心数据")
|
||||||
|
@GetMapping("/risk-analysis")
|
||||||
|
public AjaxResult riskAnalysis(@Validated @ParameterObject SecurityPostureQueryParam query) {
|
||||||
|
IntelligentRiskAnalysisResp resp = securityPostureService.queryIntelligentRiskAnalysis(query);
|
||||||
|
return AjaxResult.success(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取安全治理闭环数据")
|
||||||
|
@GetMapping("/governance-loop")
|
||||||
|
public AjaxResult governanceLoop(@Validated @ParameterObject SecurityPostureQueryParam query) {
|
||||||
|
SecurityGovernanceLoopResp resp = securityPostureService.querySecurityGovernanceLoop(query);
|
||||||
|
return AjaxResult.success(resp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.llm.guard.biz.domain.param;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Schema(description = "词库分页查询参数")
|
||||||
|
public class ContentAuditCorpusQueryParam extends PageQueryParam {
|
||||||
|
|
||||||
|
@Schema(description = "作用域编码:GLOBAL/APP/TENANT", example = "GLOBAL")
|
||||||
|
private String scopeCode = "GLOBAL";
|
||||||
|
|
||||||
|
@Schema(description = "词条内容(模糊匹配)", example = "内部架构")
|
||||||
|
private String corpusText;
|
||||||
|
|
||||||
|
@Schema(description = "词条标签(精确匹配)", example = "Confidential")
|
||||||
|
private String tag;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.llm.guard.biz.domain.resp;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "词库词条分页项")
|
||||||
|
public class ContentAuditCorpusResp {
|
||||||
|
|
||||||
|
@Schema(description = "词条ID")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Schema(description = "作用域编码")
|
||||||
|
private String scopeCode;
|
||||||
|
|
||||||
|
@Schema(description = "词条内容")
|
||||||
|
private String corpusText;
|
||||||
|
|
||||||
|
@Schema(description = "词条标签")
|
||||||
|
private String tag;
|
||||||
|
|
||||||
|
@Schema(description = "到期日期")
|
||||||
|
private String expireDate;
|
||||||
|
|
||||||
|
@Schema(description = "状态")
|
||||||
|
private String status;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.llm.guard.biz.domain.resp;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "智能风险分析中心响应")
|
||||||
|
public class IntelligentRiskAnalysisResp {
|
||||||
|
|
||||||
|
@Schema(description = "统计周期(小时)")
|
||||||
|
private Integer periodHours;
|
||||||
|
|
||||||
|
@Schema(description = "周期内活跃资产数")
|
||||||
|
private Long activeAssetCount;
|
||||||
|
|
||||||
|
@Schema(description = "高危规则统计")
|
||||||
|
private List<RuleStatItem> highRiskRules = new ArrayList<>();
|
||||||
|
|
||||||
|
@Schema(description = "风险返回码展示")
|
||||||
|
private List<RiskCodeItem> riskCodeDistribution = new ArrayList<>();
|
||||||
|
|
||||||
|
@Schema(description = "调用者统计")
|
||||||
|
private List<ApiConsumerItem> apiConsumers = new ArrayList<>();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class RuleStatItem {
|
||||||
|
private Integer rankNo;
|
||||||
|
private String ruleName;
|
||||||
|
private Long count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class RiskCodeItem {
|
||||||
|
private Integer riskCode;
|
||||||
|
private Long count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class ApiConsumerItem {
|
||||||
|
private Integer rankNo;
|
||||||
|
private String callerId;
|
||||||
|
private String consumerType;
|
||||||
|
private Long count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.llm.guard.biz.domain.resp;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "安全治理闭环响应")
|
||||||
|
public class SecurityGovernanceLoopResp {
|
||||||
|
|
||||||
|
@Schema(description = "统计周期(小时)")
|
||||||
|
private Integer periodHours;
|
||||||
|
|
||||||
|
@Schema(description = "治理卡片列表")
|
||||||
|
private List<GovernanceCard> cards = new ArrayList<>();
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class GovernanceCard {
|
||||||
|
@Schema(description = "治理域编码")
|
||||||
|
private String domainKey;
|
||||||
|
|
||||||
|
@Schema(description = "治理域标题")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "治理域副标题")
|
||||||
|
private String subtitle;
|
||||||
|
|
||||||
|
@Schema(description = "影响量")
|
||||||
|
private Long impact;
|
||||||
|
|
||||||
|
@Schema(description = "成功率(百分比)")
|
||||||
|
private Double successRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.llm.guard.biz.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.llm.guard.common.mybatisplus.entity.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@TableName("d_log_alert_event")
|
||||||
|
public class LogAlertEvent extends BaseEntity {
|
||||||
|
|
||||||
|
private String eventNo;
|
||||||
|
private String requestId;
|
||||||
|
private String traceId;
|
||||||
|
private String scopeCode;
|
||||||
|
private String moduleType;
|
||||||
|
private String eventType;
|
||||||
|
private String ruleType;
|
||||||
|
private String ruleId;
|
||||||
|
private String ruleCode;
|
||||||
|
private String switchKey;
|
||||||
|
private String severity;
|
||||||
|
private String actionTaken;
|
||||||
|
private String hitMessage;
|
||||||
|
private String requestPath;
|
||||||
|
private String requestMethod;
|
||||||
|
private String sourceIp;
|
||||||
|
private String callerId;
|
||||||
|
private Integer isStream;
|
||||||
|
private String alertStatus;
|
||||||
|
private LocalDateTime occurredAt;
|
||||||
|
private LocalDateTime partitionDate;
|
||||||
|
private Object hitDetailJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,289 @@
|
||||||
|
package com.llm.guard.biz.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.llm.guard.biz.entity.LogAlertEvent;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface LogAlertEventMapper extends BaseMapper<LogAlertEvent> {
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT COUNT(DISTINCT request_id)
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
Long countDistinctRequests(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT COUNT(DISTINCT request_id)
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
AND UPPER(action_taken) = 'BLOCK'
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
Long countDistinctBlockedRequests(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT COUNT(1)
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
Long countMatchEvents(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT AVG(ABS(EXTRACT(EPOCH FROM (COALESCE(create_time, occurred_at) - occurred_at))) * 1000)
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
Double avgLatencyMs(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT date_trunc('hour', occurred_at) bucket, COUNT(1) cnt
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
GROUP BY date_trunc('hour', occurred_at)
|
||||||
|
ORDER BY bucket
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
List<Map<String, Object>> selectTrendBuckets(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT module_type, event_type, rule_code, hit_message, COUNT(1) cnt
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
GROUP BY module_type, event_type, rule_code, hit_message
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
List<Map<String, Object>> selectThreatRows(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT source_ip, COUNT(1) cnt, SUM(CASE WHEN UPPER(action_taken) = 'BLOCK' THEN 1 ELSE 0 END) block_cnt
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
AND source_ip IS NOT NULL AND source_ip <> ''
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
GROUP BY source_ip
|
||||||
|
ORDER BY cnt DESC
|
||||||
|
LIMIT #{topN}
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
List<Map<String, Object>> selectSourceIpRank(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end,
|
||||||
|
@Param("topN") Integer topN);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT request_path, COUNT(1) cnt
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
AND request_path IS NOT NULL AND request_path <> ''
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
GROUP BY request_path
|
||||||
|
ORDER BY cnt DESC
|
||||||
|
LIMIT #{topN}
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
List<Map<String, Object>> selectPathRank(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end,
|
||||||
|
@Param("topN") Integer topN);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT hit_detail_json
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND hit_detail_json IS NOT NULL
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
List<Map<String, Object>> selectHitDetailRows(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT COUNT(DISTINCT asset_key) FROM (
|
||||||
|
SELECT caller_id AS asset_key
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND caller_id IS NOT NULL AND caller_id <> ''
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
UNION ALL
|
||||||
|
SELECT source_ip AS asset_key
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND source_ip IS NOT NULL AND source_ip <> ''
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
UNION ALL
|
||||||
|
SELECT request_path AS asset_key
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND request_path IS NOT NULL AND request_path <> ''
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
) t
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
Long countActiveAssets(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT rule_code, event_type, hit_message, COUNT(1) cnt
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
<if test="onlyHigh">
|
||||||
|
AND UPPER(COALESCE(severity, '')) IN ('HIGH','CRITICAL')
|
||||||
|
</if>
|
||||||
|
GROUP BY rule_code, event_type, hit_message
|
||||||
|
ORDER BY cnt DESC
|
||||||
|
LIMIT #{topN}
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
List<Map<String, Object>> selectTopRules(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end,
|
||||||
|
@Param("topN") Integer topN,
|
||||||
|
@Param("onlyHigh") boolean onlyHigh);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT scope_code, module_type, event_type, action_taken, COUNT(1) cnt
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
GROUP BY scope_code, module_type, event_type, action_taken
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
List<Map<String, Object>> selectRiskCodeRows(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT caller_id, COUNT(1) cnt
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND caller_id IS NOT NULL AND caller_id <> ''
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
GROUP BY caller_id
|
||||||
|
ORDER BY cnt DESC
|
||||||
|
LIMIT #{topN}
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
List<Map<String, Object>> selectCallerRank(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end,
|
||||||
|
@Param("topN") Integer topN);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT scope_code, reject_code
|
||||||
|
FROM d_content_audit_setting
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
List<Map<String, Object>> selectRejectCodeRows(@Param("scopeCode") String scopeCode);
|
||||||
|
|
||||||
|
@Select("""
|
||||||
|
<script>
|
||||||
|
SELECT module_type, event_type, rule_code, hit_message, action_taken, COUNT(1) cnt
|
||||||
|
FROM d_log_alert_event
|
||||||
|
WHERE is_deleted = 0
|
||||||
|
AND occurred_at BETWEEN #{start} AND #{end}
|
||||||
|
<if test="scopeCode != null and scopeCode != ''">
|
||||||
|
AND scope_code = #{scopeCode}
|
||||||
|
</if>
|
||||||
|
GROUP BY module_type, event_type, rule_code, hit_message, action_taken
|
||||||
|
</script>
|
||||||
|
""")
|
||||||
|
List<Map<String, Object>> selectGovernanceRows(@Param("scopeCode") String scopeCode,
|
||||||
|
@Param("start") LocalDateTime start,
|
||||||
|
@Param("end") LocalDateTime end);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -3,11 +3,13 @@ package com.llm.guard.biz.service;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.llm.guard.biz.domain.param.ContentAuditCorpusParam;
|
import com.llm.guard.biz.domain.param.ContentAuditCorpusParam;
|
||||||
|
import com.llm.guard.biz.domain.param.ContentAuditCorpusQueryParam;
|
||||||
import com.llm.guard.biz.domain.param.ContentAuditPageQueryParam;
|
import com.llm.guard.biz.domain.param.ContentAuditPageQueryParam;
|
||||||
import com.llm.guard.biz.domain.param.ContentAuditPolicyCreateParam;
|
import com.llm.guard.biz.domain.param.ContentAuditPolicyCreateParam;
|
||||||
import com.llm.guard.biz.domain.param.ContentAuditRecallConfigParam;
|
import com.llm.guard.biz.domain.param.ContentAuditRecallConfigParam;
|
||||||
import com.llm.guard.biz.domain.param.ContentAuditRejectConfigParam;
|
import com.llm.guard.biz.domain.param.ContentAuditRejectConfigParam;
|
||||||
import com.llm.guard.biz.domain.param.ContentAuditScopeConfigParam;
|
import com.llm.guard.biz.domain.param.ContentAuditScopeConfigParam;
|
||||||
|
import com.llm.guard.biz.domain.resp.ContentAuditCorpusResp;
|
||||||
import com.llm.guard.biz.domain.resp.ContentAuditPageResp;
|
import com.llm.guard.biz.domain.resp.ContentAuditPageResp;
|
||||||
import com.llm.guard.biz.domain.resp.ContentPolicyResp;
|
import com.llm.guard.biz.domain.resp.ContentPolicyResp;
|
||||||
import com.llm.guard.biz.domain.resp.PageResp;
|
import com.llm.guard.biz.domain.resp.PageResp;
|
||||||
|
|
@ -43,6 +45,59 @@ public class ContentAuditPageService {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PageResp<ContentAuditCorpusResp> corpusPage(ContentAuditCorpusQueryParam query) {
|
||||||
|
ContentAuditCorpusQueryParam q = normalizeCorpusQuery(query);
|
||||||
|
StringBuilder fromWhere = new StringBuilder(" FROM d_content_corpus WHERE is_deleted = 0 AND scope_code = ?");
|
||||||
|
List<Object> args = new ArrayList<>();
|
||||||
|
args.add(q.getScopeCode());
|
||||||
|
if (StringUtils.hasText(q.getCorpusText())) {
|
||||||
|
fromWhere.append(" AND corpus_text LIKE ?");
|
||||||
|
args.add("%" + q.getCorpusText().trim() + "%");
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(q.getTag())) {
|
||||||
|
fromWhere.append(" AND tag = ?");
|
||||||
|
args.add(q.getTag().trim());
|
||||||
|
}
|
||||||
|
Long total = jdbcTemplate.queryForObject("SELECT COUNT(1)" + fromWhere, args.toArray(), Long.class);
|
||||||
|
PageResp<ContentAuditCorpusResp> resp = new PageResp<>();
|
||||||
|
Integer pageNum = q.getPageNum();
|
||||||
|
resp.setPageSize(q.getPageSize());
|
||||||
|
resp.setTotal(total == null ? 0L : total);
|
||||||
|
long pages = (resp.getTotal() + q.getPageSize() - 1) / q.getPageSize();
|
||||||
|
resp.setPages(pages);
|
||||||
|
if (pages > 0 && pageNum > pages) {
|
||||||
|
pageNum = (int) pages;
|
||||||
|
}
|
||||||
|
resp.setPageNum(pageNum);
|
||||||
|
if (resp.getTotal() <= 0L) {
|
||||||
|
resp.setItems(List.of());
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
int offset = (pageNum - 1) * q.getPageSize();
|
||||||
|
List<Object> pageArgs = new ArrayList<>(args);
|
||||||
|
pageArgs.add(q.getPageSize());
|
||||||
|
pageArgs.add(offset);
|
||||||
|
List<Map<String, Object>> rows = jdbcTemplate.queryForList(
|
||||||
|
"SELECT id, scope_code, corpus_text, tag, expire_date, status"
|
||||||
|
+ fromWhere
|
||||||
|
+ " ORDER BY update_time DESC LIMIT ? OFFSET ?",
|
||||||
|
pageArgs.toArray()
|
||||||
|
);
|
||||||
|
List<ContentAuditCorpusResp> items = new ArrayList<>();
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
ContentAuditCorpusResp item = new ContentAuditCorpusResp();
|
||||||
|
item.setId(str(row.get("id")));
|
||||||
|
item.setScopeCode(str(row.get("scope_code")));
|
||||||
|
item.setCorpusText(str(row.get("corpus_text")));
|
||||||
|
item.setTag(str(row.get("tag")));
|
||||||
|
item.setExpireDate(str(row.get("expire_date")));
|
||||||
|
item.setStatus(str(row.get("status")));
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
resp.setItems(items);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean updateScopeConfig(ContentAuditScopeConfigParam param) {
|
public boolean updateScopeConfig(ContentAuditScopeConfigParam param) {
|
||||||
if (param == null) {
|
if (param == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -399,6 +454,22 @@ public class ContentAuditPageService {
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ContentAuditCorpusQueryParam normalizeCorpusQuery(ContentAuditCorpusQueryParam query) {
|
||||||
|
ContentAuditCorpusQueryParam q = query == null ? new ContentAuditCorpusQueryParam() : query;
|
||||||
|
if (!StringUtils.hasText(q.getScopeCode())) {
|
||||||
|
q.setScopeCode("GLOBAL");
|
||||||
|
} else {
|
||||||
|
q.setScopeCode(q.getScopeCode().trim().toUpperCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
if (q.getPageNum() == null || q.getPageNum() < 1) {
|
||||||
|
q.setPageNum(1);
|
||||||
|
}
|
||||||
|
if (q.getPageSize() == null || q.getPageSize() < 1) {
|
||||||
|
q.setPageSize(10);
|
||||||
|
}
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
private String defaultScope(String scopeCode) {
|
private String defaultScope(String scopeCode) {
|
||||||
return StringUtils.hasText(scopeCode) ? scopeCode : "GLOBAL";
|
return StringUtils.hasText(scopeCode) ? scopeCode : "GLOBAL";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@ package com.llm.guard.biz.service;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.llm.guard.biz.domain.resp.IntelligentRiskAnalysisResp;
|
||||||
|
import com.llm.guard.biz.domain.resp.SecurityGovernanceLoopResp;
|
||||||
import com.llm.guard.biz.domain.param.SecurityPostureQueryParam;
|
import com.llm.guard.biz.domain.param.SecurityPostureQueryParam;
|
||||||
import com.llm.guard.biz.domain.resp.SecurityPostureResp;
|
import com.llm.guard.biz.domain.resp.SecurityPostureResp;
|
||||||
|
import com.llm.guard.biz.mapper.LogAlertEventMapper;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
@ -27,7 +29,7 @@ public class SecurityPostureService {
|
||||||
private static final String METRIC_COUNT = "COUNT";
|
private static final String METRIC_COUNT = "COUNT";
|
||||||
private static final String METRIC_TOKEN = "TOKEN";
|
private static final String METRIC_TOKEN = "TOKEN";
|
||||||
|
|
||||||
private final JdbcTemplate jdbcTemplate;
|
private final LogAlertEventMapper logAlertEventMapper;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
public SecurityPostureResp queryPosture(SecurityPostureQueryParam query) {
|
public SecurityPostureResp queryPosture(SecurityPostureQueryParam query) {
|
||||||
|
|
@ -45,6 +47,59 @@ public class SecurityPostureService {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IntelligentRiskAnalysisResp queryIntelligentRiskAnalysis(SecurityPostureQueryParam query) {
|
||||||
|
SecurityPostureQueryParam q = normalize(query);
|
||||||
|
LocalDateTime end = LocalDateTime.now();
|
||||||
|
LocalDateTime start = end.minusHours(q.getHours());
|
||||||
|
|
||||||
|
IntelligentRiskAnalysisResp resp = new IntelligentRiskAnalysisResp();
|
||||||
|
resp.setPeriodHours(q.getHours());
|
||||||
|
resp.setActiveAssetCount(countActiveAssets(q, start, end));
|
||||||
|
resp.setHighRiskRules(buildHighRiskRules(q, start, end));
|
||||||
|
resp.setRiskCodeDistribution(buildRiskCodeDistribution(q, start, end));
|
||||||
|
resp.setApiConsumers(buildApiConsumerRank(q, start, end));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SecurityGovernanceLoopResp querySecurityGovernanceLoop(SecurityPostureQueryParam query) {
|
||||||
|
SecurityPostureQueryParam q = normalize(query);
|
||||||
|
LocalDateTime end = LocalDateTime.now();
|
||||||
|
LocalDateTime start = end.minusHours(q.getHours());
|
||||||
|
|
||||||
|
List<Map<String, Object>> rows = logAlertEventMapper.selectGovernanceRows(q.getScopeCode(), start, end);
|
||||||
|
|
||||||
|
Map<String, long[]> statByDomain = new LinkedHashMap<>();
|
||||||
|
statByDomain.put("INJECTION", new long[]{0L, 0L});
|
||||||
|
statByDomain.put("PROMPT", new long[]{0L, 0L});
|
||||||
|
statByDomain.put("DDOS", new long[]{0L, 0L});
|
||||||
|
statByDomain.put("PROTOCOL", new long[]{0L, 0L});
|
||||||
|
statByDomain.put("VULN", new long[]{0L, 0L});
|
||||||
|
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
String domain = mapGovernanceDomain(
|
||||||
|
str(row.get("module_type")),
|
||||||
|
str(row.get("event_type")),
|
||||||
|
str(row.get("rule_code")),
|
||||||
|
str(row.get("hit_message"))
|
||||||
|
);
|
||||||
|
long cnt = toLong(row.get("cnt"));
|
||||||
|
long[] stat = statByDomain.computeIfAbsent(domain, k -> new long[]{0L, 0L});
|
||||||
|
stat[0] += cnt;
|
||||||
|
if ("BLOCK".equalsIgnoreCase(str(row.get("action_taken")))) {
|
||||||
|
stat[1] += cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SecurityGovernanceLoopResp resp = new SecurityGovernanceLoopResp();
|
||||||
|
resp.setPeriodHours(q.getHours());
|
||||||
|
resp.getCards().add(buildGovernanceCard("INJECTION", "注入攻击防护", "SQL / 命令 / 代码注入", statByDomain.get("INJECTION")));
|
||||||
|
resp.getCards().add(buildGovernanceCard("PROMPT", "提示词安全", "诱导越狱 / 角色扮演", statByDomain.get("PROMPT")));
|
||||||
|
resp.getCards().add(buildGovernanceCard("DDOS", "DDoS / 滥用", "TOKEN 消耗 / 频率控制", statByDomain.get("DDOS")));
|
||||||
|
resp.getCards().add(buildGovernanceCard("PROTOCOL", "协议合规", "HEADER 检查 / 字段篡改", statByDomain.get("PROTOCOL")));
|
||||||
|
resp.getCards().add(buildGovernanceCard("VULN", "漏洞与溢出", "XSS / 缓冲区溢出防护", statByDomain.get("VULN")));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
private SecurityPostureResp.Overview buildOverview(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
private SecurityPostureResp.Overview buildOverview(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
SecurityPostureResp.Overview overview = new SecurityPostureResp.Overview();
|
SecurityPostureResp.Overview overview = new SecurityPostureResp.Overview();
|
||||||
|
|
||||||
|
|
@ -62,17 +117,7 @@ public class SecurityPostureService {
|
||||||
|
|
||||||
private List<SecurityPostureResp.TrendPoint> buildTrend(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
private List<SecurityPostureResp.TrendPoint> buildTrend(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
List<SecurityPostureResp.TrendPoint> trend = new ArrayList<>();
|
List<SecurityPostureResp.TrendPoint> trend = new ArrayList<>();
|
||||||
List<Map<String, Object>> rows = queryForListByScope(
|
List<Map<String, Object>> rows = logAlertEventMapper.selectTrendBuckets(q.getScopeCode(), start, end);
|
||||||
q.getScopeCode(),
|
|
||||||
"SELECT date_trunc('hour', occurred_at) bucket, COUNT(1) cnt " +
|
|
||||||
"FROM d_log_alert_event WHERE occurred_at BETWEEN ? AND ? AND is_deleted = 0 " +
|
|
||||||
"GROUP BY date_trunc('hour', occurred_at) ORDER BY bucket",
|
|
||||||
"SELECT date_trunc('hour', occurred_at) bucket, COUNT(1) cnt " +
|
|
||||||
"FROM d_log_alert_event WHERE scope_code = ? AND occurred_at BETWEEN ? AND ? AND is_deleted = 0 " +
|
|
||||||
"GROUP BY date_trunc('hour', occurred_at) ORDER BY bucket",
|
|
||||||
start,
|
|
||||||
end
|
|
||||||
);
|
|
||||||
Map<String, Long> bucketMap = new LinkedHashMap<>();
|
Map<String, Long> bucketMap = new LinkedHashMap<>();
|
||||||
for (Map<String, Object> row : rows) {
|
for (Map<String, Object> row : rows) {
|
||||||
LocalDateTime bucket = toDateTime(row.get("bucket"));
|
LocalDateTime bucket = toDateTime(row.get("bucket"));
|
||||||
|
|
@ -95,17 +140,7 @@ public class SecurityPostureService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SecurityPostureResp.ThreatSlice> buildThreatDistribution(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
private List<SecurityPostureResp.ThreatSlice> buildThreatDistribution(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
List<Map<String, Object>> rows = queryForListByScope(
|
List<Map<String, Object>> rows = logAlertEventMapper.selectThreatRows(q.getScopeCode(), start, end);
|
||||||
q.getScopeCode(),
|
|
||||||
"SELECT module_type, event_type, rule_code, hit_message, COUNT(1) cnt " +
|
|
||||||
"FROM d_log_alert_event WHERE occurred_at BETWEEN ? AND ? AND is_deleted = 0 " +
|
|
||||||
"GROUP BY module_type, event_type, rule_code, hit_message",
|
|
||||||
"SELECT module_type, event_type, rule_code, hit_message, COUNT(1) cnt " +
|
|
||||||
"FROM d_log_alert_event WHERE scope_code = ? AND occurred_at BETWEEN ? AND ? AND is_deleted = 0 " +
|
|
||||||
"GROUP BY module_type, event_type, rule_code, hit_message",
|
|
||||||
start,
|
|
||||||
end
|
|
||||||
);
|
|
||||||
Map<String, Long> dist = new LinkedHashMap<>();
|
Map<String, Long> dist = new LinkedHashMap<>();
|
||||||
dist.put("注入攻击", 0L);
|
dist.put("注入攻击", 0L);
|
||||||
dist.put("提示词注入", 0L);
|
dist.put("提示词注入", 0L);
|
||||||
|
|
@ -138,18 +173,7 @@ public class SecurityPostureService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SecurityPostureResp.IpRankItem> buildSourceIpRank(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
private List<SecurityPostureResp.IpRankItem> buildSourceIpRank(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
List<Map<String, Object>> rows = queryForListByScope(
|
List<Map<String, Object>> rows = logAlertEventMapper.selectSourceIpRank(q.getScopeCode(), start, end, q.getTopN());
|
||||||
q.getScopeCode(),
|
|
||||||
"SELECT source_ip, COUNT(1) cnt, SUM(CASE WHEN UPPER(action_taken) = 'BLOCK' THEN 1 ELSE 0 END) block_cnt " +
|
|
||||||
"FROM d_log_alert_event WHERE occurred_at BETWEEN ? AND ? AND is_deleted = 0 AND source_ip IS NOT NULL AND source_ip <> '' " +
|
|
||||||
"GROUP BY source_ip ORDER BY cnt DESC LIMIT ?",
|
|
||||||
"SELECT source_ip, COUNT(1) cnt, SUM(CASE WHEN UPPER(action_taken) = 'BLOCK' THEN 1 ELSE 0 END) block_cnt " +
|
|
||||||
"FROM d_log_alert_event WHERE scope_code = ? AND occurred_at BETWEEN ? AND ? AND is_deleted = 0 AND source_ip IS NOT NULL AND source_ip <> '' " +
|
|
||||||
"GROUP BY source_ip ORDER BY cnt DESC LIMIT ?",
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
q.getTopN()
|
|
||||||
);
|
|
||||||
List<SecurityPostureResp.IpRankItem> list = new ArrayList<>();
|
List<SecurityPostureResp.IpRankItem> list = new ArrayList<>();
|
||||||
for (Map<String, Object> row : rows) {
|
for (Map<String, Object> row : rows) {
|
||||||
long cnt = toLong(row.get("cnt"));
|
long cnt = toLong(row.get("cnt"));
|
||||||
|
|
@ -164,18 +188,7 @@ public class SecurityPostureService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SecurityPostureResp.PathRankItem> buildInterfaceRank(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
private List<SecurityPostureResp.PathRankItem> buildInterfaceRank(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
List<Map<String, Object>> rows = queryForListByScope(
|
List<Map<String, Object>> rows = logAlertEventMapper.selectPathRank(q.getScopeCode(), start, end, q.getTopN());
|
||||||
q.getScopeCode(),
|
|
||||||
"SELECT request_path, COUNT(1) cnt FROM d_log_alert_event " +
|
|
||||||
"WHERE occurred_at BETWEEN ? AND ? AND is_deleted = 0 AND request_path IS NOT NULL AND request_path <> '' " +
|
|
||||||
"GROUP BY request_path ORDER BY cnt DESC LIMIT ?",
|
|
||||||
"SELECT request_path, COUNT(1) cnt FROM d_log_alert_event " +
|
|
||||||
"WHERE scope_code = ? AND occurred_at BETWEEN ? AND ? AND is_deleted = 0 AND request_path IS NOT NULL AND request_path <> '' " +
|
|
||||||
"GROUP BY request_path ORDER BY cnt DESC LIMIT ?",
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
q.getTopN()
|
|
||||||
);
|
|
||||||
List<SecurityPostureResp.PathRankItem> list = new ArrayList<>();
|
List<SecurityPostureResp.PathRankItem> list = new ArrayList<>();
|
||||||
for (Map<String, Object> row : rows) {
|
for (Map<String, Object> row : rows) {
|
||||||
SecurityPostureResp.PathRankItem item = new SecurityPostureResp.PathRankItem();
|
SecurityPostureResp.PathRankItem item = new SecurityPostureResp.PathRankItem();
|
||||||
|
|
@ -187,13 +200,7 @@ public class SecurityPostureService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<SecurityPostureResp.ModelRankItem> buildModelRank(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
private List<SecurityPostureResp.ModelRankItem> buildModelRank(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
List<Map<String, Object>> rows = queryForListByScope(
|
List<Map<String, Object>> rows = logAlertEventMapper.selectHitDetailRows(q.getScopeCode(), start, end);
|
||||||
q.getScopeCode(),
|
|
||||||
"SELECT hit_detail_json FROM d_log_alert_event WHERE occurred_at BETWEEN ? AND ? AND is_deleted = 0 AND hit_detail_json IS NOT NULL",
|
|
||||||
"SELECT hit_detail_json FROM d_log_alert_event WHERE scope_code = ? AND occurred_at BETWEEN ? AND ? AND is_deleted = 0 AND hit_detail_json IS NOT NULL",
|
|
||||||
start,
|
|
||||||
end
|
|
||||||
);
|
|
||||||
Map<String, long[]> agg = new LinkedHashMap<>();
|
Map<String, long[]> agg = new LinkedHashMap<>();
|
||||||
for (Map<String, Object> row : rows) {
|
for (Map<String, Object> row : rows) {
|
||||||
String detail = str(row.get("hit_detail_json"));
|
String detail = str(row.get("hit_detail_json"));
|
||||||
|
|
@ -222,6 +229,70 @@ public class SecurityPostureService {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Long countActiveAssets(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
|
return logAlertEventMapper.countActiveAssets(q.getScopeCode(), start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<IntelligentRiskAnalysisResp.RuleStatItem> buildHighRiskRules(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
|
List<Map<String, Object>> rows = logAlertEventMapper.selectTopRules(q.getScopeCode(), start, end, q.getTopN(), true);
|
||||||
|
if (rows.isEmpty()) {
|
||||||
|
rows = logAlertEventMapper.selectTopRules(q.getScopeCode(), start, end, q.getTopN(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IntelligentRiskAnalysisResp.RuleStatItem> list = new ArrayList<>();
|
||||||
|
int idx = 1;
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
IntelligentRiskAnalysisResp.RuleStatItem item = new IntelligentRiskAnalysisResp.RuleStatItem();
|
||||||
|
item.setRankNo(idx++);
|
||||||
|
item.setRuleName(resolveRuleName(str(row.get("rule_code")), str(row.get("event_type")), str(row.get("hit_message"))));
|
||||||
|
item.setCount(toLong(row.get("cnt")));
|
||||||
|
list.add(item);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<IntelligentRiskAnalysisResp.RiskCodeItem> buildRiskCodeDistribution(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
|
Map<String, Integer> rejectCodeByScope = loadRejectCodeByScope(q.getScopeCode());
|
||||||
|
List<Map<String, Object>> rows = logAlertEventMapper.selectRiskCodeRows(q.getScopeCode(), start, end);
|
||||||
|
Map<Integer, Long> dist = new LinkedHashMap<>();
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
int code = mapRiskCode(
|
||||||
|
str(row.get("scope_code")),
|
||||||
|
str(row.get("module_type")),
|
||||||
|
str(row.get("event_type")),
|
||||||
|
str(row.get("action_taken")),
|
||||||
|
rejectCodeByScope
|
||||||
|
);
|
||||||
|
dist.put(code, dist.getOrDefault(code, 0L) + toLong(row.get("cnt")));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IntelligentRiskAnalysisResp.RiskCodeItem> list = new ArrayList<>();
|
||||||
|
for (Map.Entry<Integer, Long> entry : dist.entrySet()) {
|
||||||
|
IntelligentRiskAnalysisResp.RiskCodeItem item = new IntelligentRiskAnalysisResp.RiskCodeItem();
|
||||||
|
item.setRiskCode(entry.getKey());
|
||||||
|
item.setCount(entry.getValue());
|
||||||
|
list.add(item);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<IntelligentRiskAnalysisResp.ApiConsumerItem> buildApiConsumerRank(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
|
List<Map<String, Object>> rows = logAlertEventMapper.selectCallerRank(q.getScopeCode(), start, end, q.getTopN());
|
||||||
|
|
||||||
|
List<IntelligentRiskAnalysisResp.ApiConsumerItem> list = new ArrayList<>();
|
||||||
|
int idx = 1;
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
String callerId = str(row.get("caller_id"));
|
||||||
|
IntelligentRiskAnalysisResp.ApiConsumerItem item = new IntelligentRiskAnalysisResp.ApiConsumerItem();
|
||||||
|
item.setRankNo(idx++);
|
||||||
|
item.setCallerId(callerId);
|
||||||
|
item.setConsumerType(classifyConsumerType(callerId));
|
||||||
|
item.setCount(toLong(row.get("cnt")));
|
||||||
|
list.add(item);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
private long metricValue(long[] stat, String metric) {
|
private long metricValue(long[] stat, String metric) {
|
||||||
return METRIC_TOKEN.equals(metric) ? stat[1] : stat[0];
|
return METRIC_TOKEN.equals(metric) ? stat[1] : stat[0];
|
||||||
}
|
}
|
||||||
|
|
@ -235,49 +306,19 @@ public class SecurityPostureService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long countDistinctRequests(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
private Long countDistinctRequests(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
return queryForObjectByScope(
|
return logAlertEventMapper.countDistinctRequests(q.getScopeCode(), start, end);
|
||||||
q.getScopeCode(),
|
|
||||||
Long.class,
|
|
||||||
"SELECT COUNT(DISTINCT request_id) FROM d_log_alert_event WHERE occurred_at BETWEEN ? AND ? AND is_deleted = 0",
|
|
||||||
"SELECT COUNT(DISTINCT request_id) FROM d_log_alert_event WHERE scope_code = ? AND occurred_at BETWEEN ? AND ? AND is_deleted = 0",
|
|
||||||
start,
|
|
||||||
end
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long countDistinctBlockedRequests(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
private Long countDistinctBlockedRequests(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
return queryForObjectByScope(
|
return logAlertEventMapper.countDistinctBlockedRequests(q.getScopeCode(), start, end);
|
||||||
q.getScopeCode(),
|
|
||||||
Long.class,
|
|
||||||
"SELECT COUNT(DISTINCT request_id) FROM d_log_alert_event WHERE occurred_at BETWEEN ? AND ? AND is_deleted = 0 AND UPPER(action_taken) = 'BLOCK'",
|
|
||||||
"SELECT COUNT(DISTINCT request_id) FROM d_log_alert_event WHERE scope_code = ? AND occurred_at BETWEEN ? AND ? AND is_deleted = 0 AND UPPER(action_taken) = 'BLOCK'",
|
|
||||||
start,
|
|
||||||
end
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long countMatchEvents(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
private Long countMatchEvents(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
return queryForObjectByScope(
|
return logAlertEventMapper.countMatchEvents(q.getScopeCode(), start, end);
|
||||||
q.getScopeCode(),
|
|
||||||
Long.class,
|
|
||||||
"SELECT COUNT(1) FROM d_log_alert_event WHERE occurred_at BETWEEN ? AND ? AND is_deleted = 0",
|
|
||||||
"SELECT COUNT(1) FROM d_log_alert_event WHERE scope_code = ? AND occurred_at BETWEEN ? AND ? AND is_deleted = 0",
|
|
||||||
start,
|
|
||||||
end
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long queryAvgLatencyMs(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
private Long queryAvgLatencyMs(SecurityPostureQueryParam q, LocalDateTime start, LocalDateTime end) {
|
||||||
Double avg = queryForObjectByScope(
|
Double avg = logAlertEventMapper.avgLatencyMs(q.getScopeCode(), start, end);
|
||||||
q.getScopeCode(),
|
|
||||||
Double.class,
|
|
||||||
"SELECT AVG(ABS(EXTRACT(EPOCH FROM (COALESCE(create_time, occurred_at) - occurred_at))) * 1000) " +
|
|
||||||
"FROM d_log_alert_event WHERE occurred_at BETWEEN ? AND ? AND is_deleted = 0",
|
|
||||||
"SELECT AVG(ABS(EXTRACT(EPOCH FROM (COALESCE(create_time, occurred_at) - occurred_at))) * 1000) " +
|
|
||||||
"FROM d_log_alert_event WHERE scope_code = ? AND occurred_at BETWEEN ? AND ? AND is_deleted = 0",
|
|
||||||
start,
|
|
||||||
end
|
|
||||||
);
|
|
||||||
return avg == null ? 0L : Math.round(avg);
|
return avg == null ? 0L : Math.round(avg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,6 +332,105 @@ public class SecurityPostureService {
|
||||||
return "一般";
|
return "一般";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String resolveRuleName(String ruleCode, String eventType, String hitMessage) {
|
||||||
|
if (ruleCode != null && !ruleCode.isBlank()) {
|
||||||
|
return ruleCode;
|
||||||
|
}
|
||||||
|
if (hitMessage != null && !hitMessage.isBlank()) {
|
||||||
|
return hitMessage;
|
||||||
|
}
|
||||||
|
return eventType == null ? "UNKNOWN_RULE" : eventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int mapRiskCode(String scopeCode,
|
||||||
|
String moduleType,
|
||||||
|
String eventType,
|
||||||
|
String actionTaken,
|
||||||
|
Map<String, Integer> rejectCodeByScope) {
|
||||||
|
String m = upper(moduleType);
|
||||||
|
String e = upper(eventType);
|
||||||
|
String a = upper(actionTaken);
|
||||||
|
if (!"BLOCK".equals(a)) {
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
if ("CONTENT".equals(m)) {
|
||||||
|
return rejectCodeByScope.getOrDefault(scopeCode, 403);
|
||||||
|
}
|
||||||
|
if ("ACL".equals(m) && "ACL_IP_WHITELIST".equals(e)) {
|
||||||
|
return 401;
|
||||||
|
}
|
||||||
|
if ("ACL".equals(m) || "ATTACK".equals(m)) {
|
||||||
|
return 429;
|
||||||
|
}
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Integer> loadRejectCodeByScope(String scopeCode) {
|
||||||
|
List<Map<String, Object>> rows = logAlertEventMapper.selectRejectCodeRows(scopeCode);
|
||||||
|
Map<String, Integer> map = new LinkedHashMap<>();
|
||||||
|
for (Map<String, Object> row : rows) {
|
||||||
|
String key = str(row.get("scope_code"));
|
||||||
|
Integer code = toInt(row.get("reject_code"), 403);
|
||||||
|
if (key != null && !key.isBlank()) {
|
||||||
|
map.put(key, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String classifyConsumerType(String callerId) {
|
||||||
|
String val = callerId == null ? "" : callerId.trim().toLowerCase(Locale.ROOT);
|
||||||
|
if (val.contains("admin") || val.contains("internal") || val.contains("sys")) {
|
||||||
|
return "Internal";
|
||||||
|
}
|
||||||
|
if (val.contains("public") || val.contains("frontend") || val.contains("web")) {
|
||||||
|
return "Public";
|
||||||
|
}
|
||||||
|
if (val.contains("api") || val.contains("service")) {
|
||||||
|
return "API";
|
||||||
|
}
|
||||||
|
return "External";
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecurityGovernanceLoopResp.GovernanceCard buildGovernanceCard(String domainKey,
|
||||||
|
String title,
|
||||||
|
String subtitle,
|
||||||
|
long[] stat) {
|
||||||
|
long impact = stat == null ? 0L : stat[0];
|
||||||
|
long success = stat == null ? 0L : stat[1];
|
||||||
|
SecurityGovernanceLoopResp.GovernanceCard card = new SecurityGovernanceLoopResp.GovernanceCard();
|
||||||
|
card.setDomainKey(domainKey);
|
||||||
|
card.setTitle(title);
|
||||||
|
card.setSubtitle(subtitle);
|
||||||
|
card.setImpact(impact);
|
||||||
|
card.setSuccessRate(ratio(success, impact == 0 ? 1 : impact));
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String mapGovernanceDomain(String moduleType, String eventType, String ruleCode, String hitMessage) {
|
||||||
|
String m = upper(moduleType);
|
||||||
|
String e = upper(eventType);
|
||||||
|
String r = upper(ruleCode);
|
||||||
|
String msg = upper(hitMessage);
|
||||||
|
|
||||||
|
if ("ATTACK".equals(m)) {
|
||||||
|
if (r.contains("PROMPT") || r.contains("JAILBREAK") || msg.contains("提示词") || msg.contains("越狱")) {
|
||||||
|
return "PROMPT";
|
||||||
|
}
|
||||||
|
return "INJECTION";
|
||||||
|
}
|
||||||
|
if ("ACL".equals(m)) {
|
||||||
|
if (e.contains("IP_") || r.contains("RATE") || r.contains("TOKEN") || msg.contains("频率") || msg.contains("滥用") || msg.contains("DDOS")) {
|
||||||
|
return "DDOS";
|
||||||
|
}
|
||||||
|
return "PROTOCOL";
|
||||||
|
}
|
||||||
|
if ("CONTENT".equals(m)) {
|
||||||
|
return "VULN";
|
||||||
|
}
|
||||||
|
return "PROTOCOL";
|
||||||
|
}
|
||||||
|
|
||||||
private String mapThreatType(String moduleType, String eventType, String ruleCode, String hitMessage) {
|
private String mapThreatType(String moduleType, String eventType, String ruleCode, String hitMessage) {
|
||||||
String m = upper(moduleType);
|
String m = upper(moduleType);
|
||||||
String e = upper(eventType);
|
String e = upper(eventType);
|
||||||
|
|
@ -314,29 +454,6 @@ public class SecurityPostureService {
|
||||||
return "协议漏洞";
|
return "协议漏洞";
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T queryForObjectByScope(String scopeCode, Class<T> cls, String noScopeSql, String withScopeSql, Object... params) {
|
|
||||||
if (scopeCode == null || scopeCode.isBlank()) {
|
|
||||||
return jdbcTemplate.queryForObject(noScopeSql, cls, params);
|
|
||||||
}
|
|
||||||
Object[] args = prepend(scopeCode, params);
|
|
||||||
return jdbcTemplate.queryForObject(withScopeSql, cls, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Map<String, Object>> queryForListByScope(String scopeCode, String noScopeSql, String withScopeSql, Object... params) {
|
|
||||||
if (scopeCode == null || scopeCode.isBlank()) {
|
|
||||||
return jdbcTemplate.queryForList(noScopeSql, params);
|
|
||||||
}
|
|
||||||
Object[] args = prepend(scopeCode, params);
|
|
||||||
return jdbcTemplate.queryForList(withScopeSql, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Object[] prepend(Object first, Object[] tail) {
|
|
||||||
Object[] args = new Object[tail.length + 1];
|
|
||||||
args[0] = first;
|
|
||||||
System.arraycopy(tail, 0, args, 1, tail.length);
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
private SecurityPostureQueryParam normalize(SecurityPostureQueryParam query) {
|
private SecurityPostureQueryParam normalize(SecurityPostureQueryParam query) {
|
||||||
SecurityPostureQueryParam q = query == null ? new SecurityPostureQueryParam() : query;
|
SecurityPostureQueryParam q = query == null ? new SecurityPostureQueryParam() : query;
|
||||||
if (q.getHours() == null || q.getHours() <= 0) {
|
if (q.getHours() == null || q.getHours() <= 0) {
|
||||||
|
|
@ -441,4 +558,18 @@ public class SecurityPostureService {
|
||||||
return 0L;
|
return 0L;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Integer toInt(Object value, int defaultVal) {
|
||||||
|
if (value == null) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
if (value instanceof Number n) {
|
||||||
|
return n.intValue();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(String.valueOf(value));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return defaultVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue