| Version | Supported |
|---|---|
| 0.1.x | ✅ |
We take security vulnerabilities seriously. If you discover a security issue, please report it responsibly.
DO NOT open a public GitHub issue for security vulnerabilities.
Instead, please email: security@heysalad.app
Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if any)
- Acknowledgment: Within 48 hours
- Initial Assessment: Within 5 business days
- Status Updates: Every 7 days
- Resolution: Varies by severity
- We will work with you to understand and resolve the issue
- We request that you do not publicly disclose the issue until we've addressed it
- We will credit you in the security advisory (unless you prefer to remain anonymous)
Never commit API keys to version control
// ✅ Good - Use environment variables
const client = new HeySaladAI();
client.configureProvider('openai', {
apiKey: process.env.OPENAI_API_KEY,
});
// ❌ Bad - Hardcoded keys
const client = new HeySaladAI();
client.configureProvider('openai', {
apiKey: 'sk-1234567890abcdef',
});Use .env files for sensitive data:
# .env (add to .gitignore)
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
DATABASE_URL=postgresql://user:pass@host:5432/dbname// Load environment variables
import 'dotenv/config';
const apiKey = process.env.OPENAI_API_KEY;
if (!apiKey) {
throw new Error('OPENAI_API_KEY is required');
}Always validate and sanitize user input:
function processUserInput(input: string): string {
// Validate input
if (!input || typeof input !== 'string') {
throw new ValidationError('Invalid input');
}
// Check length
if (input.length > 10000) {
throw new ValidationError('Input too long');
}
// Sanitize
return input.trim().replace(/[<>]/g, '');
}Implement rate limiting to prevent abuse:
import rateLimit from 'express-rate-limit';
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per window
message: 'Too many requests, please try again later',
});
app.use('/api/', limiter);For production deployments:
// Verify API keys
function authenticateRequest(req: Request): void {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
throw new AuthError('API key required');
}
if (!isValidApiKey(apiKey)) {
throw new AuthError('Invalid API key');
}
}Configure CORS properly:
import cors from 'cors';
app.use(cors({
origin: [
'https://yourdomain.com',
'https://app.yourdomain.com',
],
credentials: true,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
}));import helmet from 'helmet';
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
},
}));# Check for vulnerabilities
npm audit
# Fix vulnerabilities
npm audit fix
# Update dependencies
npm updateWe use:
- Dependabot: Automated dependency updates
- GitHub Security Advisories: Vulnerability alerts
- npm audit: Regular security audits
- Never log API keys or secrets
- Redact sensitive information in logs
- Use secure storage for credentials
// ✅ Good - Redact sensitive data
function logRequest(config: any) {
const safe = { ...config };
if (safe.apiKey) {
safe.apiKey = '***REDACTED***';
}
console.log('Request:', safe);
}- Minimize data collection
- Delete data when no longer needed
- Follow privacy regulations (GDPR, CCPA)
Always use HTTPS in production:
// Enforce HTTPS
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https') {
res.redirect(`https://${req.header('host')}${req.url}`);
} else {
next();
}
});Don't expose sensitive information in errors:
// ✅ Good - Generic error message
app.use((err, req, res, next) => {
console.error(err); // Log full error internally
res.status(500).json({
error: 'Internal server error',
});
});
// ❌ Bad - Exposes internal details
app.use((err, req, res, next) => {
res.status(500).json({
error: err.message,
stack: err.stack,
});
});import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: true,
crossOriginEmbedderPolicy: true,
crossOriginOpenerPolicy: true,
crossOriginResourcePolicy: true,
dnsPrefetchControl: true,
frameguard: true,
hidePoweredBy: true,
hsts: true,
ieNoOpen: true,
noSniff: true,
originAgentCluster: true,
permittedCrossDomainPolicies: true,
referrerPolicy: true,
xssFilter: true,
}));For the web dashboard:
# Set secrets (not in wrangler.toml)
wrangler secret put OPENAI_API_KEY
wrangler secret put ANTHROPIC_API_KEY// Access secrets securely
export default {
async fetch(request: Request, env: Env) {
const apiKey = env.OPENAI_API_KEY;
// Use apiKey
},
};If a security incident occurs:
- Contain: Isolate affected systems
- Assess: Determine scope and impact
- Notify: Inform affected users
- Remediate: Fix the vulnerability
- Document: Record lessons learned
Before deploying to production:
- All API keys in environment variables
- Input validation on all endpoints
- Rate limiting configured
- HTTPS enforced
- Security headers configured
- Error messages don't expose internals
- Dependencies up to date
- npm audit shows no vulnerabilities
- Authentication required for protected routes
- CORS properly configured
- Logging doesn't include sensitive data
Security concerns: security@heysalad.app
General questions: dev@heysalad.app
Thank you for helping keep HeySalad AI secure!