Skip to content

feat: implement full CyberPot Attack Map redesign - #4

Open
khulnasoft-bot wants to merge 2 commits into
masterfrom
cyberpot-attack-map-redesign
Open

feat: implement full CyberPot Attack Map redesign#4
khulnasoft-bot wants to merge 2 commits into
masterfrom
cyberpot-attack-map-redesign

Conversation

@khulnasoft-bot

@khulnasoft-bot khulnasoft-bot commented Feb 6, 2026

Copy link
Copy Markdown

This PR introduces a complete redesign of the CyberPot Attack Map, enhancing its visual foundation and interactive capabilities.

Problem/Issue/Goal:

  • The existing Attack Map required a modern visual overhaul.
  • There was a need for enhanced map features and an improved dashboard.
  • Interactive features and real-time animations were desired for a better user experience.

Fix/Solution:

  • Implemented a complete redesign of the visual foundation.
  • Enhanced map functionalities and the dashboard.
  • Introduced interactive features and real-time animations.
  • Applied comprehensive optimizations for improved performance and user experience.

Chat link: https://v0.app/chat/EQkhcBRpF6c

Summary by Sourcery

Redesign the CyberPot Attack Map as an interactive, responsive cybersecurity dashboard with richer visualizations, analytics, and real-time feedback.

New Features:

  • Add multiple attack-map visualization modes, including heatmap, arc, and node views with enhanced animated attack paths and markers.
  • Introduce dashboard tabs for live feeds, top attackers, geographic summaries, and aggregated statistics.
  • Add interactive filtering, advanced search, map tooltips, region focus, and context-menu support.
  • Add real-time counter transitions, anomaly detection and alerts, and pulse/glow effects.

Enhancements:

  • Refresh the interface with a professional cybersecurity color system, redesigned dashboard and map styling, and enhanced light and dark themes.
  • Improve responsive behavior, keyboard accessibility, reduced-motion support, contrast handling, and rendering performance across screen sizes.

Build:

  • Add Node/Next.js project metadata and dependency lockfile.

Documentation:

  • Add a comprehensive implementation guide and design-token reference for the redesigned attack map.

Chores:

  • Add editor settings and wire the new enhancement modules into the attack-map page.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced dashboard with tabs, real-time statistics, and live attack feeds
    • Added heatmap, arc, and node-based map visualizations
    • Added advanced filtering, search, tooltips, and context menus
    • Added real-time counters, transitions, anomaly alerts, and visual effects
  • Styling

    • Introduced a cybersecurity-focused color palette, design tokens, and animations
    • Improved responsive layouts, keyboard focus states, accessibility, and reduced-motion support
  • Documentation

    • Added design token documentation and a phased redesign implementation guide

Complete redesign of visual foundation, map enhancements, dashboard, interactive features, and real-time animations with comprehensive optimizations.

Co-authored-by: KhulnaSoft bot <43526132+khulnasoft-bot@users.noreply.github.com>
@vercel

vercel Bot commented Feb 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v0-cyberpot-attack-map Error Error Aug 24, 2026 11:21pm

@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e6d9c82d-becc-487c-abfc-fe3544bfac9c

📥 Commits

Reviewing files that changed from the base of the PR and between d6ea26d and b4ddfc9.

📒 Files selected for processing (1)
  • static/realtime-animations.js

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

The redesign adds map, dashboard, interaction, and real-time animation modules. It also adds design documentation, CSS themes and animations, deferred script loading, and project configuration.

Changes

Project setup and design documentation

Layer / File(s) Summary
Project configuration
.vscode/settings.json, package.json, node_modules
Adds read-only workspace settings, Next.js TypeScript project metadata, scripts, dependencies, and a node_modules path entry.
Design tokens and implementation guide
static/design-tokens.css, REDESIGN_IMPLEMENTATION_GUIDE.md
Adds design-token documentation and a six-phase implementation guide for the redesigned attack map frontend.

Frontend integration and presentation

Layer / File(s) Summary
Styling system
static/index.css
Adds cybersecurity color tokens, theme refinements, animations, map and dashboard styles, protocol badges, responsive rules, accessibility support, print rules, and performance hints.
Script integration
static/index.html
Loads map, dashboard, interactive, and real-time animation modules with defer before map.js.

Client-side enhancement modules

Layer / File(s) Summary
Map visualization
static/map-enhancements.js
Adds heatmap, arc, and node visualization modes, density tracking, intensity styling, animations, ripples, and the window.mapEnhancements API.
Dashboard management
static/dashboard-enhancements.js
Adds managers for tabs, statistic cards, live attack feeds, and data aggregation through window.dashboardEnhancements.
Interactive features
static/interactive-features.js
Adds filtering, map tooltips, search, and context menus through window.interactiveFeatures.
Real-time animation and anomaly management
static/realtime-animations.js
Adds counter animation, queued transitions, anomaly detection and alerts, pulse effects, and the window.realtimeAnimations API.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: 🔵 Low · up to b4ddf

The redesign may throw a runtime error when anomaly detection receives missing statistics data. The change is localized and mergeable with owner follow-up, but normalizing the input before reading its fields is recommended.

Poem

A rabbit counts the glowing beats,

Smooth transitions move through streets,
Alerts rise when anomalies appear,
Maps and dashboards shift gear,
Four new helpers brighten the night.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: a full redesign of the CyberPot Attack Map. It matches the documented visual, map, dashboard, interactive, and animation enhancements.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 2 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cyberpot-attack-map-redesign

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
static/index.css (1)

1790-1834: ⚠️ Potential issue | 🟠 Major

Hiding all scrollbars globally is an accessibility concern.

Setting scrollbar-width: none on * and hiding WebKit scrollbars removes a critical visual affordance. Users who rely on visible scrollbars (e.g., those with motor impairments or those unfamiliar with trackpad gestures) will have no indication that content is scrollable. Consider restricting this to specific containers where it's a deliberate design choice, or use thin scrollbars (scrollbar-width: thin) as a compromise.

static/index.html (1)

345-363: ⚠️ Potential issue | 🔴 Critical

HTML tab data-tab values don't match DashboardTabManager.tabs keys — tab switching will silently fail.

