skillstore-cli 1.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.
- package/README.md +95 -0
- package/data/bundles/devflow-complete.json +19 -0
- package/data/free-skills/devflow-agile/manifest.json +19 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/retro.md +23 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/review.md +21 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/sprint.md +30 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/standup.md +20 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile.md +35 -0
- package/data/free-skills/devflow-agile/plugin/commands/devflow.md +42 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/SKILL.md +93 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/assets/sample-output.md +182 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/clean-architecture.md +361 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/clean-code-guide.md +207 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/debugging-methodology.md +191 -0
- package/data/free-skills/devflow-agile/template/agents/agile-coach.md +76 -0
- package/data/free-skills/devflow-agile/template/workflows/agile-sprint-workflow.md +81 -0
- package/data/free-skills/devflow-bootstrap/manifest.json +8 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/bootstrap/auto.md +31 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/bootstrap.md +38 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/devflow.md +20 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/SKILL.md +56 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/assets/sample-output.md +216 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/references/architecture-decisions.md +254 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/references/stack-templates.md +400 -0
- package/data/free-skills/devflow-bootstrap/template/agents/bootstrap-specialist.md +56 -0
- package/data/free-skills/devflow-bootstrap/template/workflows/bootstrap-workflow.md +70 -0
- package/data/free-skills/devflow-docs/manifest.json +8 -0
- package/data/free-skills/devflow-docs/plugin/commands/devflow.md +20 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs/generate.md +17 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs/parse.md +19 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs.md +26 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/SKILL.md +59 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/assets/sample-output.md +114 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/references/extraction-techniques.md +115 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/references/ocr-strategies.md +167 -0
- package/data/free-skills/devflow-docs/template/agents/docs-specialist.md +35 -0
- package/data/free-skills/devflow-docs/template/workflows/docs-workflow.md +70 -0
- package/data/free-skills/devflow-postproject/manifest.json +13 -0
- package/data/free-skills/devflow-postproject/plugin/commands/devflow.md +34 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/handover.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/retro.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/support.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject.md +32 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/SKILL.md +70 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/assets/sample-output.md +79 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/facilitation-techniques.md +178 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/lessons-learned-template.md +118 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/retro-techniques.md +100 -0
- package/data/free-skills/devflow-postproject/template/agents/transition-manager.md +71 -0
- package/data/free-skills/devflow-postproject/template/workflows/transition-workflow.md +72 -0
- package/data/free-skills/devflow-presale/manifest.json +15 -0
- package/data/free-skills/devflow-presale/plugin/commands/devflow.md +47 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/analyze.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/estimate.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/price.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/propose.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale.md +42 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/SKILL.md +63 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/assets/sample-output.md +129 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/references/extraction-framework.md +140 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/references/output-template.md +132 -0
- package/data/free-skills/devflow-presale/template/agents/presale-lead.md +83 -0
- package/data/free-skills/devflow-presale/template/agents/proposal-reviewer.md +63 -0
- package/data/free-skills/devflow-presale/template/workflows/presale-workflow.md +70 -0
- package/data/registry/categories.json +7 -0
- package/data/registry/packages.json +184 -0
- package/data/shared/framework/agents/brainstormer.md +74 -0
- package/data/shared/framework/agents/code-reviewer.md +87 -0
- package/data/shared/framework/agents/debugger.md +84 -0
- package/data/shared/framework/agents/docs-manager.md +55 -0
- package/data/shared/framework/agents/git-manager.md +59 -0
- package/data/shared/framework/agents/planner.md +68 -0
- package/data/shared/framework/agents/researcher.md +66 -0
- package/data/shared/framework/agents/tester.md +65 -0
- package/data/shared/framework/commands/cook/auto.md +27 -0
- package/data/shared/framework/commands/cook.md +45 -0
- package/data/shared/framework/commands/fix/ci.md +21 -0
- package/data/shared/framework/commands/fix/test.md +26 -0
- package/data/shared/framework/commands/fix/types.md +29 -0
- package/data/shared/framework/commands/fix.md +26 -0
- package/data/shared/framework/commands/git/cm.md +37 -0
- package/data/shared/framework/commands/git/pr.md +40 -0
- package/data/shared/framework/config/CLAUDE.md.template +26 -0
- package/data/shared/framework/config/settings.json +41 -0
- package/data/shared/framework/config/skillstore.config.json +29 -0
- package/data/shared/framework/hooks/discord-notify.sh +85 -0
- package/data/shared/framework/hooks/docs-sync.sh +53 -0
- package/data/shared/framework/hooks/modularization-hook.js +103 -0
- package/data/shared/framework/hooks/notification.js +94 -0
- package/data/shared/framework/hooks/quality-gate.js +109 -0
- package/data/shared/framework/hooks/scout-block.js +77 -0
- package/data/shared/framework/hooks/telegram-notify.sh +77 -0
- package/data/shared/framework/protocols/error-recovery.md +80 -0
- package/data/shared/framework/protocols/orchestration-protocol.md +112 -0
- package/data/shared/framework/quality/review-protocol.md +76 -0
- package/data/shared/framework/quality/verification-protocol.md +66 -0
- package/data/shared/framework/rules/development-rules.md +75 -0
- package/data/shared/framework/skills/backend-development/SKILL.md +77 -0
- package/data/shared/framework/skills/backend-development/assets/sample-output.md +175 -0
- package/data/shared/framework/skills/backend-development/references/advanced-patterns.md +180 -0
- package/data/shared/framework/skills/backend-development/references/api-design-guide.md +160 -0
- package/data/shared/framework/skills/backend-development/references/architecture-patterns.md +183 -0
- package/data/shared/framework/skills/backend-development/references/observability-resilience.md +155 -0
- package/data/shared/framework/skills/backend-development/references/troubleshooting.md +199 -0
- package/data/shared/framework/skills/codebase-analysis/SKILL.md +72 -0
- package/data/shared/framework/skills/codebase-analysis/assets/sample-output.md +263 -0
- package/data/shared/framework/skills/codebase-analysis/references/analysis-techniques.md +241 -0
- package/data/shared/framework/skills/codebase-analysis/references/dependency-mapping.md +280 -0
- package/data/shared/framework/skills/codebase-analysis/references/tech-debt-assessment.md +208 -0
- package/data/shared/framework/skills/databases/SKILL.md +72 -0
- package/data/shared/framework/skills/databases/assets/sample-output.md +212 -0
- package/data/shared/framework/skills/databases/references/advanced-data-patterns.md +259 -0
- package/data/shared/framework/skills/databases/references/query-optimization.md +214 -0
- package/data/shared/framework/skills/databases/references/schema-design.md +159 -0
- package/data/shared/framework/skills/databases/references/troubleshooting.md +214 -0
- package/data/shared/framework/skills/debugging-investigation/SKILL.md +84 -0
- package/data/shared/framework/skills/debugging-investigation/assets/sample-output.md +314 -0
- package/data/shared/framework/skills/debugging-investigation/references/systematic-debugging.md +197 -0
- package/data/shared/framework/skills/debugging-investigation/references/tool-specific-guides.md +202 -0
- package/data/shared/framework/skills/debugging-investigation/references/troubleshooting-patterns.md +196 -0
- package/data/shared/framework/skills/frontend-development/SKILL.md +67 -0
- package/data/shared/framework/skills/frontend-development/assets/sample-output.md +110 -0
- package/data/shared/framework/skills/frontend-development/references/component-patterns.md +112 -0
- package/data/shared/framework/skills/frontend-development/references/performance-guide.md +169 -0
- package/data/shared/framework/skills/frontend-development/references/routing-forms-realtime.md +374 -0
- package/data/shared/framework/skills/frontend-development/references/ssr-rsc-patterns.md +284 -0
- package/data/shared/framework/skills/frontend-development/references/troubleshooting.md +154 -0
- package/data/shared/framework/skills/mobile-development/SKILL.md +67 -0
- package/data/shared/framework/skills/mobile-development/assets/sample-output.md +382 -0
- package/data/shared/framework/skills/mobile-development/references/mobile-patterns.md +681 -0
- package/data/shared/framework/skills/mobile-development/references/mobile-performance.md +524 -0
- package/data/shared/framework/skills/mobile-development/references/troubleshooting.md +158 -0
- package/data/shared/framework/skills/security-audit/SKILL.md +83 -0
- package/data/shared/framework/skills/security-audit/assets/sample-output.md +451 -0
- package/data/shared/framework/skills/security-audit/references/owasp-checklist.md +580 -0
- package/data/shared/framework/skills/security-audit/references/secure-coding-patterns.md +433 -0
- package/data/shared/framework/skills/security-audit/references/vulnerability-remediation.md +331 -0
- package/data/shared/framework/skills/ui-generation/SKILL.md +70 -0
- package/data/shared/framework/skills/ui-generation/assets/sample-output.md +139 -0
- package/data/shared/framework/skills/ui-generation/references/accessibility-responsive.md +127 -0
- package/data/shared/framework/skills/ui-generation/references/compound-components.md +252 -0
- package/data/shared/framework/skills/ui-generation/references/generation-patterns.md +110 -0
- package/data/shared/framework/skills/ui-generation/references/storybook-design-system.md +278 -0
- package/data/shared/framework/skills/ui-generation/references/troubleshooting.md +198 -0
- package/data/shared/framework/workflows/documentation-management.md +58 -0
- package/data/shared/framework/workflows/primary-workflow.md +88 -0
- package/dist/commands/activate.d.ts +3 -0
- package/dist/commands/activate.d.ts.map +1 -0
- package/dist/commands/activate.js +34 -0
- package/dist/commands/activate.js.map +1 -0
- package/dist/commands/bundle.d.ts +3 -0
- package/dist/commands/bundle.d.ts.map +1 -0
- package/dist/commands/bundle.js +64 -0
- package/dist/commands/bundle.js.map +1 -0
- package/dist/commands/install.d.ts +3 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +99 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +37 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +30 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +35 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +68 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/download/cache.d.ts +3 -0
- package/dist/download/cache.d.ts.map +1 -0
- package/dist/download/cache.js +18 -0
- package/dist/download/cache.js.map +1 -0
- package/dist/download/client.d.ts +2 -0
- package/dist/download/client.d.ts.map +1 -0
- package/dist/download/client.js +58 -0
- package/dist/download/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/file-copier.d.ts +6 -0
- package/dist/installer/file-copier.d.ts.map +1 -0
- package/dist/installer/file-copier.js +32 -0
- package/dist/installer/file-copier.js.map +1 -0
- package/dist/installer/plugin-installer.d.ts +12 -0
- package/dist/installer/plugin-installer.d.ts.map +1 -0
- package/dist/installer/plugin-installer.js +33 -0
- package/dist/installer/plugin-installer.js.map +1 -0
- package/dist/installer/template-installer.d.ts +12 -0
- package/dist/installer/template-installer.d.ts.map +1 -0
- package/dist/installer/template-installer.js +45 -0
- package/dist/installer/template-installer.js.map +1 -0
- package/dist/license/crypto.d.ts +16 -0
- package/dist/license/crypto.d.ts.map +1 -0
- package/dist/license/crypto.js +50 -0
- package/dist/license/crypto.js.map +1 -0
- package/dist/license/license-store.d.ts +19 -0
- package/dist/license/license-store.d.ts.map +1 -0
- package/dist/license/license-store.js +99 -0
- package/dist/license/license-store.js.map +1 -0
- package/dist/license/validator.d.ts +32 -0
- package/dist/license/validator.d.ts.map +1 -0
- package/dist/license/validator.js +81 -0
- package/dist/license/validator.js.map +1 -0
- package/dist/registry/loader.d.ts +30 -0
- package/dist/registry/loader.d.ts.map +1 -0
- package/dist/registry/loader.js +22 -0
- package/dist/registry/loader.js.map +1 -0
- package/dist/registry/search-engine.d.ts +9 -0
- package/dist/registry/search-engine.d.ts.map +1 -0
- package/dist/registry/search-engine.js +30 -0
- package/dist/registry/search-engine.js.map +1 -0
- package/dist/utils/config.d.ts +14 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +28 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +22 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +20 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +79 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
# OWASP Top 10 (2021) — Security Audit Checklist
|
|
2
|
+
|
|
3
|
+
Comprehensive checklist with detection techniques, vulnerable code examples, and remediation patterns for each OWASP Top 10 category.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## A01: Broken Access Control
|
|
8
|
+
|
|
9
|
+
The most common web application vulnerability. Occurs when users can act outside their intended permissions.
|
|
10
|
+
|
|
11
|
+
### What to Look For
|
|
12
|
+
- **IDOR (Insecure Direct Object Reference):** User-controlled IDs in URLs/request bodies without ownership verification
|
|
13
|
+
- **Horizontal privilege escalation:** User A accessing User B's resources
|
|
14
|
+
- **Vertical privilege escalation:** Regular user accessing admin functionality
|
|
15
|
+
- **Missing function-level access control:** Endpoints without authentication/authorization middleware
|
|
16
|
+
- **Metadata manipulation:** Tampering with JWTs, cookies, or hidden fields to elevate privileges
|
|
17
|
+
|
|
18
|
+
### Vulnerable Code Example (IDOR)
|
|
19
|
+
```javascript
|
|
20
|
+
// VULNERABLE: No ownership check — any authenticated user can access any profile
|
|
21
|
+
app.get('/api/users/:id/profile', authenticate, async (req, res) => {
|
|
22
|
+
const profile = await db.query('SELECT * FROM profiles WHERE user_id = $1', [req.params.id]);
|
|
23
|
+
res.json(profile.rows[0]);
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Fix: Middleware-Based RBAC
|
|
28
|
+
```javascript
|
|
29
|
+
// SECURE: Ownership verification + role-based access control
|
|
30
|
+
const authorizeResource = (resourceType) => async (req, res, next) => {
|
|
31
|
+
const resourceId = req.params.id;
|
|
32
|
+
const userId = req.user.id;
|
|
33
|
+
const userRole = req.user.role;
|
|
34
|
+
|
|
35
|
+
// Admins bypass ownership check
|
|
36
|
+
if (userRole === 'admin') return next();
|
|
37
|
+
|
|
38
|
+
// Verify resource ownership
|
|
39
|
+
const resource = await db.query(
|
|
40
|
+
`SELECT owner_id FROM ${resourceType} WHERE id = $1`,
|
|
41
|
+
[resourceId]
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
if (!resource.rows[0] || resource.rows[0].owner_id !== userId) {
|
|
45
|
+
return res.status(403).json({ error: 'Access denied' });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
next();
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
app.get('/api/users/:id/profile', authenticate, authorizeResource('profiles'), async (req, res) => {
|
|
52
|
+
const profile = await db.query('SELECT * FROM profiles WHERE user_id = $1', [req.params.id]);
|
|
53
|
+
res.json(profile.rows[0]);
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Checklist
|
|
58
|
+
- [ ] Every endpoint has authentication middleware
|
|
59
|
+
- [ ] Every data-access endpoint verifies resource ownership
|
|
60
|
+
- [ ] Role checks are enforced server-side, not just UI-hidden
|
|
61
|
+
- [ ] Directory listing is disabled on static file servers
|
|
62
|
+
- [ ] CORS is restricted to known origins
|
|
63
|
+
- [ ] JWT claims are validated on every request
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## A02: Cryptographic Failures
|
|
68
|
+
|
|
69
|
+
Sensitive data exposed due to weak or missing cryptography.
|
|
70
|
+
|
|
71
|
+
### What to Look For
|
|
72
|
+
- **Weak hashing:** MD5 or SHA-1 for passwords
|
|
73
|
+
- **Plaintext storage:** Passwords, API keys, or PII stored unencrypted in database
|
|
74
|
+
- **Insecure TLS:** TLS 1.0/1.1, weak cipher suites, missing HSTS
|
|
75
|
+
- **Hardcoded secrets:** API keys or credentials in source code
|
|
76
|
+
- **Insufficient key management:** Encryption keys in environment variables without rotation
|
|
77
|
+
|
|
78
|
+
### Vulnerable Code Example
|
|
79
|
+
```javascript
|
|
80
|
+
// VULNERABLE: MD5 is cryptographically broken for password hashing
|
|
81
|
+
const crypto = require('crypto');
|
|
82
|
+
const hashedPassword = crypto.createHash('md5').update(password).digest('hex');
|
|
83
|
+
await db.query('INSERT INTO users (email, password) VALUES ($1, $2)', [email, hashedPassword]);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Fix: Proper Password Hashing
|
|
87
|
+
```javascript
|
|
88
|
+
// SECURE: Argon2id with appropriate parameters
|
|
89
|
+
const argon2 = require('argon2');
|
|
90
|
+
|
|
91
|
+
// Hashing
|
|
92
|
+
const hashedPassword = await argon2.hash(password, {
|
|
93
|
+
type: argon2.argon2id,
|
|
94
|
+
memoryCost: 65536, // 64 MB
|
|
95
|
+
timeCost: 3, // 3 iterations
|
|
96
|
+
parallelism: 4
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Verification
|
|
100
|
+
const isValid = await argon2.verify(hashedPassword, candidatePassword);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Fix: TLS Configuration (Node.js)
|
|
104
|
+
```javascript
|
|
105
|
+
const tls = require('tls');
|
|
106
|
+
const server = https.createServer({
|
|
107
|
+
key: fs.readFileSync('private-key.pem'),
|
|
108
|
+
cert: fs.readFileSync('certificate.pem'),
|
|
109
|
+
minVersion: 'TLSv1.2',
|
|
110
|
+
cipherSuites: 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256',
|
|
111
|
+
honorCipherOrder: true
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Checklist
|
|
116
|
+
- [ ] Passwords hashed with Argon2id or bcrypt (cost factor >= 12)
|
|
117
|
+
- [ ] No MD5 or SHA-1 used for any security purpose
|
|
118
|
+
- [ ] TLS 1.2+ enforced, TLS 1.0/1.1 disabled
|
|
119
|
+
- [ ] HSTS header set with max-age >= 31536000 and includeSubDomains
|
|
120
|
+
- [ ] Sensitive data encrypted at rest (AES-256-GCM)
|
|
121
|
+
- [ ] No secrets in source code or version control
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## A03: Injection
|
|
126
|
+
|
|
127
|
+
Untrusted data sent to an interpreter as part of a command or query.
|
|
128
|
+
|
|
129
|
+
### What to Look For
|
|
130
|
+
- **SQL injection:** String concatenation in queries
|
|
131
|
+
- **NoSQL injection:** Unvalidated operators in MongoDB queries
|
|
132
|
+
- **Command injection:** User input passed to shell commands
|
|
133
|
+
- **XSS (Reflected):** User input reflected in HTML without encoding
|
|
134
|
+
- **XSS (Stored):** User-submitted content rendered without sanitization
|
|
135
|
+
- **XSS (DOM):** Client-side JavaScript using untrusted data in dangerous sinks
|
|
136
|
+
|
|
137
|
+
### Vulnerable Code: SQL Injection
|
|
138
|
+
```javascript
|
|
139
|
+
// VULNERABLE: String interpolation creates injection point
|
|
140
|
+
app.get('/api/search', async (req, res) => {
|
|
141
|
+
const results = await db.query(`SELECT * FROM products WHERE name LIKE '%${req.query.q}%'`);
|
|
142
|
+
res.json(results.rows);
|
|
143
|
+
});
|
|
144
|
+
// Attack: /api/search?q=' OR 1=1; DROP TABLE products; --
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Fix: Parameterized Queries
|
|
148
|
+
```javascript
|
|
149
|
+
// SECURE: Parameterized query prevents injection
|
|
150
|
+
app.get('/api/search', async (req, res) => {
|
|
151
|
+
const results = await db.query(
|
|
152
|
+
'SELECT * FROM products WHERE name LIKE $1',
|
|
153
|
+
[`%${req.query.q}%`]
|
|
154
|
+
);
|
|
155
|
+
res.json(results.rows);
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Vulnerable Code: NoSQL Injection
|
|
160
|
+
```javascript
|
|
161
|
+
// VULNERABLE: User can pass { "$gt": "" } to bypass authentication
|
|
162
|
+
app.post('/api/login', async (req, res) => {
|
|
163
|
+
const user = await User.findOne({ email: req.body.email, password: req.body.password });
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Fix: Input Type Enforcement
|
|
168
|
+
```javascript
|
|
169
|
+
// SECURE: Enforce string type + use hashed password comparison
|
|
170
|
+
app.post('/api/login', async (req, res) => {
|
|
171
|
+
if (typeof req.body.email !== 'string' || typeof req.body.password !== 'string') {
|
|
172
|
+
return res.status(400).json({ error: 'Invalid input' });
|
|
173
|
+
}
|
|
174
|
+
const user = await User.findOne({ email: req.body.email });
|
|
175
|
+
if (!user || !(await argon2.verify(user.passwordHash, req.body.password))) {
|
|
176
|
+
return res.status(401).json({ error: 'Invalid credentials' });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Vulnerable Code: Command Injection
|
|
182
|
+
```javascript
|
|
183
|
+
// VULNERABLE: User input passed directly to shell
|
|
184
|
+
const { exec } = require('child_process');
|
|
185
|
+
app.get('/api/ping', (req, res) => {
|
|
186
|
+
exec(`ping -c 3 ${req.query.host}`, (error, stdout) => res.send(stdout));
|
|
187
|
+
});
|
|
188
|
+
// Attack: /api/ping?host=; cat /etc/passwd
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Fix: Use Safe APIs
|
|
192
|
+
```javascript
|
|
193
|
+
// SECURE: execFile does not invoke a shell
|
|
194
|
+
const { execFile } = require('child_process');
|
|
195
|
+
const validator = require('validator');
|
|
196
|
+
|
|
197
|
+
app.get('/api/ping', (req, res) => {
|
|
198
|
+
if (!validator.isIP(req.query.host) && !validator.isFQDN(req.query.host)) {
|
|
199
|
+
return res.status(400).json({ error: 'Invalid host' });
|
|
200
|
+
}
|
|
201
|
+
execFile('ping', ['-c', '3', req.query.host], (error, stdout) => res.send(stdout));
|
|
202
|
+
});
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### XSS Fix: Context-Specific Output Encoding
|
|
206
|
+
```javascript
|
|
207
|
+
// React: JSX auto-escapes by default — avoid dangerouslySetInnerHTML
|
|
208
|
+
// If raw HTML is required, sanitize first:
|
|
209
|
+
import DOMPurify from 'dompurify';
|
|
210
|
+
const SafeContent = ({ html }) => (
|
|
211
|
+
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} />
|
|
212
|
+
);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Checklist
|
|
216
|
+
- [ ] All SQL queries use parameterized statements (no string concatenation)
|
|
217
|
+
- [ ] NoSQL queries enforce input types before querying
|
|
218
|
+
- [ ] No user input passed to exec(), eval(), or similar interpreters
|
|
219
|
+
- [ ] User-generated content sanitized before rendering (DOMPurify for HTML)
|
|
220
|
+
- [ ] Content-Security-Policy header blocks inline scripts
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## A04: Insecure Design
|
|
225
|
+
|
|
226
|
+
Security flaws rooted in missing or ineffective design-level controls, not implementation bugs.
|
|
227
|
+
|
|
228
|
+
### What to Look For
|
|
229
|
+
- **Missing rate limiting:** Login, password reset, or API endpoints without throttling
|
|
230
|
+
- **Business logic flaws:** Price manipulation, workflow bypass, race conditions
|
|
231
|
+
- **Missing threat modeling:** No consideration of abuse scenarios during design
|
|
232
|
+
- **Inadequate separation of privilege:** Single token grants access to everything
|
|
233
|
+
|
|
234
|
+
### Fix: Threat Modeling Checklist
|
|
235
|
+
For every new feature, answer:
|
|
236
|
+
1. What data does this feature handle? What is its sensitivity classification?
|
|
237
|
+
2. Who should access this? What are the trust boundaries?
|
|
238
|
+
3. What happens if input is malicious? (SQL/XSS/command injection, overflow, type confusion)
|
|
239
|
+
4. What happens if the user calls this 10,000 times per second?
|
|
240
|
+
5. What happens if requests arrive in an unexpected order? (race conditions, TOCTOU)
|
|
241
|
+
6. What happens if this feature is used in a way we didn't intend? (business logic abuse)
|
|
242
|
+
7. Can we detect and alert on abuse of this feature?
|
|
243
|
+
|
|
244
|
+
### Fix: Rate Limiting Implementation
|
|
245
|
+
```javascript
|
|
246
|
+
const rateLimit = require('express-rate-limit');
|
|
247
|
+
|
|
248
|
+
const loginLimiter = rateLimit({
|
|
249
|
+
windowMs: 15 * 60 * 1000, // 15-minute window
|
|
250
|
+
max: 5, // 5 attempts per window
|
|
251
|
+
message: { error: 'Too many login attempts. Try again in 15 minutes.' },
|
|
252
|
+
standardHeaders: true,
|
|
253
|
+
legacyHeaders: false,
|
|
254
|
+
keyGenerator: (req) => req.body.email || req.ip, // Per-account limiting
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
app.post('/api/auth/login', loginLimiter, loginHandler);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Checklist
|
|
261
|
+
- [ ] Threat model exists for authentication, payments, and data-access features
|
|
262
|
+
- [ ] Rate limiting on login, registration, password reset, and API endpoints
|
|
263
|
+
- [ ] Business logic validated server-side (prices, quantities, permissions)
|
|
264
|
+
- [ ] Race conditions mitigated with database-level locking or idempotency keys
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## A05: Security Misconfiguration
|
|
269
|
+
|
|
270
|
+
Default, incomplete, or ad-hoc configurations that leave the application vulnerable.
|
|
271
|
+
|
|
272
|
+
### What to Look For
|
|
273
|
+
- **Default credentials:** Unchanged admin passwords on databases, dashboards, or services
|
|
274
|
+
- **Verbose error messages:** Stack traces or internal paths exposed to users
|
|
275
|
+
- **Unnecessary features:** Debug endpoints, sample applications, unused HTTP methods
|
|
276
|
+
- **Missing security headers:** No CSP, HSTS, X-Frame-Options, etc.
|
|
277
|
+
- **Permissive cloud IAM:** Overly broad S3 policies, wildcard permissions
|
|
278
|
+
|
|
279
|
+
### Fix: Hardening Checklist
|
|
280
|
+
- [ ] Remove or disable debug endpoints and sample code in production
|
|
281
|
+
- [ ] Error responses return generic messages; details logged server-side only
|
|
282
|
+
- [ ] All default credentials changed; enforce strong passwords
|
|
283
|
+
- [ ] Unnecessary HTTP methods disabled (TRACE, OPTIONS if unused)
|
|
284
|
+
- [ ] Security headers set: CSP, HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy
|
|
285
|
+
- [ ] Cloud IAM follows least-privilege principle
|
|
286
|
+
- [ ] Directory listing disabled
|
|
287
|
+
- [ ] Server version headers removed (X-Powered-By)
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
// Express security headers with helmet
|
|
291
|
+
const helmet = require('helmet');
|
|
292
|
+
app.use(helmet({
|
|
293
|
+
contentSecurityPolicy: {
|
|
294
|
+
directives: {
|
|
295
|
+
defaultSrc: ["'self'"],
|
|
296
|
+
scriptSrc: ["'self'"],
|
|
297
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
298
|
+
imgSrc: ["'self'", "data:", "https:"],
|
|
299
|
+
connectSrc: ["'self'"],
|
|
300
|
+
fontSrc: ["'self'"],
|
|
301
|
+
objectSrc: ["'none'"],
|
|
302
|
+
frameSrc: ["'none'"],
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
|
|
306
|
+
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
|
|
307
|
+
}));
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## A06: Vulnerable and Outdated Components
|
|
313
|
+
|
|
314
|
+
Using libraries, frameworks, or components with known vulnerabilities.
|
|
315
|
+
|
|
316
|
+
### What to Look For
|
|
317
|
+
- **Outdated dependencies:** npm packages, Python packages, container base images with known CVEs
|
|
318
|
+
- **Unmaintained libraries:** No updates in 12+ months with open security issues
|
|
319
|
+
- **Transitive vulnerabilities:** Vulnerable sub-dependencies you don't directly control
|
|
320
|
+
|
|
321
|
+
### Fix: Dependency Scanning Pipeline
|
|
322
|
+
```yaml
|
|
323
|
+
# GitHub Actions workflow for dependency scanning
|
|
324
|
+
name: Security Scan
|
|
325
|
+
on:
|
|
326
|
+
pull_request:
|
|
327
|
+
branches: [main]
|
|
328
|
+
schedule:
|
|
329
|
+
- cron: '0 6 * * 1' # Weekly Monday scan
|
|
330
|
+
|
|
331
|
+
jobs:
|
|
332
|
+
dependency-scan:
|
|
333
|
+
runs-on: ubuntu-latest
|
|
334
|
+
steps:
|
|
335
|
+
- uses: actions/checkout@v4
|
|
336
|
+
- run: npm audit --audit-level=high
|
|
337
|
+
- uses: snyk/actions/node@master
|
|
338
|
+
env:
|
|
339
|
+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
|
340
|
+
- uses: aquasecurity/trivy-action@master
|
|
341
|
+
with:
|
|
342
|
+
scan-type: 'fs'
|
|
343
|
+
severity: 'HIGH,CRITICAL'
|
|
344
|
+
exit-code: '1'
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Checklist
|
|
348
|
+
- [ ] `npm audit` / `pip audit` runs in CI and blocks on high/critical
|
|
349
|
+
- [ ] Dependabot or Renovate enabled for automated dependency PRs
|
|
350
|
+
- [ ] Container base images pinned to specific digests and scanned with Trivy
|
|
351
|
+
- [ ] License compliance checked (no copyleft in proprietary code)
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## A07: Identification and Authentication Failures
|
|
356
|
+
|
|
357
|
+
Weaknesses in authentication mechanisms that allow attackers to compromise credentials or sessions.
|
|
358
|
+
|
|
359
|
+
### What to Look For
|
|
360
|
+
- **Credential stuffing:** No protection against automated login attempts with leaked credentials
|
|
361
|
+
- **Weak password policy:** Allows short or common passwords
|
|
362
|
+
- **Session fixation:** Session ID not regenerated after login
|
|
363
|
+
- **Missing MFA:** High-privilege accounts without multi-factor authentication
|
|
364
|
+
- **Insecure password recovery:** Security questions, predictable tokens
|
|
365
|
+
|
|
366
|
+
### Fix: Secure Authentication Checklist
|
|
367
|
+
```javascript
|
|
368
|
+
// Secure session configuration (express-session)
|
|
369
|
+
app.use(session({
|
|
370
|
+
secret: process.env.SESSION_SECRET, // Strong random secret from env
|
|
371
|
+
name: '__Host-sid', // __Host- prefix enforces Secure + Path=/
|
|
372
|
+
resave: false,
|
|
373
|
+
saveUninitialized: false,
|
|
374
|
+
cookie: {
|
|
375
|
+
httpOnly: true, // Not accessible via JavaScript
|
|
376
|
+
secure: true, // HTTPS only
|
|
377
|
+
sameSite: 'lax', // CSRF protection
|
|
378
|
+
maxAge: 3600000, // 1 hour
|
|
379
|
+
path: '/',
|
|
380
|
+
},
|
|
381
|
+
store: new RedisStore({ client: redisClient }), // Server-side session store
|
|
382
|
+
}));
|
|
383
|
+
|
|
384
|
+
// Regenerate session on login to prevent session fixation
|
|
385
|
+
app.post('/api/auth/login', loginLimiter, async (req, res) => {
|
|
386
|
+
const user = await authenticateUser(req.body.email, req.body.password);
|
|
387
|
+
if (!user) return res.status(401).json({ error: 'Invalid credentials' });
|
|
388
|
+
|
|
389
|
+
req.session.regenerate((err) => {
|
|
390
|
+
if (err) return res.status(500).json({ error: 'Session error' });
|
|
391
|
+
req.session.userId = user.id;
|
|
392
|
+
req.session.role = user.role;
|
|
393
|
+
res.json({ success: true });
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### Checklist
|
|
399
|
+
- [ ] Rate limiting on authentication endpoints (5 attempts / 15 min)
|
|
400
|
+
- [ ] Password policy: minimum 12 characters, checked against breached passwords (HaveIBeenPwned API)
|
|
401
|
+
- [ ] Session regenerated on login, invalidated on logout
|
|
402
|
+
- [ ] MFA available for all users, required for admin/privileged roles
|
|
403
|
+
- [ ] Password reset tokens are single-use, time-limited (< 1 hour), and cryptographically random
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## A08: Software and Data Integrity Failures
|
|
408
|
+
|
|
409
|
+
Assumptions about software updates, critical data, or CI/CD pipelines without verifying integrity.
|
|
410
|
+
|
|
411
|
+
### What to Look For
|
|
412
|
+
- **Unsigned updates:** Application or plugin updates without signature verification
|
|
413
|
+
- **CI/CD pipeline poisoning:** Untrusted code execution in build pipelines
|
|
414
|
+
- **Insecure deserialization:** Deserializing untrusted data (Java ObjectInputStream, Python pickle, Node.js node-serialize)
|
|
415
|
+
- **Dependency confusion:** Internal package names squatted on public registries
|
|
416
|
+
|
|
417
|
+
### Fix: Integrity Verification
|
|
418
|
+
```javascript
|
|
419
|
+
// Verify webhook signatures (e.g., Stripe)
|
|
420
|
+
const crypto = require('crypto');
|
|
421
|
+
|
|
422
|
+
function verifyWebhookSignature(payload, signature, secret) {
|
|
423
|
+
const expectedSignature = crypto
|
|
424
|
+
.createHmac('sha256', secret)
|
|
425
|
+
.update(payload, 'utf8')
|
|
426
|
+
.digest('hex');
|
|
427
|
+
|
|
428
|
+
return crypto.timingConstantEqual(
|
|
429
|
+
Buffer.from(signature),
|
|
430
|
+
Buffer.from(`sha256=${expectedSignature}`)
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Checklist
|
|
436
|
+
- [ ] CI/CD pipelines use pinned action versions (hash, not tag)
|
|
437
|
+
- [ ] Artifact signing enabled for deployments
|
|
438
|
+
- [ ] No deserialization of untrusted data (avoid `eval()`, `pickle.loads()`, `unserialize()`)
|
|
439
|
+
- [ ] Subresource Integrity (SRI) for CDN-loaded scripts
|
|
440
|
+
- [ ] `.npmrc` configured with `registry` pointing to private registry when applicable
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## A09: Security Logging and Monitoring Failures
|
|
445
|
+
|
|
446
|
+
Insufficient logging and monitoring prevent detection of breaches and active attacks.
|
|
447
|
+
|
|
448
|
+
### What to Look For
|
|
449
|
+
- **Missing audit logs:** Login attempts, access control failures, input validation failures not logged
|
|
450
|
+
- **Log injection:** User input written to logs without sanitization
|
|
451
|
+
- **No alerting:** No automated alerts on suspicious patterns
|
|
452
|
+
- **Insufficient log retention:** Logs rotated before incident investigation can occur
|
|
453
|
+
|
|
454
|
+
### Fix: Structured Security Logging
|
|
455
|
+
```javascript
|
|
456
|
+
const winston = require('winston');
|
|
457
|
+
|
|
458
|
+
const securityLogger = winston.createLogger({
|
|
459
|
+
level: 'info',
|
|
460
|
+
format: winston.format.combine(
|
|
461
|
+
winston.format.timestamp({ format: 'ISO' }),
|
|
462
|
+
winston.format.json()
|
|
463
|
+
),
|
|
464
|
+
defaultMeta: { service: 'security-audit' },
|
|
465
|
+
transports: [
|
|
466
|
+
new winston.transports.File({ filename: 'security-events.log' }),
|
|
467
|
+
],
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// Log security events with structured data
|
|
471
|
+
function logSecurityEvent(event) {
|
|
472
|
+
// Sanitize user-controlled fields to prevent log injection
|
|
473
|
+
const sanitized = {
|
|
474
|
+
...event,
|
|
475
|
+
userAgent: event.userAgent?.replace(/[\r\n]/g, ''),
|
|
476
|
+
email: event.email?.replace(/[\r\n]/g, ''),
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
securityLogger.info('security_event', sanitized);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Usage examples
|
|
483
|
+
logSecurityEvent({
|
|
484
|
+
type: 'AUTH_FAILURE',
|
|
485
|
+
email: req.body.email,
|
|
486
|
+
ip: req.ip,
|
|
487
|
+
userAgent: req.headers['user-agent'],
|
|
488
|
+
reason: 'invalid_password',
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
logSecurityEvent({
|
|
492
|
+
type: 'ACCESS_DENIED',
|
|
493
|
+
userId: req.user.id,
|
|
494
|
+
resource: req.originalUrl,
|
|
495
|
+
method: req.method,
|
|
496
|
+
ip: req.ip,
|
|
497
|
+
});
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Checklist
|
|
501
|
+
- [ ] Authentication events logged: success, failure, lockout, password change
|
|
502
|
+
- [ ] Authorization failures logged with user ID, resource, and action
|
|
503
|
+
- [ ] Input validation failures logged (potential injection attempts)
|
|
504
|
+
- [ ] Log injection prevented (newline stripping, structured logging)
|
|
505
|
+
- [ ] Alerts configured for: 10+ auth failures in 5 min, admin access from new IP, privilege escalation
|
|
506
|
+
- [ ] Log retention >= 90 days (or per compliance: SOC 2 = 1 year, PCI-DSS = 1 year)
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## A10: Server-Side Request Forgery (SSRF)
|
|
511
|
+
|
|
512
|
+
Application fetches a remote resource using a user-supplied URL without validation.
|
|
513
|
+
|
|
514
|
+
### What to Look For
|
|
515
|
+
- **Internal network access:** Fetching URLs that resolve to internal IPs (127.0.0.1, 10.x, 169.254.169.254)
|
|
516
|
+
- **Cloud metadata access:** SSRF to cloud metadata endpoints (AWS IMDSv1: http://169.254.169.254)
|
|
517
|
+
- **Protocol smuggling:** file://, gopher://, dict:// protocol handlers
|
|
518
|
+
- **DNS rebinding:** Domain resolving to internal IP after validation
|
|
519
|
+
|
|
520
|
+
### Vulnerable Code
|
|
521
|
+
```javascript
|
|
522
|
+
// VULNERABLE: User-controlled URL fetched without validation
|
|
523
|
+
app.post('/api/fetch-url', async (req, res) => {
|
|
524
|
+
const response = await fetch(req.body.url);
|
|
525
|
+
const data = await response.text();
|
|
526
|
+
res.json({ content: data });
|
|
527
|
+
});
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Fix: URL Validation and Allowlisting
|
|
531
|
+
```javascript
|
|
532
|
+
const { URL } = require('url');
|
|
533
|
+
const dns = require('dns').promises;
|
|
534
|
+
const ipaddr = require('ipaddr.js');
|
|
535
|
+
|
|
536
|
+
const ALLOWED_PROTOCOLS = ['https:'];
|
|
537
|
+
const BLOCKED_IP_RANGES = ['private', 'loopback', 'linkLocal', 'uniqueLocal'];
|
|
538
|
+
|
|
539
|
+
async function validateUrl(urlString) {
|
|
540
|
+
// Parse and validate protocol
|
|
541
|
+
let parsed;
|
|
542
|
+
try { parsed = new URL(urlString); }
|
|
543
|
+
catch { throw new Error('Invalid URL'); }
|
|
544
|
+
|
|
545
|
+
if (!ALLOWED_PROTOCOLS.includes(parsed.protocol)) {
|
|
546
|
+
throw new Error('Protocol not allowed');
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Resolve DNS and check IP is not internal
|
|
550
|
+
const addresses = await dns.resolve4(parsed.hostname);
|
|
551
|
+
for (const addr of addresses) {
|
|
552
|
+
const ip = ipaddr.parse(addr);
|
|
553
|
+
const range = ip.range();
|
|
554
|
+
if (BLOCKED_IP_RANGES.includes(range)) {
|
|
555
|
+
throw new Error('Internal addresses not allowed');
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return parsed.toString();
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
app.post('/api/fetch-url', async (req, res) => {
|
|
563
|
+
try {
|
|
564
|
+
const safeUrl = await validateUrl(req.body.url);
|
|
565
|
+
const response = await fetch(safeUrl, { redirect: 'error' });
|
|
566
|
+
const data = await response.text();
|
|
567
|
+
res.json({ content: data });
|
|
568
|
+
} catch (err) {
|
|
569
|
+
res.status(400).json({ error: err.message });
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Checklist
|
|
575
|
+
- [ ] All outbound HTTP requests validate destination URLs
|
|
576
|
+
- [ ] Internal/private IP ranges blocked at application and network level
|
|
577
|
+
- [ ] Cloud metadata endpoint blocked (169.254.169.254) — use IMDSv2 with hop limit
|
|
578
|
+
- [ ] Only HTTPS protocol allowed for user-supplied URLs
|
|
579
|
+
- [ ] Redirect following disabled or limited to prevent SSRF via redirect
|
|
580
|
+
- [ ] Network segmentation: application servers cannot reach internal admin interfaces
|