File size: 6,345 Bytes
5b76d85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
"""
MITRE ATT&CK Navigator JSON Schema
"""

from pydantic import BaseModel, Field, validator, StringConstraints
from typing import Optional, Dict, Any, List, Annotated
import re
from datetime import datetime

# カスタム型の定義
TechniqueID = Annotated[str, StringConstraints(pattern=r'^T\d{4}(\.\d{3})?$')]
TacticName = Annotated[str, StringConstraints(pattern=r'^[a-z-]+$')]
HexColor = Annotated[str, StringConstraints(pattern=r'^#[0-9A-Fa-f]{6}$')]
Domain = Annotated[str, StringConstraints(pattern=r'^(enterprise-attack|mobile-attack|ics-attack)$')]

class Technique(BaseModel):
    """Technique model for ATT&CK Navigator"""
    techniqueID: TechniqueID = Field(description="The MITRE ATT&CK technique ID (e.g., T1234 or T1234.001)")
    tactic: Optional[TacticName] = Field(description="The tactic this technique belongs to (lowercase with hyphens)")
    score: Optional[int] = Field(description="Score between 0 and 100", ge=0, le=100)
    color: Optional[HexColor] = Field(description="Hex color code (e.g., #FF0000)")
    comment: Optional[str] = Field(description="Comment about the technique", max_length=1000)
    enabled: Optional[bool] = Field(description="Whether the technique is enabled", default=True)
    metadata: Optional[List[Dict[str, str]]] = Field(description="Additional metadata")
    links: Optional[List[Dict[str, str]]] = Field(description="Related links")
    showSubtechniques: Optional[bool] = Field(description="Whether to show subtechniques", default=True)

    @validator('links')
    def validate_links(cls, v):
        if v is not None:
            for link in v:
                if not all(k in link for k in ['label', 'url']):
                    raise ValueError("Each link must have 'label' and 'url' fields")
        return v

class VersionInfo(BaseModel):
    """バージョン情報のサブスキーマ"""
    attack: str = Field(
        ...,
        description="""ATT&CKのバージョン。ユーザーが指定したバージョンを使用すること。
        例: ユーザーが「ATT&CKバージョンは16で」と指定した場合、"16"と設定する。
        指定がない場合は、DEFAULT_LAYER_SETTINGSの値を使用する。"""
    )
    navigator: str = Field(
        ...,
        description="""Navigatorのバージョン。ユーザーが指定したバージョンを使用すること。
        指定がない場合は、DEFAULT_LAYER_SETTINGSの値を使用する。"""
    )
    layer: str = Field(
        ...,
        description="""レイヤーのバージョン。ユーザーが指定したバージョンを使用すること。
        指定がない場合は、DEFAULT_LAYER_SETTINGSの値を使用する。"""
    )

class AttackLayer(BaseModel):
    """MITRE ATT&CK Navigator Layer"""
    name: str = Field(..., description="レイヤーの名前")
    description: str = Field(..., description="レイヤーの説明")
    domain: Domain = Field(..., description="ATT&CKドメイン")
    versions: VersionInfo = Field(..., description="バージョン情報")
    filters: Dict[str, Any] = Field(
        default_factory=lambda: {
            "platforms": [
                "Windows",
                "Linux",
                "macOS",
                "Containers",
                "IaaS",
                "Network"
            ]
        },
        description="フィルター設定"
    )
    sorting: int = Field(default=0, description="ソート順")
    layout: Dict[str, Any] = Field(
        default_factory=lambda: {
            "layout": "side",
            "showName": True,
            "showID": False,
            "showAggregateScores": True,
            "countUnscored": True,
            "aggregateFunction": "average"
        },
        description="レイアウト設定"
    )
    hideDisabled: bool = Field(default=False, description="無効なテクニックを非表示")
    techniques: List[Technique] = Field(..., description="テクニックのリスト")
    gradient: Dict[str, Any] = Field(
        default_factory=lambda: {
            "colors": ["#ff6666", "#ffe766", "#8ec843"],
            "minValue": 0,
            "maxValue": 100
        },
        description="スコアのグラデーション設定"
    )
    legendItems: List[Dict[str, Any]] = Field(
        default_factory=list,
        description="凡例アイテム"
    )
    showTacticRowBackground: bool = Field(default=False, description="タクティック行の背景を表示")
    tacticRowBackground: str = Field(default="#dddddd", description="タクティック行の背景色")
    selectTechniquesAcrossTactics: bool = Field(default=True, description="タクティック間でテクニックを選択")
    selectSubtechniquesWithParent: bool = Field(default=False, description="サブテクニックを親と共に選択")
    selectVisibleTechniques: bool = Field(default=False, description="表示されているテクニックのみを選択")
    metadata: List[Dict[str, str]] = Field(
        default_factory=lambda: [
            {"name": "作成者", "value": "AIアシスタント"},
            {"name": "作成日", "value": datetime.now().strftime("%Y-%m-%d")}
        ],
        description="メタデータ"
    )

# デフォルトのレイヤー設定
DEFAULT_LAYER_SETTINGS = {
    "versions": {
        "attack": "16.1",
        "navigator": "5.1.0",
        "layer": "4.5"
    },
    "layout": {
        "layout": "side",
        "showName": True,
        "showID": False,
        "showAggregateScores": True,
        "countUnscored": True,
        "aggregateFunction": "average"
    },
    "gradient": {
        "colors": ["#ff6666", "#ffe766", "#8ec843"],
        "minValue": 0,
        "maxValue": 100
    }
}

# プロンプトテンプレート
ATTACK_PROMPT = """
あなたはMITRE ATT&CKフレームワークの専門家です。
以下のシナリオに基づいて、MITRE ATT&CK NavigatorのレイヤーJSONを生成してください。

回答形式:
{format_instructions}

注意:
- 有効なMITRE ATT&CKテクニックIDを使用すること
- 適切なタクティクスを含めること
- スコアは0-100の範囲で設定すること
- 有効な16進数カラーコードを使用すること
- 意味のあるコメントを含めること
- 適切なメタデータを設定すること
"""