The HTML defines data-tab="countries" (line 353) and data-tab="overview" (line 357), but DashboardTabManager (in dashboard-enhancements.js) expects 'top-countries' and 'stats'. Similarly, the data-pane attributes on .tab-pane elements use IDs like overview-tab and countries-tab, which don't follow a data-pane="..." attribute pattern at all — switchTab queries [data-pane="${tabName}"] which won't find these elements.

Either update the HTML attributes to match the manager's keys, or update the manager's keys to match the HTML. Here's one direction:

🐛 Proposed HTML fix (align with manager)
                     <button class="tab-btn" data-tab="top-ips">
                         <i class="fas fa-bullseye"></i>
                         Top IPs
                     </button>
-                    <button class="tab-btn" data-tab="countries">
+                    <button class="tab-btn" data-tab="top-countries">
                         <i class="fas fa-globe"></i>
                         Top Countries
                     </button>
-                    <button class="tab-btn" data-tab="overview">
+                    <button class="tab-btn" data-tab="stats">
                         <i class="fas fa-chart-bar"></i>
                         Dashboard
                     </button>

And add data-pane attributes to .tab-pane elements:

-                    <div class="tab-pane" id="overview-tab">
+                    <div class="tab-pane" id="overview-tab" data-pane="stats">
...
-                    <div class="tab-pane" id="top-ips-tab">
+                    <div class="tab-pane" id="top-ips-tab" data-pane="top-ips">
...
-                    <div class="tab-pane" id="countries-tab">
+                    <div class="tab-pane" id="countries-tab" data-pane="top-countries">
...
-                    <div class="tab-pane" id="live-feed-tab">
+                    <div class="tab-pane" id="live-feed-tab" data-pane="live-feed">
🤖 Fix all issues with AI agents
In @.vscode/settings.json:
- Around line 1-5: Remove the workspace setting that makes every file read-only
in .vscode/settings.json by deleting the "\"**/*\": true" entry (or remove the
entire .vscode/settings.json file) so developers can edit files after cloning;
ensure no other read-only-wide globs remain in that file and commit the cleaned
settings (or remove the file) instead.

In `@node_modules`:
- Line 1: Remove the committed artifact named "node_modules" from the repo
(delete that file entry from the index/commit) and add "node_modules" to
.gitignore to prevent future commits; ensure you run git rm --cached (or
equivalent) to untrack the file and create a new commit that removes it, then
verify the real dependency directory created by npm/yarn is not shadowed by any
lingering repo entry named "node_modules".

In `@package.json`:
- Around line 1-71: The added package.json is incorrect for this repo: it
declares a Next.js app (scripts "dev"/"build"/"start"), dozens of unused
React/Radix/etc. dependencies, and mismatched Tailwind plugins (devDependency
"@tailwindcss/postcss" vs "tailwindcss"), which don't match the static
HTML/JS/CSS + Python backend; remove the package.json file entirely from the PR
(delete package.json) so the repository remains unchanged and CI/dependency
configs are not polluted.

In `@static/dashboard-enhancements.js`:
- Around line 36-58: The tabs defined in DashboardTabManager.tabs do not match
the HTML data-tab values, causing switchTab(tabName) to reject valid clicks;
update the tabs mapping in DashboardTabManager.tabs (or the HTML data-tab
attributes) so keys exactly match the data-tab values used in HTML (e.g.,
provide entries for "countries" and "overview" or rename HTML to "top-countries"
and "stats"), ensure switchTab, this.currentTab assignment, and
this.onTabActivated(tabName) continue to use the same canonical keys, and verify
any consumers that reference DashboardTabManager.tabs keys are updated to the
chosen naming.
- Around line 97-121: The template in createCard and the updateCardValue
function inject untrusted strings (title, value, icon, color, unit, trend) via
innerHTML/template literals creating XSS/CSS-injection risks; fix by
constructing the card with DOM methods or by escaping all interpolated values:
set text nodes via element.textContent for title/value/unit/trend percent and
never insert raw HTML for icon names or numeric values, instead apply icon
classes using element.classList.add(icon) on the created <i> element, and
validate/sanitize color before applying it to style (prefer a whitelist of
allowed colors or map semantic color names to CSS classes rather than writing
raw color into style). Also replace updateCardValue's element.innerHTML =
newValue with element.textContent = String(newValue) after
validation/sanitization.
- Around line 227-252: The createFeedItemElement function is vulnerable to XSS
because untrusted fields (event.src_ip, event.dst_ip, event.protocol,
event.country, event.honeypot, and event.timestamp) are interpolated into a
template string and later inserted via insertAdjacentHTML; fix it by
HTML-escaping those fields (or building DOM nodes and using
textContent/createTextNode) before interpolation or by using a trusted sanitizer
like DOMPurify, replacing the raw interpolations in createFeedItemElement and
any caller that uses insertAdjacentHTML so only escaped/sanitized strings are
inserted into the DOM.
- Around line 348-367: recordTimeSeries currently buckets by now.getMinutes()
which collides across hours; change the bucket key to a unique minute identifier
(e.g., compute epochMinute = Math.floor(now.getTime()/60000) or format a HH:mm
string) and use that as the comparison key instead of minute so entries for the
same minute-of-hour but different hours won't merge; update references in
recordTimeSeries to compare this new bucket key against the last entry in
this.stats.timeSeriesData (e.g., last.bucket or last.epochMinute), preserve the
display time (time: now.toLocaleTimeString()) and the rolling trim (keep length
<= 60) logic.

