Skip to content

Commit d559008

Browse files
authored
Merge pull request #9 from baseVISION/bv-develop
Dep upgrades and bugfixes.
2 parents 7cc2e3a + e381bad commit d559008

42 files changed

Lines changed: 1244 additions & 563 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bV-local-dev-environment-setup.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,42 @@ podman compose -f docker-compose.bv.yml -p iris-bv up -d
5656
# Stop (data is preserved in iris-bv_db_data volume)
5757
podman compose -f docker-compose.bv.yml -p iris-bv down
5858

59-
# Rebuild after code changes
59+
# Rebuild after code changes (Python backend or Dockerfile changes)
6060
podman compose -f docker-compose.bv.yml -p iris-bv up -d --build
6161

62+
# Restart app after Python file changes (always restart nginx too — it caches the app's IP)
63+
podman restart bv-iriswebapp-app bv-iriswebapp-nginx
64+
6265
# View logs
6366
podman compose -f docker-compose.bv.yml -p iris-bv logs -f bv-iriswebapp-app
6467
```
6568

6669
---
6770

71+
## Working on UI (JavaScript/Svelte) changes
72+
73+
The `ui/dist` folder is bind-mounted into the container at `/iriswebapp/static`, so you can
74+
iterate on UI changes without rebuilding the container image.
75+
76+
**First time only** — install Node dependencies on your host:
77+
```bash
78+
cd ui
79+
npm install
80+
```
81+
82+
**On every UI change:**
83+
```bash
84+
cd ui
85+
npm run build
86+
```
87+
Then hard-refresh the browser (Ctrl+Shift+R). No container restart needed.
88+
89+
> **Note:** `npm install` is only needed on your host for local builds. The container image
90+
> runs its own `npm ci` + `npm run build` during `docker build`, so a full `--build` always
91+
> produces a self-contained image regardless of your local `node_modules`.
92+
93+
---
94+
6895
## Full cleanup
6996

7097
Remove containers, networks, and all data volumes (⚠ deletes all case data):

docker-compose.bv.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ services:
5151
context: .
5252
dockerfile: docker/webApp/Dockerfile
5353
image: bv-iriswebapp-app:develop
54-
ports: []
54+
ports:
55+
- "127.0.0.1:8000:8000"
5556
volumes:
5657
- ./source/app:/iriswebapp/app:z
58+
- ./ui/dist:/iriswebapp/static:z
5759
depends_on:
5860
db:
5961
condition: service_healthy
@@ -98,6 +100,8 @@ services:
98100
NGINX_CONF_GID: 1234
99101
NGINX_CONF_FILE: ${NGINX_CONF_FILE:-nginx.conf}
100102
image: bv-iriswebapp-nginx:develop
103+
security_opt:
104+
- "label=disable"
101105
volumes:
102106
- "./certificates/web_certificates/:/www/certs/:z"
103107
depends_on:
@@ -107,7 +111,7 @@ services:
107111
condition: service_healthy
108112

109113
frontend:
110-
image: node:23-alpine
114+
image: node:24-alpine
111115
profiles: ["new-ui"]
112116
container_name: iris_sveltekit_frontend
113117
working_dir: /app

docker-compose.dev.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ services:
102102
condition: service_healthy
103103

104104
frontend:
105-
image: node:23-alpine
105+
image: node:24-alpine
106106
profiles: ["new-ui"]
107107
container_name: iris_sveltekit_frontend
108108
working_dir: /app

docker/nginx/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1818

1919

20-
FROM nginx:1.27
20+
FROM nginx:1.30
2121

2222
RUN apt-get update && apt-get install -y curl
2323

docker/nginx/nginx.conf

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,6 @@ http {
7777
proxy_set_header X-Real-IP $remote_addr;
7878
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
7979

80-
# FULLY DISABLE SERVER CACHE
81-
add_header Last-Modified $date_gmt;
82-
add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
83-
if_modified_since off;
84-
expires off;
85-
etag off;
86-
proxy_no_cache 1;
87-
proxy_cache_bypass 1;
88-
8980
# SSL CONF, STRONG CIPHERS ONLY
9081
ssl_protocols TLSv1.2 TLSv1.3;
9182

@@ -119,6 +110,45 @@ http {
119110
add_header Strict-Transport-Security "max-age=31536000: includeSubDomains" always;
120111
add_header Front-End-Https on;
121112

113+
# FULLY DISABLE SERVER CACHE
114+
# Moved to server block to fix inheritance issues.
115+
# 'always' ensures headers are sent even on error pages.
116+
add_header Last-Modified $date_gmt always;
117+
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
118+
add_header Pragma "no-cache" always;
119+
add_header Expires "0" always;
120+
if_modified_since off;
121+
expires off;
122+
etag off;
123+
proxy_no_cache 1;
124+
proxy_cache_bypass 1;
125+
126+
location /static {
127+
# Enable Caching (Overrides server "off" settings) for static
128+
# ----------------------------------------------------
129+
expires 1y;
130+
add_header Cache-Control "public, max-age=31536000, immutable";
131+
132+
# Re-enable ETags and Last-Modified so browsers can check for updates
133+
etag on;
134+
if_modified_since exact;
135+
136+
# Re-apply Security Headers
137+
add_header X-XSS-Protection "1; mode=block";
138+
add_header X-Frame-Options DENY;
139+
add_header X-Content-Type-Options nosniff;
140+
add_header Strict-Transport-Security "max-age=31536000: includeSubDomains" always;
141+
add_header Front-End-Https on;
142+
add_header Content-Security-Policy $csp_header;
143+
144+
145+
# Standard Proxy Config
146+
proxy_pass http://${IRIS_UPSTREAM_SERVER}:${IRIS_UPSTREAM_PORT};
147+
148+
proxy_no_cache 0;
149+
proxy_cache_bypass 0;
150+
}
151+
122152
location / {
123153
proxy_pass http://${IRIS_UPSTREAM_SERVER}:${IRIS_UPSTREAM_PORT};
124154

docker/webApp/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
####################
2020
# COMPILE JS IMAGE #
2121
####################
22-
FROM node:20-alpine AS compile-js-image
22+
FROM node:24-alpine AS compile-js-image
2323

2424
COPY ui/ /ui
2525

@@ -40,10 +40,12 @@ RUN python -m venv /opt/venv
4040
# Make sure we use the virtualenv:
4141
ENV PATH="/opt/venv/bin:$PATH"
4242

43+
RUN python -m pip install --upgrade pip wheel
44+
4345
COPY source/dependencies /dependencies
4446
COPY source/requirements.txt /
4547

46-
RUN pip3 install -r requirements.txt
48+
RUN python -m pip install --no-cache-dir -r /requirements.txt
4749

4850
###############
4951
# BUILD IMAGE #

push_template.sh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/bin/bash
2+
3+
# Define the local template path
4+
LOCAL_TEMPLATE="local_dev/test_template.docx"
5+
6+
# Container name
7+
CONTAINER_NAME="bv-iriswebapp-app"
8+
9+
# Find the newest docx file in the user_templates directory inside the container
10+
LATEST_TEMPLATE_FILE=$(podman exec -it $CONTAINER_NAME sh -c 'ls -t /home/iris/user_templates/*.docx | head -n 1' | tr -d '\r')
11+
12+
if [ -z "$LATEST_TEMPLATE_FILE" ]; then
13+
echo "No existing template found in the pod to overwrite. Please upload a template via UI first."
14+
exit 1
15+
fi
16+
17+
echo "Overwriting $LATEST_TEMPLATE_FILE in pod $CONTAINER_NAME..."
18+
podman cp "$LOCAL_TEMPLATE" "$CONTAINER_NAME:$LATEST_TEMPLATE_FILE"
19+
echo "Template updated successfully!"

source/app/__init__.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1818

1919
import os
20+
import bleach
21+
from markupsafe import Markup
2022
from flask import Flask
2123
from flask import g
2224
from flask import session
@@ -88,7 +90,37 @@ def ac_current_user_has_manage_perms():
8890
return False
8991

9092

93+
# Allowlist for user-defined HTML custom attributes. Matches the frontend
94+
# do_md_filter_xss() allowlist closely so behavior is consistent across sinks.
95+
# Explicitly excludes script / iframe / event handlers / javascript: URLs.
96+
_ATTR_HTML_ALLOWED_TAGS = [
97+
'a', 'abbr', 'b', 'blockquote', 'br', 'code', 'div', 'em', 'h1', 'h2', 'h3',
98+
'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'span', 'strong',
99+
'table', 'tbody', 'td', 'th', 'thead', 'tr', 'ul',
100+
]
101+
_ATTR_HTML_ALLOWED_ATTRS = {
102+
'*': ['class', 'title'],
103+
'a': ['href', 'title', 'target', 'rel'],
104+
'img': ['src', 'alt', 'title', 'width', 'height'],
105+
}
106+
_ATTR_HTML_ALLOWED_PROTOCOLS = ['http', 'https', 'mailto']
107+
108+
109+
def _sanitize_attribute_html(value):
110+
if value is None:
111+
return ''
112+
cleaned = bleach.clean(
113+
str(value),
114+
tags=_ATTR_HTML_ALLOWED_TAGS,
115+
attributes=_ATTR_HTML_ALLOWED_ATTRS,
116+
protocols=_ATTR_HTML_ALLOWED_PROTOCOLS,
117+
strip=True,
118+
)
119+
return Markup(cleaned)
120+
121+
91122
register_jinja_filters(app.jinja_env)
123+
app.jinja_env.filters['sanitize_attribute_html'] = _sanitize_attribute_html
92124

93125
app.jinja_env.globals.update(user_has_perm=ac_current_user_has_permission)
94126
app.jinja_env.globals.update(user_has_manage_perms=ac_current_user_has_manage_perms)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Merge bv-develop chain with dfir-iris v2.4.27 chain
2+
3+
Both chains share common ancestor d5a720d1b99b, then diverged:
4+
- dfir-iris added e5d79b8c4a55 (custom_dashboard tables, 2025-11-10)
5+
- bv-develop added 3715d4fac4de + afcff5ebcf7c (IOC-case link, force-confirmation setting)
6+
7+
This merge point allows databases migrated from dfir-iris v2.4.27 to be
8+
upgraded to bv-develop without a crash.
9+
10+
Revision ID: 2b9a1f8c3d47
11+
Revises: afcff5ebcf7c, e5d79b8c4a55
12+
Create Date: 2026-05-15 00:00:00.000000
13+
14+
"""
15+
16+
# revision identifiers, used by Alembic.
17+
revision = '2b9a1f8c3d47'
18+
down_revision = ('afcff5ebcf7c', 'e5d79b8c4a55')
19+
branch_labels = None
20+
depends_on = None
21+
22+
23+
def upgrade():
24+
# No schema changes — this is a pure merge point.
25+
# Both branches are additive and schema-compatible at this point.
26+
pass
27+
28+
29+
def downgrade():
30+
pass
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Add tables for custom dashboards
2+
3+
Revision ID: e5d79b8c4a55
4+
Revises: d6c49c5435c2
5+
Create Date: 2025-11-10 10:00:00.000000
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
from sqlalchemy.dialects import postgresql
11+
12+
from app.alembic.alembic_utils import _has_table
13+
14+
# revision identifiers, used by Alembic.
15+
revision = 'e5d79b8c4a55'
16+
down_revision = 'd5a720d1b99b'
17+
branch_labels = None
18+
depends_on = None
19+
20+
21+
def upgrade():
22+
if not _has_table('custom_dashboard'):
23+
op.create_table(
24+
'custom_dashboard',
25+
sa.Column('id', sa.Integer, primary_key=True),
26+
sa.Column('dashboard_uuid', postgresql.UUID(as_uuid=True), server_default=sa.text('gen_random_uuid()'), nullable=False, unique=True),
27+
sa.Column('name', sa.String(length=255), nullable=False),
28+
sa.Column('description', sa.Text, nullable=True),
29+
sa.Column('owner_id', sa.Integer, sa.ForeignKey('user.id'), nullable=False),
30+
sa.Column('is_shared', sa.Boolean, nullable=False, server_default=sa.text('false')),
31+
sa.Column('definition', postgresql.JSONB, nullable=True),
32+
sa.Column('created_at', sa.DateTime, server_default=sa.func.now(), nullable=False),
33+
sa.Column('updated_at', sa.DateTime, server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False)
34+
)
35+
36+
if not _has_table('custom_dashboard_widget'):
37+
op.create_table(
38+
'custom_dashboard_widget',
39+
sa.Column('id', sa.Integer, primary_key=True),
40+
sa.Column('widget_uuid', postgresql.UUID(as_uuid=True), server_default=sa.text('gen_random_uuid()'), nullable=False, unique=True),
41+
sa.Column('dashboard_id', sa.Integer, sa.ForeignKey('custom_dashboard.id', ondelete='CASCADE'), nullable=False),
42+
sa.Column('name', sa.String(length=255), nullable=False),
43+
sa.Column('chart_type', sa.String(length=64), nullable=False),
44+
sa.Column('definition', postgresql.JSONB, nullable=False),
45+
sa.Column('position', sa.Integer, nullable=False, server_default=sa.text('0')),
46+
sa.Column('created_at', sa.DateTime, server_default=sa.func.now(), nullable=False),
47+
sa.Column('updated_at', sa.DateTime, server_default=sa.func.now(), onupdate=sa.func.now(), nullable=False)
48+
)
49+
op.create_index('ix_custom_dashboard_widget_dashboard_id', 'custom_dashboard_widget', ['dashboard_id'])
50+
51+
52+
53+
def downgrade():
54+
if _has_table('custom_dashboard_widget'):
55+
op.drop_index('ix_custom_dashboard_widget_dashboard_id', table_name='custom_dashboard_widget')
56+
op.drop_table('custom_dashboard_widget')
57+
58+
if _has_table('custom_dashboard'):
59+
op.drop_table('custom_dashboard')

0 commit comments

Comments
 (0)