@@ -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 = "v119 ";
58+ const APP_VERSION = "v120 ";
5959
6060// ============================================================
6161// IOC Whitelist — exact-match auto-removal from parsed results
@@ -2822,6 +2822,7 @@ export default function App() {
28222822 yaraHits: Array.isArray(yara) ? yara.map((y) => y.rule_name).filter(Boolean).slice(0, 3) : [],
28232823 timesSubmitted: typeof vAttr.times_submitted === "number" ? vAttr.times_submitted : null,
28242824 firstSubmission: vAttr.first_submission_date ? new Date(vAttr.first_submission_date * 1000).toISOString().split("T")[0] : null,
2825+ lastSubmission: vAttr.last_submission_date ? new Date(vAttr.last_submission_date * 1000).toISOString().split("T")[0] : null,
28252826 signatureVerified: vAttr.signature_info?.verified || null,
28262827 signatureSigners: vAttr.signature_info?.signers || null,
28272828 };
@@ -3738,6 +3739,9 @@ export default function App() {
37383739 // AbuseIPDB lastReported = last time this IP was reported malicious —
37393740 // include as Last Seen if it's more recent than other sources.
37403741 results.abuseipdb?.lastReported,
3742+ // Tri.age's analysis-completed date and VT's last-submission date —
3743+ // both fold into the same consolidated Last Seen, not a separate chip.
3744+ results.triage?.completed, results.virustotal?.lastSubmission,
37413745 ].filter(Boolean).sort();
37423746 if (allFirsts.length || allLasts.length) {
37433747 const firstDate = allFirsts.length ? allFirsts[0] : null;
@@ -7335,12 +7339,26 @@ export default function App() {
73357339 🔴 Domain Deleted / Taken Down
73367340 </span>
73377341 )}
7338- {enr?.data?.domainReg?.state === "active" && enr.data.domainReg.ageDays != null && enr.data.domainReg.ageDays < 30 && (
7339- <span className="ml-1.5 text-[9px] rounded px-1 py-0.5 align-middle font-bold"
7340- style={{ color: "#ff4d6d", backgroundColor: "rgba(255,77,109,0.15)", border: "1px solid rgba(255,77,109,0.3)" }}>
7341- 🔴 Newly Created Domain
7342- </span>
7343- )}
7342+ {(() => {
7343+ const dr = enr?.data?.domainReg;
7344+ const sd = enr?.data?.urlscan;
7345+ const hasReg = dr?.state === "active" && dr?.ageDays != null;
7346+ const ageDays = hasReg ? dr.ageDays : (sd?.apexAgeDays != null ? sd.apexAgeDays : null);
7347+ if (ageDays == null) return null;
7348+ const isNew = ageDays < 90;
7349+ const isYoung = ageDays >= 90 && ageDays < 180;
7350+ if (!isNew && !isYoung) return null;
7351+ return (
7352+ <span className="ml-1.5 text-[9px] rounded px-1 py-0.5 align-middle font-bold"
7353+ style={{
7354+ color: isNew ? "#ff4d6d" : "#fbbf24",
7355+ backgroundColor: isNew ? "rgba(255,77,109,0.15)" : "rgba(251,191,36,0.15)",
7356+ border: `1px solid ${isNew ? "rgba(255,77,109,0.3)" : "rgba(251,191,36,0.3)"}`,
7357+ }}>
7358+ {isNew ? "🔴 Newly Created Domain" : "🟡 Young Domain"}
7359+ </span>
7360+ );
7361+ })()}
73447362 {/* In-flight indicator: partial data exists but more engines still running */}
73457363 {enr?.loading && enr?.data && (
73467364 <span className="ml-1 inline-flex items-center" title="Enrichment still in progress — more data arriving">
@@ -7930,23 +7948,16 @@ export default function App() {
79307948 ))}
79317949
79327950 {/* ── TIMELINE ── */}
7933- {!isCondensed && (hasTimeline || hasApexObs || (hasDomainReg && d.domainReg.state !== "deleted") || d.triage?.submitted || d.virustotal?.firstSubmission) && secRow("Timeline", (
7951+ {/* Tri.age (submitted/completed) and VT (first/last submission)
7952+ feed straight into the same consolidated First/Last Seen chip
7953+ below via allFirsts/allLasts — no separate per-engine chip. */}
7954+ {!isCondensed && (hasTimeline || hasApexObs || (hasDomainReg && d.domainReg.state !== "deleted")) && secRow("Timeline", (
79347955 <>
79357956 {hasTimeline && (
79367957 <span className="rounded-full px-2 py-0.5" style={{ color: "#94a3b8", backgroundColor: "rgba(148,163,184,0.08)", border: "1px solid rgba(148,163,184,0.25)" }}>
79377958 🕐{d._timeline.firstFmt ? ` First Seen: ${d._timeline.firstFmt}` : ""}{d._timeline.lastFmt ? ` · Last Seen: ${d._timeline.lastFmt}` : ""}
79387959 </span>
79397960 )}
7940- {(d.triage?.submitted || d.triage?.completed) && (
7941- <span className="rounded-full px-2 py-0.5 text-[9px]" style={{ color: "#fbbf24", backgroundColor: "rgba(251,191,36,0.08)", border: "1px solid rgba(251,191,36,0.3)" }}>
7942- 🕐 Tri.age{d.triage.submitted ? ` · Submitted ${fmtDate(d.triage.submitted)}` : ""}{d.triage.completed ? ` · Completed ${fmtDate(d.triage.completed)}` : ""}
7943- </span>
7944- )}
7945- {d.virustotal?.firstSubmission && (
7946- <span className="rounded-full px-2 py-0.5 text-[9px]" style={{ color: "#c084fc", backgroundColor: "rgba(192,132,252,0.08)", border: "1px solid rgba(192,132,252,0.3)" }}>
7947- 🕐 VT · First submission {fmtDate(d.virustotal.firstSubmission)}{d.virustotal.timesSubmitted != null ? ` · Submitted ${d.virustotal.timesSubmitted}×` : ""}
7948- </span>
7949- )}
79507961 {hasDomainReg && d.domainReg.state !== "deleted" && (() => {
79517962 const dr = d.domainReg;
79527963 const sd = d.urlscan;
0 commit comments