aws-security-mcp 0.3.1 → 0.4.1

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # aws-security-mcp
2
2
 
3
- MCP server for automated AWS security scanning — 7 modules, risk scoring, zero write operations.
3
+ MCP server for automated AWS security scanning — 14 modules, risk scoring, zero write operations.
4
4
 
5
5
  <!-- badges -->
6
6
  ![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)
@@ -9,13 +9,15 @@ MCP server for automated AWS security scanning — 7 modules, risk scoring, zero
9
9
 
10
10
  ## Features
11
11
 
12
- - **7 Security Scan Modules** — Security Groups, S3, IAM, CloudTrail, RDS, EBS, VPC
12
+ - **14 Security Scan Modules** — 10 unique scanners + 4 aggregation scanners (Security Hub, GuardDuty, Inspector, Trusted Advisor)
13
13
  - **Risk Scoring** — every finding scored 0-10 with severity (CRITICAL/HIGH/MEDIUM/LOW) and priority (P0-P3)
14
14
  - **100% Read-Only** — uses only Describe/Get/List API calls; never modifies your AWS resources
15
- - **Parallel Execution** — all 7 modules run concurrently via `Promise.allSettled`
16
- - **Markdown Report Generation** — structured report with executive summary, findings by severity, and prioritized recommendations
15
+ - **Parallel Execution** — all modules run concurrently via `Promise.allSettled`
16
+ - **Report Generation** — Markdown, professional HTML, and MLPS Level 3 compliance reports
17
+ - **React Dashboard** — local or S3-hosted dashboard with 30-day trend charts
17
18
  - **MCP Resources** — embedded security rules and risk scoring model documentation
18
19
  - **MCP Prompts** — pre-built workflows for full scans and finding analysis
20
+ - **China Region Support** — full support for aws-cn partition
19
21
 
20
22
  ## Quick Start
21
23
 
@@ -105,16 +107,30 @@ Or use the built-in `security-scan` prompt for a guided workflow.
105
107
 
106
108
  | Tool | Description |
107
109
  |------|-------------|
108
- | `scan_all` | Run all 7 security scanners in parallel |
109
- | `scan_sg` | Scan EC2 security groups for overly permissive rules |
110
- | `scan_s3` | Check S3 buckets for public access, encryption, versioning |
111
- | `scan_iam` | Audit IAM users, root account, access keys, policies |
112
- | `scan_cloudtrail` | Validate CloudTrail logging configuration |
113
- | `scan_rds` | Scan RDS instances for public access, encryption, backups |
114
- | `scan_ebs` | Check EBS volumes and snapshots for encryption and public sharing |
115
- | `scan_vpc` | Review VPC flow logs, default VPC usage, default security groups |
116
- | `generate_report` | Generate a Markdown report from scan results |
110
+ | `scan_all` | Run all 14 security scanners in parallel |
111
+ | `detect_services` | Detect enabled AWS security services and assess maturity |
112
+ | `scan_secret_exposure` | Check Lambda env vars and EC2 userData for exposed secrets |
113
+ | `scan_ssl_certificate` | Check ACM certificates for expiry and failed status |
114
+ | `scan_dns_dangling` | Detect dangling DNS records (subdomain takeover risk) |
115
+ | `scan_network_reachability` | Analyze true network reachability (SG + NACL rules) |
116
+ | `scan_iam_privilege_escalation` | Detect IAM privilege escalation paths |
117
+ | `scan_public_access_verify` | Verify actual public accessibility of resources |
118
+ | `scan_tag_compliance` | Check resources for required tags |
119
+ | `scan_idle_resources` | Find unused/idle resources |
120
+ | `scan_disaster_recovery` | Assess disaster recovery readiness |
121
+ | `scan_security_hub_findings` | Aggregate findings from AWS Security Hub |
122
+ | `scan_guardduty_findings` | Aggregate findings from Amazon GuardDuty |
123
+ | `scan_inspector_findings` | Aggregate findings from Amazon Inspector |
124
+ | `scan_trusted_advisor_findings` | Aggregate findings from AWS Trusted Advisor |
125
+ | `scan_group` | Run a predefined group of scanners for a specific scenario |
126
+ | `list_groups` | List available scan groups |
117
127
  | `list_modules` | List available scan modules with descriptions |
