ndomo 0.1.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/.bun-version +1 -0
- package/.dockerignore +79 -0
- package/.editorconfig +18 -0
- package/.env.example +19 -0
- package/.github/CODEOWNERS +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
- package/.github/ISSUE_TEMPLATE/config.yml +2 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +34 -0
- package/.github/dependabot.yml +36 -0
- package/.github/pull_request_template.md +24 -0
- package/.github/release.yml +30 -0
- package/.github/workflows/gitleaks.yml +28 -0
- package/.github/workflows/release-please.yml +27 -0
- package/.github/workflows/smoke.yml +29 -0
- package/.husky/commit-msg +1 -0
- package/CHANGELOG.md +114 -0
- package/Dockerfile +32 -0
- package/README.es.md +174 -0
- package/README.md +187 -0
- package/agents/chronicler.md +98 -0
- package/agents/ci-smith.md +136 -0
- package/agents/craftsman.md +341 -0
- package/agents/deploy-smith.md +138 -0
- package/agents/foreman.md +377 -0
- package/agents/go-smith.md +164 -0
- package/agents/guild.md +188 -0
- package/agents/inspector.md +83 -0
- package/agents/js-smith.md +127 -0
- package/agents/ops-scout.md +173 -0
- package/agents/painter.md +200 -0
- package/agents/python-smith.md +120 -0
- package/agents/ranger.md +307 -0
- package/agents/release-smith.md +165 -0
- package/agents/rust-smith.md +159 -0
- package/agents/sage.md +178 -0
- package/agents/scout.md +144 -0
- package/agents/scribe.md +156 -0
- package/agents/smith.md +201 -0
- package/agents/vue-smith.md +155 -0
- package/agents/warden.md +216 -0
- package/agents/zig-smith.md +156 -0
- package/bin/ndomo-analyses.ts +4 -0
- package/bin/ndomo-status.ts +4 -0
- package/biome.json +57 -0
- package/bun.lock +514 -0
- package/commitlint.config.js +3 -0
- package/config/ndomo.config.json +258 -0
- package/config/ndomo.schema.json +166 -0
- package/docs/agents.md +375 -0
- package/docs/bugs/plan-create-orphan-fk.md +131 -0
- package/docs/bugs/task_create_batch-order-index-collision.md +158 -0
- package/docs/configuration.md +276 -0
- package/docs/database.md +364 -0
- package/docs/features/feature-flexible-builder-v1.md +724 -0
- package/docs/features/feature-flexible-builder-v2.md +882 -0
- package/docs/features/feature-flexible-builder.md +974 -0
- package/docs/http-server.md +244 -0
- package/docs/installation.md +259 -0
- package/docs/integrations.md +129 -0
- package/docs/operations/anti-pattern-sub-agent-verify-2026-06-21.md +32 -0
- package/docs/operations/audit-v1.md +417 -0
- package/docs/operations/audit-v2.md +197 -0
- package/docs/operations/audit-v3.md +306 -0
- package/docs/operations/db-optimize-foundations.md +123 -0
- package/docs/operations/verify-gate-architecture.md +82 -0
- package/docs/workflows.md +448 -0
- package/opencode.json +5 -0
- package/package.json +65 -0
- package/release-please-config.json +11 -0
- package/scripts/dev-bust-cache.sh +164 -0
- package/scripts/install.sh +688 -0
- package/scripts/smoke-e2e.ts +704 -0
- package/scripts/smoke-hot.ts +417 -0
- package/scripts/smoke-http.sh +228 -0
- package/scripts/smoke-v4.ts +256 -0
- package/scripts/smoke-v5.ts +397 -0
- package/scripts/smoke.sh +9 -0
- package/scripts/uninstall.sh +224 -0
- package/skills/api-security-best-practices/SKILL.md +915 -0
- package/skills/bash-scripting/SKILL.md +201 -0
- package/skills/bun/SKILL.md +313 -0
- package/skills/cavecrew/SKILL.md +82 -0
- package/skills/caveman/SKILL.md +74 -0
- package/skills/caveman-review/README.md +33 -0
- package/skills/caveman-review/SKILL.md +55 -0
- package/skills/find-skills/SKILL.md +142 -0
- package/skills/frontend-design/LICENSE.txt +177 -0
- package/skills/frontend-design/SKILL.md +55 -0
- package/skills/golang-patterns/SKILL.md +674 -0
- package/skills/golang-security/SKILL.md +185 -0
- package/skills/golang-security/evals/evals.json +595 -0
- package/skills/golang-security/references/architecture.md +268 -0
- package/skills/golang-security/references/checklist.md +80 -0
- package/skills/golang-security/references/cookies.md +200 -0
- package/skills/golang-security/references/cryptography.md +424 -0
- package/skills/golang-security/references/filesystem.md +285 -0
- package/skills/golang-security/references/injection.md +315 -0
- package/skills/golang-security/references/logging.md +163 -0
- package/skills/golang-security/references/memory-safety.md +241 -0
- package/skills/golang-security/references/network.md +253 -0
- package/skills/golang-security/references/secrets.md +189 -0
- package/skills/golang-security/references/third-party.md +159 -0
- package/skills/golang-security/references/threat-modeling.md +189 -0
- package/skills/golang-testing/SKILL.md +720 -0
- package/skills/grill-me/SKILL.md +7 -0
- package/skills/javascript-testing-patterns/SKILL.md +537 -0
- package/skills/javascript-testing-patterns/references/advanced-testing-patterns.md +513 -0
- package/skills/modern-javascript-patterns/SKILL.md +43 -0
- package/skills/modern-javascript-patterns/references/advanced-patterns.md +487 -0
- package/skills/modern-javascript-patterns/references/details.md +457 -0
- package/skills/python-anti-patterns/SKILL.md +349 -0
- package/skills/python-design-patterns/SKILL.md +85 -0
- package/skills/python-design-patterns/references/details.md +353 -0
- package/skills/python-error-handling/SKILL.md +193 -0
- package/skills/python-error-handling/references/details.md +171 -0
- package/skills/python-testing-patterns/SKILL.md +278 -0
- package/skills/python-testing-patterns/references/advanced-patterns.md +411 -0
- package/skills/python-testing-patterns/references/details.md +349 -0
- package/skills/rust-patterns/SKILL.md +500 -0
- package/skills/rust-testing/SKILL.md +501 -0
- package/skills/security-review/SKILL.md +504 -0
- package/skills/security-review/cloud-infrastructure-security.md +361 -0
- package/skills/vue-best-practices/SKILL.md +154 -0
- package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
- package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
- package/skills/vue-best-practices/references/component-async.md +97 -0
- package/skills/vue-best-practices/references/component-data-flow.md +307 -0
- package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
- package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
- package/skills/vue-best-practices/references/component-slots.md +216 -0
- package/skills/vue-best-practices/references/component-suspense.md +228 -0
- package/skills/vue-best-practices/references/component-teleport.md +108 -0
- package/skills/vue-best-practices/references/component-transition-group.md +128 -0
- package/skills/vue-best-practices/references/component-transition.md +125 -0
- package/skills/vue-best-practices/references/composables.md +290 -0
- package/skills/vue-best-practices/references/directives.md +162 -0
- package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
- package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
- package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
- package/skills/vue-best-practices/references/plugins.md +166 -0
- package/skills/vue-best-practices/references/reactivity.md +344 -0
- package/skills/vue-best-practices/references/render-functions.md +201 -0
- package/skills/vue-best-practices/references/sfc.md +310 -0
- package/skills/vue-best-practices/references/state-management.md +135 -0
- package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
- package/skills/vue-pinia-best-practices/SKILL.md +21 -0
- package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
- package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
- package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
- package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
- package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
- package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
- package/skills/zig-0.16/SKILL.md +840 -0
- package/skills/zig-0.16/scripts/check-zig-version.sh +21 -0
- package/src/cli/analyses.ts +280 -0
- package/src/cli/index.ts +108 -0
- package/src/cli/serve.ts +192 -0
- package/src/cli/smoke.ts +131 -0
- package/src/cli/status.test.ts +204 -0
- package/src/cli/status.ts +263 -0
- package/src/cli/vacuum.test.ts +82 -0
- package/src/cli/vacuum.ts +96 -0
- package/src/config/schema.test.ts +88 -0
- package/src/config/schema.ts +64 -0
- package/src/db/analyses-migration.test.ts +210 -0
- package/src/db/analyses.test.ts +466 -0
- package/src/db/analyses.ts +375 -0
- package/src/db/auto-checkpoint.ts +131 -0
- package/src/db/client.test.ts +129 -0
- package/src/db/client.ts +55 -0
- package/src/db/fts-escape.ts +20 -0
- package/src/db/incidents.test.ts +201 -0
- package/src/db/incidents.ts +93 -0
- package/src/db/index.ts +86 -0
- package/src/db/migrations-v13.test.ts +141 -0
- package/src/db/migrations-v8.test.ts +301 -0
- package/src/db/migrations.ts +147 -0
- package/src/db/plan-archive.test.ts +180 -0
- package/src/db/plan-archive.ts +274 -0
- package/src/db/plan-create.test.ts +276 -0
- package/src/db/plan-create.ts +78 -0
- package/src/db/plan-files.test.ts +289 -0
- package/src/db/plan-update-status.ts +287 -0
- package/src/db/plans.test.ts +490 -0
- package/src/db/plans.ts +534 -0
- package/src/db/resolve-project-dir.test.ts +143 -0
- package/src/db/resolve-project-dir.ts +75 -0
- package/src/db/rollbacks.test.ts +150 -0
- package/src/db/rollbacks.ts +67 -0
- package/src/db/schema.ts +907 -0
- package/src/db/sessions.test.ts +80 -0
- package/src/db/sessions.ts +135 -0
- package/src/db/shutdown.test.ts +147 -0
- package/src/db/shutdown.ts +45 -0
- package/src/db/tasks.test.ts +921 -0
- package/src/db/tasks.ts +747 -0
- package/src/db/types.ts +619 -0
- package/src/http/__tests__/auth.test.ts +196 -0
- package/src/http/__tests__/routes.test.ts +465 -0
- package/src/http/__tests__/sse.test.ts +317 -0
- package/src/http/auth.ts +72 -0
- package/src/http/middleware/cors.ts +53 -0
- package/src/http/middleware/security-headers.ts +21 -0
- package/src/http/routes/events.ts +112 -0
- package/src/http/routes/health.ts +51 -0
- package/src/http/routes/plans.ts +66 -0
- package/src/http/routes/sessions.ts +50 -0
- package/src/http/routes/tasks.ts +60 -0
- package/src/http/server.ts +95 -0
- package/src/http/sse.ts +116 -0
- package/src/index.ts +37 -0
- package/src/lib.ts +65 -0
- package/src/mem/scoped.ts +65 -0
- package/src/orchestrator/background.test.ts +268 -0
- package/src/orchestrator/background.ts +293 -0
- package/src/orchestrator/memory-hook.ts +182 -0
- package/src/orchestrator/reconciler.ts +123 -0
- package/src/orchestrator/scheduler.test.ts +300 -0
- package/src/orchestrator/scheduler.ts +243 -0
- package/src/plugin.test.ts +2574 -0
- package/src/plugin.ts +1690 -0
- package/src/sdk/client.ts +66 -0
- package/src/worktrees/manager.ts +236 -0
- package/src/worktrees/state.ts +87 -0
- package/tests/integration/ranger-flow.test.ts +257 -0
- package/tools/analysis_archive.ts +28 -0
- package/tools/analysis_create.ts +55 -0
- package/tools/analysis_get.ts +33 -0
- package/tools/analysis_link_plan.ts +44 -0
- package/tools/analysis_list.ts +48 -0
- package/tools/analysis_search.ts +36 -0
- package/tools/analysis_update.ts +44 -0
- package/tools/plan_approve.ts +31 -0
- package/tools/plan_create.ts +58 -0
- package/tools/plan_get.ts +40 -0
- package/tools/plan_list.ts +37 -0
- package/tools/plan_search.ts +34 -0
- package/tools/plan_update_status.ts +71 -0
- package/tools/session_checkpoint.ts +31 -0
- package/tools/session_end.ts +26 -0
- package/tools/session_start.ts +43 -0
- package/tools/task_create_batch.ts +70 -0
- package/tools/task_list.ts +35 -0
- package/tools/task_next_for_agent.ts +30 -0
- package/tools/task_search.ts +34 -0
- package/tools/task_update_status.ts +37 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# Security Architecture Patterns
|
|
2
|
+
|
|
3
|
+
Defense-in-depth, Zero Trust, and authentication patterns for Go services.
|
|
4
|
+
|
|
5
|
+
## Defense-in-Depth Layers
|
|
6
|
+
|
|
7
|
+
Multiple security controls ensure that failure of one layer doesn't compromise the system:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Layer 1: PERIMETER — Rate limiting, DDoS mitigation, WAF
|
|
11
|
+
Layer 2: NETWORK — TLS/mTLS, network segmentation
|
|
12
|
+
Layer 3: APPLICATION — Input validation, auth, authz, secure coding
|
|
13
|
+
Layer 4: DATA — Encryption at rest/transit, access controls, backups
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Go Implementation by Layer
|
|
17
|
+
|
|
18
|
+
**Layer 1 — Rate Limiting Middleware:**
|
|
19
|
+
|
|
20
|
+
```go
|
|
21
|
+
import "golang.org/x/time/rate"
|
|
22
|
+
|
|
23
|
+
// Global rate limiter
|
|
24
|
+
func RateLimitMiddleware(rps float64, burst int) func(http.Handler) http.Handler {
|
|
25
|
+
limiter := rate.NewLimiter(rate.Limit(rps), burst)
|
|
26
|
+
return func(next http.Handler) http.Handler {
|
|
27
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
28
|
+
if !limiter.Allow() {
|
|
29
|
+
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
next.ServeHTTP(w, r)
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Per-client rate limiting** prevents a single abuser from exhausting the global limit:
|
|
39
|
+
|
|
40
|
+
```go
|
|
41
|
+
type ClientRateLimiter struct {
|
|
42
|
+
mu sync.Mutex
|
|
43
|
+
clients map[string]*rate.Limiter
|
|
44
|
+
rps rate.Limit
|
|
45
|
+
burst int
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func (crl *ClientRateLimiter) GetLimiter(clientIP string) *rate.Limiter {
|
|
49
|
+
crl.mu.Lock()
|
|
50
|
+
defer crl.mu.Unlock()
|
|
51
|
+
if limiter, exists := crl.clients[clientIP]; exists {
|
|
52
|
+
return limiter
|
|
53
|
+
}
|
|
54
|
+
limiter := rate.NewLimiter(crl.rps, crl.burst)
|
|
55
|
+
crl.clients[clientIP] = limiter
|
|
56
|
+
return limiter
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Layer 2 — mTLS for Service-to-Service:**
|
|
61
|
+
|
|
62
|
+
```go
|
|
63
|
+
func mTLSConfig(caCertFile, clientCertFile, clientKeyFile string) (*tls.Config, error) {
|
|
64
|
+
caCertPool := x509.NewCertPool()
|
|
65
|
+
caCert, err := os.ReadFile(caCertFile)
|
|
66
|
+
if err != nil { return nil, err }
|
|
67
|
+
caCertPool.AppendCertsFromPEM(caCert)
|
|
68
|
+
|
|
69
|
+
cert, err := tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
|
|
70
|
+
if err != nil { return nil, err }
|
|
71
|
+
|
|
72
|
+
return &tls.Config{
|
|
73
|
+
Certificates: []tls.Certificate{cert},
|
|
74
|
+
RootCAs: caCertPool,
|
|
75
|
+
MinVersion: tls.VersionTLS12,
|
|
76
|
+
}, nil
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Layer 3 — Request Body Size Limiting:**
|
|
81
|
+
|
|
82
|
+
```go
|
|
83
|
+
func MaxBodySize(maxBytes int64) func(http.Handler) http.Handler {
|
|
84
|
+
return func(next http.Handler) http.Handler {
|
|
85
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
86
|
+
r.Body = http.MaxBytesReader(w, r.Body, maxBytes)
|
|
87
|
+
next.ServeHTTP(w, r)
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Layer 4 — Encryption at Rest (AES-GCM):**
|
|
94
|
+
|
|
95
|
+
Use `crypto/aes` with GCM mode for authenticated encryption. See [Cryptography Security](./cryptography.md) for full `EncryptAESGCM`/`DecryptAESGCM` implementations, algorithm selection guide, and envelope encryption for key rotation.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Zero Trust Principles
|
|
100
|
+
|
|
101
|
+
| Principle | Implementation |
|
|
102
|
+
| --- | --- |
|
|
103
|
+
| Verify explicitly | Authenticate and authorize every request — no implicit trust from network location |
|
|
104
|
+
| Least privilege | Grant minimum permissions; use short-lived tokens (15min access, 7d refresh) |
|
|
105
|
+
| Assume breach | Segment services, encrypt all communication, log all access for anomaly detection |
|
|
106
|
+
|
|
107
|
+
```go
|
|
108
|
+
// Zero Trust middleware: verify identity + permissions on every request
|
|
109
|
+
func ZeroTrustMiddleware(next http.Handler) http.Handler {
|
|
110
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
111
|
+
// 1. Verify token
|
|
112
|
+
claims, err := validateJWT(r.Header.Get("Authorization"))
|
|
113
|
+
if err != nil {
|
|
114
|
+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
// 2. Verify permissions for this specific resource
|
|
118
|
+
if !hasPermission(claims.Subject, r.Method, r.URL.Path) {
|
|
119
|
+
http.Error(w, "Forbidden", http.StatusForbidden)
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
// 3. Audit log
|
|
123
|
+
logger.Info("access_granted",
|
|
124
|
+
"user", claims.Subject,
|
|
125
|
+
"method", r.Method,
|
|
126
|
+
"path", r.URL.Path,
|
|
127
|
+
"ip", r.RemoteAddr,
|
|
128
|
+
)
|
|
129
|
+
ctx := context.WithValue(r.Context(), userClaimsKey, claims)
|
|
130
|
+
next.ServeHTTP(w, r.WithContext(ctx))
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## Authentication Pattern Selection
|
|
138
|
+
|
|
139
|
+
| Use Case | Recommended Pattern | Go Implementation |
|
|
140
|
+
| --- | --- | --- |
|
|
141
|
+
| Web application | OAuth 2.0 + PKCE with OIDC | `golang.org/x/oauth2` |
|
|
142
|
+
| API authentication | JWT with short expiry + refresh tokens | `github.com/golang-jwt/jwt/v5` |
|
|
143
|
+
| Service-to-service | mTLS with certificate rotation | `crypto/tls` with `tls.LoadX509KeyPair` |
|
|
144
|
+
| CLI/Automation | API keys with IP allowlisting | Custom middleware with `net.ParseIP` |
|
|
145
|
+
| High security | FIDO2/WebAuthn hardware keys | `github.com/go-webauthn/webauthn` |
|
|
146
|
+
|
|
147
|
+
### JWT Validation — Complete Example
|
|
148
|
+
|
|
149
|
+
JWT validation must pin the signing algorithm to prevent algorithm confusion attacks (where an attacker switches RS256 to HS256 and signs with the public key):
|
|
150
|
+
|
|
151
|
+
```go
|
|
152
|
+
import "github.com/golang-jwt/jwt/v5"
|
|
153
|
+
|
|
154
|
+
func validateJWT(authHeader string) (*jwt.RegisteredClaims, error) {
|
|
155
|
+
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
|
|
156
|
+
token, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{},
|
|
157
|
+
func(token *jwt.Token) (interface{}, error) {
|
|
158
|
+
// Pin signing algorithm — prevents algorithm confusion
|
|
159
|
+
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
|
160
|
+
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
|
161
|
+
}
|
|
162
|
+
return publicKey, nil
|
|
163
|
+
},
|
|
164
|
+
jwt.WithIssuer("your-issuer"),
|
|
165
|
+
jwt.WithAudience("your-audience"),
|
|
166
|
+
jwt.WithExpirationRequired(),
|
|
167
|
+
)
|
|
168
|
+
if err != nil { return nil, err }
|
|
169
|
+
claims, ok := token.Claims.(*jwt.RegisteredClaims)
|
|
170
|
+
if !ok { return nil, errors.New("invalid claims") }
|
|
171
|
+
return claims, nil
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Password Hashing — Argon2id
|
|
176
|
+
|
|
177
|
+
Argon2id is the recommended password hashing algorithm (memory-hard, resists GPU attacks). For algorithm comparison (bcrypt, scrypt, PBKDF2), see [Cryptography Security](./cryptography.md).
|
|
178
|
+
|
|
179
|
+
```go
|
|
180
|
+
import "golang.org/x/crypto/argon2"
|
|
181
|
+
|
|
182
|
+
type PasswordConfig struct {
|
|
183
|
+
Time uint32 // iterations
|
|
184
|
+
Memory uint32 // KB
|
|
185
|
+
Threads uint8
|
|
186
|
+
KeyLen uint32
|
|
187
|
+
SaltLen uint32
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// OWASP recommended parameters
|
|
191
|
+
var DefaultConfig = PasswordConfig{
|
|
192
|
+
Time: 3, Memory: 64 * 1024, Threads: 4, KeyLen: 32, SaltLen: 16,
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
func HashPassword(password string, cfg PasswordConfig) (string, error) {
|
|
196
|
+
salt := make([]byte, cfg.SaltLen)
|
|
197
|
+
if _, err := rand.Read(salt); err != nil { return "", err }
|
|
198
|
+
hash := argon2.IDKey([]byte(password), salt, cfg.Time, cfg.Memory, cfg.Threads, cfg.KeyLen)
|
|
199
|
+
// Encode salt + hash for storage
|
|
200
|
+
return fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s",
|
|
201
|
+
argon2.Version, cfg.Memory, cfg.Time, cfg.Threads,
|
|
202
|
+
base64.RawStdEncoding.EncodeToString(salt),
|
|
203
|
+
base64.RawStdEncoding.EncodeToString(hash),
|
|
204
|
+
), nil
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## HTTP Security Headers
|
|
211
|
+
|
|
212
|
+
Set on every response via middleware:
|
|
213
|
+
|
|
214
|
+
```go
|
|
215
|
+
func SecurityHeadersMiddleware(next http.Handler) http.Handler {
|
|
216
|
+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
217
|
+
w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'")
|
|
218
|
+
w.Header().Set("X-Frame-Options", "DENY")
|
|
219
|
+
w.Header().Set("X-Content-Type-Options", "nosniff")
|
|
220
|
+
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
|
|
221
|
+
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
|
222
|
+
w.Header().Set("Permissions-Policy", "geolocation=(), microphone=(), camera=()")
|
|
223
|
+
next.ServeHTTP(w, r)
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
| Header | Purpose | Recommended Value |
|
|
229
|
+
| --- | --- | --- |
|
|
230
|
+
| Content-Security-Policy | Prevents XSS by restricting resource sources | `default-src 'self'; script-src 'self'` |
|
|
231
|
+
| X-Frame-Options | Prevents clickjacking via framing | `DENY` |
|
|
232
|
+
| X-Content-Type-Options | Prevents MIME-type sniffing | `nosniff` |
|
|
233
|
+
| Strict-Transport-Security | Forces HTTPS, prevents protocol downgrade | `max-age=31536000; includeSubDomains` |
|
|
234
|
+
| Referrer-Policy | Controls referrer header leakage | `strict-origin-when-cross-origin` |
|
|
235
|
+
| Permissions-Policy | Restricts browser features (camera, mic, geolocation) | `geolocation=(), microphone=(), camera=()` |
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Security Anti-Patterns
|
|
240
|
+
|
|
241
|
+
| Anti-Pattern | Why It Fails | Go Fix |
|
|
242
|
+
| --- | --- | --- |
|
|
243
|
+
| Security through obscurity | Hidden admin URLs are discoverable via fuzzing, logs, or source code | Authentication + authorization on all endpoints |
|
|
244
|
+
| Trusting client headers | `X-Forwarded-For`, `X-Is-Admin` — clients forge any header | Server-side identity verification; trust proxy headers only from known load balancers |
|
|
245
|
+
| Client-side authorization | JavaScript checks are trivially bypassed by any HTTP client | Server-side `if !user.HasRole("admin")` on every protected handler |
|
|
246
|
+
| Shared secrets across environments | Staging breach → production compromise | Per-environment secrets via secret manager |
|
|
247
|
+
| Catching and ignoring crypto errors | `_, _ = encrypt(data)` silently proceeds with unencrypted data | Always check error returns — fail closed, never open |
|
|
248
|
+
| Rolling your own crypto | Custom encryption hasn't been analyzed by cryptographers | Use `crypto/aes` GCM, `golang.org/x/crypto/argon2` |
|
|
249
|
+
| Verbose error responses | Stack traces and DB errors reveal internals to attackers | Generic errors to clients (`http.Error(w, "Internal error", 500)`), detailed logs server-side |
|
|
250
|
+
|
|
251
|
+
```go
|
|
252
|
+
// Anti-pattern: trusting client-provided identity
|
|
253
|
+
func badHandler(w http.ResponseWriter, r *http.Request) {
|
|
254
|
+
if r.Header.Get("X-Is-Admin") == "true" { // attacker sets this header
|
|
255
|
+
adminPanel(w, r)
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Correct: server-side identity verification
|
|
260
|
+
func goodHandler(w http.ResponseWriter, r *http.Request) {
|
|
261
|
+
claims := r.Context().Value(userClaimsKey).(*jwt.RegisteredClaims)
|
|
262
|
+
if !hasRole(claims.Subject, "admin") {
|
|
263
|
+
http.Error(w, "Forbidden", http.StatusForbidden)
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
adminPanel(w, r)
|
|
267
|
+
}
|
|
268
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Security Review Checklist
|
|
2
|
+
|
|
3
|
+
Severity: Critical, High, Medium, Low
|
|
4
|
+
|
|
5
|
+
## Input Handling
|
|
6
|
+
|
|
7
|
+
- [ ] **High** All user input validated at system boundaries — internal code trusts the boundary
|
|
8
|
+
- [ ] **High** Input uses allowlists, not blocklists — blocklists always miss something
|
|
9
|
+
- [ ] **High** Sanitized on output (HTML, SQL, shell) — context-dependent escaping
|
|
10
|
+
- [ ] **Medium** Length limits enforced — prevents buffer abuse and DoS
|
|
11
|
+
|
|
12
|
+
## Database
|
|
13
|
+
|
|
14
|
+
- [ ] **Critical** SQL queries use parameterized placeholders — keeps data and code separate
|
|
15
|
+
- [ ] **Critical** ORM/library protects against SQL injection
|
|
16
|
+
- [ ] **Critical** No direct SQL construction with user input
|
|
17
|
+
|
|
18
|
+
## Code Execution
|
|
19
|
+
|
|
20
|
+
- [ ] **Critical** No `exec.Command()` with shell arguments — metacharacters enable injection
|
|
21
|
+
- [ ] **Critical** No eval, reflection on untrusted input — arbitrary code execution risk
|
|
22
|
+
- [ ] **Critical** No deserialization of untrusted data — can trigger arbitrary constructors
|
|
23
|
+
|
|
24
|
+
## Cryptography
|
|
25
|
+
|
|
26
|
+
- [ ] **High** Uses `crypto/rand` for security-critical randomness — `math/rand` is predictable
|
|
27
|
+
- [ ] **High** Uses vetted algorithms (AES-GCM, Argon2id, bcrypt) — custom crypto hasn't been analyzed
|
|
28
|
+
- [ ] **Critical** Proper key management — hardcoded secrets leak through VCS, logs, and backups
|
|
29
|
+
- [ ] **Medium** HMAC for message authentication — prevents tampering
|
|
30
|
+
|
|
31
|
+
## Web Security
|
|
32
|
+
|
|
33
|
+
- [ ] **High** TLS 1.2+ configured correctly — older versions have known attacks
|
|
34
|
+
- [ ] **Medium** Security headers set (HSTS, CSP, X-Frame-Options) — prevents framing, sniffing, downgrade
|
|
35
|
+
- [ ] **Medium** CSRF protection for state-changing requests — prevents cross-origin action forgery
|
|
36
|
+
- [ ] **Medium** Open redirects validated — attackers use your domain to redirect to phishing
|
|
37
|
+
- [ ] **High** XSS protected via `html/template` auto-escaping
|
|
38
|
+
|
|
39
|
+
## Authentication/Authorization
|
|
40
|
+
|
|
41
|
+
- [ ] **High** Passwords hashed with Argon2id (preferred) or bcrypt — intentionally slow to resist brute-force
|
|
42
|
+
- [ ] **High** Sessions use secure tokens from `crypto/rand`
|
|
43
|
+
- [ ] **High** Authorization checked on every privileged action — not just at login
|
|
44
|
+
- [ ] **High** JWT tokens validated (algorithm, claims, expiry) — unsigned JWTs bypass auth
|
|
45
|
+
- [ ] **High** Expired/invalid sessions invalidated server-side
|
|
46
|
+
|
|
47
|
+
## Error Handling
|
|
48
|
+
|
|
49
|
+
- [ ] **Medium** Generic error messages to users — detailed errors help attackers map your system
|
|
50
|
+
- [ ] **Medium** Detailed errors logged server-side only
|
|
51
|
+
- [ ] **Medium** Stack traces not leaked to clients
|
|
52
|
+
- [ ] **Medium** Database errors not exposed — reveals schema and query structure
|
|
53
|
+
|
|
54
|
+
## Dependency Security
|
|
55
|
+
|
|
56
|
+
- [ ] **High** `govulncheck` passes — catches known CVEs in your dependency tree
|
|
57
|
+
- [ ] **High** Dependencies updated regularly — unpatched deps are the #1 attack vector
|
|
58
|
+
- [ ] **Medium** Third-party libraries reviewed for security posture
|
|
59
|
+
|
|
60
|
+
## HTTP Security Headers
|
|
61
|
+
|
|
62
|
+
- [ ] **Medium** `Content-Security-Policy` set — restricts resource sources to prevent XSS
|
|
63
|
+
- [ ] **Medium** `X-Frame-Options: DENY` — prevents clickjacking via iframe embedding
|
|
64
|
+
- [ ] **Medium** `X-Content-Type-Options: nosniff` — prevents MIME-type sniffing attacks
|
|
65
|
+
- [ ] **Medium** `Strict-Transport-Security` with `includeSubDomains` — forces HTTPS, prevents downgrade
|
|
66
|
+
- [ ] **Low** `Referrer-Policy` set — controls referrer header leakage to external sites
|
|
67
|
+
- [ ] **Low** `Permissions-Policy` set — restricts browser features (camera, mic, geolocation)
|
|
68
|
+
|
|
69
|
+
## Rate Limiting & DoS Prevention
|
|
70
|
+
|
|
71
|
+
- [ ] **Medium** HTTP server has `ReadTimeout`, `WriteTimeout`, `IdleTimeout` — prevents Slowloris
|
|
72
|
+
- [ ] **Medium** Request body size limited with `http.MaxBytesReader` — prevents memory exhaustion
|
|
73
|
+
- [ ] **Medium** Rate limiting on authentication endpoints — prevents brute-force and credential stuffing
|
|
74
|
+
- [ ] **Medium** Rate limiting on expensive operations (search, export, file upload)
|
|
75
|
+
|
|
76
|
+
## Concurrency
|
|
77
|
+
|
|
78
|
+
- [ ] **High** `-race` detector passes — races cause data corruption and can bypass auth checks
|
|
79
|
+
- [ ] **High** Shared state properly synchronized
|
|
80
|
+
- [ ] **High** No data races on global variables
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Cookie Security Rules
|
|
2
|
+
|
|
3
|
+
Cookie security is critical for preventing session hijacking and XSS exploitation.
|
|
4
|
+
|
|
5
|
+
**Rules:**
|
|
6
|
+
|
|
7
|
+
1. Cookies MUST set `HttpOnly` for session and authentication cookies.
|
|
8
|
+
2. Cookies MUST set `Secure` in production (HTTPS only).
|
|
9
|
+
3. `SameSite` SHOULD be `Lax` or `Strict` — use `None` only when cross-site access is required.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## HTTP-Only Flag Missing — Medium
|
|
14
|
+
|
|
15
|
+
Without HttpOnly flag, cookies can be accessed via JavaScript.
|
|
16
|
+
|
|
17
|
+
**Bad:**
|
|
18
|
+
|
|
19
|
+
```go
|
|
20
|
+
cookie := &http.Cookie{
|
|
21
|
+
Name: "session",
|
|
22
|
+
Value: sessionID,
|
|
23
|
+
// DON'T: Missing HttpOnly, Secure flags
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Good:**
|
|
28
|
+
|
|
29
|
+
```go
|
|
30
|
+
cookie := &http.Cookie{
|
|
31
|
+
Name: "session",
|
|
32
|
+
Value: sessionID,
|
|
33
|
+
HttpOnly: true, // Prevents JavaScript access
|
|
34
|
+
Secure: true, // Only sends over HTTPS
|
|
35
|
+
SameSite: http.SameSiteStrictMode,
|
|
36
|
+
Path: "/",
|
|
37
|
+
MaxAge: 3600,
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Insecure Cookie Configuration (Missing Secure Flag) — Medium
|
|
44
|
+
|
|
45
|
+
Without Secure flag, cookies are sent over unencrypted HTTP.
|
|
46
|
+
|
|
47
|
+
**Bad:**
|
|
48
|
+
|
|
49
|
+
```go
|
|
50
|
+
http.SetCookie(w, &http.Cookie{
|
|
51
|
+
Name: "auth_token",
|
|
52
|
+
Value: token,
|
|
53
|
+
// Missing Secure, HttpOnly flags
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Good:**
|
|
58
|
+
|
|
59
|
+
```go
|
|
60
|
+
http.SetCookie(w, &http.Cookie{
|
|
61
|
+
Name: "auth_token",
|
|
62
|
+
Value: token,
|
|
63
|
+
Secure: true, // HTTPS only
|
|
64
|
+
HttpOnly: true, // No JavaScript access
|
|
65
|
+
SameSite: http.SameSiteLaxMode,
|
|
66
|
+
Path: "/",
|
|
67
|
+
MaxAge: 86400,
|
|
68
|
+
Domain: "", // Default: send to exact host only
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## SameSite Cookie Protection — Medium
|
|
75
|
+
|
|
76
|
+
SameSite attribute protects against CSRF attacks.
|
|
77
|
+
|
|
78
|
+
**Bad:**
|
|
79
|
+
|
|
80
|
+
```go
|
|
81
|
+
cookie := &http.Cookie{
|
|
82
|
+
Name: "session",
|
|
83
|
+
Value: token,
|
|
84
|
+
Secure: true,
|
|
85
|
+
HttpOnly: true,
|
|
86
|
+
// DON'T: Missing SameSite
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Good:**
|
|
91
|
+
|
|
92
|
+
```go
|
|
93
|
+
// Strict for high-security operations
|
|
94
|
+
authCookie := &http.Cookie{
|
|
95
|
+
Name: "auth",
|
|
96
|
+
Value: token,
|
|
97
|
+
Secure: true,
|
|
98
|
+
HttpOnly: true,
|
|
99
|
+
SameSite: http.SameSiteStrictMode,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Lax for most applications
|
|
103
|
+
sessionCookie := &http.Cookie{
|
|
104
|
+
Name: "session",
|
|
105
|
+
Value: token,
|
|
106
|
+
Secure: true,
|
|
107
|
+
HttpOnly: true,
|
|
108
|
+
SameSite: http.SameSiteLaxMode,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// None for cross-site cookies (requires Secure: true)
|
|
112
|
+
crossSiteCookie := &http.Cookie{
|
|
113
|
+
Name: "analytics",
|
|
114
|
+
Value: trackingID,
|
|
115
|
+
Secure: true,
|
|
116
|
+
HttpOnly: true,
|
|
117
|
+
SameSite: http.SameSiteNoneMode,
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Cookie Prefix Examples — Low
|
|
124
|
+
|
|
125
|
+
Modern cookie prefixes enforce cookie behavior in browsers.
|
|
126
|
+
|
|
127
|
+
```go
|
|
128
|
+
// __Secure- prefix: Requires Secure flag
|
|
129
|
+
secureCookie := &http.Cookie{
|
|
130
|
+
Name: "__Secure-Session",
|
|
131
|
+
Value: token,
|
|
132
|
+
Secure: true, // Required for __Secure-
|
|
133
|
+
HttpOnly: true,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// __Host- prefix: Requires Secure, no Domain, origin-bound path
|
|
137
|
+
hostCookie := &http.Cookie{
|
|
138
|
+
Name: "__Host-CSRF",
|
|
139
|
+
Value: csrfToken,
|
|
140
|
+
Secure: true, // Required
|
|
141
|
+
HttpOnly: true,
|
|
142
|
+
Domain: "", // Must be empty
|
|
143
|
+
Path: "/", // Required
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Gorilla Sessions Cookie Security — High
|
|
150
|
+
|
|
151
|
+
**Bad:**
|
|
152
|
+
|
|
153
|
+
```go
|
|
154
|
+
import "github.com/gorilla/sessions"
|
|
155
|
+
store := sessions.NewCookieStore([]byte("secret-key")) // DON'T: Hardcoded key
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Good:**
|
|
159
|
+
|
|
160
|
+
```go
|
|
161
|
+
import "github.com/gorilla/sessions"
|
|
162
|
+
|
|
163
|
+
store := sessions.NewCookieStore(
|
|
164
|
+
[]byte(os.Getenv("SESSION_AUTH_KEY")), // Use env var
|
|
165
|
+
[]byte(os.Getenv("SESSION_ENC_KEY")), // Separate encryption key
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
store.Options = &sessions.Options{
|
|
169
|
+
Path: "/",
|
|
170
|
+
MaxAge: 86400 * 30,
|
|
171
|
+
HttpOnly: true,
|
|
172
|
+
Secure: true,
|
|
173
|
+
SameSite: http.SameSiteStrictMode,
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Cookie Best Practices Checklist
|
|
180
|
+
|
|
181
|
+
- [ ] Set `HttpOnly: true` for all authentication cookies
|
|
182
|
+
- [ ] Set `Secure: true` for all cookies over HTTPS
|
|
183
|
+
- [ ] Set appropriate `SameSite` value (Strict/Lax/None)
|
|
184
|
+
- [ ] Use short `MaxAge` expiration
|
|
185
|
+
- [ ] Avoid setting cookie `Domain` unless necessary
|
|
186
|
+
- [ ] Validate cookie values on every request
|
|
187
|
+
- [ ] Use cryptographically signed cookies
|
|
188
|
+
- [ ] Rotate cookie secrets regularly
|
|
189
|
+
- [ ] Clear cookies on logout
|
|
190
|
+
- [ ] Use double-submit cookie pattern for CSRF protection
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## CWE References
|
|
195
|
+
|
|
196
|
+
- **CWE-1004**: Sensitive Cookie Without 'HttpOnly' Flag
|
|
197
|
+
- **CWE-614**: Sensitive Cookie in HTTPS Session Without 'Secure' Attribute
|
|
198
|
+
- **CWE-352**: Cross-Site Request Forgery (CSRF)
|
|
199
|
+
- **CWE-285**: Improper Authorization
|
|
200
|
+
- **CWE-565**: Reliance on Cookies without Validation
|