ai-developer-skill-os 9.3.1 → 10.2.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 (258) hide show
  1. package/.agents/AGENTS.md +73 -40
  2. package/.agents/DEV_PROFILE.md +36 -2
  3. package/.agents/LICENSE +21 -21
  4. package/.agents/docs/ARCHITECTURE.md +56 -120
  5. package/.agents/docs/GOVERNANCE.md +3 -3
  6. package/.agents/docs/SPEC.md +137 -60
  7. package/.agents/docs/VERSIONING.md +25 -57
  8. package/.agents/docs/adr/0005-v10-platform-consolidation.md +50 -0
  9. package/.agents/docs/schemas/learning.schema.yml +22 -57
  10. package/.agents/docs/schemas/skill.schema.yml +116 -161
  11. package/.agents/docs/schemas/workflow.schema.yml +51 -26
  12. package/.agents/docs/skill-classification.md +1 -1
  13. package/.agents/registry/graph.json +193 -346
  14. package/.agents/registry/index.yaml +90 -233
  15. package/.agents/rules/coding.md +30 -12
  16. package/.agents/rules/command-safety.md +20 -9
  17. package/.agents/rules/global.md +234 -28
  18. package/.agents/rules/prompt-compiler.md +170 -0
  19. package/.agents/rules/safety.md +1 -1
  20. package/.agents/rules/security.md +1 -1
  21. package/.agents/rules/skill-quality.md +18 -3
  22. package/.agents/skills/_template/SKILL.md +238 -88
  23. package/.agents/skills/qk-api-data-discovery/SKILL.md +456 -0
  24. package/.agents/skills/qk-api-data-discovery/references/bronze-record-format.md +47 -0
  25. package/.agents/skills/qk-api-data-discovery/references/data-contract-yaml.md +72 -0
  26. package/.agents/skills/qk-api-data-discovery/references/discovery-report-template.md +110 -0
  27. package/.agents/skills/qk-backend-data/SKILL.md +339 -0
  28. package/.agents/skills/qk-bug-resolution/SKILL.md +328 -248
  29. package/.agents/skills/qk-bug-resolution/evals/scorecard.yaml +1 -1
  30. package/.agents/skills/qk-code-cleaner/SKILL.md +399 -0
  31. package/.agents/skills/qk-code-review/SKILL.md +320 -247
  32. package/.agents/skills/qk-code-review/evals/scorecard.yaml +1 -1
  33. package/.agents/skills/qk-code-review/references/ai/{v8-schema-validation.md → schema-validation.md} +2 -2
  34. package/.agents/skills/qk-code-review/references/cross-cutting/async-concurrency-patterns.md +515 -515
  35. package/.agents/skills/qk-code-review/references/cross-cutting/error-handling-principles.md +492 -492
  36. package/.agents/skills/qk-code-review/references/cross-cutting/n-plus-one-queries.md +309 -309
  37. package/.agents/skills/qk-code-review/references/cross-cutting/sql-injection-prevention.md +307 -307
  38. package/.agents/skills/qk-code-review/references/cross-cutting/xss-prevention.md +263 -263
  39. package/.agents/skills/qk-code-review/references/languages/angular.md +768 -768
  40. package/.agents/skills/qk-code-review/references/languages/c.md +890 -890
  41. package/.agents/skills/qk-code-review/references/languages/cpp.md +893 -893
  42. package/.agents/skills/qk-code-review/references/languages/css-less-sass.md +661 -661
  43. package/.agents/skills/qk-code-review/references/languages/django.md +985 -985
  44. package/.agents/skills/qk-code-review/references/languages/fastapi.md +580 -580
  45. package/.agents/skills/qk-code-review/references/languages/go.md +993 -993
  46. package/.agents/skills/qk-code-review/references/languages/java.md +409 -409
  47. package/.agents/skills/qk-code-review/references/languages/java8.md +586 -586
  48. package/.agents/skills/qk-code-review/references/languages/kotlin.md +1018 -1018
  49. package/.agents/skills/qk-code-review/references/languages/nestjs.md +593 -593
  50. package/.agents/skills/qk-code-review/references/languages/php.md +684 -684
  51. package/.agents/skills/qk-code-review/references/languages/python.md +1073 -1073
  52. package/.agents/skills/qk-code-review/references/languages/qt.md +757 -757
  53. package/.agents/skills/qk-code-review/references/languages/react.md +871 -871
  54. package/.agents/skills/qk-code-review/references/languages/ruby.md +964 -964
  55. package/.agents/skills/qk-code-review/references/languages/rust.md +846 -846
  56. package/.agents/skills/qk-code-review/references/languages/svelte.md +1064 -1064
  57. package/.agents/skills/qk-code-review/references/languages/swift.md +936 -936
  58. package/.agents/skills/qk-code-review/references/languages/typescript.md +1016 -1016
  59. package/.agents/skills/qk-code-review/references/languages/vue.md +924 -924
  60. package/.agents/skills/qk-code-review/references/languages/zig.md +440 -440
  61. package/.agents/skills/qk-devops-release/SKILL.md +294 -0
  62. package/.agents/skills/qk-feature-delivery/SKILL.md +331 -247
  63. package/.agents/skills/qk-feature-delivery/evals/scorecard.yaml +1 -1
  64. package/.agents/skills/qk-orchestrator/SKILL.md +286 -152
  65. package/.agents/skills/qk-orchestrator/evals/scorecard.yaml +1 -1
  66. package/.agents/skills/qk-orchestrator/references/routing-table.md +1 -1
  67. package/.agents/skills/qk-product-spec/SKILL.md +262 -0
  68. package/.agents/skills/qk-prompt-compiler/SKILL.md +444 -0
  69. package/.agents/skills/qk-ui-engineer/SKILL.md +286 -0
  70. package/.agents/workflows/_schema.yml +146 -146
  71. package/.agents/workflows/bug-resolution.yml +155 -121
  72. package/.agents/workflows/code-review.yml +127 -93
  73. package/.agents/workflows/context-discovery.yml +128 -94
  74. package/.agents/workflows/documentation.yml +124 -90
  75. package/.agents/workflows/feature-delivery.yml +158 -124
  76. package/.agents/workflows/production-release.yml +207 -173
  77. package/.agents/workflows/prompt-compilation.yml +126 -0
  78. package/.agents/workflows/refactor.yml +136 -102
  79. package/.agents/workflows/security-audit.yml +149 -115
  80. package/.agents/workflows/shared/quality-gate.yml +3 -1
  81. package/.agents/workflows/skin-governance.yml +149 -115
  82. package/.agents/workflows/spec-driven-development.yml +116 -87
  83. package/CHANGELOG.md +77 -0
  84. package/README.md +125 -205
  85. package/bin/install.js +324 -329
  86. package/package.json +68 -74
  87. package/tooling/build-registry.js +226 -208
  88. package/tooling/run-aar.js +55 -126
  89. package/tooling/sync-versions.js +2 -2
  90. package/tooling/validate-graph.js +100 -87
  91. package/tooling/validate-skills.js +32 -14
  92. package/.agents/README.md +0 -90
  93. package/.agents/docs/CHI_TIET_SKILLS.md +0 -126
  94. package/.agents/docs/HUONG_DAN_SU_DUNG.md +0 -120
  95. package/.agents/docs/MIGRATION-CLEANUP-V8.1.3.md +0 -36
  96. package/.agents/docs/MIGRATION-STATUS.md +0 -35
  97. package/.agents/docs/MIGRATION-V8.md +0 -10
  98. package/.agents/docs/ROADMAP-V8.2.md +0 -78
  99. package/.agents/docs/V8-CERTIFICATION.md +0 -27
  100. package/.agents/docs/decisions/ADR-001-v8-migration.md +0 -58
  101. package/.agents/docs/decisions/ADR-002-workflow-separation.md +0 -50
  102. package/.agents/docs/decisions/ADR-003-registry-generated.md +0 -54
  103. package/.agents/docs/decisions/ADR-008-skill-boundary-review.md +0 -27
  104. package/.agents/registry/capability-graph.yml +0 -390
  105. package/.agents/registry/skills-index.yml +0 -305
  106. package/.agents/skills/_template/capability.yaml +0 -34
  107. package/.agents/skills/_template/evals/scorecard.yaml +0 -19
  108. package/.agents/skills/qk-access-policy/SKILL.md +0 -206
  109. package/.agents/skills/qk-access-policy/capability.yaml +0 -23
  110. package/.agents/skills/qk-access-policy/evals/scorecard.yaml +0 -36
  111. package/.agents/skills/qk-agent-observability/SKILL.md +0 -108
  112. package/.agents/skills/qk-agent-observability/capability.yaml +0 -29
  113. package/.agents/skills/qk-agent-observability/evals/scorecard.yaml +0 -29
  114. package/.agents/skills/qk-agent-observability/references/scorecard.yaml +0 -80
  115. package/.agents/skills/qk-ai-builder/SKILL.md +0 -254
  116. package/.agents/skills/qk-ai-builder/capability.yaml +0 -23
  117. package/.agents/skills/qk-ai-builder/evals/scorecard.yaml +0 -30
  118. package/.agents/skills/qk-api-consumer/SKILL.md +0 -256
  119. package/.agents/skills/qk-api-consumer/capability.yaml +0 -21
  120. package/.agents/skills/qk-api-consumer/evals/scorecard.yaml +0 -29
  121. package/.agents/skills/qk-api-lifecycle/SKILL.md +0 -251
  122. package/.agents/skills/qk-api-lifecycle/capability.yaml +0 -23
  123. package/.agents/skills/qk-api-lifecycle/evals/scorecard.yaml +0 -29
  124. package/.agents/skills/qk-bug-resolution/capability.yaml +0 -25
  125. package/.agents/skills/qk-code-review/capability.yaml +0 -23
  126. package/.agents/skills/qk-context-loader/SKILL.md +0 -198
  127. package/.agents/skills/qk-context-loader/capability.yaml +0 -23
  128. package/.agents/skills/qk-context-loader/evals/scorecard.yaml +0 -28
  129. package/.agents/skills/qk-data-engineer/SKILL.md +0 -253
  130. package/.agents/skills/qk-data-lifecycle/SKILL.md +0 -197
  131. package/.agents/skills/qk-data-lifecycle/capability.yaml +0 -23
  132. package/.agents/skills/qk-data-lifecycle/evals/scorecard.yaml +0 -29
  133. package/.agents/skills/qk-db-optimizer/SKILL.md +0 -210
  134. package/.agents/skills/qk-db-optimizer/capability.yaml +0 -22
  135. package/.agents/skills/qk-db-optimizer/evals/scorecard.yaml +0 -28
  136. package/.agents/skills/qk-design-system-engineering/SKILL.md +0 -193
  137. package/.agents/skills/qk-design-system-engineering/capability.yaml +0 -25
  138. package/.agents/skills/qk-design-system-engineering/evals/scorecard.yaml +0 -27
  139. package/.agents/skills/qk-devops-platform/SKILL.md +0 -198
  140. package/.agents/skills/qk-devops-platform/capability.yaml +0 -29
  141. package/.agents/skills/qk-devops-platform/evals/scorecard.yaml +0 -28
  142. package/.agents/skills/qk-docs/SKILL.md +0 -193
  143. package/.agents/skills/qk-docs/capability.yaml +0 -23
  144. package/.agents/skills/qk-docs/evals/scorecard.yaml +0 -27
  145. package/.agents/skills/qk-engineering-standard/SKILL.md +0 -89
  146. package/.agents/skills/qk-engineering-standard/capability.yaml +0 -23
  147. package/.agents/skills/qk-engineering-standard/evals/scorecard.yaml +0 -28
  148. package/.agents/skills/qk-engineering-standard/references/anti-patterns.md +0 -121
  149. package/.agents/skills/qk-engineering-standard/rules/backend.md +0 -122
  150. package/.agents/skills/qk-engineering-standard/rules/database.md +0 -3
  151. package/.agents/skills/qk-engineering-standard/rules/frontend.md +0 -152
  152. package/.agents/skills/qk-engineering-standard/rules/security.md +0 -3
  153. package/.agents/skills/qk-engineering-standard/rules/testing.md +0 -3
  154. package/.agents/skills/qk-fe-api-integration/SKILL.md +0 -704
  155. package/.agents/skills/qk-fe-api-integration/capability.yaml +0 -21
  156. package/.agents/skills/qk-fe-api-integration/evals/scorecard.yaml +0 -29
  157. package/.agents/skills/qk-feature-delivery/capability.yaml +0 -24
  158. package/.agents/skills/qk-frontend-architecture/SKILL.md +0 -127
  159. package/.agents/skills/qk-frontend-architecture/capability.yaml +0 -28
  160. package/.agents/skills/qk-frontend-architecture/evals/scorecard.yaml +0 -28
  161. package/.agents/skills/qk-help/SKILL.md +0 -107
  162. package/.agents/skills/qk-help/capability.yaml +0 -20
  163. package/.agents/skills/qk-help/evals/scorecard.yaml +0 -13
  164. package/.agents/skills/qk-orchestrator/capability.yaml +0 -22
  165. package/.agents/skills/qk-product-specification/SKILL.md +0 -187
  166. package/.agents/skills/qk-product-specification/capability.yaml +0 -27
  167. package/.agents/skills/qk-product-specification/evals/scorecard.yaml +0 -27
  168. package/.agents/skills/qk-production-release/SKILL.md +0 -188
  169. package/.agents/skills/qk-production-release/capability.yaml +0 -27
  170. package/.agents/skills/qk-production-release/evals/scorecard.yaml +0 -28
  171. package/.agents/skills/qk-project-audit/SKILL.md +0 -174
  172. package/.agents/skills/qk-project-bootstrap/SKILL.md +0 -372
  173. package/.agents/skills/qk-project-bootstrap/capability.yaml +0 -23
  174. package/.agents/skills/qk-project-bootstrap/evals/scorecard.yaml +0 -28
  175. package/.agents/skills/qk-project-health/SKILL.md +0 -202
  176. package/.agents/skills/qk-project-health/capability.yaml +0 -23
  177. package/.agents/skills/qk-project-health/evals/scorecard.yaml +0 -27
  178. package/.agents/skills/qk-project-memory/SKILL.md +0 -303
  179. package/.agents/skills/qk-project-memory/capability.yaml +0 -23
  180. package/.agents/skills/qk-project-memory/evals/scorecard.yaml +0 -27
  181. package/.agents/skills/qk-refactor/SKILL.md +0 -243
  182. package/.agents/skills/qk-refactor/capability.yaml +0 -26
  183. package/.agents/skills/qk-refactor/evals/scorecard.yaml +0 -27
  184. package/.agents/skills/qk-security-audit/SKILL.md +0 -280
  185. package/.agents/skills/qk-security-audit/capability.yaml +0 -29
  186. package/.agents/skills/qk-security-audit/evals/scorecard.yaml +0 -27
  187. package/.agents/skills/qk-system-evolution/SKILL.md +0 -625
  188. package/.agents/skills/qk-system-evolution/capability.yaml +0 -24
  189. package/.agents/skills/qk-system-evolution/evals/scorecard.yaml +0 -26
  190. package/.agents/skills/qk-test-engineering/SKILL.md +0 -215
  191. package/.agents/skills/qk-test-engineering/capability.yaml +0 -28
  192. package/.agents/skills/qk-test-engineering/evals/scorecard.yaml +0 -26
  193. package/.agents/skills/qk-ui-audit/SKILL.md +0 -175
  194. package/.agents/skills/qk-ui-audit/capability.yaml +0 -23
  195. package/.agents/skills/qk-ui-audit/evals/scorecard.yaml +0 -26
  196. package/.agents/skills/qk-ui-audit/references/anti-slop-checklist.md +0 -136
  197. package/.agents/skills/qk-ui-builder/SKILL.md +0 -521
  198. package/.agents/skills/qk-ui-builder/capability.yaml +0 -29
  199. package/.agents/skills/qk-ui-builder/evals/scorecard.yaml +0 -26
  200. package/.agents/skills/qk-ui-builder/references/anti-patterns.md +0 -295
  201. package/.agents/skills/qk-ui-builder/references/color.md +0 -115
  202. package/.agents/skills/qk-ui-builder/references/component-cookbook.md +0 -458
  203. package/.agents/skills/qk-ui-builder/references/copy.md +0 -250
  204. package/.agents/skills/qk-ui-builder/references/interaction-and-states.md +0 -115
  205. package/.agents/skills/qk-ui-builder/references/layout-and-space.md +0 -111
  206. package/.agents/skills/qk-ui-builder/references/macrostructures/01-bento-grid.md +0 -48
  207. package/.agents/skills/qk-ui-builder/references/macrostructures/02-long-document.md +0 -50
  208. package/.agents/skills/qk-ui-builder/references/macrostructures/03-marquee-hero.md +0 -51
  209. package/.agents/skills/qk-ui-builder/references/macrostructures/04-stat-led.md +0 -49
  210. package/.agents/skills/qk-ui-builder/references/macrostructures/05-workbench.md +0 -44
  211. package/.agents/skills/qk-ui-builder/references/macrostructures/06-conversational-faq.md +0 -50
  212. package/.agents/skills/qk-ui-builder/references/macrostructures/07-manifesto.md +0 -51
  213. package/.agents/skills/qk-ui-builder/references/macrostructures/08-photographic.md +0 -50
  214. package/.agents/skills/qk-ui-builder/references/macrostructures/09-quote-led.md +0 -50
  215. package/.agents/skills/qk-ui-builder/references/macrostructures/11-catalogue.md +0 -49
  216. package/.agents/skills/qk-ui-builder/references/macrostructures/12-letter.md +0 -49
  217. package/.agents/skills/qk-ui-builder/references/macrostructures/13-index-first.md +0 -49
  218. package/.agents/skills/qk-ui-builder/references/macrostructures/14-narrative-workflow.md +0 -48
  219. package/.agents/skills/qk-ui-builder/references/macrostructures/15-split-studio.md +0 -48
  220. package/.agents/skills/qk-ui-builder/references/macrostructures/16-feature-stack.md +0 -51
  221. package/.agents/skills/qk-ui-builder/references/macrostructures/17-type-specimen.md +0 -48
  222. package/.agents/skills/qk-ui-builder/references/macrostructures/18-portfolio-grid.md +0 -48
  223. package/.agents/skills/qk-ui-builder/references/macrostructures/19-map-diagram.md +0 -50
  224. package/.agents/skills/qk-ui-builder/references/macrostructures/20-ecosystem-index.md +0 -48
  225. package/.agents/skills/qk-ui-builder/references/macrostructures/21-component-playground.md +0 -45
  226. package/.agents/skills/qk-ui-builder/references/macrostructures.md +0 -38
  227. package/.agents/skills/qk-ui-builder/references/motion.md +0 -95
  228. package/.agents/skills/qk-ui-builder/references/responsive.md +0 -115
  229. package/.agents/skills/qk-ui-builder/references/slop-test.md +0 -135
  230. package/.agents/skills/qk-ui-builder/references/structure.md +0 -280
  231. package/.agents/skills/qk-ui-builder/references/themes/atmospheric.md +0 -53
  232. package/.agents/skills/qk-ui-builder/references/themes/carnival.md +0 -52
  233. package/.agents/skills/qk-ui-builder/references/themes/cobalt.md +0 -52
  234. package/.agents/skills/qk-ui-builder/references/themes/editorial.md +0 -52
  235. package/.agents/skills/qk-ui-builder/references/themes/garden.md +0 -52
  236. package/.agents/skills/qk-ui-builder/references/themes/hum.md +0 -52
  237. package/.agents/skills/qk-ui-builder/references/themes/lumen.md +0 -52
  238. package/.agents/skills/qk-ui-builder/references/themes/midnight.md +0 -52
  239. package/.agents/skills/qk-ui-builder/references/themes/modern-minimal.md +0 -52
  240. package/.agents/skills/qk-ui-builder/references/themes/playful.md +0 -52
  241. package/.agents/skills/qk-ui-builder/references/themes/specimen.md +0 -52
  242. package/.agents/skills/qk-ui-builder/references/themes/terminal.md +0 -52
  243. package/.agents/skills/qk-ui-builder/references/typography.md +0 -129
  244. package/.agents/skills/qk-ui-system-builder/SKILL.md +0 -183
  245. package/.agents/skills/qk-ui-system-builder/capability.yaml +0 -25
  246. package/.agents/skills/qk-ui-system-builder/evals/scorecard.yaml +0 -26
  247. package/.agents/skills/qk-upgrade/SKILL.md +0 -301
  248. package/.agents/skills/qk-upgrade/capability.yaml +0 -24
  249. package/.agents/skills/qk-upgrade/evals/scorecard.yaml +0 -26
  250. package/.agents/skills/qk-validation-gate/SKILL.md +0 -88
  251. package/.agents/skills/qk-validation-gate/capability.yaml +0 -23
  252. package/.agents/skills/qk-validation-gate/evals/scorecard.yaml +0 -26
  253. package/.agents/skills/qk-web-quality-gate/SKILL.md +0 -197
  254. package/.agents/skills/qk-web-quality-gate/capability.yaml +0 -24
  255. package/.agents/skills/qk-web-quality-gate/evals/scorecard.yaml +0 -26
  256. package/.agents/workflows/research.yml +0 -75
  257. package/.agents/workflows/skill-evolution.yml +0 -97
  258. package/tooling/fix-refactor.js +0 -8