128
+ | `generate_report` | Generate a Markdown report from scan results |
129
+ | `generate_html_report` | Generate a professional HTML report |
130
+ | `generate_mlps3_report` | Generate a MLPS Level 3 compliance report |
131
+ | `generate_mlps3_html_report` | Generate a MLPS Level 3 HTML compliance report |
132
+ | `generate_maturity_report` | Generate a security maturity assessment |
133
+ | `save_results` | Save scan results for the dashboard |
118
134
 
119
135
  All tools accept an optional `region` parameter (defaults to the server's configured region).
120
136
 
@@ -130,38 +146,64 @@ Attach this policy to the IAM user or role running the scanner. All actions are
130
146
  "Sid": "SecurityScannerReadOnly",
131
147
  "Effect": "Allow",
132
148
  "Action": [
133
- "ec2:DescribeSecurityGroups",
149
+ "acm:DescribeCertificate",
150
+ "acm:ListCertificates",
151
+
152
+ "config:DescribeConfigurationRecorders",
153
+
154
+ "ec2:DescribeAddresses",
134
155
  "ec2:DescribeInstances",
135
- "ec2:DescribeVolumes",
156
+ "ec2:DescribeNetworkAcls",
157
+ "ec2:DescribeSecurityGroups",
136
158
  "ec2:DescribeSnapshots",
137
159
  "ec2:DescribeSnapshotAttribute",
160
+ "ec2:DescribeVolumes",
138
161
  "ec2:GetEbsEncryptionByDefault",
139
- "ec2:DescribeVpcs",
140
- "ec2:DescribeFlowLogs",
141
162
 
142
- "s3:ListAllMyBuckets",
143
- "s3:GetBucketPublicAccessBlock",
144
- "s3:GetBucketAcl",
145
- "s3:GetBucketPolicy",
146
- "s3:GetBucketPolicyStatus",
147
- "s3:GetEncryptionConfiguration",
148
- "s3:GetBucketVersioning",
163
+ "guardduty:GetDetector",
164
+ "guardduty:ListDetectors",
165
+ "guardduty:ListFindings",
166
+ "guardduty:GetFindings",
149
167
 
150
168
  "iam:GetAccountSummary",
151
169
  "iam:ListUsers",
170
+ "iam:ListRoles",
152
171
  "iam:ListAccessKeys",
153
172
  "iam:GetAccessKeyLastUsed",
154
173
  "iam:ListAttachedUserPolicies",
155
- "iam:GenerateCredentialReport",
156
- "iam:GetCredentialReport",
174
+ "iam:ListAttachedRolePolicies",
175
+ "iam:ListUserPolicies",
176
+ "iam:ListRolePolicies",
177
+ "iam:GetUserPolicy",
178
+ "iam:GetRolePolicy",
179
+ "iam:GetPolicy",
180
+ "iam:GetPolicyVersion",
181
+
182
+ "inspector2:ListFindings",
183
+
184
+ "lambda:ListFunctions",
185
+ "lambda:GetFunction",
157
186
 
158
- "cloudtrail:DescribeTrails",
159
- "cloudtrail:GetTrailStatus",
160
- "cloudtrail:GetEventSelectors",
187
+ "macie2:GetMacieSession",
161
188
 
162
189
  "rds:DescribeDBInstances",
163
190
 
164
- "sts:GetCallerIdentity"
191
+ "route53:ListHostedZones",
192
+ "route53:ListResourceRecordSets",
193
+
194
+ "s3:GetBucketPublicAccessBlock",
195
+ "s3:GetBucketVersioning",
196
+ "s3:GetBucketReplication",
197
+ "s3:GetBucketTagging",
198
+ "s3:ListAllMyBuckets",
199
+
200
+ "securityhub:DescribeHub",
201
+ "securityhub:GetFindings",
202
+
203
+ "sts:GetCallerIdentity",
204
+
205
+ "support:DescribeTrustedAdvisorChecks",
206
+ "support:DescribeTrustedAdvisorCheckResult"
165
207
  ],
166
208
  "Resource": "*"
167
209
  }
