Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions backend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ app.use(cookieParser());
app.use((req, res, next) => {
req.requestId = req.headers["x-request-id"] || randomUUID();
res.setHeader("x-request-id", req.requestId);
// SECURITY: Content-Security-Policy header to mitigate XSS attacks.
// Blocks inline scripts, restricts sources, and prevents clickjacking.
res.setHeader(
"Content-Security-Policy",
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https: blob:; connect-src 'self' https://*.supabase.co wss://*.supabase.co; font-src 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'"
);
// Additional security headers
res.setHeader("X-Content-Type-Options", "nosniff");
res.setHeader("X-Frame-Options", "DENY");
res.setHeader("X-XSS-Protection", "1; mode=block");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files matching backend/app.js =="
fd -a 'app\.js$' . | sed 's#^\./##'

echo
echo "== backend/app.js outline/snippet =="
if [ -f backend/app.js ]; then
  wc -l backend/app.js
  cat -n backend/app.js | sed -n '60,95p'
fi

echo
echo "== header-related usages =="
rg -n "X-XSS-Protection|Content-Security-Policy|X-Frame|X-Content-Type|Referrer" backend/app.js backend || true

echo
echo "== package/security/middleware context =="
fd -a 'package.json$' . | sed 's#^\./##' | while read -r f; do
  echo "--- $f"
  node -e "const p=require('${f//\'/\\\'}'); console.log(JSON.stringify({name:p.name, dependencies:p.dependencies, devDependencies:p.devDependencies, scripts:p.scripts}, null, 2))" 2>/dev/null || sed -n '1,160p' "$f"
done

Repository: durdana3105/peer-learning

Length of output: 7445


🌐 Web query:

MDN X-XSS-Protection deprecated 0 XSS protection disabled

💡 Result:

The X-XSS-Protection HTTP header is deprecated and no longer recommended for use in modern web development [1][2][3][4]. The header was originally designed to control a browser-level feature known as the XSS Auditor, which attempted to detect and block reflected cross-site scripting (XSS) attacks [2][5][6]. However, this auditor was found to be unreliable, prone to bypasses, and in some instances, capable of introducing new security vulnerabilities (such as enabling cross-site information leaks or creating XSS where none existed) [2][7][8][9]. Regarding the specific configuration X-XSS-Protection: 0: - Meaning: This value explicitly disables the legacy XSS auditor [2][7][6]. - Recommendation: While the header itself is deprecated and ignored by modern browsers, many security experts recommend explicitly setting X-XSS-Protection: 0 as a defensive measure [9][5][6]. Doing so ensures that if a user visits the site with a very old browser that still includes an XSS auditor, the feature is explicitly turned off rather than defaulting to an potentially harmful enabled state [9][6]. - Alternative: Simply omitting the header is also acceptable, as modern browsers have removed the XSS auditor entirely [6][4]. In 2026, the industry-standard approach for preventing XSS is to implement a robust Content Security Policy (CSP) [3][6][4]. CSP provides much stronger and more reliable protection against XSS by restricting the sources from which scripts can be loaded and executed, rendering the legacy XSS auditor obsolete [8][6][4].

Citations:


Remove the deprecated X-XSS-Protection header.

This non-standard header is deprecated, and modern protections are provided by your existing CSP. Remove it here, or use 0 only if the browser-support policy requires explicitly disabling legacy XSS auditors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/app.js` at line 82, Remove the res.setHeader call that sets
X-XSS-Protection in the backend response setup, leaving the existing CSP
protections and other response headers unchanged.

res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
next();
});

Expand Down
51 changes: 19 additions & 32 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"date-fns": "^3.6.0",
"dompurify": "^3.4.7",
"dompurify": "^3.4.12",
"embla-carousel-react": "^8.6.0",
"framer-motion": "^11.18.2",
"input-otp": "^1.4.2",
Expand Down
7 changes: 5 additions & 2 deletions src/components/chat/MessageBubble.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { memo } from "react";
import { memo, useMemo } from "react";
import { sanitizeMessageContent } from "@/utils/sanitize";

interface MessageBubbleProps {
text: string;
Expand All @@ -12,6 +13,8 @@ const MessageBubble = ({
time,
}: MessageBubbleProps) => {
const isUser = sender === "user";
// SECURITY (#1852): Sanitize message content to prevent XSS on render
const safeText = useMemo(() => sanitizeMessageContent(text), [text]);

return (
<div className={`flex mb-3 ${isUser ? "justify-end" : "justify-start"}`}>
Expand All @@ -22,7 +25,7 @@ const MessageBubble = ({
: "bg-white text-black rounded-bl-none"
}`}
>
<p>{text}</p>
<p>{safeText}</p>
<span className="block text-xs mt-1 opacity-70 text-right">{time}</span>
</div>
</div>
Expand Down
8 changes: 6 additions & 2 deletions src/components/messages/utils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { ProfileSummary, MessageRow } from "@/hooks/useMessages";
import { sanitizeMessageContent } from "@/utils/sanitize";

export const getDisplayName = (profile?: Pick<ProfileSummary, "name" | "email"> | null) =>
profile?.name?.trim() || profile?.email?.split("@")[0] || "Learner";

export const getInitial = (profile?: Pick<ProfileSummary, "name" | "email"> | null) =>
getDisplayName(profile).charAt(0).toUpperCase();

export const getMessageBody = (message: MessageRow) =>
message.content || message.text || message.message || "";
/** SECURITY (#1852): Sanitize message body to prevent Stored XSS on render */
export const getMessageBody = (message: MessageRow) => {
const raw = message.content || message.text || message.message || "";
return sanitizeMessageContent(raw);
};

export const getRoleLabel = (profile: ProfileSummary) => {
if (profile.is_mentor && profile.is_learner) return "Mentor + Learner";
Expand Down
Loading