Skip to content

Commit 2cba25d

Browse files
Merge pull request #5 from RootThePlanet/copilot/improve-toolbar-popup-info
Improve popup alert usefulness: re-evaluate severities, add recommendations, group by site
2 parents 759eec6 + 4375810 commit 2cba25d

3 files changed

Lines changed: 73 additions & 20 deletions

File tree

background.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ async function processTokenObservation({
193193
alertsToAdd.push(
194194
buildAlert(
195195
"unexpected_token_change",
196-
sensitivity === "high" ? "high" : "medium",
196+
sensitivity === "high" ? "medium" : "low",
197197
{
198198
site,
199199
source,

popup.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,21 @@
219219
.dismiss-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
220220
.dismiss-btn svg { width: 14px; height: 14px; }
221221

222+
/* =================== SITE GROUP HEADER =================== */
223+
.site-group-header {
224+
display: flex; align-items: center; gap: 6px;
225+
padding: 6px 4px 4px; margin-top: 4px;
226+
font-size: 11px; font-weight: 600; color: var(--fg-muted);
227+
text-transform: uppercase; letter-spacing: 0.04em;
228+
}
229+
.site-group-header:first-child { margin-top: 0; }
230+
.site-group-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
231+
.site-group-count {
232+
font-size: 10px; font-weight: 700; min-width: 18px; height: 18px;
233+
display: inline-flex; align-items: center; justify-content: center;
234+
border-radius: var(--radius-full); background: var(--border); color: var(--fg-secondary);
235+
}
236+
222237
/* =================== EMPTY STATE =================== */
223238
.empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 32px 16px; text-align: center; }
224239
.empty-icon { width: 48px; height: 48px; border-radius: 50%; background: var(--accent-soft); color: var(--accent); display: flex; align-items: center; justify-content: center; margin-bottom: 12px; }

popup.js

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -148,46 +148,56 @@ function getAlertInsights(alert, severity) {
148148

149149
if (alert.type === "unexpected_token_change") {
150150
return {
151-
reason: `SessionSentinel observed ${token} changing repeatedly within a short time window on ${site}.`,
151+
reason: `The ${source} token "${token}" on ${site} changed multiple times within a short window.`,
152152
severityReason:
153-
severity === "high"
154-
? "Marked high because rapid token churn strongly suggests active session instability or tampering."
155-
: "Marked medium because repeated token changes can indicate unusual authentication behavior.",
153+
severity === "medium"
154+
? "Rated medium because this was detected at high sensitivity. Frequent changes may still be normal for this site."
155+
: "Rated low because many websites routinely rotate session tokens as a security measure to prevent theft. This is usually expected behavior.",
156156
plainMeaning:
157-
"Your signed-in state may be getting replaced repeatedly. This can happen during risky account activity or aggressive re-authentication."
157+
"Your session token was refreshed several times in quick succession. Most sites do this intentionally to protect your account — it's a standard security practice called token rotation.",
158+
recommendation:
159+
"No action needed. This is almost always normal. Only investigate if you also see unexpected account activity (e.g. password change emails, unfamiliar logins) or if you were not actively using the site at the time."
158160
};
159161
}
160162

161163
if (alert.type === "possible_replay") {
162164
return {
163-
reason: `A previously seen ${token} value for ${site} appeared again in ${source}.`,
165+
reason: `A previously used value for "${token}" on ${site} has reappeared in ${source}. This means a token that was already replaced is now active again.`,
164166
severityReason:
165-
"Marked high because replayed session values are a strong indicator of token reuse risk.",
167+
"Rated high because a replayed token can indicate that someone captured an old session value and is attempting to reuse it to gain access to your account.",
166168
plainMeaning:
167-
"An old sign-in token seems to be active again. That can mean someone reused a prior session."
169+
"An old login token that should have been expired or replaced is being used again. This is a potential sign of session hijacking — someone may have stolen a previous token and is replaying it.",
170+
recommendation:
171+
"Take action: Log out of the affected site and log back in to force a new session. If available, revoke all active sessions from the site's security settings. Change your password if you suspect unauthorized access."
168172
};
169173
}
170174

171175
if (alert.type === "concurrent_session_usage") {
172176
return {
173-
reason: `${token} was detected in multiple browser stores/profiles for ${site}.`,
177+
reason: `The token "${token}" for ${site} was found active in multiple browser cookie stores simultaneously. This means the same session credential exists in more than one browser profile or container.`,
174178
severityReason:
175-
"Marked high because simultaneous reuse across stores can indicate session sharing or hijacking.",
179+
"Rated high because a legitimate session token should only exist in one browser context. Duplication across stores can indicate the token was copied or exported.",
176180
plainMeaning:
177-
"The same login session appears in more than one browser profile at once, which can be suspicious."
181+
"Your login session for this site is active in multiple browser profiles at the same time. This could mean someone copied your session cookie to another browser to impersonate you.",
182+
recommendation:
183+
"Take action: Log out of the affected site across all profiles. Revoke active sessions from the site's security settings if available. If you intentionally use multiple profiles on the same site, you can dismiss this alert."
178184
};
179185
}
180186

181187
return {
182188
reason: alert.message || `SessionSentinel flagged ${prettifyType(alert.type)} on ${site}.`,
183189
severityReason:
184190
severity === "high"
185-
? "Marked high because this event matches a high-risk session behavior pattern."
191+
? "Rated high because this event matches a pattern strongly associated with session compromise."
186192
: severity === "medium"
187-
? "Marked medium because this event is suspicious but less conclusive."
188-
: "Marked low because this is informational or lower-confidence suspicious behavior.",
193+
? "Rated medium because this event is suspicious but not conclusive on its own."
194+
: "Rated low this is informational. The detected behavior is likely normal but was logged for your awareness.",
189195
plainMeaning:
190-
"Something unusual was detected in your session activity. Review where and when this happened."
196+
"Something unusual was detected in your session activity. Review the details to determine if this aligns with your recent actions on the site.",
197+
recommendation:
198+
severity === "high"
199+
? "Take action: Review your account activity on the affected site and consider logging out and back in."
200+
: "No action needed unless you notice other suspicious activity on the affected site."
191201
};
192202
}
193203

@@ -293,9 +303,10 @@ function renderAlert(alert) {
293303
details.className = "alert-details";
294304
details.hidden = !isExpanded;
295305
details.append(
296-
createDetailsRow("Reason", insights.reason),
297-
createDetailsRow("Severity", insights.severityReason),
298-
createDetailsRow("Meaning", insights.plainMeaning)
306+
createDetailsRow("Why this was flagged", insights.reason),
307+
createDetailsRow("Severity rationale", insights.severityReason),
308+
createDetailsRow("What this means", insights.plainMeaning),
309+
createDetailsRow("Recommended action", insights.recommendation)
299310
);
300311

301312
content.append(header, msg, meta, details);
@@ -413,7 +424,34 @@ async function render() {
413424
return;
414425
}
415426

416-
alertsEl.replaceChildren(...filtered.map(renderAlert));
427+
// Group alerts by site
428+
const groups = new Map();
429+
for (const a of filtered) {
430+
const site = a.site || "Unknown site";
431+
if (!groups.has(site)) groups.set(site, []);
432+
groups.get(site).push(a);
433+
}
434+
435+
const fragment = document.createDocumentFragment();
436+
for (const [site, siteAlerts] of groups) {
437+
// Site group header
438+
const header = document.createElement("div");
439+
header.className = "site-group-header";
440+
const siteLabel = document.createElement("span");
441+
siteLabel.className = "site-group-name";
442+
siteLabel.textContent = site;
443+
const countBadge = document.createElement("span");
444+
countBadge.className = "site-group-count";
445+
countBadge.textContent = siteAlerts.length;
446+
header.append(siteLabel, countBadge);
447+
fragment.appendChild(header);
448+
449+
// Alerts for this site
450+
for (const a of siteAlerts) {
451+
fragment.appendChild(renderAlert(a));
452+
}
453+
}
454+
alertsEl.replaceChildren(fragment);
417455
}
418456

419457
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)