cc-codeconductor 0.2.1

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 (93) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +319 -0
  3. package/dist/index.js +12741 -0
  4. package/package.json +51 -0
  5. package/policy.yml +124 -0
  6. package/presets/claude/CLAUDE.md +598 -0
  7. package/presets/claude/commands/cc/feature.md +115 -0
  8. package/presets/claude/commands/cc/fix.md +121 -0
  9. package/presets/claude/commands/cc/refactor.md +148 -0
  10. package/presets/claude/commands/cc/review.md +126 -0
  11. package/presets/claude/commands/cc/tdd-cycle.md +226 -0
  12. package/presets/claude/commands/cc/test-plan.md +138 -0
  13. package/presets/claude/settings.json +37 -0
  14. package/presets/claude/skills/api-versioning/SKILL.md +389 -0
  15. package/presets/claude/skills/django-orm/SKILL.md +455 -0
  16. package/presets/claude/skills/django-testing/SKILL.md +409 -0
  17. package/presets/claude/skills/jpa-postgres/SKILL.md +618 -0
  18. package/presets/claude/skills/python/SKILL.md +606 -0
  19. package/presets/claude/skills/python-django-stack/SKILL.md +492 -0
  20. package/presets/claude/skills/python-fastapi-stack/SKILL.md +457 -0
  21. package/presets/claude/skills/spring-boot-feature/SKILL.md +558 -0
  22. package/presets/claude/skills/spring-boot-kotlin/SKILL.md +400 -0
  23. package/presets/claude/skills/sqlalchemy/SKILL.md +466 -0
  24. package/presets/claude/skills/testing-strategy/SKILL.md +479 -0
  25. package/presets/codex/AGENTS.md +883 -0
  26. package/presets/codex/README.md +102 -0
  27. package/presets/codex/skills/api-versioning/SKILL.md +389 -0
  28. package/presets/codex/skills/django-orm/SKILL.md +455 -0
  29. package/presets/codex/skills/django-testing/SKILL.md +409 -0
  30. package/presets/codex/skills/jpa-postgres/SKILL.md +618 -0
  31. package/presets/codex/skills/python/SKILL.md +606 -0
  32. package/presets/codex/skills/python-django-stack/SKILL.md +492 -0
  33. package/presets/codex/skills/python-fastapi-stack/SKILL.md +457 -0
  34. package/presets/codex/skills/spring-boot-feature/SKILL.md +558 -0
  35. package/presets/codex/skills/spring-boot-kotlin/SKILL.md +400 -0
  36. package/presets/codex/skills/sqlalchemy/SKILL.md +466 -0
  37. package/presets/codex/skills/testing-strategy/SKILL.md +479 -0
  38. package/presets/opencode/README.md +191 -0
  39. package/presets/opencode/agents/architect.md +133 -0
  40. package/presets/opencode/agents/docs.md +113 -0
  41. package/presets/opencode/agents/implementer.md +116 -0
  42. package/presets/opencode/agents/orchestrator.md +248 -0
  43. package/presets/opencode/agents/repo-explorer.md +119 -0
  44. package/presets/opencode/agents/reviewer.md +130 -0
  45. package/presets/opencode/agents/task-coach.md +103 -0
  46. package/presets/opencode/agents/tester.md +269 -0
  47. package/presets/opencode/commands/cc-feature.md +114 -0
  48. package/presets/opencode/commands/cc-fix.md +123 -0
  49. package/presets/opencode/commands/cc-refactor.md +148 -0
  50. package/presets/opencode/commands/cc-review.md +141 -0
  51. package/presets/opencode/commands/cc-tdd-cycle.md +225 -0
  52. package/presets/opencode/commands/cc-test-plan.md +144 -0
  53. package/presets/opencode/opencode.jsonc +89 -0
  54. package/presets/opencode/prompts/v0.1.0/architect.md +213 -0
  55. package/presets/opencode/prompts/v0.1.0/docs.md +181 -0
  56. package/presets/opencode/prompts/v0.1.0/implementer.md +154 -0
  57. package/presets/opencode/prompts/v0.1.0/orchestrator.md +169 -0
  58. package/presets/opencode/prompts/v0.1.0/repo-explorer.md +102 -0
  59. package/presets/opencode/prompts/v0.1.0/reviewer.md +183 -0
  60. package/presets/opencode/prompts/v0.1.0/task-coach.md +142 -0
  61. package/presets/opencode/prompts/v0.1.0/tester.md +160 -0
  62. package/presets/opencode/prompts/v0.2.0/architect.md +219 -0
  63. package/presets/opencode/prompts/v0.2.0/docs.md +187 -0
  64. package/presets/opencode/prompts/v0.2.0/implementer.md +160 -0
  65. package/presets/opencode/prompts/v0.2.0/orchestrator.md +238 -0
  66. package/presets/opencode/prompts/v0.2.0/repo-explorer.md +108 -0
  67. package/presets/opencode/prompts/v0.2.0/reviewer.md +190 -0
  68. package/presets/opencode/prompts/v0.2.0/task-coach.md +153 -0
  69. package/presets/opencode/prompts/v0.2.0/tester.md +249 -0
  70. package/presets/opencode/skills/api-versioning/SKILL.md +388 -0
  71. package/presets/opencode/skills/astro/SKILL.md +313 -0
  72. package/presets/opencode/skills/code-review/SKILL.md +202 -0
  73. package/presets/opencode/skills/django-orm/SKILL.md +455 -0
  74. package/presets/opencode/skills/django-testing/SKILL.md +408 -0
  75. package/presets/opencode/skills/django-uv/SKILL.md +400 -0
  76. package/presets/opencode/skills/jpa-postgres/SKILL.md +617 -0
  77. package/presets/opencode/skills/nextjs-typescript/SKILL.md +385 -0
  78. package/presets/opencode/skills/python/SKILL.md +605 -0
  79. package/presets/opencode/skills/python-django-stack/SKILL.md +491 -0
  80. package/presets/opencode/skills/python-fastapi-stack/SKILL.md +456 -0
  81. package/presets/opencode/skills/security/SKILL.md +376 -0
  82. package/presets/opencode/skills/spring-boot-feature/SKILL.md +557 -0
  83. package/presets/opencode/skills/spring-boot-kotlin/SKILL.md +399 -0
  84. package/presets/opencode/skills/spring-boot-testing-strategy/SKILL.md +470 -0
  85. package/presets/opencode/skills/sqlalchemy/SKILL.md +465 -0
  86. package/presets/opencode/skills/testing-tdd/SKILL.md +586 -0
  87. package/src/presets/council/council.yml +59 -0
  88. package/src/presets/manifests/claude.yml +23 -0
  89. package/src/presets/manifests/codex.yml +12 -0
  90. package/src/presets/manifests/opencode.yml +18 -0
  91. package/src/presets/models/claude.yml +37 -0
  92. package/src/presets/models/codex.yml +38 -0
  93. package/src/presets/models/opencode.yml +37 -0
