secure-coding-rules 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,125 @@
1
+ # Injection Security Rules
2
+
3
+ > OWASP Top 10 2025 - A05: Injection
4
+
5
+ ## Rules
6
+
7
+ ### 1. Use Parameterized Queries for All Database Operations
8
+ - **DO**: Use parameterized queries, prepared statements, or ORM query builders for all SQL and NoSQL operations.
9
+ - **DON'T**: Concatenate or interpolate user input into query strings.
10
+ - **WHY**: SQL/NoSQL injection allows attackers to read, modify, or delete entire databases and potentially execute system commands.
11
+
12
+ ### 2. Sanitize and Escape All Output
13
+ - **DO**: Context-encode output before rendering in HTML, JavaScript, CSS, or URL contexts. Use framework auto-escaping.
14
+ - **DON'T**: Insert raw user input into HTML templates or DOM elements using `innerHTML` or `dangerouslySetInnerHTML`.
15
+ - **WHY**: Cross-Site Scripting (XSS) enables session hijacking, credential theft, and defacement.
16
+
17
+ ### 3. Validate and Sanitize User Input
18
+ - **DO**: Validate input against strict schemas (type, length, format, allowed characters). Use allowlists over denylists.
19
+ - **DON'T**: Accept any input and try to filter out known-bad patterns.
20
+ - **WHY**: Denylist-based filtering is always incomplete. Allowlisting ensures only expected input is processed.
21
+
22
+ ### 4. Prevent Command Injection
23
+ - **DO**: Avoid shell commands. If necessary, use `execFile` with an argument array instead of `exec` with string interpolation.
24
+ - **DON'T**: Pass user input to `child_process.exec()`, `eval()`, `Function()`, or template literals in shell commands.
25
+ - **WHY**: Command injection gives attackers full control over the server operating system.
26
+
27
+ ### 5. Guard Against Template Injection
28
+ - **DO**: Use logic-less templates or sandbox template rendering. Never pass user input as template source.
29
+ - **DON'T**: Allow users to control template strings that are compiled or rendered server-side.
30
+ - **WHY**: Server-Side Template Injection (SSTI) can lead to Remote Code Execution (RCE).
31
+
32
+ ### 6. Prevent Path Traversal
33
+ - **DO**: Resolve file paths and verify they fall within the intended directory using `path.resolve()` and prefix checking.
34
+ - **DON'T**: Use user input directly in file system operations without path validation.
35
+ - **WHY**: Path traversal allows reading arbitrary files like `/etc/passwd` or application secrets.
36
+
37
+ ### 7. Sanitize Regular Expressions
38
+ - **DO**: Escape user input used in regular expressions. Set timeouts or use RE2 for untrusted patterns.
39
+ - **DON'T**: Pass user input directly to `new RegExp()` without escaping.
40
+ - **WHY**: ReDoS (Regular Expression Denial of Service) can hang the event loop with crafted input.
41
+
42
+ ## Code Examples
43
+
44
+ ### Bad Practice
45
+ ```javascript
46
+ // SQL Injection
47
+ const query = `SELECT * FROM users WHERE id = '${req.params.id}'`;
48
+ await db.query(query);
49
+
50
+ // NoSQL Injection
51
+ const user = await User.findOne({ username: req.body.username, password: req.body.password });
52
+
53
+ // Command Injection
54
+ const { exec } = require("child_process");
55
+ exec(`convert ${req.query.filename} output.png`);
56
+
57
+ // Path Traversal
58
+ const filePath = `./uploads/${req.params.filename}`;
59
+ res.sendFile(filePath);
60
+
61
+ // XSS via innerHTML
62
+ element.innerHTML = userInput;
63
+
64
+ // eval with user input
65
+ const result = eval(req.body.expression);
66
+ ```
67
+
68
+ ### Good Practice
69
+ ```javascript
70
+ import { execFile } from "node:child_process";
71
+ import path from "node:path";
72
+
73
+ // Parameterized SQL query
74
+ const [rows] = await db.execute(
75
+ "SELECT * FROM users WHERE id = ?",
76
+ [req.params.id]
77
+ );
78
+
79
+ // Safe NoSQL query - validate types explicitly
80
+ const username = String(req.body.username);
81
+ const user = await User.findOne({ username });
82
+ const isValid = await verifyPassword(req.body.password, user.passwordHash);
83
+
84
+ // Safe command execution with execFile
85
+ execFile("convert", [validatedFilename, "output.png"], (error, stdout) => {
86
+ if (error) handleError(error);
87
+ });
88
+
89
+ // Path traversal prevention
90
+ function getSafeFilePath(userInput, baseDir) {
91
+ const resolved = path.resolve(baseDir, userInput);
92
+ if (!resolved.startsWith(path.resolve(baseDir) + path.sep)) {
93
+ throw new Error("Path traversal detected");
94
+ }
95
+ return resolved;
96
+ }
97
+
98
+ // Safe DOM manipulation
99
+ element.textContent = userInput; // Auto-escaped, no HTML parsing
100
+
101
+ // Input validation with schema
102
+ import { z } from "zod";
103
+ const UserInput = z.object({
104
+ email: z.string().email().max(254),
105
+ name: z.string().min(1).max(100).regex(/^[a-zA-Z\s'-]+$/),
106
+ age: z.number().int().min(0).max(150),
107
+ });
108
+ const validated = UserInput.parse(req.body);
109
+
110
+ // Safe regex from user input
111
+ function escapeRegex(str) {
112
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
113
+ }
114
+ const safePattern = new RegExp(escapeRegex(userInput), "i");
115
+ ```
116
+
117
+ ## Quick Checklist
118
+ - [ ] All database queries use parameterized statements or ORM query builders
119
+ - [ ] User input is never concatenated into SQL, NoSQL, LDAP, or OS commands
120
+ - [ ] Output is context-encoded for the rendering context (HTML, JS, URL, CSS)
121
+ - [ ] `eval()`, `Function()`, and `setTimeout(string)` are never used with user input
122
+ - [ ] File paths from user input are resolved and validated against a base directory
123
+ - [ ] Shell commands use `execFile` with argument arrays, not `exec` with string interpolation
124
+ - [ ] Input is validated with strict schemas (type, length, format, allowlist)
125
+ - [ ] Regular expressions from user input are escaped or use safe regex engines
@@ -0,0 +1,156 @@
1
+ # Security Logging and Alerting Rules
2
+
3
+ > OWASP Top 10 2025 - A09: Security Logging and Alerting Failures
4
+
5
+ ## Rules
6
+
7
+ ### 1. Log All Security-Relevant Events
8
+ - **DO**: Log authentication attempts (success and failure), access control failures, input validation failures, and administrative actions.
9
+ - **DON'T**: Rely on generic application logs to detect security incidents.
10
+ - **WHY**: Without security event logging, breaches go undetected. Mean time to detect a breach averages 200+ days without proper logging.
11
+
12
+ ### 2. Never Log Sensitive Data
13
+ - **DO**: Sanitize logs to exclude passwords, tokens, credit card numbers, PII, and session IDs. Use structured logging with redaction.
14
+ - **DON'T**: Log request bodies, full headers, or raw user input without sanitization.
15
+ - **WHY**: Logs are often stored with weaker access controls than primary data stores. Leaked logs expose credentials and PII.
16
+
17
+ ### 3. Use Structured, Consistent Log Formats
18
+ - **DO**: Use JSON-structured logging with consistent fields (timestamp, level, event type, user ID, IP, request ID).
19
+ - **DON'T**: Use unstructured `console.log()` for security events or mix log formats.
20
+ - **WHY**: Structured logs enable automated parsing, correlation, and alerting by SIEM tools.
21
+
22
+ ### 4. Implement Tamper-Evident Logging
23
+ - **DO**: Send logs to a centralized, append-only logging service. Implement log integrity checks.
24
+ - **DON'T**: Store security logs only on the application server where an attacker could modify them.
25
+ - **WHY**: Attackers routinely delete or modify local logs to cover their tracks.
26
+
27
+ ### 5. Set Up Real-Time Alerting for Critical Events
28
+ - **DO**: Configure alerts for brute-force attempts, privilege escalation, unusual data access patterns, and authentication anomalies.
29
+ - **DON'T**: Only review logs manually or after an incident is reported by users.
30
+ - **WHY**: Real-time alerting reduces breach detection time from months to minutes, limiting damage.
31
+
32
+ ### 6. Protect Log Injection
33
+ - **DO**: Sanitize log inputs to prevent log injection (newlines, control characters). Use parameterized logging.
34
+ - **DON'T**: Directly interpolate user input into log messages.
35
+ - **WHY**: Log injection can forge log entries, corrupt log analysis, or exploit log viewer vulnerabilities.
36
+
37
+ ### 7. Ensure Adequate Log Retention
38
+ - **DO**: Retain security logs for at least 90 days (hot) and 1 year (cold) per compliance requirements.
39
+ - **DON'T**: Delete logs immediately after processing or keep them indefinitely without retention policies.
40
+ - **WHY**: Incident investigation often requires historical log analysis. Retention policies balance security with storage costs and privacy regulations.
41
+
42
+ ## Code Examples
43
+
44
+ ### Bad Practice
45
+ ```javascript
46
+ // Logging sensitive data
47
+ console.log(`User login: ${email}, password: ${password}`);
48
+ console.log(`Payment processed: card=${cardNumber}, amount=${amount}`);
49
+
50
+ // Log injection vulnerability
51
+ const username = req.body.username; // Could contain "\nAdmin login successful"
52
+ console.log(`Login attempt for user: ${username}`);
53
+
54
+ // No security event logging
55
+ app.post("/api/login", async (req, res) => {
56
+ const user = await authenticate(req.body);
57
+ if (user) res.json({ token: createToken(user) });
58
+ else res.status(401).json({ error: "Invalid" });
59
+ // No logging of success or failure
60
+ });
61
+ ```
62
+
63
+ ### Good Practice
64
+ ```javascript
65
+ import pino from "pino";
66
+
67
+ // Structured logger with redaction
68
+ const logger = pino({
69
+ level: "info",
70
+ redact: {
71
+ paths: ["password", "token", "authorization", "cookie", "*.password", "*.token"],
72
+ censor: "[REDACTED]",
73
+ },
74
+ serializers: {
75
+ req: pino.stdSerializers.req,
76
+ err: pino.stdSerializers.err,
77
+ },
78
+ });
79
+
80
+ // Security event logger
81
+ function logSecurityEvent(event) {
82
+ logger.info({
83
+ type: "security",
84
+ event: event.action,
85
+ userId: event.userId ?? "anonymous",
86
+ ip: event.ip,
87
+ userAgent: event.userAgent,
88
+ resource: event.resource,
89
+ outcome: event.outcome,
90
+ timestamp: new Date().toISOString(),
91
+ requestId: event.requestId,
92
+ });
93
+ }
94
+
95
+ // Login with comprehensive security logging
96
+ app.post("/api/login", loginLimiter, async (req, res) => {
97
+ const { email, password } = req.body;
98
+ const requestId = crypto.randomUUID();
99
+ const user = await db.findUser(email);
100
+ const isValid = user ? await verifyPassword(password, user.passwordHash) : false;
101
+
102
+ logSecurityEvent({
103
+ action: isValid ? "login_success" : "login_failure",
104
+ userId: user?.id,
105
+ ip: req.ip,
106
+ userAgent: req.get("user-agent"),
107
+ resource: "/api/login",
108
+ outcome: isValid ? "success" : "failure",
109
+ requestId,
110
+ });
111
+
112
+ if (!isValid) {
113
+ return res.status(401).json({ error: "Invalid credentials", requestId });
114
+ }
115
+
116
+ setSessionCookie(res, user);
117
+ res.json({ success: true });
118
+ });
119
+
120
+ // Safe log message - prevent log injection
121
+ function sanitizeForLog(input) {
122
+ if (typeof input !== "string") return String(input);
123
+ return input.replace(/[\n\r\t]/g, "").substring(0, 500);
124
+ }
125
+
126
+ // Access control failure logging middleware
127
+ function logAccessDenied(req, res, next) {
128
+ const originalJson = res.json.bind(res);
129
+ res.json = (body) => {
130
+ if (res.statusCode === 403) {
131
+ logSecurityEvent({
132
+ action: "access_denied",
133
+ userId: req.user?.id,
134
+ ip: req.ip,
135
+ userAgent: req.get("user-agent"),
136
+ resource: `${req.method} ${req.originalUrl}`,
137
+ outcome: "denied",
138
+ requestId: req.id,
139
+ });
140
+ }
141
+ return originalJson(body);
142
+ };
143
+ next();
144
+ }
145
+ ```
146
+
147
+ ## Quick Checklist
148
+ - [ ] Authentication successes and failures are logged
149
+ - [ ] Access control violations are logged
150
+ - [ ] No passwords, tokens, or PII in log output
151
+ - [ ] Structured JSON logging format with consistent fields
152
+ - [ ] Logs sent to centralized, tamper-resistant storage
153
+ - [ ] Real-time alerts configured for critical security events
154
+ - [ ] Log inputs sanitized to prevent log injection
155
+ - [ ] Log retention policy defined and enforced (90 days+)
156
+ - [ ] Each log entry includes timestamp, user ID, IP, and request ID
@@ -0,0 +1,117 @@
1
+ # Insecure Design Security Rules
2
+
3
+ > OWASP Top 10 2025 - A06: Insecure Design
4
+
5
+ ## Rules
6
+
7
+ ### 1. Apply Threat Modeling During Design
8
+ - **DO**: Identify trust boundaries, data flows, and threat actors before writing code. Use STRIDE or similar frameworks.
9
+ - **DON'T**: Bolt security onto the application after implementation.
10
+ - **WHY**: Security flaws rooted in design cannot be fixed by better implementation alone. Architecture-level vulnerabilities require redesign.
11
+
12
+ ### 2. Enforce Business Logic Limits
13
+ - **DO**: Implement server-side limits for business operations (e.g., max transaction amount, order quantity, API calls per user).
14
+ - **DON'T**: Rely on the UI to enforce business rules like purchase limits or booking constraints.
15
+ - **WHY**: Business logic abuse (e.g., buying items at negative prices, mass-redeeming coupons) bypasses client-side validation easily.
16
+
17
+ ### 3. Implement Defense in Depth
18
+ - **DO**: Layer multiple security controls so that failure of one does not compromise the system. Combine input validation, access control, encryption, and monitoring.
19
+ - **DON'T**: Depend on a single security mechanism to protect critical assets.
20
+ - **WHY**: No single control is perfect. Layered defenses ensure that an attacker must defeat multiple barriers.
21
+
22
+ ### 4. Follow the Principle of Least Privilege
23
+ - **DO**: Grant minimum necessary permissions to users, services, and processes. Use separate service accounts with scoped credentials.
24
+ - **DON'T**: Run services as root, use admin-level database credentials for application queries, or grant blanket permissions.
25
+ - **WHY**: Over-privileged components amplify the impact of any breach, turning a small vulnerability into full system compromise.
26
+
27
+ ### 5. Design for Abuse Cases
28
+ - **DO**: For every feature, ask "how could this be abused?" and implement safeguards (rate limiting, CAPTCHA, fraud detection).
29
+ - **DON'T**: Only consider the happy path in feature design. Assume all input can be malicious.
30
+ - **WHY**: Attackers use features in unintended ways. Referral systems get exploited, file uploads deliver malware, search becomes a data exfiltration tool.
31
+
32
+ ### 6. Separate Sensitive Operations
33
+ - **DO**: Require re-authentication or multi-factor confirmation for critical operations (password change, fund transfer, account deletion).
34
+ - **DON'T**: Allow destructive or sensitive operations with a single click or without additional verification.
35
+ - **WHY**: Session hijacking or CSRF can trigger sensitive operations. Step-up authentication adds a security boundary.
36
+
37
+ ## Code Examples
38
+
39
+ ### Bad Practice
40
+ ```javascript
41
+ // No business logic validation on the server
42
+ app.post("/api/transfer", authenticate, async (req, res) => {
43
+ const { amount, toAccount } = req.body;
44
+ // Trusting client-sent amount without validation
45
+ await transferFunds(req.user.id, toAccount, amount);
46
+ res.json({ success: true });
47
+ });
48
+
49
+ // Feature without abuse consideration
50
+ app.post("/api/referral", authenticate, async (req, res) => {
51
+ // No limit on referral bonuses - can be exploited with fake accounts
52
+ await addReferralBonus(req.user.id, req.body.referralCode);
53
+ res.json({ success: true });
54
+ });
55
+ ```
56
+
57
+ ### Good Practice
58
+ ```javascript
59
+ import { z } from "zod";
60
+ import { rateLimit } from "express-rate-limit";
61
+
62
+ // Business logic with server-side validation and limits
63
+ const TransferSchema = z.object({
64
+ amount: z.number().positive().max(10000), // Business limit
65
+ toAccount: z.string().regex(/^\d{10,12}$/),
66
+ });
67
+
68
+ app.post("/api/transfer", authenticate, async (req, res) => {
69
+ const { amount, toAccount } = TransferSchema.parse(req.body);
70
+
71
+ // Check daily transfer limit
72
+ const dailyTotal = await getDailyTransferTotal(req.user.id);
73
+ if (dailyTotal + amount > req.user.dailyLimit) {
74
+ return res.status(400).json({ error: "Daily transfer limit exceeded" });
75
+ }
76
+
77
+ // Require step-up authentication for large transfers
78
+ if (amount > 1000) {
79
+ const mfaVerified = await verifyMFA(req.user.id, req.body.mfaToken);
80
+ if (!mfaVerified) {
81
+ return res.status(403).json({ error: "MFA required for large transfers" });
82
+ }
83
+ }
84
+
85
+ await transferFunds(req.user.id, toAccount, amount);
86
+ await logAuditEvent("transfer", { userId: req.user.id, amount, toAccount });
87
+ res.json({ success: true });
88
+ });
89
+
90
+ // Referral system with abuse prevention
91
+ const referralLimiter = rateLimit({ windowMs: 24 * 60 * 60 * 1000, max: 5 });
92
+
93
+ app.post("/api/referral", authenticate, referralLimiter, async (req, res) => {
94
+ const referrer = await getUserByReferralCode(req.body.referralCode);
95
+
96
+ // Abuse checks
97
+ if (referrer.id === req.user.id) {
98
+ return res.status(400).json({ error: "Cannot refer yourself" });
99
+ }
100
+ const existingReferral = await getReferral(req.user.id);
101
+ if (existingReferral) {
102
+ return res.status(400).json({ error: "Already used a referral" });
103
+ }
104
+
105
+ await addReferralBonus(referrer.id, req.user.id);
106
+ res.json({ success: true });
107
+ });
108
+ ```
109
+
110
+ ## Quick Checklist
111
+ - [ ] Threat modeling performed for critical features
112
+ - [ ] Business logic limits enforced server-side (not just client-side)
113
+ - [ ] Multiple layers of security controls (defense in depth)
114
+ - [ ] Services run with minimum required privileges
115
+ - [ ] Abuse cases identified and mitigated for each feature
116
+ - [ ] Sensitive operations require step-up authentication or MFA
117
+ - [ ] Data flows and trust boundaries documented
@@ -0,0 +1,118 @@
1
+ # Security Configuration Rules
2
+
3
+ > OWASP Top 10 2025 - A02: Security Misconfiguration
4
+
5
+ ## Rules
6
+
7
+ ### 1. Disable Verbose Error Messages in Production
8
+ - **DO**: Return generic error responses to clients. Log detailed errors server-side only.
9
+ - **DON'T**: Expose stack traces, database errors, or internal paths in API responses.
10
+ - **WHY**: Verbose errors leak implementation details that attackers use for targeted exploitation.
11
+
12
+ ### 2. Remove Default Credentials and Configurations
13
+ - **DO**: Change all default passwords, API keys, and configuration values before deployment.
14
+ - **DON'T**: Ship applications with default admin accounts, sample configurations, or debug settings enabled.
15
+ - **WHY**: Default credentials are the first thing attackers try and are widely documented.
16
+
17
+ ### 3. Set Secure HTTP Headers
18
+ - **DO**: Configure security headers: `Strict-Transport-Security`, `X-Content-Type-Options`, `X-Frame-Options`, `Content-Security-Policy`, and `Permissions-Policy`.
19
+ - **DON'T**: Rely on framework defaults without verifying which security headers are actually set.
20
+ - **WHY**: Security headers provide defense-in-depth against XSS, clickjacking, MIME sniffing, and protocol downgrade attacks.
21
+
22
+ ### 4. Disable Unnecessary Features and Services
23
+ - **DO**: Remove or disable unused routes, middleware, debug endpoints, and server features (e.g., directory listing, `X-Powered-By`).
24
+ - **DON'T**: Leave development tools, test endpoints, or administrative panels accessible in production.
25
+ - **WHY**: Every unnecessary feature expands the attack surface.
26
+
27
+ ### 5. Enforce HTTPS Everywhere
28
+ - **DO**: Redirect all HTTP traffic to HTTPS. Use HSTS with a long `max-age` and `includeSubDomains`.
29
+ - **DON'T**: Allow mixed content or serve any resources over plain HTTP.
30
+ - **WHY**: Unencrypted traffic is vulnerable to interception, modification, and man-in-the-middle attacks.
31
+
32
+ ### 6. Configure CORS Restrictively
33
+ - **DO**: Set `Access-Control-Allow-Origin` to specific trusted domains. Validate the `Origin` header server-side.
34
+ - **DON'T**: Use `Access-Control-Allow-Origin: *` with credentials or reflect any origin without validation.
35
+ - **WHY**: Overly permissive CORS allows malicious sites to make authenticated requests on behalf of users.
36
+
37
+ ### 7. Harden Environment Configuration
38
+ - **DO**: Use environment variables or secret managers for sensitive configuration. Validate all config values at startup.
39
+ - **DON'T**: Commit secrets to version control or store them in plain-text config files.
40
+ - **WHY**: Leaked secrets in repositories are a leading cause of breaches.
41
+
42
+ ## Code Examples
43
+
44
+ ### Bad Practice
45
+ ```javascript
46
+ // Leaking internal details in error responses
47
+ app.use((err, req, res, next) => {
48
+ res.status(500).json({
49
+ error: err.message,
50
+ stack: err.stack, // Exposes internals
51
+ query: err.sql, // Exposes database queries
52
+ });
53
+ });
54
+
55
+ // Permissive CORS
56
+ app.use(cors({ origin: "*", credentials: true })); // Dangerous combination
57
+
58
+ // Hardcoded secrets
59
+ const JWT_SECRET = "super-secret-key-123";
60
+ const DB_PASSWORD = "admin123";
61
+ ```
62
+
63
+ ### Good Practice
64
+ ```javascript
65
+ import helmet from "helmet";
66
+ import cors from "cors";
67
+
68
+ // Security headers with helmet
69
+ app.use(helmet());
70
+ app.use(helmet.hsts({ maxAge: 31536000, includeSubDomains: true, preload: true }));
71
+
72
+ // Remove powered-by header
73
+ app.disable("x-powered-by");
74
+
75
+ // Restrictive CORS
76
+ const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS?.split(",") ?? [];
77
+ app.use(cors({
78
+ origin(origin, callback) {
79
+ if (!origin || ALLOWED_ORIGINS.includes(origin)) {
80
+ callback(null, true);
81
+ } else {
82
+ callback(new Error("Not allowed by CORS"));
83
+ }
84
+ },
85
+ credentials: true,
86
+ methods: ["GET", "POST", "PUT", "DELETE"],
87
+ maxAge: 86400,
88
+ }));
89
+
90
+ // Safe error handler for production
91
+ app.use((err, req, res, next) => {
92
+ const errorId = crypto.randomUUID();
93
+ console.error({ errorId, message: err.message, stack: err.stack });
94
+ res.status(err.status ?? 500).json({
95
+ error: "An internal error occurred",
96
+ errorId, // For support reference only
97
+ });
98
+ });
99
+
100
+ // Validate required config at startup
101
+ const requiredEnvVars = ["JWT_SECRET", "DATABASE_URL", "ALLOWED_ORIGINS"];
102
+ for (const envVar of requiredEnvVars) {
103
+ if (!process.env[envVar]) {
104
+ throw new Error(`Missing required environment variable: ${envVar}`);
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## Quick Checklist
110
+ - [ ] No stack traces or internal details in production error responses
111
+ - [ ] All default credentials removed or changed
112
+ - [ ] Security headers configured (HSTS, CSP, X-Content-Type-Options, etc.)
113
+ - [ ] `X-Powered-By` and other fingerprinting headers disabled
114
+ - [ ] HTTPS enforced with HSTS
115
+ - [ ] CORS configured with specific allowed origins
116
+ - [ ] No secrets in source code or version control
117
+ - [ ] Unused routes, debug endpoints, and dev tools disabled in production
118
+ - [ ] Environment configuration validated at application startup
@@ -0,0 +1,127 @@
1
+ # Software Supply Chain Security Rules
2
+
3
+ > OWASP Top 10 2025 - A03: Software Supply Chain Failures (NEW)
4
+
5
+ ## Rules
6
+
7
+ ### 1. Audit Dependencies Regularly
8
+ - **DO**: Run `npm audit` in CI/CD pipelines. Use tools like Socket.dev or Snyk to detect malicious or vulnerable packages.
9
+ - **DON'T**: Ignore audit warnings or suppress them without reviewing each finding.
10
+ - **WHY**: Known vulnerabilities in dependencies are a top attack vector. Automated auditing catches issues before deployment.
11
+
12
+ ### 2. Pin Dependency Versions
13
+ - **DO**: Use exact versions or lockfiles (`package-lock.json`, `pnpm-lock.yaml`) and commit them to version control.
14
+ - **DON'T**: Use loose version ranges (e.g., `^` or `*`) for production dependencies without lockfile enforcement.
15
+ - **WHY**: Unpinned versions allow silent upgrades that may introduce vulnerabilities or malicious code.
16
+
17
+ ### 3. Verify Lockfile Integrity
18
+ - **DO**: Enable lockfile-only installs in CI (`npm ci` or `--frozen-lockfile`). Detect and reject unexpected lockfile changes.
19
+ - **DON'T**: Run `npm install` in CI, which can modify the lockfile and pull in unreviewed versions.
20
+ - **WHY**: Lockfile manipulation is a supply chain attack vector. Strict installs ensure reproducible, verified builds.
21
+
22
+ ### 4. Use Subresource Integrity (SRI)
23
+ - **DO**: Add `integrity` attributes to all external `<script>` and `<link>` tags loading from CDNs.
24
+ - **DON'T**: Load external scripts without integrity verification.
25
+ - **WHY**: SRI ensures the browser rejects tampered CDN assets, preventing supply chain attacks via compromised CDNs.
26
+
27
+ ### 5. Minimize Dependency Surface
28
+ - **DO**: Evaluate each dependency for necessity, maintenance status, and security posture before adding it.
29
+ - **DON'T**: Add packages for trivial functionality that can be implemented in a few lines of code.
30
+ - **WHY**: Each dependency is a trust relationship. Fewer dependencies mean a smaller attack surface and less risk of transitive vulnerabilities.
31
+
32
+ ### 6. Monitor for Typosquatting and Malicious Packages
33
+ - **DO**: Double-check package names before installing. Use scoped packages (`@org/package`) where possible.
34
+ - **DON'T**: Install packages without verifying the publisher, download count, and repository link.
35
+ - **WHY**: Typosquatting attacks publish packages with similar names to popular libraries, injecting malicious code.
36
+
37
+ ### 7. Enforce Build Reproducibility
38
+ - **DO**: Use deterministic builds. Pin Node.js versions, use lockfiles, and build in controlled environments.
39
+ - **DON'T**: Allow builds to fetch latest versions at build time or rely on mutable tags like `latest`.
40
+ - **WHY**: Non-reproducible builds make it impossible to verify that deployed code matches reviewed source.
41
+
42
+ ### 8. Restrict Install Scripts
43
+ - **DO**: Use `--ignore-scripts` flag when installing untrusted packages. Review `preinstall` and `postinstall` scripts.
44
+ - **DON'T**: Allow arbitrary install scripts to run without review, especially from new or unvetted dependencies.
45
+ - **WHY**: Malicious install scripts can execute arbitrary code during `npm install`, compromising the build environment.
46
+
47
+ ## Code Examples
48
+
49
+ ### Bad Practice
50
+ ```json
51
+ // package.json with loose version ranges
52
+ {
53
+ "dependencies": {
54
+ "lodash": "*",
55
+ "express": "^4",
56
+ "some-unknown-pkg": "latest"
57
+ }
58
+ }
59
+ ```
60
+
61
+ ```html
62
+ <!-- Loading CDN scripts without integrity check -->
63
+ <script src="https://cdn.example.com/lib/v3/analytics.min.js"></script>
64
+ ```
65
+
66
+ ```yaml
67
+ # CI pipeline using npm install (modifies lockfile)
68
+ steps:
69
+ - run: npm install
70
+ - run: npm run build
71
+ ```
72
+
73
+ ### Good Practice
74
+ ```json
75
+ // package.json with pinned versions
76
+ {
77
+ "dependencies": {
78
+ "lodash": "4.17.21",
79
+ "express": "4.21.2"
80
+ },
81
+ "overrides": {
82
+ "vulnerable-transitive-dep": ">=2.0.1"
83
+ }
84
+ }
85
+ ```
86
+
87
+ ```html
88
+ <!-- CDN scripts with SRI -->
89
+ <script
90
+ src="https://cdn.example.com/lib/v3/analytics.min.js"
91
+ integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8w"
92
+ crossorigin="anonymous"
93
+ ></script>
94
+ ```
95
+
96
+ ```yaml
97
+ # CI pipeline with frozen lockfile and audit
98
+ steps:
99
+ - run: npm ci --ignore-scripts # Frozen lockfile, no scripts
100
+ - run: npm audit --audit-level=high
101
+ - run: npx lockfile-lint --path package-lock.json --type npm --allowed-hosts npm
102
+ - run: npm run build
103
+ ```
104
+
105
+ ```javascript
106
+ // Runtime dependency verification helper
107
+ import { createHash } from "node:crypto";
108
+ import { readFile } from "node:fs/promises";
109
+
110
+ async function verifyFileIntegrity(filePath, expectedHash) {
111
+ const content = await readFile(filePath);
112
+ const hash = createHash("sha384").update(content).digest("base64");
113
+ if (hash !== expectedHash) {
114
+ throw new Error(`Integrity check failed for ${filePath}`);
115
+ }
116
+ }
117
+ ```
118
+
119
+ ## Quick Checklist
120
+ - [ ] `npm audit` (or equivalent) runs in CI on every build
121
+ - [ ] `package-lock.json` is committed and CI uses `npm ci`
122
+ - [ ] No wildcard (`*`) or `latest` version ranges in production dependencies
123
+ - [ ] External CDN scripts use SRI `integrity` attributes
124
+ - [ ] New dependencies are reviewed for security posture before adoption
125
+ - [ ] Install scripts are reviewed or disabled for untrusted packages
126
+ - [ ] Node.js version is pinned in `.nvmrc` or `engines` field
127
+ - [ ] Dependency update PRs are reviewed for unexpected changes