Skip to content

Commit d95e9cf

Browse files
Combine AI Summary and AI Scan into one row
1 parent 94c2cdb commit d95e9cf

1 file changed

Lines changed: 145 additions & 127 deletions

File tree

src/App.jsx

Lines changed: 145 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const SESSION_ID = getSessionId();
5555
// onto the /fetch, /parse, and /enrich requests the app already makes for
5656
// functional reasons — SESSION_ID is attached to those, but there is no
5757
// dedicated client-initiated logging call. Invisible to browser DevTools.
58-
const APP_VERSION = "v134";
58+
const APP_VERSION = "v135";
5959

6060
// ============================================================
6161
// IOC Whitelist — exact-match auto-removal from parsed results
@@ -2220,6 +2220,7 @@ export default function App() {
22202220
const [aiSummary, setAiSummary] = useState(null); // { headline, summary, recommendations[] }
22212221
const [aiState, setAiState] = useState("idle"); // idle | loading | done | error
22222222
const [aiOpen, setAiOpen] = useState(false);
2223+
const [aiScanOpen, setAiScanOpen] = useState(false);
22232224
const [retryCount, setRetryCount] = useState(0);
22242225
const [cooldown, setCooldown] = useState(0); // seconds until retry re-enabled
22252226
const [enrichCache, setEnrichCache] = useState({}); // {iocKey: {loading,data,error}}
@@ -7239,142 +7240,159 @@ export default function App() {
72397240
)}
72407241