@@ -0,0 +1,376 @@
1
+ ---
2
+ id: security
3
+ version: 1.0.0
4
+ name: Security
5
+ description: >
6
+ Provides expert knowledge for implementing and reviewing application security following OWASP Top 10 (2021), with stack-specific patterns for Spring Boot, Django, Next.js, and Astro.
7
+
8
+ compatibility:
9
+ tools: [claude, codex, opencode]
10
+ stacks:
11
+ languages: []
12
+ frameworks: []
13
+
14
+ risk:
15
+ level: high
16
+ can_execute_shell: false
17
+ can_modify_files: true
18
+ requires_network: false
19
+
20
+ inputs: []
21
+
22
+ outputs: []
23
+
24
+ quality:
25
+ reviewed_by: codeconductor-core
26
+ version: 0.1.0
27
+ ---
28
+
29
+
30
+
31
+ # Security
32
+
33
+ ## OWASP Top 10 (2021) — Quick Reference
34
+
35
+ | # | Category | What to check |
36
+ |---|----------|--------------|
37
+ | A01 | Broken Access Control | Is authorization enforced on every endpoint? |
38
+ | A02 | Cryptographic Failures | Are secrets encrypted at rest and in transit? |
39
+ | A03 | Injection | Is all input sanitized or parameterized? |
40
+ | A04 | Insecure Design | Is the threat model documented? |
41
+ | A05 | Security Misconfiguration | Are defaults changed? Debug off in prod? |
42
+ | A06 | Vulnerable Components | Are dependencies up to date? |
43
+ | A07 | Auth & Session Failures | Are sessions invalidated on logout? Token expiry set? |
44
+ | A08 | Software Integrity Failures | Are supply-chain dependencies verified? |
45
+ | A09 | Logging & Monitoring Failures | Are security events logged without leaking PII? |
46
+ | A10 | SSRF | Are outbound URLs allowlisted? |
47
+
48
+ ## Input Validation
49
+
50
+ Validate all input at system boundaries. Never trust data from: HTTP request
51
+ body, query parameters, headers, cookies, or environment variables passed by
52
+ external systems.
53
+
54
+ **Rules:**
55
+
56
+ - Validate type, format, length, and range
57
+ - Reject unknown fields — do not silently ignore them
58
+ - Use allowlists (accept known-good), not blocklists (reject known-bad)
59
+ - Validate at the API layer before it reaches the service layer
60
+
61
+ ```kotlin
62
+ // Spring Boot — Bean Validation
63
+ data class CreateUserRequest(
64
+ @field:NotBlank @field:Email val email: String,
65
+ @field:Size(min = 8, max = 72) val password: String,
66
+ @field:Size(max = 100) val name: String
67
+ )
68
+
69
+ @PostMapping
70
+ fun createUser(@Valid @RequestBody request: CreateUserRequest): ResponseEntity<UserResponse> {
71
+ // request is guaranteed valid here
72
+ }
73
+ ```
74
+
75
+ ```python
76
+ # Django REST Framework — serializer validation
77
+ class CreateUserSerializer(serializers.Serializer):
78
+ email = serializers.EmailField(max_length=254)
79
+ password = serializers.CharField(min_length=8, max_length=72, write_only=True)
80
+ name = serializers.CharField(max_length=100)
81
+
82
+ def validate_email(self, value):
83
+ if User.objects.filter(email=value).exists():
84
+ raise serializers.ValidationError("Email already registered.")
85
+ return value.lower()
86
+ ```
87
+
88
+ ```typescript
89
+ // Next.js / Astro — Zod
90
+ import { z } from 'zod';
91
+
92
+ const CreateUserSchema = z.object({
93
+ email: z.string().email().max(254),
94
+ password: z.string().min(8).max(72),
95
+ name: z.string().max(100),
96
+ });
97
+
98
+ const result = CreateUserSchema.safeParse(body);
99
+ if (!result.success) {
100
+ return Response.json({ error: result.error.flatten() }, { status: 400 });
101
+ }
102
+ ```
103
+
104
+ ## Authentication
105
+
106
+ ### JWT (Stateless)
107
+
108
+ Use JWTs for stateless API authentication. Keep access tokens short-lived.
109
+
110
+ **Token lifetimes:**
111
+
112
+ | Token | Lifetime | Storage |
113
+ |-------|----------|---------|
114
+ | Access token | 15 minutes | Memory (never localStorage) |
115
+ | Refresh token | 7–30 days | HttpOnly cookie |
116
+
117
+ Never store access tokens in `localStorage` — XSS can read them. Store in
118
+ memory (React state, module variable) and refresh via the HttpOnly cookie.
119
+
120
+ ```kotlin
121
+ // Spring Security 6 + JWT
122
+ @Configuration
123
+ @EnableWebSecurity
124
+ class SecurityConfig(private val jwtFilter: JwtAuthenticationFilter) {
125
+
126
+ @Bean
127
+ fun filterChain(http: HttpSecurity): SecurityFilterChain = http
128
+ .csrf { it.disable() } // stateless API — CSRF not needed
129
+ .sessionManagement { it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) }
130
+ .authorizeHttpRequests { auth ->
131
+ auth
132
+ .requestMatchers("/api/auth/**").permitAll()
133
+ .requestMatchers("/api/admin/**").hasRole("ADMIN")
134
+ .anyRequest().authenticated()
135
+ }
136
+ .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter::class.java)
137
+ .build()
138
+ }
139
+ ```
140
+
141
+ ```python
142
+ # Django — djangorestframework-simplejwt
143
+ # settings.py
144
+ SIMPLE_JWT = {
145
+ "ACCESS_TOKEN_LIFETIME": timedelta(minutes=15),
146
+ "REFRESH_TOKEN_LIFETIME": timedelta(days=7),
147
+ "ROTATE_REFRESH_TOKENS": True,
148
+ "BLACKLIST_AFTER_ROTATION": True,
149
+ "ALGORITHM": "HS256",
150
+ "AUTH_HEADER_TYPES": ("Bearer",),
151
+ }
152
+ ```
153
+
154
+ ```typescript
155
+ // Next.js — Auth.js (NextAuth v5)
156
+ // auth.ts
157
+ import NextAuth from 'next-auth';
158
+ import Credentials from 'next-auth/providers/credentials';
159
+
160
+ export const { auth, signIn, signOut } = NextAuth({
161
+ providers: [
162
+ Credentials({
163
+ authorize: async (credentials) => {
164
+ // validate and return user or null
165
+ },
166
+ }),
167
+ ],
168
+ session: { strategy: 'jwt', maxAge: 7 * 24 * 60 * 60 },
169
+ });
170
+ ```
171
+
172
+ ### Password Hashing
173
+
174
+ Never store plaintext passwords. Never use MD5 or SHA-1 for password hashing.
175
+
176
+ | Algorithm | Use | Work factor |
177
+ |-----------|-----|-------------|
178
+ | bcrypt | default | cost=12 |
179
+ | Argon2id | preferred when available | m=65536, t=3 |
180
+ | PBKDF2 | FIPS environments | iterations=600000 |
181
+
182
+ ```kotlin
183
+ // Spring Security uses bcrypt by default
184
+ @Bean
185
+ fun passwordEncoder(): PasswordEncoder = BCryptPasswordEncoder(12)
186
+ ```
187
+
188
+ ```python
189
+ # Django uses PBKDF2 by default; switch to Argon2 for stronger hashing
190
+ # pip install django[argon2]
191
+ PASSWORD_HASHERS = [
192
+ "django.contrib.auth.hashers.Argon2PasswordHasher",
193
+ "django.contrib.auth.hashers.PBKDF2PasswordHasher", # fallback for existing hashes
194
+ ]
195
+ ```
196
+
197
+ ## Authorization
198
+
199
+ **Principle of least privilege:** every endpoint must explicitly declare who
200
+ can access it. Deny by default.
201
+
202
+ ```kotlin
203
+ // Spring — method-level security
204
+ @PreAuthorize("hasRole('ADMIN') or #userId == authentication.name")
205
+ fun getUser(userId: String): UserResponse { ... }
206
+ ```
207
+
208
+ ```python
209
+ # Django REST Framework
210
+ class UserDetailView(RetrieveUpdateAPIView):
211
+ permission_classes = [IsAuthenticated, IsOwnerOrAdmin]
212
+ queryset = User.objects.all()
213
+
214
+ class IsOwnerOrAdmin(BasePermission):
215
+ def has_object_permission(self, request, view, obj):
216
+ return request.user.is_staff or obj == request.user
217
+ ```
218
+
219
+ Always check authorization at the **object level**, not just the endpoint
220
+ level. An authenticated user should not be able to access another user's data
221
+ by changing an ID in the URL.
222
+
223
+ ## CORS
224
+
225
+ ```kotlin
226
+ // Spring Boot — explicit allowlist; never use "*" in production
227
+ @Bean
228
+ fun corsConfigurationSource(): CorsConfigurationSource {
229
+ val config = CorsConfiguration().apply {
230
+ allowedOrigins = listOf("https://app.example.com")
231
+ allowedMethods = listOf("GET", "POST", "PUT", "DELETE")
232
+ allowedHeaders = listOf("Authorization", "Content-Type")
233
+ allowCredentials = true
234
+ maxAge = 3600L
235
+ }
236
+ return UrlBasedCorsConfigurationSource().apply {
237
+ registerCorsConfiguration("/api/**", config)
238
+ }
239
+ }
240
+ ```
241
+
242
+ ```python
243
+ # Django — django-cors-headers
244
+ CORS_ALLOWED_ORIGINS = [
245
+ "https://app.example.com",
246
+ ]
247
+ CORS_ALLOW_CREDENTIALS = True
248
+ CORS_ALLOW_ALL_ORIGINS = False # never True in production
249
+ ```
250
+
251
+ ## Security Headers
252
+
253
+ Set these headers on every response. They defend against XSS, clickjacking,
254
+ and MIME sniffing.
255
+
256
+ ```text
257
+ Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
258
+ Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'
259
+ X-Frame-Options: DENY
260
+ X-Content-Type-Options: nosniff
261
+ Referrer-Policy: strict-origin-when-cross-origin
262
+ Permissions-Policy: camera=(), microphone=(), geolocation=()
263
+ ```
264
+
265
+ ```typescript
266
+ // Next.js — next.config.ts
267
+ const securityHeaders = [
268
+ { key: 'X-Frame-Options', value: 'DENY' },
269
+ { key: 'X-Content-Type-Options', value: 'nosniff' },
270
+ { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
271
+ {
272
+ key: 'Strict-Transport-Security',
273
+ value: 'max-age=63072000; includeSubDomains; preload',
274
+ },
275
+ ];
276
+
277
+ export default {
278
+ headers: async () => [
279
+ { source: '/(.*)', headers: securityHeaders },
280
+ ],
281
+ };
282
+ ```
283
+
284
+ ## Secrets Management
285
+
286
+ **Never commit secrets to source control.** No exceptions.
287
+
288
+ ```text
289
+ ✗ DATABASE_URL = "postgres://user:password@prod.example.com/db" # in source
290
+ ✓ DATABASE_URL = env("DATABASE_URL") # from environment
291
+ ```
292
+
293
+ Rules:
294
+
295
+ - Load secrets from environment variables or a secrets manager (AWS Secrets
296
+ Manager, Vault, GCP Secret Manager)
297
+ - Rotate credentials on any suspected exposure
298
+ - Use separate credentials per environment (dev, staging, prod)
299
+ - Add `.env` to `.gitignore` and never commit it
300
+
301
+ ```python
302
+ # Django — django-environ
303
+ import environ
304
+ env = environ.Env()
305
+ environ.Env.read_env() # reads .env in development only
306
+
307
+ SECRET_KEY = env("SECRET_KEY")
308
+ DATABASE_URL = env.db("DATABASE_URL")
309
+ ```
310
+
311
+ ## Rate Limiting
312
+
313
+ Protect authentication endpoints. Brute-force without rate limiting can
314
+ compromise any account with a weak password.
315
+
316
+ ```kotlin
317
+ // Spring Boot — Bucket4j
318
+ @Component
319
+ class RateLimitFilter(private val rateLimiter: RateLimiter) : OncePerRequestFilter() {
320
+
321
+ override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) {
322
+ if (request.requestURI.startsWith("/api/auth/")) {
323
+ val probe = rateLimiter.tryConsumeAndReturnRemaining(1)
324
+ if (!probe.isConsumed) {
325
+ response.status = HttpStatus.TOO_MANY_REQUESTS.value()
326
+ return
327
+ }
328
+ }
329
+ chain.doFilter(request, response)
330
+ }
331
+ }
332
+ ```
333
+
334
+ Minimum rate limits for auth endpoints: 5 requests per minute per IP. Stricter
335
+ limits (e.g., 3 per 15 minutes) are better for login endpoints.
336
+
337
+ ## SQL Injection Prevention
338
+
339
+ Never concatenate user input into SQL strings. Always use parameterized queries
340
+ or an ORM.
341
+
342
+ ```kotlin
343
+ // bad — SQL injection risk
344
+ val user = jdbcTemplate.queryForObject("SELECT * FROM users WHERE email = '$email'")
345
+
346
+ // good — parameterized
347
+ val user = jdbcTemplate.queryForObject(
348
+ "SELECT * FROM users WHERE email = ?",
349
+ UserRowMapper(),
350
+ email
351
+ )
352
+ ```
353
+
354
+ ```python
355
+ # bad
356
+ User.objects.raw(f"SELECT * FROM users WHERE email = '{email}'")
357
+
358
+ # good — Django ORM handles parameterization automatically
359
+ User.objects.filter(email=email)
360
+ ```
361
+
362
+ ## Logging Security Events
363
+
364
+ Log authentication events without including sensitive data.
365
+
366
+ ```text
367
+ Log: login success/failure, permission denied, password reset, token refresh
368
+ Never: passwords, tokens, PII (full email in prod logs), credit card numbers
369
+ ```
370
+
371
+ ```kotlin
372
+ // Log security events at INFO level; failures at WARN
373
+ log.info("Authentication successful for user={}", userId)
374
+ log.warn("Authentication failed: invalid credentials for user={}", maskedEmail)
375
+ log.warn("Authorization denied: user={} attempted access to resource={}", userId, resourceId)
376
+ ```