In `@static/index.css`:
- Around line 2961-2970: There are duplicate/conflicting `@keyframes`
definitions—@keyframes slideIn, `@keyframes` countUp, and `@keyframes`
fadeOut—causing later definitions to override earlier ones; locate all
definitions of these keyframe names (search for "@keyframes slideIn",
"@keyframes countUp", "@keyframes fadeOut") and consolidate or rename them so
each animation name has a single canonical definition (or give distinct names
like slideIn-up/slideIn-down, countUp-scale/countUp-slide, fadeOut-x/fadeOut-y)
and then update any CSS classes or HTML that reference the old names (animation
or animation-name) to use the chosen canonical/renamed keyframe to preserve
intended behavior.
- Around line 4190-4198: The CSS custom property --text-primary is declared
directly inside the `@media` (prefers-color-scheme: dark) block without a
selector, which is invalid; move the variable declaration into a valid selector
(e.g., :root or the `@media` block's body selector) so browsers apply it correctly
— update the `@media` (prefers-color-scheme: dark) block to contain a selector
(such as :root or body) and place --text-primary: `#E8EFF7`; there, leaving the
existing body { background-attachment: fixed; } rule intact.

In `@static/index.html`:
- Around line 553-556: Add SHA-384 SRI integrity attributes to the four new
script tags so they match the rest of the file's CSP/SRI setup: compute each
file's base64 SHA-384 using the provided command (openssl dgst -sha384 -binary
your.file.name | openssl base64 -A) and add integrity="sha384-<BASE64_HASH>" and
crossorigin="anonymous" to the <script> tags for static/map-enhancements.js,
static/dashboard-enhancements.js, static/interactive-features.js, and
static/realtime-animations.js; ensure the integrity value is the exact output
and that crossorigin="anonymous" is present to mirror the existing script tag
pattern.

In `@static/interactive-features.js`:
- Around line 228-256: The formatEventContent method is vulnerable to XSS
because it interpolates event.src_ip, event.dst_ip, event.protocol,
event.country, and event.dst_port directly into an HTML template; fix it by
creating and using an HTML-escape helper (e.g., escapeHtml) and apply it to each
dynamic value before interpolation in formatEventContent (e.g.,
escapeHtml(event.src_ip) etc.), and ensure this.escapeHtml is used for the value
passed into this.formatTime if that returns user-controllable text; update
references in formatEventContent to call the sanitizer for protocol, src_ip,
dst_ip, country, dst_port (and any other interpolated event fields).

In `@static/map-enhancements.js`:
- Around line 16-39: attackDensityGrid currently grows unbounded because
updateAttackDensity only increments entries; add a bounded eviction/reset
mechanism by implementing a clearDensityGrid() function (and optionally a
touch/timestamp map) and call it periodically or when visualization mode
switches; modify updateAttackDensity to record last-seen timestamps (or
increment a rolling counter) so you can prune stale keys when grid size exceeds
a cap, and ensure getGridCellKey and GRID_SIZE remain the aggregation basis
while you add the periodic setInterval or mode-switch hook to clear or prune
attackDensityGrid.

In `@static/realtime-animations.js`:
- Around line 197-241: The detectAnomalies function currently calls
Object.entries on currentStats.protocolStats and currentStats.countryStats
without guarding for null/undefined; update detectAnomalies to first normalize
currentStats (e.g., const cs = currentStats || {}) and derive safe locals like
const protocolStats = cs.protocolStats || {} and const countryStats =
cs.countryStats || {} and use a safe attacksPerMinute (e.g., const
attacksPerMinute = cs.attacksPerMinute || 0) so the Object.entries and
comparisons never receive null/undefined; modify all usages in detectAnomalies
(references to currentStats, currentStats.protocolStats,
currentStats.countryStats, and currentStats.attacksPerMinute) to use these safe
locals.
- Around line 287-303: The showAnomalyAlert method currently sets
alertDiv.innerHTML with unsanitized anomaly.message and anomaly.value causing an
XSS risk; change it to build the alert DOM using createElement and textContent
(or setAttribute for non-text) so that anomaly.message and anomaly.value are
never injected as HTML—locate showAnomalyAlert and replace the innerHTML
template with explicit DOM nodes for the icon, title, details and close button,
using textContent for the title/details and className or appendChild to compose
the structure.
🟡 Minor comments (7)
static/map-enhancements.js-200-215 (1)

200-215: ⚠️ Potential issue | 🟡 Minor

lineGraph.node() can be null — unguarded getTotalLength() call will throw.

If the SVG element isn't ready or the append fails, lineGraph.node() returns null, and calling .getTotalLength() on it will throw a TypeError. Add a null check.

🛡️ Defensive fix
     // Line animation with dash effect
     const length = lineGraph.node().getTotalLength();
+    if (!lineGraph.node()) return;
+    const length = lineGraph.node().getTotalLength();
static/map-enhancements.js-240-268 (1)

240-268: ⚠️ Potential issue | 🟡 Minor

Ripple setTimeout callbacks append to SVG but are not cleaned up on map zoom.

Per project conventions, D3 elements should be cleared on zoom to prevent coordinate desynchronization. The setTimeout-based ripple circles persist across zoom events and will render at stale coordinates. Based on learnings, D3 elements should be cleared on zoom to prevent coordinate desynchronization between visual elements and data.

REDESIGN_IMPLEMENTATION_GUIDE.md-159-159 (1)

159-159: ⚠️ Potential issue | 🟡 Minor

Minor grammar: compound adjectives before nouns need hyphens.

  • Line 159: "Full featured interface" → "Full-featured interface"
  • Line 387: "Production Ready" → "Production-ready"

Also applies to: 387-387

REDESIGN_IMPLEMENTATION_GUIDE.md-154-170 (1)

154-170: ⚠️ Potential issue | 🟡 Minor

WCAG 2.1 Level AA compliance claim is premature given current implementation gaps.

The guide claims WCAG 2.1 Level AA compliance (line 165), but the reviewed JS modules generate interactive HTML (tooltips, context menus, feed items, stat cards) without ARIA roles, labels, or keyboard interaction support. The ContextMenuManager creates menu items with no keyboard navigation, no focus management, and no role="menu" / role="menuitem" attributes. Consider softening the claim to an aspiration or adding the missing accessibility attributes.

static/interactive-features.js-413-415 (1)

413-415: ⚠️ Potential issue | 🟡 Minor

Global contextmenu listener registered with no-op handler and no preventDefault.

ContextMenuManager.init() attaches a global contextmenu listener, but handleContextMenu is empty — it neither calls e.preventDefault() nor creates any menu. This means a new listener is added on every instantiation with no effect, and the native context menu is never suppressed. Either implement the handler or defer registration until createContextMenu is actually wired up.

