docs: add SECURITY.md and API_KEYS.md documentation
- Create SECURITY.md with vulnerability reporting policy - Add environment variables & secrets best practices - Document input validation and rate limiting strategies - Create docs/API_KEYS.md with step-by-step API credential guides - IGDB OAuth 2.0 via Twitch setup - RAWG API key simple registration - TheGamesDB API key registration - Update README.md with security and API configuration sections - Add tests/documentation.spec.ts with 12 validation tests
This commit is contained in:
18
README.md
18
README.md
@@ -11,6 +11,24 @@ Quasar es una aplicación web para al gestión de una biblioteca personal de vid
|
||||
- **Búsqueda Avanzada**: Filtros para encontrar juegos rápidamente según diferentes criterios.
|
||||
- **Búsqueda de Metadatos**: Integración con APIs externas para obtener información adicional sobre los juegos.
|
||||
|
||||
## Seguridad
|
||||
|
||||
Para información sobre políticas de seguridad, vulnerabilidades y prácticas recomendadas, consulta [SECURITY.md](SECURITY.md).
|
||||
|
||||
## Configuración de APIs
|
||||
|
||||
Quasar se integra con múltiples servicios de metadatos: IGDB, RAWG y TheGamesDB. Para obtener credenciales y configurar estas APIs, consulta [docs/API_KEYS.md](docs/API_KEYS.md).
|
||||
|
||||
### Variables de Entorno
|
||||
|
||||
La aplicación requiere variables de entorno sensibles (como claves de API y credenciales de base de datos).
|
||||
Usa `.env.local` o `.env.{NODE_ENV}.local` para desarrollo. **Nunca** hagas commit de archivos `.env` al repositorio:
|
||||
|
||||
```bash
|
||||
cp .env.example .env.local
|
||||
# Edita .env.local con tus credenciales
|
||||
```
|
||||
|
||||
## Otros proyectos relacionados, para coger ideas y funcionalidades
|
||||
|
||||
| Herramienta | Categoría | Descripción | Features Destacadas | Ideal Para | Enlace Oficial |
|
||||
|
||||
67
SECURITY.md
Normal file
67
SECURITY.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting Security Vulnerabilities
|
||||
|
||||
If you discover a security vulnerability in Quasar, please email security@quasar.local with:
|
||||
|
||||
- Description of the vulnerability
|
||||
- Steps to reproduce
|
||||
- Potential impact
|
||||
- Suggested fix (if any)
|
||||
|
||||
We'll acknowledge your report within 48 hours and work on a fix.
|
||||
|
||||
## Environment Variables & Secrets
|
||||
|
||||
**IMPORTANT:** Never commit `.env` files to the repository.
|
||||
|
||||
### Sensitive Variables
|
||||
|
||||
- `IGDB_CLIENT_ID`, `IGDB_CLIENT_SECRET` — Twitch OAuth credentials
|
||||
- `RAWG_API_KEY` — RAWG API key (rate limited)
|
||||
- `THEGAMESDB_API_KEY` — TheGamesDB key
|
||||
- `DATABASE_URL` — SQLite file path (contains password if using remote DB)
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. Use `.env.local` or `.env.{NODE_ENV}.local` for local development
|
||||
2. Never log or print secrets
|
||||
3. Use GitHub/Gitea Secrets for CI/CD pipelines
|
||||
4. Rotate keys regularly
|
||||
5. Use separate keys for development, staging, production
|
||||
|
||||
## Input Validation & Sanitization
|
||||
|
||||
All user inputs are validated using **Zod** schemas:
|
||||
|
||||
- Backend: `src/validators/*.ts` define strict schemas
|
||||
- Frontend: React Hook Form + Zod validation
|
||||
- Game titles, ROM file paths, and user uploads are sanitized
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
API calls to metadata services are rate-limited:
|
||||
|
||||
- IGDB: 4 requests/second
|
||||
- RAWG: 20 requests/second (free tier)
|
||||
- TheGamesDB: 1 request/second
|
||||
|
||||
## Database Security
|
||||
|
||||
SQLite is used for MVP. For production:
|
||||
|
||||
- Consider PostgreSQL or MySQL
|
||||
- Enable encrypted connections (SSL/TLS)
|
||||
- Use connection pooling (current: Prisma with pool settings)
|
||||
- Regular backups
|
||||
|
||||
## CORS & CSP
|
||||
|
||||
Configure appropriate CORS headers in backend:
|
||||
|
||||
- Frontend origin: `http://localhost:3000` (dev), `https://yourdomain.com` (prod)
|
||||
- Content Security Policy headers recommended for production
|
||||
|
||||
## Changelog
|
||||
|
||||
- v1.0.0 (2026-02-12): Initial security guidelines
|
||||
88
docs/API_KEYS.md
Normal file
88
docs/API_KEYS.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Obtaining API Keys
|
||||
|
||||
This guide explains how to get credentials for each metadata service.
|
||||
|
||||
## IGDB (Internet Game Database)
|
||||
|
||||
IGDB uses **OAuth 2.0 via Twitch**. Steps:
|
||||
|
||||
1. Go to [Twitch Developer Console](https://dev.twitch.tv/console/apps)
|
||||
2. Sign in with your Twitch account (create one if needed)
|
||||
3. Click "Create Application"
|
||||
- Name: "Quasar" (or your app name)
|
||||
- Category: Select relevant category
|
||||
- Accept terms, click Create
|
||||
4. You'll see:
|
||||
- **Client ID** — Copy this
|
||||
- Click "New Secret" to generate **Client Secret** — Copy this
|
||||
5. Go to Settings → OAuth Redirect URLs
|
||||
- Add: `http://localhost:3000/oauth/callback` (development)
|
||||
- For production: `https://yourdomain.com/oauth/callback`
|
||||
6. In your `.env` file:
|
||||
```
|
||||
IGDB_CLIENT_ID=your_client_id
|
||||
IGDB_CLIENT_SECRET=your_client_secret
|
||||
```
|
||||
7. Start Quasar, it will use IGDB automatically
|
||||
|
||||
**Rate Limit:** 4 requests/second
|
||||
|
||||
## RAWG (Rawg.io)
|
||||
|
||||
RAWG has a simpler **API Key** approach:
|
||||
|
||||
1. Go to [RAWG Settings](https://rawg.io/settings/account)
|
||||
2. Sign up if needed, then login
|
||||
3. Find "API Key" section
|
||||
4. Click "Create new key" (if needed) or copy existing key
|
||||
5. In your `.env` file:
|
||||
```
|
||||
RAWG_API_KEY=your_api_key_here
|
||||
```
|
||||
6. Start Quasar
|
||||
|
||||
**Rate Limit:** 20 requests/second (free tier)
|
||||
|
||||
**Note:** RAWG requires attribution in UI (include "Powered by RAWG" somewhere visible)
|
||||
|
||||
## TheGamesDB (thegamesdb.net)
|
||||
|
||||
TheGamesDB uses a simple **API Key**:
|
||||
|
||||
1. Go to [TheGamesDB API](https://thegamesdb.net/api)
|
||||
2. Find "API Key" section (free registration required)
|
||||
3. Register or login
|
||||
4. Copy your API key
|
||||
5. In your `.env` file:
|
||||
```
|
||||
THEGAMESDB_API_KEY=your_api_key_here
|
||||
```
|
||||
6. Start Quasar
|
||||
|
||||
**Rate Limit:** 1 request/second (free tier)
|
||||
|
||||
## Testing Without Real Keys
|
||||
|
||||
For development/testing:
|
||||
|
||||
- Leave API keys as `your_*_here` in `.env.local`
|
||||
- Quasar will gracefully degrade and show limited metadata
|
||||
- Frontend will still work with manual game entry
|
||||
|
||||
## Production Deployment
|
||||
|
||||
For production:
|
||||
|
||||
1. Generate new keys on each service (don't reuse dev keys)
|
||||
2. Store keys in **Gitea Secrets** (for CI/CD)
|
||||
3. Or use environment variables on your hosting provider
|
||||
4. Rotate keys every 3 months
|
||||
5. Monitor rate limits in service dashboards
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"IGDB_CLIENT_ID not found"** → Check `.env` file exists and has correct format
|
||||
|
||||
**"429 Too Many Requests"** → Rate limit exceeded, wait and retry
|
||||
|
||||
**"Invalid API Key"** → Copy key exactly (no spaces), verify it's active on service website
|
||||
70
tests/documentation.spec.ts
Normal file
70
tests/documentation.spec.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { readFileSync } from 'fs';
|
||||
import { existsSync } from 'fs';
|
||||
|
||||
describe('Documentation - Security and API Keys', () => {
|
||||
// SECURITY.md tests
|
||||
it('SECURITY.md exists and contains "Reporting Security Vulnerabilities"', () => {
|
||||
expect(existsSync('./SECURITY.md')).toBe(true);
|
||||
const content = readFileSync('./SECURITY.md', 'utf-8');
|
||||
expect(content).toContain('Reporting Security Vulnerabilities');
|
||||
});
|
||||
|
||||
it('SECURITY.md contains "Environment Variables & Secrets" section', () => {
|
||||
const content = readFileSync('./SECURITY.md', 'utf-8');
|
||||
expect(content).toContain('Environment Variables & Secrets');
|
||||
});
|
||||
|
||||
it('SECURITY.md contains "Input Validation & Sanitization" section', () => {
|
||||
const content = readFileSync('./SECURITY.md', 'utf-8');
|
||||
expect(content).toContain('Input Validation & Sanitization');
|
||||
});
|
||||
|
||||
it('SECURITY.md contains "Rate Limiting" section', () => {
|
||||
const content = readFileSync('./SECURITY.md', 'utf-8');
|
||||
expect(content).toContain('Rate Limiting');
|
||||
});
|
||||
|
||||
it('SECURITY.md contains "Database Security" section', () => {
|
||||
const content = readFileSync('./SECURITY.md', 'utf-8');
|
||||
expect(content).toContain('Database Security');
|
||||
});
|
||||
|
||||
// docs/API_KEYS.md tests
|
||||
it('docs/API_KEYS.md exists and contains "IGDB" section', () => {
|
||||
expect(existsSync('./docs/API_KEYS.md')).toBe(true);
|
||||
const content = readFileSync('./docs/API_KEYS.md', 'utf-8');
|
||||
expect(content).toContain('IGDB');
|
||||
});
|
||||
|
||||
it('docs/API_KEYS.md contains "RAWG" section', () => {
|
||||
const content = readFileSync('./docs/API_KEYS.md', 'utf-8');
|
||||
expect(content).toContain('RAWG');
|
||||
});
|
||||
|
||||
it('docs/API_KEYS.md contains "TheGamesDB" section', () => {
|
||||
const content = readFileSync('./docs/API_KEYS.md', 'utf-8');
|
||||
expect(content).toContain('TheGamesDB');
|
||||
});
|
||||
|
||||
it('docs/API_KEYS.md contains step-by-step instructions', () => {
|
||||
const content = readFileSync('./docs/API_KEYS.md', 'utf-8');
|
||||
expect(content).toMatch(/steps?|step-by-step|guide/i);
|
||||
});
|
||||
|
||||
// README.md tests
|
||||
it('README.md contains link to SECURITY.md', () => {
|
||||
const content = readFileSync('./README.md', 'utf-8');
|
||||
expect(content).toMatch(/SECURITY\.md|security/i);
|
||||
});
|
||||
|
||||
it('README.md contains link to docs/API_KEYS.md', () => {
|
||||
const content = readFileSync('./README.md', 'utf-8');
|
||||
expect(content).toMatch(/API_KEYS\.md|api.*key|obtaining.*key/i);
|
||||
});
|
||||
|
||||
it('README.md mentions .env.example template', () => {
|
||||
const content = readFileSync('./README.md', 'utf-8');
|
||||
expect(content).toMatch(/\.env|environment.*variable/i);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user