This document outlines security practices for RSS Skull Bot to prevent credential leaks and ensure safe operation.
RSS Skull Bot handles sensitive credentials including:
- Telegram Bot Tokens
- Reddit OAuth credentials (Client ID, Client Secret, Username, Password)
- Redis passwords
- OAuth access tokens
All credentials must be kept secure and never exposed in logs, error messages, or code.
❌ BAD:
const botToken = '123456789:ABCdefGHIjklMNOpqrsTUVwxyz-123456789';✅ GOOD:
const botToken = process.env.BOT_TOKEN || '';❌ BAD:
logger.info(`Bot token: ${config.bot.token}`);
logger.error(`Failed with token: ${token}`);✅ GOOD:
logger.info('Bot token configured');
logger.error('Authentication failed'); // Token is automatically sanitizedThe logger automatically sanitizes all data before logging. However, when using console.log directly, you must sanitize manually:
❌ BAD:
console.log('Error:', error);
console.log('Data:', JSON.stringify(data));✅ GOOD:
import { sanitizeForLogging } from './utils/security/sanitizer.js';
console.log('Error:', sanitizeForLogging(error));
console.log('Data:', JSON.stringify(sanitizeForLogging(data)));❌ BAD:
await ctx.reply(`Error: ${error.message}`);✅ GOOD:
import { getSafeErrorMessage } from './utils/security/error-sanitizer.js';
await ctx.reply(`Error: ${getSafeErrorMessage(error)}`);❌ BAD:
logger.debug('Update:', JSON.stringify(ctx.update));✅ GOOD:
import { sanitizeForLogging } from './utils/security/sanitizer.js';
logger.debug('Update:', sanitizeForLogging(ctx.update));Main sanitization functions:
sanitizeString(str: string): string- Sanitizes a stringsanitizeError(error: Error): Error- Sanitizes an error objectsanitizeObject(obj: any): any- Recursively sanitizes an objectsanitizeForLogging(data: any): any- Main function for sanitizing any data before loggingsanitizeUrl(url: string): string- Removes credentials from URLs
Usage:
import { sanitizeForLogging } from './utils/security/sanitizer.js';
// Before logging
const sanitized = sanitizeForLogging(data);
logger.info('Data:', sanitized);Functions for safe error messages:
getSafeErrorMessage(error: Error | unknown): string- Safe error message for usersgetSafeErrorDetails(error: Error | unknown): object- Safe error details for loggingcreateSafeError(error: Error | unknown): Error- Creates a sanitized error object
Usage:
import { getSafeErrorMessage } from './utils/security/error-sanitizer.js';
try {
// ... code that might throw
} catch (error) {
await ctx.reply(`Error: ${getSafeErrorMessage(error)}`);
}The sanitizer automatically detects and redacts:
- Telegram Bot Tokens - Format:
123456789:ABCdefGHIjklMNOpqrsTUVwxyz-123456789 - OAuth Tokens - Bearer tokens, access tokens, refresh tokens
- Base64 Encoded Credentials - In authorization headers
- API Keys - In various formats
- Passwords - In field names containing "password", "secret", "token", etc.
- URLs with Credentials - Removes username:password from URLs
- Environment Variables - Values of sensitive env vars in strings
The sanitizer uses these patterns to detect sensitive data:
- Telegram tokens:
\d{8,}:[A-Za-z0-9_-]{35,} - OAuth tokens:
[A-Za-z0-9_-]{40,} - Bearer tokens:
bearer\s+[A-Za-z0-9_-]{20,} - Base64 tokens:
[A-Za-z0-9+/]{40,}={0,2} - URLs with credentials:
https?://[^:]+:[^@]+@[^\s]+
These field names are automatically sanitized in objects:
token,password,secretapikey,api_keyaccess_token,refresh_tokenclient_secret,client_idauthorization,authcredential,credentialsbot_tokenreddit_client_secret,reddit_passwordredis_password
Sensitive environment variables (values are sanitized if found in strings):
BOT_TOKENREDDIT_CLIENT_SECRETREDDIT_PASSWORDREDIS_PASSWORDREDDIT_CLIENT_IDREDDIT_USERNAME
When adding new logging code, follow this checklist:
- Use
logger.*methods (automatic sanitization) instead ofconsole.* - If using
console.*, manually sanitize withsanitizeForLogging() - Never log raw error objects - use
getSafeErrorMessage()for user-facing errors - Sanitize objects before
JSON.stringify() - Test that logs don't contain actual tokens (use test tokens)
When adding error handling:
- Use
getSafeErrorMessage()for user-facing error messages - Use
getSafeErrorDetails()for logging error details - Never return
error.messagedirectly to users - Never log
error.stackwithout sanitization (logger does this automatically)
- Set a test token:
BOT_TOKEN=123456789:TEST_TOKEN_FOR_SECURITY_TEST - Trigger error conditions
- Check logs - token should appear as
[REDACTED_TELEGRAM_TOKEN] - Check user-facing error messages - should not contain tokens
Run security validation (development only):
import { validateSecurity } from './utils/security/security-validator.js';
const result = await validateSecurity();
if (!result.isValid) {
console.error('Security validation failed:', result.issues);
}❌ BAD:
logger.debug('Config:', config); // May contain tokens✅ GOOD:
logger.debug('Config loaded'); // Logger automatically sanitizes
// Or if you need to log specific non-sensitive fields:
logger.debug('Config:', { port: config.server.port, host: config.server.host });❌ BAD:
catch (error) {
await ctx.reply(`Failed: ${error.message}`);
}✅ GOOD:
catch (error) {
await ctx.reply(`Failed: ${getSafeErrorMessage(error)}`);
}❌ BAD:
logger.debug('Data:', JSON.stringify(data));✅ GOOD:
import { sanitizeForLogging } from './utils/security/sanitizer.js';
logger.debug('Data:', sanitizeForLogging(data));
// Or use logger which does this automatically:
logger.debug('Data:', data); // Automatically sanitizedBefore committing code:
- No hardcoded tokens or credentials
- All
console.*calls sanitize data - All user-facing error messages use
getSafeErrorMessage() - All
JSON.stringify()calls sanitize data first - No tokens in commit messages or PR descriptions
-
.envfile is in.gitignore -
.env.examplecontains only example values
If credentials are accidentally exposed:
- Immediately rotate the exposed credentials
- Review logs to determine scope of exposure
- Check git history if credentials were committed
- Update
.gitignoreif needed - Update documentation with lessons learned
If you're unsure about security practices, ask before committing code that handles credentials.