REDESIGN_IMPLEMENTATION_GUIDE.md-96-98 (1)

96-98: ⚠️ Potential issue | 🟡 Minor

Documentation lists "Critical" severity but the code only implements "high", "medium", and "low".

Line 97 lists four severity levels: Low/Medium/High/Critical. However, FilterSystemManager.checkSeverity (in interactive-features.js, lines 78-92) only handles 'high', 'medium', and 'low' — there is no 'critical' branch. Either add the missing severity level in the code or correct the documentation.

,

static/interactive-features.js-187-212 (1)

187-212: ⚠️ Potential issue | 🟡 Minor

Remove the closeButton option from the tooltip configuration at line 199 — it is not supported by Leaflet's L.tooltip() and will be silently ignored.

The closeButton option is only valid for L.popup(), not L.tooltip(). Either use L.popup if you need a dismissible overlay with a close button, or remove this option from the tooltip configuration.

🧹 Nitpick comments (10)
static/index.css (2)

1199-1208: Redundant theme-specific overrides — both are identical to the base rule.

Both [data-theme="light"] .protocol-other and [data-theme="dark"] .protocol-other declare the exact same values (background: #78909C; color: white;) as the generic .protocol-other at Line 1194. These blocks can be removed.


4142-4144: Permanent will-change: transform on .map-container wastes GPU memory.

will-change should be applied just before an animation and removed after. Keeping it permanently on a large container promotes it to its own compositing layer, consuming GPU memory for the lifetime of the page. If the map container isn't frequently animated, remove this or scope it to an active animation class.

static/design-tokens.css (1)

1-200: This file is pure documentation (100% comments) — consider moving it out of the stylesheet pipeline.

Loading this as a <link rel="stylesheet"> adds an HTTP request that delivers zero CSS rules. The documentation is valuable but would be better served as:

  • Inline comments in index.css (where the tokens are already defined), or
  • A Markdown file (e.g., DESIGN_TOKENS.md)
static/realtime-animations.js (2)

359-369: duration parameter is accepted but never used.

createPulse(element, color, duration) accepts a duration parameter but doesn't apply it. The animation duration is controlled entirely by the .glow-element CSS class (hardcoded at 2s in index.css). Either use the parameter to set animation-duration on the element's style, or remove it from the signature to avoid misleading callers.


87-115: Serial transition queue blocks all queued transitions behind each other.

SmoothTransitionManager processes one transition at a time. If multiple independent elements are queued (e.g., several feed items appearing simultaneously), they'll animate sequentially rather than in parallel. For a real-time dashboard with high event throughput, this can cause a growing backlog.

Consider allowing parallel transitions for distinct elements and only serializing transitions on the same element.

static/interactive-features.js (4)

10-22: this.filters is initialized but never read — only this.activeFilters is used.

The this.filters object (lines 12-19) with its predefined keys is set in the constructor but never referenced anywhere; all filter logic operates on this.activeFilters. This creates confusion about which is the source of truth. Either remove this.filters or use it as a schema to validate filterType in applyFilter.

♻️ Option: use `filters` as a validation schema
     applyFilter(filterType, filterValue) {
+        if (!(filterType in this.filters)) {
+            console.warn(`[FILTER-SYSTEM] Unknown filter type: ${filterType}`);
+            return;
+        }
         if (filterValue === null || filterValue === '') {

326-333: Math.random() as fallback ID risks collisions.

Using Math.random() for the search index id (line 329) can produce duplicates across items in the same batch. Consider crypto.randomUUID() (widely supported in modern browsers) or a simple incrementing counter for uniqueness.


370-385: advancedSearch regex allows injection of arbitrary data keys.

The regex /(\w+):([^\s]+)/g parses user input into key-value pairs and performs a strict equality check against item.data[key]. While there's no direct security exploit here, a query like __proto__:foo or constructor:bar would access prototype properties. Consider validating key against a whitelist of known fields.

♻️ Proposed safeguard
     advancedSearch(query) {
+        const allowedFields = ['src_ip', 'dst_ip', 'protocol', 'country', 'honeypot', 'src_ip_rep', 'region', 'dst_port'];
         const params = {};
         const regex = /(\w+):([^\s]+)/g;
         let match;
 
         while ((match = regex.exec(query)) !== null) {
-            params[match[1]] = match[2];
+            if (allowedFields.includes(match[1])) {
+                params[match[1]] = match[2];
+            }
         }
 
         return this.searchIndex.filter(item => {
-            for (const [key, value] of Object.entries(params)) {
-                if (item.data[key] !== value) return false;
+            for (const [key, value] of Object.entries(params)) {
+                if (!Object.prototype.hasOwnProperty.call(item.data, key) || item.data[key] !== value) return false;
             }
             return true;
         }).map(item => item.data);
     }

468-473: Exporting classes rather than instances means each consumer must instantiate separately.

window.interactiveFeatures exposes the raw classes, while window.dashboardEnhancements (in the other file) also exports classes. This is consistent, but note that multiple instantiations of ContextMenuManager will register duplicate global contextmenu listeners. Consider documenting that each manager should be instantiated at most once, or export singleton instances instead.

static/dashboard-enhancements.js (1)

306-316: uniqueAttackers Set grows unboundedly for long-running sessions.

In a cybersecurity dashboard that may run for hours or days, every distinct src_ip is permanently retained in the Set (line 310, 326). There is no eviction strategy. Consider capping it or resetting periodically (e.g., per time window), or tracking only a count/HyperLogLog approximation.

Comment thread .vscode/settings.json
Comment on lines +1 to +5
{
"files.readonlyInclude": {
"**/*": true
}
} No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Do not commit workspace settings that mark all files as read-only.

This setting will prevent any developer from editing files in VSCode after cloning the repo. It appears to be a leftover from the code-generation/preview environment (v0.app). Remove this file or at least remove the "**/*": true glob.

🤖 Prompt for AI Agents
In @.vscode/settings.json around lines 1 - 5, Remove the workspace setting that
makes every file read-only in .vscode/settings.json by deleting the "\"**/*\":
true" entry (or remove the entire .vscode/settings.json file) so developers can
edit files after cloning; ensure no other read-only-wide globs remain in that
file and commit the cleaned settings (or remove the file) instead.

Comment thread node_modules
@@ -0,0 +1 @@
/vercel/share/v0-next-shadcn/node_modules No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove this file — node_modules must never be committed.

This is a platform artifact from the v0.app/Vercel generation environment (/vercel/share/v0-next-shadcn/node_modules). Committing a file named node_modules will shadow the real dependency directory after npm install and break the build. Add node_modules to .gitignore if not already present.

🤖 Prompt for AI Agents
In `@node_modules` at line 1, Remove the committed artifact named "node_modules"
from the repo (delete that file entry from the index/commit) and add
"node_modules" to .gitignore to prevent future commits; ensure you run git rm
--cached (or equivalent) to untrack the file and create a new commit that
removes it, then verify the real dependency directory created by npm/yarn is not
shadowed by any lingering repo entry named "node_modules".

Comment thread package.json
Comment on lines +1 to +71
{
"name": "my-project",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint ."
},
"dependencies": {
"@hookform/resolvers": "^3.9.1",
"@radix-ui/react-accordion": "1.2.2",
"@radix-ui/react-alert-dialog": "1.1.4",
"@radix-ui/react-aspect-ratio": "1.1.1",
"@radix-ui/react-avatar": "1.1.2",
"@radix-ui/react-checkbox": "1.1.3",
"@radix-ui/react-collapsible": "1.1.2",
"@radix-ui/react-context-menu": "2.2.4",
"@radix-ui/react-dialog": "1.1.4",
"@radix-ui/react-dropdown-menu": "2.1.4",
"@radix-ui/react-hover-card": "1.1.4",
"@radix-ui/react-label": "2.1.1",
"@radix-ui/react-menubar": "1.1.4",
"@radix-ui/react-navigation-menu": "1.2.3",
"@radix-ui/react-popover": "1.1.4",
"@radix-ui/react-progress": "1.1.1",
"@radix-ui/react-radio-group": "1.2.2",
"@radix-ui/react-scroll-area": "1.2.2",
"@radix-ui/react-select": "2.1.4",
"@radix-ui/react-separator": "1.1.1",
"@radix-ui/react-slider": "1.2.2",
"@radix-ui/react-slot": "1.1.1",
"@radix-ui/react-switch": "1.1.2",
"@radix-ui/react-tabs": "1.1.2",
"@radix-ui/react-toast": "1.2.4",
"@radix-ui/react-toggle": "1.1.1",
"@radix-ui/react-toggle-group": "1.1.1",
"@radix-ui/react-tooltip": "1.1.6",
"autoprefixer": "^10.4.20",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "1.1.1",
"date-fns": "4.1.0",
"embla-carousel-react": "8.5.1",
"input-otp": "1.4.1",
"lucide-react": "^0.544.0",
"next": "16.1.6",
"next-themes": "^0.4.6",
"react": "^19",
"react-day-picker": "8.10.1",
"react-dom": "^19",
"react-hook-form": "^7.54.1",
"react-resizable-panels": "^2.1.7",
"recharts": "2.15.0",
"sonner": "^1.7.1",
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7",
"vaul": "^1.1.2",
"zod": "^3.24.1"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.1.13",
"@types/node": "^22",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8.5",
"tailwindcss": "^3.4.17",
"typescript": "5.7.3"
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

latest Next.js version 2026 release

💡 Result:

As of February 6, 2026, the latest stable Next.js release is Next.js 16.1.6, released January 27, 2026. [1][2]

If you also count pre-release (canary) builds, the newest tag is 16.2.0-canary.29 from February 5, 2026. [3]

Sources:
[1] GitHub tags (vercel/next.js) – v16.1.6 release date
[2] endoflife.date – Next.js latest versions table
[3] GitHub tags (vercel/next.js) – latest canary tag/date


🏁 Script executed:

# Check the overall project structure to understand what we're dealing with
git ls-files | head -20

Repository: khulnasoft/cyberpot-attack-map

Length of output: 455


🏁 Script executed:

# Look for the actual files mentioned in the review
find . -name "map-enhancements.js" -o -name "realtime-animations.js" 2>/dev/null | head -10

Repository: khulnasoft/cyberpot-attack-map

Length of output: 134


🏁 Script executed:

# Check if there's an app directory structure (Next.js 13+ uses this)
ls -la | grep -E "^d.*\.(next|app|pages)" || echo "No .next, app, or pages directories found"
fd -t d "app|pages" -d 2 2>/dev/null || echo "No app or pages directories"

Repository: khulnasoft/cyberpot-attack-map

Length of output: 115


🏁 Script executed:

# Check content of the new JS files to see if they're plain browser scripts
cat -n static/map-enhancements.js | head -50

Repository: khulnasoft/cyberpot-attack-map

Length of output: 1897


🏁 Script executed:

# Check if package.json was part of the original repo or added in this PR
git log --oneline package.json 2>/dev/null | head -5 || echo "No git history for package.json"

Repository: khulnasoft/cyberpot-attack-map

Length of output: 131


🏁 Script executed:

# Check git status to see what files are in this PR/change
git diff --name-only HEAD~1 2>/dev/null || git status --short 2>/dev/null | head -20 || echo "Cannot determine PR files"

Repository: khulnasoft/cyberpot-attack-map

Length of output: 159


🏁 Script executed:

# Look at the entire package.json to understand its scope
cat package.json

Repository: khulnasoft/cyberpot-attack-map

Length of output: 2274


🏁 Script executed:

# Check if there's any Next.js config or build setup
ls -la next.config.* tsconfig.json postcss.config.* 2>/dev/null || echo "No Next.js or PostCSS config found"

Repository: khulnasoft/cyberpot-attack-map

Length of output: 108


This package.json is entirely disconnected from the actual project and should not be merged.

The existing project is a static HTML/JS/CSS application with a Python backend (AttackMapServer.py, DataServer.py). All new JS files (map-enhancements.js, realtime-animations.js, etc.) are plain browser scripts—they don't use React, Next.js, Radix UI, or any dependency listed here. The repo has no Next.js structure (no app/, pages/, next.config.js, or tsconfig.json), yet this file declares Next.js scripts and 48+ unused dependencies, creating confusion for contributors and CI.

Key issues:

  1. Tailwind version conflict@tailwindcss/postcss: ^4.1.13 (Tailwind v4 plugin) is incompatible with tailwindcss: ^3.4.17 (Tailwind v3).
  2. ~48 unused dependencies — None of the Radix UI, Recharts, react-hook-form, zod, etc. packages are referenced anywhere in the added code.
  3. No Next.js application — Scripts reference next dev and next build, but the repo is a static project with no app or pages directory.

Remove this file.

🤖 Prompt for AI Agents
In `@package.json` around lines 1 - 71, The added package.json is incorrect for
this repo: it declares a Next.js app (scripts "dev"/"build"/"start"), dozens of
unused React/Radix/etc. dependencies, and mismatched Tailwind plugins
(devDependency "@tailwindcss/postcss" vs "tailwindcss"), which don't match the
static HTML/JS/CSS + Python backend; remove the package.json file entirely from
the PR (delete package.json) so the repository remains unchanged and
CI/dependency configs are not polluted.

Comment on lines +36 to +58
switchTab(tabName) {
if (!this.tabs[tabName]) {
console.warn('[DASHBOARD] Invalid tab:', tabName);
return;
}

// Update active states
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active');
});
document.querySelector(`[data-tab="${tabName}"]`)?.classList.add('active');

// Update active pane
document.querySelectorAll('.tab-pane').forEach(pane => {
pane.classList.remove('active');
});
document.querySelector(`[data-pane="${tabName}"]`)?.classList.add('active');

this.currentTab = tabName;

// Trigger tab-specific initialization if needed
this.onTabActivated(tabName);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Tab name mismatch: DashboardTabManager.tabs keys don't match the HTML data-tab values.

The manager defines tabs 'live-feed', 'top-ips', 'top-countries', 'stats' (lines 14-18), but the HTML in index.html uses data-tab="countries" (line 353) and data-tab="overview" (line 357). switchTab validates against this.tabs (line 37) so clicking "Top Countries" or "Dashboard" in the HTML will be rejected as invalid.

🤖 Prompt for AI Agents
In `@static/dashboard-enhancements.js` around lines 36 - 58, The tabs defined in
DashboardTabManager.tabs do not match the HTML data-tab values, causing
switchTab(tabName) to reject valid clicks; update the tabs mapping in
DashboardTabManager.tabs (or the HTML data-tab attributes) so keys exactly match
the data-tab values used in HTML (e.g., provide entries for "countries" and
"overview" or rename HTML to "top-countries" and "stats"), ensure switchTab,
this.currentTab assignment, and this.onTabActivated(tabName) continue to use the
same canonical keys, and verify any consumers that reference
DashboardTabManager.tabs keys are updated to the chosen naming.

Comment on lines +97 to +121
const cardHtml = `
<div class="stat-card stat-card-${id}" data-card-id="${id}">
<div class="stat-card-header">
<div class="stat-card-title-section">
<i class="fas ${icon}" style="color: ${color};"></i>
<h4 class="stat-card-title">${title}</h4>
</div>
${trend ? `
<div class="stat-card-trend ${trend > 0 ? 'positive' : 'negative'}">
<i class="fas ${trend > 0 ? 'fa-arrow-up' : 'fa-arrow-down'}"></i>
<span>${Math.abs(trend)}%</span>
</div>
` : ''}
</div>
<div class="stat-card-content">
<div class="stat-card-value" data-value="${value}">
${value}
</div>
${unit ? `<div class="stat-card-unit">${unit}</div>` : ''}
</div>
<div class="stat-card-footer">
<div class="stat-card-bar"></div>
</div>
</div>
`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

createCard and updateCardValue also use raw HTML interpolation.

createCard builds HTML with title, value, icon, and color (lines 97-121) via template literals. updateCardValue sets element.innerHTML = newValue (line 153). If any of these values originate from attack data, they are XSS vectors. Additionally, injecting color directly into a style attribute (line 101) enables CSS injection.

Use textContent instead of innerHTML in updateCardValue, and escape values in createCard.

🔒 Partial fix for updateCardValue
-            element.innerHTML = newValue;
+            element.textContent = newValue;

Also applies to: 142-161

🤖 Prompt for AI Agents
In `@static/dashboard-enhancements.js` around lines 97 - 121, The template in
createCard and the updateCardValue function inject untrusted strings (title,
value, icon, color, unit, trend) via innerHTML/template literals creating
XSS/CSS-injection risks; fix by constructing the card with DOM methods or by
escaping all interpolated values: set text nodes via element.textContent for
title/value/unit/trend percent and never insert raw HTML for icon names or
numeric values, instead apply icon classes using element.classList.add(icon) on
the created <i> element, and validate/sanitize color before applying it to style
(prefer a whitelist of allowed colors or map semantic color names to CSS classes
rather than writing raw color into style). Also replace updateCardValue's
element.innerHTML = newValue with element.textContent = String(newValue) after
validation/sanitization.

Comment thread static/index.html
Comment on lines +553 to +556
<script src="static/map-enhancements.js" defer></script>
<script src="static/dashboard-enhancements.js" defer></script>
<script src="static/interactive-features.js" defer></script>
<script src="static/realtime-animations.js" defer></script>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing Subresource Integrity (SRI) hashes on all four new script tags.

Every other <script> in this file includes an integrity attribute with an SRI hash. The four new enhancement scripts omit it entirely. This breaks the security posture established by the existing CSP and SRI setup — if any of these files are tampered with (CDN compromise, MITM, accidental corruption), the browser will still load them without verification.

Generate and add integrity attributes matching the pattern used elsewhere (SHA-384):

-        <script src="static/map-enhancements.js" defer></script>
-        <script src="static/dashboard-enhancements.js" defer></script>
-        <script src="static/interactive-features.js" defer></script>
-        <script src="static/realtime-animations.js" defer></script>
+        <script src="static/map-enhancements.js" defer integrity="sha384-HASH_HERE"></script>
+        <script src="static/dashboard-enhancements.js" defer integrity="sha384-HASH_HERE"></script>
+        <script src="static/interactive-features.js" defer integrity="sha384-HASH_HERE"></script>
+        <script src="static/realtime-animations.js" defer integrity="sha384-HASH_HERE"></script>

The comment on line 11 even provides the command: openssl dgst -sha384 -binary your.file.name | openssl base64 -A

🤖 Prompt for AI Agents
In `@static/index.html` around lines 553 - 556, Add SHA-384 SRI integrity
attributes to the four new script tags so they match the rest of the file's
CSP/SRI setup: compute each file's base64 SHA-384 using the provided command
(openssl dgst -sha384 -binary your.file.name | openssl base64 -A) and add
integrity="sha384-<BASE64_HASH>" and crossorigin="anonymous" to the <script>
tags for static/map-enhancements.js, static/dashboard-enhancements.js,
static/interactive-features.js, and static/realtime-animations.js; ensure the
integrity value is the exact output and that crossorigin="anonymous" is present
to mirror the existing script tag pattern.

Comment on lines +228 to +256
formatEventContent(event) {
return `
<div class="attack-tooltip">
<div class="attack-tooltip-header">
${event.protocol || 'UNKNOWN'} Attack
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">From:</span>
<span class="attack-tooltip-value">${event.src_ip || 'Unknown'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">To:</span>
<span class="attack-tooltip-value">${event.dst_ip || 'Local'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">Country:</span>
<span class="attack-tooltip-value">${event.country || 'Unknown'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">Port:</span>
<span class="attack-tooltip-value">${event.dst_port || 'N/A'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">Time:</span>
<span class="attack-tooltip-value">${this.formatTime(event.timestamp)}</span>
</div>
</div>
`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

XSS vulnerability: event data is interpolated directly into HTML without sanitization.

formatEventContent injects event.src_ip, event.dst_ip, event.protocol, event.country, and event.dst_port directly into an HTML template string via ${}. If any of these fields contain malicious HTML or JavaScript (e.g., a crafted hostname or IP field from a WebSocket event), it will be rendered as live markup when the tooltip is added to the DOM.

Sanitize or escape all dynamic values before interpolation. A minimal helper:

🔒 Proposed fix
+    /**
+     * Escape HTML special characters
+     */
+    escapeHtml(str) {
+        if (!str) return '';
+        return String(str)
+            .replace(/&/g, '&amp;')
+            .replace(/</g, '&lt;')
+            .replace(/>/g, '&gt;')
+            .replace(/"/g, '&quot;')
+            .replace(/'/g, '&#039;');
+    }
+
     formatEventContent(event) {
         return `
             <div class="attack-tooltip">
                 <div class="attack-tooltip-header">
-                    ${event.protocol || 'UNKNOWN'} Attack
+                    ${this.escapeHtml(event.protocol) || 'UNKNOWN'} Attack
                 </div>
                 <div class="attack-tooltip-row">
                     <span class="attack-tooltip-label">From:</span>
-                    <span class="attack-tooltip-value">${event.src_ip || 'Unknown'}</span>
+                    <span class="attack-tooltip-value">${this.escapeHtml(event.src_ip) || 'Unknown'}</span>
                 </div>
                 <div class="attack-tooltip-row">
                     <span class="attack-tooltip-label">To:</span>
-                    <span class="attack-tooltip-value">${event.dst_ip || 'Local'}</span>
+                    <span class="attack-tooltip-value">${this.escapeHtml(event.dst_ip) || 'Local'}</span>
                 </div>
                 <div class="attack-tooltip-row">
                     <span class="attack-tooltip-label">Country:</span>
-                    <span class="attack-tooltip-value">${event.country || 'Unknown'}</span>
+                    <span class="attack-tooltip-value">${this.escapeHtml(event.country) || 'Unknown'}</span>
                 </div>
                 <div class="attack-tooltip-row">
                     <span class="attack-tooltip-label">Port:</span>
-                    <span class="attack-tooltip-value">${event.dst_port || 'N/A'}</span>
+                    <span class="attack-tooltip-value">${this.escapeHtml(event.dst_port) || 'N/A'}</span>
                 </div>
                 <div class="attack-tooltip-row">
                     <span class="attack-tooltip-label">Time:</span>
-                    <span class="attack-tooltip-value">${this.formatTime(event.timestamp)}</span>
+                    <span class="attack-tooltip-value">${this.escapeHtml(this.formatTime(event.timestamp))}</span>
                 </div>
             </div>
         `;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
formatEventContent(event) {
return `
<div class="attack-tooltip">
<div class="attack-tooltip-header">
${event.protocol || 'UNKNOWN'} Attack
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">From:</span>
<span class="attack-tooltip-value">${event.src_ip || 'Unknown'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">To:</span>
<span class="attack-tooltip-value">${event.dst_ip || 'Local'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">Country:</span>
<span class="attack-tooltip-value">${event.country || 'Unknown'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">Port:</span>
<span class="attack-tooltip-value">${event.dst_port || 'N/A'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">Time:</span>
<span class="attack-tooltip-value">${this.formatTime(event.timestamp)}</span>
</div>
</div>
`;
}
/**
* Escape HTML special characters
*/
escapeHtml(str) {
if (!str) return '';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
formatEventContent(event) {
return `
<div class="attack-tooltip">
<div class="attack-tooltip-header">
${this.escapeHtml(event.protocol) || 'UNKNOWN'} Attack
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">From:</span>
<span class="attack-tooltip-value">${this.escapeHtml(event.src_ip) || 'Unknown'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">To:</span>
<span class="attack-tooltip-value">${this.escapeHtml(event.dst_ip) || 'Local'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">Country:</span>
<span class="attack-tooltip-value">${this.escapeHtml(event.country) || 'Unknown'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">Port:</span>
<span class="attack-tooltip-value">${this.escapeHtml(event.dst_port) || 'N/A'}</span>
</div>
<div class="attack-tooltip-row">
<span class="attack-tooltip-label">Time:</span>
<span class="attack-tooltip-value">${this.escapeHtml(this.formatTime(event.timestamp))}</span>
</div>
</div>
`;
}
🤖 Prompt for AI Agents
In `@static/interactive-features.js` around lines 228 - 256, The
formatEventContent method is vulnerable to XSS because it interpolates
event.src_ip, event.dst_ip, event.protocol, event.country, and event.dst_port
directly into an HTML template; fix it by creating and using an HTML-escape
helper (e.g., escapeHtml) and apply it to each dynamic value before
interpolation in formatEventContent (e.g., escapeHtml(event.src_ip) etc.), and
ensure this.escapeHtml is used for the value passed into this.formatTime if that
returns user-controllable text; update references in formatEventContent to call
the sanitizer for protocol, src_ip, dst_ip, country, dst_port (and any other
interpolated event fields).

Comment on lines +16 to +39
// Attack density tracking for heatmap mode
const attackDensityGrid = {};
const GRID_SIZE = 50; // Size of grid cells for density calculation

/**
* Get grid cell key for a given latitude/longitude
* Used to aggregate attacks into heatmap cells
*/
function getGridCellKey(lat, lng) {
const cellLat = Math.floor(lat / GRID_SIZE) * GRID_SIZE;
const cellLng = Math.floor(lng / GRID_SIZE) * GRID_SIZE;
return `${cellLat},${cellLng}`;
}

/**
* Update attack density for heatmap visualization
*/
function updateAttackDensity(lat, lng, intensity = 1) {
const cellKey = getGridCellKey(lat, lng);
if (!attackDensityGrid[cellKey]) {
attackDensityGrid[cellKey] = 0;
}
attackDensityGrid[cellKey] += intensity;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

attackDensityGrid grows without bound — potential memory leak.

updateAttackDensity only ever adds to the grid; there's no eviction, TTL, or reset mechanism. On a long-running dashboard receiving continuous attack data, this object will grow indefinitely. Consider adding a clearDensityGrid() function and calling it periodically or on visualization mode switch.

♻️ Suggested addition
+/**
+ * Clear the density grid to free memory
+ */
+function clearDensityGrid() {
+    Object.keys(attackDensityGrid).forEach(key => delete attackDensityGrid[key]);
+}
+
 window.mapEnhancements = {
     ...
+    clearDensityGrid,
 };
🤖 Prompt for AI Agents
In `@static/map-enhancements.js` around lines 16 - 39, attackDensityGrid currently
grows unbounded because updateAttackDensity only increments entries; add a
bounded eviction/reset mechanism by implementing a clearDensityGrid() function
(and optionally a touch/timestamp map) and call it periodically or when
visualization mode switches; modify updateAttackDensity to record last-seen
timestamps (or increment a rolling counter) so you can prune stale keys when
grid size exceeds a cap, and ensure getGridCellKey and GRID_SIZE remain the
aggregation basis while you add the periodic setInterval or mode-switch hook to
clear or prune attackDensityGrid.

Comment thread static/realtime-animations.js
Comment on lines +287 to +303
showAnomalyAlert(anomaly) {
const alertDiv = document.createElement('div');
alertDiv.className = `anomaly-alert anomaly-alert-${anomaly.severity}`;
alertDiv.innerHTML = `
<div class="anomaly-alert-icon">
<i class="fas fa-exclamation-triangle"></i>
</div>
<div class="anomaly-alert-content">
<div class="anomaly-alert-title">${anomaly.message}</div>
<div class="anomaly-alert-details">
Value: <strong>${anomaly.value}</strong>
</div>
</div>
<button class="anomaly-alert-close">
<i class="fas fa-times"></i>
</button>
`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

XSS vulnerability: innerHTML with unsanitized data.

anomaly.message and anomaly.value are interpolated directly into innerHTML. The message field is constructed from protocol and country names derived from attack traffic data (Lines 224, 233), which could contain attacker-controlled strings. A crafted protocol or country name like <img src=x onerror=alert(1)> would execute arbitrary JavaScript.

Use textContent for user-facing text or sanitize before interpolation.

🐛 Proposed fix using DOM API
     showAnomalyAlert(anomaly) {
         const alertDiv = document.createElement('div');
         alertDiv.className = `anomaly-alert anomaly-alert-${anomaly.severity}`;
-        alertDiv.innerHTML = `
-            <div class="anomaly-alert-icon">
-                <i class="fas fa-exclamation-triangle"></i>
-            </div>
-            <div class="anomaly-alert-content">
-                <div class="anomaly-alert-title">${anomaly.message}</div>
-                <div class="anomaly-alert-details">
-                    Value: <strong>${anomaly.value}</strong>
-                </div>
-            </div>
-            <button class="anomaly-alert-close">
-                <i class="fas fa-times"></i>
-            </button>
-        `;
+
+        const icon = document.createElement('div');
+        icon.className = 'anomaly-alert-icon';
+        icon.innerHTML = '<i class="fas fa-exclamation-triangle"></i>';
+
+        const content = document.createElement('div');
+        content.className = 'anomaly-alert-content';
+
+        const title = document.createElement('div');
+        title.className = 'anomaly-alert-title';
+        title.textContent = anomaly.message;
+
+        const details = document.createElement('div');
+        details.className = 'anomaly-alert-details';
+        details.textContent = `Value: ${anomaly.value}`;
+
+        content.appendChild(title);
+        content.appendChild(details);
+
+        const closeBtn = document.createElement('button');
+        closeBtn.className = 'anomaly-alert-close';
+        closeBtn.innerHTML = '<i class="fas fa-times"></i>';
+
+        alertDiv.appendChild(icon);
+        alertDiv.appendChild(content);
+        alertDiv.appendChild(closeBtn);
🤖 Prompt for AI Agents
In `@static/realtime-animations.js` around lines 287 - 303, The showAnomalyAlert
method currently sets alertDiv.innerHTML with unsanitized anomaly.message and
anomaly.value causing an XSS risk; change it to build the alert DOM using
createElement and textContent (or setAttribute for non-text) so that
anomaly.message and anomaly.value are never injected as HTML—locate
showAnomalyAlert and replace the innerHTML template with explicit DOM nodes for
the icon, title, details and close button, using textContent for the
title/details and className or appendChild to compose the structure.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: KhulnaSoft bot <43526132+khulnasoft-bot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant