cursorrules / rules
ivangrynenko/cursorrules/.cursor/rules/javascript-security-logging-monitoring-failures.mdc
Detect and prevent security logging and monitoring failures in JavaScript applications as defined in OWASP Top 10:2021-A09
Cursor rule88 starsChanged 11 months ago
- Reads credentials
- Sends data out
---
description: Detect and prevent security logging and monitoring failures in JavaScript applications as defined in OWASP Top 10:2021-A09
globs: **/*.js, **/*.jsx, **/*.ts, **/*.tsx, !**/node_modules/**, !**/dist/**, !**/build/**, !**/coverage/**
---
# JavaScript Security Logging and Monitoring Failures (OWASP A09:2021)
<rule>
name: javascript_security_logging_monitoring_failures
description: Detect and prevent security logging and monitoring failures in JavaScript applications as defined in OWASP Top 10:2021-A09
actions:
- type: enforce
conditions:
# Pattern 1: Missing Error Logging
- pattern: "(?:try\\s*{[^}]*}\\s*catch\\s*\\([^)]*\\)\\s*{[^}]*})(?![^;{]*(?:console\\.(?:error|warn|log)|logger?\\.(?:error|warn|log)|captureException))"
message: "Error caught without proper logging. Implement structured error logging for security events."
# Pattern 2: Sensitive Data in Logs
- pattern: "console\\.(?:log|warn|error|info|debug)\\s*\\([^)]*(?:password|token|secret|key|credential|auth|jwt|session|cookie)"
negative_pattern: "\\*\\*\\*|redact|mask|sanitize"
message: "Potential sensitive data in logs. Ensure sensitive information is redacted before logging."
# Pattern 3: Missing Authentication Logging
- pattern: "(?:login|signin|authenticate|auth)\\s*\\([^)]*\\)\\s*{[^}]*}"
negative_pattern: "(?:log|audit|record|track)\\s*\\("
message: "Authentication function without logging. Log authentication attempts, successes, and failures."
# Pattern 4: Missing Authorization Logging
- pattern: "(?:authorize|checkPermission|hasAccess|isAuthorized|can)\\s*\\([^)]*\\)\\s*{[^}]*}"
negative_pattern: "(?:log|audit|record|track)\\s*\\("
message: "Authorization check without logging. Log access control decisions, especially denials."
# Pattern 5: Insufficient Error Detail
- pattern: "(?:console\\.error|logger?\\.error)\\s*\\([^)]*(?:error|err|exception)\\s*\\)"
negative_pattern: "(?:error\\.(?:message|stack|code|name)|JSON\\.stringify\\(error\\)|serialize)"
message: "Error logging with insufficient detail. Include error type, message, stack trace, and context."
# Pattern 6: Missing Security Event Logging
- pattern: "(?:bruteForce|rateLimit|block|blacklist|suspicious|anomaly|threat|attack|intrusion|malicious)"
negative_pattern: "(?:log|audit|record|track|monitor|alert|notify)"
message: "Security event detection without logging. Implement logging for all security-relevant events."
# Pattern 7: Inconsistent Log Formats
- pattern: "console\\.(?:log|warn|error|info|debug)\\s*\\("
negative_pattern: "JSON\\.stringify|structured|format"
message: "Inconsistent log format. Use structured logging with consistent formats for easier analysis."
# Pattern 8: Missing Log Correlation ID
- pattern: "(?:api|http|fetch|axios|request)\\s*\\([^)]*\\)"
negative_pattern: "(?:correlationId|requestId|traceId|spanId|context)"
message: "API request without correlation ID. Include correlation IDs in logs for request tracing."
# Pattern 9: Missing High-Value Transaction Logging
- pattern: "(?:payment|transaction|order|purchase|transfer|withdraw|deposit)\\s*\\([^)]*\\)"
negative_pattern: "(?:log|audit|record|track)"
message: "High-value transaction without audit logging. Implement comprehensive logging for all transactions."
# Pattern 10: Client-Side Logging Issues
- pattern: "(?:window\\.onerror|window\\.addEventListener\\s*\\(\\s*['\"]error['\"])"
negative_pattern: "(?:send|report|log|capture|track)"
message: "Client-side error handler without reporting. Implement error reporting to backend services."
# Pattern 11: Missing Log Levels
- pattern: "console\\.log\\s*\\("
negative_pattern: "logger?\\.(?:error|warn|info|debug|trace)"
message: "Using console.log without proper log levels. Implement a logging library with appropriate log levels."
# Pattern 12: Missing Monitoring Integration
- pattern: "package\\.json"
negative_pattern: "(?:sentry|newrelic|datadog|appinsights|loggly|splunk|elasticsearch|winston|bunyan|pino|loglevel)"
file_pattern: "package\\.json$"
message: "No logging or monitoring dependencies detected. Consider adding a proper logging library and monitoring integration."
# Pattern 13: Missing Log Aggregation
- pattern: "(?:docker-compose\\.ya?ml|\\.env|\\.env\\.example|Dockerfile)"
negative_pattern: "(?:sentry|newrelic|datadog|appinsights|loggly|splunk|elasticsearch|logstash|fluentd|kibana)"
file_pattern: "(?:docker-compose\\.ya?ml|\\.env|\\.env\\.example|Dockerfile)$"
message: "No log aggregation service configured. Implement centralized log collection and analysis."
# Pattern 14: Missing Health Checks
- pattern: "(?:express|koa|fastify|hapi|http\\.createServer)"
negative_pattern: "(?:health|status|heartbeat|alive|ready)"
message: "Server without health check endpoint. Implement health checks for monitoring service status."
# Pattern 15: Missing Rate Limiting Logs
- pattern: "(?:rateLimit|throttle|limiter)"
negative_pattern: "(?:log|record|track|monitor|alert|notify)"
message: "Rate limiting without logging. Log rate limit events to detect potential attacks."
- type: suggest
message: |
**JavaScript Security Logging and Monitoring Best Practices:**
1. **Structured Error Logging:**
- Use structured logging formats (JSON)
- Include contextual information with errors
- Example:
```javascript
try {
// Operation that might fail
processUserData(userData);
} catch (error) {
logger.error({
message: 'Failed to process user data',
error: {
name: error.name,
message: error.message,
stack: error.stack
},
userId: userData.id,
context: 'user-processing',
timestamp: new Date().toISOString()
});
// Handle the error appropriately
}
```
2. **Sensitive Data Redaction:**
- Redact sensitive information before logging
- Use dedicated functions for sanitization
- Example:
```javascript
function redactSensitiveData(obj) {
const sensitiveFields = ['password', 'token', 'secret', 'creditCard', 'ssn'];
const redacted = { ...obj };
for (const field of sensitiveFields) {
if (field in redacted) {
redacted[field] = '***REDACTED***';
}
}
return redacted;
}
// Usage
logger.info({
message: 'User login attempt',
user: redactSensitiveData(userData),
timestamp: new Date().toISOString()
});
```
3. **Authentication Logging:**
- Log all authentication events
- Include success/failure status
- Example:
```javascript
async function authenticateUser(username, password) {
try {
const user = await User.findOne({ username });
if (!user) {
logger.warn({
message: 'Authentication failed: user not found',
username,
ipAddress: req.ip,
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString()
});
return { success: false, reason: 'invalid_credentials' };
}
const isValid = await bcrypt.compare(password, user.passwordHash);
if (!isValid) {
logger.warn({
message: 'Authentication failed: invalid password',
username,
userId: user.id,
ipAddress: req.ip,
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString()
});
return { success: false, reason: 'invalid_credentials' };
}
logger.info({
message: 'User authenticated successfully',
username,
userId: user.id,
ipAddress: req.ip,
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString()
});
return { success: true, user };
} catch (error) {
logger.error({
message: 'Authentication error',
username,
error: {
name: error.name,
message: error.message,
stack: error.stack
},
timestamp: new Date().toISOString()
});
return { success: false, reason: 'system_error' };
}
}
```
4. **Authorization Logging:**
- Log access control decisions
- Include user, resource, and action
- Example:
```javascript
function checkPermission(user, resource, action) {
const hasPermission = user.permissions.some(p =>
p.resource === resource && p.actions.includes(action)
);
logger.info({
message: `Authorization ${hasPermission ? 'granted' : 'denied'}`,
userId: user.id,
username: user.username,
resource,
action,
decision: hasPermission ? 'allow' : 'deny',
timestamp: new Date().toISOString()
});
return hasPermission;
}
```
5. **Comprehensive Error Logging:**
- Include detailed error information
- Add context for troubleshooting
- Example:
```javascript
// Using a logging library like Winston
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
defaultMeta: { service: 'user-service' },
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// Usage
try {
// Operation that might fail
} catch (error) {
logger.error({
message: 'Operation failed',
operationName: 'processData',
error: {
name: error.name,
message: error.message,
code: error.code,
stack: error.stack
},
context: {
userId: req.user?.id,
requestId: req.id,
path: req.path,
method: req.method
}
});
}
```
6. **Security Event Logging:**
- Log all security-relevant events
- Include detailed context
- Example:
```javascript
function detectBruteForce(username, ipAddress) {
const attempts = getLoginAttempts(username, ipAddress);
if (attempts > MAX_ATTEMPTS) {
logger.warn({
message: 'Possible brute force attack detected',
username,
ipAddress,
attempts,
threshold: MAX_ATTEMPTS,
action: 'account_temporarily_locked',
timestamp: new Date().toISOString()
});
// Implement account lockout or IP blocking
lockAccount(username, LOCKOUT_DURATION);
return true;
}
return false;
}
```
7. **Structured Logging Format:**
- Use JSON for machine-readable logs
- Maintain consistent field names
- Example:
```javascript
// Using a structured logging library like Pino
const pino = require('pino');
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
base: { pid: process.pid, hostname: os.hostname() },
timestamp: pino.stdTimeFunctions.isoTime,
formatters: {
level: (label) => {
return { level: label };
}
}
});
// Usage
logger.info({
msg: 'User profile updated',
userId: user.id,
changes: ['email', 'preferences'],
source: 'api'
});
```
8. **Request Correlation:**
- Use correlation IDs across services
- Track request flow through the system
- Example:
```javascript
// Express middleware for adding correlation IDs
const { v4: uuidv4 } = require('uuid');
function correlationMiddleware(req, res, next) {
// Use existing correlation ID from headers or generate a new one
const correlationId = req.headers['x-correlation-id'] || uuidv4();
req.correlationId = correlationId;
// Add to response headers
res.setHeader('x-correlation-id', correlationId);
// Add to logger context for this request
req.logger = logger.child({ correlationId });
next();
}
// Usage in route handlers
app.get('/api/users/:id', (req, res) => {
req.logger.info({
msg: 'User profile requested',
userId: req.params.id,
path: req.path,
method: req.method
});
// Process request...
});
```
9. **Transaction Logging:**
- Log all high-value transactions
- Include before/after states
- Example:
```javascript
async function processPayment(userId, amount, paymentMethod) {
logger.info({
message: 'Payment processing started',
userId,
amount,
paymentMethod: {
type: paymentMethod.type,
lastFour: paymentMethod.lastFour
},
transactionId: generateTransactionId(),
timestamp: new Date().toISOString()
});
try {
const result = await paymentGateway.charge({
amount,
source: paymentMethod.token
});
logger.info({
message: 'Payment processed successfully',
userId,
amount,
transactionId: result.transactionId,
gatewayReference: result.reference,
status: 'success',
timestamp: new Date().toISOString()
});
return { success: true, transactionId: result.transactionId };
} catch (error) {
logger.error({
message: 'Payment processing failed',
userId,
amount,
error: {
name: error.name,
message: error.message,
code: error.code
},
status: 'failed',
timestamp: new Date().toISOString()
});
return { success: false, error: error.message };
}
}
```
10. **Client-Side Error Reporting:**
- Send client errors to the backend
- Include browser and user context
- Example:
```javascript
// Client-side error tracking
window.addEventListener('error', function(event) {
const errorDetails = {
message: event.message,
source: event.filename,
lineno: event.lineno,
colno: event.colno,
error: {
stack: event.error?.stack
},
url: window.location.href,
userAgent: navigator.userAgent,
timestamp: new Date().toISOString(),
// Add user context if available
userId: window.currentUser?.id
};
// Send to backend logging endpoint
fetch('/api/log/client-error', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(errorDetails),
// Use keepalive to ensure the request completes even if the page is unloading
keepalive: true
}).catch(err => {
// Fallback if the logging endpoint fails
console.error('Failed to send error report:', err);
});
});
```
11. **Proper Log Levels:**
- Use appropriate log levels
- Configure based on environment
- Example:
```javascript
// Using Winston with proper log levels
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
levels: winston.config.npm.levels,
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
})
]
});
// Usage with appropriate levels
logger.error('Critical application error'); // Always logged
logger.warn('Potential issue detected'); // Warning conditions
logger.info('Normal operational message'); // Normal but significant
logger.http('HTTP request received'); // HTTP request logging
logger.verbose('Detailed information'); // Detailed debug information
logger.debug('Debugging information'); // For developers
logger.silly('Extremely detailed tracing'); // Most granular
```
12. **Monitoring Integration:**
- Integrate with monitoring services
- Set up alerts for critical issues
- Example:
```javascript
// Using Sentry for error monitoring
const Sentry = require('@sentry/node');
const Tracing = require('@sentry/tracing');
const express = require('express');
const app = express();
Sentry.init({
dsn: process.env.SENTRY_DSN,
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
new Tracing.Integrations.Express({ app })
],
tracesSampleRate: 1.0
});
// Use Sentry middleware
app.use(Sentry.Handlers.requestHandler());
app.use(Sentry.Handlers.tracingHandler());
// Your routes here
// Error handler
app.use(Sentry.Handlers.errorHandler());
app.use((err, req, res, next) => {
// Custom error handling
logger.error({
message: 'Express error',
error: {
name: err.name,
message: err.message,
stack: err.stack
},
request: {
path: req.path,
method: req.method,
correlationId: req.correlationId
}
});
res.status(500).json({ error: 'Internal server error' });
});
```
13. **Log Aggregation:**
- Set up centralized log collection
- Configure log shipping
- Example:
```javascript
// Using Winston with Elasticsearch transport
const winston = require('winston');
const { ElasticsearchTransport } = require('winston-elasticsearch');
const esTransportOpts = {
level: 'info',
clientOpts: {
node: process.env.ELASTICSEARCH_URL,
auth: {
username: process.env.ELASTICSEARCH_USERNAME,
password: process.env.ELASTICSEARCH_PASSWORD
}
},
indexPrefix: 'app-logs'
};
const logger = winston.createLogger({
transports: [
new winston.transports.Console(),
new ElasticsearchTransport(esTransportOpts)
]
});
```
```yaml
# docker-compose.yml example with ELK stack
version: '3'
services:
app:
build: .
environment:
- NODE_ENV=production
- ELASTICSEARCH_URL=http://elasticsearch:9200
depends_on:
- elasticsearch
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.14.0
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m
volumes:
- es_data:/usr/share/elasticsearch/data
kibana:
image: docker.elastic.co/kibana/kibana:7.14.0
ports:
- "5601:5601"
depends_on:
- elasticsearch
logstash:
image: docker.elastic.co/logstash/logstash:7.14.0
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline
depends_on:
- elasticsearch
volumes:
es_data:
```
14. **Health Checks and Monitoring:**
- Implement health check endpoints
- Monitor application status
- Example:
```javascript
const express = require('express');
const app = express();
// Basic health check endpoint
app.get('/health', (req, res) => {
const status = {
status: 'UP',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
memoryUsage: process.memoryUsage(),
version: process.env.npm_package_version
};
// Add database health check
try {
// Check database connection
status.database = { status: 'UP' };
} catch (error) {
status.database = { status: 'DOWN', error: error.message };
status.status = 'DEGRADED';
}
// Add external service health checks
// ...
// Log health check results
logger.debug({
message: 'Health check performed',
result: status
});
const statusCode = status.status === 'UP' ? 200 :
status.status === 'DEGRADED' ? 200 : 503;
res.status(statusCode).json(status);
});
// Detailed readiness probe
app.get('/ready', async (req, res) => {
const checks = [];
let isReady = true;
// Check database
try {
await db.ping();
checks.push({ component: 'database', status: 'ready' });
} catch (error) {
isReady = false;
checks.push({
component: 'database',
status: 'not ready',
error: error.message
});
}
// Check cache
try {
await cache.ping();
checks.push({ component: 'cache', status: 'ready' });
} catch (error) {
isReady = false;
checks.push({
component: 'cache',
status: 'not ready',
error: error.message
});
}
// Log readiness check
logger.debug({
message: 'Readiness check performed',
isReady,
checks
});
res.status(isReady ? 200 : 503).json({
status: isReady ? 'ready' : 'not ready',
checks,
timestamp: new Date().toISOString()
});
});
```
15. **Rate Limiting with Logging:**
- Log rate limit events
- Track potential abuse
- Example:
```javascript
const rateLimit = require('express-rate-limit');
// Create rate limiter with logging
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
standardHeaders: true,
legacyHeaders: false,
handler: (req, res, next, options) => {
// Log rate limit exceeded
logger.warn({
message: 'Rate limit exceeded',
ip: req.ip,
path: req.path,
method: req.method,
userAgent: req.headers['user-agent'],
currentLimit: options.max,
windowMs: options.windowMs,
correlationId: req.correlationId,
userId: req.user?.id,
timestamp: new Date().toISOString()
});
res.status(options.statusCode).json({
status: 'error',
message: options.message
});
},
// Called on all requests to track usage
onLimitReached: (req, res, options) => {
// This is called when a client hits the rate limit
logger.warn({
message: 'Client reached rate limit',
ip: req.ip,
path: req.path,
method: req.method,
userAgent: req.headers['user-agent'],
correlationId: req.correlationId,
userId: req.user?.id,
timestamp: new Date().toISOString()
});
// Consider additional actions like temporary IP ban
// or sending alerts for potential attacks
}
});
// Apply to all API routes
app.use('/api/', apiLimiter);
```
- type: validate
conditions:
# Check 1: Structured Logging
- pattern: "(?:winston|pino|bunyan|loglevel|morgan|log4js)"
message: "Using a structured logging library."
# Check 2: Error Logging
- pattern: "try\\s*{[^}]*}\\s*catch\\s*\\([^)]*\\)\\s*{[^}]*(?:logger?\\.error|captureException)\\s*\\([^)]*\\)"
message: "Implementing proper error logging in catch blocks."
# Check 3: Sensitive Data Handling
- pattern: "(?:redact|mask|sanitize|filter)\\s*\\([^)]*(?:password|token|secret|key|credential)"
message: "Implementing sensitive data redaction in logs."
# Check 4: Correlation IDs
- pattern: "(?:correlationId|requestId|traceId)"
message: "Using correlation IDs for request tracing."
# Check 5: Monitoring Integration
- pattern: "(?:sentry|newrelic|datadog|appinsights|loggly|splunk|elasticsearch)"
message: "Integrating with monitoring or log aggregation services."
metadata:
priority: high
version: 1.0
tags:
- security
- javascript
- nodejs
- browser
- logging
- monitoring
- owasp
- language:javascript
- framework:express
- framework:react
- framework:vue
- framework:angular
- category:security
- subcategory:logging
- standard:owasp-top10
- risk:a09-security-logging-monitoring-failures
references:
- "https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/"
- "https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html"
- "https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/10-Business_Logic_Testing/08-Test_for_Process_Timing"
- "https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Logging_Vocabulary_Cheat_Sheet.md"
- "https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#security-logging-monitoring"
- "https://cheatsheetseries.owasp.org/cheatsheets/Application_Logging_Vocabulary_Cheat_Sheet.html"
- "https://cheatsheetseries.owasp.org/cheatsheets/Transaction_Authorization_Cheat_Sheet.html#monitor-activity"
</rule>
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
Posts are public.Sign in to post
No one has posted yet. Be the first.

