Skip to content

Commit 5b6cc4d

Browse files
authored
feat(opensearch-web): dest_ip filter, per-tab session persistence, sidebar redesign, and overview table fixes (#31)
* feat(opensearch-web): add destination IP filter to query pipeline and search bar Extend the query backend and web form to support filtering by destination IP alongside the existing src_ip filter. - build_base_query gains a dest_ip_filter param that appends a destination.ip term clause when set - run_query guards dest_ip_filter the same way src_ip is guarded: only applied to modules that declare destination.ip in SOURCE_FIELDS - build_search_params_from_request reads the dest_ip form value - base.html search bar exposes a Dst IP text input * fix(opensearch-web): skip em-dash placeholder IPs in overview table Zeek modules emit "—" (U+2014) as src_ip when no source IP is present in a record. run_cross_protocol_query was aggregating these into a phantom "—" row in the cross-protocol overview matrix. Extend the IP guard to also skip em-dash values alongside empty strings. * feat(opensearch-web): redesign sidebar nav, persist filters per-tab, and fix sticky-column rendering Sidebar navigation: - Add always-visible Overview link (cross-protocol matrix) to sidebar - Replace Hub home link with brand logo that routes to Hub when mounted under a script name, or to Overview when running standalone - Polish collapsed sidebar: hide scrollbar, center icons, apply category colour accents to group headers, add .sidebar-overview highlight class - Update category icons (alerts, network, web, remote, auth, messaging) - Reorder MODULES so suricata_alert appears before weird in the registry Filter persistence: - Replace localStorage-based sensor/time_range persistence with sessionStorage so each browser tab maintains independent filter state - Snapshot all filter keys (time_range, sensor, src_ip, dest_ip, direction, public_only, limit, min_risk) on form submit; restore on link navigation - Remove inline onchange handler from the time_range select CSS cache-busting: - Compute pisces.css version from file mtime at app startup and inject as a ?v= query param in the stylesheet link Sticky-column rendering: - Raise col-ip-addr z-index to 20 (top-left corner) and add explicit top+left sticky declarations to thead .col-ip-addr and thead .col-total so both axes stick reliably at the corner - Remove drop shadows from col-ip-addr and col-total; keep only the inset separator line - Remove z-index from the shared thead rule to avoid clobbering the per-column corner values * ci(djlint): add HTML linting to pre-commit and CI pipeline Introduces djLint as a dev dependency and wires it into both pre-commit and the GitHub Actions CI workflow, following the same advisory-on-dev / blocking-on-PR-to-main pattern used by the existing ruff checks. - djlint-jinja pre-commit hook lints Jinja templates on every commit - CI: "Lint HTML (djlint check)" runs blocking on PRs to main, advisory on pushes to dev (mirrors ruff check behaviour) - CI: "Format check HTML (djlint format)" runs advisory on all triggers - [tool.djlint] config added to pyproject.toml: jinja profile, 100-char line limit, H021/H023/H030/H031 suppressed with rationale * style(templates): fix all djlint lint errors across 13 HTML files Resolves all djlint warnings to reach 0 errors across 32 HTML files: - T003: added block names to all bare endblock tags (21 occurrences) across dashboard_web, mantis_web, and opensearch_web templates - H006: added height/width attributes to brand logo img tags in 4 base templates - H025: added closing </option> tags to datalist options in filter_form.html - H014: removed extra blank lines in opensearch_web base.html and record_detail.html - T032: removed extra whitespace in Jinja set tags in ticket_detail.html and record_detail.html - H029: lowercased form method="GET" to method="get" in opensearch_web base.html - H020: replaced empty <span></span> with <span>&nbsp;</span> in threat_card.html Also adds J018 to the djlint ignore list in pyproject.toml — cross-app internal links cannot use url_for() in a multi-app Flask setup.
1 parent b644bc0 commit 5b6cc4d

22 files changed

Lines changed: 358 additions & 83 deletions

File tree

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,11 @@ jobs:
4444
- name: SAST (bandit) — advisory
4545
run: uv run bandit -r src/ apps/ mcp/ -c pyproject.toml
4646
continue-on-error: true
47+
48+
- name: Lint HTML (djlint check)
49+
run: uv run djlint apps/ --lint
50+
continue-on-error: ${{ env.ADVISORY == 'true' }}
51+
52+
- name: Format check HTML (djlint format) — advisory
53+
run: uv run djlint apps/ --check
54+
continue-on-error: true

.pre-commit-config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ repos:
1818
- id: detect-secrets
1919
args: [--baseline, .secrets.baseline]
2020

21+
- repo: https://github.com/djlint/djLint
22+
rev: v1.36.4
23+
hooks:
24+
- id: djlint-jinja
25+
args: [--lint]
26+
2127
- repo: https://github.com/pre-commit/pre-commit-hooks
2228
rev: v5.0.0
2329
hooks:

apps/dashboard_web/templates/base.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1">
6-
<title>{% block title %}PISCES · Dashboard{% endblock %}</title>
6+
<title>{% block title %}PISCES · Dashboard{% endblock title %}</title>
77
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='pisces-logo.ico') }}">
88
<script>
99
/* Apply saved theme before CSS loads to prevent flash */
@@ -200,7 +200,7 @@
200200
<div id="htmx-bar"></div>
201201

