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
Binary file added HotmodelleringFas1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion backend/middleware/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ export const authenticateUser = async (req, res, next) => {
return res.status(401).json({ success: false, message: "No token provided" })
}
try {
// POSITIVT: Verifierar token mot hemlig nyckel (JWT_SECRET) innan åtkomst godkänns
const decoded = jwt.verify(token, process.env.JWT_SECRET)
const user = await User.findById(decoded.userId)
if (!user) {
// SÄKERHETSBRIST (User Enumeration): Filen returnerar olika felmeddelanden beroende på varför autentisering misslyckas ("No token provided", "User not found", "Invalid token").
// Kan avslöja info för angriparen om varför åtkomst nekades. Enligt säkerhetskrav från fas 1 bör samma generiska felmeddelande returneras oavsett orsak.
if (!user) {
return res.status(401).json({ success: false, message: "User not found" })
}
req.user = user
Expand Down
3 changes: 3 additions & 0 deletions backend/models/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import mongoose from "mongoose"

const messageSchema = new mongoose.Schema({
message: {
// SÄKERHETSBRIST (XSS/Injection): Meddelandefältet saknar validering och sanering av innehåll. Skadlig kod kan injiceras och lagras i databasen.
// SÄKERHETSBRIST (BAC): Modellen saknar maxlength på meddelandefältet vilket innebär att extremt långa meddelanden kan skickas. Behörighetskontroll, dvs vem som får läsa, ändra eller ta bort ett meddelande, hanteras inte här utan måste implementeras i API-logiken.
Comment thread
happ100 marked this conversation as resolved.
type: String,
required: true
},
user: {
// POSITIVT: Meddelandet är kopplat till en specifik användare via user-fältet, en bra grund för behörighetskontroll.
Comment thread
happ100 marked this conversation as resolved.
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
Expand Down
1 change: 1 addition & 0 deletions backend/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ app.patch("/messages/:id", authenticateUser, async (req, res) => {
}
})

// SÄKERHETSBRIST (BAC): DELETE-endpointen saknar autentisering (authenticateUser), vem som helst kan ta bort vilket meddelande som helst utan att vara inloggad.
app.delete("/messages/:id", async (req, res) => {
if (!isValidId(req.params.id)) return res.status(400).json({ error: "Invalid message ID" })
try {
Expand Down
20 changes: 20 additions & 0 deletions fas3-sammanfattning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Fas 3 – Granskning: Sammanfattning

Verktyg som använts:
CodeQL och Dependabot via GitHub.

Fynd:
CodeQL identifierade 8 alerts i backend/server.js, 7 endpoints saknar rate limiting (High) och en tillåtande CORS-konfiguration (Medium).
Dependabot identifierade 16 alerts, varav JWT-sårbarheterna (jsonwebtoken) är de mest kritiska då det är ett direkt beroende aktivt i appen.

Koppling till OWASP Top 10:
Saknad rate limiting → A07: Identification and Authentication Failures
JWT-sårbarheter → A02: Cryptographic Failures

Åtgärdsförslag:
- Installera express-rate-limit och begränsa känsliga endpoints
- Uppdatera jsonwebtoken till senaste versionen


Slutsats:
Manuell granskning och automatiserade verktyg kompletterar varandra. Verktygen fångade konfigurationsbrister som missades i Fas 2, medan den manuella granskningen identifierade logikfel som verktygen inte upptäckte.
12 changes: 8 additions & 4 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const App = () => {

const fetchPosts = () => {
setLoading(true)
// SÄKERHETSBRIST (BAC): /messages-endpointen hämtas utan autentisering. Vem som helst kan se alla meddelanden utan att vara inloggad.
fetch(`${BASE_URL}/messages`)
.then((res) => res.json())
.then((data) => setMessageList(data))
Expand All @@ -27,11 +28,13 @@ export const App = () => {
setMessageList([newMessage, ...messageList])
}

//POSITIVT: handleUnauthorized nollställer användarsessionen när token går ut. Logout-funktionen finns och nollställer användaren korrekt.
const handleUnauthorized = () => {
setUser(null)
// SÄKERHETSBRIST (User Enumeration): Felmeddelandet "Your session has expired, please log in again" avslöjar info om sessionshanteringen för användaren.
setError("Your session has expired, please log in again")
}

return (
<>
{user ? (
Expand Down Expand Up @@ -64,10 +67,11 @@ export const App = () => {
<AuthModal
mode={modal}
onClose={() => setModal(null)}
onSuccess={(data) => {
onSuccess={(data) => {
// SÄKERHETSBRIST (Information Disclosure): console.log("User logged in:", data) loggar användardata i webbläsarens konsol vilket är synligt för vem som helst som öppnar dev-verktygen i webbläsaren. Bör tas bort i skarp miljö.
console.log("User logged in:", data)
setUser(data)
setModal(null)
setUser(data)
setModal(null)
}}
/>
)}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/api.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
// SÄKERHETSBRIST (Information Disclosure): URL:en till backend är hårdkodad och synlig i frontend-koden. Angriparen kan enkelt identifiera var API:et finns och rikta attacker direkt dit. URL:en bör inte exponeras i koden utan hanteras på ett säkrare sätt.
export const BASE_URL = "https://yh-message-app-fullstack.onrender.com"
4 changes: 4 additions & 0 deletions frontend/src/components/AuthModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ export const AuthModal = ({ mode, onClose, onSuccess }) => {
const data = await res.json()
if (!res.ok) throw new Error(data.message || "Something went wrong")

// SÄKERHETSBRIST (Information Disclosure): console.log("Auth successful:", data) och console.log(err) loggar känslig autentiseringsdata i webbläsarens konsol, synligt för vem som helst med tillgång till dev-verktygen.
console.log("Auth successful:", data)
onSuccess(data)
} catch (err) {
console.log(err)
// SÄKERHETSBRIST (User Enumeration): setError(err.message) visar exakta felmeddelanden från backend direkt i gränssnittet vilket kan avslöja för en angripare om ett konto existerar eller inte.
setError(err.message)
} finally {
setSubmitting(false)
Expand All @@ -41,6 +43,7 @@ export const AuthModal = ({ mode, onClose, onSuccess }) => {

return (
<div onClick={onClose}>
{/* SÄKERHETSBRIST (Brute Force): Saknar begränsning av antal inloggningsförsök i formuläret, kan skickas hur många gånger som helst utan blockering. */}
<form
onClick={(e) => e.stopPropagation()}
onSubmit={handleSubmit}
Expand Down Expand Up @@ -78,6 +81,7 @@ export const AuthModal = ({ mode, onClose, onSuccess }) => {
/>
)}

{/* POSITIVT: Använder type="password" för lösenordsfältet så att texten döljs. autoComplete är korrekt satt för respektive fält.*/}
<input
type="password"
placeholder="Password"
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/components/PostMessage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const PostMessage = ({ newMessage, fetchPosts, user, onUnauthorized }) =>
const [errorMessage, setErrorMessage] = useState("")
const [submitting, setSubmitting] = useState(false)

// POSITIVT: Kräver att användaren är inloggad innan formuläret visas. Skickar JWT-token med varje anrop via Authorization-headern. Hanterar 401-svar korrekt via onUnauthorized.
const handleFormSubmit = async (event) => {
event.preventDefault()
setSubmitting(true)
Expand All @@ -20,6 +21,7 @@ export const PostMessage = ({ newMessage, fetchPosts, user, onUnauthorized }) =>
body: JSON.stringify({ message: newPost }),
})

// SÄKERHETSBRIST (Information Disclosure): console.log("Token being sent:", user?.response?.accessToken) loggar JWT-token i klartext i webbläsarens konsol. Angripare med tillgång till konsolen kan stjäla token och utge sig för att vara användaren.
console.log("Token being sent:", user?.response?.accessToken)

if (res.status === 401) {
Expand Down Expand Up @@ -56,6 +58,7 @@ export const PostMessage = ({ newMessage, fetchPosts, user, onUnauthorized }) =>
<div id="post-form-wrapper" className="post-wrapper">
<p>What's making you happy right now?</p>
<form id="post-form" onSubmit={handleFormSubmit}>
{/* SÄKERHETSBRIST (XSS/Injection): textarea-fältet saknar maxlength och ingen sanering av input sker innan det skickas till backend, skadlig kod kan skickas och lagras i databasen. */}
<textarea
id="post-textarea"
rows="3"
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/components/SingleMessage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const SingleMessage = ({ message, user, onUnauthorized, fetchPosts }) =>
{isEditing && (
<div className="edit-wrapper">
<label>
{/* SÄKERHETSBRIST (XSS/Injection): edit-textarea saknar maxlength, ingen begränsning på hur lång eller skadlig text som kan skickas vid redigering. */}
<textarea
className="edit-textarea"
rows="3"
Expand All @@ -83,12 +84,14 @@ export const SingleMessage = ({ message, user, onUnauthorized, fetchPosts }) =>
)}

<div className="message-actions">
{/* POSITIVT: isOwner-kontrollen döljer redigera-knappen för användare som inte äger meddelandet. JWT-token skickas med vid både DELETE och PATCH-anrop. */}
<button type="button" className="delete-btn" onClick={onDelete}>🗑️</button>

{isOwner && !isEditing && (
<button type="button" className="edit-btn" onClick={() => setIsEditing(true)}>✏️</button>
)}

{/* POSITIVT: Ägarskapskontrollen sker även i backend, endast ägaren av meddelandet kan redigera det. */}
{isOwner && isEditing && (
<button type="button" className="save-btn" onClick={onSave}>💾</button>
)}
Expand Down
1 change: 0 additions & 1 deletion planeringsfasen.md

This file was deleted.