sadpandajoe commented on code in PR #35828:
URL: https://github.com/apache/superset/pull/35828#discussion_r2462094311


##########
cypress/e2e/dashboards/export-special-chars.cy.js:
##########
@@ -0,0 +1,175 @@
+// cypress/e2e/dashboards/export-special-chars.cy.js
+// Superset Issue #31158 - E2E Test for Dashboard Export with Special 
Characters
+
+describe("Dashboard Export API - Issue #31158", () => {
+  beforeEach(() => {
+    // We test the API directly without UI since we're in headless environment
+    // In a real Superset instance, these would interact with the UI
+    cy.session("admin", () => {
+      cy.visit("http://localhost:8088/login/";);
+      cy.get('input[id="username"]').type("admin");
+      cy.get('input[id="password"]').type("admin");
+      cy.get("button[type='submit']").click();
+      cy.url().should("include", "/dashboard/list");
+    });
+  });
+
+  it("should properly encode dashboard export filename with special 
characters", () => {
+    // Test the export API endpoint directly
+    // This verifies the fix for issue #31158
+
+    // Test Case 1: Normal dashboard name (baseline)
+    cy.request({
+      method: "GET",
+      url: "http://localhost:8088/api/v1/dashboard/export";,
+      headers: {
+        Authorization: "Bearer admin",
+      },
+    }).then((response) => {
+      expect(response.status).to.eq(200);
+      expect(response.headers["content-type"]).to.include("application/zip");
+
+      // Verify Content-Disposition header is present
+      const contentDisposition = response.headers["content-disposition"];
+      expect(contentDisposition).to.exist;
+      expect(contentDisposition).to.include("attachment");
+      expect(contentDisposition).to.include("filename=");
+
+      cy.log("✅ Normal export works");
+    });
+  });
+
+  it("should handle dashboard names with brackets [2024]", () => {
+    // Test that quote() properly encodes brackets
+    cy.request({
+      method: "GET",
+      url: "http://localhost:8088/api/v1/dashboard/export";,
+      query: { q: "[1]" }, // Export dashboard with ID 1
+      headers: {
+        Authorization: "Bearer admin",
+      },
+    }).then((response) => {
+      expect(response.status).to.eq(200);
+
+      const contentDisposition = response.headers["content-disposition"];
+
+      // Verify brackets are either not present or properly encoded
+      if (contentDisposition.includes("[")) {
+        // If brackets somehow get through, they should be percent-encoded
+        expect(contentDisposition).to.include("%5B");
+        cy.log("✅ Brackets properly encoded as %5B");
+      }
+
+      // ZIP content should be valid
+      expect(response.body).to.be.instanceof(ArrayBuffer);
+      expect(response.body.byteLength).to.be.greaterThan(0);
+
+      cy.log("✅ Brackets in filename handled correctly");
+    });
+  });
+
+  it("should verify Content-Disposition header format", () => {
+    // This test verifies the HTTP header format is correct for downloads
+    cy.request({
+      method: "GET",
+      url: "http://localhost:8088/api/v1/dashboard/export?q=[1]";,
+      headers: {
+        Authorization: "Bearer admin",
+      },
+      // Don't fail on non-2xx responses for this test
+      failOnStatusCode: false,
+    }).then((response) => {
+      if (response.status === 200) {
+        const disposition = response.headers["content-disposition"];
+
+        // Verify proper RFC 2183/2184 format
+        expect(disposition).to.match(/^attachment/);
+
+        // Filename should be present and properly quoted
+        expect(disposition).to.include('filename=');
+
+        // Dangerous HTTP header characters should be encoded
+        expect(disposition).not.to.include(';');
+        expect(disposition).not.to.include(',');
+        expect(disposition).not.to.include('"');
+
+        cy.log("✅ Content-Disposition header properly formatted");
+        cy.log(`   Header: ${disposition.substring(0, 80)}...`);
+      }
+    });
+  });
+
+  it("should export valid ZIP file", () => {
+    // Test that the exported file is a valid ZIP
+    cy.request({
+      method: "GET",
+      url: "http://localhost:8088/api/v1/dashboard/export?q=[1]";,
+      headers: {
+        Authorization: "Bearer admin",
+      },
+      responseType: "arraybuffer",
+    }).then((response) => {
+      if (response.status === 200) {
+        // Check ZIP magic bytes (PK\x03\x04)
+        const view = new Uint8Array(response.body);
+        const header = `${String.fromCharCode(view[0])}${String.fromCharCode(
+          view[1]
+        )}${String.fromCharCode(view[2])}${String.fromCharCode(view[3])}`;
+
+        expect(header).to.equal("PK\x03\x04");
+        cy.log("✅ ZIP file has valid magic bytes");
+
+        // File should have reasonable size (>100 bytes)
+        expect(response.body.byteLength).to.be.greaterThan(100);
+        cy.log(`✅ ZIP file size: ${response.body.byteLength} bytes`);
+      }
+    });
+  });
+
+  it("should handle URL encoding without breaking dashboard content", () => {
+    // Verify that quote() encoding doesn't affect ZIP contents
+    cy.request({
+      method: "GET",
+      url: "http://localhost:8088/api/v1/dashboard/export?q=[1]";,
+      headers: {
+        Authorization: "Bearer admin",
+      },
+      responseType: "arraybuffer",
+    }).then((response) => {
+      if (response.status === 200) {
+        // Convert to text to verify YAML content is intact
+        const buffer = new Uint8Array(response.body);
+        const compressed = buffer.buffer;
+
+        // ZIP should contain readable YAML files (we can check for "YAML" 
text)
+        // This is a basic content verification
+        expect(buffer.length).to.be.greaterThan(0);
+
+        cy.log("✅ Export contains dashboard content");
+      }
+    });
+  });
+});
+
+/**

Review Comment:
   Probably leftover fragments of AI. Should be removed.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to