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.
Files changed (247) hide show
  1. package/.bun-version +1 -0
  2. package/.dockerignore +79 -0
  3. package/.editorconfig +18 -0
  4. package/.env.example +19 -0
  5. package/.github/CODEOWNERS +8 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
  7. package/.github/ISSUE_TEMPLATE/config.yml +2 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.yml +34 -0
  9. package/.github/dependabot.yml +36 -0
  10. package/.github/pull_request_template.md +24 -0
  11. package/.github/release.yml +30 -0
  12. package/.github/workflows/gitleaks.yml +28 -0
  13. package/.github/workflows/release-please.yml +27 -0
  14. package/.github/workflows/smoke.yml +29 -0
  15. package/.husky/commit-msg +1 -0
  16. package/CHANGELOG.md +114 -0
  17. package/Dockerfile +32 -0
  18. package/README.es.md +174 -0
  19. package/README.md +187 -0
  20. package/agents/chronicler.md +98 -0
  21. package/agents/ci-smith.md +136 -0
  22. package/agents/craftsman.md +341 -0
  23. package/agents/deploy-smith.md +138 -0
  24. package/agents/foreman.md +377 -0
  25. package/agents/go-smith.md +164 -0
  26. package/agents/guild.md +188 -0
  27. package/agents/inspector.md +83 -0
  28. package/agents/js-smith.md +127 -0
  29. package/agents/ops-scout.md +173 -0
  30. package/agents/painter.md +200 -0
  31. package/agents/python-smith.md +120 -0
  32. package/agents/ranger.md +307 -0
  33. package/agents/release-smith.md +165 -0
  34. package/agents/rust-smith.md +159 -0
  35. package/agents/sage.md +178 -0
  36. package/agents/scout.md +144 -0
  37. package/agents/scribe.md +156 -0
  38. package/agents/smith.md +201 -0
  39. package/agents/vue-smith.md +155 -0
  40. package/agents/warden.md +216 -0
  41. package/agents/zig-smith.md +156 -0
  42. package/bin/ndomo-analyses.ts +4 -0
  43. package/bin/ndomo-status.ts +4 -0
  44. package/biome.json +57 -0
  45. package/bun.lock +514 -0
  46. package/commitlint.config.js +3 -0
  47. package/config/ndomo.config.json +258 -0
  48. package/config/ndomo.schema.json +166 -0
  49. package/docs/agents.md +375 -0
  50. package/docs/bugs/plan-create-orphan-fk.md +131 -0
  51. package/docs/bugs/task_create_batch-order-index-collision.md +158 -0
  52. package/docs/configuration.md +276 -0
  53. package/docs/database.md +364 -0
  54. package/docs/features/feature-flexible-builder-v1.md +724 -0
  55. package/docs/features/feature-flexible-builder-v2.md +882 -0
  56. package/docs/features/feature-flexible-builder.md +974 -0
  57. package/docs/http-server.md +244 -0
  58. package/docs/installation.md +259 -0
  59. package/docs/integrations.md +129 -0
  60. package/docs/operations/anti-pattern-sub-agent-verify-2026-06-21.md +32 -0
  61. package/docs/operations/audit-v1.md +417 -0
  62. package/docs/operations/audit-v2.md +197 -0
  63. package/docs/operations/audit-v3.md +306 -0
  64. package/docs/operations/db-optimize-foundations.md +123 -0
  65. package/docs/operations/verify-gate-architecture.md +82 -0
  66. package/docs/workflows.md +448 -0
  67. package/opencode.json +5 -0
  68. package/package.json +65 -0
  69. package/release-please-config.json +11 -0
  70. package/scripts/dev-bust-cache.sh +164 -0
  71. package/scripts/install.sh +688 -0
  72. package/scripts/smoke-e2e.ts +704 -0
  73. package/scripts/smoke-hot.ts +417 -0
  74. package/scripts/smoke-http.sh +228 -0
  75. package/scripts/smoke-v4.ts +256 -0
  76. package/scripts/smoke-v5.ts +397 -0
  77. package/scripts/smoke.sh +9 -0
  78. package/scripts/uninstall.sh +224 -0
  79. package/skills/api-security-best-practices/SKILL.md +915 -0
  80. package/skills/bash-scripting/SKILL.md +201 -0
  81. package/skills/bun/SKILL.md +313 -0
  82. package/skills/cavecrew/SKILL.md +82 -0
  83. package/skills/caveman/SKILL.md +74 -0
  84. package/skills/caveman-review/README.md +33 -0
  85. package/skills/caveman-review/SKILL.md +55 -0
  86. package/skills/find-skills/SKILL.md +142 -0
  87. package/skills/frontend-design/LICENSE.txt +177 -0
  88. package/skills/frontend-design/SKILL.md +55 -0
  89. package/skills/golang-patterns/SKILL.md +674 -0
  90. package/skills/golang-security/SKILL.md +185 -0
  91. package/skills/golang-security/evals/evals.json +595 -0
  92. package/skills/golang-security/references/architecture.md +268 -0
  93. package/skills/golang-security/references/checklist.md +80 -0
  94. package/skills/golang-security/references/cookies.md +200 -0
  95. package/skills/golang-security/references/cryptography.md +424 -0
  96. package/skills/golang-security/references/filesystem.md +285 -0
  97. package/skills/golang-security/references/injection.md +315 -0
  98. package/skills/golang-security/references/logging.md +163 -0
  99. package/skills/golang-security/references/memory-safety.md +241 -0
  100. package/skills/golang-security/references/network.md +253 -0
  101. package/skills/golang-security/references/secrets.md +189 -0
  102. package/skills/golang-security/references/third-party.md +159 -0
  103. package/skills/golang-security/references/threat-modeling.md +189 -0
  104. package/skills/golang-testing/SKILL.md +720 -0
  105. package/skills/grill-me/SKILL.md +7 -0
  106. package/skills/javascript-testing-patterns/SKILL.md +537 -0
  107. package/skills/javascript-testing-patterns/references/advanced-testing-patterns.md +513 -0
  108. package/skills/modern-javascript-patterns/SKILL.md +43 -0
  109. package/skills/modern-javascript-patterns/references/advanced-patterns.md +487 -0
  110. package/skills/modern-javascript-patterns/references/details.md +457 -0
  111. package/skills/python-anti-patterns/SKILL.md +349 -0
  112. package/skills/python-design-patterns/SKILL.md +85 -0
  113. package/skills/python-design-patterns/references/details.md +353 -0
  114. package/skills/python-error-handling/SKILL.md +193 -0
  115. package/skills/python-error-handling/references/details.md +171 -0
  116. package/skills/python-testing-patterns/SKILL.md +278 -0
  117. package/skills/python-testing-patterns/references/advanced-patterns.md +411 -0
  118. package/skills/python-testing-patterns/references/details.md +349 -0
  119. package/skills/rust-patterns/SKILL.md +500 -0
  120. package/skills/rust-testing/SKILL.md +501 -0
  121. package/skills/security-review/SKILL.md +504 -0
  122. package/skills/security-review/cloud-infrastructure-security.md +361 -0
  123. package/skills/vue-best-practices/SKILL.md +154 -0
  124. package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
  125. package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
  126. package/skills/vue-best-practices/references/component-async.md +97 -0
  127. package/skills/vue-best-practices/references/component-data-flow.md +307 -0
  128. package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
  129. package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
  130. package/skills/vue-best-practices/references/component-slots.md +216 -0
  131. package/skills/vue-best-practices/references/component-suspense.md +228 -0
  132. package/skills/vue-best-practices/references/component-teleport.md +108 -0
  133. package/skills/vue-best-practices/references/component-transition-group.md +128 -0
  134. package/skills/vue-best-practices/references/component-transition.md +125 -0
  135. package/skills/vue-best-practices/references/composables.md +290 -0
  136. package/skills/vue-best-practices/references/directives.md +162 -0
  137. package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
  138. package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
  139. package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
  140. package/skills/vue-best-practices/references/plugins.md +166 -0
  141. package/skills/vue-best-practices/references/reactivity.md +344 -0
  142. package/skills/vue-best-practices/references/render-functions.md +201 -0
  143. package/skills/vue-best-practices/references/sfc.md +310 -0
  144. package/skills/vue-best-practices/references/state-management.md +135 -0
  145. package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
  146. package/skills/vue-pinia-best-practices/SKILL.md +21 -0
  147. package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
  148. package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
  149. package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
  150. package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
  151. package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
  152. package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
  153. package/skills/zig-0.16/SKILL.md +840 -0
  154. package/skills/zig-0.16/scripts/check-zig-version.sh +21 -0
  155. package/src/cli/analyses.ts +280 -0
  156. package/src/cli/index.ts +108 -0
  157. package/src/cli/serve.ts +192 -0
  158. package/src/cli/smoke.ts +131 -0
  159. package/src/cli/status.test.ts +204 -0
  160. package/src/cli/status.ts +263 -0
  161. package/src/cli/vacuum.test.ts +82 -0
  162. package/src/cli/vacuum.ts +96 -0
  163. package/src/config/schema.test.ts +88 -0
  164. package/src/config/schema.ts +64 -0
  165. package/src/db/analyses-migration.test.ts +210 -0
  166. package/src/db/analyses.test.ts +466 -0
  167. package/src/db/analyses.ts +375 -0
  168. package/src/db/auto-checkpoint.ts +131 -0
  169. package/src/db/client.test.ts +129 -0
  170. package/src/db/client.ts +55 -0
  171. package/src/db/fts-escape.ts +20 -0
  172. package/src/db/incidents.test.ts +201 -0
  173. package/src/db/incidents.ts +93 -0
  174. package/src/db/index.ts +86 -0
  175. package/src/db/migrations-v13.test.ts +141 -0
  176. package/src/db/migrations-v8.test.ts +301 -0
  177. package/src/db/migrations.ts +147 -0
  178. package/src/db/plan-archive.test.ts +180 -0
  179. package/src/db/plan-archive.ts +274 -0
  180. package/src/db/plan-create.test.ts +276 -0
  181. package/src/db/plan-create.ts +78 -0
  182. package/src/db/plan-files.test.ts +289 -0
  183. package/src/db/plan-update-status.ts +287 -0
  184. package/src/db/plans.test.ts +490 -0
  185. package/src/db/plans.ts +534 -0
  186. package/src/db/resolve-project-dir.test.ts +143 -0
  187. package/src/db/resolve-project-dir.ts +75 -0
  188. package/src/db/rollbacks.test.ts +150 -0
  189. package/src/db/rollbacks.ts +67 -0
  190. package/src/db/schema.ts +907 -0
  191. package/src/db/sessions.test.ts +80 -0
  192. package/src/db/sessions.ts +135 -0
  193. package/src/db/shutdown.test.ts +147 -0
  194. package/src/db/shutdown.ts +45 -0
  195. package/src/db/tasks.test.ts +921 -0
  196. package/src/db/tasks.ts +747 -0
  197. package/src/db/types.ts +619 -0
  198. package/src/http/__tests__/auth.test.ts +196 -0
  199. package/src/http/__tests__/routes.test.ts +465 -0
  200. package/src/http/__tests__/sse.test.ts +317 -0
  201. package/src/http/auth.ts +72 -0
  202. package/src/http/middleware/cors.ts +53 -0
  203. package/src/http/middleware/security-headers.ts +21 -0
  204. package/src/http/routes/events.ts +112 -0
  205. package/src/http/routes/health.ts +51 -0
  206. package/src/http/routes/plans.ts +66 -0
  207. package/src/http/routes/sessions.ts +50 -0
  208. package/src/http/routes/tasks.ts +60 -0
  209. package/src/http/server.ts +95 -0
  210. package/src/http/sse.ts +116 -0
  211. package/src/index.ts +37 -0
  212. package/src/lib.ts +65 -0
  213. package/src/mem/scoped.ts +65 -0
  214. package/src/orchestrator/background.test.ts +268 -0
  215. package/src/orchestrator/background.ts +293 -0
  216. package/src/orchestrator/memory-hook.ts +182 -0
  217. package/src/orchestrator/reconciler.ts +123 -0
  218. package/src/orchestrator/scheduler.test.ts +300 -0
  219. package/src/orchestrator/scheduler.ts +243 -0
  220. package/src/plugin.test.ts +2574 -0
  221. package/src/plugin.ts +1690 -0
  222. package/src/sdk/client.ts +66 -0
  223. package/src/worktrees/manager.ts +236 -0
  224. package/src/worktrees/state.ts +87 -0
  225. package/tests/integration/ranger-flow.test.ts +257 -0
  226. package/tools/analysis_archive.ts +28 -0
  227. package/tools/analysis_create.ts +55 -0
  228. package/tools/analysis_get.ts +33 -0
  229. package/tools/analysis_link_plan.ts +44 -0
  230. package/tools/analysis_list.ts +48 -0
  231. package/tools/analysis_search.ts +36 -0
  232. package/tools/analysis_update.ts +44 -0
  233. package/tools/plan_approve.ts +31 -0
  234. package/tools/plan_create.ts +58 -0
  235. package/tools/plan_get.ts +40 -0
  236. package/tools/plan_list.ts +37 -0
  237. package/tools/plan_search.ts +34 -0
  238. package/tools/plan_update_status.ts +71 -0
  239. package/tools/session_checkpoint.ts +31 -0
  240. package/tools/session_end.ts +26 -0
  241. package/tools/session_start.ts +43 -0
  242. package/tools/task_create_batch.ts +70 -0
  243. package/tools/task_list.ts +35 -0
  244. package/tools/task_next_for_agent.ts +30 -0
  245. package/tools/task_search.ts +34 -0
  246. package/tools/task_update_status.ts +37 -0
  247. package/tsconfig.json +31 -0