@@ -171,15 +213,29 @@ Attach this policy to the IAM user or role running the scanner. All actions are
171
213
 
172
214
  ## Scan Modules
173
215
 
216
+ ### Unique Scanners (10)
217
+
174
218
  | Module | What It Checks | Risk Score Range |
175
219
  |--------|---------------|-----------------|
176
- | **Security Groups** | Open ports to 0.0.0.0/0 (SSH, RDP, databases), all-ports rules | 7.5 - 9.5 |
177
- | **S3** | Public ACLs, public bucket policies, Block Public Access, encryption, versioning | 3.0 - 9.5 |
178
- | **IAM** | Root MFA, root access keys, inactive users, old access keys, over-permissive policies | 5.0 - 10.0 |
179
- | **CloudTrail** | Trail existence, multi-region logging, log validation, CloudWatch integration, management events | 5.5 - 9.5 |
180
- | **RDS** | Public accessibility, storage encryption, backup retention, deletion protection | 4.0 - 8.0 |
181
- | **EBS** | Default encryption, unencrypted volumes, unencrypted snapshots, public snapshots | 5.5 - 9.5 |
182
- | **VPC** | Default VPC usage, VPC Flow Logs, default security group rules | 5.5 - 7.0 |
220
+ | **Service Detection** | Enabled security services (Security Hub, GuardDuty, Inspector, Config, Macie) and maturity level | 5.0 - 7.5 |
221
+ | **Secret Exposure** | Lambda env vars and EC2 userData for exposed secrets (AWS keys, private keys, passwords) | 7.0 - 9.5 |
222
+ | **SSL Certificate** | ACM certificate expiry, failed status, upcoming renewals | 5.5 - 9.0 |
223
+ | **Dangling DNS** | Route53 CNAME records pointing to non-existent resources (subdomain takeover) | 7.0 - 8.5 |
224
+ | **Network Reachability** | True network reachability combining Security Group + NACL rules for public EC2 instances | 5.5 - 9.5 |
225
+ | **IAM Privilege Escalation** | Privilege escalation paths via policy manipulation, role creation, or service abuse | 7.0 - 9.5 |
226
+ | **Public Access Verify** | Actual public accessibility of resources marked as public (S3 HTTP, RDS DNS) | 7.0 - 9.0 |
227
+ | **Tag Compliance** | Required tags (Environment, Project, Owner) on EC2, RDS, S3 resources | 3.0 - 5.0 |
228
+ | **Idle Resources** | Unused resources (unattached EBS, unused EIPs, stopped instances, unused SGs) | 3.0 - 5.0 |
229
+ | **Disaster Recovery** | RDS Multi-AZ & backups, EBS snapshot coverage, S3 versioning & replication | 4.0 - 7.5 |
230
+
231
+ ### Aggregation Scanners (4)
232
+
233
+ | Module | Source Service | Risk Score Range |
234
+ |--------|---------------|-----------------|
235
+ | **Security Hub Findings** | AWS Security Hub (FSBP, CIS, PCI DSS) | 3.0 - 9.5 |
236
+ | **GuardDuty Findings** | Amazon GuardDuty threat detection | 3.0 - 9.5 |
237
+ | **Inspector Findings** | Amazon Inspector vulnerability scanning | 3.0 - 9.5 |
238
+ | **Trusted Advisor Findings** | AWS Trusted Advisor security checks (requires Business/Enterprise Support) | 5.5 - 8.0 |
183
239
 
184
240
  ### Risk Scoring
185
241
 
@@ -190,6 +246,26 @@ Attach this policy to the IAM user or role running the scanner. All actions are
190
246
  | 4.0 - 6.9 | MEDIUM | P2 |
191
247
  | 0.0 - 3.9 | LOW | P3 |
