@hoanghainh/komagu 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +1 -1
- package/assess/lsa/scripts/attack_chains.py +573 -0
- package/assess/lsa/scripts/baseline.py +14 -0
- package/assess/lsa/scripts/findings_to_sarif.py +11 -0
- package/assess/lsa/scripts/forecast.py +544 -0
- package/assess/lsa/scripts/merge_findings.py +14 -0
- package/assess/lsa/scripts/render_report.py +86 -0
- package/assess/lsa/tests/test_attack_chains.py +292 -0
- package/assess/lsa/tests/test_forecast.py +160 -0
- package/contracts/schemas/api-inventory-diff.schema.json +213 -0
- package/contracts/schemas/attack-chain.schema.json +181 -0
- package/contracts/schemas/bas-scenario.schema.json +217 -0
- package/contracts/schemas/evidence-manifest.schema.json +117 -0
- package/contracts/schemas/finding.schema.json +7 -0
- package/contracts/schemas/forecast.schema.json +172 -0
- package/examples/member-portal/backend-dotnet/MemberPortal.Backend.csproj +2 -2
- package/governance/scs/__init__.py +21 -0
- package/governance/scs/mapping.py +133 -0
- package/governance/scs/pack.py +354 -0
- package/operate/cli/__init__.py +1 -1
- package/operate/cli/commands/evidence.py +138 -0
- package/operate/cli/commands/inventory.py +187 -0
- package/operate/cli/commands/simulate.py +175 -58
- package/operate/cli/main.py +7 -3
- package/operate/inventory/__init__.py +22 -0
- package/operate/inventory/api_inventory.py +566 -0
- package/operate/simulate/__init__.py +16 -0
- package/operate/simulate/engine.py +370 -0
- package/operate/simulate/loader.py +158 -0
- package/operate/simulate/scenarios/idc-frontier-ransomware.json +47 -0
- package/operate/simulate/scenarios/metabase-cve-2026-72898.json +84 -0
- package/operate/simulate/scenarios/times-car-breach.json +74 -0
- package/operate/simulate/scenarios.py +52 -0
- package/operate/tests/test_api_inventory.py +239 -0
- package/operate/tests/test_declarative_scenarios.py +169 -0
- package/operate/tests/test_evidence_pack.py +128 -0
- package/package.json +1 -1
- package/remediate/authz/dotnet/src/Komagu.Security.EntityFrameworkCore/Komagu.Security.EntityFrameworkCore.csproj +1 -1
- package/remediate/authz/dotnet/tests/Komagu.Security.Authorization.Tests/Komagu.Security.Authorization.Tests.csproj +1 -1
- package/remediate/authz/spring/build.gradle.kts +4 -4
- package/remediate/identity/dotnet/src/Komagu.Security.Identity/Komagu.Security.Identity.csproj +1 -1
- package/remediate/identity/dotnet/tests/Komagu.Security.Identity.Tests/Komagu.Security.Identity.Tests.csproj +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Command: komagu simulate - Empirical Breach & Attack Simulation (BAS)."""
|
|
1
|
+
"""Command: komagu simulate - Empirical Breach & Attack Simulation (BAS) (#317)."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -7,7 +7,7 @@ import json
|
|
|
7
7
|
import sys
|
|
8
8
|
import tempfile
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Any, List
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
11
|
|
|
12
12
|
from operate.cli.ui import (
|
|
13
13
|
BOLD,
|
|
@@ -23,12 +23,18 @@ from operate.cli.ui import (
|
|
|
23
23
|
step_ok,
|
|
24
24
|
step_warn,
|
|
25
25
|
)
|
|
26
|
+
from operate.simulate.engine import (
|
|
27
|
+
DeclarativeScenario,
|
|
28
|
+
generate_execution_evidence,
|
|
29
|
+
)
|
|
30
|
+
from operate.simulate.loader import (
|
|
31
|
+
discover_scenarios,
|
|
32
|
+
load_scenario_file,
|
|
33
|
+
validate_scenario,
|
|
34
|
+
)
|
|
26
35
|
from operate.simulate.scenarios import (
|
|
27
36
|
ExecutiveReportGenerator,
|
|
28
|
-
IDCFrontierRansomwareScenario,
|
|
29
|
-
MetabaseZeroDayScenario,
|
|
30
37
|
ScenarioResult,
|
|
31
|
-
TimesCarBreachScenario,
|
|
32
38
|
)
|
|
33
39
|
|
|
34
40
|
|
|
@@ -41,28 +47,71 @@ def register_subcommand(subparsers: argparse._SubParsersAction) -> None:
|
|
|
41
47
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
42
48
|
epilog="""
|
|
43
49
|
Examples:
|
|
50
|
+
# List all discovered attack scenarios
|
|
51
|
+
komagu simulate --list
|
|
52
|
+
|
|
53
|
+
# Validate a custom declarative scenario JSON against bas-scenario.schema.json
|
|
54
|
+
komagu simulate --validate ./custom-scenario.json
|
|
55
|
+
|
|
44
56
|
# Run all simulated attack scenarios against a target staging API
|
|
45
57
|
komagu simulate --target http://localhost:5001
|
|
46
58
|
|
|
47
59
|
# Simulate the Times Car mass IDOR harvesting attack specifically
|
|
48
60
|
komagu simulate --scenario times-car-breach --target http://staging-api:8000
|
|
49
61
|
|
|
50
|
-
#
|
|
51
|
-
komagu simulate --scenario
|
|
52
|
-
|
|
53
|
-
# Simulate IDC Frontier ransomware immutable storage attack
|
|
54
|
-
komagu simulate --scenario idc-frontier-ransomware --target /tmp/backup-dir
|
|
62
|
+
# Run a custom scenario file with parameter override
|
|
63
|
+
komagu simulate --scenario-file ./my-scenario.json --set endpoint_pattern=/api/v2/items/{id}
|
|
55
64
|
|
|
56
|
-
# Export Executive C-Level Report
|
|
57
|
-
komagu simulate --target http://localhost:5001 --output ./
|
|
65
|
+
# Export Executive C-Level Report and METI SCS audit evidence
|
|
66
|
+
komagu simulate --target http://localhost:5001 --output ./bas-report.md --evidence ./bas-evidence.json
|
|
58
67
|
""",
|
|
59
68
|
)
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"--list",
|
|
71
|
+
action="store_true",
|
|
72
|
+
help="List all available built-in and discovered scenarios",
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"--validate",
|
|
76
|
+
metavar="PATH",
|
|
77
|
+
help="Validate a scenario JSON file against schema and exit",
|
|
78
|
+
)
|
|
60
79
|
parser.add_argument(
|
|
61
80
|
"--scenario",
|
|
62
81
|
"-s",
|
|
63
82
|
default="all",
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
help="Attack scenario ID to execute, or 'all' (default: all)",
|
|
84
|
+
)
|
|
85
|
+
parser.add_argument(
|
|
86
|
+
"--scenario-file",
|
|
87
|
+
action="append",
|
|
88
|
+
dest="scenario_files",
|
|
89
|
+
metavar="PATH",
|
|
90
|
+
help="Path to custom scenario file (repeatable)",
|
|
91
|
+
)
|
|
92
|
+
parser.add_argument(
|
|
93
|
+
"--scenario-dir",
|
|
94
|
+
action="append",
|
|
95
|
+
dest="scenario_dirs",
|
|
96
|
+
metavar="DIR",
|
|
97
|
+
help="Path to directory containing scenario files (repeatable)",
|
|
98
|
+
)
|
|
99
|
+
parser.add_argument(
|
|
100
|
+
"--set",
|
|
101
|
+
action="append",
|
|
102
|
+
dest="param_sets",
|
|
103
|
+
metavar="KEY=VALUE",
|
|
104
|
+
help="Override scenario parameter (repeatable, e.g. --set token=secret)",
|
|
105
|
+
)
|
|
106
|
+
parser.add_argument(
|
|
107
|
+
"--allow-draft",
|
|
108
|
+
action="store_true",
|
|
109
|
+
help="Allow execution of scenarios with status != 'approved'",
|
|
110
|
+
)
|
|
111
|
+
parser.add_argument(
|
|
112
|
+
"--allow-destructive",
|
|
113
|
+
action="store_true",
|
|
114
|
+
help="Allow execution of steps marked destructive: true",
|
|
66
115
|
)
|
|
67
116
|
parser.add_argument(
|
|
68
117
|
"--target",
|
|
@@ -89,6 +138,10 @@ Examples:
|
|
|
89
138
|
"-o",
|
|
90
139
|
help="Output file path for detailed executive report",
|
|
91
140
|
)
|
|
141
|
+
parser.add_argument(
|
|
142
|
+
"--evidence",
|
|
143
|
+
help="Output file path for METI SCS / ISO 27001 audit evidence JSON (#271)",
|
|
144
|
+
)
|
|
92
145
|
parser.add_argument(
|
|
93
146
|
"--format",
|
|
94
147
|
default="table",
|
|
@@ -100,59 +153,118 @@ Examples:
|
|
|
100
153
|
|
|
101
154
|
def execute_simulate(args: argparse.Namespace) -> int:
|
|
102
155
|
"""Execute Breach & Attack Simulation scenarios."""
|
|
156
|
+
# Mode 1: Validate scenario file
|
|
157
|
+
if args.validate:
|
|
158
|
+
v_path = Path(args.validate).resolve()
|
|
159
|
+
if not v_path.is_file():
|
|
160
|
+
step_fail(f"Scenario file not found: {v_path}")
|
|
161
|
+
return 2
|
|
162
|
+
try:
|
|
163
|
+
doc = json.loads(v_path.read_text(encoding="utf-8"))
|
|
164
|
+
except Exception as ex:
|
|
165
|
+
step_fail(f"Invalid JSON in {v_path}: {ex}")
|
|
166
|
+
return 2
|
|
167
|
+
|
|
168
|
+
errs = validate_scenario(doc)
|
|
169
|
+
if errs:
|
|
170
|
+
step_fail(f"Scenario {v_path.name} failed schema validation:")
|
|
171
|
+
for err in errs:
|
|
172
|
+
print(f" • {err}")
|
|
173
|
+
return 2
|
|
174
|
+
step_ok(f"Scenario {color(doc.get('id', v_path.name), BOLD)} is valid ({doc.get('status', 'draft')})")
|
|
175
|
+
return 0
|
|
176
|
+
|
|
177
|
+
# Discover all scenarios
|
|
178
|
+
scenarios_map = discover_scenarios(
|
|
179
|
+
scenario_files=args.scenario_files,
|
|
180
|
+
scenario_dirs=args.scenario_dirs,
|
|
181
|
+
include_builtins=True,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Mode 2: List scenarios
|
|
185
|
+
if args.list:
|
|
186
|
+
step("Discovered Breach & Attack Simulation (BAS) Scenarios:")
|
|
187
|
+
rows = []
|
|
188
|
+
for sid, sc in sorted(scenarios_map.items()):
|
|
189
|
+
status = sc.get("status", "draft")
|
|
190
|
+
status_color = GREEN if status == "approved" else (YELLOW if status == "draft" else RED)
|
|
191
|
+
date_str = sc.get("incident", {}).get("date", "-")
|
|
192
|
+
title = sc.get("title", sid)
|
|
193
|
+
rows.append((f"{color(sid, BOLD)} (v{sc.get('version', '1.0.0')})", f"[{color(status, status_color)}] {title} ({date_str})"))
|
|
194
|
+
|
|
195
|
+
print_summary_table(
|
|
196
|
+
title="Available BAS Scenarios",
|
|
197
|
+
rows=rows,
|
|
198
|
+
footer="Execute with: komagu simulate --scenario <id> --target <url>"
|
|
199
|
+
)
|
|
200
|
+
return 0
|
|
201
|
+
|
|
202
|
+
# Mode 3: Run simulation
|
|
103
203
|
step(f"Initializing Komagu Breach & Attack Simulation against {color(args.target, BOLD)}")
|
|
104
204
|
|
|
205
|
+
# Parse --set parameter overrides
|
|
206
|
+
overrides: Dict[str, Any] = {}
|
|
207
|
+
if args.param_sets:
|
|
208
|
+
for p_set in args.param_sets:
|
|
209
|
+
if "=" in p_set:
|
|
210
|
+
k, v = p_set.split("=", 1)
|
|
211
|
+
overrides[k.strip()] = v.strip()
|
|
212
|
+
|
|
213
|
+
# Legacy CLI flag bridges
|
|
214
|
+
if args.endpoint:
|
|
215
|
+
overrides["endpoint_pattern"] = args.endpoint
|
|
216
|
+
overrides["query_path"] = args.endpoint
|
|
217
|
+
if args.param:
|
|
218
|
+
overrides["target_param"] = args.param
|
|
219
|
+
if args.token:
|
|
220
|
+
overrides["attacker_token"] = args.token
|
|
221
|
+
|
|
222
|
+
if args.scenario == "all":
|
|
223
|
+
scenarios_to_run = list(scenarios_map.keys())
|
|
224
|
+
else:
|
|
225
|
+
if args.scenario not in scenarios_map:
|
|
226
|
+
step_fail(f"Unknown scenario ID: '{args.scenario}'. Use --list to view available scenarios.")
|
|
227
|
+
return 2
|
|
228
|
+
scenarios_to_run = [args.scenario]
|
|
229
|
+
|
|
105
230
|
results: List[ScenarioResult] = []
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
for sc in scenarios_to_run:
|
|
113
|
-
if sc == "times-car-breach":
|
|
114
|
-
ep = args.endpoint or "/api/legacy-orders/{id}"
|
|
115
|
-
step_info(f"Launching Scenario: Times Car Mass BOLA/IDOR Harvesting ({ep})")
|
|
116
|
-
runner = TimesCarBreachScenario(
|
|
117
|
-
target=args.target,
|
|
118
|
-
endpoint_pattern=ep,
|
|
119
|
-
attacker_token=args.token,
|
|
120
|
-
)
|
|
121
|
-
res = runner.run()
|
|
122
|
-
results.append(res)
|
|
123
|
-
if res.passed:
|
|
124
|
-
step_ok(f"Times Car Simulation: {res.resilience_score}% resilient (0 breaches)")
|
|
125
|
-
else:
|
|
126
|
-
step_fail(f"Times Car Simulation: {res.breaches_leaked} data leaks detected ({res.resilience_score}% resilient)")
|
|
127
|
-
|
|
128
|
-
elif sc == "metabase-cve-2026-72898":
|
|
129
|
-
ep = args.endpoint or "/api/products"
|
|
130
|
-
step_info(f"Launching Scenario: Metabase Zero-Day SQLi Simulation ({ep}?{args.param}=...)")
|
|
131
|
-
runner = MetabaseZeroDayScenario(
|
|
132
|
-
target=args.target,
|
|
133
|
-
query_path=ep,
|
|
134
|
-
target_param=args.param,
|
|
135
|
-
)
|
|
136
|
-
res = runner.run()
|
|
137
|
-
results.append(res)
|
|
138
|
-
if res.passed:
|
|
139
|
-
step_ok(f"Metabase Zero-Day Simulation: {res.resilience_score}% resilient (All SQLi probes blocked)")
|
|
140
|
-
else:
|
|
141
|
-
step_fail(f"Metabase Zero-Day Simulation: SQLi probes unhandled ({res.resilience_score}% resilient)")
|
|
231
|
+
runners: List[DeclarativeScenario] = []
|
|
232
|
+
|
|
233
|
+
for sid in scenarios_to_run:
|
|
234
|
+
sc_data = scenarios_map.get(sid)
|
|
235
|
+
if not sc_data:
|
|
236
|
+
continue
|
|
142
237
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
238
|
+
step_info(f"Launching Scenario: {sc_data.get('title', sid)} ({sid})")
|
|
239
|
+
|
|
240
|
+
# Handle local dir target if needed for idc-frontier-ransomware
|
|
241
|
+
eff_target = args.target
|
|
242
|
+
if sid == "idc-frontier-ransomware" and args.target.startswith("http"):
|
|
243
|
+
eff_target = tempfile.mkdtemp(prefix="komagu-backup-sim-")
|
|
244
|
+
overrides["target_dir"] = eff_target
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
runner = DeclarativeScenario(
|
|
248
|
+
scenario_data=sc_data,
|
|
249
|
+
target=eff_target,
|
|
250
|
+
param_overrides=overrides,
|
|
251
|
+
allow_draft=args.allow_draft,
|
|
252
|
+
allow_destructive=args.allow_destructive,
|
|
147
253
|
)
|
|
148
|
-
|
|
149
|
-
runner = IDCFrontierRansomwareScenario(target_dir=storage_path)
|
|
254
|
+
runners.append(runner)
|
|
150
255
|
res = runner.run()
|
|
151
256
|
results.append(res)
|
|
257
|
+
|
|
152
258
|
if res.passed:
|
|
153
|
-
step_ok(f"
|
|
259
|
+
step_ok(f"{sc_data.get('title', sid)}: {res.resilience_score}% resilient (0 breaches)")
|
|
154
260
|
else:
|
|
155
|
-
|
|
261
|
+
step_fail(f"{sc_data.get('title', sid)}: {res.breaches_leaked} data leaks detected ({res.resilience_score}% resilient)")
|
|
262
|
+
except PermissionError as p_err:
|
|
263
|
+
step_warn(f"Skipping {sid}: {p_err}")
|
|
264
|
+
|
|
265
|
+
if not results:
|
|
266
|
+
step_fail("No scenarios were executed.")
|
|
267
|
+
return 1
|
|
156
268
|
|
|
157
269
|
generator = ExecutiveReportGenerator(results, args.target)
|
|
158
270
|
overall_score = generator.overall_resilience_score
|
|
@@ -177,6 +289,11 @@ def execute_simulate(args: argparse.Namespace) -> int:
|
|
|
177
289
|
footer="Empirical simulation against landmark real-world enterprise breaches.",
|
|
178
290
|
)
|
|
179
291
|
|
|
292
|
+
# Generate METI SCS audit evidence JSON if requested
|
|
293
|
+
if args.evidence:
|
|
294
|
+
generate_execution_evidence(runners, results, args.target, output_path=args.evidence)
|
|
295
|
+
step_ok(f"Saved METI SCS Audit Evidence to {color(args.evidence, BOLD)}")
|
|
296
|
+
|
|
180
297
|
# Save output if requested
|
|
181
298
|
if args.output or args.format in ("markdown", "json"):
|
|
182
299
|
if args.format == "json":
|
package/operate/cli/main.py
CHANGED
|
@@ -7,7 +7,7 @@ import sys
|
|
|
7
7
|
from typing import List, Optional
|
|
8
8
|
|
|
9
9
|
from operate.cli import __version__
|
|
10
|
-
from operate.cli.commands import demo, devsecops, fix, init, protect, scan, simulate, test
|
|
10
|
+
from operate.cli.commands import demo, devsecops, evidence, fix, init, inventory, protect, scan, simulate, test
|
|
11
11
|
from operate.cli.ui import print_banner
|
|
12
12
|
|
|
13
13
|
|
|
@@ -27,6 +27,8 @@ Quick Start Workflow:
|
|
|
27
27
|
6. Remediate vulnerabilities: komagu fix ./legacy-app --dry-run
|
|
28
28
|
7. Install AI audit agents: komagu init --ai codex ~/audits/acme
|
|
29
29
|
8. Simulate breach scenarios: komagu simulate --scenario times-car-breach
|
|
30
|
+
9. Reconcile API inventory: komagu inventory --routes OUT/routes.json --openapi openapi.json
|
|
31
|
+
10. Bundle METI SCS evidence: komagu evidence --audit-dir OUT/ --output-dir ./evidence-pack
|
|
30
32
|
|
|
31
33
|
Documentation & Guides:
|
|
32
34
|
Comprehensive User Guide: docs/cli-guide.md
|
|
@@ -62,6 +64,8 @@ Learn more about a specific command:
|
|
|
62
64
|
fix.register_subcommand(subparsers)
|
|
63
65
|
init.register_subcommand(subparsers)
|
|
64
66
|
simulate.register_subcommand(subparsers)
|
|
67
|
+
inventory.register_subcommand(subparsers)
|
|
68
|
+
evidence.register_subcommand(subparsers)
|
|
65
69
|
|
|
66
70
|
help_parser = subparsers.add_parser(
|
|
67
71
|
"help",
|
|
@@ -71,8 +75,8 @@ Learn more about a specific command:
|
|
|
71
75
|
help_parser.add_argument(
|
|
72
76
|
"topic",
|
|
73
77
|
nargs="?",
|
|
74
|
-
choices=["scan", "protect", "test", "demo", "devsecops", "fix", "init", "simulate"],
|
|
75
|
-
help="Subcommand topic to display help for (scan, protect, test, demo, devsecops, fix, init, simulate)",
|
|
78
|
+
choices=["scan", "protect", "test", "demo", "devsecops", "fix", "init", "simulate", "inventory", "evidence"],
|
|
79
|
+
help="Subcommand topic to display help for (scan, protect, test, demo, devsecops, fix, init, simulate, inventory, evidence)",
|
|
76
80
|
)
|
|
77
81
|
|
|
78
82
|
return parser
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""API inventory and shadow/zombie API detection package."""
|
|
2
|
+
from operate.inventory.api_inventory import (
|
|
3
|
+
diff_api_inventory,
|
|
4
|
+
load_code_routes,
|
|
5
|
+
load_openapi_spec,
|
|
6
|
+
load_traffic_log,
|
|
7
|
+
normalize_path,
|
|
8
|
+
normalize_route_key,
|
|
9
|
+
render_inventory_html,
|
|
10
|
+
render_inventory_md,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"diff_api_inventory",
|
|
15
|
+
"load_code_routes",
|
|
16
|
+
"load_openapi_spec",
|
|
17
|
+
"load_traffic_log",
|
|
18
|
+
"normalize_path",
|
|
19
|
+
"normalize_route_key",
|
|
20
|
+
"render_inventory_html",
|
|
21
|
+
"render_inventory_md",
|
|
22
|
+
]
|