@@ -1,308 +1,308 @@
1
- # SQL Injection Prevention Guide
2
-
3
- Language-agnostic SQL injection prevention strategies with cross-language code examples.
4
-
5
- > **Related**: [Security Review Guide](../security-review-guide.md) for comprehensive security checklist and decision framework.
6
-
7
- ## Attack Types
8
-
9
- SQL injection (SQLi) is ranked #3 in the OWASP Top 10 (2021). Three common variants:
10
-
11
- | Type | Description | Risk |
12
- |------|-------------|------|
13
- | **Classic (In-band)** | Attacker receives results directly in the HTTP response | Data exfiltration, authentication bypass |
14
- | **Blind (Boolean/Time-based)** | Attacker infers data from response differences or timing | Slower but still viable for data extraction |
15
- | **Out-of-band** | Attacker uses DNS/HTTP callbacks to exfiltrate data | Less common but harder to detect |
16
-
17
- ## Universal Prevention Strategy
18
-
19
- 1. **Parameterized queries** — always (the #1 defense)
20
- 2. **ORM safe usage** — understand what your ORM escapes
21
- 3. **Input validation** — whitelist over blacklist
22
- 4. **Least privilege** — database user with minimal permissions
23
- 5. **WAF** — web application firewall as defense-in-depth
24
-
25
- ---
26
-
27
- ## Cross-Language Examples
28
-
29
- ### Python
30
-
31
- ```python
32
- # ❌ Vulnerable: string formatting
33
- query = f"SELECT * FROM users WHERE id = {user_id}"
34
- cursor.execute(query)
35
-
36
- # ❌ Vulnerable: % formatting
37
- cursor.execute("SELECT * FROM users WHERE id = %s" % user_id)
38
-
39
- # ✅ Parameterized (DB-API)
40
- cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
41
-
42
- # ✅ SQLAlchemy ORM
43
- User.query.filter(User.id == user_id).all()
44
-
45
- # ❌ SQLAlchemy raw SQL with string interpolation
46
- session.execute(text(f"SELECT * FROM users WHERE id = {user_id}"))
47
-
48
- # ✅ SQLAlchemy raw SQL with bound parameters
49
- session.execute(text("SELECT * FROM users WHERE id = :id"), {"id": user_id})
50
-
51
- # ✅ Django ORM
52
- User.objects.filter(id=user_id)
53
-
54
- # ❌ Django extra() with string interpolation
55
- User.objects.extra(where=[f"username = '{username}'"])
56
-
57
- # ✅ Django raw() with parameters
58
- User.objects.raw("SELECT * FROM users WHERE id = %s", [user_id])
59
- ```
60
-
61
- ### Java
62
-
63
- ```java
64
- // ❌ Vulnerable: string concatenation
65
- String query = "SELECT * FROM users WHERE id = " + userId;
66
- Statement stmt = connection.createStatement();
67
- ResultSet rs = stmt.executeQuery(query);
68
-
69
- // ✅ JDBC PreparedStatement
70
- String query = "SELECT * FROM users WHERE id = ?";
71
- PreparedStatement stmt = connection.prepareStatement(query);
72
- stmt.setLong(1, userId);
73
- ResultSet rs = stmt.executeQuery();
74
-
75
- // ✅ JPA parameter binding
76
- @Query("SELECT u FROM User u WHERE u.id = :id")
77
- User findById(@Param("id") Long id);
78
-
79
- // ✅ Spring Data JPA method naming
80
- User findById(Long id);
81
-
82
- // ❌ JPA native query with string concatenation
83
- entityManager.createNativeQuery(
84
- "SELECT * FROM users WHERE name = '" + name + "'"
85
- );
86
-
87
- // ✅ JPA native query with parameter binding
88
- Query query = entityManager.createNativeQuery(
89
- "SELECT * FROM users WHERE name = :name"
90
- );
91
- query.setParameter("name", name);
92
- ```
93
-
94
- ### Go
95
-
96
- ```go
97
- // ❌ Vulnerable: fmt.Sprintf
98
- query := fmt.Sprintf("SELECT * FROM users WHERE id = %s", userID)
99
- rows, err := db.Query(query)
100
-
101
- // ✅ database/sql parameterized
102
- rows, err := db.Query("SELECT * FROM users WHERE id = ?", userID)
103
-
104
- // ✅ Named parameters (sqlx)
105
- rows, err := db.NamedQuery(
106
- "SELECT * FROM users WHERE id = :id",
107
- map[string]interface{}{"id": userID},
108
- )
109
-
110
- // ⚠️ Dynamic identifiers (table/column names) can't use placeholders
111
- // Must validate against whitelist
112
- var allowedColumns = map[string]bool{
113
- "id": true, "name": true, "email": true, "created_at": true,
114
- }
115
-
116
- func queryWithOrder(db *sql.DB, orderBy string) (*sql.Rows, error) {
117
- if !allowedColumns[orderBy] {
118
- return nil, fmt.Errorf("invalid column: %s", orderBy)
119
- }
120
- return db.Query(
121
- fmt.Sprintf("SELECT * FROM users ORDER BY %s", orderBy),
122
- )
123
- }
124
- ```
125
-
126
- ### Node.js
127
-
128
- ```typescript
129
- // ❌ Vulnerable: template literal
130
- const query = `SELECT * FROM users WHERE id = ${userId}`;
131
- const result = await client.query(query);
132
-
133
- // ✅ pg parameterized ($1, $2, ...)
134
- const result = await client.query(
135
- "SELECT * FROM users WHERE id = $1",
136
- [userId]
137
- );
138
-
139
- // ✅ Prisma ORM (parameterized by default)
140
- const user = await prisma.user.findUnique({
141
- where: { id: userId },
142
- });
143
-
144
- // ❌ Prisma $queryRawUnsafe with string interpolation
145
- await prisma.$queryRawUnsafe(
146
- `SELECT * FROM users WHERE id = ${userId}`
147
- );
148
-
149
- // ✅ Prisma $queryRaw with tagged template (safe)
150
- await prisma.$queryRaw`
151
- SELECT * FROM users WHERE id = ${userId}
152
- `;
153
- ```
154
-
155
- ### PHP
156
-
157
- ```php
158
- <?php
159
-
160
- // ❌ Vulnerable: string concatenation
161
- $sql = "SELECT * FROM users WHERE email = '" . $_GET['email'] . "'";
162
- $user = $pdo->query($sql)->fetch();
163
-
164
- // ✅ PDO prepared statements
165
- $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
166
- $stmt->execute(['email' => $email]);
167
- $user = $stmt->fetch(PDO::FETCH_ASSOC);
168
-
169
- // ✅ PDO positional placeholders
170
- $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
171
- $stmt->execute([$id]);
172
-
173
- // ❌ mysqli with string interpolation
174
- $result = mysqli_query($conn,
175
- "SELECT * FROM users WHERE id = " . $id
176
- );
177
-
178
- // ✅ mysqli prepared statements
179
- $stmt = mysqli_prepare($conn, "SELECT * FROM users WHERE id = ?");
180
- mysqli_stmt_bind_param($stmt, "i", $id);
181
- mysqli_stmt_execute($stmt);
182
-
183
- // ✅ Laravel Eloquent ORM
184
- User::where('id', $id)->first();
185
-
186
- // ❌ Laravel DB::raw with interpolation
187
- DB::select(DB::raw("SELECT * FROM users WHERE id = {$id}"));
188
-
189
- // ✅ Laravel parameterized raw
190
- DB::select("SELECT * FROM users WHERE id = ?", [$id]);
191
- ```
192
-
193
- ### C# / .NET
194
-
195
- ```csharp
196
- // ❌ Vulnerable: string concatenation
197
- var query = $"SELECT * FROM Users WHERE Id = {userId}";
198
- using var cmd = new SqlCommand(query, connection);
199
- var reader = cmd.ExecuteReader();
200
-
201
- // ✅ ADO.NET parameterized
202
- var query = "SELECT * FROM Users WHERE Id = @Id";
203
- using var cmd = new SqlCommand(query, connection);
204
- cmd.Parameters.AddWithValue("@Id", userId);
205
-
206
- // ✅ Dapper parameterized
207
- var users = connection.Query<User>(
208
- "SELECT * FROM Users WHERE Id = @Id",
209
- new { Id = userId }
210
- );
211
-
212
- // ❌ Dapper with string interpolation
213
- var users = connection.Query<User>(
214
- $"SELECT * FROM Users WHERE Id = {userId}"
215
- );
216
-
217
- // ✅ EF Core (parameterized by default)
218
- var user = await context.Users
219
- .Where(u => u.Id == userId)
220
- .FirstOrDefaultAsync();
221
-
222
- // ❌ EF Core FromSqlRaw with interpolation
223
- var users = context.Users
224
- .FromSqlRaw($"SELECT * FROM Users WHERE Id = {userId}")
225
- .ToList();
226
-
227
- // ✅ EF Core FromSql with FormattableString (parameterized)
228
- var users = context.Users
229
- .FromSql($"SELECT * FROM Users WHERE Id = {userId}")
230
- .ToList();
231
- ```
232
-
233
- ---
234
-
235
- ## ORM Unsafe Usage Patterns
236
-
237
- ORMs do NOT automatically prevent SQL injection in all cases:
238
-
239
- ```python
240
- # ❌ SQLAlchemy: text() with f-string
241
- session.execute(text(f"SELECT * FROM users WHERE id = {user_id}"))
242
-
243
- # ❌ Django: extra() / RawSQL() with string interpolation
244
- User.objects.extra(where=[f"username = '{username}'"])
245
- User.objects.annotate(
246
- val=RawSQL(f"SELECT col FROM other WHERE id = {user_id}")
247
- )
248
-
249
- # ❌ JPA: createNativeQuery with string concatenation
250
- entityManager.createNativeQuery("SELECT * FROM users WHERE name = '" + name + "'")
251
-
252
- # ❌ EF Core: FromSqlRaw with string interpolation
253
- context.Users.FromSqlRaw($"SELECT * FROM Users WHERE Id = {userId}")
254
- ```
255
-
256
- **Rule**: Every ORM has a "raw SQL" escape hatch. String interpolation in that escape hatch = SQL injection. Always use the ORM's parameter binding mechanism.
257
-
258
- ---
259
-
260
- ## Dynamic Identifiers (Table/Column Names)
261
-
262
- Placeholders can only bind **values**, not table names, column names, or SQL keywords. For dynamic identifiers:
263
-
264
- ```python
265
- # ✅ Whitelist validation
266
- ALLOWED_COLUMNS = {"id", "name", "email", "created_at"}
267
- ALLOWED_DIRECTIONS = {"ASC", "DESC"}
268
-
269
- def get_users(order_by: str, direction: str) -> list[User]:
270
- if order_by not in ALLOWED_COLUMNS:
271
- raise ValueError(f"Invalid column: {order_by}")
272
- if direction.upper() not in ALLOWED_DIRECTIONS:
273
- raise ValueError(f"Invalid direction: {direction}")
274
-
275
- return User.objects.order_by(
276
- f"{'-' if direction.upper() == 'DESC' else ''}{order_by}"
277
- )
278
- ```
279
-
280
- ---
281
-
282
- ## Detection & Testing
283
-
284
- ```bash
285
- # Automated scanning
286
- sqlmap -u "https://example.com/api/users?id=1" --batch
287
-
288
- # Static analysis (Python)
289
- bandit -r src/ -f custom
290
-
291
- # Static analysis (Java)
292
- spotbugs -textui build/classes
293
-
294
- # Code review keywords to search for
295
- grep -rn "f\".*SELECT\|f'.*SELECT\|fmt.Sprintf.*SELECT\|format.*SELECT" src/
296
- grep -rn "query.*\+.*\|query.*&\|query.*concat" src/
297
- ```
298
-
299
- ---
300
-
301
- ## Review Checklist
302
-
303
- - [ ] All SQL queries use parameterized queries (no string interpolation)
304
- - [ ] ORM raw SQL methods use bound parameters, not string formatting
305
- - [ ] Dynamic identifiers (table/column names) validated against whitelist
306
- - [ ] Database user has least privilege (no DROP/ALTER for app user)
307
- - [ ] No SQL queries constructed from user input without parameterization
1
+ # SQL Injection Prevention Guide
2
+
3
+ Language-agnostic SQL injection prevention strategies with cross-language code examples.
4
+
5
+ > **Related**: [Security Review Guide](../security-review-guide.md) for comprehensive security checklist and decision framework.
6
+
7
+ ## Attack Types
8
+
9
+ SQL injection (SQLi) is ranked #3 in the OWASP Top 10 (2021). Three common variants:
10
+
11
+ | Type | Description | Risk |
12
+ |------|-------------|------|
13
+ | **Classic (In-band)** | Attacker receives results directly in the HTTP response | Data exfiltration, authentication bypass |
14
+ | **Blind (Boolean/Time-based)** | Attacker infers data from response differences or timing | Slower but still viable for data extraction |
15
+ | **Out-of-band** | Attacker uses DNS/HTTP callbacks to exfiltrate data | Less common but harder to detect |
16
+
17
+ ## Universal Prevention Strategy
18
+
19
+ 1. **Parameterized queries** — always (the #1 defense)
20
+ 2. **ORM safe usage** — understand what your ORM escapes
21
+ 3. **Input validation** — whitelist over blacklist
22
+ 4. **Least privilege** — database user with minimal permissions
23
+ 5. **WAF** — web application firewall as defense-in-depth
24
+
25
+ ---
26
+
27
+ ## Cross-Language Examples
28
+
29
+ ### Python
30
+
31
+ ```python
32
+ # ❌ Vulnerable: string formatting
33
+ query = f"SELECT * FROM users WHERE id = {user_id}"
34
+ cursor.execute(query)
35
+
36
+ # ❌ Vulnerable: % formatting
37
+ cursor.execute("SELECT * FROM users WHERE id = %s" % user_id)
38
+
39
+ # ✅ Parameterized (DB-API)
40
+ cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
41
+
42
+ # ✅ SQLAlchemy ORM
43
+ User.query.filter(User.id == user_id).all()
44
+
45
+ # ❌ SQLAlchemy raw SQL with string interpolation
46
+ session.execute(text(f"SELECT * FROM users WHERE id = {user_id}"))
47
+
48
+ # ✅ SQLAlchemy raw SQL with bound parameters
49
+ session.execute(text("SELECT * FROM users WHERE id = :id"), {"id": user_id})
50
+
51
+ # ✅ Django ORM
52
+ User.objects.filter(id=user_id)
53
+
54
+ # ❌ Django extra() with string interpolation
55
+ User.objects.extra(where=[f"username = '{username}'"])
56
+
57
+ # ✅ Django raw() with parameters
58
+ User.objects.raw("SELECT * FROM users WHERE id = %s", [user_id])
59
+ ```
60
+
61
+ ### Java
62
+
63
+ ```java
64
+ // ❌ Vulnerable: string concatenation
65
+ String query = "SELECT * FROM users WHERE id = " + userId;
66
+ Statement stmt = connection.createStatement();
67
+ ResultSet rs = stmt.executeQuery(query);
68
+
69
+ // ✅ JDBC PreparedStatement
70
+ String query = "SELECT * FROM users WHERE id = ?";
71
+ PreparedStatement stmt = connection.prepareStatement(query);
72
+ stmt.setLong(1, userId);
73
+ ResultSet rs = stmt.executeQuery();
74
+
75
+ // ✅ JPA parameter binding
76
+ @Query("SELECT u FROM User u WHERE u.id = :id")
77
+ User findById(@Param("id") Long id);
78
+
79
+ // ✅ Spring Data JPA method naming
80
+ User findById(Long id);
81
+
82
+ // ❌ JPA native query with string concatenation
83
+ entityManager.createNativeQuery(
84
+ "SELECT * FROM users WHERE name = '" + name + "'"
85
+ );
86
+
87
+ // ✅ JPA native query with parameter binding
88
+ Query query = entityManager.createNativeQuery(
89
+ "SELECT * FROM users WHERE name = :name"
90
+ );
91
+ query.setParameter("name", name);
92
+ ```
93
+
94
+ ### Go
95
+
96
+ ```go
97
+ // ❌ Vulnerable: fmt.Sprintf
98
+ query := fmt.Sprintf("SELECT * FROM users WHERE id = %s", userID)
99
+ rows, err := db.Query(query)
100
+
101
+ // ✅ database/sql parameterized
102
+ rows, err := db.Query("SELECT * FROM users WHERE id = ?", userID)
103
+
104
+ // ✅ Named parameters (sqlx)
105
+ rows, err := db.NamedQuery(
106
+ "SELECT * FROM users WHERE id = :id",
107
+ map[string]interface{}{"id": userID},
108
+ )
109
+
110
+ // ⚠️ Dynamic identifiers (table/column names) can't use placeholders
111
+ // Must validate against whitelist
112
+ var allowedColumns = map[string]bool{
113
+ "id": true, "name": true, "email": true, "created_at": true,
114
+ }
115
+
116
+ func queryWithOrder(db *sql.DB, orderBy string) (*sql.Rows, error) {
117
+ if !allowedColumns[orderBy] {
118
+ return nil, fmt.Errorf("invalid column: %s", orderBy)
119
+ }
120
+ return db.Query(
121
+ fmt.Sprintf("SELECT * FROM users ORDER BY %s", orderBy),
122
+ )
123
+ }
124
+ ```
125
+
126
+ ### Node.js
127
+
128
+ ```typescript
129
+ // ❌ Vulnerable: template literal
130
+ const query = `SELECT * FROM users WHERE id = ${userId}`;
131
+ const result = await client.query(query);
132
+
133
+ // ✅ pg parameterized ($1, $2, ...)
134
+ const result = await client.query(
135
+ "SELECT * FROM users WHERE id = $1",
136
+ [userId]
137
+ );
138
+
139
+ // ✅ Prisma ORM (parameterized by default)
140
+ const user = await prisma.user.findUnique({
141
+ where: { id: userId },
142
+ });
143
+
144
+ // ❌ Prisma $queryRawUnsafe with string interpolation
145
+ await prisma.$queryRawUnsafe(
146
+ `SELECT * FROM users WHERE id = ${userId}`
147
+ );
148
+
149
+ // ✅ Prisma $queryRaw with tagged template (safe)
150
+ await prisma.$queryRaw`
151
+ SELECT * FROM users WHERE id = ${userId}
152
+ `;
153
+ ```
154
+
155
+ ### PHP
156
+
157
+ ```php
158
+ <?php
159
+
160
+ // ❌ Vulnerable: string concatenation
161
+ $sql = "SELECT * FROM users WHERE email = '" . $_GET['email'] . "'";
162
+ $user = $pdo->query($sql)->fetch();
163
+
164
+ // ✅ PDO prepared statements
165
+ $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
166
+ $stmt->execute(['email' => $email]);
167
+ $user = $stmt->fetch(PDO::FETCH_ASSOC);
168
+
169
+ // ✅ PDO positional placeholders
170
+ $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
171
+ $stmt->execute([$id]);
172
+
173
+ // ❌ mysqli with string interpolation
174
+ $result = mysqli_query($conn,
175
+ "SELECT * FROM users WHERE id = " . $id
176
+ );
177
+
178
+ // ✅ mysqli prepared statements
179
+ $stmt = mysqli_prepare($conn, "SELECT * FROM users WHERE id = ?");
180
+ mysqli_stmt_bind_param($stmt, "i", $id);
181
+ mysqli_stmt_execute($stmt);
182
+
183
+ // ✅ Laravel Eloquent ORM
184
+ User::where('id', $id)->first();
185
+
186
+ // ❌ Laravel DB::raw with interpolation
187
+ DB::select(DB::raw("SELECT * FROM users WHERE id = {$id}"));
188
+
189
+ // ✅ Laravel parameterized raw
190
+ DB::select("SELECT * FROM users WHERE id = ?", [$id]);
191
+ ```
192
+
193
+ ### C# / .NET
194
+
195
+ ```csharp
196
+ // ❌ Vulnerable: string concatenation
197
+ var query = $"SELECT * FROM Users WHERE Id = {userId}";
198
+ using var cmd = new SqlCommand(query, connection);
199
+ var reader = cmd.ExecuteReader();
200
+
201
+ // ✅ ADO.NET parameterized
202
+ var query = "SELECT * FROM Users WHERE Id = @Id";
203
+ using var cmd = new SqlCommand(query, connection);
204
+ cmd.Parameters.AddWithValue("@Id", userId);
205
+
206
+ // ✅ Dapper parameterized
207
+ var users = connection.Query<User>(
208
+ "SELECT * FROM Users WHERE Id = @Id",
209
+ new { Id = userId }
210
+ );
211
+
212
+ // ❌ Dapper with string interpolation
213
+ var users = connection.Query<User>(
214
+ $"SELECT * FROM Users WHERE Id = {userId}"
215
+ );
216
+
217
+ // ✅ EF Core (parameterized by default)
218
+ var user = await context.Users
219
+ .Where(u => u.Id == userId)
220
+ .FirstOrDefaultAsync();
221
+
222
+ // ❌ EF Core FromSqlRaw with interpolation
223
+ var users = context.Users
224
+ .FromSqlRaw($"SELECT * FROM Users WHERE Id = {userId}")
225
+ .ToList();
226
+
227
+ // ✅ EF Core FromSql with FormattableString (parameterized)
228
+ var users = context.Users
229
+ .FromSql($"SELECT * FROM Users WHERE Id = {userId}")
230
+ .ToList();
231
+ ```
232
+
233
+ ---
234
+
235
+ ## ORM Unsafe Usage Patterns
236
+
237
+ ORMs do NOT automatically prevent SQL injection in all cases:
238
+
239
+ ```python
240
+ # ❌ SQLAlchemy: text() with f-string
241
+ session.execute(text(f"SELECT * FROM users WHERE id = {user_id}"))
242
+
243
+ # ❌ Django: extra() / RawSQL() with string interpolation
244
+ User.objects.extra(where=[f"username = '{username}'"])
245
+ User.objects.annotate(
246
+ val=RawSQL(f"SELECT col FROM other WHERE id = {user_id}")
247
+ )
248
+
249
+ # ❌ JPA: createNativeQuery with string concatenation
250
+ entityManager.createNativeQuery("SELECT * FROM users WHERE name = '" + name + "'")
251
+
252
+ # ❌ EF Core: FromSqlRaw with string interpolation
253
+ context.Users.FromSqlRaw($"SELECT * FROM Users WHERE Id = {userId}")
254
+ ```
255
+
256
+ **Rule**: Every ORM has a "raw SQL" escape hatch. String interpolation in that escape hatch = SQL injection. Always use the ORM's parameter binding mechanism.
257
+
258
+ ---
259
+
260
+ ## Dynamic Identifiers (Table/Column Names)
261
+
262
+ Placeholders can only bind **values**, not table names, column names, or SQL keywords. For dynamic identifiers:
263
+
264
+ ```python
265
+ # ✅ Whitelist validation
266
+ ALLOWED_COLUMNS = {"id", "name", "email", "created_at"}
267
+ ALLOWED_DIRECTIONS = {"ASC", "DESC"}
268
+
269
+ def get_users(order_by: str, direction: str) -> list[User]:
270
+ if order_by not in ALLOWED_COLUMNS:
271
+ raise ValueError(f"Invalid column: {order_by}")
272
+ if direction.upper() not in ALLOWED_DIRECTIONS:
273
+ raise ValueError(f"Invalid direction: {direction}")
274
+
275
+ return User.objects.order_by(
276
+ f"{'-' if direction.upper() == 'DESC' else ''}{order_by}"
277
+ )
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Detection & Testing
283
+
284
+ ```bash
285
+ # Automated scanning
286
+ sqlmap -u "https://example.com/api/users?id=1" --batch
287
+
288
+ # Static analysis (Python)
289
+ bandit -r src/ -f custom
290
+
291
+ # Static analysis (Java)
292
+ spotbugs -textui build/classes
293
+
294
+ # Code review keywords to search for
295
+ grep -rn "f\".*SELECT\|f'.*SELECT\|fmt.Sprintf.*SELECT\|format.*SELECT" src/
296
+ grep -rn "query.*\+.*\|query.*&\|query.*concat" src/
297
+ ```
298
+
299
+ ---
300
+
301
+ ## Review Checklist
302
+
303
+ - [ ] All SQL queries use parameterized queries (no string interpolation)
304
+ - [ ] ORM raw SQL methods use bound parameters, not string formatting
305
+ - [ ] Dynamic identifiers (table/column names) validated against whitelist
306
+ - [ ] Database user has least privilege (no DROP/ALTER for app user)
307
+ - [ ] No SQL queries constructed from user input without parameterization
308
308
  - [ ] Static analysis tools (Bandit, SpotBugs, SonarQube) run in CI