202202
<nav>
203-
<a class="brand" href="{{ script_name }}/"><img src="{{ url_for('static', filename='pisces-logo.png') }}" alt="PISCES" class="brand-logo">PISCES <span class="brand-sub">Dashboard</span></a>
203+
<a class="brand" href="{{ script_name }}/"><img src="{{ url_for('static', filename='pisces-logo.png') }}" alt="PISCES" class="brand-logo" height="32" width="auto">PISCES <span class="brand-sub">Dashboard</span></a>
204204
{% if script_name %}
205205
<a href="/" class="btn-icon" title="Back to PISCES Hub">
206206
<i class="fa-solid fa-house"></i>
@@ -213,7 +213,7 @@
213213
</nav>
214214

215215
<main>
216-
{% block content %}{% endblock %}
216+
{% block content %}{% endblock content %}
217217
</main>
218218

219219
<script>
@@ -270,6 +270,6 @@
270270
})();
271271
</script>
272272

273-
{% block extra_scripts %}{% endblock %}
273+
{% block extra_scripts %}{% endblock extra_scripts %}
274274
</body>
275275
</html>

apps/dashboard_web/templates/dashboard.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
<p class="loading-msg"><i class="fa-solid fa-spinner fa-spin"></i> Loading Threat Intel data…</p>
4949
</div>
5050
</div>
51-
{% endblock %}
51+
{% endblock content %}
5252

5353
{% block extra_scripts %}
5454
<script>
@@ -95,4 +95,4 @@
9595
loadSection('overview');
9696
});
9797
</script>
98-
{% endblock %}
98+
{% endblock extra_scripts %}

apps/hub/templates/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
</head>
7777
<body>
7878
<nav>
79-
<a class="brand" href="/"><img src="{{ url_for('static', filename='pisces-logo.png') }}" alt="PISCES" class="brand-logo">PISCES <span class="brand-sub">Hub</span></a>
79+
<a class="brand" href="/"><img src="{{ url_for('static', filename='pisces-logo.png') }}" alt="PISCES" class="brand-logo" height="32" width="auto">PISCES <span class="brand-sub">Hub</span></a>
8080
<div class="spacer"></div>
8181
<button class="btn-theme" id="theme-toggle" onclick="toggleTheme()" title="Toggle light/dark theme">
8282
<i class="fa-solid fa-moon" id="theme-icon"></i>

