Skip to content

[Feature] New NEBULA UI#40

Merged
enriquetomasmb merged 9 commits into
mainfrom
ui-improvements
May 13, 2025
Merged

[Feature] New NEBULA UI#40
enriquetomasmb merged 9 commits into
mainfrom
ui-improvements

Conversation

@enriquetomasmb
Copy link
Copy Markdown
Collaborator

No description provided.

const scenarioData = collectScenarioData();
scenariosList.push(scenarioData);
actual_scenario = scenariosList.length - 1;
sessionStorage.setItem("ScenarioList", JSON.stringify(scenariosList));

Check failure

Code scanning / CodeQL

Clear text storage of sensitive information High

This stores sensitive data returned by
an access to latitude
as clear text.
This stores sensitive data returned by
an access to longitude
as clear text.
clearFields();
}

sessionStorage.setItem("ScenarioList", JSON.stringify(scenariosList));

Check failure

Code scanning / CodeQL

Clear text storage of sensitive information High

This stores sensitive data returned by
an access to latitude
as clear text.
This stores sensitive data returned by
an access to longitude
as clear text.

const scenarioData = collectScenarioData();
scenariosList[actual_scenario] = scenarioData;
sessionStorage.setItem("ScenarioList", JSON.stringify(scenariosList));

Check failure

Code scanning / CodeQL

Clear text storage of sensitive information High

This stores sensitive data returned by
an access to latitude
as clear text.
This stores sensitive data returned by
an access to longitude
as clear text.

Copilot Autofix

AI about 1 year ago

To address the issue, we will encrypt the sensitive data (e.g., latitude and longitude) before storing it in sessionStorage. We will use the Web Crypto API, a standard and secure way to perform cryptographic operations in the browser. Specifically, we will:

  1. Define a utility function to encrypt data using the AES-GCM algorithm.
  2. Modify the saveScenario, deleteScenario, and replaceScenario functions to encrypt the scenariosList before storing it in sessionStorage.
  3. Add a corresponding decryption function to decrypt the data when retrieving it from sessionStorage.

This approach ensures that sensitive data is protected while maintaining the existing functionality of the application.