@@ -0,0 +1,424 @@
1
+ # Cryptography Security Rules
2
+
3
+ Cryptography vulnerabilities threaten confidentiality and integrity of sensitive data.
4
+
5
+ **Rules:**
6
+
7
+ 1. TLS MUST use 1.2+.
8
+ 2. NEVER use DES, RC4, MD5, or SHA1 for security purposes.
9
+ 3. SSH host keys MUST be verified — NEVER use `InsecureIgnoreHostKey`.
10
+ 4. Passwords MUST be hashed with Argon2id (preferred) or bcrypt.
11
+ 5. Security-critical randomness MUST use `crypto/rand`.
12
+
13
+ ---
14
+
15
+ ## Algorithm Selection Guide
16
+
17
+ Choose the right algorithm for the job — using the wrong primitive (e.g. SHA256 for passwords) is as dangerous as using a broken one:
18
+
19
+ | Use Case | Recommended | Avoid | Why |
20
+ | --- | --- | --- | --- |
21
+ | Symmetric encryption | AES-256-GCM, ChaCha20-Poly1305 | DES, 3DES, AES-ECB, RC4 | ECB reveals patterns; DES/RC4 are broken |
22
+ | Password hashing | Argon2id (preferred), bcrypt, scrypt | MD5, SHA-1, plain SHA-256 | Fast hashes enable brute-force; memory-hard functions resist GPU attacks |
23
+ | Message authentication | HMAC-SHA256, Poly1305 | HMAC-MD5, HMAC-SHA1 | MD5/SHA1 have known collision weaknesses |
24
+ | Digital signatures | Ed25519, ECDSA P-256 | RSA-PKCS1v1.5 | PKCS1v1.5 has padding oracle vulnerabilities |
25
+ | Key exchange | X25519, ECDH P-256 | Static RSA key transport | Forward secrecy requires ephemeral keys |
26
+ | Random generation | `crypto/rand` | `math/rand` | `math/rand` output is predictable |
27
+ | TLS | TLS 1.2+ (prefer 1.3) | TLS 1.0, 1.1, SSL | Known attacks (BEAST, POODLE) on older versions |
28
+
29
+ ### Key Size Requirements
30
+
31
+ | Algorithm | Minimum Key Size | Recommended |
32
+ | --------- | ------------------------ | ---------------- |
33
+ | RSA | 2048 bits | 4096 bits |
34
+ | AES | 128 bits | 256 bits |
35
+ | ECDSA | P-256 (128-bit security) | P-256 or Ed25519 |
36
+
37
+ ---
38
+
39
+ ## Key Rotation Pattern
40
+
41
+ Keys should be rotated periodically. Use envelope encryption so rotating the Key Encryption Key (KEK) doesn't require re-encrypting all data:
42
+
43
+ ```go
44
+ // Envelope encryption: encrypt data with a DEK, encrypt DEK with KEK
45
+ func EnvelopeEncrypt(kek, plaintext []byte) (encryptedDEK, ciphertext []byte, err error) {
46
+ // 1. Generate random Data Encryption Key
47
+ dek := make([]byte, 32)
48
+ if _, err := rand.Read(dek); err != nil {
49
+ return nil, nil, err
50
+ }
51
+
52
+ // 2. Encrypt data with DEK
53
+ ciphertext, err = EncryptAESGCM(dek, plaintext)
54
+ if err != nil {
55
+ return nil, nil, err
56
+ }
57
+
58
+ // 3. Encrypt DEK with KEK
59
+ encryptedDEK, err = EncryptAESGCM(kek, dek)
60
+ if err != nil {
61
+ return nil, nil, err
62
+ }
63
+
64
+ return encryptedDEK, ciphertext, nil
65
+ }
66
+
67
+ func EnvelopeDecrypt(kek, encryptedDEK, ciphertext []byte) ([]byte, error) {
68
+ dek, err := DecryptAESGCM(kek, encryptedDEK)
69
+ if err != nil {
70
+ return nil, err
71
+ }
72
+ return DecryptAESGCM(dek, ciphertext)
73
+ }
74
+
75
+ func EncryptAESGCM(key, plaintext []byte) ([]byte, error) {
76
+ block, err := aes.NewCipher(key)
77
+ if err != nil { return nil, err }
78
+ aead, err := cipher.NewGCM(block)
79
+ if err != nil { return nil, err }
80
+ nonce := make([]byte, aead.NonceSize())
81
+ if _, err := rand.Read(nonce); err != nil { return nil, err }
82
+ return aead.Seal(nonce, nonce, plaintext, nil), nil
83
+ }
84
+
85
+ func DecryptAESGCM(key, ciphertext []byte) ([]byte, error) {
86
+ block, err := aes.NewCipher(key)
87
+ if err != nil { return nil, err }
88
+ aead, err := cipher.NewGCM(block)
89
+ if err != nil { return nil, err }
90
+ nonceSize := aead.NonceSize()
91
+ if len(ciphertext) < nonceSize {
92
+ return nil, errors.New("ciphertext too short")
93
+ }
94
+ return aead.Open(nil, ciphertext[:nonceSize], ciphertext[nonceSize:], nil)
95
+ }
96
+ ```
97
+
98
+ When the KEK is rotated, only re-encrypt the DEKs (small), not the data (potentially large).
99
+
100
+ ---
101
+
102
+ ## Common Cryptographic Mistakes
103
+
104
+ ### Mistake 1: AES-ECB reveals patterns — High
105
+
106
+ ECB encrypts each block independently — identical plaintext blocks produce identical ciphertext blocks, revealing data structure:
107
+
108
+ ```go
109
+ // Bad — ECB mode reveals patterns in structured data
110
+ block, _ := aes.NewCipher(key)
111
+ // Using block.Encrypt directly = ECB mode
112
+
113
+ // Good — GCM provides authenticated encryption
114
+ aead, err := cipher.NewGCM(block) // randomized, authenticated
115
+ if err != nil {
116
+ return nil, err
117
+ }
118
+ nonce := make([]byte, aead.NonceSize())
119
+ if _, err := rand.Read(nonce); err != nil {
120
+ return nil, err
121
+ }
122
+ ciphertext := aead.Seal(nonce, nonce, plaintext, nil)
123
+ ```
124
+
125
+ ### Mistake 2: Reusing nonces — Critical
126
+
127
+ A nonce reuse with AES-GCM completely breaks confidentiality and authentication:
128
+
129
+ ```go
130
+ // Bad — static or reused nonce
131
+ nonce := []byte("fixed_nonce!") // catastrophic with GCM
132
+
133
+ // Good — random nonce per encryption
134
+ nonce := make([]byte, 12) // 96-bit for GCM
135
+ if _, err := rand.Read(nonce); err != nil {
136
+ return nil, err
137
+ }
138
+ ```
139
+
140
+ ### Mistake 3: Non-constant-time comparison for secrets — Medium
141
+
142
+ Comparing secrets with `==` short-circuits on the first differing byte, leaking timing information. See [Network/Web Security — Observable Timing](./network.md) for constant-time comparison patterns using `crypto/subtle`.
143
+
144
+ ---
145
+
146
+ ## Insecure TLS Configuration — High
147
+
148
+ Using insecure TLS configurations can expose your application to man-in-the-middle attacks.
149
+
150
+ **Bad:**
151
+
152
+ ```go
153
+ transport := &http.Transport{
154
+ TLSClientConfig: &tls.Config{
155
+ InsecureSkipVerify: true, // DON'T: verify certificates
156
+ },
157
+ }
158
+ ```
159
+
160
+ **Good:**
161
+
162
+ ```go
163
+ import "crypto/tls"
164
+
165
+ func secureConfig() *tls.Config {
166
+ return &tls.Config{
167
+ MinVersion: tls.VersionTLS12,
168
+ CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
169
+ }
170
+ }
171
+ ```
172
+
173
+ ---
174
+
175
+ ## DES Encryption — High
176
+
177
+ DES is cryptographically broken.
178
+
179
+ **Bad:**
180
+
181
+ ```go
182
+ import "crypto/des"
183
+ block, _ := des.NewCipher(key) // DON'T: broken
184
+ ```
185
+
186
+ **Good:**
187
+
188
+ ```go
189
+ import "crypto/aes"
190
+ block, _ := aes.NewCipher(key) // OK: AES
191
+ cipher.NewGCM(block) // OK: GCM for auth
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Insecure SSH Host Key Verification — High
197
+
198
+ **Bad:**
199
+
200
+ ```go
201
+ import "golang.org/x/crypto/ssh"
202
+ &ssh.ClientConfig{
203
+ HostKeyCallback: ssh.InsecureIgnoreHostKey(), // DON'T
204
+ }
205
+ ```
206
+
207
+ **Good:**
208
+
209
+ ```go
210
+ import "golang.org/x/crypto/ssh"
211
+ &ssh.ClientConfig{
212
+ HostKeyCallback: ssh.FixedHostKey(publicKey),
213
+ }
214
+ ```
215
+
216
+ ---
217
+
218
+ ## MD5 Hash — High
219
+
220
+ MD5 is collision-prone and weak for security.
221
+
222
+ **Bad:**
223
+
224
+ ```go
225
+ import "crypto/md5"
226
+ hash := md5.Sum([]byte(data)) // DON'T: weak
227
+ ```
228
+
229
+ **Good:**
230
+
231
+ ```go
232
+ // For password hashing:
233
+ import "golang.org/x/crypto/argon2"
234
+ hash := argon2.IDKey([]byte(pw), salt, 3, 64*1024, 4, 32)
235
+
236
+ // Or bcrypt (simpler API, no salt management):
237
+ import "golang.org/x/crypto/bcrypt"
238
+ hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)
239
+ if err != nil {
240
+ return nil, err
241
+ }
242
+
243
+ // For general-purpose hashing (not passwords):
244
+ import "crypto/sha256"
245
+ digest := sha256.Sum256(data)
246
+ ```
247
+
248
+ ---
249
+
250
+ ## RC4 Cipher — High
251
+
252
+ RC4 is cryptographically broken.
253
+
254
+ **Bad:**
255
+
256
+ ```go
257
+ import "crypto/rc4"
258
+ cipher, _ := rc4.NewCipher(key) // DON'T: broken
259
+ ```
260
+
261
+ **Good:**
262
+
263
+ ```go
264
+ import "crypto/cipher"
265
+ import "crypto/aes"
266
+ aead, _ := cipher.NewGCM(block) // OK: AES-GCM
267
+
268
+ // Or ChaCha20:
269
+ import "golang.org/x/crypto/chacha20poly1305"
270
+ aead, _ := chacha20poly1305.New(key)
271
+ ```
272
+
273
+ ---
274
+
275
+ ## SHA1 Hash — Medium
276
+
277
+ SHA1 provides insufficient collision resistance.
278
+
279
+ **Bad:**
280
+
281
+ ```go
282
+ import "crypto/sha1"
283
+ hash := sha1.Sum(data) // DON'T: weak
284
+ ```
285
+
286
+ **Good:**
287
+
288
+ ```go
289
+ import "crypto/sha256"
290
+ hash := sha256.Sum256(data)
291
+ ```
292
+
293
+ ---
294
+
295
+ ## Weak Cryptographic Algorithms — Medium
296
+
297
+ **Bad:**
298
+
299
+ ```go
300
+ import "crypto/hmac"
301
+ import "crypto/md5"
302
+ mac := hmac.New(md5.New, key) // DON'T: HMAC-MD5
303
+ ```
304
+
305
+ **Good:**
306
+
307
+ ```go
308
+ import "crypto/sha256"
309
+ mac := hmac.New(sha256.New, key)
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Insufficient Key Strength — Medium
315
+
316
+ RSA keys smaller than 2048 bits are insufficient.
317
+
318
+ **Bad:**
319
+
320
+ ```go
321
+ import "crypto/rsa"
322
+ key, _ := rsa.GenerateKey(rand.Reader, 1024) // DON'T: too weak
323
+ ```
324
+
325
+ **Good:**
326
+
327
+ ```go
328
+ key, _ := rsa.GenerateKey(rand.Reader, 4096) // OK: 2048+ bits
329
+ ```
330
+
331
+ ---
332
+
333
+ ## Weak Random Number Generators — High
334
+
335
+ `math/rand` is predictable, never use for security.
336
+
337
+ **Bad:**
338
+
339
+ ```go
340
+ import "math/rand"
341
+ bytes := make([]byte, 16)
342
+ rand.Read(bytes) // DON'T: predictable
343
+ ```
344
+
345
+ **Good:**
346
+
347
+ ```go
348
+ import "crypto/rand"
349
+ _, err := rand.Read(bytes) // OK: cryptographically secure
350
+ ```
351
+
352
+ ---
353
+
354
+ ## Weak TLS Versions — High
355
+
356
+ TLS 1.0 and 1.1 have known vulnerabilities.
357
+
358
+ **Bad:**
359
+
360
+ ```go
361
+ import "crypto/tls"
362
+ &tls.Config{MinVersion: tls.VersionTLS10} // DON'T
363
+ ```
364
+
365
+ **Good:**
366
+
367
+ ```go
368
+ &tls.Config{MinVersion: tls.VersionTLS12} // OK
369
+ ```
370
+
371
+ ---
372
+
373
+ ## Password Hashing — High
374
+
375
+ Don't use MD5, SHA1, or single-iteration hashes for passwords.
376
+
377
+ **Bad:**
378
+
379
+ ```go
380
+ import "crypto/sha256"
381
+ hash := sha256.Sum256([]byte(password)) // DON'T: too fast
382
+ ```
383
+
384
+ **Good:**
385
+
386
+ ```go
387
+ // Argon2id (preferred) — memory-hard, resists GPU attacks:
388
+ import "golang.org/x/crypto/argon2"
389
+ key := argon2.IDKey([]byte(password), salt, 3, 64*1024, 4, 32)
390
+
391
+ // Or bcrypt (simpler API, widely supported):
392
+ import "golang.org/x/crypto/bcrypt"
393
+ hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)
394
+ if err != nil {
395
+ return nil, err
396
+ }
397
+
398
+ // Or PBKDF2 with 600,000+ iterations (Go 1.24+ stdlib):
399
+ import "crypto/pbkdf2"
400
+ key, err := pbkdf2.Key(sha512.New, password, salt, 600_000, 32)
401
+ if err != nil {
402
+ return err
403
+ }
404
+
405
+ // Or scrypt:
406
+ import "golang.org/x/crypto/scrypt"
407
+ key, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
408
+ if err != nil {
409
+ return err
410
+ }
411
+ ```
412
+
413
+ For Go 1.24+, prefer stdlib `crypto/hkdf`, `crypto/pbkdf2`, and `crypto/sha3`. Use `golang.org/x/crypto/...` fallbacks only for modules targeting older Go versions or for algorithms still outside the standard library.
414
+
415
+ ---
416
+
417
+ ## CWE References
418
+
419
+ - **CWE-327**: Use of a Broken or Risky Cryptographic Algorithm
420
+ - **CWE-331**: Insufficient Entropy
421
+ - **CWE-326**: Inadequate Encryption Strength
422
+ - **CWE-295**: Improper Certificate Validation
423
+ - **CWE-330**: Use of Insufficiently Random Values
424
+ - **CWE-916**: Use of Password Hash With Insufficient Computational Effort
@@ -0,0 +1,285 @@
1
+ # Filesystem Security Rules
2
+
3
+ Filesystem vulnerabilities can lead to unauthorized file access, data leakage, and denial-of-service attacks.
4
+
5
+ **Rules:**
6
+
7
+ 1. User-controlled file paths MUST be confined to an allowed root.
8
+ 2. `os.Root` SHOULD be used for scoped file access (Go 1.24+).
9
+ 3. Zip extraction MUST check for ZipSlip path traversal.
10
+ 4. Temporary files MUST use `os.CreateTemp` — NEVER predictable names.
11
+ 5. File permissions MUST be restrictive (0600 for secrets, 0750 for directories).
12
+
13
+ ---
14
+
15
+ ## Directory Traversal — High
16
+
17
+ Paths like `../../etc/passwd` access files outside intended directory.
18
+
19
+ **Bad:**
20
+
21
+ ```go
22
+ filepath := filepath.Join("/var/www", filename) // DON'T
23
+ http.ServeFile(w, r, filepath)
24
+ ```
25
+
26
+ **Good (Go 1.24+) — use `os.Root` for safe, scoped directory access:**
27
+
28
+ ```go
29
+ root, err := os.OpenRoot("/var/www")
30
+ if err != nil { return err }
31
+ defer root.Close()
32
+ f, err := root.Open(filename) // cannot escape root directory
33
+ ```
34
+
35
+ `os.Root` prevents ordinary path traversal at the OS level. All operations (`Open`, `Create`, `Stat`, `OpenFile`, etc.) are confined to the root directory, and symlinks that resolve outside the root are rejected. It is not a full sandbox: it does not by itself block bind mounts, special device files, or all `/proc`-style filesystem behavior. For archive extraction and uploads, still reject special files and choose a root without attacker-controlled mounts.
36
+
37
+ **Good (pre-Go 1.24 fallback):**
38
+
39
+ ```go
40
+ func safeJoin(baseDir, userPath string) (string, error) {
41
+ if userPath == "" || filepath.IsAbs(userPath) || !filepath.IsLocal(userPath) {
42
+ return "", errors.New("invalid relative path")
43
+ }
44
+
45
+ full := filepath.Join(baseDir, userPath)
46
+
47
+ rel, err := filepath.Rel(baseDir, full)
48
+ if err != nil {
49
+ return "", fmt.Errorf("checking path: %w", err)
50
+ }
51
+ if rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
52
+ return "", errors.New("path escapes base directory")
53
+ }
54
+
55
+ return full, nil
56
+ }
57
+ ```
58
+
59
+ This lexical fallback is not a full symlink-resistant substitute for `os.Root`.
60
+
61
+ **Bad:**
62
+
63
+ ```go
64
+ fullPath := filepath.Join(baseDir, filename)
65
+ if !strings.HasPrefix(filepath.Clean(fullPath), filepath.Clean(baseDir)) {
66
+ return errors.New("access denied")
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Zip Archive Path Traversal — High
73
+
74
+ Malicious zip files can escape extraction directory.
75
+
76
+ **Bad:**
77
+
78
+ ```go
79
+ for _, file := range reader.File {
80
+ path := filepath.Join(dest, file.Name) // DON'T: No validation
81
+ file.Create(path)
82
+ }
83
+ ```
84
+
85
+ **Good (Go 1.24+) — use `os.Root` to scope extraction:**
86
+
87
+ ```go
88
+ root, err := os.OpenRoot(dest)
89
+ if err != nil { return err }
90
+ defer root.Close()
91
+ for _, file := range reader.File {
92
+ f, err := root.OpenFile(file.Name, os.O_CREATE|os.O_WRONLY, 0644)
93
+ if err != nil { return err } // rejects paths escaping root
94
+ // ... copy contents ...
95
+ f.Close()
96
+ }
97
+ ```
98
+
99
+ **Good (pre-Go 1.24 fallback):**
100
+
101
+ ```go
102
+ for _, file := range reader.File {
103
+ if !filepath.IsLocal(file.Name) {
104
+ return fmt.Errorf("unsafe archive path: %q", file.Name)
105
+ }
106
+
107
+ targetPath, err := safeJoin(dest, file.Name)
108
+ if err != nil {
109
+ return err
110
+ }
111
+
112
+ // create parent directories, then write targetPath
113
+ _ = targetPath
114
+ }
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Decompression Bomb — Medium
120
+
121
+ Tiny compressed files can expand to GBs.
122
+
123
+ **Bad:**
124
+
125
+ ```go
126
+ gr, _ := gzip.NewReader(f)
127
+ out, _ := os.Create(dst)
128
+ io.Copy(out, gr) // DON'T: No size limits
129
+ ```
130
+
131
+ **Good:**
132
+
133
+ ```go
134
+ const maxDecompressedSize = 100 * 1024 * 1024 // 100MB limit
135
+
136
+ var errDecompressedSizeLimitExceeded = errors.New("decompressed size limit exceeded")
137
+
138
+ type limitedReader struct {
139
+ r io.Reader
140
+ read int64
141
+ }
142
+
143
+ func (l *limitedReader) Read(p []byte) (int, error) {
144
+ if l.read >= maxDecompressedSize {
145
+ // Return a sentinel error — io.EOF would be treated as success by io.Copy
146
+ return 0, errDecompressedSizeLimitExceeded
147
+ }
148
+ n, err := l.r.Read(p)
149
+ l.read += int64(n)
150
+ return n, err
151
+ }
152
+
153
+ lr := &limitedReader{r: gr}
154
+ if _, err := io.Copy(out, lr); err != nil {
155
+ return fmt.Errorf("decompressing: %w", err)
156
+ }
157
+ ```
158
+
159
+ ---
160
+
161
+ ## Insecure Temporary File Creation — Medium
162
+
163
+ Creating temp files without proper permissions.
164
+
165
+ **Bad:**
166
+
167
+ ```go
168
+ f, _ := os.Create("/tmp/myapp.temp") // DON'T: Predictable name
169
+ f.WriteString(data)
170
+ ```
171
+
172
+ **Good:**
173
+
174
+ ```go
175
+ f, err := os.CreateTemp("", "myapp.*")
176
+ defer os.Remove(f.Name())
177
+ f.Chmod(0600) // Restrictive permissions
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Insecure File Permissions — Medium
183
+
184
+ Opening files with excessive permissions.
185
+
186
+ **Bad:**
187
+
188
+ ```go
189
+ f, _ := os.OpenFile("config.json", os.O_CREATE, 0644) // DON'T: World-readable
190
+ ```
191
+
192
+ **Good:**
193
+
194
+ ```go
195
+ f, _ := os.OpenFile("config.json", os.O_CREATE, 0600) // OK: Owner only
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Insecure mkdir — Low
201
+
202
+ Creating directories with overly permissive permissions.
203
+
204
+ **Bad:**
205
+
206
+ ```go
207
+ os.MkdirAll("/var/myapp/cache", 0777) // DON'T: World-writable
208
+ ```
209
+
210
+ **Good:**
211
+
212
+ ```go
213
+ os.MkdirAll("/var/myapp/cache", 0750) // OK: Group-writable
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Insecure File Write Permissions — Medium
219
+
220
+ Opening files for writing with inappropriate permissions.
221
+
222
+ **Bad:**
223
+
224
+ ```go
225
+ os.OpenFile("app.log", os.O_CREATE, 0666) // DON'T: World-writable
226
+ ```
227
+
228
+ **Good:**
229
+
230
+ ```go
231
+ os.OpenFile("app.log", os.O_CREATE|os.O_APPEND, 0640) // OK
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Tainted File Read — High
237
+
238
+ Reading files based on unvalidated input.
239
+
240
+ **Bad:**
241
+
242
+ ```go
243
+ func readFile(filename string) ([]byte, error) {
244
+ return os.ReadFile(filename) // DON'T: No validation
245
+ }
246
+ ```
247
+
248
+ **Good (Go 1.24+):**
249
+
250
+ ```go
251
+ const allowedDir = "/var/www/public/"
252
+
253
+ func readFile(filename string) ([]byte, error) {
254
+ root, err := os.OpenRoot(allowedDir)
255
+ if err != nil { return nil, err }
256
+ defer root.Close()
257
+ f, err := root.Open(filename) // cannot escape root directory
258
+ if err != nil { return nil, err }
259
+ defer f.Close()
260
+ return io.ReadAll(f)
261
+ }
262
+ ```
263
+
264
+ **Good (pre-Go 1.24 fallback):**
265
+
266
+ ```go
267
+ const allowedDir = "/var/www/public/"
268
+
269
+ func readFile(filename string) ([]byte, error) {
270
+ fullPath, err := safeJoin(allowedDir, filename)
271
+ if err != nil {
272
+ return nil, err
273
+ }
274
+ return os.ReadFile(fullPath)
275
+ }
276
+ ```
277
+
278
+ ---
279
+
280
+ ## CWE References
281
+
282
+ - **CWE-22**: Path Traversal (Directory Traversal)
283
+ - **CWE-409**: Zip Bomb Decompression
284
+ - **CWE-379**: Insecure Temp File Creation
285
+ - **CWE-732**: Incorrect File Permissions