apps/mantis_web/templates/base.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1">
6-
<title>{% block title %}PISCES Threat Modeling{% endblock %}</title>
6+
<title>{% block title %}PISCES Threat Modeling{% endblock title %}</title>
77
<link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='pisces-logo.ico') }}">
88
<script>
99
(function() {
@@ -22,7 +22,7 @@
2222
<div id="htmx-bar"></div>
2323

2424
<nav>
25-
<a class="brand" href="{{ script_name }}/"><img src="{{ url_for('static', filename='pisces-logo.png') }}" alt="PISCES" class="brand-logo">PISCES <span class="brand-sub">Threat Intel</span></a>
25+
<a class="brand" href="{{ script_name }}/"><img src="{{ url_for('static', filename='pisces-logo.png') }}" alt="PISCES" class="brand-logo" height="32" width="auto">PISCES <span class="brand-sub">Threat Intel</span></a>
2626
{% if script_name %}
2727
<a href="/" class="btn-icon" title="Back to PISCES Hub">
2828
<i class="fa-solid fa-house"></i>
@@ -37,7 +37,7 @@
3737
</nav>
3838

3939
<main>
40-
{% block content %}{% endblock %}
40+
{% block content %}{% endblock content %}
4141
</main>
4242

4343
{# ── Ticket detail side panel ──────────────────────── #}
@@ -618,7 +618,7 @@
618618
})();
619619
</script>
620620

621-
{% block extra_scripts %}{% endblock %}
621+
{% block extra_scripts %}{% endblock extra_scripts %}
622622

623623
</body>
624624
</html>

apps/mantis_web/templates/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{% extends "base.html" %}
22

3-
{% block title %}PISCES Threat Intel{% endblock %}
3+
{% block title %}PISCES Threat Intel{% endblock title %}
44

55
{% block content %}
66

@@ -409,4 +409,4 @@
409409

410410
</div>{# ── end tm-tabs-section ── #}
411411

412-
{% endblock %}
412+
{% endblock content %}

apps/mantis_web/templates/partials/threat_card.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
<div class="threat-summaries">
6767
<div class="threat-kv" style="margin-bottom:4px">
6868
<span class="k" style="font-weight:600">Summaries</span>
69-
<span></span>
69+
<span>&nbsp;</span>
7070
</div>
7171
<ul style="list-style:disc;padding-left:1.2rem;font-size:0.8rem;color:var(--on-surface-dim)">
7272
{% for s in malicious.summaries %}

apps/mantis_web/templates/partials/ticket_detail.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343

4444
{# Reporter / handler #}
4545
{% set reporter_name = (t.reporter or {}).get('name') or (t.reporter if t.reporter is string else None) %}
46-
{% set handler_name = (t.handler or {}).get('name') or (t.handler if t.handler is string else None) %}
46+
{% set handler_name = (t.handler or {}).get('name') or (t.handler if t.handler is string else None) %}
4747
{% if reporter_name or handler_name %}
4848
<div style="display:flex;gap:1.5rem;font-size:0.78rem;color:var(--on-surface-dim)">
4949
{% if reporter_name %}

apps/opensearch_web/app.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,26 +84,31 @@ def create_app() -> Flask:
8484
app.register_blueprint(make_mantis_blueprint(_resolve_city))
8585
app.register_blueprint(make_cache_blueprint(wcache))
8686

87+
# Compute CSS version once at startup from file mtime — busts browser cache on deploy
88+
_css_path = os.path.join(app.static_folder, "pisces.css")
89+
_css_version = str(int(os.path.getmtime(_css_path))) if os.path.exists(_css_path) else "1"
90+
8791
# Make TIME_RANGES, MODULES, and nav data available to all templates
8892
@app.context_processor
8993
def inject_globals() -> dict:
9094
return {
9195
"TIME_RANGES": TIME_RANGES,
9296
"MODULES": MODULES,
9397
"script_name": request.environ.get("SCRIPT_NAME", ""),
98+
"css_version": _css_version,
9499
}
95100

96101
@app.context_processor
97102
def inject_nav_data() -> dict:
98103
return {
99104
"proto_icons": {lt: mod.WEB_ICON for lt, mod in MODULES.items()},
100105
"category_icons": {
101-
"alerts": "fa-bell",
102-
"network": "fa-network-wired",
103-
"web": "fa-globe",
104-
"remote": "fa-terminal",
105-
"auth": "fa-shield-halved",
106-
"messaging": "fa-envelope",
106+
"alerts": "fa-fire",
107+
"network": "fa-sitemap",
108+
"web": "fa-cloud",
109+
"remote": "fa-right-to-bracket",
110+
"auth": "fa-fingerprint",
111+
"messaging": "fa-comments",
107112
"files": "fa-folder",
108113
"ot": "fa-industry",
109114
},

0 commit comments

Comments
 (0)