diff --git a/docker/nginx/entrypoint.sh b/docker/nginx/entrypoint.sh index 2b8591f6d..25cba44d6 100644 --- a/docker/nginx/entrypoint.sh +++ b/docker/nginx/entrypoint.sh @@ -46,9 +46,18 @@ if [ -n "${SERVER_NAME:-}" ]; then export SERVER_NAME fi +# The /collab/ location resolves its upstream at request time so nginx +# doesn't refuse to start when the (optional) collab service is absent. +# The resolver address is container-runtime-specific: Docker's embedded +# DNS is always 127.0.0.11, but podman/netavark's aardvark-dns listens on +# the network gateway instead. Read the real nameserver out of this +# container's own /etc/resolv.conf so both runtimes work. +DNS_RESOLVER=$(awk '/^nameserver/{print $2; exit}' /etc/resolv.conf) +export DNS_RESOLVER="${DNS_RESOLVER:-127.0.0.11}" + # envsubst will make a substitution on every $variable in a file, since the nginx file contains nginx variable like $host, we have to limit the substitution to this set # otherwise, each nginx variable will be replaced by an empty string -envsubst '${INTERFACE_HTTPS_PORT} ${IRIS_UPSTREAM_SERVER} ${IRIS_UPSTREAM_PORT} ${SERVER_NAME} ${KEY_FILENAME} ${CERT_FILENAME} ${IRIS_FRONTEND_SERVER} ${IRIS_FRONTEND_PORT}' < /etc/nginx/nginx.conf > /tmp/nginx.conf +envsubst '${INTERFACE_HTTPS_PORT} ${IRIS_UPSTREAM_SERVER} ${IRIS_UPSTREAM_PORT} ${SERVER_NAME} ${KEY_FILENAME} ${CERT_FILENAME} ${IRIS_FRONTEND_SERVER} ${IRIS_FRONTEND_PORT} ${DNS_RESOLVER}' < /etc/nginx/nginx.conf > /tmp/nginx.conf cp /tmp/nginx.conf /etc/nginx/nginx.conf rm /tmp/nginx.conf diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index 42e400927..b3351982c 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -155,13 +155,21 @@ http { } location /collab/ { - # Resolve at request time (Docker's embedded DNS) instead of at - # nginx startup: the collab service is a BV-only optional - # feature and isn't present in every compose stack (e.g. the - # upstream-derived dev/CI stack). A static proxy_pass hostname - # would make nginx refuse to start entirely when "collab" can't - # be resolved; this way only /collab/ requests fail if it's absent. - resolver 127.0.0.11 valid=30s; + # Resolve at request time (the container's own DNS resolver) + # instead of at nginx startup: the collab service is a BV-only + # optional feature and isn't present in every compose stack (e.g. + # the upstream-derived dev/CI stack). A static proxy_pass + # hostname would make nginx refuse to start entirely when + # "collab" can't be resolved; this way only /collab/ requests + # fail if it's absent. + # + # ${DNS_RESOLVER} is templated by entrypoint.sh from the + # container's /etc/resolv.conf. It is NOT hardcoded to Docker's + # embedded-DNS address (127.0.0.11) because that address doesn't + # exist under podman/netavark, where aardvark-dns listens on the + # network gateway instead — a hardcoded 127.0.0.11 here silently + # 502s every /collab/ request after a 30s resolver timeout. + resolver ${DNS_RESOLVER} valid=30s; set $collab_upstream collab; proxy_set_header Host $http_host; diff --git a/e2e/tests/administrator/case/ioc.spec.js b/e2e/tests/administrator/case/ioc.spec.js index 0db39df0f..70c13d0c0 100644 --- a/e2e/tests/administrator/case/ioc.spec.js +++ b/e2e/tests/administrator/case/ioc.spec.js @@ -13,14 +13,17 @@ test.beforeEach(async({ page }) => { test('should be able to update IOC', async ({ page }) => { const iocValue = `IOC value - ${crypto.randomUUID()}`; + const iocDescription = `IOC description - ${crypto.randomUUID()}`; await page.getByRole('button', { name: 'Add IOC' }).click(); await page.getByRole('button', { name: 'None' }).click(); await page.getByRole('listbox').getByRole('option', { name: 'AS', exact: true }).click(); await page.getByLabel('IOC Value *').fill(iocValue); + await page.locator('#ioc_description .ProseMirror').fill(iocDescription); await page.getByRole('button', { name: 'Save' }).click(); await page.getByRole('link', { name: iocValue }).click(); + await expect(page.locator('#ioc_description .ProseMirror')).toContainText(iocDescription); const newIocValue = `IOC value - ${crypto.randomUUID()}`; await page.getByLabel('IOC Value *').fill(newIocValue); await page.getByRole('button', { name: 'Update' }).click(); @@ -89,4 +92,4 @@ test('should be able to update IOC custom attribute', async ({ page, rest }) => partial_overwrite: false } }) -}); \ No newline at end of file +}); diff --git a/source/app/blueprints/access_controls.py b/source/app/blueprints/access_controls.py index 6bed54d62..d6b9befd6 100644 --- a/source/app/blueprints/access_controls.py +++ b/source/app/blueprints/access_controls.py @@ -413,9 +413,17 @@ def wrap(*args, **kwargs): return redirect(not_authenticated_redirection_url(request.full_path)) chan_id = args[0].get('channel') - if chan_id: + if not chan_id: + return _ac_return_access_denied(caseid=0) + + # Clients occasionally join a room before a case is selected + # (e.g. `case-null`), producing a non-numeric id. That's not an + # authorization bypass attempt, just a premature join - deny + # gracefully instead of raising and killing the socketio + # event-handler thread. + try: case_id = int(chan_id.replace('case-', '').split('-')[0]) - else: + except ValueError: return _ac_return_access_denied(caseid=0) access = ac_fast_check_user_has_case_access(iris_current_user.id, case_id, access_level) diff --git a/source/app/blueprints/pages/case/templates/case.html b/source/app/blueprints/pages/case/templates/case.html index 7a10c821f..aa13f5f87 100644 --- a/source/app/blueprints/pages/case/templates/case.html +++ b/source/app/blueprints/pages/case/templates/case.html @@ -4,7 +4,6 @@ - {% endblock stylesheets %} {% block content %} {% if current_user.is_authenticated %} diff --git a/source/app/blueprints/pages/case/templates/case_assets.html b/source/app/blueprints/pages/case/templates/case_assets.html index a18096bab..11581b1b5 100644 --- a/source/app/blueprints/pages/case/templates/case_assets.html +++ b/source/app/blueprints/pages/case/templates/case_assets.html @@ -1,7 +1,6 @@ {% extends "layouts/default_ext.html" %} {% block title %} Case Assets {% endblock title %} {% block stylesheets %} {% include 'includes/header_case.html' %} - {% endblock stylesheets %} {% block content %} {% include 'includes/navigation_ext.html' %} @@ -148,4 +147,4 @@
Upload assets list (CSV format)
-{% endblock javascripts %} \ No newline at end of file +{% endblock javascripts %} diff --git a/source/app/blueprints/pages/case/templates/case_notes_v2.html b/source/app/blueprints/pages/case/templates/case_notes_v2.html index 4d58ab289..5bf1c2811 100644 --- a/source/app/blueprints/pages/case/templates/case_notes_v2.html +++ b/source/app/blueprints/pages/case/templates/case_notes_v2.html @@ -1,7 +1,6 @@ {% extends "layouts/default_ext.html" %} {% block title %} Case notes {% endblock title %} {% block stylesheets %} {% include 'includes/header_case.html' %} - {% endblock stylesheets %} {% block content %} {% include 'includes/navigation_ext.html' %} @@ -88,7 +87,7 @@

- - diff --git a/source/app/blueprints/pages/case/templates/modal_add_case_event.html b/source/app/blueprints/pages/case/templates/modal_add_case_event.html index 2a6d4fa21..b5b981b36 100644 --- a/source/app/blueprints/pages/case/templates/modal_add_case_event.html +++ b/source/app/blueprints/pages/case/templates/modal_add_case_event.html @@ -79,26 +79,7 @@