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,253 @@
|
|
|
1
|
+
# Network/Web Security Rules
|
|
2
|
+
|
|
3
|
+
Network and web security vulnerabilities can lead to data leakage and unauthorized access.
|
|
4
|
+
|
|
5
|
+
**Rules:**
|
|
6
|
+
|
|
7
|
+
1. Redirects MUST be validated against an allowlist of domains.
|
|
8
|
+
2. HTTP servers MUST configure `ReadTimeout`, `WriteTimeout`, and `IdleTimeout`.
|
|
9
|
+
3. Pprof endpoints MUST NEVER be exposed publicly.
|
|
10
|
+
4. XML parsers MUST disable XXE — reject `<!DOCTYPE` and `<!ENTITY` declarations.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Open Redirect Vulnerability — Medium
|
|
15
|
+
|
|
16
|
+
Redirects to unvalidated URLs can be used for phishing.
|
|
17
|
+
|
|
18
|
+
**Bad:**
|
|
19
|
+
|
|
20
|
+
```go
|
|
21
|
+
target := r.URL.Query().Get("url")
|
|
22
|
+
http.Redirect(w, r, target, http.StatusFound) // DON'T
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Good:**
|
|
26
|
+
|
|
27
|
+
```go
|
|
28
|
+
target := r.URL.Query().Get("url")
|
|
29
|
+
u, _ := url.Parse(target)
|
|
30
|
+
// Only allow http/https
|
|
31
|
+
if u.Scheme != "http" && u.Scheme != "https" {
|
|
32
|
+
return errors.New("invalid scheme")
|
|
33
|
+
}
|
|
34
|
+
// Block javascript/data schemes
|
|
35
|
+
if strings.HasPrefix(target, "javascript:") || strings.HasPrefix(target, "data:") {
|
|
36
|
+
return errors.New("blocked scheme")
|
|
37
|
+
}
|
|
38
|
+
// Check against whitelist
|
|
39
|
+
if !isAllowedDomain(u.Host) {
|
|
40
|
+
return errors.New("invalid domain")
|
|
41
|
+
}
|
|
42
|
+
http.Redirect(w, r, target, http.StatusFound)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Bind to All Interfaces — Medium
|
|
48
|
+
|
|
49
|
+
Binding to 0.0.0.0 exposes services to all network interfaces.
|
|
50
|
+
|
|
51
|
+
**Bad:**
|
|
52
|
+
|
|
53
|
+
```go
|
|
54
|
+
listener, _ := net.Listen("tcp", "0.0.0.0:8080") // DON'T: Exposes all interfaces
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Good:**
|
|
58
|
+
|
|
59
|
+
```go
|
|
60
|
+
// Bind only to localhost
|
|
61
|
+
listener, _ := net.Listen("tcp", "127.0.0.1:8080")
|
|
62
|
+
|
|
63
|
+
// Or specific internal IP
|
|
64
|
+
listener, _ := net.Listen("tcp", "10.0.1.5:8080")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Slowloris Attack Vulnerability — Medium
|
|
70
|
+
|
|
71
|
+
Slowloris attacks exhaust connection pools.
|
|
72
|
+
|
|
73
|
+
**Bad:**
|
|
74
|
+
|
|
75
|
+
```go
|
|
76
|
+
server := &http.Server{
|
|
77
|
+
Addr: ":8080",
|
|
78
|
+
// Missing ReadTimeout, WriteTimeout, IdleTimeout
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Good:**
|
|
83
|
+
|
|
84
|
+
```go
|
|
85
|
+
server := &http.Server{
|
|
86
|
+
Addr: ":8080",
|
|
87
|
+
ReadTimeout: 5 * time.Second,
|
|
88
|
+
WriteTimeout: 10 * time.Second,
|
|
89
|
+
IdleTimeout: 120 * time.Second,
|
|
90
|
+
MaxHeaderBytes: 1 << 20, // Limit header size to 1MB
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Insecure HTTP Server Configuration — Medium
|
|
97
|
+
|
|
98
|
+
Running HTTP servers without proper security settings.
|
|
99
|
+
|
|
100
|
+
**Bad:**
|
|
101
|
+
|
|
102
|
+
```go
|
|
103
|
+
http.ListenAndServe(":8080", handler) // DON'T: No security hardening
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Good:**
|
|
107
|
+
|
|
108
|
+
```go
|
|
109
|
+
server := &http.Server{
|
|
110
|
+
Addr: ":443",
|
|
111
|
+
ReadTimeout: 5 * time.Second,
|
|
112
|
+
WriteTimeout: 10 * time.Second,
|
|
113
|
+
IdleTimeout: 120 * time.Second,
|
|
114
|
+
MaxHeaderBytes: 1 << 20,
|
|
115
|
+
}
|
|
116
|
+
server.ListenAndServeTLS("cert.pem", "key.pem")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Observable Timing (Timing Attacks) — Medium
|
|
122
|
+
|
|
123
|
+
Timing differences can leak sensitive information.
|
|
124
|
+
|
|
125
|
+
**Bad:**
|
|
126
|
+
|
|
127
|
+
```go
|
|
128
|
+
func checkPassword(input, secret string) bool {
|
|
129
|
+
return input == secret // DON'T: Short-circuit leaks length
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Good:**
|
|
134
|
+
|
|
135
|
+
```go
|
|
136
|
+
import "crypto/subtle"
|
|
137
|
+
|
|
138
|
+
// For comparing fixed-length tokens or hashes:
|
|
139
|
+
func checkToken(input, expected string) bool {
|
|
140
|
+
// ConstantTimeCompare already handles unequal lengths without leaking timing
|
|
141
|
+
return subtle.ConstantTimeCompare([]byte(input), []byte(expected)) == 1
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// For passwords, use Argon2id (preferred) or bcrypt — they handle hashing
|
|
145
|
+
// and constant-time comparison internally:
|
|
146
|
+
// argon2: hash := argon2.IDKey([]byte(password), salt, 3, 64*1024, 4, 32)
|
|
147
|
+
// bcrypt: err := bcrypt.CompareHashAndPassword([]byte(storedHash), []byte(password))
|
|
148
|
+
|
|
149
|
+
// For HMAC verification:
|
|
150
|
+
import "crypto/hmac"
|
|
151
|
+
func verifyHMAC(message, messageMAC, key []byte) bool {
|
|
152
|
+
mac := hmac.New(sha256.New, key)
|
|
153
|
+
mac.Write(message)
|
|
154
|
+
expectedMAC := mac.Sum(nil)
|
|
155
|
+
return hmac.Equal(messageMAC, expectedMAC) // Constant-time
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Exposed pprof Profiling Endpoints — High
|
|
162
|
+
|
|
163
|
+
Debug pprof endpoints expose sensitive runtime information.
|
|
164
|
+
|
|
165
|
+
**Bad:**
|
|
166
|
+
|
|
167
|
+
```go
|
|
168
|
+
import _ "net/http/pprof" // DON'T: Automatically registers /debug/pprof
|
|
169
|
+
http.ListenAndServe(":8080", handler)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Good:**
|
|
173
|
+
|
|
174
|
+
```go
|
|
175
|
+
// Option 1: Use build tags to exclude pprof from production builds
|
|
176
|
+
// File: debug_pprof.go
|
|
177
|
+
//go:build !production
|
|
178
|
+
|
|
179
|
+
package main
|
|
180
|
+
|
|
181
|
+
import _ "net/http/pprof"
|
|
182
|
+
|
|
183
|
+
// Option 2: Serve pprof on a separate internal-only listener
|
|
184
|
+
func startDebugServer() {
|
|
185
|
+
debugMux := http.NewServeMux()
|
|
186
|
+
debugMux.HandleFunc("/debug/pprof/", pprof.Index)
|
|
187
|
+
debugMux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
|
188
|
+
debugMux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
|
189
|
+
debugMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
|
190
|
+
debugMux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
|
191
|
+
go http.ListenAndServe("127.0.0.1:6060", debugMux) // localhost only
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## XXE Vulnerability — High
|
|
198
|
+
|
|
199
|
+
XML parsers that process external entity references.
|
|
200
|
+
|
|
201
|
+
**Bad:**
|
|
202
|
+
|
|
203
|
+
```go
|
|
204
|
+
decoder := xml.NewDecoder(bytes.NewReader(xmlData))
|
|
205
|
+
decoder.Decode(&person) // DON'T: May process external entities
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Good:**
|
|
209
|
+
|
|
210
|
+
```go
|
|
211
|
+
decoder := xml.NewDecoder(bytes.NewReader(xmlData))
|
|
212
|
+
decoder.Strict = true
|
|
213
|
+
|
|
214
|
+
// Block DTD declarations
|
|
215
|
+
xmlStr := string(xmlData)
|
|
216
|
+
if strings.Contains(xmlStr, "<!DOCTYPE") || strings.Contains(xmlStr, "<!ENTITY") {
|
|
217
|
+
return errors.New("XML contains DTD - potential XXE")
|
|
218
|
+
}
|
|
219
|
+
decoder.Decode(&person)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Permissive Regex Validation — Low
|
|
225
|
+
|
|
226
|
+
Weak regex validation can allow malicious input.
|
|
227
|
+
|
|
228
|
+
**Bad:**
|
|
229
|
+
|
|
230
|
+
```go
|
|
231
|
+
matched, _ := regexp.MatchString(`.+@.+\..+`, email) // DON'T: Too permissive
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Good:**
|
|
235
|
+
|
|
236
|
+
```go
|
|
237
|
+
var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
|
238
|
+
if !emailRegex.MatchString(email) {
|
|
239
|
+
return errors.New("invalid email")
|
|
240
|
+
}
|
|
241
|
+
// Also block injection patterns
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## CWE References
|
|
247
|
+
|
|
248
|
+
- **CWE-601**: Open Redirect
|
|
249
|
+
- **CWE-208**: Observable Timing Discrepancy
|
|
250
|
+
- **CWE-611**: Improper Restriction of XML External Entity Reference
|
|
251
|
+
- **CWE-770**: Allocation of Resources Without Limits
|
|
252
|
+
- **CWE-20**: Improper Input Validation
|
|
253
|
+
- **CWE-200**: Exposure of Sensitive Information
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Secrets Management Security Rules
|
|
2
|
+
|
|
3
|
+
Hardcoded secrets, credentials, and sensitive data in source code is a major security vulnerability.
|
|
4
|
+
|
|
5
|
+
**Rules:**
|
|
6
|
+
|
|
7
|
+
1. Secrets MUST be loaded from environment variables or secret managers.
|
|
8
|
+
2. NEVER commit secrets to VCS.
|
|
9
|
+
3. `.gitignore` MUST exclude secret files (`.env`, `*.key`, `*.pem`).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Hardcoded Secrets and Credentials — Critical
|
|
14
|
+
|
|
15
|
+
**Bad:**
|
|
16
|
+
|
|
17
|
+
```go
|
|
18
|
+
const (
|
|
19
|
+
AWS_ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE" // DON'T
|
|
20
|
+
AWS_SECRET_KEY = "wJalrXUtnFEMI/K7MDENG" // DON'T
|
|
21
|
+
DATABASE_PASSWORD = "SuperSecret123!" // DON'T
|
|
22
|
+
JWT_SECRET = "my-super-secret-jwt-key" // DON'T
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
var config = Config{
|
|
26
|
+
APIKey: "abc123-xyz789-secret-key", // DON'T
|
|
27
|
+
Secret: "my-super-secret-value", // DON'T
|
|
28
|
+
DatabaseURL: "user:passw0rd!@localhost:5432/db", // DON'T
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Good:**
|
|
33
|
+
|
|
34
|
+
```go
|
|
35
|
+
import "os"
|
|
36
|
+
|
|
37
|
+
type Config struct {
|
|
38
|
+
AWSAccessKey string
|
|
39
|
+
AWSSecretKey string
|
|
40
|
+
DatabasePassword string
|
|
41
|
+
JWTSecret string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func LoadConfig() (*Config, error) {
|
|
45
|
+
cfg := &Config{
|
|
46
|
+
AWSAccessKey: os.Getenv("AWS_ACCESS_KEY_ID"),
|
|
47
|
+
AWSSecretKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
|
|
48
|
+
DatabasePassword: os.Getenv("DATABASE_PASSWORD"),
|
|
49
|
+
JWTSecret: os.Getenv("JWT_SECRET"),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if cfg.JWTSecret == "" {
|
|
53
|
+
return nil, errors.New("JWT_SECRET is required")
|
|
54
|
+
}
|
|
55
|
+
return cfg, nil
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Hardcoded Database Passwords — Critical
|
|
62
|
+
|
|
63
|
+
**Bad:**
|
|
64
|
+
|
|
65
|
+
```go
|
|
66
|
+
// MySQL
|
|
67
|
+
dsn := "user:Password123!@tcp(localhost:3306)/dbname" // DON'T
|
|
68
|
+
|
|
69
|
+
// PostgreSQL
|
|
70
|
+
dsn := "user=postgres password=P@ssw0rd! dbname=mydb host=localhost" // DON'T
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Good:**
|
|
74
|
+
|
|
75
|
+
```go
|
|
76
|
+
// MySQL
|
|
77
|
+
func connectMySQL() (*sql.DB, error) {
|
|
78
|
+
user := os.Getenv("DB_USER")
|
|
79
|
+
password := os.Getenv("DB_PASSWORD")
|
|
80
|
+
if password == "" {
|
|
81
|
+
return nil, errors.New("DB_PASSWORD required")
|
|
82
|
+
}
|
|
83
|
+
host := getEnvWithDefault("DB_HOST", "localhost")
|
|
84
|
+
port := getEnvWithDefault("DB_PORT", "3306")
|
|
85
|
+
addr := net.JoinHostPort(host, port)
|
|
86
|
+
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s",
|
|
87
|
+
user, password, addr, getEnvWithDefault("DB_NAME", "mydb"))
|
|
88
|
+
return sql.Open("mysql", dsn)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// PostgreSQL
|
|
92
|
+
func connectPostgres() (*sql.DB, error) {
|
|
93
|
+
connStr := os.Getenv("DATABASE_URL")
|
|
94
|
+
if connStr == "" {
|
|
95
|
+
return nil, errors.New("DATABASE_URL required")
|
|
96
|
+
}
|
|
97
|
+
return sql.Open("postgres", connStr)
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Secrets Storage Best Practices
|
|
104
|
+
|
|
105
|
+
### Environment Variables
|
|
106
|
+
|
|
107
|
+
```go
|
|
108
|
+
type EnvSecretLoader struct{}
|
|
109
|
+
|
|
110
|
+
func (l *EnvSecretLoader) Load(required []string) (map[string]string, error) {
|
|
111
|
+
secrets := make(map[string]string)
|
|
112
|
+
missing := []string{}
|
|
113
|
+
for _, name := range required {
|
|
114
|
+
value := os.Getenv(name)
|
|
115
|
+
if value == "" {
|
|
116
|
+
missing = append(missing, name)
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
secrets[name] = value
|
|
120
|
+
}
|
|
121
|
+
if len(missing) > 0 {
|
|
122
|
+
return nil, fmt.Errorf("missing: %v", missing)
|
|
123
|
+
}
|
|
124
|
+
return secrets, nil
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Secret Managers
|
|
129
|
+
|
|
130
|
+
```go
|
|
131
|
+
type SecretManager interface {
|
|
132
|
+
GetSecret(name string) (string, error)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// AWS Secrets Manager
|
|
136
|
+
type AWSSecretsManager struct {
|
|
137
|
+
client *secretsmanager.Client
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
func (m *AWSSecretsManager) GetSecret(name string) (string, error) {
|
|
141
|
+
result, err := m.client.GetSecretValue(context.TODO(), &secretsmanager.GetSecretValueInput{
|
|
142
|
+
SecretId: aws.String(name),
|
|
143
|
+
})
|
|
144
|
+
if err != nil {
|
|
145
|
+
return "", err
|
|
146
|
+
}
|
|
147
|
+
if result.SecretString != nil {
|
|
148
|
+
return *result.SecretString, nil
|
|
149
|
+
}
|
|
150
|
+
return string(result.SecretBinary), nil
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### .gitignore Patterns
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
# Secrets
|
|
158
|
+
.env
|
|
159
|
+
.env.local
|
|
160
|
+
.env.*.local
|
|
161
|
+
*.key
|
|
162
|
+
*.pem
|
|
163
|
+
*.p12
|
|
164
|
+
*.pfx
|
|
165
|
+
secrets/
|
|
166
|
+
credentials/
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Secret Detection Patterns
|
|
172
|
+
|
|
173
|
+
| Pattern | Example |
|
|
174
|
+
| --------------- | ------------------------------- |
|
|
175
|
+
| API Keys | `Key = "sk_live_..."` |
|
|
176
|
+
| Passwords | `password = "..."` |
|
|
177
|
+
| Tokens | `token = "..."` |
|
|
178
|
+
| Private Keys | `BEGIN PRIVATE KEY` |
|
|
179
|
+
| AWS Credentials | `AWS_ACCESS_KEY_ID = "AKIA..."` |
|
|
180
|
+
| JWT Secrets | `jwtSecret = "..."` |
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## CWE References
|
|
185
|
+
|
|
186
|
+
- **CWE-798**: Use of Hard-coded Credentials
|
|
187
|
+
- **CWE-312**: Cleartext Storage of Sensitive Information
|
|
188
|
+
- **CWE-532**: Insertion of Sensitive Information into Log File
|
|
189
|
+
- **CWE-359**: Exposure of Private Personal Information
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Third-Party Data Leak Rules
|
|
2
|
+
|
|
3
|
+
Third-party monitoring and analytics services can inadvertently transmit sensitive user data to external systems.
|
|
4
|
+
|
|
5
|
+
**Rules:**
|
|
6
|
+
|
|
7
|
+
1. PII MUST be filtered before sending to third-party services.
|
|
8
|
+
2. Error tracking MUST NOT receive raw user data — use `BeforeSend` hooks to redact.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
These rules detect Go code that sends data to third-party services. Always review what data is being transmitted and ensure it complies with privacy regulations (GDPR, CCPA, etc.).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Common Vulnerable Services
|
|
19
|
+
|
|
20
|
+
| Service | Risk |
|
|
21
|
+
| ---------------- | ----------------------------------------------------- |
|
|
22
|
+
| Airbrake | Error tracking - sensitive data may be sent |
|
|
23
|
+
| Bugsnag | Error tracking - sensitive data exposure |
|
|
24
|
+
| Sentry | Error tracking - sensitive data in breadcrumbs/events |
|
|
25
|
+
| Rollbar | Error tracking - sensitive data leaks |
|
|
26
|
+
| Honeybadger | Error tracking - sensitive data leaks |
|
|
27
|
+
| New Relic | Monitoring - sensitive data exposure |
|
|
28
|
+
| Datadog | Monitoring - sensitive data in telemetry |
|
|
29
|
+
| OpenTelemetry | Observability - sensitive data in traces/metrics |
|
|
30
|
+
| Google Analytics | Analytics - PII tracking risks |
|
|
31
|
+
| Algolia | Search API - data exfiltration risks |
|
|
32
|
+
| Segment | Analytics - PII tracking risks |
|
|
33
|
+
| BigQuery | Analytics - sensitive data in queries |
|
|
34
|
+
| ClickHouse | Database - sensitive data queries |
|
|
35
|
+
| Elasticsearch | Search engine - sensitive data in queries |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Error Tracking Services — Medium
|
|
40
|
+
|
|
41
|
+
**Bad:**
|
|
42
|
+
|
|
43
|
+
```go
|
|
44
|
+
import "github.com/getsentry/sentry-go"
|
|
45
|
+
|
|
46
|
+
sentry.CaptureException(err) // DON'T: Captures full request context
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Good:**
|
|
50
|
+
|
|
51
|
+
```go
|
|
52
|
+
sentry.Init(sentry.ClientOptions{
|
|
53
|
+
Dsn: "https://xxx@sentry.io/123",
|
|
54
|
+
RequestHeaders: []string{"Accept", "User-Agent"},
|
|
55
|
+
BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
|
|
56
|
+
// Remove sensitive headers
|
|
57
|
+
if event.Request != nil {
|
|
58
|
+
delete(event.Request.Headers, "Authorization")
|
|
59
|
+
delete(event.Request.Headers, "Cookie")
|
|
60
|
+
}
|
|
61
|
+
return event
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Analytics/Monitoring Services — Medium
|
|
69
|
+
|
|
70
|
+
**Bad:**
|
|
71
|
+
|
|
72
|
+
```go
|
|
73
|
+
analytics.Track("user_signed_up", analytics.Properties{
|
|
74
|
+
"email": user.Email, // DON'T: PII!
|
|
75
|
+
"phone": user.Phone, // DON'T: PII!
|
|
76
|
+
"address": user.Address, // DON'T: PII!
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Good:**
|
|
81
|
+
|
|
82
|
+
```go
|
|
83
|
+
analytics.Track("user_signed_up", analytics.Properties{
|
|
84
|
+
"user_id": user.ID, // OK: Internal identifier
|
|
85
|
+
"plan": user.Plan, // OK: Business data
|
|
86
|
+
"country": user.CountryCode, // OK: Non-identifying
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// Hash PII for correlation
|
|
90
|
+
func hashEmail(email string) string {
|
|
91
|
+
h := sha256.New()
|
|
92
|
+
h.Write([]byte(email))
|
|
93
|
+
return hex.EncodeToString(h.Sum(nil))[:8]
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Data Filtering Layer
|
|
100
|
+
|
|
101
|
+
```go
|
|
102
|
+
type DataFilter struct {
|
|
103
|
+
sensitiveFields []string
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func NewDataFilter() *DataFilter {
|
|
107
|
+
return &DataFilter{
|
|
108
|
+
sensitiveFields: []string{
|
|
109
|
+
"password", "token", "secret", "key", "email",
|
|
110
|
+
"phone", "address", "ssn", "credit_card", "bank_account",
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func (f *DataFilter) Filter(data map[string]interface{}) map[string]interface{} {
|
|
116
|
+
result := make(map[string]interface{})
|
|
117
|
+
for k, v := range data {
|
|
118
|
+
keyLower := strings.ToLower(k)
|
|
119
|
+
isSensitive := false
|
|
120
|
+
for _, field := range f.sensitiveFields {
|
|
121
|
+
if strings.Contains(keyLower, field) {
|
|
122
|
+
isSensitive = true
|
|
123
|
+
break
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if isSensitive {
|
|
127
|
+
result[k] = "[REDACTED]"
|
|
128
|
+
} else {
|
|
129
|
+
result[k] = v
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return result
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Review Checklist
|
|
139
|
+
|
|
140
|
+
Before integrating any third-party service:
|
|
141
|
+
|
|
142
|
+
- [ ] Identify what data is being sent
|
|
143
|
+
- [ ] Remove any PII/PHI from transmitted data
|
|
144
|
+
- [ ] Review data residency requirements
|
|
145
|
+
- [ ] Implement data retention policies
|
|
146
|
+
- [ ] Set up data export logging/auditing
|
|
147
|
+
- [ ] Configure error handling to avoid data exposure
|
|
148
|
+
- [ ] Review terms of service for data usage
|
|
149
|
+
- [ ] Implement user consent management
|
|
150
|
+
- [ ] Support data deletion requests
|
|
151
|
+
- [ ] Conduct regular data flow audits
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## CWE References
|
|
156
|
+
|
|
157
|
+
- **CWE-200**: Exposure of Sensitive Information
|
|
158
|
+
- **CWE-359**: Exposure of Private Personal Information
|
|
159
|
+
- **CWE-201**: Information Exposure Through Sent Data
|