cyntrisec 0.1.7__py3-none-any.whl
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.
- cyntrisec/__init__.py +3 -0
- cyntrisec/__main__.py +6 -0
- cyntrisec/aws/__init__.py +6 -0
- cyntrisec/aws/collectors/__init__.py +17 -0
- cyntrisec/aws/collectors/ec2.py +30 -0
- cyntrisec/aws/collectors/iam.py +116 -0
- cyntrisec/aws/collectors/lambda_.py +45 -0
- cyntrisec/aws/collectors/network.py +70 -0
- cyntrisec/aws/collectors/rds.py +38 -0
- cyntrisec/aws/collectors/s3.py +68 -0
- cyntrisec/aws/collectors/usage.py +188 -0
- cyntrisec/aws/credentials.py +153 -0
- cyntrisec/aws/normalizers/__init__.py +17 -0
- cyntrisec/aws/normalizers/ec2.py +115 -0
- cyntrisec/aws/normalizers/iam.py +182 -0
- cyntrisec/aws/normalizers/lambda_.py +83 -0
- cyntrisec/aws/normalizers/network.py +225 -0
- cyntrisec/aws/normalizers/rds.py +130 -0
- cyntrisec/aws/normalizers/s3.py +184 -0
- cyntrisec/aws/relationship_builder.py +1359 -0
- cyntrisec/aws/scanner.py +303 -0
- cyntrisec/cli/__init__.py +5 -0
- cyntrisec/cli/analyze.py +747 -0
- cyntrisec/cli/ask.py +412 -0
- cyntrisec/cli/can.py +307 -0
- cyntrisec/cli/comply.py +226 -0
- cyntrisec/cli/cuts.py +231 -0
- cyntrisec/cli/diff.py +332 -0
- cyntrisec/cli/errors.py +105 -0
- cyntrisec/cli/explain.py +348 -0
- cyntrisec/cli/main.py +114 -0
- cyntrisec/cli/manifest.py +893 -0
- cyntrisec/cli/output.py +117 -0
- cyntrisec/cli/remediate.py +643 -0
- cyntrisec/cli/report.py +462 -0
- cyntrisec/cli/scan.py +207 -0
- cyntrisec/cli/schemas.py +391 -0
- cyntrisec/cli/serve.py +164 -0
- cyntrisec/cli/setup.py +260 -0
- cyntrisec/cli/validate.py +101 -0
- cyntrisec/cli/waste.py +323 -0
- cyntrisec/core/__init__.py +31 -0
- cyntrisec/core/business_config.py +110 -0
- cyntrisec/core/business_logic.py +131 -0
- cyntrisec/core/compliance.py +437 -0
- cyntrisec/core/cost_estimator.py +301 -0
- cyntrisec/core/cuts.py +360 -0
- cyntrisec/core/diff.py +361 -0
- cyntrisec/core/graph.py +202 -0
- cyntrisec/core/paths.py +830 -0
- cyntrisec/core/schema.py +317 -0
- cyntrisec/core/simulator.py +371 -0
- cyntrisec/core/waste.py +309 -0
- cyntrisec/mcp/__init__.py +5 -0
- cyntrisec/mcp/server.py +862 -0
- cyntrisec/storage/__init__.py +7 -0
- cyntrisec/storage/filesystem.py +344 -0
- cyntrisec/storage/memory.py +113 -0
- cyntrisec/storage/protocol.py +92 -0
- cyntrisec-0.1.7.dist-info/METADATA +672 -0
- cyntrisec-0.1.7.dist-info/RECORD +65 -0
- cyntrisec-0.1.7.dist-info/WHEEL +4 -0
- cyntrisec-0.1.7.dist-info/entry_points.txt +2 -0
- cyntrisec-0.1.7.dist-info/licenses/LICENSE +190 -0
- cyntrisec-0.1.7.dist-info/licenses/NOTICE +5 -0
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
"""
|
|
2
|
+
manifest command - Self-describing tool capabilities for AI agents.
|
|
3
|
+
|
|
4
|
+
This command enables AI agents to discover, understand, and invoke
|
|
5
|
+
Cyntrisec commands programmatically without parsing help text.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
cyntrisec manifest
|
|
9
|
+
cyntrisec manifest --command scan
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import typer
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
from typer.models import OptionInfo
|
|
17
|
+
|
|
18
|
+
from cyntrisec import __version__
|
|
19
|
+
from cyntrisec.cli.errors import EXIT_CODE_MAP, CyntriError, ErrorCode, handle_errors
|
|
20
|
+
from cyntrisec.cli.output import SCHEMA_VERSION, emit_agent_or_json, resolve_format
|
|
21
|
+
from cyntrisec.cli.schemas import ManifestResponse, schema_json
|
|
22
|
+
|
|
23
|
+
console = Console()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Command capability definitions
|
|
27
|
+
CAPABILITIES = [
|
|
28
|
+
{
|
|
29
|
+
"name": "scan",
|
|
30
|
+
"description": "Scan an AWS account for security issues and attack paths",
|
|
31
|
+
"parameters": [
|
|
32
|
+
{
|
|
33
|
+
"name": "role_arn",
|
|
34
|
+
"type": "string",
|
|
35
|
+
"required": False,
|
|
36
|
+
"description": "AWS IAM role ARN to assume for scanning (uses default credentials if not provided)",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "external_id",
|
|
40
|
+
"type": "string",
|
|
41
|
+
"required": False,
|
|
42
|
+
"description": "External ID for role assumption",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"name": "regions",
|
|
46
|
+
"type": "array",
|
|
47
|
+
"required": False,
|
|
48
|
+
"default": ["us-east-1"],
|
|
49
|
+
"description": "AWS regions to scan",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "profile",
|
|
53
|
+
"type": "string",
|
|
54
|
+
"required": False,
|
|
55
|
+
"description": "AWS CLI profile for base credentials",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "format",
|
|
59
|
+
"type": "string",
|
|
60
|
+
"required": False,
|
|
61
|
+
"default": "text",
|
|
62
|
+
"enum": ["text", "json", "agent"],
|
|
63
|
+
"description": "Output format",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
"output": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"properties": {
|
|
69
|
+
"scan_id": {"type": "string"},
|
|
70
|
+
"snapshot_id": {"type": "string"},
|
|
71
|
+
"account_id": {"type": "string"},
|
|
72
|
+
"regions": {"type": "array"},
|
|
73
|
+
"asset_count": {"type": "integer"},
|
|
74
|
+
"relationship_count": {"type": "integer"},
|
|
75
|
+
"finding_count": {"type": "integer"},
|
|
76
|
+
"attack_path_count": {"type": "integer"},
|
|
77
|
+
"warnings": {"type": "array"},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
"exit_codes": {"0": "success", "1": "scan completed with findings", "2": "error"},
|
|
81
|
+
"example": "cyntrisec scan --role-arn arn:aws:iam::123:role/Scanner",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "cuts",
|
|
85
|
+
"description": "Find minimal set of remediations that block all attack paths",
|
|
86
|
+
"parameters": [
|
|
87
|
+
{
|
|
88
|
+
"name": "max_cuts",
|
|
89
|
+
"type": "integer",
|
|
90
|
+
"required": False,
|
|
91
|
+
"default": 5,
|
|
92
|
+
"description": "Maximum number of remediations to return",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"name": "format",
|
|
96
|
+
"type": "string",
|
|
97
|
+
"required": False,
|
|
98
|
+
"default": "table",
|
|
99
|
+
"enum": ["table", "json", "agent"],
|
|
100
|
+
"description": "Output format",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "snapshot",
|
|
104
|
+
"type": "string",
|
|
105
|
+
"required": False,
|
|
106
|
+
"description": "Specific snapshot ID (default: latest)",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"name": "cost_source",
|
|
110
|
+
"type": "string",
|
|
111
|
+
"required": False,
|
|
112
|
+
"default": "estimate",
|
|
113
|
+
"description": "Cost data source: estimate (static), pricing-api, cost-explorer",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
"output": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"properties": {
|
|
119
|
+
"total_paths": {"type": "integer"},
|
|
120
|
+
"paths_blocked": {"type": "integer"},
|
|
121
|
+
"coverage": {"type": "number"},
|
|
122
|
+
"remediations": {"type": "array"},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
126
|
+
"example": "cyntrisec cuts --format json",
|
|
127
|
+
"suggested_after": ["scan"],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"name": "waste",
|
|
131
|
+
"description": "Analyze IAM roles for unused permissions (blast radius reduction)",
|
|
132
|
+
"parameters": [
|
|
133
|
+
{
|
|
134
|
+
"name": "days",
|
|
135
|
+
"type": "integer",
|
|
136
|
+
"required": False,
|
|
137
|
+
"default": 90,
|
|
138
|
+
"description": "Days threshold for considering a permission unused",
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "live",
|
|
142
|
+
"type": "boolean",
|
|
143
|
+
"required": False,
|
|
144
|
+
"default": False,
|
|
145
|
+
"description": "Fetch live usage data from AWS IAM Access Advisor",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "format",
|
|
149
|
+
"type": "string",
|
|
150
|
+
"required": False,
|
|
151
|
+
"default": "table",
|
|
152
|
+
"enum": ["table", "json", "agent"],
|
|
153
|
+
"description": "Output format",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "snapshot",
|
|
157
|
+
"type": "string",
|
|
158
|
+
"required": False,
|
|
159
|
+
"description": "Specific snapshot ID (default: latest)",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"name": "cost_source",
|
|
163
|
+
"type": "string",
|
|
164
|
+
"required": False,
|
|
165
|
+
"default": "estimate",
|
|
166
|
+
"description": "Cost data source: estimate (static), pricing-api, cost-explorer",
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"name": "max_roles",
|
|
170
|
+
"type": "integer",
|
|
171
|
+
"required": False,
|
|
172
|
+
"default": 20,
|
|
173
|
+
"description": "Maximum number of roles to analyze (API throttling)",
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
"output": {
|
|
177
|
+
"type": "object",
|
|
178
|
+
"properties": {
|
|
179
|
+
"total_permissions": {"type": "integer"},
|
|
180
|
+
"total_unused": {"type": "integer"},
|
|
181
|
+
"blast_radius_reduction": {"type": "number"},
|
|
182
|
+
"roles": {"type": "array"},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
186
|
+
"example": "cyntrisec waste --live --format json",
|
|
187
|
+
"suggested_after": ["scan"],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"name": "can",
|
|
191
|
+
"description": "Test if a principal can access a resource (IAM policy simulation)",
|
|
192
|
+
"parameters": [
|
|
193
|
+
{
|
|
194
|
+
"name": "principal",
|
|
195
|
+
"type": "string",
|
|
196
|
+
"required": True,
|
|
197
|
+
"description": "IAM principal (role/user name or ARN)",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"name": "access",
|
|
201
|
+
"type": "string",
|
|
202
|
+
"required": True,
|
|
203
|
+
"const": "access",
|
|
204
|
+
"description": "Literal 'access' keyword",
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"name": "resource",
|
|
208
|
+
"type": "string",
|
|
209
|
+
"required": True,
|
|
210
|
+
"description": "Target resource (ARN, bucket name, or s3://path)",
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"name": "action",
|
|
214
|
+
"type": "string",
|
|
215
|
+
"required": False,
|
|
216
|
+
"description": "Specific action to test (auto-detected if not provided)",
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"name": "live",
|
|
220
|
+
"type": "boolean",
|
|
221
|
+
"required": False,
|
|
222
|
+
"default": False,
|
|
223
|
+
"description": "Use AWS Policy Simulator API",
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"name": "format",
|
|
227
|
+
"type": "string",
|
|
228
|
+
"required": False,
|
|
229
|
+
"default": "text",
|
|
230
|
+
"enum": ["text", "json", "agent"],
|
|
231
|
+
"description": "Output format",
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"name": "snapshot",
|
|
235
|
+
"type": "string",
|
|
236
|
+
"required": False,
|
|
237
|
+
"description": "Specific snapshot ID (default: latest)",
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
"output": {
|
|
241
|
+
"type": "object",
|
|
242
|
+
"properties": {
|
|
243
|
+
"principal": {"type": "string"},
|
|
244
|
+
"resource": {"type": "string"},
|
|
245
|
+
"can_access": {"type": "boolean"},
|
|
246
|
+
"simulations": {"type": "array"},
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
"exit_codes": {"0": "access allowed", "1": "access denied", "2": "error"},
|
|
250
|
+
"example": "cyntrisec can ECforS access s3://prod-bucket --format json",
|
|
251
|
+
"suggested_after": ["scan", "cuts"],
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"name": "diff",
|
|
255
|
+
"description": "Compare two scan snapshots to detect changes and regressions",
|
|
256
|
+
"parameters": [
|
|
257
|
+
{
|
|
258
|
+
"name": "old",
|
|
259
|
+
"type": "string",
|
|
260
|
+
"required": False,
|
|
261
|
+
"description": "Old snapshot ID (default: second most recent)",
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"name": "new",
|
|
265
|
+
"type": "string",
|
|
266
|
+
"required": False,
|
|
267
|
+
"description": "New snapshot ID (default: most recent)",
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"name": "format",
|
|
271
|
+
"type": "string",
|
|
272
|
+
"required": False,
|
|
273
|
+
"default": "table",
|
|
274
|
+
"enum": ["table", "json", "agent"],
|
|
275
|
+
"description": "Output format",
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"name": "all",
|
|
279
|
+
"type": "boolean",
|
|
280
|
+
"required": False,
|
|
281
|
+
"default": False,
|
|
282
|
+
"description": "Show all changes including assets and relationships",
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
"output": {
|
|
286
|
+
"type": "object",
|
|
287
|
+
"properties": {
|
|
288
|
+
"has_regressions": {"type": "boolean"},
|
|
289
|
+
"has_improvements": {"type": "boolean"},
|
|
290
|
+
"summary": {"type": "object"},
|
|
291
|
+
"path_changes": {"type": "array"},
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
"exit_codes": {"0": "no regressions", "1": "regressions detected", "2": "error"},
|
|
295
|
+
"example": "cyntrisec diff --format json",
|
|
296
|
+
"suggested_after": ["scan"],
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"name": "comply",
|
|
300
|
+
"description": "Check compliance against CIS AWS Foundations or SOC 2",
|
|
301
|
+
"parameters": [
|
|
302
|
+
{
|
|
303
|
+
"name": "framework",
|
|
304
|
+
"type": "string",
|
|
305
|
+
"required": False,
|
|
306
|
+
"default": "cis-aws",
|
|
307
|
+
"enum": ["cis-aws", "soc2"],
|
|
308
|
+
"description": "Compliance framework",
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
"name": "format",
|
|
312
|
+
"type": "string",
|
|
313
|
+
"required": False,
|
|
314
|
+
"default": "table",
|
|
315
|
+
"enum": ["table", "json", "agent"],
|
|
316
|
+
"description": "Output format",
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"name": "snapshot",
|
|
320
|
+
"type": "string",
|
|
321
|
+
"required": False,
|
|
322
|
+
"description": "Specific snapshot ID (default: latest)",
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
"output": {
|
|
326
|
+
"type": "object",
|
|
327
|
+
"properties": {
|
|
328
|
+
"framework": {"type": "string"},
|
|
329
|
+
"compliance_score": {"type": "number"},
|
|
330
|
+
"passing": {"type": "integer"},
|
|
331
|
+
"failing": {"type": "integer"},
|
|
332
|
+
"controls": {"type": "array"},
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
"exit_codes": {"0": "fully compliant", "1": "compliance failures", "2": "error"},
|
|
336
|
+
"example": "cyntrisec comply --framework soc2 --format json",
|
|
337
|
+
"suggested_after": ["scan"],
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
"name": "analyze paths",
|
|
341
|
+
"description": "View discovered attack paths from the latest scan",
|
|
342
|
+
"parameters": [
|
|
343
|
+
{
|
|
344
|
+
"name": "format",
|
|
345
|
+
"type": "string",
|
|
346
|
+
"required": False,
|
|
347
|
+
"default": "table",
|
|
348
|
+
"enum": ["table", "json", "agent"],
|
|
349
|
+
"description": "Output format",
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"name": "scan",
|
|
353
|
+
"type": "string",
|
|
354
|
+
"required": False,
|
|
355
|
+
"description": "Scan ID (default: latest)",
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
"name": "min_risk",
|
|
359
|
+
"type": "number",
|
|
360
|
+
"required": False,
|
|
361
|
+
"default": 0.0,
|
|
362
|
+
"description": "Minimum risk score (0-1)",
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"name": "limit",
|
|
366
|
+
"type": "integer",
|
|
367
|
+
"required": False,
|
|
368
|
+
"default": 20,
|
|
369
|
+
"description": "Maximum number of paths to show",
|
|
370
|
+
},
|
|
371
|
+
],
|
|
372
|
+
"output": {
|
|
373
|
+
"type": "object",
|
|
374
|
+
"properties": {
|
|
375
|
+
"paths": {"type": "array"},
|
|
376
|
+
"total": {"type": "integer"},
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
380
|
+
"example": "cyntrisec analyze paths --format json",
|
|
381
|
+
"suggested_after": ["scan"],
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
"name": "analyze business",
|
|
385
|
+
"description": "Map business entrypoints vs attackable assets (waste = attackable - business)",
|
|
386
|
+
"parameters": [
|
|
387
|
+
{
|
|
388
|
+
"name": "entrypoints",
|
|
389
|
+
"type": "array",
|
|
390
|
+
"required": False,
|
|
391
|
+
"description": "Business entrypoint names/ARNs (comma-separated)",
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
"name": "business_entrypoint",
|
|
395
|
+
"type": "array",
|
|
396
|
+
"required": False,
|
|
397
|
+
"description": "Repeatable business entrypoint flags (--business-entrypoint)",
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
"name": "business_tags",
|
|
401
|
+
"type": "object",
|
|
402
|
+
"required": False,
|
|
403
|
+
"description": "Tag filters marking business assets",
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
"name": "business_config",
|
|
407
|
+
"type": "string",
|
|
408
|
+
"required": False,
|
|
409
|
+
"description": "Path to business config (JSON/YAML)",
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"name": "report",
|
|
413
|
+
"type": "boolean",
|
|
414
|
+
"required": False,
|
|
415
|
+
"default": False,
|
|
416
|
+
"description": "Emit full coverage report",
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"name": "format",
|
|
420
|
+
"type": "string",
|
|
421
|
+
"required": False,
|
|
422
|
+
"default": "table",
|
|
423
|
+
"enum": ["table", "json", "agent"],
|
|
424
|
+
"description": "Output format",
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
"output": {
|
|
428
|
+
"type": "object",
|
|
429
|
+
"properties": {
|
|
430
|
+
"entrypoints_found": {"type": "array"},
|
|
431
|
+
"attackable_count": {"type": "integer"},
|
|
432
|
+
"waste_candidate_count": {"type": "integer"},
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
436
|
+
"example": "cyntrisec analyze business --entrypoints web,api --format agent",
|
|
437
|
+
"suggested_after": ["scan"],
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
"name": "remediate",
|
|
441
|
+
"description": "Generate remediation plan or optionally execute Terraform (gated)",
|
|
442
|
+
"parameters": [
|
|
443
|
+
{
|
|
444
|
+
"name": "max_cuts",
|
|
445
|
+
"type": "integer",
|
|
446
|
+
"required": False,
|
|
447
|
+
"default": 5,
|
|
448
|
+
"description": "Maximum remediations to include",
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
"name": "apply",
|
|
452
|
+
"type": "boolean",
|
|
453
|
+
"required": False,
|
|
454
|
+
"default": False,
|
|
455
|
+
"description": "Write remediation plan to disk (safety stub)",
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"name": "dry_run",
|
|
459
|
+
"type": "boolean",
|
|
460
|
+
"required": False,
|
|
461
|
+
"default": False,
|
|
462
|
+
"description": "Simulate apply and write plan/IaC artifacts",
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
"name": "execute_terraform",
|
|
466
|
+
"type": "boolean",
|
|
467
|
+
"required": False,
|
|
468
|
+
"default": False,
|
|
469
|
+
"description": "UNSAFE: execute terraform apply locally. Requires --enable-unsafe-write-mode.",
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
"name": "terraform_plan",
|
|
473
|
+
"type": "boolean",
|
|
474
|
+
"required": False,
|
|
475
|
+
"default": False,
|
|
476
|
+
"description": "Run terraform init/plan against generated module",
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
"name": "terraform_output",
|
|
480
|
+
"type": "string",
|
|
481
|
+
"required": False,
|
|
482
|
+
"description": "Terraform hints output path",
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
"name": "enable_unsafe_write_mode",
|
|
486
|
+
"type": "boolean",
|
|
487
|
+
"required": False,
|
|
488
|
+
"description": "Required to allow --apply/--execute-terraform (defaults to off for safety)",
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
"name": "terraform_dir",
|
|
492
|
+
"type": "string",
|
|
493
|
+
"required": False,
|
|
494
|
+
"description": "Directory to write Terraform module",
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
"name": "output",
|
|
498
|
+
"type": "string",
|
|
499
|
+
"required": False,
|
|
500
|
+
"description": "Output path for remediation plan",
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
"name": "format",
|
|
504
|
+
"type": "string",
|
|
505
|
+
"required": False,
|
|
506
|
+
"default": "table",
|
|
507
|
+
"enum": ["table", "json", "agent"],
|
|
508
|
+
"description": "Output format",
|
|
509
|
+
},
|
|
510
|
+
],
|
|
511
|
+
"output": {
|
|
512
|
+
"type": "object",
|
|
513
|
+
"properties": {
|
|
514
|
+
"plan": {"type": "array"},
|
|
515
|
+
"coverage": {"type": "number"},
|
|
516
|
+
"paths_blocked": {"type": "integer"},
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
520
|
+
"example": "cyntrisec remediate --format agent",
|
|
521
|
+
"suggested_after": ["cuts", "analyze paths"],
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
"name": "ask",
|
|
525
|
+
"description": "Natural language interface to query scan results",
|
|
526
|
+
"parameters": [
|
|
527
|
+
{"name": "query", "type": "string", "required": True, "description": "NL question"},
|
|
528
|
+
{
|
|
529
|
+
"name": "format",
|
|
530
|
+
"type": "string",
|
|
531
|
+
"required": False,
|
|
532
|
+
"default": "text",
|
|
533
|
+
"enum": ["text", "json", "agent"],
|
|
534
|
+
"description": "Output format",
|
|
535
|
+
},
|
|
536
|
+
],
|
|
537
|
+
"output": {
|
|
538
|
+
"type": "object",
|
|
539
|
+
"properties": {
|
|
540
|
+
"intent": {"type": "string"},
|
|
541
|
+
"results": {"type": "object"},
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
545
|
+
"example": 'cyntrisec ask "what can reach the production database?" --format agent',
|
|
546
|
+
"suggested_after": ["scan", "analyze paths"],
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
"name": "report",
|
|
550
|
+
"description": "Generate HTML or JSON report from scan results",
|
|
551
|
+
"parameters": [
|
|
552
|
+
{
|
|
553
|
+
"name": "scan",
|
|
554
|
+
"type": "string",
|
|
555
|
+
"required": False,
|
|
556
|
+
"description": "Scan ID (default: latest)",
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
"name": "output",
|
|
560
|
+
"type": "string",
|
|
561
|
+
"required": False,
|
|
562
|
+
"default": "cyntrisec-report.html",
|
|
563
|
+
"description": "Output file path",
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
"name": "title",
|
|
567
|
+
"type": "string",
|
|
568
|
+
"required": False,
|
|
569
|
+
"description": "Report title",
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
"name": "format",
|
|
573
|
+
"type": "string",
|
|
574
|
+
"required": False,
|
|
575
|
+
"default": "html",
|
|
576
|
+
"enum": ["html", "json", "agent"],
|
|
577
|
+
"description": "Output format",
|
|
578
|
+
},
|
|
579
|
+
],
|
|
580
|
+
"output": {
|
|
581
|
+
"type": "object",
|
|
582
|
+
"properties": {
|
|
583
|
+
"snapshot_id": {"type": "string"},
|
|
584
|
+
"account_id": {"type": "string"},
|
|
585
|
+
"output_path": {"type": "string"},
|
|
586
|
+
"findings": {"type": "integer"},
|
|
587
|
+
"paths": {"type": "integer"},
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
591
|
+
"example": "cyntrisec report --output report.html",
|
|
592
|
+
"suggested_after": ["scan"],
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
"name": "validate-role",
|
|
596
|
+
"description": "Validate that an IAM role can be assumed",
|
|
597
|
+
"parameters": [
|
|
598
|
+
{
|
|
599
|
+
"name": "role_arn",
|
|
600
|
+
"type": "string",
|
|
601
|
+
"required": True,
|
|
602
|
+
"description": "IAM role ARN to validate",
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
"name": "external_id",
|
|
606
|
+
"type": "string",
|
|
607
|
+
"required": False,
|
|
608
|
+
"description": "External ID for role assumption",
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
"name": "profile",
|
|
612
|
+
"type": "string",
|
|
613
|
+
"required": False,
|
|
614
|
+
"description": "AWS CLI profile for base credentials",
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
"name": "format",
|
|
618
|
+
"type": "string",
|
|
619
|
+
"required": False,
|
|
620
|
+
"default": "text",
|
|
621
|
+
"enum": ["text", "json", "agent"],
|
|
622
|
+
"description": "Output format",
|
|
623
|
+
},
|
|
624
|
+
],
|
|
625
|
+
"output": {
|
|
626
|
+
"type": "object",
|
|
627
|
+
"properties": {
|
|
628
|
+
"success": {"type": "boolean"},
|
|
629
|
+
"role_arn": {"type": "string"},
|
|
630
|
+
"account": {"type": "string"},
|
|
631
|
+
"arn": {"type": "string"},
|
|
632
|
+
"user_id": {"type": "string"},
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
"exit_codes": {"0": "role valid", "1": "role invalid", "2": "error"},
|
|
636
|
+
"example": "cyntrisec validate-role --role-arn arn:aws:iam::123:role/Scanner",
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
"name": "setup iam",
|
|
640
|
+
"description": "Generate IAM role template for Cyntrisec scanning",
|
|
641
|
+
"parameters": [
|
|
642
|
+
{
|
|
643
|
+
"name": "account_id",
|
|
644
|
+
"type": "string",
|
|
645
|
+
"required": True,
|
|
646
|
+
"description": "AWS account ID (12 digits)",
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
"name": "role_name",
|
|
650
|
+
"type": "string",
|
|
651
|
+
"required": False,
|
|
652
|
+
"default": "CyntrisecReadOnly",
|
|
653
|
+
"description": "Name for the IAM role",
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
"name": "external_id",
|
|
657
|
+
"type": "string",
|
|
658
|
+
"required": False,
|
|
659
|
+
"description": "External ID for extra security",
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
"name": "format",
|
|
663
|
+
"type": "string",
|
|
664
|
+
"required": False,
|
|
665
|
+
"default": "terraform",
|
|
666
|
+
"enum": ["terraform", "cloudformation", "policy"],
|
|
667
|
+
"description": "Template format",
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
"name": "output",
|
|
671
|
+
"type": "string",
|
|
672
|
+
"required": False,
|
|
673
|
+
"description": "Output file path",
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
"name": "output_format",
|
|
677
|
+
"type": "string",
|
|
678
|
+
"required": False,
|
|
679
|
+
"default": "text",
|
|
680
|
+
"enum": ["text", "json", "agent"],
|
|
681
|
+
"description": "Render format for CLI output",
|
|
682
|
+
},
|
|
683
|
+
],
|
|
684
|
+
"output": {
|
|
685
|
+
"type": "object",
|
|
686
|
+
"properties": {
|
|
687
|
+
"account_id": {"type": "string"},
|
|
688
|
+
"role_name": {"type": "string"},
|
|
689
|
+
"external_id": {"type": "string"},
|
|
690
|
+
"template_format": {"type": "string"},
|
|
691
|
+
"template": {"type": "string"},
|
|
692
|
+
"output_path": {"type": "string"},
|
|
693
|
+
},
|
|
694
|
+
},
|
|
695
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
696
|
+
"example": "cyntrisec setup iam 123456789012 --output role.tf",
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
"name": "explain",
|
|
700
|
+
"description": "Get natural language explanation of paths, controls, or findings",
|
|
701
|
+
"parameters": [
|
|
702
|
+
{
|
|
703
|
+
"name": "category",
|
|
704
|
+
"type": "string",
|
|
705
|
+
"required": True,
|
|
706
|
+
"enum": ["finding", "path", "control"],
|
|
707
|
+
"description": "Category to explain: finding, path, control",
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
"name": "identifier",
|
|
711
|
+
"type": "string",
|
|
712
|
+
"required": True,
|
|
713
|
+
"description": "Identifier of the item to explain",
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
"name": "format",
|
|
717
|
+
"type": "string",
|
|
718
|
+
"required": False,
|
|
719
|
+
"default": "text",
|
|
720
|
+
"enum": ["text", "json", "markdown", "agent"],
|
|
721
|
+
"description": "Output format",
|
|
722
|
+
},
|
|
723
|
+
],
|
|
724
|
+
"output": {
|
|
725
|
+
"type": "object",
|
|
726
|
+
"properties": {
|
|
727
|
+
"type": {"type": "string"},
|
|
728
|
+
"id": {"type": "string"},
|
|
729
|
+
"explanation": {"type": "object"},
|
|
730
|
+
},
|
|
731
|
+
},
|
|
732
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
733
|
+
"example": "cyntrisec explain finding security_group_open_to_world --format agent",
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
"name": "analyze findings",
|
|
737
|
+
"description": "View security findings from the latest scan",
|
|
738
|
+
"parameters": [
|
|
739
|
+
{
|
|
740
|
+
"name": "scan",
|
|
741
|
+
"type": "string",
|
|
742
|
+
"required": False,
|
|
743
|
+
"description": "Scan ID (default: latest)",
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
"name": "severity",
|
|
747
|
+
"type": "string",
|
|
748
|
+
"required": False,
|
|
749
|
+
"enum": ["critical", "high", "medium", "low", "info"],
|
|
750
|
+
"description": "Filter by severity",
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
"name": "format",
|
|
754
|
+
"type": "string",
|
|
755
|
+
"required": False,
|
|
756
|
+
"default": "table",
|
|
757
|
+
"enum": ["table", "json", "agent"],
|
|
758
|
+
"description": "Output format",
|
|
759
|
+
},
|
|
760
|
+
],
|
|
761
|
+
"output": {
|
|
762
|
+
"type": "object",
|
|
763
|
+
"properties": {
|
|
764
|
+
"findings": {"type": "array"},
|
|
765
|
+
"total": {"type": "integer"},
|
|
766
|
+
"filter": {"type": "string"},
|
|
767
|
+
},
|
|
768
|
+
},
|
|
769
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
770
|
+
"example": "cyntrisec analyze findings --severity high --format json",
|
|
771
|
+
"suggested_after": ["scan"],
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
"name": "analyze stats",
|
|
775
|
+
"description": "View summary statistics from the latest scan",
|
|
776
|
+
"parameters": [
|
|
777
|
+
{
|
|
778
|
+
"name": "scan",
|
|
779
|
+
"type": "string",
|
|
780
|
+
"required": False,
|
|
781
|
+
"description": "Scan ID (default: latest)",
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
"name": "format",
|
|
785
|
+
"type": "string",
|
|
786
|
+
"required": False,
|
|
787
|
+
"default": "text",
|
|
788
|
+
"enum": ["text", "json", "agent"],
|
|
789
|
+
"description": "Output format",
|
|
790
|
+
},
|
|
791
|
+
],
|
|
792
|
+
"output": {
|
|
793
|
+
"type": "object",
|
|
794
|
+
"properties": {
|
|
795
|
+
"snapshot_id": {"type": "string"},
|
|
796
|
+
"scan_id": {"type": "string"},
|
|
797
|
+
"account_id": {"type": "string"},
|
|
798
|
+
"asset_count": {"type": "integer"},
|
|
799
|
+
"relationship_count": {"type": "integer"},
|
|
800
|
+
"finding_count": {"type": "integer"},
|
|
801
|
+
"path_count": {"type": "integer"},
|
|
802
|
+
"regions": {"type": "array"},
|
|
803
|
+
"status": {"type": "string"},
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
"exit_codes": {"0": "success", "2": "error"},
|
|
807
|
+
"example": "cyntrisec analyze stats --format json",
|
|
808
|
+
"suggested_after": ["scan"],
|
|
809
|
+
},
|
|
810
|
+
]
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
@handle_errors
|
|
814
|
+
def manifest_cmd(
|
|
815
|
+
command: str | None = typer.Option(
|
|
816
|
+
None,
|
|
817
|
+
"--command",
|
|
818
|
+
"-c",
|
|
819
|
+
help="Get manifest for a specific command",
|
|
820
|
+
),
|
|
821
|
+
format: str | None = typer.Option(
|
|
822
|
+
None,
|
|
823
|
+
"--format",
|
|
824
|
+
"-f",
|
|
825
|
+
help="Output format: json, agent (defaults to json when piped)",
|
|
826
|
+
),
|
|
827
|
+
):
|
|
828
|
+
"""
|
|
829
|
+
Output machine-readable manifest of tool capabilities.
|
|
830
|
+
|
|
831
|
+
Use this command to discover what Cyntrisec can do and how to invoke it.
|
|
832
|
+
This is designed for AI agents to understand the tool programmatically.
|
|
833
|
+
"""
|
|
834
|
+
if isinstance(command, OptionInfo):
|
|
835
|
+
command = None
|
|
836
|
+
if isinstance(format, OptionInfo):
|
|
837
|
+
format = None
|
|
838
|
+
output_format = resolve_format(
|
|
839
|
+
format,
|
|
840
|
+
default_tty="json",
|
|
841
|
+
allowed=["json", "agent"],
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
schemas = schema_json()
|
|
845
|
+
|
|
846
|
+
if command:
|
|
847
|
+
# Find specific command
|
|
848
|
+
for cap in CAPABILITIES:
|
|
849
|
+
if cap["name"] == command:
|
|
850
|
+
emit_agent_or_json(output_format, cap)
|
|
851
|
+
return
|
|
852
|
+
raise CyntriError(
|
|
853
|
+
error_code=ErrorCode.INVALID_QUERY,
|
|
854
|
+
message=f"Command '{command}' not found",
|
|
855
|
+
exit_code=EXIT_CODE_MAP["usage"],
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
# Full manifest
|
|
859
|
+
manifest = {
|
|
860
|
+
"name": "cyntrisec",
|
|
861
|
+
"version": __version__,
|
|
862
|
+
"description": "AWS capability graph analysis and attack path discovery",
|
|
863
|
+
"agentic_features": {
|
|
864
|
+
"json_output": True,
|
|
865
|
+
"structured_errors": True,
|
|
866
|
+
"exit_codes": True,
|
|
867
|
+
"suggested_actions": True,
|
|
868
|
+
"artifact_paths": True,
|
|
869
|
+
},
|
|
870
|
+
"schemas": {
|
|
871
|
+
"version": SCHEMA_VERSION,
|
|
872
|
+
"base_url": "https://cyntrisec.dev/schemas/cli",
|
|
873
|
+
"responses": schemas,
|
|
874
|
+
},
|
|
875
|
+
"capabilities": CAPABILITIES,
|
|
876
|
+
"usage_pattern": [
|
|
877
|
+
"1. Run 'cyntrisec scan' to collect AWS data",
|
|
878
|
+
"2. Run 'cyntrisec analyze paths' to see attack paths",
|
|
879
|
+
"3. Run 'cyntrisec cuts' to get prioritized fixes",
|
|
880
|
+
"4. Run 'cyntrisec can X access Y' to verify specific access",
|
|
881
|
+
],
|
|
882
|
+
"error_codes": [
|
|
883
|
+
ErrorCode.AWS_ACCESS_DENIED,
|
|
884
|
+
ErrorCode.AWS_THROTTLED,
|
|
885
|
+
ErrorCode.AWS_REGION_DISABLED,
|
|
886
|
+
ErrorCode.SNAPSHOT_NOT_FOUND,
|
|
887
|
+
ErrorCode.SCHEMA_MISMATCH,
|
|
888
|
+
ErrorCode.INVALID_QUERY,
|
|
889
|
+
ErrorCode.INTERNAL_ERROR,
|
|
890
|
+
],
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
emit_agent_or_json(output_format, manifest, schema=ManifestResponse)
|