192
248
 
249
+ ## Scan Groups
250
+
251
+ Pre-defined scanner groupings for common scenarios:
252
+
253
+ | Group | Description | Modules |
254
+ |-------|-------------|---------|
255
+ | `mlps3_precheck` | GB/T 22239-2019 等保三级预检 | 12 modules |
256
+ | `hw_defense` | 护网蓝队加固 | 7 modules |
257
+ | `exposure` | 公网暴露面评估 | 5 modules |
258
+ | `pre_launch` | 生产上线前检查 | ALL modules |
259
+ | `aggregation` | 安全服务聚合 | 4 modules |
260
+ | `new_account_baseline` | 新账户基线检查 | 5 modules |
261
+ | `disaster_recovery` | 灾备评估 | 2 modules |
262
+ | `least_privilege` | 最小权限审计 | 2 modules |
263
+ | `idle_resources` | 闲置资源清理 | 2 modules |
264
+ | `tag_compliance` | 资源标签合规 | 1 module |
265
+ | `public_access_verify` | 公网可达性验证 | 1 module |
266
+
267
+ Use `list_groups` to see all available groups with their module lists.
268
+
193
269
  ## Output Format
194
270
 
195
271
  ### Scan Results (JSON)
@@ -198,7 +274,7 @@ Each scan tool returns structured JSON:
198
274
 
