@hoanghainh/komagu 2.2.1 → 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.
Files changed (36) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/assess/lsa/scripts/attack_chains.py +573 -0
  3. package/assess/lsa/scripts/baseline.py +14 -0
  4. package/assess/lsa/scripts/findings_to_sarif.py +11 -0
  5. package/assess/lsa/scripts/forecast.py +544 -0
  6. package/assess/lsa/scripts/merge_findings.py +14 -0
  7. package/assess/lsa/scripts/render_report.py +86 -0
  8. package/assess/lsa/tests/test_attack_chains.py +292 -0
  9. package/assess/lsa/tests/test_forecast.py +160 -0
  10. package/contracts/schemas/api-inventory-diff.schema.json +213 -0
  11. package/contracts/schemas/attack-chain.schema.json +181 -0
  12. package/contracts/schemas/bas-scenario.schema.json +217 -0
  13. package/contracts/schemas/evidence-manifest.schema.json +117 -0
  14. package/contracts/schemas/finding.schema.json +7 -0
  15. package/contracts/schemas/forecast.schema.json +172 -0
  16. package/governance/scs/__init__.py +21 -0
  17. package/governance/scs/mapping.py +133 -0
  18. package/governance/scs/pack.py +354 -0
  19. package/operate/cli/__init__.py +1 -1
  20. package/operate/cli/commands/evidence.py +138 -0
  21. package/operate/cli/commands/inventory.py +187 -0
  22. package/operate/cli/commands/simulate.py +175 -58
  23. package/operate/cli/main.py +7 -3
  24. package/operate/inventory/__init__.py +22 -0
  25. package/operate/inventory/api_inventory.py +566 -0
  26. package/operate/simulate/__init__.py +16 -0
  27. package/operate/simulate/engine.py +370 -0
  28. package/operate/simulate/loader.py +158 -0
  29. package/operate/simulate/scenarios/idc-frontier-ransomware.json +47 -0
  30. package/operate/simulate/scenarios/metabase-cve-2026-72898.json +84 -0
  31. package/operate/simulate/scenarios/times-car-breach.json +74 -0
  32. package/operate/simulate/scenarios.py +52 -0
  33. package/operate/tests/test_api_inventory.py +239 -0
  34. package/operate/tests/test_declarative_scenarios.py +169 -0
  35. package/operate/tests/test_evidence_pack.py +128 -0
  36. package/package.json +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
- # Simulate Metabase unauthenticated SQL injection probe
51
- komagu simulate --scenario metabase-cve-2026-72898 --target http://localhost:5001
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 for Board of Directors
57
- komagu simulate --target http://localhost:5001 --output ./executive-bas-report.md --format markdown
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
- choices=["all", "times-car-breach", "metabase-cve-2026-72898", "idc-frontier-ransomware"],
65
- help="Attack scenario to execute (default: all)",
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
- scenarios_to_run = [args.scenario] if args.scenario != "all" else [
107
- "times-car-breach",
108
- "metabase-cve-2026-72898",
109
- "idc-frontier-ransomware",
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
- elif sc == "idc-frontier-ransomware":
144
- # A fresh private dir, not a fixed /tmp path another local user could pre-create or symlink
145
- storage_path = (
146
- args.target if not args.target.startswith("http") else tempfile.mkdtemp(prefix="komagu-backup-sim-")
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
- step_info(f"Launching Scenario: IDC Frontier Cloud Storage Ransomware Simulation ({storage_path})")
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"IDC Frontier Simulation: {res.resilience_score}% resilient (WORM lock enforced)")
259
+ step_ok(f"{sc_data.get('title', sid)}: {res.resilience_score}% resilient (0 breaches)")
154
260
  else:
155
- step_warn(f"IDC Frontier Simulation: Writable storage ({res.resilience_score}% resilient)")
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":
@@ -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
+ ]