/**
 * vulnerable-api.js.txt
 * =====================
 * INTENTIONALLY VULNERABLE Node.js Express API for CTF / code-auditing practice.
 * DO NOT deploy or run in production. All vulnerabilities are educational.
 */

const express = require('express');
const jwt = require('jsonwebtoken');
const axios = require('axios');
const mongoose = require('mongoose');
const _ = require('lodash');

const app = express();
app.use(express.json());

// VULNERABILITY: Weak JWT secret and no expiry
const JWT_SECRET = 'secret';  // TODO: use strong secret from env

const UserSchema = new mongoose.Schema({
  username: String,
  password: String,  // stored in plaintext -- TODO: hash passwords
  role: { type: String, default: 'user' },
  email: String
});
const User = mongoose.model('User', UserSchema);

const DocumentSchema = new mongoose.Schema({
  title: String,
  content: String,
  owner: String
});
const Document = mongoose.model('Document', DocumentSchema);

// ---------- Authentication ----------

// VULNERABILITY: Broken authentication -- JWT without expiry, weak secret
app.post('/api/login', async (req, res) => {
  const { username, password } = req.body;
  const user = await User.findOne({ username, password });  // plaintext compare
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });

  // Vulnerable: no expiresIn, weak secret, algorithm not pinned
  const token = jwt.sign(
    { id: user._id, username: user.username, role: user.role },
    JWT_SECRET
  );  // TODO: add expiration
  res.json({ token });
});

// Middleware -- VULNERABILITY: does not verify algorithm, allows "none"
function authMiddleware(req, res, next) {
  const header = req.headers.authorization;
  if (!header) return res.status(401).json({ error: 'No token' });
  try {
    // Vulnerable: algorithms not restricted -- attacker can use "none"
    req.user = jwt.verify(header.replace('Bearer ', ''), JWT_SECRET);
    next();
  } catch (e) {
    res.status(403).json({ error: 'Invalid token' });
  }
}

// ---------- Routes ----------

// VULNERABILITY: NoSQL Injection (user-controlled query object)
app.post('/api/users/search', authMiddleware, async (req, res) => {
  const { username, role } = req.body;
  // Vulnerable: if username is { "$gt": "" }, returns all users
  const users = await User.find({ username, role });  // TODO: validate input types
  res.json(users);
});

// VULNERABILITY: IDOR -- no ownership check on document access
app.get('/api/documents/:id', authMiddleware, async (req, res) => {
  // Vulnerable: any authenticated user can access any document by ID
  const doc = await Document.findById(req.params.id);  // TODO: check ownership
  if (!doc) return res.status(404).json({ error: 'Not found' });
  res.json(doc);
});

app.delete('/api/documents/:id', authMiddleware, async (req, res) => {
  // Vulnerable: no ownership or role check before deletion
  await Document.findByIdAndDelete(req.params.id);
  res.json({ message: 'Deleted' });
});

// VULNERABILITY: SSRF -- user-controlled URL fetched by server
app.post('/api/fetch-url', authMiddleware, async (req, res) => {
  const { url } = req.body;
  try {
    // Vulnerable: no URL validation -- attacker can request internal services
    // e.g., http://169.254.169.254/latest/meta-data/ (AWS metadata)
    // e.g., http://localhost:6379/ (internal Redis)
    const response = await axios.get(url);  // TODO: validate URL domain
    res.json({ status: response.status, data: response.data });
  } catch (e) {
    res.status(500).json({ error: e.message });
  }
});

// VULNERABILITY: Prototype Pollution via lodash merge
app.put('/api/settings', authMiddleware, async (req, res) => {
  let settings = { theme: 'dark', notifications: true };
  // Vulnerable: deep merge of user-controlled object into settings
  // Payload: { "__proto__": { "isAdmin": true } }
  _.merge(settings, req.body);  // TODO: sanitize input keys
  res.json({ message: 'Settings updated', settings });
});

// VULNERABILITY: Mass Assignment -- user can set their own role
app.put('/api/profile', authMiddleware, async (req, res) => {
  // Vulnerable: directly applies all user-supplied fields including 'role'
  const updated = await User.findByIdAndUpdate(req.user.id, req.body, { new: true });
  res.json(updated);
});

// VULNERABILITY: Verbose error leaking stack traces
app.use((err, req, res, next) => {
  res.status(500).json({
    error: err.message,
    stack: err.stack,        // leaks internal paths and code structure
    env: process.env.NODE_ENV
  });
});

app.listen(3000, '0.0.0.0', () => {
  console.log('API running on port 3000');
});