72417242
{(articleClean || rawArticle) && sourceUrl && (
7242-
<div className="rounded-xl mb-4 overflow-hidden" style={{ ...panel, borderColor: "rgba(253,224,71,0.35)" }}>
7243-
<div className="flex items-center justify-between px-4 py-3 gap-3 flex-wrap">
7244-
<span className="flex items-center gap-2.5 min-w-0">
7245-
<span className="shrink-0 flex h-7 w-7 items-center justify-center rounded-lg"
7246-
style={{ backgroundColor: "rgba(253,224,71,0.08)", border: "1px solid rgba(253,224,71,0.35)" }}>
7247-
<span style={{ fontSize: 14 }}>🧠</span>
7248-
</span>
7249-
<span className="text-sm font-bold tracking-wide" style={{ color: "#fde047" }}>AI Scan Threat Hunting Artifacts</span>
7250-
{aiScanState === "loading" && (
7251-
<span className="text-[10px] uppercase tracking-widest flex items-center gap-1" style={{ color: "#8aa0ad" }}>
7252-
<Loader2 size={11} className="animate-spin" /> scanning
7253-
</span>
7254-
)}
7255-
{aiScanState === "done" && aiScanCounts && (
7256-
<span className="text-[10px] uppercase tracking-widest rounded-full px-2 py-0.5"
7257-
style={{ color: "#00ff9c", border: "1px solid rgba(0,255,156,0.35)", backgroundColor: "rgba(0,255,156,0.06)" }}>
7258-
+{aiScanCounts.scheduled_tasks + aiScanCounts.services + aiScanCounts.registry_ops + aiScanCounts.command_lines + aiScanCounts.file_paths} artifacts merged
7243+
<div className="flex flex-col sm:flex-row gap-3 mb-4 items-start">
7244+
<div className="rounded-xl overflow-hidden flex-1 w-full" style={{ ...panel, borderColor: "rgba(192,132,252,0.35)", boxShadow: aiOpen ? "0 0 24px rgba(192,132,252,0.10)" : "none" }}>
7245+
<button onClick={toggleAiPanel}
7246+
className="w-full flex items-center justify-between px-4 py-3 text-left gap-3"
7247+
style={{ backgroundColor: aiOpen ? "rgba(192,132,252,0.06)" : "transparent" }}>
7248+
<span className="flex items-center gap-2.5 min-w-0">
7249+
<span className="shrink-0 flex h-7 w-7 items-center justify-center rounded-lg"
7250+
style={{ backgroundColor: "rgba(192,132,252,0.08)", border: "1px solid rgba(192,132,252,0.35)" }}>
7251+
<Sparkles size={14} style={{ color: "#c084fc" }} />
72597252
</span>
7260-
)}
7261-
</span>
7262-
{aiScanState === "done" ? (
7263-
<button onClick={() => { setAiScanState("idle"); setAiScanCounts(null); }}
7264-
className="text-[11px] underline shrink-0" style={{ color: "#8aa0ad", cursor: "pointer", background: "none", border: "none" }}>
7265-
re-scan
7266-
</button>
7267-
) : (
7268-
<button onClick={runAIScan}
7269-
disabled={aiScanState === "loading"}
7270-
title="Deep artifact extraction — scheduled tasks, services, registry ops, command lines, file paths"
7271-
className="flex items-center gap-1 rounded-lg px-2.5 py-1 text-[11px] font-semibold shrink-0"
7272-
style={{
7273-
color: "#04111a",
7274-
backgroundColor: "#fde047",
7275-
border: "1px solid rgba(253,224,71,0.6)",
7276-
cursor: aiScanState === "loading" ? "not-allowed" : "pointer",
7277-
}}>
7278-
{aiScanState === "loading" ? <Loader2 size={12} className="animate-spin" /> : <span style={{ fontSize: 11 }}>🧠</span>}
7279-
{aiScanState === "loading" ? "Scanning…" : "AI Scan Artifacts"}
7280-
</button>
7253+
<span className="text-sm font-bold tracking-wide truncate" style={{ color: "#c084fc" }}>AI Summary</span>
7254+
{aiState === "idle" && (
7255+
<span className="text-[10px] uppercase tracking-widest rounded-full px-2 py-0.5 hidden sm:inline shrink-0"
7256+
style={{ color: "#8aa0ad", border: "1px solid rgba(120,160,180,0.3)" }}>
7257+
click to generate
7258+
</span>
7259+
)}
7260+
</span>
7261+
<ChevronDown size={18} className="shrink-0 transition-transform"
7262+
style={{ color: "#c084fc", transform: aiOpen ? "rotate(180deg)" : "rotate(0deg)" }} />
7263+
</button>
7264+
7265+
{aiOpen && (
7266+
<div className="px-4 pb-4 pt-1" style={{ borderTop: "1px solid rgba(192,132,252,0.2)" }}>
7267+
{aiState === "loading" && (
7268+
<p className="text-xs sm:text-sm animate-pulse pt-2" style={{ color: "#9fb3bd" }}>
7269+
Analyzing article and generating summary…
7270+
</p>
7271+
)}
7272+
7273+
{aiState === "done" && aiSummary && (
7274+
<div className="pt-2">
7275+
<h2 className="text-sm sm:text-base font-extrabold leading-snug" style={{ color: "#eafcff" }}>{aiSummary.headline}</h2>
7276+
{aiSummary.executive_summary && (
7277+
<>
7278+
<p className="text-xs sm:text-sm uppercase tracking-widest font-bold mt-2.5 mb-1" style={{ color: "#00e5ff" }}>Executive Summary</p>
7279+
<p className="text-xs sm:text-sm leading-relaxed" style={{ color: "#d4e3ea" }}>{defangProse(aiSummary.executive_summary)}</p>
7280+
</>
7281+
)}
7282+
<p className="text-xs sm:text-sm uppercase tracking-widest font-bold mt-3 mb-1" style={{ color: "#00e5ff" }}>Technical Analysis</p>
7283+
<p className="text-xs sm:text-sm font-medium leading-relaxed" style={{ color: "#b8c9d1" }}>{defangProse(aiSummary.summary)}</p>
7284+
{aiSummary.recommendations.length > 0 && (
7285+
<div className="mt-2.5">
7286+
<p className="text-xs sm:text-sm uppercase tracking-widest font-bold mb-1" style={{ color: "#00e5ff" }}>Recommendations</p>
7287+
{aiSummary.recommendations.map((rec, i) => (
7288+
<div key={i} className="flex items-start gap-1.5 text-xs sm:text-sm py-0.5 leading-relaxed font-medium" style={{ color: "#9fb3bd" }}>
7289+
<span className="shrink-0" style={{ color: "#c084fc" }}>▸</span> <span>{defangProse(rec)}</span>
7290+
</div>
7291+
))}
7292+
</div>
7293+
)}
7294+
</div>
7295+
)}
7296+
7297+
{aiState === "error" && (
7298+
<div className="pt-2">
7299+
<p className="text-xs sm:text-sm leading-relaxed" style={{ color: "#ffb4b4" }}>
7300+
The AI engines are experiencing high traffic right now, so a summary couldn't be generated. Please give it a moment and retry.
7301+
</p>
7302+
<button onClick={retryAi} disabled={cooldown > 0}
7303+
className="mt-2.5 flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold"
7304+
style={{
7305+
color: cooldown > 0 ? "#5d7382" : "#c084fc",
7306+
border: `1px solid ${cooldown > 0 ? "rgba(120,160,180,0.25)" : "rgba(192,132,252,0.45)"}`,
7307+
backgroundColor: cooldown > 0 ? "rgba(120,160,180,0.06)" : "rgba(192,132,252,0.10)",
7308+
cursor: cooldown > 0 ? "not-allowed" : "pointer",
7309+
}}>
7310+
<RefreshCw size={13} />
7311+
{cooldown > 0 ? `Retry available in ${cooldown}s` : "Retry AI Summary"}
7312+
</button>
7313+
</div>
7314+
)}
7315+
7316+
{aiState === "idle" && (
7317+
<p className="text-xs sm:text-sm pt-2 animate-pulse" style={{ color: "#9fb3bd" }}>
7318+
Initializing…
7319+
</p>
7320+
)}
7321+
</div>
72817322
)}
72827323
</div>
7283-
{aiScanState === "error" && (
7284-
<div className="px-4 pb-3 pt-1 text-xs" style={{ color: "#ffb4b4", borderTop: "1px solid rgba(255,77,77,0.2)" }}>
7285-
{aiScanError || "AI scan failed. Please retry."}
7286-
<button onClick={() => { setAiScanState("idle"); setAiScanError(""); }}
7287-
className="ml-2 underline" style={{ color: "#fde047" }}>reset</button>
7288-
</div>
7289-
)}
7290-
{aiScanState === "done" && aiScanCounts && (aiScanCounts.scheduled_tasks + aiScanCounts.services + aiScanCounts.registry_ops + aiScanCounts.command_lines + aiScanCounts.file_paths) === 0 && (
7291-
<div className="px-4 pb-3 pt-1 text-xs" style={{ color: "#8aa0ad", borderTop: "1px solid rgba(253,224,71,0.15)" }}>
7292-
No additional artifacts found beyond what regex already captured.
7293-
</div>
7294-
)}
7295-
</div>
7296-
)}
72977324

7298-
{(articleClean || rawArticle) && sourceUrl && (
7299-
<div className="rounded-xl mb-4 overflow-hidden" style={{ ...panel, borderColor: "rgba(192,132,252,0.35)", boxShadow: aiOpen ? "0 0 24px rgba(192,132,252,0.10)" : "none" }}>
7300-
<button onClick={toggleAiPanel}
7301-
className="w-full flex items-center justify-between px-4 py-3 text-left"
7302-
style={{ backgroundColor: aiOpen ? "rgba(192,132,252,0.06)" : "transparent" }}>
7303-
<span className="flex items-center gap-2.5 min-w-0">
7304-
<span className="shrink-0 flex h-7 w-7 items-center justify-center rounded-lg"
7305-
style={{ backgroundColor: "rgba(192,132,252,0.08)", border: "1px solid rgba(192,132,252,0.35)" }}>
7306-
<Sparkles size={14} style={{ color: "#c084fc" }} />
7307-
</span>
7308-
<span className="text-sm font-bold tracking-wide" style={{ color: "#c084fc" }}>AI Summary</span>
7309-
{aiState === "idle" && (
7310-
<span className="text-[10px] uppercase tracking-widest rounded-full px-2 py-0.5 hidden sm:inline"
7311-
style={{ color: "#8aa0ad", border: "1px solid rgba(120,160,180,0.3)" }}>
7312-
click to generate
7325+
<div className="rounded-xl overflow-hidden flex-1 w-full" style={{ ...panel, borderColor: "rgba(253,224,71,0.35)", boxShadow: aiScanOpen ? "0 0 24px rgba(253,224,71,0.10)" : "none" }}>
7326+
<button onClick={() => setAiScanOpen((v) => !v)}
7327+
className="w-full flex items-center justify-between px-4 py-3 text-left gap-3"
7328+
style={{ backgroundColor: aiScanOpen ? "rgba(253,224,71,0.06)" : "transparent" }}>
7329+
<span className="flex items-center gap-2.5 min-w-0">
7330+
<span className="shrink-0 flex h-7 w-7 items-center justify-center rounded-lg"
7331+
style={{ backgroundColor: "rgba(253,224,71,0.08)", border: "1px solid rgba(253,224,71,0.35)" }}>
7332+
<span style={{ fontSize: 14 }}>🧠</span>
73137333
</span>
7314-
)}
7315-
</span>
7316-
<ChevronDown size={18} className="shrink-0 transition-transform"
7317-
style={{ color: "#c084fc", transform: aiOpen ? "rotate(180deg)" : "rotate(0deg)" }} />
7318-
</button>
7319-
7320-
{aiOpen && (
7321-
<div className="px-4 pb-4 pt-1" style={{ borderTop: "1px solid rgba(192,132,252,0.2)" }}>
7322-
{aiState === "loading" && (
7323-
<p className="text-xs sm:text-sm animate-pulse pt-2" style={{ color: "#9fb3bd" }}>
7324-
Analyzing article and generating summary…
7325-
</p>
7326-
)}
7334+
<span className="text-sm font-bold tracking-wide truncate" style={{ color: "#fde047" }}>AI Scan Artifacts</span>
7335+
{aiScanState === "loading" && (
7336+
<span className="text-[10px] uppercase tracking-widest flex items-center gap-1 shrink-0" style={{ color: "#8aa0ad" }}>
7337+
<Loader2 size={11} className="animate-spin" /> scanning
7338+
</span>
7339+
)}
7340+
{aiScanState === "done" && aiScanCounts && (
7341+
<span className="text-[10px] uppercase tracking-widest rounded-full px-2 py-0.5 shrink-0"
7342+
style={{ color: "#00ff9c", border: "1px solid rgba(0,255,156,0.35)", backgroundColor: "rgba(0,255,156,0.06)" }}>
7343+
+{aiScanCounts.scheduled_tasks + aiScanCounts.services + aiScanCounts.registry_ops + aiScanCounts.command_lines + aiScanCounts.file_paths} merged
7344+
</span>
7345+
)}
7346+
{aiScanState === "idle" && (
7347+
<span className="text-[10px] uppercase tracking-widest rounded-full px-2 py-0.5 hidden sm:inline shrink-0"
7348+
style={{ color: "#8aa0ad", border: "1px solid rgba(120,160,180,0.3)" }}>
7349+
click to scan
7350+
</span>
7351+
)}
7352+
</span>
7353+
<ChevronDown size={18} className="shrink-0 transition-transform"
7354+
style={{ color: "#fde047", transform: aiScanOpen ? "rotate(180deg)" : "rotate(0deg)" }} />
7355+
</button>
73277356

7328-
{aiState === "done" && aiSummary && (
7357+
{aiScanOpen && (
7358+
<div className="px-4 pb-4 pt-1" style={{ borderTop: "1px solid rgba(253,224,71,0.2)" }}>
73297359
<div className="pt-2">
7330-
<h2 className="text-sm sm:text-base font-extrabold leading-snug" style={{ color: "#eafcff" }}>{aiSummary.headline}</h2>
7331-
{aiSummary.executive_summary && (
7332-
<>
7333-
<p className="text-xs sm:text-sm uppercase tracking-widest font-bold mt-2.5 mb-1" style={{ color: "#00e5ff" }}>Executive Summary</p>
7334-
<p className="text-xs sm:text-sm leading-relaxed" style={{ color: "#d4e3ea" }}>{defangProse(aiSummary.executive_summary)}</p>
7335-
</>
7336-
)}
7337-
<p className="text-xs sm:text-sm uppercase tracking-widest font-bold mt-3 mb-1" style={{ color: "#00e5ff" }}>Technical Analysis</p>
7338-
<p className="text-xs sm:text-sm font-medium leading-relaxed" style={{ color: "#b8c9d1" }}>{defangProse(aiSummary.summary)}</p>
7339-
{aiSummary.recommendations.length > 0 && (
7340-
<div className="mt-2.5">
7341-
<p className="text-xs sm:text-sm uppercase tracking-widest font-bold mb-1" style={{ color: "#00e5ff" }}>Recommendations</p>
7342-
{aiSummary.recommendations.map((rec, i) => (
7343-
<div key={i} className="flex items-start gap-1.5 text-xs sm:text-sm py-0.5 leading-relaxed font-medium" style={{ color: "#9fb3bd" }}>
7344-
<span className="shrink-0" style={{ color: "#c084fc" }}>▸</span> <span>{defangProse(rec)}</span>
7345-
</div>
7346-
))}
7347-
</div>
7360+
{aiScanState === "done" ? (
7361+
<button onClick={() => { setAiScanState("idle"); setAiScanCounts(null); }}
7362+
className="text-[11px] underline" style={{ color: "#8aa0ad", cursor: "pointer", background: "none", border: "none" }}>
7363+
re-scan
7364+
</button>
7365+
) : (
7366+
<button onClick={runAIScan}
7367+
disabled={aiScanState === "loading"}
7368+
title="Deep artifact extraction — scheduled tasks, services, registry ops, command lines, file paths"
7369+
className="flex items-center gap-1 rounded-lg px-2.5 py-1.5 text-xs font-semibold"
7370+
style={{
7371+
color: "#04111a",
7372+
backgroundColor: "#fde047",
7373+
border: "1px solid rgba(253,224,71,0.6)",
7374+
cursor: aiScanState === "loading" ? "not-allowed" : "pointer",
7375+
}}>
7376+
{aiScanState === "loading" ? <Loader2 size={12} className="animate-spin" /> : <span style={{ fontSize: 11 }}>🧠</span>}
7377+
{aiScanState === "loading" ? "Scanning…" : "AI Scan Artifacts"}
7378+
</button>
73487379
)}
73497380
</div>
7350-
)}
7351-
7352-
{aiState === "error" && (
7353-
<div className="pt-2">
7354-
<p className="text-xs sm:text-sm leading-relaxed" style={{ color: "#ffb4b4" }}>
7355-
The AI engines are experiencing high traffic right now, so a summary couldn't be generated. Please give it a moment and retry.
7381+
{aiScanState === "error" && (
7382+
<p className="pt-2 text-xs" style={{ color: "#ffb4b4" }}>
7383+
{aiScanError || "AI scan failed. Please retry."}
7384+
<button onClick={() => { setAiScanState("idle"); setAiScanError(""); }}
7385+
className="ml-2 underline" style={{ color: "#fde047" }}>reset</button>
73567386
</p>
7357-
<button onClick={retryAi} disabled={cooldown > 0}
7358-
className="mt-2.5 flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold"
7359-
style={{
7360-
color: cooldown > 0 ? "#5d7382" : "#c084fc",
7361-
border: `1px solid ${cooldown > 0 ? "rgba(120,160,180,0.25)" : "rgba(192,132,252,0.45)"}`,
7362-
backgroundColor: cooldown > 0 ? "rgba(120,160,180,0.06)" : "rgba(192,132,252,0.10)",
7363-
cursor: cooldown > 0 ? "not-allowed" : "pointer",
7364-
}}>
7365-
<RefreshCw size={13} />
7366-
{cooldown > 0 ? `Retry available in ${cooldown}s` : "Retry AI Summary"}
7367-
</button>
7368-
</div>
7369-
)}
7370-
7371-
{aiState === "idle" && (
7372-
<p className="text-xs sm:text-sm pt-2 animate-pulse" style={{ color: "#9fb3bd" }}>
7373-
Initializing…
7374-
</p>
7375-
)}
7376-
</div>
7377-
)}
7387+
)}
7388+
{aiScanState === "done" && aiScanCounts && (aiScanCounts.scheduled_tasks + aiScanCounts.services + aiScanCounts.registry_ops + aiScanCounts.command_lines + aiScanCounts.file_paths) === 0 && (
7389+
<p className="pt-2 text-xs" style={{ color: "#8aa0ad" }}>
7390+
No additional artifacts found beyond what regex already captured.
7391+
</p>
7392+
)}
7393+
</div>
7394+
)}
7395+
</div>
73787396
</div>
73797397
)}
73807398

0 commit comments

Comments
 (0)