This document outlines the security measures, best practices, and protocols implemented in the Continuo platform to ensure data protection, user privacy, and system integrity.
- Algorithm: HS256 (HMAC SHA-256)
- Expiration: Configurable (default: 7 days)
- Refresh: Automatic token refresh mechanism
- Storage: Secure HTTP-only cookies (production) / localStorage (development)
// Token generation with proper expiration
const token = jwt.sign(
{
id: user.id,
email: user.email,
role: user.role,
companyId: user.companyId
},
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
);- Hashing: bcrypt with 12 salt rounds
- Validation: Strong password requirements
- Storage: Never stored in plain text
- Reset: Secure token-based password reset
// Password validation requirements
const validatePassword = (password) => {
if (!password || password.length < 8) return false;
if (!/[A-Z]/.test(password)) return false; // Uppercase
if (!/[a-z]/.test(password)) return false; // Lowercase
if (!/\d/.test(password)) return false; // Number
if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) return false; // Special char
return true;
};- Roles: Owner, Admin, Manager, Employee, Viewer
- Permissions: Granular access control per resource
- Company Isolation: Multi-tenant data separation
- Audit Trail: Complete action logging
// Role-based authorization
const requireRole = (roles) => {
if (!user) {
throw new AuthenticationError('Authentication required');
}
if (!roles.includes(user.role)) {
throw new ForbiddenError('Insufficient permissions');
}
return user;
};- Encryption: Data encrypted at rest
- Connection: SSL/TLS encrypted connections
- ORM: Prisma ORM prevents SQL injection
- Migrations: Secure schema evolution
- GraphQL: Schema-based validation
- Custom: Comprehensive input sanitization
- Type Safety: TypeScript for frontend validation
- Rate Limiting: Protection against abuse
// Input validation example
const validateEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const validatePersonName = (name) => {
return name && name.length >= 1 && name.length <= 50;
};- Multi-tenancy: Complete company data separation
- Queries: Automatic company ID filtering
- Access Control: User can only access their company data
// Automatic company isolation
const users = await prisma.user.findMany({
where: { companyId: currentUser.companyId }
});app.use(helmet({
contentSecurityPolicy: process.env.NODE_ENV === 'production' ? {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
},
} : false,
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));app.use(cors({
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}));- Default: 100 requests per 15 minutes per IP
- Authentication: 10 requests per 15 minutes per IP
- GraphQL: 100 requests per 15 minutes per IP
- Custom Limits: Configurable per endpoint
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: 'Too many requests from this IP, please try again later.',
standardHeaders: true,
legacyHeaders: false,
});- User Actions: All user interactions logged
- System Events: Authentication, authorization, errors
- Data Changes: Create, update, delete operations
- Security Events: Failed logins, permission violations
// Audit log example
await prisma.auditLog.create({
data: {
userId: currentUser.id,
companyId: currentUser.companyId,
action: 'CREATE',
entity: 'User',
entityId: newUser.id,
newValues: { email: newUser.email, role: newUser.role },
ipAddress: req.ip,
userAgent: req.get('User-Agent')
}
});- Production: Generic error messages
- Development: Detailed error information
- Logging: Complete error details logged
- No Data Leakage: Sensitive information never exposed
// Secure error handling
const errorHandler = (err, req, res, next) => {
const isDevelopment = process.env.NODE_ENV === 'development';
res.status(err.status || 500).json({
error: 'Internal Server Error',
message: isDevelopment ? err.message : 'Something went wrong',
...(isDevelopment && { stack: err.stack }),
});
};# Required environment variables
JWT_SECRET=your-super-secret-jwt-key
DATABASE_URL=postgresql://user:password@localhost:5432/db
REDIS_URL=redis://localhost:6379
CORS_ORIGIN=http://localhost:3000
NODE_ENV=development- HTTPS enabled
- Environment variables secured
- Database credentials rotated
- JWT secret changed
- Rate limiting configured
- Security headers enabled
- Error logging configured
- Backup strategy implemented
# Run security audits
npm audit
# Check for vulnerabilities
npm audit fix
# Run security tests
npm run test:security-
Authentication Testing
- Test invalid credentials
- Test expired tokens
- Test role-based access
-
Input Validation Testing
- Test SQL injection attempts
- Test XSS payloads
- Test malformed data
-
Authorization Testing
- Test cross-company data access
- Test privilege escalation
- Test unauthorized operations
// β
Good - Validate all inputs
if (!validateEmail(input.email)) {
throw new UserInputError('Invalid email format');
}
// β
Good - Use parameterized queries (Prisma handles this)
const user = await prisma.user.findUnique({
where: { email: input.email }
});
// β
Good - Never expose sensitive data
const user = await prisma.user.findUnique({
where: { id },
select: {
id: true,
email: true,
firstName: true,
lastName: true,
// password: false - Never select password
}
});// β
Good - Always verify authentication
const currentUser = requireAuth();
// β
Good - Check company isolation
if (currentUser.companyId !== targetUser.companyId) {
throw new ForbiddenError('Access denied');
}
// β
Good - Use role-based authorization
const currentUser = requireRole(['OWNER', 'ADMIN']);- Keep systems updated
- Use strong passwords
- Enable firewall
- Monitor logs
- Regular backups
- SSL certificates
- Regular security updates
- Access control
- Encryption at rest
- Backup encryption
- Audit logging
-
Detection
- Monitor logs for suspicious activity
- Automated alerts for security events
- User reports of suspicious behavior
-
Assessment
- Determine scope and impact
- Identify affected systems and data
- Assess potential data exposure
-
Response
- Immediate containment
- User notification if required
- System recovery procedures
-
Recovery
- System restoration
- Security improvements
- Post-incident analysis
- Security Team: security@continuo.com
- Emergency: +1-XXX-XXX-XXXX
- Bug Reports: GitHub Issues
- Failed login attempts
- Rate limit violations
- Unauthorized access attempts
- System vulnerabilities
- Security patch status
- Monthly security audits
- Quarterly penetration testing
- Annual security assessments
- Continuous vulnerability scanning
- Critical: Immediate deployment
- High: Within 24 hours
- Medium: Within 1 week
- Low: Within 1 month
- Security team reviews updates
- Testing in staging environment
- Deployment to production
- Verification and monitoring
Continuo is committed to maintaining the highest security standards to protect our users' data and ensure system integrity. We regularly review and update our security measures to address emerging threats and maintain compliance with industry best practices.
For security concerns or vulnerability reports, please contact our security team at security@[final-domain].com.
Last updated: July 19, 2025