199
275
  ```json
200
276
  {
201
- "module": "security_group",
277
+ "module": "network_reachability",
202
278
  "status": "success",
203
279
  "resourcesScanned": 12,
204
280
  "findingsCount": 3,
@@ -206,10 +282,10 @@ Each scan tool returns structured JSON:
206
282
  "findings": [
207
283
  {
208
284
  "severity": "CRITICAL",
209
- "title": "Security group sg-abc123 allows SSH (22) from 0.0.0.0/0",
210
- "resourceType": "AWS::EC2::SecurityGroup",
211
- "resourceId": "sg-abc123",
212
- "resourceArn": "arn:aws:ec2:ap-northeast-1:123456789012:security-group/sg-abc123",
285
+ "title": "EC2 instance i-abc123 has SSH (22) reachable from 0.0.0.0/0",
286
+ "resourceType": "AWS::EC2::Instance",
287
+ "resourceId": "i-abc123",
288
+ "resourceArn": "arn:aws:ec2:ap-northeast-1:123456789012:instance/i-abc123",
213
289
  "region": "ap-northeast-1",
214
290
  "description": "...",
215
291
  "impact": "...",
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-slate-50:oklch(98.4% .003 247.858);--color-slate-100:oklch(96.8% .007 247.896);--color-slate-200:oklch(92.9% .013 255.508);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-800:oklch(27.9% .041 260.031);--color-slate-900:oklch(20.8% .042 265.755);--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--leading-tight:1.25;--radius-lg:.5rem;--ease-in:cubic-bezier(.4, 0, 1, 1);--ease-out:cubic-bezier(0, 0, .2, 1);--ease-in-out:cubic-bezier(.4, 0, .2, 1);--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.right-0{right:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.z-10{z-index:10}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-16{margin-top:calc(var(--spacing) * 16)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.table{display:table}.h-\[300px\]{height:300px}.h-screen{height:100vh}.min-h-screen{min-height:100vh}.w-64{width:calc(var(--spacing) * 64)}.w-full{width:100%}.flex-1{flex:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-lg{border-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-blue-500\/30{border-color:#3080ff4d}@supports (color:color-mix(in lab, red, red)){.border-blue-500\/30{border-color:color-mix(in oklab, var(--color-blue-500) 30%, transparent)}}.border-current{border-color:currentColor}.border-green-500\/30{border-color:#00c7584d}@supports (color:color-mix(in lab, red, red)){.border-green-500\/30{border-color:color-mix(in oklab, var(--color-green-500) 30%, transparent)}}.border-orange-500\/30{border-color:#fe6e004d}@supports (color:color-mix(in lab, red, red)){.border-orange-500\/30{border-color:color-mix(in oklab, var(--color-orange-500) 30%, transparent)}}.border-red-500\/30{border-color:#fb2c364d}@supports (color:color-mix(in lab, red, red)){.border-red-500\/30{border-color:color-mix(in oklab, var(--color-red-500) 30%, transparent)}}.border-slate-700{border-color:var(--color-slate-700)}.border-yellow-500\/30{border-color:#edb2004d}@supports (color:color-mix(in lab, red, red)){.border-yellow-500\/30{border-color:color-mix(in oklab, var(--color-yellow-500) 30%, transparent)}}.bg-blue-500\/20{background-color:#3080ff33}@supports (color:color-mix(in lab, red, red)){.bg-blue-500\/20{background-color:color-mix(in oklab, var(--color-blue-500) 20%, transparent)}}.bg-green-500\/20{background-color:#00c75833}@supports (color:color-mix(in lab, red, red)){.bg-green-500\/20{background-color:color-mix(in oklab, var(--color-green-500) 20%, transparent)}}.bg-orange-500\/20{background-color:#fe6e0033}@supports (color:color-mix(in lab, red, red)){.bg-orange-500\/20{background-color:color-mix(in oklab, var(--color-orange-500) 20%, transparent)}}.bg-red-500\/20{background-color:#fb2c3633}@supports (color:color-mix(in lab, red, red)){.bg-red-500\/20{background-color:color-mix(in oklab, var(--color-red-500) 20%, transparent)}}.bg-slate-800{background-color:var(--color-slate-800)}.bg-slate-800\/30{background-color:#1d293d4d}@supports (color:color-mix(in lab, red, red)){.bg-slate-800\/30{background-color:color-mix(in oklab, var(--color-slate-800) 30%, transparent)}}.bg-slate-800\/50{background-color:#1d293d80}@supports (color:color-mix(in lab, red, red)){.bg-slate-800\/50{background-color:color-mix(in oklab, var(--color-slate-800) 50%, transparent)}}.bg-slate-800\/80{background-color:#1d293dcc}@supports (color:color-mix(in lab, red, red)){.bg-slate-800\/80{background-color:color-mix(in oklab, var(--color-slate-800) 80%, transparent)}}.bg-slate-900{background-color:var(--color-slate-900)}.bg-yellow-500\/20{background-color:#edb20033}@supports (color:color-mix(in lab, red, red)){.bg-yellow-500\/20{background-color:color-mix(in oklab, var(--color-yellow-500) 20%, transparent)}}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-nowrap{white-space:nowrap}.text-blue-400{color:var(--color-blue-400)}.text-green-400{color:var(--color-green-400)}.text-orange-400{color:var(--color-orange-400)}.text-red-400{color:var(--color-red-400)}.text-slate-50{color:var(--color-slate-50)}.text-slate-100{color:var(--color-slate-100)}.text-slate-200{color:var(--color-slate-200)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-yellow-400{color:var(--color-yellow-400)}.capitalize{text-transform:capitalize}.italic{font-style:italic}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-in{--tw-ease:var(--ease-in);transition-timing-function:var(--ease-in)}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.hover\:border-slate-500:hover{border-color:var(--color-slate-500)}.hover\:bg-slate-700\/50:hover{background-color:#31415880}@supports (color:color-mix(in lab, red, red)){.hover\:bg-slate-700\/50:hover{background-color:color-mix(in oklab, var(--color-slate-700) 50%, transparent)}}.hover\:text-slate-200:hover{color:var(--color-slate-200)}}@media (width>=40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width>=48rem){.md\:col-span-2{grid-column:span 2/span 2}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width>=64rem){.lg\:mt-0{margin-top:calc(var(--spacing) * 0)}.lg\:ml-64{margin-left:calc(var(--spacing) * 64)}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}}body{color:#f8fafc;background-color:#0f172a;margin:0;font-family:Inter,system-ui,-apple-system,sans-serif}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:#1e293b}::-webkit-scrollbar-thumb{background:#475569;border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#64748b}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}