mirror of
https://github.com/FunnyWolf/agentic-soc-platform.git
synced 2026-08-22 13:12:56 +02:00
update case interface
This commit is contained in:
@@ -21,7 +21,6 @@ Case is the core investigation object in ASP. One case can have one or more aler
|
||||
|
||||
- The user gives a case ID and wants to review, triage, or quickly summarize it.
|
||||
- The user wants to find cases by status, severity, confidence, verdict, correlation UID, title, or tags.
|
||||
- The user wants case discussion context.
|
||||
- The user wants to update case workflow fields or AI analysis fields.
|
||||
- The user wants to attach enrichment or structured analysis to a case.
|
||||
- The user wants to attach an external ticket record to a case.
|
||||
@@ -38,10 +37,9 @@ Case is the core investigation object in ASP. One case can have one or more aler
|
||||
|
||||
## Decision Flow
|
||||
|
||||
1. If the user provides a specific case ID or says "open", "show", "review", or "summarize" a case, call `list_cases(case_id=<id>, limit=1)`.
|
||||
2. If the user wants discussion history or analyst context, call `get_case_discussions` after retrieving the case.
|
||||
3. If the user wants to browse or compare cases, use `list_cases`.
|
||||
4. If the user wants to change status, verdict, severity, or AI fields, use `update_case`.
|
||||
1. If the user provides a specific case ID or says "open", "show", "review", or "summarize" a case, call `list_cases(case_id=<id>, limit=1)`. Discussions are included by default.
|
||||
2. If the user wants to browse or compare cases, use `list_cases`. Pass `include_discussions=False` if discussions are not needed.
|
||||
3. If the user wants to change status, verdict, severity, or AI fields, use `update_case`.
|
||||
5. If the user wants to update a case but did not provide a case ID, ask for the case ID.
|
||||
6. If the user gives multiple filters, apply only the ones ASP supports directly and state any unsupported filters explicitly.
|
||||
7. If the user wants to attach enrichment or structured analysis to the case, use the `asp-enrichment-en` skill.
|
||||
@@ -51,12 +49,11 @@ Case is the core investigation object in ASP. One case can have one or more aler
|
||||
|
||||
### Review One Case
|
||||
|
||||
1. If the user wants to review, analyze, or inspect case details, call `list_cases(case_id=<id>, limit=1, lazy_load=false)` to fetch the full related data, including alerts, enrichments, and tickets.
|
||||
2. If the user only needs the basic case information, call `list_cases(case_id=<id>, limit=1)`.
|
||||
1. If the user wants to review, analyze, or inspect case details, call `list_cases(case_id=<id>, limit=1, lazy_load=false)` to fetch the full related data, including alerts, enrichments, and tickets. Discussions are included by default.
|
||||
2. If the user only needs the basic case information, call `list_cases(case_id=<id>, limit=1, include_discussions=false)`.
|
||||
3. If the result is empty, state that the case was not found.
|
||||
4. If the user wants analyst context, call `get_case_discussions(case_id)`.
|
||||
5. Present only the parts most relevant to the user's request.
|
||||
6. Only emphasize missing or suspicious fields when they matter to the user's goal.
|
||||
4. Present only the parts most relevant to the user's request.
|
||||
5. Only emphasize missing or suspicious fields when they matter to the user's goal.
|
||||
|
||||
Preferred response structure:
|
||||
|
||||
@@ -105,9 +102,9 @@ Common update targets:
|
||||
- `verdict`
|
||||
- `severity_ai`
|
||||
- `confidence_ai`
|
||||
- `attack_stage_ai`
|
||||
- `comment_ai`
|
||||
- `summary_ai`
|
||||
- `verdict_ai`
|
||||
- `comment`
|
||||
- `summary`
|
||||
|
||||
Preferred response structure:
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ Case。
|
||||
|
||||
- 用户给出一个 case ID,希望查看、分诊或快速总结。
|
||||
- 用户希望按状态、严重级别、置信度、verdict、correlation UID、标题或标签查找 case。
|
||||
- 用户想查看 case 的讨论上下文。
|
||||
- 用户想更新 case 工作流字段或 AI 分析字段。
|
||||
- 用户想把 enrichment 或结构化分析附加到 case。
|
||||
- 用户想把外部 ticket 记录附加到 case。
|
||||
@@ -38,10 +37,9 @@ Case。
|
||||
|
||||
## 决策流程
|
||||
|
||||
1. 如果用户提供了具体 case ID,或说要“open”“show”“review”“summarize”某个 case,调用 `list_cases(case_id=<id>, limit=1)`。
|
||||
2. 如果用户想看讨论历史或分析上下文,在取回 case 后调用 `get_case_discussions`。
|
||||
3. 如果用户要查找、浏览或对比 case,使用 `list_cases`。
|
||||
4. 如果用户要修改 status、verdict、severity 或 AI 字段,使用 `update_case`。
|
||||
1. 如果用户提供了具体 case ID,或说要”open””show””review””summarize”某个 case,调用 `list_cases(case_id=<id>, limit=1)`。默认已包含讨论记录。
|
||||
2. 如果用户要查找、浏览或对比 case,使用 `list_cases`。不需要讨论时传 `include_discussions=False`。
|
||||
3. 如果用户要修改 status、verdict、severity 或 AI 字段,使用 `update_case`。
|
||||
5. 如果用户要更新 case 但没提供 case ID,询问 case ID。
|
||||
6. 如果用户给出了多个过滤条件,只应用 ASP 直接支持的部分,并明确说明不支持的过滤条件。
|
||||
7. 如果用户要把 enrichment 或结构化分析附加到 case,使用 `asp-enrichment-zh` skill。
|
||||
@@ -52,12 +50,11 @@ Case。
|
||||
### 审查单个 Case
|
||||
|
||||
1. 如果用户要求审查、分析或查看 case 详情,调用 `list_cases(case_id=<id>, limit=1, lazy_load=false)`
|
||||
获取完整关联数据(alerts、enrichments、tickets)。
|
||||
2. 如果只需要快速查看 case 基本信息,调用 `list_cases(case_id=<id>, limit=1)` 即可。
|
||||
获取完整关联数据(alerts、enrichments、tickets)。默认已包含讨论记录。
|
||||
2. 如果只需要快速查看 case 基本信息,调用 `list_cases(case_id=<id>, limit=1, include_discussions=false)` 即可。
|
||||
3. 如果结果为空,直接说明找不到该 case。
|
||||
4. 如果用户想看分析上下文,调用 `get_case_discussions(case_id)`。
|
||||
5. 只展示与用户请求最相关的部分。
|
||||
6. 只有在确实影响用户目标时,才强调缺失字段或可疑字段。
|
||||
4. 只展示与用户请求最相关的部分。
|
||||
5. 只有在确实影响用户目标时,才强调缺失字段或可疑字段。
|
||||
|
||||
首选回复结构:
|
||||
|
||||
@@ -106,9 +103,9 @@ Case。
|
||||
- `verdict`
|
||||
- `severity_ai`
|
||||
- `confidence_ai`
|
||||
- `attack_stage_ai`
|
||||
- `comment_ai`
|
||||
- `summary_ai`
|
||||
- `verdict_ai`
|
||||
- `comment`
|
||||
- `summary`
|
||||
|
||||
首选回复结构:
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
name: asp-ticket-en
|
||||
description: 'Sync external tickets into ASP, attach tickets to cases, list synced tickets, or update existing ticket records.'
|
||||
argument-hint: 'list tickets [filters] | create ticket <uid> | attach ticket to case <case_id> | update ticket <ticket_id> <fields>'
|
||||
description: 'Sync external tickets into ASP, create tickets linked to cases, list synced tickets, or update existing ticket records.'
|
||||
argument-hint: 'list tickets [filters] | create ticket <case_id> <uid> | update ticket <ticket_id> <fields>'
|
||||
compatibility: connect to asp mcp server
|
||||
metadata:
|
||||
author: Funnywolf
|
||||
version: 0.1.0
|
||||
version: 0.2.0
|
||||
mcp-server: asp
|
||||
category: cyber security
|
||||
tags: [ ticket, case, sync, workflow ]
|
||||
@@ -18,25 +18,22 @@ Use this skill when the user needs to sync external tickets on ASP.
|
||||
|
||||
## When to Use
|
||||
|
||||
- The user wants to create a synced external ticket record.
|
||||
- The user wants to attach a ticket to a case.
|
||||
- The user wants to create a synced external ticket record linked to a case.
|
||||
- The user wants to list synced tickets by status, type, or external UID.
|
||||
- The user wants to update synced ticket fields.
|
||||
|
||||
## Operating Rules
|
||||
|
||||
- Treat tickets as synced external workflow records, not as the platform's main investigation object.
|
||||
- Use `create_ticket` to create the synced ticket record.
|
||||
- Use `attach_ticket_to_case` to link an existing ticket record to a case after you already have the ticket row_id.
|
||||
- Use `create_ticket` to create the synced ticket record and attach it to a case in one step.
|
||||
- Use `list_tickets` for browsing and lookup.
|
||||
- Use `update_ticket` only for fields the user explicitly wants changed.
|
||||
|
||||
## Decision Flow
|
||||
|
||||
1. If the user wants to create a synced ticket record, call `create_ticket`.
|
||||
2. If the user wants to attach a ticket to a case, create the ticket when needed or retrieve the existing ticket row_id first, then call `attach_ticket_to_case`.
|
||||
3. If the user wants to browse or compare synced tickets, call `list_tickets`.
|
||||
4. If the user wants to update synced ticket fields, call `update_ticket`.
|
||||
1. If the user wants to create a synced ticket record linked to a case, call `create_ticket(case_id=..., uid=...)`.
|
||||
2. If the user wants to browse or compare synced tickets, call `list_tickets`.
|
||||
3. If the user wants to update synced ticket fields, call `update_ticket`.
|
||||
|
||||
## SOP
|
||||
|
||||
@@ -45,7 +42,7 @@ Use this skill when the user needs to sync external tickets on ASP.
|
||||
1. Extract the narrowest useful filters from the request.
|
||||
2. Call `list_tickets`.
|
||||
3. Parse the returned JSON strings.
|
||||
4. Present a compact workflow-oriented view, and surface the ticket row_id when the user will likely attach or reuse the ticket next.
|
||||
4. Present a compact workflow-oriented view, and surface the ticket row_id when the user will likely reuse the ticket next.
|
||||
|
||||
Preferred response structure:
|
||||
|
||||
@@ -56,17 +53,10 @@ Then add one short explanation line when needed.
|
||||
|
||||
### Create Ticket
|
||||
|
||||
1. Collect the external ticket details the user wants to sync.
|
||||
2. Call `create_ticket`.
|
||||
3. Confirm the created ticket row_id.
|
||||
4. If the ticket should be linked to a case, suggest attaching it next.
|
||||
|
||||
### Attach Ticket To Case
|
||||
|
||||
1. Require `case_id`.
|
||||
2. If the user does not already have a ticket row_id, either call `create_ticket` for a new ticket or retrieve the existing ticket first.
|
||||
3. Call `attach_ticket_to_case(case_id=<case_id>, ticket_row_id=<ticket_row_id>)`.
|
||||
4. Confirm that the ticket is attached.
|
||||
2. Collect the external ticket details the user wants to sync.
|
||||
3. Call `create_ticket(case_id=..., uid=..., ...)`.
|
||||
4. Confirm the created ticket row_id and that it is attached to the case.
|
||||
|
||||
### Update Ticket
|
||||
|
||||
@@ -80,13 +70,11 @@ Preferred response structure:
|
||||
|
||||
- `Updated ticket`: ticket ID or returned row_id
|
||||
- `Changed fields`: only the fields sent in the request
|
||||
- `Next useful step`: optional, usually to attach it to a case or review the refreshed ticket
|
||||
|
||||
## Clarification Rules
|
||||
|
||||
- Ask for `case_id` only when the user wants case attachment and did not provide it.
|
||||
- Ask for `case_id` only when creating a ticket and it was not provided.
|
||||
- Ask for `ticket_id` only when the user wants to update a specific synced ticket and did not provide it.
|
||||
- If the user wants to create a ticket and attach it in one request, do both steps without forcing them to separate the workflow.
|
||||
|
||||
## Output Rules
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
name: asp-ticket-zh
|
||||
description: '把外部 ticket 同步到 ASP、把 ticket 关联到 case、列出已同步 ticket,或更新已有 ticket 记录。'
|
||||
argument-hint: 'list tickets [filters] | create ticket <uid> | attach ticket to case <case_id> | update ticket <ticket_id> <fields>'
|
||||
description: '把外部 ticket 同步到 ASP、创建并关联到 case、列出已同步 ticket,或更新已有 ticket 记录。'
|
||||
argument-hint: 'list tickets [filters] | create ticket <case_id> <uid> | update ticket <ticket_id> <fields>'
|
||||
compatibility: connect to asp mcp server
|
||||
metadata:
|
||||
author: Funnywolf
|
||||
version: 0.1.0
|
||||
version: 0.2.0
|
||||
mcp-server: asp
|
||||
category: cyber security
|
||||
tags: [ ticket, case, sync, workflow ]
|
||||
@@ -18,25 +18,22 @@ metadata:
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 用户想创建一条已同步的外部 ticket 记录。
|
||||
- 用户想把 ticket 关联到 case。
|
||||
- 用户想创建一条已同步的外部 ticket 记录并关联到 case。
|
||||
- 用户想按状态、类型或外部 UID 列出已同步 ticket。
|
||||
- 用户想更新已同步 ticket 的字段。
|
||||
|
||||
## 运行规则
|
||||
|
||||
- 把 ticket 视为已同步的外部工作流记录,而不是平台的主要调查对象。
|
||||
- 使用 `create_ticket` 创建已同步 ticket 记录。
|
||||
- 使用 `attach_ticket_to_case` 把已有 ticket 记录关联到 case,前提是已经拿到 ticket row_id。
|
||||
- 使用 `create_ticket` 创建已同步 ticket 记录并自动关联到 case。
|
||||
- 使用 `list_tickets` 浏览和查询。
|
||||
- 使用 `update_ticket` 只修改用户明确要求变更的字段。
|
||||
|
||||
## 决策流程
|
||||
|
||||
1. 如果用户想创建已同步 ticket 记录,调用 `create_ticket`。
|
||||
2. 如果用户想把 ticket 关联到 case,视情况先创建 ticket 或先取回已有 ticket row_id,再调用 `attach_ticket_to_case`。
|
||||
3. 如果用户想浏览或对比已同步 ticket,调用 `list_tickets`。
|
||||
4. 如果用户想修改已同步 ticket 字段,调用 `update_ticket`。
|
||||
1. 如果用户想创建已同步 ticket 并关联到 case,调用 `create_ticket(case_id=..., uid=...)`。
|
||||
2. 如果用户想浏览或对比已同步 ticket,调用 `list_tickets`。
|
||||
3. 如果用户想修改已同步 ticket 字段,调用 `update_ticket`。
|
||||
|
||||
## SOP
|
||||
|
||||
@@ -45,7 +42,7 @@ metadata:
|
||||
1. 从请求中提取最窄且最有用的过滤条件。
|
||||
2. 调用 `list_tickets`。
|
||||
3. 解析返回的 JSON 字符串。
|
||||
4. 以紧凑的工作流视图呈现;如果用户大概率下一步要附加或复用该 ticket,则显式展示 ticket row_id。
|
||||
4. 以紧凑的工作流视图呈现;如果用户大概率下一步要复用该 ticket,则显式展示 ticket row_id。
|
||||
|
||||
首选回复结构:
|
||||
|
||||
@@ -56,17 +53,10 @@ metadata:
|
||||
|
||||
### 创建 Ticket
|
||||
|
||||
1. 收集用户想同步的外部 ticket 详情。
|
||||
2. 调用 `create_ticket`。
|
||||
3. 确认创建后的 ticket row_id。
|
||||
4. 如果该 ticket 应该关联到 case,建议下一步附加到 case。
|
||||
|
||||
### 把 Ticket 附加到 Case
|
||||
|
||||
1. 要求提供 `case_id`。
|
||||
2. 如果用户还没有 ticket row_id,则先为新 ticket 调用 `create_ticket`,或先取回已有 ticket。
|
||||
3. 调用 `attach_ticket_to_case(case_id=<case_id>, ticket_row_id=<ticket_row_id>)`。
|
||||
4. 确认 ticket 已附加成功。
|
||||
2. 收集用户想同步的外部 ticket 详情。
|
||||
3. 调用 `create_ticket(case_id=..., uid=..., ...)`。
|
||||
4. 确认创建后的 ticket row_id 及其已关联到指定 case。
|
||||
|
||||
### 更新 Ticket
|
||||
|
||||
@@ -83,9 +73,8 @@ metadata:
|
||||
|
||||
## 澄清规则
|
||||
|
||||
- 只有当用户要附加到 case 却未提供时,才询问 `case_id`。
|
||||
- 只有当用户创建 ticket 却未提供 case_id 时,才询问 `case_id`。
|
||||
- 只有当用户要更新特定已同步 ticket 却未提供时,才询问 `ticket_id`。
|
||||
- 如果用户想在一次请求中创建并附加 ticket,就完成两步,不要强迫用户拆分工作流。
|
||||
|
||||
## 输出规则
|
||||
|
||||
|
||||
+29
-104
@@ -12,9 +12,10 @@ from PLUGINS.SIEM.tools import SIEMToolKit
|
||||
from PLUGINS.SIRP.nocolymodel import Group, Condition, Operator
|
||||
from PLUGINS.SIRP.sirpapi import Alert, Artifact, Case, Enrichment, Knowledge, Playbook, Ticket
|
||||
from PLUGINS.SIRP.sirpbasemodel import AI_PROFILE_MCP
|
||||
from PLUGINS.SIRP.sirpcoremodel import TicketStatus, TicketType, ArtifactName, ArtifactType, ArtifactRole, ArtifactReputationScore, Severity, AttackStage, Confidence, \
|
||||
AlertStatus, CaseStatus, CaseVerdict, EnrichmentModel, EnrichmentType, EnrichmentProvider, TicketModel, ArtifactModel
|
||||
from PLUGINS.SIRP.sirpextramodel import PlaybookType, KnowledgeSource, PlaybookJobStatus
|
||||
from PLUGINS.SIRP.sirpcoremodel import TicketStatus, TicketType, ArtifactType, ArtifactRole, Severity, AttackStage, \
|
||||
Confidence, \
|
||||
AlertStatus, CaseStatus, CaseVerdict, EnrichmentModel, EnrichmentType, EnrichmentProvider, TicketModel
|
||||
from PLUGINS.SIRP.sirpextramodel import PlaybookType, PlaybookJobStatus
|
||||
|
||||
|
||||
def _dump_models_for_ai(models, limit: int) -> list[dict]:
|
||||
@@ -58,6 +59,7 @@ def list_cases(
|
||||
title: Annotated[Optional[str], Field(description="Fuzzy case title filter (Case 标题模糊过滤)")] = None,
|
||||
tags: Annotated[Optional[list[str]], Field(description="Case tag filter (Case 标签过滤)")] = None,
|
||||
lazy_load: Annotated[bool, Field(description="True means do not load attached related data (True 表示不加载关联数据)")] = True,
|
||||
include_discussions: Annotated[bool, Field(description="Include case discussions in the result (返回结果中包含讨论记录)")] = True,
|
||||
limit: Annotated[int, Field(description="Max cases to return (最多返回条数)")] = 10
|
||||
) -> Annotated[list[dict], Field(description="Matching cases as AI-friendly JSON list (匹配的 Case 列表)")]:
|
||||
"""List cases with optional filters. (列出 Case,支持多条件过滤)"""
|
||||
@@ -83,38 +85,34 @@ def list_cases(
|
||||
|
||||
filter_model = Group(logic="AND", children=conditions or [])
|
||||
models = Case.list(filter_model, lazy_load=lazy_load)
|
||||
return _dump_models_for_ai(models, limit)
|
||||
results = _dump_models_for_ai(models, limit)
|
||||
|
||||
if include_discussions:
|
||||
for item in results:
|
||||
row_id_val = item.get("row_id")
|
||||
if row_id_val:
|
||||
discussions = Case.get_discussions_by_row_id(row_id_val) or []
|
||||
item["discussions"] = discussions
|
||||
|
||||
def get_case_discussions(
|
||||
case_id: Annotated[str, Field(description="Case ID, e.g. case_000005 (Case ID)")]
|
||||
) -> Annotated[Optional[list[str]], Field(
|
||||
description="Case discussions as JSON string list, or None if case not found (Case 讨论记录 JSON 字符串列表,Case 不存在时返回 None)")]:
|
||||
"""Get case discussions by case ID. (通过 Case ID 获取讨论记录)"""
|
||||
discussions = Case.get_discussions_by_id(case_id)
|
||||
if discussions is None:
|
||||
return None
|
||||
return [json.dumps(item, ensure_ascii=False) for item in discussions]
|
||||
return results
|
||||
|
||||
|
||||
def update_case(
|
||||
case_id: Annotated[str, Field(description="Case ID to update (待更新的 Case ID)")],
|
||||
severity_ai: Annotated[Optional[Severity], Field(description="Updated AI-assessed severity (更新 AI 评估严重程度)")] = None,
|
||||
confidence_ai: Annotated[Optional[Confidence], Field(description="Updated AI-assessed confidence (更新 AI 评估置信度)")] = None,
|
||||
attack_stage_ai: Annotated[Optional[AttackStage], Field(description="Updated AI-assessed attack stage (更新 AI 评估攻击阶段)")] = None,
|
||||
comment_ai: Annotated[Optional[str], Field(description="Updated AI comment, Markdown supported (更新 AI 注释,支持 Markdown)")] = None,
|
||||
verdict_ai: Annotated[Optional[CaseVerdict], Field(description="Updated AI-assessed verdict (更新 AI 评估判定结果)")] = None,
|
||||
summary_ai: Annotated[Optional[str], Field(description="Updated AI closure summary, Markdown supported (更新 AI 结案摘要,支持 Markdown)")] = None
|
||||
comment: Annotated[Optional[str], Field(description="Analyst comment, Markdown supported (分析师注释,支持 Markdown)")] = None,
|
||||
summary: Annotated[Optional[str], Field(description="Closure summary, Markdown supported (结案摘要,支持 Markdown)")] = None,
|
||||
) -> Annotated[Optional[str], Field(description="Updated case row ID, or None if not found (更新后的 Case 行 ID,不存在时返回 None)")]:
|
||||
"""Update AI-assessed fields on a case. (更新 Case 的 AI 评估字段)"""
|
||||
"""Update case fields including AI-assessed fields, analyst comment, and closure summary. (更新 Case 字段,包括 AI 评估字段、分析师注释和结案摘要)"""
|
||||
return Case.update_by_id(
|
||||
case_id=case_id,
|
||||
severity_ai=severity_ai,
|
||||
confidence_ai=confidence_ai,
|
||||
attack_stage_ai=attack_stage_ai,
|
||||
comment_ai=comment_ai,
|
||||
verdict_ai=verdict_ai,
|
||||
summary_ai=summary_ai
|
||||
comment=comment,
|
||||
summary=summary,
|
||||
)
|
||||
|
||||
|
||||
@@ -150,40 +148,6 @@ def list_alerts(
|
||||
return _dump_models_for_ai(models, limit)
|
||||
|
||||
|
||||
def get_alert_discussions(
|
||||
alert_id: Annotated[str, Field(description="Alert ID, e.g. alert_000001 (Alert ID)")]
|
||||
) -> Annotated[Optional[list[str]], Field(
|
||||
description="Alert discussions as JSON string list, or None if alert not found (Alert 讨论记录 JSON 字符串列表,Alert 不存在时返回 None)")]:
|
||||
"""Get alert discussions by alert ID. (通过 Alert ID 获取讨论记录)"""
|
||||
discussions = Alert.get_discussions(alert_id)
|
||||
if discussions is None:
|
||||
return None
|
||||
return [json.dumps(item, ensure_ascii=False) for item in discussions]
|
||||
|
||||
|
||||
# Artifact
|
||||
# Do not open to mcp , because we think artifact is add only by automation, not human
|
||||
def create_artifact(
|
||||
name: Annotated[ArtifactName, Field(description="Artifact name (实体名称)")] = ArtifactName.UNKNOWN,
|
||||
type: Annotated[Optional[ArtifactType], Field(description="Artifact type (实体类型)")] = None,
|
||||
role: Annotated[Optional[ArtifactRole], Field(description="Artifact role in event (实体在事件中的角色)")] = None,
|
||||
owner: Annotated[str, Field(description="Owning system or user (所属系统或用户)")] = "",
|
||||
value: Annotated[str, Field(description="Artifact value (实体值)")] = "",
|
||||
reputation_provider: Annotated[str, Field(description="Threat intel provider (威胁情报提供商)")] = "",
|
||||
reputation_score: Annotated[Optional[ArtifactReputationScore], Field(description="Artifact reputation score (实体信誉评分)")] = None
|
||||
) -> Annotated[str, Field(description="Created artifact record row ID (创建的 Artifact 行 ID)")]:
|
||||
"""Create one artifact record. (创建一条 Artifact 记录)"""
|
||||
model = ArtifactModel()
|
||||
model.name = name
|
||||
model.type = type
|
||||
model.role = role
|
||||
model.owner = owner
|
||||
model.value = value
|
||||
model.reputation_provider = reputation_provider
|
||||
model.reputation_score = reputation_score
|
||||
return Artifact.create(model)
|
||||
|
||||
|
||||
# Do not open to mcp , because we think artifact is add only by automation, not human
|
||||
def attach_artifact_to_alert(
|
||||
alert_id: Annotated[str, Field(description="Target alert ID to receive the existing artifact (接收 Artifact 的目标 Alert ID)")],
|
||||
@@ -202,7 +166,6 @@ def list_artifacts(
|
||||
artifact_id: Annotated[Optional[str], Field(description="Artifact ID filter, e.g. artifact_000001 (Artifact ID 过滤)")] = None,
|
||||
type: Annotated[Optional[list[ArtifactType]], Field(description="Artifact type filter (实体类型过滤)")] = None,
|
||||
role: Annotated[Optional[list[ArtifactRole]], Field(description="Artifact role filter (实体角色过滤)")] = None,
|
||||
reputation_score: Annotated[Optional[list[ArtifactReputationScore]], Field(description="Artifact reputation filter (实体信誉过滤)")] = None,
|
||||
owner: Annotated[Optional[str], Field(description="Artifact owner filter, exact match (实体所有者过滤,精确匹配)")] = None,
|
||||
value: Annotated[Optional[str], Field(description="Artifact value filter, exact match (实体值过滤,精确匹配)")] = None,
|
||||
lazy_load: Annotated[bool, Field(description="True means do not load attached related data (True 表示不加载关联数据)")] = True,
|
||||
@@ -218,8 +181,6 @@ def list_artifacts(
|
||||
conditions.append(Condition(field="type", operator=Operator.IN, value=type))
|
||||
if role:
|
||||
conditions.append(Condition(field="role", operator=Operator.IN, value=role))
|
||||
if reputation_score:
|
||||
conditions.append(Condition(field="reputation_score", operator=Operator.IN, value=reputation_score))
|
||||
if owner:
|
||||
conditions.append(Condition(field="owner", operator=Operator.EQ, value=owner))
|
||||
if value:
|
||||
@@ -285,31 +246,23 @@ def attach_enrichment_to_target(
|
||||
|
||||
# Ticket
|
||||
def create_ticket(
|
||||
case_id: Annotated[str, Field(description="Target case ID to attach the ticket to (挂载 Ticket 的目标 Case ID)")],
|
||||
uid: Annotated[str, Field(description="External ticket ID to sync into SIRP (同步到 SIRP 的外部工单 ID)")],
|
||||
title: Annotated[str, Field(description="Ticket title (工单标题)")] = "",
|
||||
status: Annotated[Optional[TicketStatus], Field(description="External ticket status (外部工单状态)")] = None,
|
||||
type: Annotated[Optional[TicketType], Field(description="External ticket type (外部工单类型)")] = None,
|
||||
src_url: Annotated[str, Field(description="External ticket URL (外部工单 URL)")] = ""
|
||||
) -> Annotated[str, Field(description="Created ticket record row ID (创建的 Ticket 行 ID)")]:
|
||||
"""Create one synced external ticket record in SIRP. (在 SIRP 中创建一条同步的外部工单记录)"""
|
||||
"""Create one synced external ticket record in SIRP and attach it to a case. (在 SIRP 中创建一条同步的外部工单记录并挂载到 Case)"""
|
||||
model = TicketModel()
|
||||
model.uid = uid
|
||||
model.title = title
|
||||
model.status = status
|
||||
model.type = type
|
||||
model.src_url = src_url
|
||||
return Ticket.create(model)
|
||||
|
||||
|
||||
def attach_ticket_to_case(
|
||||
case_id: Annotated[str, Field(description="Target case ID to receive the existing ticket (接收 Ticket 的目标 Case ID)")],
|
||||
ticket_row_id: Annotated[str, Field(description="Ticket record row ID returned by create_ticket (由 create_ticket 返回的 Ticket 行 ID)")]
|
||||
) -> Annotated[Optional[str], Field(description="Attached ticket record row ID, or None if case not found (挂载后的 Ticket 行 ID,Case 不存在时返回 None)")]:
|
||||
"""Attach one existing ticket record to an existing case. (将已有 Ticket 挂载到已有 Case)"""
|
||||
return Case.attach_ticket(
|
||||
case_id=case_id,
|
||||
ticket_row_id=ticket_row_id
|
||||
)
|
||||
ticket_row_id = Ticket.create(model)
|
||||
Case.attach_ticket(case_id=case_id, ticket_row_id=ticket_row_id)
|
||||
return ticket_row_id
|
||||
|
||||
|
||||
def list_tickets(
|
||||
@@ -411,38 +364,12 @@ def execute_playbook(
|
||||
return result.model_dump_json_for_ai(profile=AI_PROFILE_MCP)
|
||||
|
||||
|
||||
def list_knowledge(
|
||||
row_id: Annotated[Optional[str], Field(description="Knowledge row ID filter, e.g. 03c26478-b213-44c8-b651-3cc88abaac01 (知识条目行 ID 过滤)")] = None,
|
||||
source: Annotated[Optional[list[KnowledgeSource]], Field(description="Knowledge source filter (知识来源过滤)")] = None,
|
||||
title: Annotated[Optional[str], Field(description="Fuzzy knowledge title filter (知识标题模糊过滤)")] = None,
|
||||
body: Annotated[Optional[str], Field(description="Fuzzy knowledge body filter (知识内容模糊过滤)")] = None,
|
||||
tags: Annotated[Optional[list[str]], Field(description="Knowledge tag filter (知识标签过滤)")] = None,
|
||||
limit: Annotated[int, Field(description="Max knowledge records to return (最多返回条数)")] = 10
|
||||
) -> Annotated[list[dict], Field(description="Matching knowledge records as AI-friendly JSON list (匹配的知识条目列表)")]:
|
||||
"""List knowledge records with optional filters. (列出知识条目,支持多条件过滤)"""
|
||||
conditions = []
|
||||
|
||||
if row_id:
|
||||
conditions.append(Condition(field="rowId", operator=Operator.EQ, value=row_id))
|
||||
if source:
|
||||
conditions.append(Condition(field="source", operator=Operator.IN, value=source))
|
||||
if title:
|
||||
conditions.append(Condition(field="title", operator=Operator.CONTAINS, value=title))
|
||||
if body:
|
||||
conditions.append(Condition(field="body", operator=Operator.CONTAINS, value=body))
|
||||
if tags:
|
||||
conditions.append(Condition(field="tags", operator=Operator.CONTAINS, value=tags))
|
||||
|
||||
filter_model = Group(logic="AND", children=conditions or [])
|
||||
models = Knowledge.list(filter_model, lazy_load=True)
|
||||
return _dump_models_for_ai(models, limit)
|
||||
|
||||
|
||||
def update_knowledge(
|
||||
knowledge_id: Annotated[str, Field(description="Knowledge ID to update (待更新的知识条目 ID)")],
|
||||
title: Annotated[Optional[str], Field(description="Updated knowledge title (更新知识标题)")] = None,
|
||||
body: Annotated[Optional[str], Field(description="Updated knowledge body (更新知识内容)")] = None,
|
||||
expires_at: Annotated[Optional[str], Field(description="Updated expiration time; omit or keep empty for permanently valid knowledge (更新过期时间,不填写表示永久有效)")] = None,
|
||||
expires_at: Annotated[Optional[str], Field(
|
||||
description="Updated expiration time; omit or keep empty for permanently valid knowledge (更新过期时间,不填写表示永久有效)")] = None,
|
||||
tags: Annotated[Optional[list[str]], Field(description="Updated knowledge tags; pass [] to clear (更新知识标签,传 [] 可清空)")] = None
|
||||
) -> Annotated[Optional[str], Field(description="Updated knowledge row ID, or None if not found (更新后的知识条目行 ID,不存在时返回 None)")]:
|
||||
"""Update one knowledge record in SIRP. (更新 SIRP 中一条知识条目)"""
|
||||
@@ -456,9 +383,11 @@ def update_knowledge(
|
||||
|
||||
|
||||
def search_knowledge(
|
||||
keyword: Annotated[Union[str, list[str]], Field(description="Search keyword or keyword list; when a list is provided, records matching at least one item are returned (搜索关键词或关键词列表;传入列表时返回匹配至少一个列表项的记录)")],
|
||||
keyword: Annotated[Union[str, list[str]], Field(
|
||||
description="Search keyword or keyword list; when a list is provided, records matching at least one item are returned (搜索关键词或关键词列表;传入列表时返回匹配至少一个列表项的记录)")],
|
||||
limit: Annotated[int, Field(description="Maximum number of knowledge records to return (最多返回的知识记录数量)")] = 10
|
||||
) -> Annotated[str, Field(description="Relevant knowledge entries, policies, and special handling instructions as a JSON list string (JSON 列表字符串形式的相关知识条目、策略及特殊处理说明)")]:
|
||||
) -> Annotated[str, Field(
|
||||
description="Relevant knowledge entries, policies, and special handling instructions as a JSON list string (JSON 列表字符串形式的相关知识条目、策略及特殊处理说明)")]:
|
||||
"""Search the internal knowledge base by keyword. (按关键词搜索内部知识库)"""
|
||||
results = Knowledge.search(keyword, limit=limit)
|
||||
return results
|
||||
@@ -536,12 +465,10 @@ REGISTERED_MCP_TOOLS = [
|
||||
|
||||
# case
|
||||
list_cases,
|
||||
get_case_discussions,
|
||||
update_case,
|
||||
|
||||
# alert
|
||||
list_alerts,
|
||||
get_alert_discussions,
|
||||
|
||||
# artifact
|
||||
list_artifacts,
|
||||
@@ -556,7 +483,6 @@ REGISTERED_MCP_TOOLS = [
|
||||
list_playbook_runs,
|
||||
|
||||
# knowledge
|
||||
list_knowledge,
|
||||
update_knowledge,
|
||||
search_knowledge,
|
||||
|
||||
@@ -564,7 +490,6 @@ REGISTERED_MCP_TOOLS = [
|
||||
list_tickets,
|
||||
create_ticket,
|
||||
update_ticket,
|
||||
attach_ticket_to_case,
|
||||
|
||||
# SIEM
|
||||
get_current_time,
|
||||
|
||||
@@ -769,12 +769,11 @@ class Case(BaseWorksheetEntity[CaseModel]):
|
||||
severity: Union[Severity, None] = None,
|
||||
status=None,
|
||||
verdict=None,
|
||||
comment: Union[str, None] = None,
|
||||
summary: Union[str, None] = None,
|
||||
severity_ai: Union[Severity, None] = None,
|
||||
confidence_ai: Union[Confidence, None] = None,
|
||||
attack_stage_ai=None,
|
||||
comment_ai: Union[str, None] = None,
|
||||
verdict_ai=None,
|
||||
summary_ai: Union[str, None] = None
|
||||
) -> Union[str, None]:
|
||||
case_old = cls.get_by_id(case_id, lazy_load=True)
|
||||
if not case_old:
|
||||
@@ -788,18 +787,16 @@ class Case(BaseWorksheetEntity[CaseModel]):
|
||||
case_new.status = status
|
||||
if verdict is not None:
|
||||
case_new.verdict = verdict
|
||||
if comment is not None:
|
||||
case_new.comment = comment
|
||||
if summary is not None:
|
||||
case_new.summary = summary
|
||||
if severity_ai is not None:
|
||||
case_new.severity_ai = severity_ai
|
||||
if confidence_ai is not None:
|
||||
case_new.confidence_ai = confidence_ai
|
||||
if attack_stage_ai is not None:
|
||||
case_new.attack_stage_ai = attack_stage_ai
|
||||
if comment_ai is not None:
|
||||
case_new.comment_ai = comment_ai
|
||||
if verdict_ai is not None:
|
||||
case_new.verdict_ai = verdict_ai
|
||||
if summary_ai is not None:
|
||||
case_new.summary_ai = summary_ai
|
||||
|
||||
return cls.update(case_new)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user