Repository: SaaShup/netbox-docker-plugin (language: python)

Issue:
Host endpoints passwords are visible
When we add an Host with an endpoint that need password, this password is actually visible on Host list.

Exemple: http://user:pass@10.10.10.1:1880

this endpoint should be visible like:
http://user:***@10.10.10.1:1880

Open question: how to print Host endpoint with password on edition form?


Discussion / hints:
Best would be to hide the url.
Why not when we create a host request host (from the vm list ? ), port (from the service ?), login, password, protocol (http/https)....
url info would be created from those fields and hidden in gui
@lvenier can we link a host with this kind of service?

<img width="985" alt="image" src="https://github.com/SaaShup/netbox-docker-plugin/assets/1986015/42b02ffb-ac19-4138-b0f8-235438563cef">


Fix patch:
diff --git a/netbox_docker_plugin/tables.py b/netbox_docker_plugin/tables.py
index 6ae2afb..9de68af 100644
--- a/netbox_docker_plugin/tables.py
+++ b/netbox_docker_plugin/tables.py
@@ -7,6 +7,7 @@
 from .models.volume import Volume
 from .models.network import Network
 from .models.container import Container, Env, Label, Port, Mount, NetworkSetting
+from .templatetags.host import remove_password
 
 
 class HostTable(NetBoxTable):
@@ -35,6 +36,10 @@ class HostTable(NetBoxTable):
     )
     tags = columns.TagColumn()
 
+    def render_endpoint(self, value):
+        """Render endpoint without password"""
+        return remove_password(value)
+
     class Meta(NetBoxTable.Meta):
         """Host Table definition Meta class"""
 
@@ -67,7 +72,6 @@ class ImageTable(NetBoxTable):
 
     host = tables.Column(linkify=True)
     name = tables.Column(linkify=True)
-    size = tables.Column(verbose_name="Size (MB)")
     container_count = columns.LinkedCountColumn(
         viewname="plugins:netbox_docker_plugin:container_list",
         url_params={"image_id": "pk"},
@@ -75,6 +79,10 @@ class ImageTable(NetBoxTable):
     )
     tags = columns.TagColumn()
 
+    def render_size(self, value):
+        """Render the image size with unity"""
+        return f"{value} MB"
+
     class Meta(NetBoxTable.Meta):
         """Image Table definition Meta class"""
 
diff --git a/netbox_docker_plugin/templates/netbox_docker_plugin/host.html b/netbox_docker_plugin/templates/netbox_docker_plugin/host.html
index 133069f..09809f4 100644
--- a/netbox_docker_plugin/templates/netbox_docker_plugin/host.html
+++ b/netbox_docker_plugin/templates/netbox_docker_plugin/host.html
@@ -1,6 +1,7 @@
 {% extends 'generic/object.html' %}
 
 {% load plugins %}
+{% load host %}
 
 {% block content %}
 <div class="row mb-3">
@@ -15,7 +16,7 @@ <h5 class="card-header">HOST</h5>
           </tr>
           <tr>
             <th scope="row">Endpoint</th>
-            <td>{{ object.endpoint }}</td>
+            <td>{{ object.endpoint|remove_password }}</td>
           </tr>
           <tr>
             <th scope="row">State</th>
diff --git a/netbox_docker_plugin/templatetags/__init__.py b/netbox_docker_plugin/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/netbox_docker_plugin/templatetags/host.py b/netbox_docker_plugin/templatetags/host.py
new file mode 100644
index 0000000..bc0164c
--- /dev/null
+++ b/netbox_docker_plugin/templatetags/host.py
@@ -0,0 +1,14 @@
+"""Host tag definitions"""
+
+from re import sub
+from django import template
+
+register = template.Library()
+
+
+def remove_password(value):
+    """Replace password in URL by ***"""
+    return sub(r"(.+):\/\/(.+):(.+)@(.+)", r"\1://\2:***@\4", value)
+
+
+register.filter("remove_password", remove_password)


Test patch:
diff --git a/netbox_docker_plugin/tests/host/test_replace_password.py b/netbox_docker_plugin/tests/host/test_replace_password.py
new file mode 100644
index 0000000..dbc744b
--- /dev/null
+++ b/netbox_docker_plugin/tests/host/test_replace_password.py
@@ -0,0 +1,22 @@
+"""Host API Test Case"""
+
+from unittest import TestCase
+from netbox_docker_plugin.templatetags.host import remove_password
+
+
+class ReplacePasswordTestCase(TestCase):
+    """Replace Password function Test Case Class"""
+
+    def test_replace_password(self):
+        """Test that replace password works"""
+        self.assertEqual(remove_password("http://localhost"), "http://localhost")
+        self.assertEqual(remove_password("https://localhost"), "https://localhost")
+        self.assertEqual(
+            remove_password("http://titi@localhost"), "http://titi@localhost"
+        )
+        self.assertEqual(
+            remove_password("http://titi:@localhost"), "http://titi:@localhost"
+        )
+        self.assertEqual(
+            remove_password("http://titi:toto@localhost"), "http://titi:***@localhost"
+        )