@aicgen/aicgen 1.0.0-beta.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.
- package/.claude/agents/architecture-reviewer.md +88 -0
- package/.claude/agents/guideline-checker.md +73 -0
- package/.claude/agents/security-auditor.md +108 -0
- package/.claude/guidelines/api-design.md +645 -0
- package/.claude/guidelines/architecture.md +2503 -0
- package/.claude/guidelines/best-practices.md +618 -0
- package/.claude/guidelines/code-style.md +304 -0
- package/.claude/guidelines/design-patterns.md +573 -0
- package/.claude/guidelines/devops.md +226 -0
- package/.claude/guidelines/error-handling.md +413 -0
- package/.claude/guidelines/language.md +782 -0
- package/.claude/guidelines/performance.md +706 -0
- package/.claude/guidelines/security.md +583 -0
- package/.claude/guidelines/testing.md +568 -0
- package/.claude/settings.json +98 -0
- package/.claude/settings.local.json +8 -0
- package/.env.example +23 -0
- package/.eslintrc.json +28 -0
- package/.github/workflows/release.yml +180 -0
- package/.github/workflows/test.yml +81 -0
- package/.gitmodules +3 -0
- package/.vs/ProjectSettings.json +3 -0
- package/.vs/VSWorkspaceState.json +16 -0
- package/.vs/aicgen.slnx/FileContentIndex/5f0ce2a3-fd68-4863-9e23-e428cf1794e3.vsidx +0 -0
- package/.vs/aicgen.slnx/v18/.wsuo +0 -0
- package/.vs/aicgen.slnx/v18/DocumentLayout.json +54 -0
- package/.vs/slnx.sqlite +0 -0
- package/AGENTS.md +121 -0
- package/CLAUDE.md +36 -0
- package/CONTRIBUTING.md +821 -0
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/assets/icon.svg +34 -0
- package/assets/logo.svg +41 -0
- package/bun.lock +848 -0
- package/data/LICENSE +21 -0
- package/data/README.md +203 -0
- package/data/api/basics.md +292 -0
- package/data/api/index.md +8 -0
- package/data/api/pagination.md +142 -0
- package/data/api/rest.md +137 -0
- package/data/api/versioning.md +60 -0
- package/data/architecture/clean-architecture/index.md +7 -0
- package/data/architecture/clean-architecture/layers.md +111 -0
- package/data/architecture/ddd/index.md +8 -0
- package/data/architecture/ddd/strategic.md +89 -0
- package/data/architecture/ddd/tactical.md +132 -0
- package/data/architecture/event-driven/index.md +7 -0
- package/data/architecture/event-driven/messaging.md +242 -0
- package/data/architecture/event-driven/patterns.md +129 -0
- package/data/architecture/feature-toggles/index.md +7 -0
- package/data/architecture/feature-toggles/patterns.md +73 -0
- package/data/architecture/gui/index.md +7 -0
- package/data/architecture/gui/patterns.md +132 -0
- package/data/architecture/hexagonal/ports-adapters.md +132 -0
- package/data/architecture/index.md +12 -0
- package/data/architecture/layered/index.md +7 -0
- package/data/architecture/layered/layers.md +100 -0
- package/data/architecture/microservices/api-gateway.md +56 -0
- package/data/architecture/microservices/boundaries.md +80 -0
- package/data/architecture/microservices/communication.md +97 -0
- package/data/architecture/microservices/data.md +92 -0
- package/data/architecture/microservices/index.md +11 -0
- package/data/architecture/microservices/resilience.md +111 -0
- package/data/architecture/modular-monolith/boundaries.md +133 -0
- package/data/architecture/modular-monolith/structure.md +131 -0
- package/data/architecture/serverless/best-practices.md +322 -0
- package/data/architecture/serverless/index.md +7 -0
- package/data/architecture/serverless/patterns.md +80 -0
- package/data/architecture/solid/index.md +7 -0
- package/data/architecture/solid/principles.md +187 -0
- package/data/database/basics.md +365 -0
- package/data/database/design-patterns.md +68 -0
- package/data/database/index.md +8 -0
- package/data/database/indexing.md +136 -0
- package/data/database/nosql.md +223 -0
- package/data/database/schema.md +137 -0
- package/data/devops/ci-cd.md +66 -0
- package/data/devops/index.md +8 -0
- package/data/devops/observability.md +73 -0
- package/data/devops/practices.md +77 -0
- package/data/error-handling/basics.md +222 -0
- package/data/error-handling/index.md +7 -0
- package/data/error-handling/strategy.md +185 -0
- package/data/guideline-mappings.yml +1077 -0
- package/data/index.md +3 -0
- package/data/language/csharp/basics.md +210 -0
- package/data/language/csharp/testing.md +252 -0
- package/data/language/go/basics.md +158 -0
- package/data/language/go/testing.md +192 -0
- package/data/language/index.md +14 -0
- package/data/language/java/basics.md +184 -0
- package/data/language/java/testing.md +273 -0
- package/data/language/javascript/basics.md +217 -0
- package/data/language/javascript/testing.md +269 -0
- package/data/language/python/async.md +100 -0
- package/data/language/python/basics.md +100 -0
- package/data/language/python/index.md +10 -0
- package/data/language/python/testing.md +125 -0
- package/data/language/python/types.md +99 -0
- package/data/language/ruby/basics.md +227 -0
- package/data/language/ruby/testing.md +267 -0
- package/data/language/rust/basics.md +175 -0
- package/data/language/rust/testing.md +219 -0
- package/data/language/typescript/async.md +103 -0
- package/data/language/typescript/basics.md +87 -0
- package/data/language/typescript/config.md +95 -0
- package/data/language/typescript/error-handling.md +98 -0
- package/data/language/typescript/generics.md +85 -0
- package/data/language/typescript/index.md +14 -0
- package/data/language/typescript/interfaces-types.md +83 -0
- package/data/language/typescript/performance.md +103 -0
- package/data/language/typescript/testing.md +98 -0
- package/data/patterns/base-patterns.md +105 -0
- package/data/patterns/concurrency.md +87 -0
- package/data/patterns/data-access.md +83 -0
- package/data/patterns/distribution.md +86 -0
- package/data/patterns/domain-logic.md +81 -0
- package/data/patterns/gof.md +109 -0
- package/data/patterns/index.md +12 -0
- package/data/performance/async.md +148 -0
- package/data/performance/basics.md +324 -0
- package/data/performance/caching-strategies.md +68 -0
- package/data/performance/caching.md +152 -0
- package/data/performance/index.md +8 -0
- package/data/practices/code-review.md +52 -0
- package/data/practices/documentation.md +260 -0
- package/data/practices/index.md +11 -0
- package/data/practices/planning.md +142 -0
- package/data/practices/refactoring.md +91 -0
- package/data/practices/version-control.md +55 -0
- package/data/security/auth-jwt.md +159 -0
- package/data/security/headers.md +143 -0
- package/data/security/index.md +10 -0
- package/data/security/injection.md +119 -0
- package/data/security/secrets.md +148 -0
- package/data/style/index.md +8 -0
- package/data/style/naming.md +136 -0
- package/data/style/organization.md +162 -0
- package/data/templates/agents/architecture-reviewer.md +88 -0
- package/data/templates/agents/guideline-checker.md +73 -0
- package/data/templates/agents/security-auditor.md +108 -0
- package/data/templates/antigravity/rules/architecture.md.hbs +5 -0
- package/data/templates/antigravity/rules/code-style.md.hbs +5 -0
- package/data/templates/antigravity/rules/language.md.hbs +5 -0
- package/data/templates/antigravity/rules/performance.md.hbs +5 -0
- package/data/templates/antigravity/rules/security.md.hbs +5 -0
- package/data/templates/antigravity/rules/testing.md.hbs +5 -0
- package/data/templates/antigravity/workflows/add-documentation.md.hbs +23 -0
- package/data/templates/antigravity/workflows/generate-integration-tests.md.hbs +17 -0
- package/data/templates/antigravity/workflows/generate-unit-tests.md.hbs +20 -0
- package/data/templates/antigravity/workflows/performance-audit.md.hbs +24 -0
- package/data/templates/antigravity/workflows/refactor-extract-module.md.hbs +17 -0
- package/data/templates/antigravity/workflows/security-audit.md.hbs +20 -0
- package/data/templates/hooks/formatting.json +26 -0
- package/data/templates/hooks/security.json +35 -0
- package/data/templates/hooks/testing.json +17 -0
- package/data/testing/basics.md +151 -0
- package/data/testing/index.md +9 -0
- package/data/testing/integration.md +159 -0
- package/data/testing/unit-fundamentals.md +128 -0
- package/data/testing/unit-mocking.md +116 -0
- package/data/version.json +49 -0
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +46 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/config/profiles.d.ts +4 -0
- package/dist/config/profiles.d.ts.map +1 -0
- package/dist/config/profiles.js +30 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/settings.d.ts +7 -0
- package/dist/config/settings.d.ts.map +1 -0
- package/dist/config/settings.js +7 -0
- package/dist/config/settings.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58489 -0
- package/dist/index.js.map +1 -0
- package/dist/models/guideline.d.ts +15 -0
- package/dist/models/guideline.d.ts.map +1 -0
- package/dist/models/guideline.js +2 -0
- package/dist/models/guideline.js.map +1 -0
- package/dist/models/preference.d.ts +9 -0
- package/dist/models/preference.d.ts.map +1 -0
- package/dist/models/preference.js +2 -0
- package/dist/models/preference.js.map +1 -0
- package/dist/models/profile.d.ts +9 -0
- package/dist/models/profile.d.ts.map +1 -0
- package/dist/models/profile.js +2 -0
- package/dist/models/profile.js.map +1 -0
- package/dist/models/project.d.ts +13 -0
- package/dist/models/project.d.ts.map +1 -0
- package/dist/models/project.js +2 -0
- package/dist/models/project.js.map +1 -0
- package/dist/services/ai/anthropic.d.ts +7 -0
- package/dist/services/ai/anthropic.d.ts.map +1 -0
- package/dist/services/ai/anthropic.js +39 -0
- package/dist/services/ai/anthropic.js.map +1 -0
- package/dist/services/generator.d.ts +2 -0
- package/dist/services/generator.d.ts.map +1 -0
- package/dist/services/generator.js +4 -0
- package/dist/services/generator.js.map +1 -0
- package/dist/services/learner.d.ts +2 -0
- package/dist/services/learner.d.ts.map +1 -0
- package/dist/services/learner.js +4 -0
- package/dist/services/learner.js.map +1 -0
- package/dist/services/scanner.d.ts +3 -0
- package/dist/services/scanner.d.ts.map +1 -0
- package/dist/services/scanner.js +54 -0
- package/dist/services/scanner.js.map +1 -0
- package/dist/utils/errors.d.ts +15 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +27 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/file.d.ts +7 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +32 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +17 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/path.d.ts +6 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +14 -0
- package/dist/utils/path.js.map +1 -0
- package/docs/planning/memory-lane.md +83 -0
- package/package.json +64 -0
- package/packaging/linux/aicgen.spec +23 -0
- package/packaging/linux/control +9 -0
- package/packaging/macos/scripts/postinstall +12 -0
- package/packaging/windows/setup.nsi +92 -0
- package/planning/BRANDING-SUMMARY.md +194 -0
- package/planning/BRANDING.md +174 -0
- package/planning/BUILD.md +186 -0
- package/planning/CHUNK-IMPLEMENTATION-PLAN.md +87 -0
- package/planning/CHUNK-TAXONOMY.md +375 -0
- package/planning/CHUNKS-COMPLETE.md +382 -0
- package/planning/DESIGN.md +313 -0
- package/planning/DYNAMIC-GUIDELINES-DESIGN.md +265 -0
- package/planning/ENTERPRISE-UX-COMPLETE.md +281 -0
- package/planning/IMPLEMENTATION-PLAN.md +20 -0
- package/planning/PHASE1-COMPLETE.md +211 -0
- package/planning/PHASE2-COMPLETE.md +350 -0
- package/planning/PHASE3-COMPLETE.md +399 -0
- package/planning/PHASE4-COMPLETE.md +361 -0
- package/planning/PHASE4.5-CHUNKS.md +462 -0
- package/planning/STRUCTURE.md +170 -0
- package/scripts/add-categories.ts +87 -0
- package/scripts/build-binary.ts +46 -0
- package/scripts/embed-data.ts +105 -0
- package/scripts/generate-version.ts +150 -0
- package/scripts/test-decompress.ts +27 -0
- package/scripts/test-extract.ts +31 -0
- package/src/__tests__/services/assistant-file-writer.test.ts +400 -0
- package/src/__tests__/services/guideline-loader.test.ts +281 -0
- package/src/__tests__/services/tarball-extraction.test.ts +125 -0
- package/src/commands/add-guideline.ts +296 -0
- package/src/commands/clear.ts +61 -0
- package/src/commands/guideline-selector.ts +123 -0
- package/src/commands/init.ts +645 -0
- package/src/commands/quick-add.ts +586 -0
- package/src/commands/remove-guideline.ts +152 -0
- package/src/commands/stats.ts +49 -0
- package/src/commands/update.ts +240 -0
- package/src/config.ts +82 -0
- package/src/embedded-data.ts +1492 -0
- package/src/index.ts +67 -0
- package/src/models/profile.ts +24 -0
- package/src/models/project.ts +43 -0
- package/src/services/assistant-file-writer.ts +612 -0
- package/src/services/config-generator.ts +150 -0
- package/src/services/config-manager.ts +70 -0
- package/src/services/data-source.ts +248 -0
- package/src/services/first-run-init.ts +148 -0
- package/src/services/guideline-loader.ts +311 -0
- package/src/services/hook-generator.ts +178 -0
- package/src/services/subagent-generator.ts +310 -0
- package/src/utils/banner.ts +66 -0
- package/src/utils/errors.ts +27 -0
- package/src/utils/file.ts +67 -0
- package/src/utils/formatting.ts +172 -0
- package/src/utils/logger.ts +89 -0
- package/src/utils/path.ts +17 -0
- package/src/utils/wizard-state.ts +132 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Security Headers
|
|
2
|
+
|
|
3
|
+
## Essential Headers with Helmet
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import helmet from 'helmet';
|
|
7
|
+
|
|
8
|
+
// ✅ Apply security headers with sensible defaults
|
|
9
|
+
app.use(helmet());
|
|
10
|
+
|
|
11
|
+
// ✅ Custom configuration
|
|
12
|
+
app.use(helmet({
|
|
13
|
+
contentSecurityPolicy: {
|
|
14
|
+
directives: {
|
|
15
|
+
defaultSrc: ["'self'"],
|
|
16
|
+
scriptSrc: ["'self'", "'unsafe-inline'"],
|
|
17
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
18
|
+
imgSrc: ["'self'", "data:", "https:"],
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
hsts: {
|
|
22
|
+
maxAge: 31536000,
|
|
23
|
+
includeSubDomains: true,
|
|
24
|
+
preload: true
|
|
25
|
+
}
|
|
26
|
+
}));
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Manual Header Configuration
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
app.use((req, res, next) => {
|
|
33
|
+
// Prevent MIME sniffing
|
|
34
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
35
|
+
|
|
36
|
+
// Prevent clickjacking
|
|
37
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
38
|
+
|
|
39
|
+
// XSS protection
|
|
40
|
+
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
41
|
+
|
|
42
|
+
// Force HTTPS
|
|
43
|
+
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
44
|
+
|
|
45
|
+
// Referrer policy
|
|
46
|
+
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
47
|
+
|
|
48
|
+
// Permissions policy
|
|
49
|
+
res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
|
|
50
|
+
|
|
51
|
+
next();
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Content Security Policy (CSP)
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// ✅ Strict CSP for maximum protection
|
|
59
|
+
res.setHeader('Content-Security-Policy', [
|
|
60
|
+
"default-src 'self'",
|
|
61
|
+
"script-src 'self'",
|
|
62
|
+
"style-src 'self' 'unsafe-inline'",
|
|
63
|
+
"img-src 'self' data: https:",
|
|
64
|
+
"font-src 'self'",
|
|
65
|
+
"connect-src 'self' https://api.example.com",
|
|
66
|
+
"frame-ancestors 'none'",
|
|
67
|
+
"form-action 'self'"
|
|
68
|
+
].join('; '));
|
|
69
|
+
|
|
70
|
+
// For APIs that don't serve HTML
|
|
71
|
+
res.setHeader('Content-Security-Policy', "default-src 'none'");
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## CORS Configuration
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import cors from 'cors';
|
|
78
|
+
|
|
79
|
+
// ✅ Configure CORS properly
|
|
80
|
+
app.use(cors({
|
|
81
|
+
origin: ['https://example.com', 'https://app.example.com'],
|
|
82
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
83
|
+
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
84
|
+
credentials: true,
|
|
85
|
+
maxAge: 86400 // Cache preflight for 24 hours
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
// ❌ Never use in production
|
|
89
|
+
app.use(cors({ origin: '*' })); // Allows any origin
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## HTTPS Enforcement
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// ✅ Redirect HTTP to HTTPS
|
|
96
|
+
app.use((req, res, next) => {
|
|
97
|
+
if (!req.secure && req.get('x-forwarded-proto') !== 'https') {
|
|
98
|
+
return res.redirect(301, `https://${req.hostname}${req.url}`);
|
|
99
|
+
}
|
|
100
|
+
next();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ✅ HSTS header (included in helmet)
|
|
104
|
+
res.setHeader(
|
|
105
|
+
'Strict-Transport-Security',
|
|
106
|
+
'max-age=31536000; includeSubDomains; preload'
|
|
107
|
+
);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Cookie Security
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// ✅ Secure cookie settings
|
|
114
|
+
app.use(session({
|
|
115
|
+
cookie: {
|
|
116
|
+
secure: true, // Only send over HTTPS
|
|
117
|
+
httpOnly: true, // Not accessible via JavaScript
|
|
118
|
+
sameSite: 'strict', // CSRF protection
|
|
119
|
+
maxAge: 30 * 60 * 1000
|
|
120
|
+
}
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
// ✅ Set secure cookies manually
|
|
124
|
+
res.cookie('token', value, {
|
|
125
|
+
httpOnly: true,
|
|
126
|
+
secure: process.env.NODE_ENV === 'production',
|
|
127
|
+
sameSite: 'strict',
|
|
128
|
+
maxAge: 3600000
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Header Checklist
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
✅ X-Content-Type-Options: nosniff
|
|
136
|
+
✅ X-Frame-Options: DENY
|
|
137
|
+
✅ X-XSS-Protection: 1; mode=block
|
|
138
|
+
✅ Strict-Transport-Security: max-age=31536000
|
|
139
|
+
✅ Content-Security-Policy: (appropriate policy)
|
|
140
|
+
✅ Referrer-Policy: strict-origin-when-cross-origin
|
|
141
|
+
✅ Permissions-Policy: restrict unused features
|
|
142
|
+
✅ Secure, HttpOnly, SameSite cookies
|
|
143
|
+
```
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Security Guidelines
|
|
2
|
+
|
|
3
|
+
This directory contains security best practices.
|
|
4
|
+
|
|
5
|
+
## Available Chunks
|
|
6
|
+
|
|
7
|
+
- **injection.md** - SQL injection, NoSQL injection, command injection prevention
|
|
8
|
+
- **auth-jwt.md** - Password storage, JWT, session management, authorization
|
|
9
|
+
- **secrets.md** - Environment variables, secret generation, CI/CD secrets
|
|
10
|
+
- **headers.md** - Security headers, CORS, HTTPS, CSP
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Injection Prevention
|
|
2
|
+
|
|
3
|
+
## SQL Injection Prevention
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// ❌ DANGEROUS: String concatenation
|
|
7
|
+
const getUserByEmail = async (email: string) => {
|
|
8
|
+
const query = `SELECT * FROM users WHERE email = '${email}'`;
|
|
9
|
+
// Input: ' OR '1'='1
|
|
10
|
+
// Result: SELECT * FROM users WHERE email = '' OR '1'='1'
|
|
11
|
+
return db.query(query);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// ✅ SAFE: Parameterized queries
|
|
15
|
+
const getUserByEmail = async (email: string) => {
|
|
16
|
+
return db.query('SELECT * FROM users WHERE email = ?', [email]);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// ✅ SAFE: Using ORM
|
|
20
|
+
const getUserByEmail = async (email: string) => {
|
|
21
|
+
return userRepository.findOne({ where: { email } });
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// ✅ SAFE: Query builder
|
|
25
|
+
const getUsers = async (minAge: number) => {
|
|
26
|
+
return db
|
|
27
|
+
.select('*')
|
|
28
|
+
.from('users')
|
|
29
|
+
.where('age', '>', minAge); // Automatically parameterized
|
|
30
|
+
};
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## NoSQL Injection Prevention
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// ❌ DANGEROUS: Accepting objects from user input
|
|
37
|
+
app.post('/login', (req, res) => {
|
|
38
|
+
const { username, password } = req.body;
|
|
39
|
+
// If password = {$gt: ""}, it bypasses password check!
|
|
40
|
+
db.users.findOne({ username, password });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// ✅ SAFE: Validate input types
|
|
44
|
+
app.post('/login', (req, res) => {
|
|
45
|
+
const { username, password } = req.body;
|
|
46
|
+
|
|
47
|
+
if (typeof username !== 'string' || typeof password !== 'string') {
|
|
48
|
+
throw new Error('Invalid input types');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
db.users.findOne({ username, password });
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Command Injection Prevention
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// ❌ DANGEROUS: Shell command with user input
|
|
59
|
+
const convertImage = async (filename: string) => {
|
|
60
|
+
exec(`convert ${filename} output.jpg`);
|
|
61
|
+
// Input: "file.png; rm -rf /"
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// ✅ SAFE: Use arrays, avoid shell
|
|
65
|
+
import { execFile } from 'child_process';
|
|
66
|
+
|
|
67
|
+
const convertImage = async (filename: string) => {
|
|
68
|
+
execFile('convert', [filename, 'output.jpg']);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// ✅ SAFE: Validate input against whitelist
|
|
72
|
+
const allowedFilename = /^[a-zA-Z0-9_-]+\.(png|jpg|gif)$/;
|
|
73
|
+
if (!allowedFilename.test(filename)) {
|
|
74
|
+
throw new Error('Invalid filename');
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Path Traversal Prevention
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
// ❌ DANGEROUS: Direct path usage
|
|
82
|
+
app.get('/files/:filename', (req, res) => {
|
|
83
|
+
res.sendFile(`/uploads/${req.params.filename}`);
|
|
84
|
+
// Input: ../../etc/passwd
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// ✅ SAFE: Validate and normalize path
|
|
88
|
+
import path from 'path';
|
|
89
|
+
|
|
90
|
+
app.get('/files/:filename', (req, res) => {
|
|
91
|
+
const safeName = path.basename(req.params.filename);
|
|
92
|
+
const filePath = path.join('/uploads', safeName);
|
|
93
|
+
const normalizedPath = path.normalize(filePath);
|
|
94
|
+
|
|
95
|
+
if (!normalizedPath.startsWith('/uploads/')) {
|
|
96
|
+
return res.status(400).json({ error: 'Invalid filename' });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
res.sendFile(normalizedPath);
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Input Validation
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// ✅ Whitelist validation
|
|
107
|
+
import { z } from 'zod';
|
|
108
|
+
|
|
109
|
+
const userSchema = z.object({
|
|
110
|
+
email: z.string().email(),
|
|
111
|
+
password: z.string().min(12).max(160),
|
|
112
|
+
age: z.number().int().min(0).max(150),
|
|
113
|
+
role: z.enum(['user', 'admin'])
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const validateUser = (data: unknown) => {
|
|
117
|
+
return userSchema.parse(data);
|
|
118
|
+
};
|
|
119
|
+
```
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Secrets Management
|
|
2
|
+
|
|
3
|
+
## Environment Variables
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// ❌ NEVER hardcode secrets
|
|
7
|
+
const config = {
|
|
8
|
+
dbPassword: 'super_secret_password',
|
|
9
|
+
apiKey: 'sk-1234567890abcdef'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// ✅ Use environment variables
|
|
13
|
+
import dotenv from 'dotenv';
|
|
14
|
+
dotenv.config();
|
|
15
|
+
|
|
16
|
+
const config = {
|
|
17
|
+
dbPassword: process.env.DB_PASSWORD,
|
|
18
|
+
apiKey: process.env.API_KEY,
|
|
19
|
+
sessionSecret: process.env.SESSION_SECRET
|
|
20
|
+
};
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Validate Required Secrets
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// ✅ Fail fast if secrets missing
|
|
27
|
+
const requiredEnvVars = [
|
|
28
|
+
'DB_PASSWORD',
|
|
29
|
+
'API_KEY',
|
|
30
|
+
'SESSION_SECRET',
|
|
31
|
+
'JWT_SECRET'
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
requiredEnvVars.forEach(varName => {
|
|
35
|
+
if (!process.env[varName]) {
|
|
36
|
+
throw new Error(`Missing required environment variable: ${varName}`);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ✅ Type-safe config
|
|
41
|
+
interface Config {
|
|
42
|
+
dbPassword: string;
|
|
43
|
+
apiKey: string;
|
|
44
|
+
sessionSecret: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function loadConfig(): Config {
|
|
48
|
+
const dbPassword = process.env.DB_PASSWORD;
|
|
49
|
+
if (!dbPassword) throw new Error('DB_PASSWORD required');
|
|
50
|
+
|
|
51
|
+
// ... validate all required vars
|
|
52
|
+
|
|
53
|
+
return { dbPassword, apiKey, sessionSecret };
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Generate Strong Secrets
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Generate cryptographically secure secrets
|
|
61
|
+
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
62
|
+
|
|
63
|
+
# Or using OpenSSL
|
|
64
|
+
openssl rand -base64 32
|
|
65
|
+
|
|
66
|
+
# Or using head
|
|
67
|
+
head -c32 /dev/urandom | base64
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## .gitignore Configuration
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# .gitignore - NEVER commit secrets
|
|
74
|
+
.env
|
|
75
|
+
.env.local
|
|
76
|
+
.env.*.local
|
|
77
|
+
*.key
|
|
78
|
+
*.pem
|
|
79
|
+
secrets/
|
|
80
|
+
credentials.json
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Environment Example File
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# .env.example - commit this to show required variables
|
|
87
|
+
DB_HOST=localhost
|
|
88
|
+
DB_PORT=5432
|
|
89
|
+
DB_NAME=myapp
|
|
90
|
+
DB_USER=
|
|
91
|
+
DB_PASSWORD=
|
|
92
|
+
|
|
93
|
+
API_KEY=
|
|
94
|
+
SESSION_SECRET=
|
|
95
|
+
JWT_SECRET=
|
|
96
|
+
|
|
97
|
+
# Copy to .env and fill in actual values
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Secrets in CI/CD
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
# GitHub Actions
|
|
104
|
+
- name: Deploy
|
|
105
|
+
env:
|
|
106
|
+
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
|
|
107
|
+
API_KEY: ${{ secrets.API_KEY }}
|
|
108
|
+
run: ./deploy.sh
|
|
109
|
+
|
|
110
|
+
# ❌ Never echo secrets in logs
|
|
111
|
+
- name: Configure
|
|
112
|
+
run: |
|
|
113
|
+
echo "Configuring application..."
|
|
114
|
+
# echo "DB_PASSWORD=$DB_PASSWORD" # NEVER do this!
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Secrets Rotation
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
// ✅ Support for rotating secrets
|
|
121
|
+
class SecretManager {
|
|
122
|
+
async getSecret(name: string): Promise<string> {
|
|
123
|
+
// Check for new secret first (during rotation)
|
|
124
|
+
const newSecret = process.env[`${name}_NEW`];
|
|
125
|
+
if (newSecret) {
|
|
126
|
+
return newSecret;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const secret = process.env[name];
|
|
130
|
+
if (!secret) {
|
|
131
|
+
throw new Error(`Secret ${name} not found`);
|
|
132
|
+
}
|
|
133
|
+
return secret;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ✅ Accept multiple JWT signing keys during rotation
|
|
138
|
+
function verifyToken(token: string) {
|
|
139
|
+
const keys = [process.env.JWT_SECRET, process.env.JWT_SECRET_OLD].filter(Boolean);
|
|
140
|
+
|
|
141
|
+
for (const key of keys) {
|
|
142
|
+
try {
|
|
143
|
+
return jwt.verify(token, key);
|
|
144
|
+
} catch {}
|
|
145
|
+
}
|
|
146
|
+
throw new Error('Invalid token');
|
|
147
|
+
}
|
|
148
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Code Style Guidelines
|
|
2
|
+
|
|
3
|
+
This directory contains coding style and organization patterns.
|
|
4
|
+
|
|
5
|
+
## Available Chunks
|
|
6
|
+
|
|
7
|
+
- **naming.md** - Variables, constants, classes, files, avoiding magic numbers
|
|
8
|
+
- **organization.md** - Function length, nesting depth, file structure, SRP
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Naming Conventions
|
|
2
|
+
|
|
3
|
+
## Variables and Functions
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
// camelCase for variables and functions
|
|
7
|
+
const userName = 'John';
|
|
8
|
+
const isActive = true;
|
|
9
|
+
const itemCount = 42;
|
|
10
|
+
|
|
11
|
+
function calculateTotal(items: Item[]): number {
|
|
12
|
+
return items.reduce((sum, item) => sum + item.price, 0);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Boolean variables: use is/has/can/should prefix
|
|
16
|
+
const isValid = validate(input);
|
|
17
|
+
const hasPermission = checkPermission(user);
|
|
18
|
+
const canEdit = user.role === 'admin';
|
|
19
|
+
const shouldRetry = error.code === 'TIMEOUT';
|
|
20
|
+
|
|
21
|
+
// Collections: use plural names
|
|
22
|
+
const users = getUsers();
|
|
23
|
+
const activeOrders = orders.filter(o => o.status === 'active');
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Constants
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// UPPER_SNAKE_CASE for constants
|
|
30
|
+
const MAX_RETRY_ATTEMPTS = 3;
|
|
31
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
32
|
+
const API_BASE_URL = 'https://api.example.com';
|
|
33
|
+
|
|
34
|
+
// Enum-like objects
|
|
35
|
+
const ORDER_STATUS = {
|
|
36
|
+
PENDING: 'pending',
|
|
37
|
+
PROCESSING: 'processing',
|
|
38
|
+
SHIPPED: 'shipped',
|
|
39
|
+
DELIVERED: 'delivered',
|
|
40
|
+
CANCELLED: 'cancelled'
|
|
41
|
+
} as const;
|
|
42
|
+
|
|
43
|
+
const HTTP_STATUS = {
|
|
44
|
+
OK: 200,
|
|
45
|
+
CREATED: 201,
|
|
46
|
+
BAD_REQUEST: 400,
|
|
47
|
+
NOT_FOUND: 404
|
|
48
|
+
} as const;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Classes and Types
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// PascalCase for classes and types
|
|
55
|
+
class UserService {
|
|
56
|
+
constructor(private userRepository: UserRepository) {}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface User {
|
|
60
|
+
id: string;
|
|
61
|
+
name: string;
|
|
62
|
+
email: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type UserRole = 'admin' | 'editor' | 'viewer';
|
|
66
|
+
|
|
67
|
+
// Avoid prefixes
|
|
68
|
+
// ❌ IUser, CUser, TUser
|
|
69
|
+
// ✅ User
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Files and Modules
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// kebab-case for files
|
|
76
|
+
user-service.ts
|
|
77
|
+
order-repository.ts
|
|
78
|
+
create-user.dto.ts
|
|
79
|
+
|
|
80
|
+
// Match file name to primary export
|
|
81
|
+
// user-service.ts exports UserService
|
|
82
|
+
// order-repository.ts exports OrderRepository
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Avoid Bad Names
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// ❌ Bad - unclear
|
|
89
|
+
const d = Date.now();
|
|
90
|
+
const tmp = user.name;
|
|
91
|
+
const data = fetchData();
|
|
92
|
+
const flag = true;
|
|
93
|
+
|
|
94
|
+
// ✅ Good - descriptive
|
|
95
|
+
const currentDate = Date.now();
|
|
96
|
+
const originalUserName = user.name;
|
|
97
|
+
const customerOrders = fetchCustomerOrders();
|
|
98
|
+
const isEmailVerified = true;
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Avoid Magic Numbers
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// ❌ Magic numbers
|
|
105
|
+
if (user.age >= 18) { ... }
|
|
106
|
+
if (items.length > 100) { ... }
|
|
107
|
+
setTimeout(callback, 5000);
|
|
108
|
+
|
|
109
|
+
// ✅ Named constants
|
|
110
|
+
const LEGAL_AGE = 18;
|
|
111
|
+
const MAX_BATCH_SIZE = 100;
|
|
112
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
113
|
+
|
|
114
|
+
if (user.age >= LEGAL_AGE) { ... }
|
|
115
|
+
if (items.length > MAX_BATCH_SIZE) { ... }
|
|
116
|
+
setTimeout(callback, DEFAULT_TIMEOUT_MS);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Consistency
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Pick ONE style and stick with it across the project
|
|
123
|
+
|
|
124
|
+
// ✅ Consistent camelCase in APIs
|
|
125
|
+
{
|
|
126
|
+
"userId": 123,
|
|
127
|
+
"firstName": "John",
|
|
128
|
+
"createdAt": "2024-01-01"
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ❌ Mixed styles
|
|
132
|
+
{
|
|
133
|
+
"user_id": 123, // snake_case
|
|
134
|
+
"firstName": "John", // camelCase - inconsistent!
|
|
135
|
+
}
|
|
136
|
+
```
|