@kaademos/secure-sdlc 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.
Files changed (53) hide show
  1. package/.claude/agents/ai-security-engineer.md +209 -0
  2. package/.claude/agents/appsec-engineer.md +131 -0
  3. package/.claude/agents/cloud-platform-engineer.md +119 -0
  4. package/.claude/agents/dev-lead.md +138 -0
  5. package/.claude/agents/grc-analyst.md +143 -0
  6. package/.claude/agents/product-manager.md +100 -0
  7. package/.claude/agents/release-manager.md +126 -0
  8. package/.claude/agents/security-champion.md +148 -0
  9. package/.cursor/rules/secure-sdlc.mdc +98 -0
  10. package/.github/workflows/secure-sdlc-gate.yml +325 -0
  11. package/CHANGELOG.md +49 -0
  12. package/CLAUDE.md +195 -0
  13. package/LICENSE +21 -0
  14. package/README.md +394 -0
  15. package/cli/bin/secure-sdlc.js +95 -0
  16. package/cli/src/commands/gate.js +129 -0
  17. package/cli/src/commands/init.js +219 -0
  18. package/cli/src/commands/install-mcp.js +121 -0
  19. package/cli/src/commands/kickoff.js +261 -0
  20. package/cli/src/commands/paths.js +33 -0
  21. package/cli/src/commands/review.js +53 -0
  22. package/cli/src/commands/status.js +122 -0
  23. package/cli/src/utils/banner.js +43 -0
  24. package/cli/src/utils/package-root.js +23 -0
  25. package/cli/src/utils/phase-detect.js +107 -0
  26. package/cli/src/utils/stack-detect.js +138 -0
  27. package/docs/templates/compliance-attestation.md +159 -0
  28. package/docs/templates/infra-security-review.md +133 -0
  29. package/docs/templates/release-sign-off.md +119 -0
  30. package/docs/templates/risk-register.md +72 -0
  31. package/docs/templates/sast-findings.md +110 -0
  32. package/docs/templates/security-requirements.md +98 -0
  33. package/docs/templates/test-security-report.md +143 -0
  34. package/docs/templates/threat-model.md +129 -0
  35. package/hooks/install.sh +37 -0
  36. package/hooks/pre-commit +208 -0
  37. package/hooks/pre-push +127 -0
  38. package/mcp/README.md +116 -0
  39. package/mcp/package.json +23 -0
  40. package/mcp/src/server.js +638 -0
  41. package/package.json +67 -0
  42. package/stacks/django.md +216 -0
  43. package/stacks/express.md +229 -0
  44. package/stacks/fastapi.md +247 -0
  45. package/stacks/nextjs.md +198 -0
  46. package/stacks/nodejs.md +28 -0
  47. package/stacks/rails.md +247 -0
  48. package/warp-workflows/README.md +25 -0
  49. package/warp-workflows/feature-kickoff.yaml +49 -0
  50. package/warp-workflows/pr-security-review.yaml +47 -0
  51. package/warp-workflows/release-gate.yaml +44 -0
  52. package/warp-workflows/sdlc-status.yaml +48 -0
  53. package/warp-workflows/threat-model.yaml +56 -0
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@kaademos/secure-sdlc",
3
+ "version": "1.0.0",
4
+ "description": "Secure SDLC agent team — CLI to scaffold docs, hooks, CI, and MCP-ready security workflows",
5
+ "type": "module",
6
+ "bin": {
7
+ "secure-sdlc": "./cli/bin/secure-sdlc.js"
8
+ },
9
+ "files": [
10
+ "cli/bin",
11
+ "cli/src",
12
+ "mcp/package.json",
13
+ "mcp/README.md",
14
+ "mcp/src",
15
+ "docs/templates",
16
+ "hooks",
17
+ "stacks",
18
+ "warp-workflows",
19
+ ".github/workflows/secure-sdlc-gate.yml",
20
+ ".cursor/rules",
21
+ ".claude/agents",
22
+ "LICENSE",
23
+ "README.md",
24
+ "CHANGELOG.md",
25
+ "CLAUDE.md"
26
+ ],
27
+ "scripts": {
28
+ "prepack": "node cli/bin/secure-sdlc.js --version",
29
+ "sdlc": "node cli/bin/secure-sdlc.js",
30
+ "test:pack": "npm pack --dry-run --ignore-scripts 2>&1"
31
+ },
32
+ "keywords": [
33
+ "security",
34
+ "sdlc",
35
+ "appsec",
36
+ "compliance",
37
+ "owasp",
38
+ "asvs",
39
+ "claude",
40
+ "cursor",
41
+ "mcp",
42
+ "ai-coding",
43
+ "secure-by-default"
44
+ ],
45
+ "license": "MIT",
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "git+https://github.com/Kaademos/secure-sdlc-agents.git"
49
+ },
50
+ "bugs": {
51
+ "url": "https://github.com/Kaademos/secure-sdlc-agents/issues"
52
+ },
53
+ "homepage": "https://github.com/Kaademos/secure-sdlc-agents#readme",
54
+ "engines": {
55
+ "node": ">=18.0.0"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
59
+ },
60
+ "dependencies": {
61
+ "@modelcontextprotocol/sdk": "^1.0.0",
62
+ "chalk": "^5.3.0",
63
+ "commander": "^12.0.0",
64
+ "inquirer": "^9.2.0",
65
+ "ora": "^8.0.0"
66
+ }
67
+ }
@@ -0,0 +1,216 @@
1
+ # Django Security Profile
2
+
3
+ **Framework:** Django (4.x / 5.x)
4
+ **Language:** Python 3.10+
5
+ **ASVS Baseline:** L2
6
+
7
+ ---
8
+
9
+ ## Django's Built-In Security — Don't Disable It
10
+
11
+ Django ships with more security defaults than most frameworks. The most common vulnerability
12
+ pattern in Django apps is **disabling or misconfiguring built-in protections**, not missing them.
13
+
14
+ ### Never disable these:
15
+
16
+ ```python
17
+ # settings.py — NEVER set these to False in production
18
+
19
+ # CSRF protection
20
+ MIDDLEWARE = [
21
+ ...
22
+ 'django.middleware.csrf.CsrfViewMiddleware', # Never remove
23
+ ...
24
+ ]
25
+
26
+ # Clickjacking protection
27
+ X_FRAME_OPTIONS = 'DENY' # Default; never change to 'ALLOWALL'
28
+
29
+ # Secure cookies
30
+ SESSION_COOKIE_SECURE = True # HTTPS only
31
+ CSRF_COOKIE_SECURE = True
32
+ SESSION_COOKIE_HTTPONLY = True # Prevents JS access to session cookie
33
+ ```
34
+
35
+ ### Production Settings Checklist
36
+
37
+ ```python
38
+ # settings.py — required for production
39
+
40
+ DEBUG = False # Non-negotiable
41
+ ALLOWED_HOSTS = ['yourdomain.com'] # Explicit, never ['*']
42
+ SECRET_KEY = env('SECRET_KEY') # From environment, never in code
43
+
44
+ # HTTPS enforcement
45
+ SECURE_SSL_REDIRECT = True
46
+ SECURE_HSTS_SECONDS = 63072000 # 2 years
47
+ SECURE_HSTS_INCLUDE_SUBDOMAINS = True
48
+ SECURE_HSTS_PRELOAD = True
49
+ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # If behind load balancer
50
+
51
+ # Session
52
+ SESSION_COOKIE_AGE = 3600 # 1 hour default; set appropriately
53
+ SESSION_EXPIRE_AT_BROWSER_CLOSE = True # Optional but good for sensitive apps
54
+
55
+ # Content security
56
+ SECURE_CONTENT_TYPE_NOSNIFF = True
57
+ SECURE_BROWSER_XSS_FILTER = True # Deprecated in modern browsers but harmless
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Authentication
63
+
64
+ Django's built-in auth is solid. Common mistakes:
65
+
66
+ ### Password Storage (Already Handled by Django)
67
+
68
+ Django uses PBKDF2 by default. For higher assurance, switch to Argon2:
69
+
70
+ ```python
71
+ # settings.py
72
+ PASSWORD_HASHERS = [
73
+ 'django.contrib.auth.hashers.Argon2PasswordHasher', # Best
74
+ 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', # Good
75
+ 'django.contrib.auth.hashers.PBKDF2PasswordHasher', # Default — acceptable
76
+ ]
77
+
78
+ # Minimum password validation
79
+ AUTH_PASSWORD_VALIDATORS = [
80
+ {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator'},
81
+ {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
82
+ 'OPTIONS': {'min_length': 12}},
83
+ {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator'},
84
+ {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator'},
85
+ ]
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Access Control — Django Views and DRF
91
+
92
+ ### Function-Based Views
93
+
94
+ ```python
95
+ from django.contrib.auth.decorators import login_required
96
+ from django.core.exceptions import PermissionDenied
97
+
98
+ @login_required # Redirects unauthenticated users to LOGIN_URL
99
+ def my_view(request):
100
+ # IDOR check: verify the requested object belongs to request.user
101
+ post = get_object_or_404(Post, pk=pk)
102
+ if post.author != request.user:
103
+ raise PermissionDenied # Returns 403
104
+ ...
105
+ ```
106
+
107
+ ### Django REST Framework
108
+
109
+ ```python
110
+ from rest_framework.permissions import IsAuthenticated
111
+ from rest_framework.views import APIView
112
+
113
+ class PostDetailView(APIView):
114
+ permission_classes = [IsAuthenticated]
115
+
116
+ def get(self, request, pk):
117
+ # ✓ Object-level permission check
118
+ post = get_object_or_404(Post, pk=pk)
119
+ if post.author != request.user:
120
+ return Response(status=403)
121
+ serializer = PostSerializer(post)
122
+ return Response(serializer.data)
123
+
124
+ # Or using DRF object-level permissions:
125
+ class IsOwner(BasePermission):
126
+ def has_object_permission(self, request, view, obj):
127
+ return obj.author == request.user
128
+ ```
129
+
130
+ **Never use `DEFAULT_AUTHENTICATION_CLASSES = []`** or **`DEFAULT_PERMISSION_CLASSES = []`** in
131
+ production DRF settings — these remove authentication and permission checks globally.
132
+
133
+ ---
134
+
135
+ ## ORM — Avoiding the Rare Django SQL Injection
136
+
137
+ Django's ORM is safe by default. Injection is only possible when using `.raw()` or `extra()`:
138
+
139
+ ```python
140
+ # ✓ Safe — ORM parameterises automatically
141
+ Post.objects.filter(title=user_input)
142
+
143
+ # ✗ Unsafe — direct string formatting in raw query
144
+ Post.objects.raw(f"SELECT * FROM posts WHERE title = '{user_input}'")
145
+
146
+ # ✓ Safe raw query — use %s parameterisation
147
+ Post.objects.raw("SELECT * FROM posts WHERE title = %s", [user_input])
148
+
149
+ # ✗ Unsafe extra()
150
+ Post.objects.extra(where=[f"title = '{user_input}'"])
151
+
152
+ # ✓ Safe extra()
153
+ Post.objects.extra(where=["title = %s"], params=[user_input])
154
+ ```
155
+
156
+ ---
157
+
158
+ ## CSRF for APIs
159
+
160
+ If you're building a REST API consumed by non-browser clients, configure CSRF correctly:
161
+
162
+ ```python
163
+ # For DRF APIs using session auth from a browser:
164
+ # Keep CSRF enabled — use CsrfExemptSessionAuthentication or the Django CSRF view decorator
165
+
166
+ # For DRF APIs using token auth (not cookies):
167
+ # CSRF protection is not needed — tokens in Authorization header are CSRF-safe by design
168
+
169
+ # For hybrid (some browser, some API clients):
170
+ # Exempt specific views using @csrf_exempt and compensate with other controls (CORS, Origin check)
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Secrets Management
176
+
177
+ ```python
178
+ # ✗ Never
179
+ SECRET_KEY = 'hardcoded-secret-key'
180
+ DATABASE_URL = 'postgresql://user:password@localhost/db'
181
+
182
+ # ✓ Use django-environ or python-decouple
183
+ import environ
184
+ env = environ.Env()
185
+ environ.Env.read_env() # Reads .env for local dev (never commit .env)
186
+
187
+ SECRET_KEY = env('SECRET_KEY')
188
+ DATABASE_URL = env('DATABASE_URL')
189
+ ```
190
+
191
+ ---
192
+
193
+ ## ASVS Controls for Django Projects
194
+
195
+ | ASVS Ref | Control | Django Implementation |
196
+ |----------|---------|----------------------|
197
+ | V2.1.1 | Password complexity | `AUTH_PASSWORD_VALIDATORS` |
198
+ | V3.3.1 | Session invalidation on logout | `django.contrib.auth.logout()` clears session |
199
+ | V4.1.1 | Auth on endpoints | `@login_required` / `permission_classes` |
200
+ | V4.2.1 | Object-level auth | Explicit ownership checks before returning objects |
201
+ | V5.3.4 | No SQL injection | Use ORM; avoid `.raw()` with user input |
202
+ | V14.4.1 | Security headers | Django SecurityMiddleware |
203
+ | V14.4.5 | CSRF | CsrfViewMiddleware (enabled by default) |
204
+
205
+ ---
206
+
207
+ ## Recommended Tools
208
+
209
+ | Category | Tool |
210
+ |----------|------|
211
+ | SAST | Bandit, Semgrep (Django rules) |
212
+ | DAST | OWASP ZAP |
213
+ | Dependency scan | pip-audit, Safety |
214
+ | Secrets | python-decouple, django-environ |
215
+ | 2FA | django-two-factor-auth, django-otp |
216
+ | Rate limiting | django-ratelimit, django-axes (login lockout) |
@@ -0,0 +1,229 @@
1
+ # Express.js Security Profile
2
+
3
+ **Framework:** Express.js 4.x / 5.x
4
+ **Language:** JavaScript / TypeScript (Node.js)
5
+ **ASVS Baseline:** L2
6
+
7
+ ---
8
+
9
+ ## Express has No Security Defaults — You Must Add Everything
10
+
11
+ Express is minimal by design. Unlike Django or Rails, it ships with no security headers, no CSRF
12
+ protection, no input validation, and no rate limiting. Every security control must be explicitly added.
13
+
14
+ ---
15
+
16
+ ## Minimum Required Security Middleware
17
+
18
+ Add these to every new Express application:
19
+
20
+ ```javascript
21
+ import express from 'express';
22
+ import helmet from 'helmet';
23
+ import { rateLimit } from 'express-rate-limit';
24
+ import cors from 'cors';
25
+ import { doubleCsrf } from 'csrf-csrf';
26
+
27
+ const app = express();
28
+
29
+ // 1. Security headers (CSP, HSTS, X-Frame-Options, etc.)
30
+ app.use(helmet({
31
+ contentSecurityPolicy: {
32
+ directives: {
33
+ defaultSrc: ["'self'"],
34
+ scriptSrc: ["'self'"],
35
+ styleSrc: ["'self'", "'unsafe-inline'"],
36
+ imgSrc: ["'self'", "data:", "https:"],
37
+ },
38
+ },
39
+ hsts: {
40
+ maxAge: 63072000,
41
+ includeSubDomains: true,
42
+ preload: true,
43
+ },
44
+ }));
45
+
46
+ // 2. Rate limiting — global baseline
47
+ const globalLimiter = rateLimit({
48
+ windowMs: 15 * 60 * 1000, // 15 minutes
49
+ max: 100,
50
+ standardHeaders: true,
51
+ legacyHeaders: false,
52
+ message: { error: 'Too many requests' },
53
+ });
54
+ app.use(globalLimiter);
55
+
56
+ // 3. Strict rate limiting for auth endpoints
57
+ const authLimiter = rateLimit({
58
+ windowMs: 15 * 60 * 1000,
59
+ max: 10, // Only 10 login attempts per 15 minutes per IP
60
+ skipSuccessfulRequests: true,
61
+ });
62
+ app.use('/auth', authLimiter);
63
+
64
+ // 4. CORS — explicit origins only
65
+ app.use(cors({
66
+ origin: process.env.ALLOWED_ORIGINS?.split(',') ?? [],
67
+ credentials: true,
68
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
69
+ }));
70
+
71
+ // 5. CSRF protection (for session-based auth)
72
+ const { doubleCsrfProtection } = doubleCsrf({
73
+ getSecret: () => process.env.CSRF_SECRET,
74
+ cookieName: '__Host-psifi.x-csrf-token',
75
+ cookieOptions: { secure: true, sameSite: 'strict' },
76
+ });
77
+ app.use(doubleCsrfProtection);
78
+
79
+ // 6. Body parsing with size limits
80
+ app.use(express.json({ limit: '10kb' })); // Prevent JSON body DoS
81
+ app.use(express.urlencoded({ extended: true, limit: '10kb' }));
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Authentication
87
+
88
+ Express has no built-in auth. Options:
89
+
90
+ ```javascript
91
+ // Using passport.js with bcrypt
92
+ import passport from 'passport';
93
+ import { Strategy as LocalStrategy } from 'passport-local';
94
+ import bcrypt from 'bcrypt';
95
+
96
+ passport.use(new LocalStrategy(async (username, password, done) => {
97
+ try {
98
+ const user = await User.findOne({ username });
99
+ if (!user) {
100
+ // ✓ Same error for wrong username OR wrong password (user enumeration prevention)
101
+ return done(null, false, { message: 'Invalid credentials' });
102
+ }
103
+
104
+ const isValid = await bcrypt.compare(password, user.passwordHash);
105
+ if (!isValid) {
106
+ return done(null, false, { message: 'Invalid credentials' });
107
+ }
108
+
109
+ return done(null, user);
110
+ } catch (err) {
111
+ return done(err);
112
+ }
113
+ }));
114
+
115
+ // Password hashing — minimum cost factor 12
116
+ const BCRYPT_ROUNDS = 12;
117
+ const hash = await bcrypt.hash(password, BCRYPT_ROUNDS);
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Input Validation with Zod
123
+
124
+ `req.body`, `req.params`, and `req.query` are **completely untyped** in Express. Validate everything:
125
+
126
+ ```typescript
127
+ import { z } from 'zod';
128
+ import type { Request, Response, NextFunction } from 'express';
129
+
130
+ const CreateUserSchema = z.object({
131
+ username: z.string().min(3).max(50).regex(/^[a-zA-Z0-9_]+$/),
132
+ email: z.string().email().max(255),
133
+ password: z.string().min(12).max(128),
134
+ });
135
+
136
+ // Reusable validation middleware factory
137
+ const validate = (schema: z.ZodSchema) => (req: Request, res: Response, next: NextFunction) => {
138
+ const result = schema.safeParse(req.body);
139
+ if (!result.success) {
140
+ return res.status(400).json({
141
+ error: 'Validation failed',
142
+ details: result.error.flatten().fieldErrors // Safe to return — no internal info
143
+ });
144
+ }
145
+ req.body = result.data; // Replace with validated/parsed data
146
+ next();
147
+ };
148
+
149
+ router.post('/users', validate(CreateUserSchema), async (req, res) => {
150
+ // req.body is now validated and typed
151
+ const { username, email, password } = req.body;
152
+ ...
153
+ });
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Error Handling — No Stack Trace Leakage
159
+
160
+ ```javascript
161
+ // ✗ Express default error handler sends the full error in development
162
+ // ✗ Custom handler that leaks details
163
+ app.use((err, req, res, next) => {
164
+ res.status(500).json({ error: err.message, stack: err.stack }); // Never in production
165
+ });
166
+
167
+ // ✓ Generic user response, detailed server-side logging
168
+ import { logger } from './logger';
169
+
170
+ app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
171
+ logger.error({
172
+ message: err.message,
173
+ stack: err.stack,
174
+ path: req.path,
175
+ method: req.method,
176
+ userId: req.user?.id ?? 'anonymous',
177
+ requestId: req.id,
178
+ });
179
+
180
+ res.status(500).json({ error: 'An internal error occurred' });
181
+ });
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Database Queries — Never String Concatenate
187
+
188
+ ```javascript
189
+ // Using pg (node-postgres)
190
+
191
+ // ✗ SQL injection
192
+ const user = await pool.query(`SELECT * FROM users WHERE id = ${userId}`);
193
+
194
+ // ✓ Parameterised query
195
+ const user = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);
196
+
197
+ // Using an ORM (Prisma, Drizzle) — safe by default
198
+ const user = await prisma.user.findUnique({ where: { id: userId } });
199
+ ```
200
+
201
+ ---
202
+
203
+ ## ASVS Controls for Express Projects
204
+
205
+ | ASVS Ref | Control | Express Implementation |
206
+ |----------|---------|----------------------|
207
+ | V4.1.1 | Auth middleware | passport.js + per-route authentication middleware |
208
+ | V4.2.1 | Object-level auth | Ownership check in every route handler |
209
+ | V5.1.3 | Input validation | Zod validation middleware |
210
+ | V13.2.5 | Rate limiting | express-rate-limit per endpoint |
211
+ | V14.4.1 | Security headers | helmet() |
212
+ | V14.4.5 | CSRF | csrf-csrf or csurf |
213
+
214
+ ---
215
+
216
+ ## Recommended Security Stack (2026)
217
+
218
+ | Category | Recommended |
219
+ |----------|-------------|
220
+ | Security headers | helmet |
221
+ | Rate limiting | express-rate-limit |
222
+ | CSRF | csrf-csrf |
223
+ | Auth | passport.js, express-jwt |
224
+ | Input validation | zod, express-validator |
225
+ | Password hashing | bcrypt (min rounds: 12) |
226
+ | Session | express-session + connect-redis |
227
+ | ORM | Prisma, Drizzle ORM |
228
+ | Logging | pino (structured JSON) |
229
+ | Secrets | dotenv-vault, Doppler |