Skip to content

TomerShech/penguwave

Repository files navigation

PenguWave: Security Operations Portal

A security operations portal for monitoring security events across your infrastructure.

Overview

This is a React + TypeScript security operations portal with a Node.js + Express backend API.

Prerequisites

  • Node.js 24+

This project uses Node's built-in node:sqlite module for local SQLite persistence.

Getting Started

(I was using pnpm for this project but of course npm would work just fine)

Install dependencies:

npm install

Start the API:

npm run start:server

For backend development with automatic restarts, use:

npm run dev:server

Start the frontend:

npm run dev

The frontend runs at http://localhost:5173 and the API runs at http://localhost:3001.

To build the frontend and type-check the project:

npm run build

To run linting:

npm run lint

Seeded credentials:

  • admin@penguwave.io / admin123
  • analyst@penguwave.io / pass456
  • viewer@penguwave.io / view789 is disabled and cannot sign in

The local database is created automatically at data/penguwave.sqlite the first time the API needs user or event data.

What's Included

  • React + Vite + TypeScript frontend
  • Express API with cookie-based authentication
  • Persistent SQLite database at data/penguwave.sqlite
  • 50 realistic mock security events (data/mock_events.json)
  • API endpoint contract (docs/api_contract.md)
  • Threat-thinking document (THREAT-THINKING.md)

Authentication

Users sign in through POST /api/auth/login. The backend normalizes the supplied email, looks up the user in SQLite, compares the submitted password with the stored bcrypt hash, and rejects disabled accounts.

On a successful login, the API signs a JWT containing the user's id and stores it in an HttpOnly, SameSite=Lax cookie named penguwave_session. The cookie lasts 12 hours. In production, the cookie is also marked Secure, so it's only sent over HTTPS.

The React app doesn't store the JWT in local storage. It restores the current session by calling GET /api/auth/me, which verifies the cookie, reloads the user from SQLite, and confirms the account is still active. Logout uses POST /api/auth/logout, which clears both the session cookie and the CSRF cookie.

State-changing API calls are protected with a CSRF double-submit token:

  1. The frontend calls GET /api/auth/csrf.
  2. The backend returns a random token and sets the readable penguwave_csrf cookie.
  3. The frontend sends the same value in the X-CSRF-Token header on POST, PATCH, and DELETE requests.
  4. The API rejects missing or mismatched tokens with 403.

Protected frontend routes wait for the /api/auth/me check before rendering page content. This improves the user experience, but it's not the security boundary - every protected API endpoint also verifies authentication on the server.

Authorization

Authorization is enforced on the backend. The events API uses requireAuth, so only authenticated, active users can read security events. Event queries are also scoped by userId, so /api/events and /api/events/:id only return events owned by the authenticated user. User management routes use both requireAuth and requireRole("admin"), so non-admin users receive 403 Access forbidden even if they call /api/users directly.

The frontend mirrors these rules for usability. The /users page is wrapped in AdminRoute, so analysts don't see the management UI. This client-side guard is only a convenience - the Express middleware is what actually prevents unauthorized access.

User records returned by the API are passed through toPublicUser, which removes password hashes before data leaves the server. Admin actions are also constrained by route-level checks:

  • Admins cannot delete their own account.
  • Admins cannot delete other admin accounts.
  • Admins cannot disable or demote other admin accounts.
  • The API rejects any role/status update that would leave the system with no active admin accounts.
  • User creation validates role, duplicate email, and minimum password length before storing a bcrypt hash.

This prevents analysts from managing users, prevents password hashes from leaking through responses, and reduces the chance of accidentally locking all administrators out of the system.

Storage

User and event data are persisted in data/penguwave.sqlite using Node's built-in node:sqlite module. On first use, the backend creates the users and events tables. Tables are seeded only when empty. Security events are imported from data/mock_events.json, and user records are loaded from data/users.json.

After the initial seed, login lookups, user creation, user updates, user deletes, and event reads are served from SQLite.

Production Deployment

If this system were deployed to production, I would make the following changes before exposing it to real users:

  • Run the frontend and API only over HTTPS with HSTS.
  • Set NODE_ENV=production so session and CSRF cookies use the Secure flag.
  • Replace the development JWT_SECRET with a high-entropy secret from a secret manager, and rotate it through a documented process.
  • Restrict CORS to the exact deployed frontend origin instead of allowing broad origins.
  • Put the API behind a reverse proxy or load balancer with request size limits, security headers, access logs, and rate limiting for login attempts.
  • Use a managed relational database such as PostgreSQL instead of local SQLite, with migrations, encrypted backups, monitoring, least-privilege database credentials, and a tested restore process.
  • Keep password storage on a slow hash such as bcrypt or Argon2, with appropriate cost settings for the production hardware.
  • Store secrets outside the repository and CI logs.
  • Add centralized audit logging for login attempts and user-management actions.
  • Serve built frontend assets from a static host or CDN and deploy the API as a separate service with health checks.
  • Continue enforcing authentication and authorization in the backend, with frontend route guards treated only as UX.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages