diff --git a/backend/handlers/bookings/manage.go b/backend/handlers/bookings/manage.go index 286ab6d..f139446 100644 --- a/backend/handlers/bookings/manage.go +++ b/backend/handlers/bookings/manage.go @@ -1583,6 +1583,8 @@ func AdminApproveEditRequestHandler(w http.ResponseWriter, r *http.Request) { // Build update query for bookings table if newStartTime != nil || notes != nil { + // setClauses contains only hardcoded column name assignments ("start_time = $N", "notes = $N"). + // Column names are never derived from user input. User values are in args and always parameterised. var setClauses []string var args []interface{} argNum := 1 diff --git a/compose.yml b/compose.yml index 1928717..f7b125e 100644 --- a/compose.yml +++ b/compose.yml @@ -41,6 +41,7 @@ services: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + DAV_ADMIN_PASSWORD: ${DAV_ADMIN_PASSWORD:-admin} # Must be set in production volumes: - ./sabredav:/var/www/dav depends_on: diff --git a/nginx/conf.d/default.conf b/nginx/conf.d/default.conf index 62290d3..771933b 100644 --- a/nginx/conf.d/default.conf +++ b/nginx/conf.d/default.conf @@ -15,6 +15,7 @@ server { add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block"; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';" always; # Rate limiting (per IP) limit_req_zone $binary_remote_addr zone=api_limit:10m rate=20r/m; @@ -80,7 +81,7 @@ server { # Allow DAV methods if ($request_method = 'OPTIONS') { - add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Origin' '$http_origin'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PROPFIND, PROPPATCH, REPORT, MKCOL, MOVE, COPY'; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-None-Match,If-Modified-Since,Cache-Control,Content-Type,Range,Depth,Authorization,If-Match,Destination,Overwrite,Lock-Token,Timeout'; add_header 'Access-Control-Max-Age' 1728000; diff --git a/sabredav/server.php b/sabredav/server.php index ffd291e..d05f7b0 100644 --- a/sabredav/server.php +++ b/sabredav/server.php @@ -144,11 +144,11 @@ if ($stmt->fetchColumn() == 0) { "); } -// Create default user if not exists (username: admin, password: admin) +// Create default user if not exists (username: admin, password from DAV_ADMIN_PASSWORD env) +$davPassword = getenv('DAV_ADMIN_PASSWORD') ?: 'admin'; $stmt = $pdo->query("SELECT COUNT(*) FROM dav_users"); if ($stmt->fetchColumn() == 0) { - // MD5 hash of "admin:SabreDAV:admin" - $digest = md5('admin:SabreDAV:admin'); + $digest = md5('admin:SabreDAV:' . $davPassword); $pdo->exec(" INSERT INTO dav_users (username, digesta1) VALUES ('admin', '$digest')