CVE-2024-36105: Insufficient Information (NVD-CWE-noinfo)
value: dbt enables data analysts and engineers to transform their data using the same practices that software engineers use to build applications. Prior to versions 1.6.15, 1.7.15, and 1.8.1, Binding to `INADDR_ANY (0.0.0.0)` or `IN6ADDR_ANY (::)` exposes an application on all network interfaces, increasing the risk of unauthorized access. As stated in the Python docs, a special form for address is accepted instead of a host address: `''` represents `INADDR_ANY`, equivalent to `"0.0.0.0"`. On systems with IPv6, '' represents `IN6ADDR_ANY`, which is equivalent to `"::"`. A user who serves docs on an unsecured public network, may unknowingly be hosting an unsecured (http) web site for any remote user/system to access on the same network. The issue has has been mitigated in dbt-core v1.6.15, dbt-core v1.7.15, and dbt-core v1.8.1 by binding to localhost explicitly by default in `dbt docs serve`.

CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-05-27T18:15Z
Repository: https://github.com/dbt-labs/dbt-core
Language: Python
Files changed: .changes/unreleased/Security-20240522-094540.yaml, core/dbt/task/docs/serve.py, tests/unit/task/docs/test_serve.py
Commit date: 2024-05-22 10:00:54 -0400
Commit message: change port bind and add a unittest (#10208)


Diff: diff --git a/.changes/unreleased/Security-20240522-094540.yaml b/.changes/unreleased/Security-20240522-094540.yaml
new file mode 100644
index 000000000..b35f96dc0
--- /dev/null
+++ b/.changes/unreleased/Security-20240522-094540.yaml
@@ -0,0 +1,6 @@
+kind: Security
+body: Explicitly bind to localhost in docs serve
+time: 2024-05-22T09:45:40.748185-04:00
+custom:
+  Author: ChenyuLInx michelleark
+  Issue: "10209"
diff --git a/core/dbt/task/docs/serve.py b/core/dbt/task/docs/serve.py
index b6aca1519..c0cd4e570 100644
--- a/core/dbt/task/docs/serve.py
+++ b/core/dbt/task/docs/serve.py
@@ -18,11 +18,11 @@ class ServeTask(ConfiguredTask):
         port = self.args.port
 
         if self.args.browser:
             webbrowser.open_new_tab(f"http://localhost:{port}")
 
-        with socketserver.TCPServer(("", port), SimpleHTTPRequestHandler) as httpd:
+        with socketserver.TCPServer(("127.0.0.1", port), SimpleHTTPRequestHandler) as httpd:
             click.echo(f"Serving docs at {port}")
             click.echo(f"To access from your browser, navigate to: http://localhost:{port}")
             click.echo("\n\n")
             click.echo("Press Ctrl+C to exit.")
             httpd.serve_forever()
diff --git a/tests/unit/task/docs/__init__.py b/tests/unit/task/docs/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/unit/task/docs/test_serve.py b/tests/unit/task/docs/test_serve.py
new file mode 100644
index 000000000..cedb234a2
--- /dev/null
+++ b/tests/unit/task/docs/test_serve.py
@@ -0,0 +1,23 @@
+from http.server import SimpleHTTPRequestHandler
+from unittest.mock import MagicMock, patch
+
+import pytest
+
+from dbt.task.docs.serve import ServeTask
+
+
+@pytest.fixture
+def serve_task():
+    # Set up
+    task = ServeTask(config=MagicMock(), args=MagicMock())
+    task.config.project_target_path = "."
+    task.args.port = 8000
+    return task
+
+
+def test_serve_bind_to_127(serve_task):
+    serve_task.args.browser = False
+    with patch("dbt.task.docs.serve.socketserver.TCPServer") as patched_TCPServer:
+        patched_TCPServer.return_value = MagicMock()
+        serve_task.run()
+        patched_TCPServer.assert_called_once_with(("127.0.0.1", 8000), SimpleHTTPRequestHandler)

Vulnerable code:         with socketserver.TCPServer(("", port), SimpleHTTPRequestHandler) as httpd:
Fixed code: kind: Security
body: Explicitly bind to localhost in docs serve
time: 2024-05-22T09:45:40.748185-04:00
custom:
  Author: ChenyuLInx michelleark
  Issue: "10209"
        with socketserver.TCPServer(("127.0.0.1", port), SimpleHTTPRequestHandler) as httpd:
from http.server import SimpleHTTPRequestHandler
from unittest.mock import MagicMock, patch

import pytest

from dbt.task.docs.serve import ServeTask


@pytest.fixture
def serve_task():
    # Set up
    task = ServeTask(config=MagicMock(), args=MagicMock())
    task.config.project_target_path = "."
    task.args.port = 8000
    return task


def test_serve_bind_to_127(serve_task):
    serve_task.args.browser = False
    with patch("dbt.task.docs.serve.socketserver.TCPServer") as patched_TCPServer:
        patched_TCPServer.return_value = MagicMock()
        serve_task.run()
        patched_TCPServer.assert_called_once_with(("127.0.0.1", 8000), SimpleHTTPRequestHandler)