Suggested changeset 1
nebula/frontend/static/js/deployment/scenario.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/nebula/frontend/static/js/deployment/scenario.js b/nebula/frontend/static/js/deployment/scenario.js
--- a/nebula/frontend/static/js/deployment/scenario.js
+++ b/nebula/frontend/static/js/deployment/scenario.js
@@ -2,2 +2,31 @@
 const ScenarioManager = (function() {
+
+    // Encryption utilities
+    const encryptionKey = crypto.subtle.generateKey(
+        { name: "AES-GCM", length: 256 },
+        true,
+        ["encrypt", "decrypt"]
+    );
+
+    async function encryptData(data) {
+        const encoder = new TextEncoder();
+        const iv = crypto.getRandomValues(new Uint8Array(12)); // Initialization vector
+        const encodedData = encoder.encode(JSON.stringify(data));
+        const encrypted = await crypto.subtle.encrypt(
+            { name: "AES-GCM", iv },
+            await encryptionKey,
+            encodedData
+        );
+        return { encryptedData: new Uint8Array(encrypted), iv };
+    }
+
+    async function decryptData(encryptedData, iv) {
+        const decoder = new TextDecoder();
+        const decrypted = await crypto.subtle.decrypt(
+            { name: "AES-GCM", iv },
+            await encryptionKey,
+            encryptedData
+        );
+        return JSON.parse(decoder.decode(decrypted));
+    }
     let scenariosList = [];
@@ -8,3 +37,11 @@
         // Clear session storage
-        sessionStorage.removeItem("ScenarioList");
+        const storedData = sessionStorage.getItem("ScenarioList");
+        if (storedData) {
+            const { encryptedData, iv } = JSON.parse(storedData);
+            decryptData(new Uint8Array(encryptedData), new Uint8Array(iv)).then(data => {
+                scenariosList = data;
+            });
+        } else {
+            scenariosList = [];
+        }
         
@@ -214,3 +251,5 @@
         actual_scenario = scenariosList.length - 1;
-        sessionStorage.setItem("ScenarioList", JSON.stringify(scenariosList));
+        encryptData(scenariosList).then(({ encryptedData, iv }) => {
+            sessionStorage.setItem("ScenarioList", JSON.stringify({ encryptedData: Array.from(encryptedData), iv: Array.from(iv) }));
+        });
         updateScenariosPosition();
EOF
@@ -2,2 +2,31 @@
const ScenarioManager = (function() {

// Encryption utilities
const encryptionKey = crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);

async function encryptData(data) {
const encoder = new TextEncoder();
const iv = crypto.getRandomValues(new Uint8Array(12)); // Initialization vector
const encodedData = encoder.encode(JSON.stringify(data));
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
await encryptionKey,
encodedData
);
return { encryptedData: new Uint8Array(encrypted), iv };
}

async function decryptData(encryptedData, iv) {
const decoder = new TextDecoder();
const decrypted = await crypto.subtle.decrypt(
{ name: "AES-GCM", iv },
await encryptionKey,
encryptedData
);
return JSON.parse(decoder.decode(decrypted));
}
let scenariosList = [];
@@ -8,3 +37,11 @@
// Clear session storage
sessionStorage.removeItem("ScenarioList");
const storedData = sessionStorage.getItem("ScenarioList");
if (storedData) {
const { encryptedData, iv } = JSON.parse(storedData);
decryptData(new Uint8Array(encryptedData), new Uint8Array(iv)).then(data => {
scenariosList = data;
});
} else {
scenariosList = [];
}

@@ -214,3 +251,5 @@
actual_scenario = scenariosList.length - 1;
sessionStorage.setItem("ScenarioList", JSON.stringify(scenariosList));
encryptData(scenariosList).then(({ encryptedData, iv }) => {
sessionStorage.setItem("ScenarioList", JSON.stringify({ encryptedData: Array.from(encryptedData), iv: Array.from(iv) }));
});
updateScenariosPosition();
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +292 to +294
confirmModalBody.innerHTML = `Are you sure you want to run the scenario?
<br><p class="badge text-bg-warning">The scenario will be deployed using the selected deployment option: ${deploymentOption.value}</p>
<br><p class="badge text-bg-danger">Warning: you will stop the running scenario and start a new one</p>`;

Check failure

Code scanning / CodeQL

DOM text reinterpreted as HTML High

DOM text
is reinterpreted as HTML without escaping meta-characters.

Copilot Autofix

AI about 1 year ago

To fix the issue, we need to ensure that any untrusted data (like deploymentOption.value) is properly escaped before being inserted into the DOM. Instead of using innerHTML, which interprets the string as HTML, we can use textContent to safely insert plain text. This approach prevents the browser from interpreting the content as HTML, thereby mitigating the XSS risk.

Specifically:

  1. Replace the use of innerHTML with textContent for confirmModalBody.
  2. For the dynamic content that requires formatting (e.g., badges), create and append DOM elements programmatically instead of using HTML strings.

Suggested changeset 1
nebula/frontend/static/js/deployment/ui-controls.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/nebula/frontend/static/js/deployment/ui-controls.js b/nebula/frontend/static/js/deployment/ui-controls.js
--- a/nebula/frontend/static/js/deployment/ui-controls.js
+++ b/nebula/frontend/static/js/deployment/ui-controls.js
@@ -283,3 +283,3 @@
         if (!document.querySelector(".participant-started")) {
-            confirmModalBody.innerHTML = 'Please select one "start" participant for the scenario';
+            confirmModalBody.textContent = 'Please select one "start" participant for the scenario';
             yesButton.disabled = true;
@@ -291,5 +291,15 @@
         const deploymentOption = document.querySelector('input[name="deploymentRadioOptions"]:checked');
-        confirmModalBody.innerHTML = `Are you sure you want to run the scenario?
-            <br><p class="badge text-bg-warning">The scenario will be deployed using the selected deployment option: ${deploymentOption.value}</p>
-            <br><p class="badge text-bg-danger">Warning: you will stop the running scenario and start a new one</p>`;
+        confirmModalBody.textContent = "Are you sure you want to run the scenario?";
+
+        const warningBadge = document.createElement("p");
+        warningBadge.className = "badge text-bg-warning";
+        warningBadge.textContent = `The scenario will be deployed using the selected deployment option: ${deploymentOption.value}`;
+        confirmModalBody.appendChild(document.createElement("br"));
+        confirmModalBody.appendChild(warningBadge);
+
+        const dangerBadge = document.createElement("p");
+        dangerBadge.className = "badge text-bg-danger";
+        dangerBadge.textContent = "Warning: you will stop the running scenario and start a new one";
+        confirmModalBody.appendChild(document.createElement("br"));
+        confirmModalBody.appendChild(dangerBadge);
         yesButton.disabled = false;
EOF
@@ -283,3 +283,3 @@
if (!document.querySelector(".participant-started")) {
confirmModalBody.innerHTML = 'Please select one "start" participant for the scenario';
confirmModalBody.textContent = 'Please select one "start" participant for the scenario';
yesButton.disabled = true;
@@ -291,5 +291,15 @@
const deploymentOption = document.querySelector('input[name="deploymentRadioOptions"]:checked');
confirmModalBody.innerHTML = `Are you sure you want to run the scenario?
<br><p class="badge text-bg-warning">The scenario will be deployed using the selected deployment option: ${deploymentOption.value}</p>
<br><p class="badge text-bg-danger">Warning: you will stop the running scenario and start a new one</p>`;
confirmModalBody.textContent = "Are you sure you want to run the scenario?";

const warningBadge = document.createElement("p");
warningBadge.className = "badge text-bg-warning";
warningBadge.textContent = `The scenario will be deployed using the selected deployment option: ${deploymentOption.value}`;
confirmModalBody.appendChild(document.createElement("br"));
confirmModalBody.appendChild(warningBadge);

const dangerBadge = document.createElement("p");
dangerBadge.className = "badge text-bg-danger";
dangerBadge.textContent = "Warning: you will stop the running scenario and start a new one";
confirmModalBody.appendChild(document.createElement("br"));
confirmModalBody.appendChild(dangerBadge);
yesButton.disabled = false;
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +711 to +715
roleCell.innerHTML = `
<span class="badge bg-info-subtle text-black">
<i class="fa fa-server me-1"></i>${data.role}
</span>
`;

Check failure

Code scanning / CodeQL

Client-side cross-site scripting High

Cross-site scripting vulnerability due to
user-provided value
.
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://unpkg.com/3d-force-graph@1.67.0/dist/3d-force-graph.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

Check warning

Code scanning / CodeQL

Inclusion of functionality from an untrusted source Medium

Script loaded from content delivery network with no integrity check.
@enriquetomasmb enriquetomasmb merged commit 329674f into main May 13, 2025
4 of 6 checks passed
@enriquetomasmb enriquetomasmb deleted the ui-improvements branch May 13, 2025 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants