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,315 @@
|
|
|
1
|
+
# Injection Security Rules
|
|
2
|
+
|
|
3
|
+
Injection vulnerabilities allow attackers to execute arbitrary code, queries, or commands.
|
|
4
|
+
|
|
5
|
+
**Rules:**
|
|
6
|
+
|
|
7
|
+
1. SQL queries MUST use parameterized placeholders — NEVER concatenate user input.
|
|
8
|
+
2. Command execution MUST use `exec.Command` with separate args — NEVER shell interpolation.
|
|
9
|
+
3. HTML output MUST use `html/template` for automatic escaping.
|
|
10
|
+
4. SSRF: outbound URLs MUST be validated against an allowlist.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## SQL Injection — Critical
|
|
15
|
+
|
|
16
|
+
Building SQL queries by concatenating user input. Always use prepared statements with placeholders.
|
|
17
|
+
|
|
18
|
+
**Bad:**
|
|
19
|
+
|
|
20
|
+
```go
|
|
21
|
+
query := fmt.Sprintf("SELECT * FROM users WHERE name = '%s'", input)
|
|
22
|
+
query := "SELECT * FROM users WHERE id = " + id
|
|
23
|
+
query := "DELETE FROM orders WHERE id = " + strconv.Itoa(orderID) // safe but inconsistent — use placeholders everywhere
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Good:**
|
|
27
|
+
|
|
28
|
+
```go
|
|
29
|
+
// Placeholder syntax varies by driver: $1 (pgx/lib/pq), ? (MySQL/SQLite)
|
|
30
|
+
db.QueryRow("SELECT * FROM users WHERE name = $1", input)
|
|
31
|
+
db.Exec("DELETE FROM orders WHERE id = $1", orderID)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Dynamic IN clauses
|
|
35
|
+
|
|
36
|
+
Never build `IN (...)` by joining user strings. Generate numbered placeholders.
|
|
37
|
+
|
|
38
|
+
**Bad:**
|
|
39
|
+
|
|
40
|
+
```go
|
|
41
|
+
query := fmt.Sprintf("SELECT * FROM users WHERE id IN (%s)", strings.Join(ids, ","))
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Good:**
|
|
45
|
+
|
|
46
|
+
```go
|
|
47
|
+
// Build placeholders: $1, $2, $3, ...
|
|
48
|
+
placeholders := make([]string, len(ids))
|
|
49
|
+
args := make([]any, len(ids))
|
|
50
|
+
for i, id := range ids {
|
|
51
|
+
placeholders[i] = fmt.Sprintf("$%d", i+1)
|
|
52
|
+
args[i] = id
|
|
53
|
+
}
|
|
54
|
+
query := fmt.Sprintf("SELECT * FROM users WHERE id IN (%s)", strings.Join(placeholders, ","))
|
|
55
|
+
rows, err := db.Query(query, args...)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
With `sqlx`:
|
|
59
|
+
|
|
60
|
+
```go
|
|
61
|
+
query, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids)
|
|
62
|
+
query = db.Rebind(query) // converts ? to $1,$2,... for postgres
|
|
63
|
+
rows, err := db.Query(query, args...)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Dynamic column names and ORDER BY
|
|
67
|
+
|
|
68
|
+
Placeholders only work for **values**, not identifiers (table/column names) or SQL keywords. Allowlist identifiers explicitly.
|
|
69
|
+
|
|
70
|
+
**Bad:**
|
|
71
|
+
|
|
72
|
+
```go
|
|
73
|
+
query := fmt.Sprintf("SELECT * FROM users ORDER BY %s", sortCol) // SQL injection
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Good:**
|
|
77
|
+
|
|
78
|
+
```go
|
|
79
|
+
allowed := map[string]string{
|
|
80
|
+
"name": "name", "created": "created_at", "email": "email",
|
|
81
|
+
}
|
|
82
|
+
col, ok := allowed[sortCol]
|
|
83
|
+
if !ok {
|
|
84
|
+
col = "created_at"
|
|
85
|
+
}
|
|
86
|
+
query := fmt.Sprintf("SELECT * FROM users ORDER BY %s", col) // safe: col is from allowlist
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Dynamic WHERE filters
|
|
90
|
+
|
|
91
|
+
Build queries incrementally; parameterize every user-supplied value.
|
|
92
|
+
|
|
93
|
+
```go
|
|
94
|
+
var conditions []string
|
|
95
|
+
var args []any
|
|
96
|
+
idx := 1
|
|
97
|
+
|
|
98
|
+
if name != "" {
|
|
99
|
+
conditions = append(conditions, fmt.Sprintf("name = $%d", idx))
|
|
100
|
+
args = append(args, name)
|
|
101
|
+
idx++
|
|
102
|
+
}
|
|
103
|
+
if minAge > 0 {
|
|
104
|
+
conditions = append(conditions, fmt.Sprintf("age >= $%d", idx))
|
|
105
|
+
args = append(args, minAge)
|
|
106
|
+
idx++
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
query := "SELECT * FROM users"
|
|
110
|
+
if len(conditions) > 0 {
|
|
111
|
+
query += " WHERE " + strings.Join(conditions, " AND ")
|
|
112
|
+
}
|
|
113
|
+
rows, err := db.Query(query, args...)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Prefer `sqlx` or `pgx` over raw `database/sql`
|
|
117
|
+
|
|
118
|
+
Libraries like `sqlx` and `pgx` provide safer ergonomics (named parameters, `IN` clause expansion, struct scanning) while still using prepared statements under the hood. They reduce the temptation to fall back to string concatenation for complex queries.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## XPath Injection — High
|
|
123
|
+
|
|
124
|
+
XPath injection allows manipulation of XML data queries.
|
|
125
|
+
|
|
126
|
+
**Bad:**
|
|
127
|
+
|
|
128
|
+
```go
|
|
129
|
+
xpathQuery := "//user[@username='" + username + "']" // Vulnerable
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Good:**
|
|
133
|
+
|
|
134
|
+
```go
|
|
135
|
+
// Use numeric ID
|
|
136
|
+
xpathQuery := fmt.Sprintf("//user[@id='%d']", userID)
|
|
137
|
+
|
|
138
|
+
// Or parse XML without XPath
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Code Injection — Critical
|
|
144
|
+
|
|
145
|
+
Generating code from unvalidated user input.
|
|
146
|
+
|
|
147
|
+
**Bad:**
|
|
148
|
+
|
|
149
|
+
```go
|
|
150
|
+
template := "func handle" + resourceName + "() {...}" // DON'T
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Good:**
|
|
154
|
+
|
|
155
|
+
```go
|
|
156
|
+
// Validate resource name matches whitelist
|
|
157
|
+
if !allowedResources[resourceName] {
|
|
158
|
+
return errors.New("invalid resource")
|
|
159
|
+
}
|
|
160
|
+
// Use predefined templates
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Command Injection — Critical
|
|
166
|
+
|
|
167
|
+
Passing unvalidated input to shell commands.
|
|
168
|
+
|
|
169
|
+
**Bad:**
|
|
170
|
+
|
|
171
|
+
```go
|
|
172
|
+
cmd := exec.Command("sh", "-c", "rm -f /tmp/"+filename) // DON'T
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Good:**
|
|
176
|
+
|
|
177
|
+
```go
|
|
178
|
+
cmd := exec.Command("rm", "-f", filepath.Join("/tmp", filename))
|
|
179
|
+
|
|
180
|
+
// Better: validate filename
|
|
181
|
+
if filepath.Base(filename) != filename {
|
|
182
|
+
return errors.New("invalid filename")
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Template Injection — High
|
|
189
|
+
|
|
190
|
+
Using untrusted input in templates.
|
|
191
|
+
|
|
192
|
+
**Bad:**
|
|
193
|
+
|
|
194
|
+
```go
|
|
195
|
+
data := r.URL.Query().Get("user") // Untrusted input
|
|
196
|
+
t.Execute(w, data)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Good:**
|
|
200
|
+
|
|
201
|
+
```go
|
|
202
|
+
// Validate input
|
|
203
|
+
user := strings.TrimSpace(r.URL.Query().Get("user"))
|
|
204
|
+
if !allowedRoles[role] {
|
|
205
|
+
role = "user"
|
|
206
|
+
}
|
|
207
|
+
t.Execute(w, data)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Cross-Site Scripting (XSS) — High
|
|
213
|
+
|
|
214
|
+
XSS allows attackers to execute malicious scripts.
|
|
215
|
+
|
|
216
|
+
**Bad:**
|
|
217
|
+
|
|
218
|
+
```go
|
|
219
|
+
w.Write([]byte(fmt.Sprintf("<div>%s</div>", data))) // DON'T
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Good:**
|
|
223
|
+
|
|
224
|
+
```go
|
|
225
|
+
import "html/template"
|
|
226
|
+
t := template.Must(template.New("safe").Parse("<div>{{.}}</div>"))
|
|
227
|
+
t.Execute(w, data) // Auto-escapes
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## HTML Tag Injection — High
|
|
233
|
+
|
|
234
|
+
Injecting HTML tags through unvalidated input.
|
|
235
|
+
|
|
236
|
+
**Bad:**
|
|
237
|
+
|
|
238
|
+
```go
|
|
239
|
+
fmt.Fprintf(w, "<div>Welcome, %s!</div>", input) // DON'T
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Good:**
|
|
243
|
+
|
|
244
|
+
```go
|
|
245
|
+
import "html"
|
|
246
|
+
escaped := html.EscapeString(input)
|
|
247
|
+
fmt.Fprintf(w, "<div>Welcome, %s!</div>", escaped)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Server-Side Request Forgery (SSRF) — High
|
|
253
|
+
|
|
254
|
+
Forcing the server to make requests to unintended endpoints.
|
|
255
|
+
|
|
256
|
+
**Bad:**
|
|
257
|
+
|
|
258
|
+
```go
|
|
259
|
+
url := r.URL.Query().Get("url")
|
|
260
|
+
resp, _ := http.Get(url) // DON'T: No validation
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Good:**
|
|
264
|
+
|
|
265
|
+
```go
|
|
266
|
+
u, err := url.Parse(targetURL)
|
|
267
|
+
// Block non-HTTP/S protocols
|
|
268
|
+
if u.Scheme != "http" && u.Scheme != "https" {
|
|
269
|
+
return errors.New("invalid scheme")
|
|
270
|
+
}
|
|
271
|
+
// Block internal hosts
|
|
272
|
+
if isInternalIP(u.Hostname()) {
|
|
273
|
+
return errors.New("internal host not allowed")
|
|
274
|
+
}
|
|
275
|
+
// Block metadata endpoints
|
|
276
|
+
if strings.Contains(u.Hostname(), "metadata.") {
|
|
277
|
+
return errors.New("metadata endpoint blocked")
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Unsafe Deserialization — Critical
|
|
284
|
+
|
|
285
|
+
Deserializing untrusted input can lead to resource exhaustion, type confusion, or unsafe object construction.
|
|
286
|
+
|
|
287
|
+
**Bad:**
|
|
288
|
+
|
|
289
|
+
```go
|
|
290
|
+
dec := gob.NewDecoder(r.Body) // DON'T: gob is not hardened for adversarial input
|
|
291
|
+
var user interface{}
|
|
292
|
+
dec.Decode(&user)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Good:**
|
|
296
|
+
|
|
297
|
+
```go
|
|
298
|
+
import "encoding/json"
|
|
299
|
+
dec := json.NewDecoder(r.Body)
|
|
300
|
+
var user User
|
|
301
|
+
dec.Decode(&user) // JSON doesn't execute code
|
|
302
|
+
// Validate fields
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## CWE References
|
|
308
|
+
|
|
309
|
+
- **CWE-78**: OS Command Injection
|
|
310
|
+
- **CWE-89**: SQL Injection
|
|
311
|
+
- **CWE-94**: Code Injection
|
|
312
|
+
- **CWE-79**: Cross-site Scripting (XSS)
|
|
313
|
+
- **CWE-918**: Server-Side Request Forgery (SSRF)
|
|
314
|
+
- **CWE-502**: Deserialization of Untrusted Data
|
|
315
|
+
- **CWE-20**: Improper Input Validation
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Logging Security Rules
|
|
2
|
+
|
|
3
|
+
Logging sensitive information can lead to data exposure and compliance violations.
|
|
4
|
+
|
|
5
|
+
**Rules:**
|
|
6
|
+
|
|
7
|
+
1. PII MUST NEVER be logged — filter passwords, tokens, emails, and personal data.
|
|
8
|
+
2. Log injection MUST be prevented — sanitize user input before logging.
|
|
9
|
+
3. Error messages MUST NOT expose internals to users — log details server-side, return generic messages.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Sensitive Data in Logs — Medium
|
|
14
|
+
|
|
15
|
+
**Bad:**
|
|
16
|
+
|
|
17
|
+
```go
|
|
18
|
+
type User struct {
|
|
19
|
+
ID string
|
|
20
|
+
Username string
|
|
21
|
+
Password string
|
|
22
|
+
Token string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
func logUserLogin(user *User) {
|
|
26
|
+
log.Printf("User logged in: %+v\n", user) // DON'T: Logs password, token
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Good:**
|
|
31
|
+
|
|
32
|
+
```go
|
|
33
|
+
import "log/slog"
|
|
34
|
+
|
|
35
|
+
func logUserLogin(logger *slog.Logger, user *User) {
|
|
36
|
+
logger.Info("user_login",
|
|
37
|
+
"user_id", user.ID,
|
|
38
|
+
"username", user.Username,
|
|
39
|
+
// Don't log: password, token
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Log Injection — Low
|
|
47
|
+
|
|
48
|
+
User input in logs can lead to log injection attacks.
|
|
49
|
+
|
|
50
|
+
**Bad:**
|
|
51
|
+
|
|
52
|
+
```go
|
|
53
|
+
log.Printf("User logged in: %s\n", username) // DON'T: No sanitization
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Good:**
|
|
57
|
+
|
|
58
|
+
```go
|
|
59
|
+
import "log/slog"
|
|
60
|
+
|
|
61
|
+
// Sanitize user input before logging
|
|
62
|
+
func sanitizeLogInput(input string) string {
|
|
63
|
+
// Remove control characters
|
|
64
|
+
var result strings.Builder
|
|
65
|
+
for _, r := range input {
|
|
66
|
+
if !unicode.IsControl(r) || r == '\n' || r == '\t' {
|
|
67
|
+
result.WriteRune(r)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result.String()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func logUsername(logger *slog.Logger, username string) {
|
|
74
|
+
sanitized := sanitizeLogInput(username)
|
|
75
|
+
logger.Info("user_login", "username", sanitized)
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Information Leakage in Error Messages — Medium
|
|
82
|
+
|
|
83
|
+
**Bad:**
|
|
84
|
+
|
|
85
|
+
```go
|
|
86
|
+
func handleDatabaseError(err error) error {
|
|
87
|
+
return fmt.Errorf("database error: %v", err) // DON'T: Leaks internal details
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
func dbErrorToHTTP(err error) {
|
|
91
|
+
http.Error(w, "Error: "+err.Error(), 500) // DON'T
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Good:**
|
|
96
|
+
|
|
97
|
+
```go
|
|
98
|
+
func handleDatabaseError(logger *slog.Logger, err error) error {
|
|
99
|
+
// Log detailed error for debugging
|
|
100
|
+
logger.Error("database_error", "error", err.Error())
|
|
101
|
+
// Return generic message to client
|
|
102
|
+
return errors.New("database operation failed")
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
func dbErrorToHTTP(w http.ResponseWriter, logger *slog.Logger, err error) {
|
|
106
|
+
logger.Error("database_error", "error", err.Error())
|
|
107
|
+
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## General Logger Security — Low
|
|
114
|
+
|
|
115
|
+
**Bad:**
|
|
116
|
+
|
|
117
|
+
```go
|
|
118
|
+
import "log"
|
|
119
|
+
log.Println("User logged in:", user.ID, password) // DON'T: Logs password
|
|
120
|
+
fmt.Printf("DEBUG: %+v\n", data) // DON'T: Raw data
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Good:**
|
|
124
|
+
|
|
125
|
+
```go
|
|
126
|
+
import "log/slog"
|
|
127
|
+
|
|
128
|
+
handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
|
129
|
+
Level: slog.LevelInfo,
|
|
130
|
+
AddSource: false,
|
|
131
|
+
})
|
|
132
|
+
logger := slog.New(handler)
|
|
133
|
+
|
|
134
|
+
logger.Info("user_login",
|
|
135
|
+
"user_id", userID,
|
|
136
|
+
"ip_address", request.RemoteIP,
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Log Security Checklist
|
|
143
|
+
|
|
144
|
+
- [ ] No passwords, tokens, or secrets in logs
|
|
145
|
+
- [ ] No PII/PHI in production logs
|
|
146
|
+
- [ ] Sanitize user input before logging
|
|
147
|
+
- [ ] Use structured logging (JSON)
|
|
148
|
+
- [ ] Implement log level strategy
|
|
149
|
+
- [ ] Separate access logs from error logs
|
|
150
|
+
- [ ] Log file permissions restricted (e.g., 600)
|
|
151
|
+
- [ ] Log rotation prevents disk exhaustion
|
|
152
|
+
- [ ] Generic error messages to clients
|
|
153
|
+
- [ ] Detailed errors only in internal logs
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## CWE References
|
|
158
|
+
|
|
159
|
+
- **CWE-532**: Insertion of Sensitive Information into Log File
|
|
160
|
+
- **CWE-117**: Improper Output Neutralization for Logs
|
|
161
|
+
- **CWE-209**: Information Exposure Through an Error Message
|
|
162
|
+
- **CWE-200**: Exposure of Sensitive Information
|
|
163
|
+
- **CWE-312**: Cleartext Storage of Sensitive Information
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Memory Safety Security Rules
|
|
2
|
+
|
|
3
|
+
Memory safety vulnerabilities can lead to crashes, data corruption, and security compromises.
|
|
4
|
+
|
|
5
|
+
**Rules:**
|
|
6
|
+
|
|
7
|
+
1. Integer overflow MUST be checked at boundaries — NEVER trust unchecked arithmetic on external input.
|
|
8
|
+
2. `unsafe` MUST NOT be used in application code — restrict to low-level libraries with thorough review.
|
|
9
|
+
3. Data races MUST be detected with `-race` flag in CI.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Integer Overflow — High
|
|
14
|
+
|
|
15
|
+
Integer overflows can cause unexpected behavior and crashes.
|
|
16
|
+
|
|
17
|
+
**Bad:**
|
|
18
|
+
|
|
19
|
+
```go
|
|
20
|
+
func allocateBuffer(rows, cols int) []byte {
|
|
21
|
+
size := rows * cols // DON'T: Can overflow
|
|
22
|
+
return make([]byte, size)
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Good:**
|
|
27
|
+
|
|
28
|
+
```go
|
|
29
|
+
import "math"
|
|
30
|
+
|
|
31
|
+
func safeMultiply(a, b int) (int, error) {
|
|
32
|
+
if a == 0 || b == 0 {
|
|
33
|
+
return 0, nil
|
|
34
|
+
}
|
|
35
|
+
if a > math.MaxInt/b {
|
|
36
|
+
return 0, errors.New("integer overflow")
|
|
37
|
+
}
|
|
38
|
+
result := a * b
|
|
39
|
+
if result/b != a {
|
|
40
|
+
return 0, errors.New("overflow detected")
|
|
41
|
+
}
|
|
42
|
+
return result, nil
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func allocateBuffer(rows, cols int) ([]byte, error) {
|
|
46
|
+
size, err := safeMultiply(rows, cols)
|
|
47
|
+
if err != nil {
|
|
48
|
+
return nil, err
|
|
49
|
+
}
|
|
50
|
+
const maxBufferSize = 100 * 1024 * 1024 // 100MB limit
|
|
51
|
+
if size > maxBufferSize {
|
|
52
|
+
return nil, errors.New("buffer size exceeds limit")
|
|
53
|
+
}
|
|
54
|
+
return make([]byte, size), nil
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## math/big.Rat Issues — Low
|
|
61
|
+
|
|
62
|
+
Rat can consume large amounts of memory if denominators grow without bounds.
|
|
63
|
+
|
|
64
|
+
**Bad:**
|
|
65
|
+
|
|
66
|
+
```go
|
|
67
|
+
import "math/big"
|
|
68
|
+
|
|
69
|
+
func unsafeFraction(operations int) *big.Rat {
|
|
70
|
+
r := big.NewRat(1, 1)
|
|
71
|
+
for i := 0; i < operations; i++ {
|
|
72
|
+
r.Mul(r, big.NewRat(int64(i+1), int64(i+2))) // DON'T
|
|
73
|
+
}
|
|
74
|
+
return r // Could be memory intensive
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Good:**
|
|
79
|
+
|
|
80
|
+
```go
|
|
81
|
+
const maxRatNumBits = 1000
|
|
82
|
+
|
|
83
|
+
func safeFraction(operations int) (*big.Rat, error) {
|
|
84
|
+
r := big.NewRat(1, 1)
|
|
85
|
+
for i := 0; i < operations; i++ {
|
|
86
|
+
r.Mul(r, big.NewRat(int64(i+1), int64(i+2)))
|
|
87
|
+
if r.Num().BitLen() > maxRatNumBits || r.Denom().BitLen() > maxRatNumBits {
|
|
88
|
+
return nil, errors.New("fraction precision too large")
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return r, nil
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Memory Aliasing Vulnerability — Medium
|
|
98
|
+
|
|
99
|
+
Memory aliasing can cause data corruption and race conditions.
|
|
100
|
+
|
|
101
|
+
**Bad:**
|
|
102
|
+
|
|
103
|
+
```go
|
|
104
|
+
func reverseBytes(data []byte) {
|
|
105
|
+
for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
|
|
106
|
+
data[i], data[j] = data[j], data[i] // DON'T if slices alias
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Good:**
|
|
112
|
+
|
|
113
|
+
```go
|
|
114
|
+
import "unsafe"
|
|
115
|
+
|
|
116
|
+
func checkOverlap(a, b []byte) bool {
|
|
117
|
+
if len(a) == 0 || len(b) == 0 {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
aStart := uintptr(unsafe.Pointer(&a[0]))
|
|
121
|
+
aEnd := aStart + uintptr(len(a))
|
|
122
|
+
bStart := uintptr(unsafe.Pointer(&b[0]))
|
|
123
|
+
bEnd := bStart + uintptr(len(b))
|
|
124
|
+
return aStart < bEnd && bStart < aEnd
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func safeCopy(dest, src []byte) {
|
|
128
|
+
if checkOverlap(dest, src) {
|
|
129
|
+
temp := make([]byte, len(src))
|
|
130
|
+
copy(temp, src)
|
|
131
|
+
copy(dest, temp)
|
|
132
|
+
} else {
|
|
133
|
+
copy(dest, src)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Use of unsafe Package — High
|
|
141
|
+
|
|
142
|
+
The unsafe package bypasses Go's type safety and memory safety.
|
|
143
|
+
|
|
144
|
+
**Bad:**
|
|
145
|
+
|
|
146
|
+
```go
|
|
147
|
+
import "unsafe"
|
|
148
|
+
|
|
149
|
+
func UnsafeStringToBytes(s string) []byte {
|
|
150
|
+
return (*[0x7fffffff]byte)(unsafe.Pointer(
|
|
151
|
+
(*reflect.StringHeader)(unsafe.Pointer(&s)).Data,
|
|
152
|
+
))[:len(s):len(s)] // DON'T: memory corruption risk
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
func TypePun(value uint64) float64 {
|
|
156
|
+
return *(*float64)(unsafe.Pointer(&value)) // DON'T
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Good:**
|
|
161
|
+
|
|
162
|
+
```go
|
|
163
|
+
// Safe string encoding
|
|
164
|
+
func StringToBytes(s string) []byte {
|
|
165
|
+
return []byte(s)
|
|
166
|
+
}
|
|
167
|
+
func BytesToString(b []byte) string {
|
|
168
|
+
return string(b)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Safe type conversion
|
|
172
|
+
import "encoding/binary"
|
|
173
|
+
func Uint64ToFloat64(value uint64) float64 {
|
|
174
|
+
buf := make([]byte, 8)
|
|
175
|
+
binary.LittleEndian.PutUint64(buf, value)
|
|
176
|
+
bits := binary.LittleEndian.Uint64(buf)
|
|
177
|
+
return math.Float64frombits(bits)
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Data Races — High
|
|
184
|
+
|
|
185
|
+
Go's race detector is your primary defense.
|
|
186
|
+
|
|
187
|
+
**Bad:**
|
|
188
|
+
|
|
189
|
+
```go
|
|
190
|
+
type Counter struct {
|
|
191
|
+
value int
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
func (c *Counter) Increment() {
|
|
195
|
+
c.value++ // DON'T: Data race without sync
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Good:**
|
|
200
|
+
|
|
201
|
+
```go
|
|
202
|
+
import "sync"
|
|
203
|
+
|
|
204
|
+
type Counter struct {
|
|
205
|
+
value int
|
|
206
|
+
mu sync.Mutex
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
func (c *Counter) Increment() {
|
|
210
|
+
c.mu.Lock()
|
|
211
|
+
defer c.mu.Unlock()
|
|
212
|
+
c.value++
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Or atomic for simple cases
|
|
216
|
+
import "sync/atomic"
|
|
217
|
+
type AtomicCounter struct {
|
|
218
|
+
value int64
|
|
219
|
+
}
|
|
220
|
+
func (c *AtomicCounter) Increment() {
|
|
221
|
+
atomic.AddInt64(&c.value, 1)
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Always Run Race Detector
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
go test -race ./...
|
|
229
|
+
go build -race
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## CWE References
|
|
235
|
+
|
|
236
|
+
- **CWE-190**: Integer Overflow or Wraparound
|
|
237
|
+
- **CWE-119**: Improper Restriction of Operations within Bounds
|
|
238
|
+
- **CWE-125**: Out-of-bounds Read
|
|
239
|
+
- **CWE-787**: Out-of-bounds Write
|
|
240
|
+
- **CWE-362**: Race Condition
|
|
241
|
+
- **CWE-367**: Time-of-check Time-of-use (TOCTOU)
|