aws-security-mcp 0.7.4 → 0.7.6

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.
@@ -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-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--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}}}@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-indigo-300:oklch(78.5% .115 274.713);--color-indigo-700:oklch(45.7% .24 277.023);--color-indigo-950:oklch(25.7% .09 281.288);--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-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;--leading-relaxed:1.625;--radius-lg:.5rem;--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}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.end{inset-inline-end:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.top-16{top:calc(var(--spacing) * 16)}.right-0{right:calc(var(--spacing) * 0)}.bottom-0{bottom: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-1{margin-top:calc(var(--spacing) * 1)}.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}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-\[300px\]{height:300px}.min-h-screen{min-height:100vh}.w-5{width:calc(var(--spacing) * 5)}.w-56{width:calc(var(--spacing) * 56)}.w-full{width:100%}.min-w-0{min-width:calc(var(--spacing) * 0)}.min-w-\[200px\]{min-width:200px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.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}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,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-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.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-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * 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-indigo-700\/50{border-color:#432dd780}@supports (color:color-mix(in lab, red, red)){.border-indigo-700\/50{border-color:color-mix(in oklab, var(--color-indigo-700) 50%, 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-600{border-color:var(--color-slate-600)}.border-slate-700{border-color:var(--color-slate-700)}.border-transparent{border-color:#0000}.border-yellow-500\/20{border-color:#edb20033}@supports (color:color-mix(in lab, red, red)){.border-yellow-500\/20{border-color:color-mix(in oklab, var(--color-yellow-500) 20%, transparent)}}.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-700{background-color:var(--color-slate-700)}.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\/5{background-color:#edb2000d}@supports (color:color-mix(in lab, red, red)){.bg-yellow-500\/5{background-color:color-mix(in oklab, var(--color-yellow-500) 5%, transparent)}}.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)}}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-slate-800{--tw-gradient-from:var(--color-slate-800);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.to-indigo-950{--tw-gradient-to:var(--color-indigo-950);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position))}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.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{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-8{padding-block:calc(var(--spacing) * 8)}.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-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.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}.whitespace-pre-wrap{white-space:pre-wrap}.text-blue-400{color:var(--color-blue-400)}.text-green-400{color:var(--color-green-400)}.text-indigo-300{color:var(--color-indigo-300)}.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)}.text-yellow-400\/80{color:#fac800cc}@supports (color:color-mix(in lab, red, red)){.text-yellow-400\/80{color:color-mix(in oklab, var(--color-yellow-400) 80%, transparent)}}.text-yellow-500\/60{color:#edb20099}@supports (color:color-mix(in lab, red, red)){.text-yellow-500\/60{color:color-mix(in oklab, var(--color-yellow-500) 60%, transparent)}}.placeholder-slate-500::placeholder{color:var(--color-slate-500)}.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))}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.hover\:border-slate-400:hover{border-color:var(--color-slate-400)}.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)}}.focus\:border-blue-500:focus{border-color:var(--color-blue-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}@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\:flex{display:flex}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,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-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"<color>";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"<length-percentage>";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"<length-percentage>";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"<length-percentage>";inherits:false;initial-value:100%}@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}
@@ -5,8 +5,8 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>AWS Security Dashboard</title>
8
- <script type="module" crossorigin src="/assets/index-DsWFAp9v.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-BXloWmhE.css">
8
+ <script type="module" crossorigin src="/assets/index-BoNhDdVm.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-DY4x_jab.css">
10
10
  </head>
11
11
  <body class="bg-slate-900 text-slate-50">
12
12
  <div id="root"></div>
@@ -205,7 +205,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
205
205
  import { z } from "zod";
206
206
 
207
207
  // src/version.ts
208
- var VERSION = "0.7.4";
208
+ var VERSION = "0.7.6";
209
209
 
210
210
  // src/utils/aws-client.ts
211
211
  import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
@@ -280,8 +280,7 @@ async function listOrgAccounts(region) {
280
280
  return accounts;
281
281
  }
282
282
 
283
- // src/scanners/runner.ts
284
- var DEFAULT_CONCURRENCY = 5;
283
+ // src/utils/concurrency.ts
285
284
  async function runWithConcurrency(tasks, limit) {
286
285
  const results = [];
287
286
  const executing = /* @__PURE__ */ new Set();
@@ -300,6 +299,9 @@ async function runWithConcurrency(tasks, limit) {
300
299
  await Promise.all(executing);
301
300
  return results;
302
301
  }
302
+
303
+ // src/scanners/runner.ts
304
+ var DEFAULT_CONCURRENCY = 5;
303
305
  var AGGREGATION_MODULES = /* @__PURE__ */ new Set([
304
306
  "security_hub_findings",
305
307
  "guardduty_findings",
@@ -870,24 +872,6 @@ import {
870
872
  DescribeInstanceAttributeCommand
871
873
  } from "@aws-sdk/client-ec2";
872
874
  var USERDATA_CONCURRENCY = 5;
873
- async function runWithConcurrency2(tasks, limit) {
874
- const results = [];
875
- const executing = /* @__PURE__ */ new Set();
876
- for (let i = 0; i < tasks.length; i++) {
877
- const idx = i;
878
- const p = tasks[idx]().then((value) => {
879
- results[idx] = { status: "fulfilled", value };
880
- }).catch((reason) => {
881
- results[idx] = { status: "rejected", reason };
882
- }).finally(() => {
883
- executing.delete(p);
884
- });
885
- executing.add(p);
886
- if (executing.size >= limit) await Promise.race(executing);
887
- }
888
- await Promise.all(executing);
889
- return results;
890
- }
891
875
  var SECRET_PATTERNS = [
892
876
  { name: "AWS Access Key", pattern: /AKIA[0-9A-Z]{16}/, matchType: "value" },
893
877
  { name: "Private Key", pattern: /-----BEGIN.*PRIVATE KEY-----/, matchType: "value" },
@@ -998,7 +982,7 @@ var SecretExposureScanner = class {
998
982
  const raw = attrResp.UserData?.Value;
999
983
  return { instId, userData: raw ? Buffer.from(raw, "base64").toString("utf-8") : void 0 };
1000
984
  });
1001
- const settled = await runWithConcurrency2(userDataTasks, USERDATA_CONCURRENCY);
985
+ const settled = await runWithConcurrency(userDataTasks, USERDATA_CONCURRENCY);
1002
986
  for (let i = 0; i < instances.length; i++) {
1003
987
  const result = settled[i];
1004
988
  const inst = instances[i];
@@ -1978,7 +1962,8 @@ async function getBucketRegion(client, bucketName, defaultRegion, warnings) {
1978
1962
  const resp = await client.send(
1979
1963
  new GetBucketLocationCommand({ Bucket: bucketName })
1980
1964
  );
1981
- const loc = String(resp.LocationConstraint ?? "") || "us-east-1";
1965
+ const rawLoc = String(resp.LocationConstraint ?? "") || "us-east-1";
1966
+ const loc = rawLoc === "EU" ? "eu-west-1" : rawLoc;
1982
1967
  return loc;
1983
1968
  } catch (e) {
1984
1969
  const msg = e instanceof Error ? e.message : String(e);
@@ -3934,6 +3919,7 @@ var zhI18n = {
3934
3919
  // Extended — MLPS extras
3935
3920
  // Markdown report
3936
3921
  executiveSummary: "\u6267\u884C\u6458\u8981",
3922
+ aiSummaryTitle: "AI \u5B89\u5168\u6001\u52BF\u603B\u7ED3",
3937
3923
  totalFindingsLabel: "\u53D1\u73B0\u603B\u6570",
3938
3924
  description: "\u63CF\u8FF0",
3939
3925
  priority: "\u4F18\u5148\u7EA7",
@@ -4243,6 +4229,7 @@ var enI18n = {
4243
4229
  // Extended \u2014 MLPS extras
4244
4230
  // Markdown report
4245
4231
  executiveSummary: "Executive Summary",
4232
+ aiSummaryTitle: "AI Security Summary",
4246
4233
  totalFindingsLabel: "Total Findings",
4247
4234
  description: "Description",
4248
4235
  priority: "Priority",
@@ -4504,6 +4491,14 @@ function generateMarkdownReport(scanResults, lang) {
4504
4491
  `- **${t.totalFindingsLabel}:** ${summary.totalFindings} (${SEVERITY_ICON.CRITICAL} ${summary.critical} ${t.critical} | ${SEVERITY_ICON.HIGH} ${summary.high} ${t.high} | ${SEVERITY_ICON.MEDIUM} ${summary.medium} ${t.medium} | ${SEVERITY_ICON.LOW} ${summary.low} ${t.low})`
4505
4492
  );
4506
4493
  lines.push("");
4494
+ if (scanResults.aiSummary && scanResults.aiSummary.trim()) {
4495
+ lines.push(`> \u2728 **${t.aiSummaryTitle}**`);
4496
+ lines.push(">");
4497
+ for (const ln of scanResults.aiSummary.trim().split(/\r?\n/)) {
4498
+ lines.push(`> ${ln}`);
4499
+ }
4500
+ lines.push("");
4501
+ }
4507
4502
  if (summary.totalFindings === 0) {
4508
4503
  lines.push(`## ${t.findingsBySeverity}`);
4509
4504
  lines.push("");
@@ -7281,6 +7276,14 @@ function generateMlps3Report(scanResults, lang) {
7281
7276
  lines.push(`## ${t.accountInfo}`);
7282
7277
  lines.push(`- ${t.account}: ${accountId} | ${t.region}: ${region} | ${t.scanTime}: ${scanTime}`);
7283
7278
  lines.push("");
7279
+ if (scanResults.aiSummary && scanResults.aiSummary.trim()) {
7280
+ lines.push(`> \u2728 **${t.aiSummaryTitle}**`);
7281
+ lines.push(">");
7282
+ for (const ln of scanResults.aiSummary.trim().split(/\r?\n/)) {
7283
+ lines.push(`> ${ln}`);
7284
+ }
7285
+ lines.push("");
7286
+ }
7284
7287
  lines.push(`## ${t.preCheckOverview}`);
7285
7288
  lines.push(`- ${t.checkedCount(checkedTotal, autoClean, autoIssues)}`);
7286
7289
  if (autoUnknown > 0) {
@@ -7383,6 +7386,14 @@ function escWithLinks(s) {
7383
7386
  return esc(part);
7384
7387
  }).join("");
7385
7388
  }
7389
+ function aiSummaryBlock(summary, title) {
7390
+ const text = (summary ?? "").trim();
7391
+ if (!text) return "";
7392
+ return `<div class="ai-summary">
7393
+ <div class="ai-summary-title">\u2728 ${esc(title)}</div>
7394
+ <div class="ai-summary-body">${esc(text)}</div>
7395
+ </div>`;
7396
+ }
7386
7397
  function calcScore(summary) {
7387
7398
  const raw = 100 - summary.critical * 15 - summary.high * 5 - summary.medium * 2 - summary.low * 0.5;
7388
7399
  return Math.max(0, Math.min(100, Math.round(raw)));
@@ -7627,7 +7638,13 @@ function sharedCss() {
7627
7638
  details{display:block}
7628
7639
  details>summary{display:block}
7629
7640
  details>:not(summary){display:block !important}
7641
+ .ai-summary{background:#f5f3ff !important;border-color:#c7d2fe !important}
7642
+ .ai-summary-title{color:#4338ca !important}
7643
+ .ai-summary-body{color:#1e293b !important}
7630
7644
  }
7645
+ .ai-summary{background:linear-gradient(135deg,#1e293b,#1e1b4b);border:1px solid #4338ca;border-radius:10px;padding:16px 20px;margin:0 0 28px}
7646
+ .ai-summary-title{font-size:14px;font-weight:700;color:#a5b4fc;margin-bottom:8px}
7647
+ .ai-summary-body{font-size:14px;line-height:1.7;color:#cbd5e1;white-space:pre-wrap}
7631
7648
  `;
7632
7649
  }
7633
7650
  function donutChart(summary) {
@@ -8179,6 +8196,8 @@ ${remaining.map(renderRec).join("\n")}
8179
8196
  <div class="meta">${esc(t.account)}: ${esc(accountId)} | ${esc(t.region)}: ${esc(region)} | ${esc(date)} | ${esc(t.duration)}: ${esc(duration)}</div>
8180
8197
  </header>
8181
8198
 
8199
+ ${aiSummaryBlock(scanResults.aiSummary, t.aiSummaryTitle)}
8200
+
8182
8201
  <section class="summary">
8183
8202
  <div class="score-card">
8184
8203
  <div class="score-value" style="color:${scoreColor(score)}">${score}</div>
@@ -8526,6 +8545,8 @@ ${mlpsRemaining.map(renderMlpsRec).join("\n")}
8526
8545
  <div class="meta">${esc(t.account)}: ${esc(accountId)} | ${esc(t.region)}: ${esc(region)} | ${esc(t.scanTime)}: ${esc(scanTime)}</div>
8527
8546
  </header>
8528
8547
 
8548
+ ${aiSummaryBlock(scanResults.aiSummary, t.aiSummaryTitle)}
8549
+
8529
8550
  <section class="summary" style="display:block;text-align:center">
8530
8551
  <div style="font-size:36px;font-weight:700;margin-bottom:12px">
8531
8552
  <span style="color:#22c55e">${autoClean}</span> <span style="color:#94a3b8;font-size:18px">${esc(t.noIssues)}</span>
@@ -8574,6 +8595,14 @@ function safeUrl2(url) {
8574
8595
  return null;
8575
8596
  }
8576
8597
  }
8598
+ function aiSummaryBlock2(summary, title) {
8599
+ const text = (summary ?? "").trim();
8600
+ if (!text) return "";
8601
+ return `<div class="ai-summary">
8602
+ <div class="ai-summary-title">\u2728 ${esc2(title)}</div>
8603
+ <div class="ai-summary-body">${esc2(text)}</div>
8604
+ </div>`;
8605
+ }
8577
8606
  function escWithLinks2(s) {
8578
8607
  const parts = s.split(/(https?:\/\/\S+)/);
8579
8608
  return parts.map((part, i) => {
@@ -8709,7 +8738,13 @@ function hwCss() {
8709
8738
  details{display:block}
8710
8739
  details>summary{display:block}
8711
8740
  details>:not(summary){display:block !important}
8741
+ .ai-summary{background:#f5f3ff !important;border-color:#c7d2fe !important}
8742
+ .ai-summary-title{color:#4338ca !important}
8743
+ .ai-summary-body{color:#1e293b !important}
8712
8744
  }
8745
+ .ai-summary{background:linear-gradient(135deg,#1e293b,#1e1b4b);border:1px solid #4338ca;border-radius:10px;padding:16px 20px;margin:0 0 28px}
8746
+ .ai-summary-title{font-size:14px;font-weight:700;color:#a5b4fc;margin-bottom:8px}
8747
+ .ai-summary-body{font-size:14px;line-height:1.7;color:#cbd5e1;white-space:pre-wrap}
8713
8748
  `;
8714
8749
  }
8715
8750
  function generateHwDefenseHtmlReport(scanResults, lang) {
@@ -8910,6 +8945,8 @@ function generateHwDefenseHtmlReport(scanResults, lang) {
8910
8945
  <div class="meta">${esc2(t.account)}: ${esc2(accountId)} | ${esc2(t.region)}: ${esc2(region)} | ${esc2(t.scanTime)}: ${esc2(scanTime)}</div>
8911
8946
  </header>
8912
8947
 
8948
+ ${aiSummaryBlock2(scanResults.aiSummary, t.aiSummaryTitle)}
8949
+
8913
8950
  <section class="summary-cards">
8914
8951
  <div class="summary-card"><div class="stat-count" style="color:${findingsColor}">${totalFindings}</div><div class="stat-label">${esc2(t.hwTotalFindings)}</div></div>
8915
8952
  <div class="summary-card"><div class="stat-count" style="color:#60a5fa">${sectionsChecked}</div><div class="stat-label">${esc2(t.hwSectionsChecked)}</div></div>
@@ -8929,6 +8966,137 @@ ${sectionsHtml}
8929
8966
  </html>`;
8930
8967
  }
8931
8968
 
8969
+ // src/tools/ai-summary-prompt.ts
8970
+ function buildFindingsDigest(scan, lang, topN = 12) {
8971
+ const zh = lang === "zh";
8972
+ const all = scan.modules.flatMap(
8973
+ (m) => m.findings.map((f) => ({ ...f, module: f.module ?? m.module }))
8974
+ );
8975
+ const s = scan.summary;
8976
+ const byModule = [...scan.modules].filter((m) => m.findingsCount > 0).sort((a, b) => b.findingsCount - a.findingsCount).slice(0, 19).map((m) => `${m.module}=${m.findingsCount}`).join(", ");
8977
+ const top = [...all].sort((a, b) => b.riskScore - a.riskScore).slice(0, topN);
8978
+ const topLines = top.map(
8979
+ (f, i) => `${i + 1}. [${f.severity}/${f.priority}] ${f.title} \u2014 ${f.resourceType} ${f.resourceId} (${f.region}) risk=${f.riskScore}`
8980
+ ).join("\n");
8981
+ const head = zh ? `\u8D26\u53F7: ${scan.accountId} | \u533A\u57DF: ${scan.region}
8982
+ \u98CE\u9669\u603B\u6570: ${s.totalFindings} (CRITICAL ${s.critical} / HIGH ${s.high} / MEDIUM ${s.medium} / LOW ${s.low})
8983
+ \u6A21\u5757\u5206\u5E03: ${byModule || "\u65E0"}
8984
+
8985
+ \u9AD8\u98CE\u9669 Top ${top.length}:
8986
+ ${topLines || "\u65E0"}` : `Account: ${scan.accountId} | Region: ${scan.region}
8987
+ Total findings: ${s.totalFindings} (CRITICAL ${s.critical} / HIGH ${s.high} / MEDIUM ${s.medium} / LOW ${s.low})
8988
+ Module breakdown: ${byModule || "none"}
8989
+
8990
+ Top ${top.length} by risk:
8991
+ ${topLines || "none"}`;
8992
+ return head;
8993
+ }
8994
+ function getProfile(type, lang) {
8995
+ const zh = lang === "zh";
8996
+ switch (type) {
8997
+ case "dashboard":
8998
+ return zh ? {
8999
+ persona: "\u4F60\u5728\u4E3A\u5B89\u5168\u8FD0\u8425\u4EEA\u8868\u76D8\u5199\u4E00\u6BB5\u9AD8\u7BA1\u901F\u89C8\u3002\u8BFB\u8005\u662F CISO / \u7BA1\u7406\u5C42\uFF0C\u65F6\u95F4\u5F88\u5C11\u3002",
9000
+ focus: "\u6574\u4F53\u5B89\u5168\u6001\u52BF\u4E00\u53E5\u8BDD\u5B9A\u8C03 + \u5B89\u5168\u8BC4\u5206\u89E3\u8BFB + \u6B64\u523B\u6700\u8BE5\u5173\u6CE8\u7684 3 \u4EF6\u4E8B\uFF08\u6309\u4E1A\u52A1\u5F71\u54CD\u6392\u5E8F\uFF09\u3002\u4E0D\u8981\u7F57\u5217\u6240\u6709 finding\u3002",
9001
+ format: "3-5 \u53E5\u8BDD\uFF0C\u5F00\u5934\u4E00\u53E5\u7ED3\u8BBA\u5148\u884C\u3002\u4E2D\u6587\u3002\u4E0D\u7528 Markdown \u6807\u9898\uFF0C\u53EF\u7528\u7B80\u77ED\u5206\u53F7\u6216\u6362\u884C\u3002\u8BED\u6C14\u4E13\u4E1A\u3001\u514B\u5236\u3001\u53EF\u6267\u884C\u3002"
9002
+ } : {
9003
+ persona: "You are writing an executive at-a-glance summary for a security operations dashboard. Readers are CISO/leadership with little time.",
9004
+ focus: "One-line overall posture verdict + interpretation of the security score + the top 3 things to focus on right now (ordered by business impact). Do NOT list every finding.",
9005
+ format: "3-5 sentences, conclusion first. English. No Markdown headings; short line breaks ok. Professional, restrained, actionable tone."
9006
+ };
9007
+ case "html":
9008
+ return zh ? {
9009
+ persona: "\u4F60\u5728\u4E3A\u4E00\u4EFD\u5B8C\u6574\u7684 AWS \u5B89\u5168\u626B\u63CF\u62A5\u544A\u5199\u6267\u884C\u6458\u8981\u3002\u8BFB\u8005\u65E2\u6709\u7BA1\u7406\u5C42\u4E5F\u6709\u6280\u672F\u8D1F\u8D23\u4EBA\u3002",
9010
+ focus: "\u6574\u4F53\u98CE\u9669\u5168\u666F + Top \u98CE\u9669\u7684\u5171\u6027\u6839\u56E0\uFF08\u5982\u516C\u7F51\u66B4\u9732\u9762\u3001IAM \u8FC7\u6743\u3001\u52A0\u5BC6/\u65E5\u5FD7\u7F3A\u5931\u7B49\uFF09+ \u5206\u4F18\u5148\u7EA7(P0\u2192P2)\u7684\u4FEE\u590D\u8DEF\u7EBF\u5EFA\u8BAE\u3002\u70B9\u51FA\u7CFB\u7EDF\u6027\u95EE\u9898\u800C\u975E\u9010\u6761\u590D\u8FF0\u3002",
9011
+ format: "1 \u6BB5\u6982\u8FF0 + 3-5 \u6761\u8981\u70B9\u3002\u4E2D\u6587\u3002\u53EF\u7528\u8981\u70B9\u5217\u8868\u3002\u6280\u672F\u4E0E\u7BA1\u7406\u53CC\u89C6\u89D2\uFF0C\u7ED9\u51FA\u53EF\u843D\u5730\u7684\u4E0B\u4E00\u6B65\u3002"
9012
+ } : {
9013
+ persona: "You are writing the executive summary for a full AWS security scan report. Readers include both management and technical leads.",
9014
+ focus: "Overall risk landscape + common root causes behind the top risks (public exposure, over-privileged IAM, missing encryption/logging, etc.) + a prioritized (P0\u2192P2) remediation roadmap. Surface systemic issues rather than restating each finding.",
9015
+ format: "1 overview paragraph + 3-5 bullet points. English. Bullet list ok. Both technical and management lens, with concrete next steps."
9016
+ };
9017
+ case "hw_defense":
9018
+ return zh ? {
9019
+ persona: "\u4F60\u662F\u62A4\u7F51\u884C\u52A8(\u7F51\u7EDC\u5B89\u5168\u653B\u9632\u6F14\u7EC3)\u7684\u84DD\u961F\u6307\u6325\u3002\u8FD9\u662F\u4E00\u4EFD\u62A4\u7F51\u5907\u6218\u8BC4\u4F30\u62A5\u544A\u3002\u6CE8\u610F\uFF1AHW=\u62A4\u7F51\uFF0C\u4E0E\u534E\u4E3A\u65E0\u5173\u3002",
9020
+ focus: "\u4ECE\u653B\u51FB\u8005\u89C6\u89D2\u4E32\u8054 findings \u6210\u653B\u51FB\u94FE(kill-chain)\uFF1A\u54EA\u4E9B\u662F\u5916\u90E8\u53EF\u8FBE\u7684\u521D\u59CB\u7A81\u7834\u53E3\u3001\u6A2A\u5411\u79FB\u52A8/\u63D0\u6743\u8DEF\u5F84\u3001\u53EF\u80FD\u7684\u5F71\u54CD\u9762\uFF1B\u7136\u540E\u7ED9\u51FA\u5907\u6218\u6536\u655B\u6E05\u5355(\u5148\u5173\u4EC0\u4E48\u3001\u5148\u76EF\u4EC0\u4E48)\u3002\u5F3A\u8C03\u66B4\u9732\u9762\u6536\u655B\u4E0E\u76D1\u63A7\u52A0\u56FA\u3002",
9021
+ format: "\u5148\u4E00\u53E5\u7814\u5224\u7ED3\u8BBA(\u5F53\u524D\u66B4\u9732\u9762/\u88AB\u6253\u7A7F\u98CE\u9669)\uFF0C\u518D\u7ED9\u300E\u653B\u51FB\u94FE\u89C6\u89D2\u300F\u548C\u300E\u5907\u6218\u6574\u6539\u4F18\u5148\u7EA7\u300F\u4E24\u5757\u3002\u4E2D\u6587\u3002\u8BED\u6C14\u8D34\u62A4\u7F51\u5B9E\u6218\u8BED\u5883\uFF0C\u7D27\u8FEB\u4F46\u4E0D\u5938\u5F20\u3002"
9022
+ } : {
9023
+ persona: "You are the blue-team lead for an HW Defense exercise (cybersecurity attack-defense drill). This is a pre-exercise readiness report. Note: HW = HuWang/\u62A4\u7F51, unrelated to Huawei.",
9024
+ focus: "From an attacker's perspective, chain findings into a kill-chain: external initial footholds, lateral movement/privilege-escalation paths, likely blast radius; then a hardening checklist (what to close/watch first). Emphasize attack-surface reduction and monitoring.",
9025
+ format: "Lead with a one-line verdict (current exposure / breach risk), then two blocks: 'Attack-chain view' and 'Readiness remediation priorities'. English. Combat-readiness tone, urgent but not alarmist."
9026
+ };
9027
+ case "mlps3":
9028
+ return zh ? {
9029
+ persona: "\u4F60\u662F\u7B49\u4FDD\u6D4B\u8BC4\u987E\u95EE\u3002\u8FD9\u662F\u4E00\u4EFD\u7B49\u4FDD\u4E09\u7EA7(GB/T 22239-2019)\u5408\u89C4\u9884\u68C0\u62A5\u544A\u3002",
9030
+ focus: "\u4ECE\u5408\u89C4\u89C6\u89D2\u603B\u7ED3\uFF1A\u5F53\u524D\u5BF9\u7167\u7B49\u4FDD\u4E09\u7EA7\u63A7\u5236\u9879\u7684\u603B\u4F53\u8FBE\u6807\u60C5\u51B5\u3001\u54EA\u4E9B\u63A7\u5236\u57DF\u5B58\u5728\u660E\u663E\u4E0D\u8FBE\u6807\u9879(\u5982\u8BBF\u95EE\u63A7\u5236\u3001\u5B89\u5168\u5BA1\u8BA1\u3001\u5165\u4FB5\u9632\u8303\u3001\u6570\u636E\u5B8C\u6574\u6027/\u4FDD\u5BC6\u6027\u7B49)\u3001\u6574\u6539\u7D27\u8FEB\u5EA6\u4E0E\u8FC7\u4FDD\u5EFA\u8BAE\u3002\u628A\u6280\u672F finding \u6620\u5C04\u5230\u5408\u89C4\u8BED\u8A00\u3002",
9031
+ format: "1 \u6BB5\u5408\u89C4\u7ED3\u8BBA + \u6309\u63A7\u5236\u57DF\u5206\u7EC4\u7684\u4E0D\u8FBE\u6807\u8981\u70B9 + \u6574\u6539\u4F18\u5148\u7EA7\u3002\u4E2D\u6587\u3002\u5F15\u7528\u63A7\u5236\u57DF\u540D\u79F0\u3002\u8BED\u6C14\u4E25\u8C28\u3001\u9762\u5411\u6D4B\u8BC4\u3002"
9032
+ } : {
9033
+ persona: "You are an MLPS assessment advisor. This is an MLPS Level 3 (GB/T 22239-2019) compliance pre-check report.",
9034
+ focus: "Summarize from a compliance lens: overall conformance against MLPS L3 controls, which control domains have clear gaps (access control, security audit, intrusion prevention, data integrity/confidentiality, etc.), remediation urgency, and certification-readiness advice. Map technical findings to compliance language.",
9035
+ format: "1 compliance verdict paragraph + non-conformance points grouped by control domain + remediation priority. English. Reference control-domain names. Rigorous, assessment-oriented tone."
9036
+ };
9037
+ }
9038
+ }
9039
+ var TYPE_LABEL = {
9040
+ dashboard: { zh: "\u5B89\u5168\u8FD0\u8425\u4EEA\u8868\u76D8", en: "Security Operations Dashboard" },
9041
+ html: { zh: "AWS \u5B89\u5168\u626B\u63CF\u62A5\u544A", en: "AWS Security Scan Report" },
9042
+ hw_defense: { zh: "\u62A4\u7F51\u884C\u52A8\u8BC4\u4F30\u62A5\u544A", en: "HW Defense (\u62A4\u7F51) Readiness Report" },
9043
+ mlps3: { zh: "\u7B49\u4FDD\u4E09\u7EA7\u5408\u89C4\u9884\u68C0\u62A5\u544A", en: "MLPS Level 3 Compliance Pre-check" }
9044
+ };
9045
+ function buildAiSummaryPrompt(type, scan, lang) {
9046
+ const zh = lang === "zh";
9047
+ const p = getProfile(type, lang);
9048
+ const label = TYPE_LABEL[type][zh ? "zh" : "en"];
9049
+ const digest = buildFindingsDigest(scan, lang);
9050
+ if (zh) {
9051
+ return [
9052
+ `# \u4EFB\u52A1\uFF1A\u4E3A\u300C${label}\u300D\u751F\u6210\u9488\u5BF9\u6027\u7684 AI \u5B89\u5168\u603B\u7ED3`,
9053
+ ``,
9054
+ `## \u89D2\u8272`,
9055
+ p.persona,
9056
+ ``,
9057
+ `## \u603B\u7ED3\u91CD\u70B9`,
9058
+ p.focus,
9059
+ ``,
9060
+ `## \u8F93\u51FA\u683C\u5F0F`,
9061
+ p.format,
9062
+ ``,
9063
+ `## \u7EA6\u675F`,
9064
+ `- \u53EA\u4F9D\u636E\u4E0B\u9762\u7684\u626B\u63CF\u6570\u636E\uFF0C\u4E0D\u8981\u7F16\u9020\u4E0D\u5B58\u5728\u7684\u8D44\u6E90\u6216 finding\u3002`,
9065
+ `- \u4E0D\u8981\u9010\u6761\u590D\u8FF0\u6240\u6709 finding\uFF1B\u63D0\u70BC\u6A21\u5F0F\u4E0E\u4F18\u5148\u7EA7\u3002`,
9066
+ `- \u76F4\u63A5\u8F93\u51FA\u603B\u7ED3\u6B63\u6587\u672C\u8EAB\uFF08\u4E0D\u8981\u5E26\u300C\u4EE5\u4E0B\u662F\u603B\u7ED3\u300D\u4E4B\u7C7B\u7684\u524D\u8A00\uFF0C\u4E0D\u8981 code fence\uFF09\u3002`,
9067
+ `- \u8BED\u8A00\uFF1A\u4E2D\u6587\u3002`,
9068
+ ``,
9069
+ `## \u626B\u63CF\u6570\u636E\u6458\u8981`,
9070
+ digest,
9071
+ ``,
9072
+ `\u8BF7\u73B0\u5728\u8F93\u51FA\u603B\u7ED3\u6B63\u6587\u3002\u751F\u6210\u540E\uFF0C\u628A\u8FD9\u6BB5\u6587\u672C\u901A\u8FC7\u5BF9\u5E94\u62A5\u544A\u5DE5\u5177\u7684 ai_summary \u53C2\u6570\u56DE\u4F20\u5373\u53EF\u3002`
9073
+ ].join("\n");
9074
+ }
9075
+ return [
9076
+ `# Task: Produce a tailored AI security summary for the "${label}"`,
9077
+ ``,
9078
+ `## Role`,
9079
+ p.persona,
9080
+ ``,
9081
+ `## Summary focus`,
9082
+ p.focus,
9083
+ ``,
9084
+ `## Output format`,
9085
+ p.format,
9086
+ ``,
9087
+ `## Constraints`,
9088
+ `- Base it ONLY on the scan data below; do not invent resources or findings.`,
9089
+ `- Do not restate every finding; distill patterns and priorities.`,
9090
+ `- Output the summary body itself (no preamble like "Here is the summary", no code fences).`,
9091
+ `- Language: English.`,
9092
+ ``,
9093
+ `## Scan data digest`,
9094
+ digest,
9095
+ ``,
9096
+ `Now output the summary body. After generating it, pass the text back via the ai_summary parameter of the corresponding report tool.`
9097
+ ].join("\n");
9098
+ }
9099
+
8932
9100
  // src/tools/save-results.ts
8933
9101
  import { writeFileSync, readFileSync, mkdirSync, existsSync } from "fs";
8934
9102
  import { join } from "path";
@@ -8985,7 +9153,8 @@ function saveResults(scanResults, outputDir) {
8985
9153
  })),
8986
9154
  findings: scanResults.modules.flatMap(
8987
9155
  (m) => m.findings.map((f) => ({ ...f, module: m.module }))
8988
- )
9156
+ ),
9157
+ aiSummary: scanResults.aiSummary
8989
9158
  },
8990
9159
  history,
8991
9160
  meta: {
@@ -9256,8 +9425,9 @@ LOW \u2192 P3 (Low)
9256
9425
  `;
9257
9426
 
9258
9427
  // src/index.ts
9259
- import { readFileSync as readFileSync2 } from "fs";
9428
+ import { readFileSync as readFileSync2, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
9260
9429
  import { join as join2, dirname } from "path";
9430
+ import { homedir as homedir2 } from "os";
9261
9431
  import { fileURLToPath } from "url";
9262
9432
  var MODULE_DESCRIPTIONS = {
9263
9433
  service_detection: "Detects which AWS security services (Security Hub, GuardDuty, Inspector, Config) are enabled and assesses security maturity.",
@@ -9600,11 +9770,13 @@ function createServer(defaultRegion) {
9600
9770
  "Generate a Markdown security report from scan results. Read-only. Does not modify any AWS resources.",
9601
9771
  {
9602
9772
  scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
9603
- lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
9773
+ lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)"),
9774
+ ai_summary: z.string().optional().describe("Optional pre-generated AI executive summary (Markdown/plain text). Rendered if present; omit to hide.")
9604
9775
  },
9605
- async ({ scan_results, lang }) => {
9776
+ async ({ scan_results, lang, ai_summary }) => {
9606
9777
  try {
9607
9778
  const parsed = JSON.parse(scan_results);
9779
+ if (ai_summary) parsed.aiSummary = ai_summary;
9608
9780
  const report = generateMarkdownReport(parsed, lang ?? "zh");
9609
9781
  return { content: [{ type: "text", text: report }] };
9610
9782
  } catch (err) {
@@ -9617,11 +9789,13 @@ function createServer(defaultRegion) {
9617
9789
  "Generate a GB/T 22239-2019 \u7B49\u4FDD\u4E09\u7EA7 compliance pre-check report from scan results. Best used with scan_group mlps3_precheck results. Read-only.",
9618
9790
  {
9619
9791
  scan_results: z.string().describe("JSON string of FullScanResult from scan_group mlps3_precheck or scan_all"),
9620
- lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
9792
+ lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)"),
9793
+ ai_summary: z.string().optional().describe("Optional pre-generated AI executive summary (Markdown/plain text). Rendered if present; omit to hide.")
9621
9794
  },
9622
- async ({ scan_results, lang }) => {
9795
+ async ({ scan_results, lang, ai_summary }) => {
9623
9796
  try {
9624
9797
  const parsed = JSON.parse(scan_results);
9798
+ if (ai_summary) parsed.aiSummary = ai_summary;
9625
9799
  const report = generateMlps3Report(parsed, lang ?? "zh");
9626
9800
  return { content: [{ type: "text", text: report }] };
9627
9801
  } catch (err) {
@@ -9635,11 +9809,13 @@ function createServer(defaultRegion) {
9635
9809
  {
9636
9810
  scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
9637
9811
  history: z.string().optional().describe("JSON string of DashboardHistoryEntry[] from dashboard data.json for 30-day trend charts"),
9638
- lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
9812
+ lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)"),
9813
+ ai_summary: z.string().optional().describe("Optional pre-generated AI executive summary (Markdown/plain text). Rendered if present; omit to hide.")
9639
9814
  },
9640
- async ({ scan_results, history, lang }) => {
9815
+ async ({ scan_results, history, lang, ai_summary }) => {
9641
9816
  try {
9642
9817
  const parsed = JSON.parse(scan_results);
9818
+ if (ai_summary) parsed.aiSummary = ai_summary;
9643
9819
  const historyData = history ? JSON.parse(history) : void 0;
9644
9820
  const report = generateHtmlReport(parsed, historyData, lang ?? "zh");
9645
9821
  return { content: [{ type: "text", text: report }] };
@@ -9654,11 +9830,13 @@ function createServer(defaultRegion) {
9654
9830
  {
9655
9831
  scan_results: z.string().describe("JSON string of FullScanResult from scan_group mlps3_precheck or scan_all"),
9656
9832
  history: z.string().optional().describe("JSON string of DashboardHistoryEntry[] from dashboard data.json for 30-day trend charts"),
9657
- lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
9833
+ lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)"),
9834
+ ai_summary: z.string().optional().describe("Optional pre-generated AI executive summary (Markdown/plain text). Rendered if present; omit to hide.")
9658
9835
  },
9659
- async ({ scan_results, history, lang }) => {
9836
+ async ({ scan_results, history, lang, ai_summary }) => {
9660
9837
  try {
9661
9838
  const parsed = JSON.parse(scan_results);
9839
+ if (ai_summary) parsed.aiSummary = ai_summary;
9662
9840
  const historyData = history ? JSON.parse(history) : void 0;
9663
9841
  const report = generateMlps3HtmlReport(parsed, historyData, lang ?? "zh");
9664
9842
  return { content: [{ type: "text", text: report }] };
@@ -9667,16 +9845,36 @@ function createServer(defaultRegion) {
9667
9845
  }
9668
9846
  }
9669
9847
  );
9848
+ server.tool(
9849
+ "get_ai_summary_prompt",
9850
+ "Return a report-type-tailored prompt (with a grounded findings digest) that the CALLING AI should run to produce an AI security summary. Then pass the generated text back via the `ai_summary` parameter of the matching report tool (or scan_and_report). The server performs no LLM calls. Use this to make each summary specific to the report type (dashboard / security scan / HW Defense \u62A4\u7F51 / MLPS3 \u7B49\u4FDD).",
9851
+ {
9852
+ report_type: z.enum(["dashboard", "html", "hw_defense", "mlps3"]).describe("Target report type the summary is for: dashboard (overview), html (AWS security scan report), hw_defense (\u62A4\u7F51 attack-defense drill), mlps3 (\u7B49\u4FDD\u4E09\u7EA7 compliance)"),
9853
+ scan_results: z.string().describe("JSON string of FullScanResult from scan_all / scan_group"),
9854
+ lang: z.enum(["zh", "en"]).optional().describe("Summary language (default: zh)")
9855
+ },
9856
+ async ({ report_type, scan_results, lang }) => {
9857
+ try {
9858
+ const parsed = JSON.parse(scan_results);
9859
+ const prompt = buildAiSummaryPrompt(report_type, parsed, lang ?? "zh");
9860
+ return { content: [{ type: "text", text: prompt }] };
9861
+ } catch (err) {
9862
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
9863
+ }
9864
+ }
9865
+ );
9670
9866
  server.tool(
9671
9867
  "generate_hw_defense_report",
9672
9868
  "Generate an HTML report organized by HW Defense (\u62A4\u7F51) SOP checklist categories. Save as .html file.",
9673
9869
  {
9674
9870
  scan_results: z.string().describe("JSON string of FullScanResult from scan_group hw_defense or scan_all"),
9675
- lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)")
9871
+ lang: z.enum(["zh", "en"]).optional().describe("Report language (default: zh)"),
9872
+ ai_summary: z.string().optional().describe("Optional pre-generated AI executive summary (Markdown/plain text). Rendered if present; omit to hide.")
9676
9873
  },
9677
- async ({ scan_results, lang }) => {
9874
+ async ({ scan_results, lang, ai_summary }) => {
9678
9875
  try {
9679
9876
  const parsed = JSON.parse(scan_results);
9877
+ if (ai_summary) parsed.aiSummary = ai_summary;
9680
9878
  const report = generateHwDefenseHtmlReport(parsed, lang ?? "zh");
9681
9879
  return { content: [{ type: "text", text: report }] };
9682
9880
  } catch (err) {
@@ -9805,11 +10003,13 @@ function createServer(defaultRegion) {
9805
10003
  "Saves scan results to local disk or S3 for dashboard display. Does not modify any AWS resources.",
9806
10004
  {
9807
10005
  scan_results: z.string().describe("JSON string of FullScanResult from scan_all"),
9808
- output_dir: z.string().optional().describe("Output directory (default: ~/.aws-security)")
10006
+ output_dir: z.string().optional().describe("Output directory (default: ~/.aws-security)"),
10007
+ ai_summary: z.string().optional().describe("Optional pre-generated AI executive summary (from get_ai_summary_prompt with report_type=dashboard). Persisted into dashboard data and rendered on the Overview; omit to hide.")
9809
10008
  },
9810
- async ({ scan_results, output_dir }) => {
10009
+ async ({ scan_results, output_dir, ai_summary }) => {
9811
10010
  try {
9812
10011
  const parsed = JSON.parse(scan_results);
10012
+ if (ai_summary) parsed.aiSummary = ai_summary;
9813
10013
  const dataPath = saveResults(parsed, output_dir);
9814
10014
  return {
9815
10015
  content: [
@@ -9898,6 +10098,81 @@ Deploy this as a StackSet from your Management Account to all member accounts.`
9898
10098
  }
9899
10099
  }
9900
10100
  );
10101
+ server.tool(
10102
+ "scan_and_report",
10103
+ "Run a full security scan AND generate reports in one step. Avoids large data transfer between tools. Reports are saved to ~/.aws-security/reports/",
10104
+ {
10105
+ region: z.string().optional().describe("AWS region (default: server region)"),
10106
+ org_mode: z.boolean().optional().describe("Enable multi-account org scanning"),
10107
+ role_name: z.string().optional().describe("IAM role name for cross-account scanning"),
10108
+ account_ids: z.array(z.string()).optional().describe("Filter to specific account IDs"),
10109
+ reports: z.array(z.enum(["html", "hw_defense", "mlps3", "markdown", "all"])).optional().describe("Report types to generate (default: all)"),
10110
+ lang: z.enum(["zh", "en"]).optional().describe("Language: zh or en (default: zh)"),
10111
+ ai_summary: z.string().optional().describe("Optional pre-generated AI executive summary (Markdown/plain text). Rendered in reports + dashboard if present; omit to hide.")
10112
+ },
10113
+ async ({ region, org_mode, role_name, account_ids, reports, lang, ai_summary }) => {
10114
+ try {
10115
+ const r = region ?? defaultRegion;
10116
+ const l = lang ?? "zh";
10117
+ const reportTypes = reports ?? ["all"];
10118
+ const wantAll = reportTypes.includes("all");
10119
+ let result;
10120
+ if (org_mode) {
10121
+ result = await runMultiAccountScanners(allScanners, r, {
10122
+ orgMode: true,
10123
+ roleName: role_name ?? "AWSSecurityMCPAudit",
10124
+ accountIds: account_ids
10125
+ });
10126
+ } else {
10127
+ result = await runAllScanners(allScanners, r);
10128
+ }
10129
+ if (ai_summary) result.aiSummary = ai_summary;
10130
+ const baseDir = join2(homedir2(), ".aws-security", "reports", (/* @__PURE__ */ new Date()).toISOString().slice(0, 10));
10131
+ mkdirSync2(baseDir, { recursive: true });
10132
+ const savedFiles = [];
10133
+ if (wantAll || reportTypes.includes("html")) {
10134
+ const html = generateHtmlReport(result, void 0, l);
10135
+ const p = join2(baseDir, "security-report.html");
10136
+ writeFileSync2(p, html);
10137
+ savedFiles.push(p);
10138
+ }
10139
+ if (wantAll || reportTypes.includes("hw_defense")) {
10140
+ const html = generateHwDefenseHtmlReport(result, l);
10141
+ const p = join2(baseDir, "hw-defense-report.html");
10142
+ writeFileSync2(p, html);
10143
+ savedFiles.push(p);
10144
+ }
10145
+ if (wantAll || reportTypes.includes("mlps3")) {
10146
+ const html = generateMlps3HtmlReport(result, void 0, l);
10147
+ const p = join2(baseDir, "mlps3-report.html");
10148
+ writeFileSync2(p, html);
10149
+ savedFiles.push(p);
10150
+ }
10151
+ if (wantAll || reportTypes.includes("markdown")) {
10152
+ const md = generateMarkdownReport(result, l);
10153
+ const p = join2(baseDir, "security-report.md");
10154
+ writeFileSync2(p, md);
10155
+ savedFiles.push(p);
10156
+ }
10157
+ saveResults(result);
10158
+ const summary = summarizeResult(result, l);
10159
+ const fileList = savedFiles.map((f) => ` ${f}`).join("\n");
10160
+ return {
10161
+ content: [{
10162
+ type: "text",
10163
+ text: `${summary}
10164
+
10165
+ Reports saved:
10166
+ ${fileList}
10167
+
10168
+ Dashboard data updated.`
10169
+ }]
10170
+ };
10171
+ } catch (err) {
10172
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
10173
+ }
10174
+ }
10175
+ );
9901
10176
  server.resource(
9902
10177
  "security-rules",
9903
10178
  "security://rules",