From 3660a817ce2f7dad2a4f10e05368d75149314248 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 19 Sep 2025 20:51:12 +0000
Subject: [PATCH 1/2] Initial plan
From 4ac2eb58ad5d00f3cabe35aa542160a9ace40bdc Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 19 Sep 2025 20:57:58 +0000
Subject: [PATCH 2/2] Add security headers and explicit robots.txt/sitemap.xml
endpoints
Co-authored-by: simensma-fresh <66635118+simensma-fresh@users.noreply.github.com>
---
services/core-web/runner/server.js | 52 ++++++++++++++++++++++++-
services/minespace-web/runner/server.js | 52 ++++++++++++++++++++++++-
2 files changed, 102 insertions(+), 2 deletions(-)
diff --git a/services/core-web/runner/server.js b/services/core-web/runner/server.js
index 3e27dd2dd6..0999ac7a6d 100755
--- a/services/core-web/runner/server.js
+++ b/services/core-web/runner/server.js
@@ -39,7 +39,22 @@ app.use(
? {
directives: CONTENT_SECURITY_POLICY,
}
- : false,
+ : {
+ directives: {
+ defaultSrc: ["'self'"],
+ scriptSrc: ["'self'", "'unsafe-inline'"], // Note: unsafe-inline should be removed when proper nonces are implemented
+ styleSrc: ["'self'", "'unsafe-inline'"], // Note: unsafe-inline should be removed when proper nonces are implemented
+ imgSrc: ["'self'", "data:", "https:"],
+ connectSrc: ["'self'"],
+ fontSrc: ["'self'"],
+ objectSrc: ["'none'"],
+ mediaSrc: ["'self'"],
+ frameSrc: ["'none'"],
+ },
+ },
+ crossOriginEmbedderPolicy: { policy: "require-corp" },
+ crossOriginOpenerPolicy: { policy: "same-origin" },
+ crossOriginResourcePolicy: { policy: "same-origin" },
})
);
@@ -100,10 +115,45 @@ app.get(`/version`, (req, res) => {
});
});
+// Explicit robots.txt endpoint with proper security headers
+app.get('/robots.txt', (req, res) => {
+ res.setHeader('Content-Type', 'text/plain');
+ res.setHeader('Cache-Control', 'public, max-age=86400');
+ res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
+ res.setHeader('X-Content-Type-Options', 'nosniff');
+ res.send('User-agent: *\nDisallow: /');
+});
+
+// Explicit sitemap.xml endpoint with proper security headers
+app.get('/sitemap.xml', (req, res) => {
+ res.setHeader('Content-Type', 'application/xml');
+ res.setHeader('Cache-Control', 'public, max-age=86400');
+ res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
+ res.setHeader('X-Content-Type-Options', 'nosniff');
+ res.send('\n\n');
+});
+
+// Handle trailing slash redirects for robots.txt and sitemap.xml
+app.get('/robots.txt/', (req, res) => {
+ res.redirect(301, '/robots.txt');
+});
+
+app.get('/sitemap.xml/', (req, res) => {
+ res.redirect(301, '/sitemap.xml');
+});
+
app.use((req, res, next) => {
if (PERMISSIONS_POLICY) {
res.setHeader("Permissions-Policy", PERMISSIONS_POLICY);
}
+
+ // Additional security headers to address ZAP scan issues
+ res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
+ res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
+ res.setHeader('X-Content-Type-Options', 'nosniff');
+ res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
+
next();
});
diff --git a/services/minespace-web/runner/server.js b/services/minespace-web/runner/server.js
index 77dfb86a41..59c3ce0fa3 100755
--- a/services/minespace-web/runner/server.js
+++ b/services/minespace-web/runner/server.js
@@ -38,7 +38,22 @@ app.use(
? {
directives: CONTENT_SECURITY_POLICY,
}
- : false,
+ : {
+ directives: {
+ defaultSrc: ["'self'"],
+ scriptSrc: ["'self'", "'unsafe-inline'"], // Note: unsafe-inline should be removed when proper nonces are implemented
+ styleSrc: ["'self'", "'unsafe-inline'"], // Note: unsafe-inline should be removed when proper nonces are implemented
+ imgSrc: ["'self'", "data:", "https:"],
+ connectSrc: ["'self'"],
+ fontSrc: ["'self'"],
+ objectSrc: ["'none'"],
+ mediaSrc: ["'self'"],
+ frameSrc: ["'none'"],
+ },
+ },
+ crossOriginEmbedderPolicy: { policy: "require-corp" },
+ crossOriginOpenerPolicy: { policy: "same-origin" },
+ crossOriginResourcePolicy: { policy: "same-origin" },
})
);
@@ -89,10 +104,45 @@ app.get(`/version`, (req, res) => {
});
});
+// Explicit robots.txt endpoint with proper security headers
+app.get('/robots.txt', (req, res) => {
+ res.setHeader('Content-Type', 'text/plain');
+ res.setHeader('Cache-Control', 'public, max-age=86400');
+ res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
+ res.setHeader('X-Content-Type-Options', 'nosniff');
+ res.send('User-agent: *\nDisallow: /');
+});
+
+// Explicit sitemap.xml endpoint with proper security headers
+app.get('/sitemap.xml', (req, res) => {
+ res.setHeader('Content-Type', 'application/xml');
+ res.setHeader('Cache-Control', 'public, max-age=86400');
+ res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
+ res.setHeader('X-Content-Type-Options', 'nosniff');
+ res.send('\n\n');
+});
+
+// Handle trailing slash redirects for robots.txt and sitemap.xml
+app.get('/robots.txt/', (req, res) => {
+ res.redirect(301, '/robots.txt');
+});
+
+app.get('/sitemap.xml/', (req, res) => {
+ res.redirect(301, '/sitemap.xml');
+});
+
app.use((req, res, next) => {
if (PERMISSIONS_POLICY) {
res.setHeader("Permissions-Policy", PERMISSIONS_POLICY);
}
+
+ // Additional security headers to address ZAP scan issues
+ res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
+ res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
+ res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
+ res.setHeader('X-Content-Type-Options', 'nosniff');
+ res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
+
next();
});