@wazir-dev/cli 1.0.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 (629) hide show
  1. package/AGENTS.md +111 -0
  2. package/CHANGELOG.md +14 -0
  3. package/CONTRIBUTING.md +101 -0
  4. package/LICENSE +21 -0
  5. package/README.md +314 -0
  6. package/assets/composition-engine.mmd +34 -0
  7. package/assets/demo-script.sh +17 -0
  8. package/assets/logo-dark.svg +14 -0
  9. package/assets/logo.svg +14 -0
  10. package/assets/pipeline.mmd +39 -0
  11. package/assets/record-demo.sh +51 -0
  12. package/docs/README.md +51 -0
  13. package/docs/adapters/context-mode.md +60 -0
  14. package/docs/concepts/architecture.md +87 -0
  15. package/docs/concepts/artifact-model.md +60 -0
  16. package/docs/concepts/composition-engine.md +36 -0
  17. package/docs/concepts/indexing-and-recall.md +160 -0
  18. package/docs/concepts/observability.md +41 -0
  19. package/docs/concepts/roles-and-workflows.md +59 -0
  20. package/docs/concepts/terminology-policy.md +27 -0
  21. package/docs/getting-started/01-installation.md +78 -0
  22. package/docs/getting-started/02-first-run.md +102 -0
  23. package/docs/getting-started/03-adding-to-project.md +15 -0
  24. package/docs/getting-started/04-host-setup.md +15 -0
  25. package/docs/guides/ci-integration.md +15 -0
  26. package/docs/guides/creating-skills.md +15 -0
  27. package/docs/guides/expertise-module-authoring.md +15 -0
  28. package/docs/guides/hook-development.md +15 -0
  29. package/docs/guides/memory-and-learnings.md +34 -0
  30. package/docs/guides/multi-host-export.md +15 -0
  31. package/docs/guides/troubleshooting.md +101 -0
  32. package/docs/guides/writing-custom-roles.md +15 -0
  33. package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
  34. package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
  35. package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
  36. package/docs/readmes/INDEX.md +99 -0
  37. package/docs/readmes/features/expertise/README.md +171 -0
  38. package/docs/readmes/features/exports/README.md +222 -0
  39. package/docs/readmes/features/hooks/README.md +103 -0
  40. package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
  41. package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
  42. package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
  43. package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
  44. package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
  45. package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
  46. package/docs/readmes/features/hooks/session-start.md +119 -0
  47. package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
  48. package/docs/readmes/features/roles/README.md +157 -0
  49. package/docs/readmes/features/roles/clarifier.md +152 -0
  50. package/docs/readmes/features/roles/content-author.md +190 -0
  51. package/docs/readmes/features/roles/designer.md +193 -0
  52. package/docs/readmes/features/roles/executor.md +184 -0
  53. package/docs/readmes/features/roles/learner.md +210 -0
  54. package/docs/readmes/features/roles/planner.md +182 -0
  55. package/docs/readmes/features/roles/researcher.md +164 -0
  56. package/docs/readmes/features/roles/reviewer.md +184 -0
  57. package/docs/readmes/features/roles/specifier.md +162 -0
  58. package/docs/readmes/features/roles/verifier.md +215 -0
  59. package/docs/readmes/features/schemas/README.md +178 -0
  60. package/docs/readmes/features/skills/README.md +63 -0
  61. package/docs/readmes/features/skills/brainstorming.md +96 -0
  62. package/docs/readmes/features/skills/debugging.md +148 -0
  63. package/docs/readmes/features/skills/design.md +120 -0
  64. package/docs/readmes/features/skills/prepare-next.md +109 -0
  65. package/docs/readmes/features/skills/run-audit.md +159 -0
  66. package/docs/readmes/features/skills/scan-project.md +109 -0
  67. package/docs/readmes/features/skills/self-audit.md +176 -0
  68. package/docs/readmes/features/skills/tdd.md +137 -0
  69. package/docs/readmes/features/skills/using-skills.md +92 -0
  70. package/docs/readmes/features/skills/verification.md +120 -0
  71. package/docs/readmes/features/skills/writing-plans.md +104 -0
  72. package/docs/readmes/features/tooling/README.md +320 -0
  73. package/docs/readmes/features/workflows/README.md +186 -0
  74. package/docs/readmes/features/workflows/author.md +181 -0
  75. package/docs/readmes/features/workflows/clarify.md +154 -0
  76. package/docs/readmes/features/workflows/design-review.md +171 -0
  77. package/docs/readmes/features/workflows/design.md +169 -0
  78. package/docs/readmes/features/workflows/discover.md +162 -0
  79. package/docs/readmes/features/workflows/execute.md +173 -0
  80. package/docs/readmes/features/workflows/learn.md +167 -0
  81. package/docs/readmes/features/workflows/plan-review.md +165 -0
  82. package/docs/readmes/features/workflows/plan.md +170 -0
  83. package/docs/readmes/features/workflows/prepare-next.md +167 -0
  84. package/docs/readmes/features/workflows/review.md +169 -0
  85. package/docs/readmes/features/workflows/run-audit.md +191 -0
  86. package/docs/readmes/features/workflows/spec-challenge.md +159 -0
  87. package/docs/readmes/features/workflows/specify.md +160 -0
  88. package/docs/readmes/features/workflows/verify.md +177 -0
  89. package/docs/readmes/packages/README.md +50 -0
  90. package/docs/readmes/packages/ajv.md +117 -0
  91. package/docs/readmes/packages/context-mode.md +118 -0
  92. package/docs/readmes/packages/gray-matter.md +116 -0
  93. package/docs/readmes/packages/node-test.md +137 -0
  94. package/docs/readmes/packages/yaml.md +112 -0
  95. package/docs/reference/configuration-reference.md +159 -0
  96. package/docs/reference/expertise-index.md +52 -0
  97. package/docs/reference/git-flow.md +43 -0
  98. package/docs/reference/hooks.md +87 -0
  99. package/docs/reference/host-exports.md +50 -0
  100. package/docs/reference/launch-checklist.md +172 -0
  101. package/docs/reference/marketplace-listings.md +76 -0
  102. package/docs/reference/release-process.md +34 -0
  103. package/docs/reference/roles-reference.md +77 -0
  104. package/docs/reference/skills.md +33 -0
  105. package/docs/reference/templates.md +29 -0
  106. package/docs/reference/tooling-cli.md +94 -0
  107. package/docs/truth-claims.yaml +222 -0
  108. package/expertise/PROGRESS.md +63 -0
  109. package/expertise/README.md +18 -0
  110. package/expertise/antipatterns/PROGRESS.md +56 -0
  111. package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
  112. package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
  113. package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
  114. package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
  115. package/expertise/antipatterns/backend/index.md +24 -0
  116. package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
  117. package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
  118. package/expertise/antipatterns/code/async-antipatterns.md +622 -0
  119. package/expertise/antipatterns/code/code-smells.md +1186 -0
  120. package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
  121. package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
  122. package/expertise/antipatterns/code/index.md +27 -0
  123. package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
  124. package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
  125. package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
  126. package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
  127. package/expertise/antipatterns/design/dark-patterns.md +1121 -0
  128. package/expertise/antipatterns/design/index.md +22 -0
  129. package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
  130. package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
  131. package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
  132. package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
  133. package/expertise/antipatterns/frontend/index.md +23 -0
  134. package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
  135. package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
  136. package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
  137. package/expertise/antipatterns/index.md +31 -0
  138. package/expertise/antipatterns/performance/index.md +20 -0
  139. package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
  140. package/expertise/antipatterns/performance/premature-optimization.md +623 -0
  141. package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
  142. package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
  143. package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
  144. package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
  145. package/expertise/antipatterns/process/index.md +23 -0
  146. package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
  147. package/expertise/antipatterns/security/index.md +20 -0
  148. package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
  149. package/expertise/antipatterns/security/security-theater.md +843 -0
  150. package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
  151. package/expertise/architecture/PROGRESS.md +70 -0
  152. package/expertise/architecture/data/caching-architecture.md +671 -0
  153. package/expertise/architecture/data/data-consistency.md +574 -0
  154. package/expertise/architecture/data/data-modeling.md +536 -0
  155. package/expertise/architecture/data/event-streams-and-queues.md +634 -0
  156. package/expertise/architecture/data/index.md +25 -0
  157. package/expertise/architecture/data/search-architecture.md +663 -0
  158. package/expertise/architecture/data/sql-vs-nosql.md +708 -0
  159. package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
  160. package/expertise/architecture/decisions/build-vs-buy.md +616 -0
  161. package/expertise/architecture/decisions/index.md +23 -0
  162. package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
  163. package/expertise/architecture/decisions/technology-selection.md +616 -0
  164. package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
  165. package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
  166. package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
  167. package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
  168. package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
  169. package/expertise/architecture/distributed/index.md +25 -0
  170. package/expertise/architecture/distributed/saga-pattern.md +797 -0
  171. package/expertise/architecture/foundations/architectural-thinking.md +460 -0
  172. package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
  173. package/expertise/architecture/foundations/design-principles-solid.md +649 -0
  174. package/expertise/architecture/foundations/domain-driven-design.md +719 -0
  175. package/expertise/architecture/foundations/index.md +25 -0
  176. package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
  177. package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
  178. package/expertise/architecture/index.md +34 -0
  179. package/expertise/architecture/integration/api-design-graphql.md +638 -0
  180. package/expertise/architecture/integration/api-design-grpc.md +804 -0
  181. package/expertise/architecture/integration/api-design-rest.md +892 -0
  182. package/expertise/architecture/integration/index.md +25 -0
  183. package/expertise/architecture/integration/third-party-integration.md +795 -0
  184. package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
  185. package/expertise/architecture/integration/websockets-realtime.md +791 -0
  186. package/expertise/architecture/mobile-architecture/index.md +22 -0
  187. package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
  188. package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
  189. package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
  190. package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
  191. package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
  192. package/expertise/architecture/patterns/event-driven.md +797 -0
  193. package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
  194. package/expertise/architecture/patterns/index.md +27 -0
  195. package/expertise/architecture/patterns/layered-architecture.md +736 -0
  196. package/expertise/architecture/patterns/microservices.md +753 -0
  197. package/expertise/architecture/patterns/modular-monolith.md +692 -0
  198. package/expertise/architecture/patterns/monolith.md +626 -0
  199. package/expertise/architecture/patterns/plugin-architecture.md +735 -0
  200. package/expertise/architecture/patterns/serverless.md +780 -0
  201. package/expertise/architecture/scaling/database-scaling.md +615 -0
  202. package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
  203. package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
  204. package/expertise/architecture/scaling/index.md +24 -0
  205. package/expertise/architecture/scaling/multi-tenancy.md +800 -0
  206. package/expertise/architecture/scaling/stateless-design.md +787 -0
  207. package/expertise/backend/embedded-firmware.md +625 -0
  208. package/expertise/backend/go.md +853 -0
  209. package/expertise/backend/index.md +24 -0
  210. package/expertise/backend/java-spring.md +448 -0
  211. package/expertise/backend/node-typescript.md +625 -0
  212. package/expertise/backend/python-fastapi.md +724 -0
  213. package/expertise/backend/rust.md +458 -0
  214. package/expertise/backend/solidity.md +711 -0
  215. package/expertise/composition-map.yaml +443 -0
  216. package/expertise/content/foundations/content-modeling.md +395 -0
  217. package/expertise/content/foundations/editorial-standards.md +449 -0
  218. package/expertise/content/foundations/index.md +24 -0
  219. package/expertise/content/foundations/microcopy.md +455 -0
  220. package/expertise/content/foundations/terminology-governance.md +509 -0
  221. package/expertise/content/index.md +34 -0
  222. package/expertise/content/patterns/accessibility-copy.md +518 -0
  223. package/expertise/content/patterns/index.md +24 -0
  224. package/expertise/content/patterns/notification-content.md +433 -0
  225. package/expertise/content/patterns/sample-content.md +486 -0
  226. package/expertise/content/patterns/state-copy.md +439 -0
  227. package/expertise/design/PROGRESS.md +58 -0
  228. package/expertise/design/disciplines/dark-mode-theming.md +577 -0
  229. package/expertise/design/disciplines/design-systems.md +595 -0
  230. package/expertise/design/disciplines/index.md +25 -0
  231. package/expertise/design/disciplines/information-architecture.md +800 -0
  232. package/expertise/design/disciplines/interaction-design.md +788 -0
  233. package/expertise/design/disciplines/responsive-design.md +552 -0
  234. package/expertise/design/disciplines/usability-testing.md +516 -0
  235. package/expertise/design/disciplines/user-research.md +792 -0
  236. package/expertise/design/foundations/accessibility-design.md +796 -0
  237. package/expertise/design/foundations/color-theory.md +797 -0
  238. package/expertise/design/foundations/iconography.md +795 -0
  239. package/expertise/design/foundations/index.md +26 -0
  240. package/expertise/design/foundations/motion-and-animation.md +653 -0
  241. package/expertise/design/foundations/rtl-design.md +585 -0
  242. package/expertise/design/foundations/spacing-and-layout.md +607 -0
  243. package/expertise/design/foundations/typography.md +800 -0
  244. package/expertise/design/foundations/visual-hierarchy.md +761 -0
  245. package/expertise/design/index.md +32 -0
  246. package/expertise/design/patterns/authentication-flows.md +474 -0
  247. package/expertise/design/patterns/content-consumption.md +789 -0
  248. package/expertise/design/patterns/data-display.md +618 -0
  249. package/expertise/design/patterns/e-commerce.md +1494 -0
  250. package/expertise/design/patterns/feedback-and-states.md +642 -0
  251. package/expertise/design/patterns/forms-and-input.md +819 -0
  252. package/expertise/design/patterns/gamification.md +801 -0
  253. package/expertise/design/patterns/index.md +31 -0
  254. package/expertise/design/patterns/microinteractions.md +449 -0
  255. package/expertise/design/patterns/navigation.md +800 -0
  256. package/expertise/design/patterns/notifications.md +705 -0
  257. package/expertise/design/patterns/onboarding.md +700 -0
  258. package/expertise/design/patterns/search-and-filter.md +601 -0
  259. package/expertise/design/patterns/settings-and-preferences.md +768 -0
  260. package/expertise/design/patterns/social-and-community.md +748 -0
  261. package/expertise/design/platforms/desktop-native.md +612 -0
  262. package/expertise/design/platforms/index.md +25 -0
  263. package/expertise/design/platforms/mobile-android.md +825 -0
  264. package/expertise/design/platforms/mobile-cross-platform.md +983 -0
  265. package/expertise/design/platforms/mobile-ios.md +699 -0
  266. package/expertise/design/platforms/tablet.md +794 -0
  267. package/expertise/design/platforms/web-dashboard.md +790 -0
  268. package/expertise/design/platforms/web-responsive.md +550 -0
  269. package/expertise/design/psychology/behavioral-nudges.md +449 -0
  270. package/expertise/design/psychology/cognitive-load.md +1191 -0
  271. package/expertise/design/psychology/error-psychology.md +778 -0
  272. package/expertise/design/psychology/index.md +22 -0
  273. package/expertise/design/psychology/persuasive-design.md +736 -0
  274. package/expertise/design/psychology/user-mental-models.md +623 -0
  275. package/expertise/design/tooling/open-pencil.md +266 -0
  276. package/expertise/frontend/angular.md +1073 -0
  277. package/expertise/frontend/desktop-electron.md +546 -0
  278. package/expertise/frontend/flutter.md +782 -0
  279. package/expertise/frontend/index.md +27 -0
  280. package/expertise/frontend/native-android.md +409 -0
  281. package/expertise/frontend/native-ios.md +490 -0
  282. package/expertise/frontend/react-native.md +1160 -0
  283. package/expertise/frontend/react.md +808 -0
  284. package/expertise/frontend/vue.md +1089 -0
  285. package/expertise/humanize/domain-rules-code.md +79 -0
  286. package/expertise/humanize/domain-rules-content.md +67 -0
  287. package/expertise/humanize/domain-rules-technical-docs.md +56 -0
  288. package/expertise/humanize/index.md +35 -0
  289. package/expertise/humanize/self-audit-checklist.md +87 -0
  290. package/expertise/humanize/sentence-patterns.md +218 -0
  291. package/expertise/humanize/vocabulary-blacklist.md +105 -0
  292. package/expertise/i18n/PROGRESS.md +65 -0
  293. package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
  294. package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
  295. package/expertise/i18n/advanced/complex-scripts.md +30 -0
  296. package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
  297. package/expertise/i18n/advanced/testing-i18n.md +28 -0
  298. package/expertise/i18n/content/content-adaptation.md +23 -0
  299. package/expertise/i18n/content/locale-specific-formatting.md +23 -0
  300. package/expertise/i18n/content/machine-translation-integration.md +28 -0
  301. package/expertise/i18n/content/translation-management.md +29 -0
  302. package/expertise/i18n/foundations/date-time-calendars.md +67 -0
  303. package/expertise/i18n/foundations/i18n-architecture.md +272 -0
  304. package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
  305. package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
  306. package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
  307. package/expertise/i18n/foundations/string-externalization.md +236 -0
  308. package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
  309. package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
  310. package/expertise/i18n/index.md +38 -0
  311. package/expertise/i18n/platform/backend-i18n.md +31 -0
  312. package/expertise/i18n/platform/flutter-i18n.md +148 -0
  313. package/expertise/i18n/platform/native-android-i18n.md +36 -0
  314. package/expertise/i18n/platform/native-ios-i18n.md +36 -0
  315. package/expertise/i18n/platform/react-i18n.md +103 -0
  316. package/expertise/i18n/platform/web-css-i18n.md +81 -0
  317. package/expertise/i18n/rtl/arabic-specific.md +175 -0
  318. package/expertise/i18n/rtl/hebrew-specific.md +149 -0
  319. package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
  320. package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
  321. package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
  322. package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
  323. package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
  324. package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
  325. package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
  326. package/expertise/i18n/rtl/rtl-typography.md +160 -0
  327. package/expertise/index.md +113 -0
  328. package/expertise/index.yaml +216 -0
  329. package/expertise/infrastructure/cloud-aws.md +597 -0
  330. package/expertise/infrastructure/cloud-gcp.md +599 -0
  331. package/expertise/infrastructure/cybersecurity.md +816 -0
  332. package/expertise/infrastructure/database-mongodb.md +447 -0
  333. package/expertise/infrastructure/database-postgres.md +400 -0
  334. package/expertise/infrastructure/devops-cicd.md +787 -0
  335. package/expertise/infrastructure/index.md +27 -0
  336. package/expertise/performance/PROGRESS.md +50 -0
  337. package/expertise/performance/backend/api-latency.md +1204 -0
  338. package/expertise/performance/backend/background-jobs.md +506 -0
  339. package/expertise/performance/backend/connection-pooling.md +1209 -0
  340. package/expertise/performance/backend/database-query-optimization.md +515 -0
  341. package/expertise/performance/backend/index.md +23 -0
  342. package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
  343. package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
  344. package/expertise/performance/foundations/caching-strategies.md +489 -0
  345. package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
  346. package/expertise/performance/foundations/index.md +24 -0
  347. package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
  348. package/expertise/performance/foundations/memory-management.md +964 -0
  349. package/expertise/performance/foundations/performance-budgets.md +1314 -0
  350. package/expertise/performance/index.md +31 -0
  351. package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
  352. package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
  353. package/expertise/performance/infrastructure/index.md +22 -0
  354. package/expertise/performance/infrastructure/load-balancing.md +1081 -0
  355. package/expertise/performance/infrastructure/observability.md +1079 -0
  356. package/expertise/performance/mobile/index.md +23 -0
  357. package/expertise/performance/mobile/mobile-animations.md +544 -0
  358. package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
  359. package/expertise/performance/mobile/mobile-network.md +452 -0
  360. package/expertise/performance/mobile/mobile-rendering.md +599 -0
  361. package/expertise/performance/mobile/mobile-startup-time.md +505 -0
  362. package/expertise/performance/platform-specific/flutter-performance.md +647 -0
  363. package/expertise/performance/platform-specific/index.md +22 -0
  364. package/expertise/performance/platform-specific/node-performance.md +1307 -0
  365. package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
  366. package/expertise/performance/platform-specific/react-performance.md +1403 -0
  367. package/expertise/performance/web/bundle-optimization.md +1239 -0
  368. package/expertise/performance/web/image-and-media.md +636 -0
  369. package/expertise/performance/web/index.md +24 -0
  370. package/expertise/performance/web/network-optimization.md +1133 -0
  371. package/expertise/performance/web/rendering-performance.md +1098 -0
  372. package/expertise/performance/web/ssr-and-hydration.md +918 -0
  373. package/expertise/performance/web/web-vitals.md +1374 -0
  374. package/expertise/quality/accessibility.md +985 -0
  375. package/expertise/quality/evidence-based-verification.md +499 -0
  376. package/expertise/quality/index.md +24 -0
  377. package/expertise/quality/ml-model-audit.md +614 -0
  378. package/expertise/quality/performance.md +600 -0
  379. package/expertise/quality/testing-api.md +891 -0
  380. package/expertise/quality/testing-mobile.md +496 -0
  381. package/expertise/quality/testing-web.md +849 -0
  382. package/expertise/security/PROGRESS.md +54 -0
  383. package/expertise/security/agentic-identity.md +540 -0
  384. package/expertise/security/compliance-frameworks.md +601 -0
  385. package/expertise/security/data/data-encryption.md +364 -0
  386. package/expertise/security/data/data-privacy-gdpr.md +692 -0
  387. package/expertise/security/data/database-security.md +1171 -0
  388. package/expertise/security/data/index.md +22 -0
  389. package/expertise/security/data/pii-handling.md +531 -0
  390. package/expertise/security/foundations/authentication.md +1041 -0
  391. package/expertise/security/foundations/authorization.md +603 -0
  392. package/expertise/security/foundations/cryptography.md +1001 -0
  393. package/expertise/security/foundations/index.md +25 -0
  394. package/expertise/security/foundations/owasp-top-10.md +1354 -0
  395. package/expertise/security/foundations/secrets-management.md +1217 -0
  396. package/expertise/security/foundations/secure-sdlc.md +700 -0
  397. package/expertise/security/foundations/supply-chain-security.md +698 -0
  398. package/expertise/security/index.md +31 -0
  399. package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
  400. package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
  401. package/expertise/security/infrastructure/container-security.md +721 -0
  402. package/expertise/security/infrastructure/incident-response.md +1295 -0
  403. package/expertise/security/infrastructure/index.md +24 -0
  404. package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
  405. package/expertise/security/infrastructure/network-security.md +1337 -0
  406. package/expertise/security/mobile/index.md +23 -0
  407. package/expertise/security/mobile/mobile-android-security.md +1218 -0
  408. package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
  409. package/expertise/security/mobile/mobile-data-storage.md +1265 -0
  410. package/expertise/security/mobile/mobile-ios-security.md +1401 -0
  411. package/expertise/security/mobile/mobile-network-security.md +1520 -0
  412. package/expertise/security/smart-contract-security.md +594 -0
  413. package/expertise/security/testing/index.md +22 -0
  414. package/expertise/security/testing/penetration-testing.md +1258 -0
  415. package/expertise/security/testing/security-code-review.md +1765 -0
  416. package/expertise/security/testing/threat-modeling.md +1074 -0
  417. package/expertise/security/testing/vulnerability-scanning.md +1062 -0
  418. package/expertise/security/web/api-security.md +586 -0
  419. package/expertise/security/web/cors-and-headers.md +433 -0
  420. package/expertise/security/web/csrf.md +562 -0
  421. package/expertise/security/web/file-upload.md +1477 -0
  422. package/expertise/security/web/index.md +25 -0
  423. package/expertise/security/web/injection.md +1375 -0
  424. package/expertise/security/web/session-management.md +1101 -0
  425. package/expertise/security/web/xss.md +1158 -0
  426. package/exports/README.md +17 -0
  427. package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
  428. package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
  429. package/exports/hosts/claude/.claude/agents/designer.md +55 -0
  430. package/exports/hosts/claude/.claude/agents/executor.md +55 -0
  431. package/exports/hosts/claude/.claude/agents/learner.md +51 -0
  432. package/exports/hosts/claude/.claude/agents/planner.md +53 -0
  433. package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
  434. package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
  435. package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
  436. package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
  437. package/exports/hosts/claude/.claude/commands/author.md +42 -0
  438. package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
  439. package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
  440. package/exports/hosts/claude/.claude/commands/design.md +44 -0
  441. package/exports/hosts/claude/.claude/commands/discover.md +37 -0
  442. package/exports/hosts/claude/.claude/commands/execute.md +48 -0
  443. package/exports/hosts/claude/.claude/commands/learn.md +38 -0
  444. package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
  445. package/exports/hosts/claude/.claude/commands/plan.md +39 -0
  446. package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
  447. package/exports/hosts/claude/.claude/commands/review.md +40 -0
  448. package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
  449. package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
  450. package/exports/hosts/claude/.claude/commands/specify.md +38 -0
  451. package/exports/hosts/claude/.claude/commands/verify.md +37 -0
  452. package/exports/hosts/claude/.claude/settings.json +34 -0
  453. package/exports/hosts/claude/CLAUDE.md +19 -0
  454. package/exports/hosts/claude/export.manifest.json +38 -0
  455. package/exports/hosts/claude/host-package.json +67 -0
  456. package/exports/hosts/codex/AGENTS.md +19 -0
  457. package/exports/hosts/codex/export.manifest.json +38 -0
  458. package/exports/hosts/codex/host-package.json +41 -0
  459. package/exports/hosts/cursor/.cursor/hooks.json +16 -0
  460. package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
  461. package/exports/hosts/cursor/export.manifest.json +38 -0
  462. package/exports/hosts/cursor/host-package.json +42 -0
  463. package/exports/hosts/gemini/GEMINI.md +19 -0
  464. package/exports/hosts/gemini/export.manifest.json +38 -0
  465. package/exports/hosts/gemini/host-package.json +41 -0
  466. package/hooks/README.md +18 -0
  467. package/hooks/definitions/loop_cap_guard.yaml +21 -0
  468. package/hooks/definitions/post_tool_capture.yaml +24 -0
  469. package/hooks/definitions/pre_compact_summary.yaml +19 -0
  470. package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
  471. package/hooks/definitions/protected_path_write_guard.yaml +19 -0
  472. package/hooks/definitions/session_start.yaml +19 -0
  473. package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
  474. package/hooks/loop-cap-guard +17 -0
  475. package/hooks/post-tool-lint +36 -0
  476. package/hooks/protected-path-write-guard +17 -0
  477. package/hooks/session-start +41 -0
  478. package/llms-full.txt +2355 -0
  479. package/llms.txt +43 -0
  480. package/package.json +79 -0
  481. package/roles/README.md +20 -0
  482. package/roles/clarifier.md +42 -0
  483. package/roles/content-author.md +63 -0
  484. package/roles/designer.md +55 -0
  485. package/roles/executor.md +55 -0
  486. package/roles/learner.md +51 -0
  487. package/roles/planner.md +53 -0
  488. package/roles/researcher.md +43 -0
  489. package/roles/reviewer.md +54 -0
  490. package/roles/specifier.md +47 -0
  491. package/roles/verifier.md +71 -0
  492. package/schemas/README.md +24 -0
  493. package/schemas/accepted-learning.schema.json +20 -0
  494. package/schemas/author-artifact.schema.json +156 -0
  495. package/schemas/clarification.schema.json +19 -0
  496. package/schemas/design-artifact.schema.json +80 -0
  497. package/schemas/docs-claim.schema.json +18 -0
  498. package/schemas/export-manifest.schema.json +20 -0
  499. package/schemas/hook.schema.json +67 -0
  500. package/schemas/host-export-package.schema.json +18 -0
  501. package/schemas/implementation-plan.schema.json +19 -0
  502. package/schemas/proposed-learning.schema.json +19 -0
  503. package/schemas/research.schema.json +18 -0
  504. package/schemas/review.schema.json +29 -0
  505. package/schemas/run-manifest.schema.json +18 -0
  506. package/schemas/spec-challenge.schema.json +18 -0
  507. package/schemas/spec.schema.json +20 -0
  508. package/schemas/usage.schema.json +102 -0
  509. package/schemas/verification-proof.schema.json +29 -0
  510. package/schemas/wazir-manifest.schema.json +173 -0
  511. package/skills/README.md +40 -0
  512. package/skills/brainstorming/SKILL.md +77 -0
  513. package/skills/debugging/SKILL.md +50 -0
  514. package/skills/design/SKILL.md +61 -0
  515. package/skills/dispatching-parallel-agents/SKILL.md +128 -0
  516. package/skills/executing-plans/SKILL.md +70 -0
  517. package/skills/finishing-a-development-branch/SKILL.md +169 -0
  518. package/skills/humanize/SKILL.md +123 -0
  519. package/skills/init-pipeline/SKILL.md +124 -0
  520. package/skills/prepare-next/SKILL.md +20 -0
  521. package/skills/receiving-code-review/SKILL.md +123 -0
  522. package/skills/requesting-code-review/SKILL.md +105 -0
  523. package/skills/requesting-code-review/code-reviewer.md +108 -0
  524. package/skills/run-audit/SKILL.md +197 -0
  525. package/skills/scan-project/SKILL.md +41 -0
  526. package/skills/self-audit/SKILL.md +153 -0
  527. package/skills/subagent-driven-development/SKILL.md +154 -0
  528. package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
  529. package/skills/subagent-driven-development/implementer-prompt.md +102 -0
  530. package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
  531. package/skills/tdd/SKILL.md +23 -0
  532. package/skills/using-git-worktrees/SKILL.md +163 -0
  533. package/skills/using-skills/SKILL.md +95 -0
  534. package/skills/verification/SKILL.md +22 -0
  535. package/skills/wazir/SKILL.md +463 -0
  536. package/skills/writing-plans/SKILL.md +30 -0
  537. package/skills/writing-skills/SKILL.md +157 -0
  538. package/skills/writing-skills/anthropic-best-practices.md +122 -0
  539. package/skills/writing-skills/persuasion-principles.md +50 -0
  540. package/templates/README.md +20 -0
  541. package/templates/artifacts/README.md +10 -0
  542. package/templates/artifacts/accepted-learning.md +19 -0
  543. package/templates/artifacts/accepted-learning.template.json +12 -0
  544. package/templates/artifacts/author.md +74 -0
  545. package/templates/artifacts/author.template.json +19 -0
  546. package/templates/artifacts/clarification.md +21 -0
  547. package/templates/artifacts/clarification.template.json +12 -0
  548. package/templates/artifacts/execute-notes.md +19 -0
  549. package/templates/artifacts/implementation-plan.md +21 -0
  550. package/templates/artifacts/implementation-plan.template.json +11 -0
  551. package/templates/artifacts/learning-proposal.md +19 -0
  552. package/templates/artifacts/next-run-handoff.md +21 -0
  553. package/templates/artifacts/plan-review.md +19 -0
  554. package/templates/artifacts/proposed-learning.template.json +12 -0
  555. package/templates/artifacts/research.md +21 -0
  556. package/templates/artifacts/research.template.json +12 -0
  557. package/templates/artifacts/review-findings.md +19 -0
  558. package/templates/artifacts/review.template.json +11 -0
  559. package/templates/artifacts/run-manifest.template.json +8 -0
  560. package/templates/artifacts/spec-challenge.md +19 -0
  561. package/templates/artifacts/spec-challenge.template.json +11 -0
  562. package/templates/artifacts/spec.md +21 -0
  563. package/templates/artifacts/spec.template.json +12 -0
  564. package/templates/artifacts/verification-proof.md +19 -0
  565. package/templates/artifacts/verification-proof.template.json +11 -0
  566. package/templates/examples/accepted-learning.example.json +14 -0
  567. package/templates/examples/author.example.json +152 -0
  568. package/templates/examples/clarification.example.json +15 -0
  569. package/templates/examples/docs-claim.example.json +8 -0
  570. package/templates/examples/export-manifest.example.json +7 -0
  571. package/templates/examples/host-export-package.example.json +11 -0
  572. package/templates/examples/implementation-plan.example.json +17 -0
  573. package/templates/examples/proposed-learning.example.json +13 -0
  574. package/templates/examples/research.example.json +15 -0
  575. package/templates/examples/research.example.md +6 -0
  576. package/templates/examples/review.example.json +17 -0
  577. package/templates/examples/run-manifest.example.json +9 -0
  578. package/templates/examples/spec-challenge.example.json +14 -0
  579. package/templates/examples/spec.example.json +21 -0
  580. package/templates/examples/verification-proof.example.json +21 -0
  581. package/templates/examples/wazir-manifest.example.yaml +65 -0
  582. package/templates/task-definition-schema.md +99 -0
  583. package/tooling/README.md +20 -0
  584. package/tooling/src/adapters/context-mode.js +50 -0
  585. package/tooling/src/capture/command.js +376 -0
  586. package/tooling/src/capture/store.js +99 -0
  587. package/tooling/src/capture/usage.js +270 -0
  588. package/tooling/src/checks/branches.js +50 -0
  589. package/tooling/src/checks/brand-truth.js +110 -0
  590. package/tooling/src/checks/changelog.js +231 -0
  591. package/tooling/src/checks/command-registry.js +36 -0
  592. package/tooling/src/checks/commits.js +102 -0
  593. package/tooling/src/checks/docs-drift.js +103 -0
  594. package/tooling/src/checks/docs-truth.js +201 -0
  595. package/tooling/src/checks/runtime-surface.js +156 -0
  596. package/tooling/src/cli.js +116 -0
  597. package/tooling/src/command-options.js +56 -0
  598. package/tooling/src/commands/validate.js +320 -0
  599. package/tooling/src/doctor/command.js +91 -0
  600. package/tooling/src/export/command.js +77 -0
  601. package/tooling/src/export/compiler.js +498 -0
  602. package/tooling/src/guards/loop-cap-guard.js +52 -0
  603. package/tooling/src/guards/protected-path-write-guard.js +67 -0
  604. package/tooling/src/index/command.js +152 -0
  605. package/tooling/src/index/storage.js +1061 -0
  606. package/tooling/src/index/summarizers.js +261 -0
  607. package/tooling/src/loaders.js +18 -0
  608. package/tooling/src/project-root.js +22 -0
  609. package/tooling/src/recall/command.js +225 -0
  610. package/tooling/src/schema-validator.js +30 -0
  611. package/tooling/src/state-root.js +40 -0
  612. package/tooling/src/status/command.js +71 -0
  613. package/wazir.manifest.yaml +135 -0
  614. package/workflows/README.md +19 -0
  615. package/workflows/author.md +42 -0
  616. package/workflows/clarify.md +38 -0
  617. package/workflows/design-review.md +46 -0
  618. package/workflows/design.md +44 -0
  619. package/workflows/discover.md +37 -0
  620. package/workflows/execute.md +48 -0
  621. package/workflows/learn.md +38 -0
  622. package/workflows/plan-review.md +42 -0
  623. package/workflows/plan.md +39 -0
  624. package/workflows/prepare-next.md +37 -0
  625. package/workflows/review.md +40 -0
  626. package/workflows/run-audit.md +41 -0
  627. package/workflows/spec-challenge.md +41 -0
  628. package/workflows/specify.md +38 -0
  629. package/workflows/verify.md +37 -0
@@ -0,0 +1,1195 @@
1
+ # Authentication & Authorization Anti-Patterns
2
+ > A catalog of authentication and authorization mistakes that lead to account takeovers, data breaches,
3
+ > and privilege escalation. Each entry documents the pattern, its real-world consequences, and concrete fixes.
4
+ > Broken Access Control is the #1 vulnerability in the OWASP Top 10 (2021 and 2025), found in 94% of
5
+ > applications tested. These anti-patterns are not theoretical -- they are extracted from breaches,
6
+ > CVEs, and bug bounty reports spanning the last fifteen years.
7
+ > **Domain:** Backend
8
+ > **Anti-patterns covered:** 20
9
+ > **Highest severity:** Critical
10
+
11
+ ---
12
+
13
+ ## Anti-Patterns
14
+
15
+ ### AP-01: Rolling Your Own Crypto
16
+
17
+ **Also known as:** Homebrew authentication, DIY encryption, custom token scheme
18
+ **Frequency:** Common
19
+ **Severity:** Critical
20
+ **Detection difficulty:** Hard
21
+
22
+ **What it looks like:**
23
+ ```python
24
+ import hashlib, time, base64
25
+
26
+ def generate_token(user_id):
27
+ raw = f"{user_id}:{time.time()}:{MY_APP_SECRET}"
28
+ return base64.b64encode(hashlib.sha256(raw.encode()).digest()).decode()
29
+
30
+ def verify_token(token, user_id):
31
+ # "We'll just regenerate and compare"
32
+ # But time.time() has moved on, so this never actually works
33
+ # Developer adds a 60-second window... then 5 minutes... then gives up and skips verification
34
+ return True
35
+ ```
36
+
37
+ **Why developers do it:**
38
+ They want full control, consider standard libraries "overkill" for their use case, or do not understand the complexity beneath JWT/OAuth/session libraries. Sometimes the motivation is avoiding a dependency.
39
+
40
+ **What goes wrong:**
41
+ Custom schemes lack peer review, formal verification, and adversarial testing. The CSL Dualcom CS2300-R alarm system used a hybrid roll-your-own crypto implementation that researchers broke trivially, putting physical security of shops and offices at risk. Research from a 2021 empirical study found that 37.2% of vulnerabilities in cryptographic libraries are memory safety issues, not even cryptographic ones -- homebrew implementations inherit both classes of bugs. AES-GCM nonce reuse, predictable IVs, and wrong-mode-of-operation errors are the most common categories.
42
+
43
+ **The fix:**
44
+ ```python
45
+ # Use established libraries: PyJWT, authlib, passlib, or your framework's auth module
46
+ import jwt
47
+
48
+ def generate_token(user_id):
49
+ return jwt.encode(
50
+ {"sub": user_id, "exp": datetime.utcnow() + timedelta(hours=1)},
51
+ settings.SECRET_KEY,
52
+ algorithm="HS256"
53
+ )
54
+
55
+ def verify_token(token):
56
+ return jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
57
+ ```
58
+
59
+ **Detection rule:**
60
+ Flag any import of `hashlib` or `hmac` used in conjunction with session/token generation logic. Flag custom `encrypt`/`decrypt` functions that do not delegate to established libraries (libsodium, OpenSSL wrappers, AWS KMS).
61
+
62
+ ---
63
+
64
+ ### AP-02: Plaintext or Weak Password Hashing
65
+
66
+ **Also known as:** MD5 passwords, unsalted hashes, reversible encryption for passwords
67
+ **Frequency:** Very Common
68
+ **Severity:** Critical
69
+ **Detection difficulty:** Easy
70
+
71
+ **What it looks like:**
72
+ ```python
73
+ # Plaintext
74
+ def store_password(user, password):
75
+ db.execute("INSERT INTO users (email, password) VALUES (?, ?)", user.email, password)
76
+
77
+ # Weak hash
78
+ import hashlib
79
+ hashed = hashlib.md5(password.encode()).hexdigest()
80
+
81
+ # Unsalted SHA1
82
+ hashed = hashlib.sha1(password.encode()).hexdigest()
83
+ ```
84
+
85
+ **Why developers do it:**
86
+ Speed of implementation, unfamiliarity with password hashing algorithms, or the assumption that "hashing is hashing." Some developers confuse message-digest functions (MD5, SHA1) with password hashing functions (bcrypt, scrypt, Argon2).
87
+
88
+ **What goes wrong:**
89
+ - **RockYou (2009):** 32 million passwords stored in plaintext. The leaked dataset became the foundation of the `rockyou.txt` wordlist used in virtually every password cracking operation since.
90
+ - **LinkedIn (2012):** 6.5 million passwords hashed with unsalted SHA1, cracked within hours using rainbow tables. The full dump later revealed 117 million credentials.
91
+ - **Adobe (2013):** 153 million accounts breached. Passwords were encrypted with 3DES in ECB mode (not hashed), and plaintext hints were stored alongside them. Identical passwords produced identical ciphertexts, enabling mass decryption via frequency analysis.
92
+ - **Yahoo (2014):** Used MD5 for password hashing before migrating to bcrypt.
93
+
94
+ A modern GPU can compute 10 billion MD5 hashes per second and hundreds of millions of SHA1 hashes per second.
95
+
96
+ **The fix:**
97
+ ```python
98
+ # Use bcrypt (or Argon2id for new projects)
99
+ from passlib.hash import bcrypt
100
+
101
+ def store_password(password):
102
+ return bcrypt.using(rounds=12).hash(password)
103
+
104
+ def verify_password(password, stored_hash):
105
+ return bcrypt.verify(password, stored_hash)
106
+ ```
107
+
108
+ **Detection rule:**
109
+ Grep for `hashlib.md5`, `hashlib.sha1`, `hashlib.sha256` adjacent to `password` variables. Flag any `INSERT` statement where a password column receives an unhashed value. Flag absence of bcrypt/scrypt/argon2 in dependency manifests.
110
+
111
+ ---
112
+
113
+ ### AP-03: JWT Stored in localStorage
114
+
115
+ **Also known as:** XSS-accessible tokens, client-side token exposure
116
+ **Frequency:** Very Common
117
+ **Severity:** High
118
+ **Detection difficulty:** Easy
119
+
120
+ **What it looks like:**
121
+ ```javascript
122
+ // After login
123
+ fetch('/api/login', { method: 'POST', body: JSON.stringify(credentials) })
124
+ .then(res => res.json())
125
+ .then(data => {
126
+ localStorage.setItem('token', data.jwt); // Accessible to any JS on the page
127
+ });
128
+
129
+ // On every request
130
+ fetch('/api/data', {
131
+ headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` }
132
+ });
133
+ ```
134
+
135
+ **Why developers do it:**
136
+ SPA tutorials universally demonstrate this pattern. localStorage is persistent across tabs, survives page reloads, and the API is trivial. Developers are unaware that any JavaScript running on the page (including XSS payloads, compromised npm packages, or injected analytics scripts) can read localStorage.
137
+
138
+ **What goes wrong:**
139
+ A single XSS vulnerability allows an attacker to exfiltrate every token in localStorage. Unlike httpOnly cookies, there is no browser-level protection. The token can be sent to an attacker-controlled server and replayed from any location. The blast radius expands if the JWT has a long lifetime (see AP-16).
140
+
141
+ **The fix:**
142
+ ```javascript
143
+ // Server sets the token as an httpOnly, Secure, SameSite cookie
144
+ // Response from POST /api/login:
145
+ // Set-Cookie: token=eyJ...; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600
146
+
147
+ // Client code needs no token handling -- the browser sends the cookie automatically
148
+ fetch('/api/data', { credentials: 'include' });
149
+ ```
150
+
151
+ If you must use a bearer token (e.g., cross-origin API), store it in a JavaScript closure or in-memory variable, and accept that it will not survive page reloads.
152
+
153
+ **Detection rule:**
154
+ Grep for `localStorage.setItem` or `sessionStorage.setItem` with token/jwt/auth keywords. Flag absence of `HttpOnly` and `Secure` flags on authentication cookies in `Set-Cookie` headers.
155
+
156
+ ---
157
+
158
+ ### AP-04: Not Validating JWT Signature or Expiration
159
+
160
+ **Also known as:** JWT bypass, `alg: none` attack, algorithm confusion
161
+ **Frequency:** Common
162
+ **Severity:** Critical
163
+ **Detection difficulty:** Moderate
164
+
165
+ **What it looks like:**
166
+ ```python
167
+ # Decoding without verification
168
+ import base64, json
169
+
170
+ def get_user_from_token(token):
171
+ payload = token.split('.')[1]
172
+ payload += '=' * (4 - len(payload) % 4) # Fix padding
173
+ return json.loads(base64.urlsafe_b64decode(payload))
174
+ # No signature check. No expiration check. Attacker can forge any payload.
175
+ ```
176
+
177
+ ```python
178
+ # Accepting any algorithm the token claims
179
+ jwt.decode(token, secret, algorithms=None) # Will accept "none" algorithm
180
+ ```
181
+
182
+ **Why developers do it:**
183
+ Developers treat JWTs as opaque session IDs rather than signed claims. Some copy decode-only snippets from Stack Overflow. Others configure JWT libraries permissively to "get it working" during development and never tighten the configuration.
184
+
185
+ **What goes wrong:**
186
+ - **`alg: none` attack:** Attacker sets the algorithm header to `"none"` and removes the signature. Early versions of many JWT libraries accepted this as valid. Auth0 disclosed critical vulnerabilities in multiple JWT libraries in 2015 enabling this exact attack.
187
+ - **Algorithm confusion (RS256 to HS256):** Attacker changes the algorithm from RS256 (asymmetric) to HS256 (symmetric) and signs the token with the server's public key (which is public). The server, configured to accept HS256, validates the signature using the public key as the HMAC secret. This was a widespread vulnerability class disclosed in 2015 affecting libraries across Node.js, PHP, Java, and Python.
188
+ - **CVE-2025-4692, CVE-2025-30144, CVE-2025-27371:** Critical JWT library bypass vulnerabilities affecting cloud platforms and SaaS stacks in 2025, exposing millions of users to remote code execution.
189
+
190
+ **The fix:**
191
+ ```python
192
+ # Always specify allowed algorithms explicitly
193
+ import jwt
194
+
195
+ def verify_token(token):
196
+ return jwt.decode(
197
+ token,
198
+ settings.PUBLIC_KEY,
199
+ algorithms=["RS256"], # Whitelist algorithms
200
+ options={
201
+ "verify_exp": True, # Enforce expiration
202
+ "verify_aud": True, # Verify audience
203
+ "require": ["exp", "sub", "aud"] # Require mandatory claims
204
+ },
205
+ audience="my-api"
206
+ )
207
+ ```
208
+
209
+ **Detection rule:**
210
+ Flag `jwt.decode` calls where `algorithms` is `None`, empty, or includes `"none"`. Flag JWT decode calls that do not verify the signature (`verify=False`, `options={"verify_signature": False}`). Flag absence of `exp` claim validation.
211
+
212
+ ---
213
+
214
+ ### AP-05: Missing Authorization Checks (IDOR)
215
+
216
+ **Also known as:** Insecure Direct Object Reference, horizontal privilege escalation, broken object-level authorization
217
+ **Frequency:** Very Common
218
+ **Severity:** Critical
219
+ **Detection difficulty:** Hard
220
+
221
+ **What it looks like:**
222
+ ```python
223
+ # User can access any invoice by changing the ID
224
+ @app.route('/api/invoices/<invoice_id>')
225
+ @login_required
226
+ def get_invoice(invoice_id):
227
+ invoice = db.invoices.find_one({"_id": invoice_id})
228
+ return jsonify(invoice) # No check: does this invoice belong to the requesting user?
229
+ ```
230
+
231
+ ```
232
+ GET /api/invoices/1001 -> User's own invoice
233
+ GET /api/invoices/1002 -> Another user's invoice (returned without error)
234
+ GET /api/invoices/1003 -> Another user's invoice (returned without error)
235
+ ```
236
+
237
+ **Why developers do it:**
238
+ Authentication is treated as sufficient. Developers verify "is this a logged-in user?" but not "does this user have access to this specific resource?" This is especially common in CRUD generators and ORM-backed REST APIs where routes are auto-generated.
239
+
240
+ **What goes wrong:**
241
+ IDOR is the most common access control vulnerability in web applications.
242
+ - **Snapchat (2015):** Personal information exposed via IDOR when attackers manipulated user ID parameters in API requests.
243
+ - **Uber (2017):** Attackers manipulated price parameters in ride-booking API to modify fares.
244
+ - **US Department of Defense (2020):** A researcher discovered an IDOR that allowed changing passwords on DoD web servers.
245
+ - **PayPal:** IDOR allowed Business Account owners to assign secondary users from other accounts.
246
+ - **Vimeo:** IDOR in password reset allowed full account takeover.
247
+
248
+ OWASP found Broken Access Control in 94% of applications tested, with over 318,000 occurrences in their contributed dataset.
249
+
250
+ **The fix:**
251
+ ```python
252
+ @app.route('/api/invoices/<invoice_id>')
253
+ @login_required
254
+ def get_invoice(invoice_id):
255
+ invoice = db.invoices.find_one({
256
+ "_id": invoice_id,
257
+ "owner_id": current_user.id # Scope query to the authenticated user
258
+ })
259
+ if not invoice:
260
+ abort(404) # Return 404, not 403 (don't reveal existence)
261
+ return jsonify(invoice)
262
+ ```
263
+
264
+ **Detection rule:**
265
+ Flag any database query in an authenticated endpoint that uses a user-supplied ID without including the authenticated user's ID in the query filter. Flag route handlers that accept resource IDs but do not reference `current_user`, `request.user`, or equivalent.
266
+
267
+ ---
268
+
269
+ ### AP-06: Client-Side Only Authorization
270
+
271
+ **Also known as:** UI-only access control, front-end gating, hidden button security
272
+ **Frequency:** Common
273
+ **Severity:** Critical
274
+ **Detection difficulty:** Moderate
275
+
276
+ **What it looks like:**
277
+ ```javascript
278
+ // React component
279
+ function AdminPanel() {
280
+ const { user } = useAuth();
281
+ if (user.role !== 'admin') return <Redirect to="/dashboard" />;
282
+
283
+ return <AdminDashboard />;
284
+ }
285
+
286
+ // But the API has no role check:
287
+ app.get('/api/admin/users', authenticateToken, (req, res) => {
288
+ // Any authenticated user can call this endpoint directly
289
+ const users = await db.query('SELECT * FROM users');
290
+ res.json(users);
291
+ });
292
+ ```
293
+
294
+ **Why developers do it:**
295
+ The developer sees the admin panel hidden in the UI and assumes the protection is complete. In SPA architectures, it is natural to gate routes on the client. The server-side check is forgotten or deferred.
296
+
297
+ **What goes wrong:**
298
+ Any user with a valid token can call the admin API directly using curl, Postman, or browser dev tools. CWE-602 (Client-Side Enforcement of Server-Side Security) and CWE-603 (Use of Client-Side Authentication) document this pattern formally. OWASP Mobile Top 10 ranks Insecure Authentication/Authorization as M3, noting that "developers should assume all client-side authorization controls can be bypassed."
299
+
300
+ A 2026 write-up demonstrated how applications returning user context objects containing role and permission fields allowed privilege escalation through simple request manipulation when the backend consumed those fields directly without server-side recalculation.
301
+
302
+ **The fix:**
303
+ ```javascript
304
+ // Server-side middleware enforces authorization
305
+ function requireRole(role) {
306
+ return (req, res, next) => {
307
+ // Role comes from the server session/token, NOT from the request body
308
+ if (req.user.role !== role) {
309
+ return res.status(403).json({ error: 'Forbidden' });
310
+ }
311
+ next();
312
+ };
313
+ }
314
+
315
+ app.get('/api/admin/users', authenticateToken, requireRole('admin'), async (req, res) => {
316
+ const users = await db.query('SELECT * FROM users');
317
+ res.json(users);
318
+ });
319
+ ```
320
+
321
+ **Detection rule:**
322
+ Flag API routes that lack authorization middleware. Compare the set of front-end route guards against server-side middleware to find gaps. Flag any endpoint under `/admin` or `/internal` paths that only has authentication (not authorization) middleware.
323
+
324
+ ---
325
+
326
+ ### AP-07: Hardcoded Credentials and API Keys
327
+
328
+ **Also known as:** Committed secrets, embedded credentials, config-in-code
329
+ **Frequency:** Very Common
330
+ **Severity:** Critical
331
+ **Detection difficulty:** Easy
332
+
333
+ **What it looks like:**
334
+ ```python
335
+ # In source code
336
+ AWS_ACCESS_KEY = "AKIAIOSFODNN7EXAMPLE"
337
+ AWS_SECRET_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
338
+ DATABASE_URL = "postgresql://admin:P@ssw0rd123@prod-db.internal:5432/app"
339
+
340
+ # In a config file checked into git
341
+ # config/settings.py
342
+ STRIPE_SECRET_KEY = "REDACTED_STRIPE_KEY"
343
+ ```
344
+
345
+ **Why developers do it:**
346
+ It is the fastest path from "not working" to "working." Environment variables and secret managers add setup friction. Developers intend to move secrets out "later" and forget or deprioritize it.
347
+
348
+ **What goes wrong:**
349
+ - **Uber (2016):** Hackers found AWS credentials on a private GitHub repo, used them to access an S3 bucket with data on 57 million customers and drivers.
350
+ - **GitHub (2023):** GitGuardian reported over 12.8 million secrets leaked in public GitHub repositories in a single year.
351
+ - **GitHub (2024):** 39 million API keys and credentials exposed across public repos. 91.6% of leaked secrets remained valid after five days. Only 2.6% were revoked within the first hour.
352
+ - **AI companies (2025):** 65% of 50 AI companies examined by Wiz had verified secrets leaked on GitHub, exposing models and training data.
353
+
354
+ Once committed, secrets persist in Git history even after deletion from the current branch.
355
+
356
+ **The fix:**
357
+ ```python
358
+ # Use environment variables
359
+ import os
360
+ AWS_ACCESS_KEY = os.environ["AWS_ACCESS_KEY"]
361
+
362
+ # Or use a secret manager
363
+ from aws_secretsmanager import get_secret
364
+ db_password = get_secret("prod/db/password")
365
+
366
+ # Pre-commit hook to prevent commits
367
+ # .pre-commit-config.yaml
368
+ repos:
369
+ - repo: https://github.com/gitleaks/gitleaks
370
+ hooks:
371
+ - id: gitleaks
372
+ ```
373
+
374
+ **Detection rule:**
375
+ Run gitleaks, truffleHog, or GitGuardian on every commit. Flag string literals matching known key patterns (AWS `AKIA*`, Stripe `sk_live_*`, GitHub `ghp_*`). Flag any variable named `password`, `secret`, `api_key`, or `token` assigned a string literal.
376
+
377
+ ---
378
+
379
+ ### AP-08: Session Fixation
380
+
381
+ **Also known as:** Pre-set session ID, session adoption
382
+ **Frequency:** Occasional
383
+ **Severity:** High
384
+ **Detection difficulty:** Moderate
385
+
386
+ **What it looks like:**
387
+ ```python
388
+ # Login handler that does NOT regenerate the session ID
389
+ @app.route('/login', methods=['POST'])
390
+ def login():
391
+ user = authenticate(request.form['username'], request.form['password'])
392
+ if user:
393
+ session['user_id'] = user.id # Set user in EXISTING session
394
+ # The session ID in the cookie remains the same as before login
395
+ return redirect('/dashboard')
396
+ ```
397
+
398
+ Attack flow:
399
+ 1. Attacker visits the site, receives session ID `abc123`.
400
+ 2. Attacker sends victim a link: `https://app.com/login?sid=abc123`.
401
+ 3. Victim logs in. Server associates `abc123` with the victim's account.
402
+ 4. Attacker uses `abc123` to access the victim's authenticated session.
403
+
404
+ **Why developers do it:**
405
+ Session regeneration is not the default in many frameworks. Developers are unaware that the session ID is security-critical and must change at authentication boundaries.
406
+
407
+ **What goes wrong:**
408
+ - **Schneider Electric EcoStruxure PME:** A session fixation vulnerability allowed attackers to send a crafted link with a predefined session ID. When the victim logged in via that link, the attacker used the same session ID to hijack the authenticated session -- without needing credentials.
409
+ - This pattern has been found in routers, industrial control systems, and enterprise applications where session management is handled manually.
410
+
411
+ **The fix:**
412
+ ```python
413
+ @app.route('/login', methods=['POST'])
414
+ def login():
415
+ user = authenticate(request.form['username'], request.form['password'])
416
+ if user:
417
+ # Regenerate session ID on authentication state change
418
+ session.regenerate() # Framework-specific: Flask-Session, Django, etc.
419
+ session['user_id'] = user.id
420
+ return redirect('/dashboard')
421
+ ```
422
+
423
+ In Django this is automatic. In Express.js: `req.session.regenerate(callback)`. In Spring Security: `sessionFixationProtection="newSession"`.
424
+
425
+ **Detection rule:**
426
+ Flag login handlers that set session/user data without calling session regeneration. Search for `session['user']` or `req.session.user =` assignments not preceded by `session.regenerate()`, `session.invalidate()`, or equivalent.
427
+
428
+ ---
429
+
430
+ ### AP-09: Missing CSRF Protection
431
+
432
+ **Also known as:** Cross-site request forgery, session riding, one-click attack
433
+ **Frequency:** Common
434
+ **Severity:** High
435
+ **Detection difficulty:** Easy
436
+
437
+ **What it looks like:**
438
+ ```html
439
+ <!-- Attacker's page at evil.com -->
440
+ <form action="https://bank.com/transfer" method="POST" id="exploit">
441
+ <input type="hidden" name="to" value="attacker-account" />
442
+ <input type="hidden" name="amount" value="10000" />
443
+ </form>
444
+ <script>document.getElementById('exploit').submit();</script>
445
+ ```
446
+
447
+ ```python
448
+ # Server has no CSRF token validation
449
+ @app.route('/transfer', methods=['POST'])
450
+ @login_required
451
+ def transfer():
452
+ # The browser sends the session cookie automatically
453
+ # No CSRF token is checked
454
+ process_transfer(request.form['to'], request.form['amount'])
455
+ ```
456
+
457
+ **Why developers do it:**
458
+ API-first architectures using JSON and bearer tokens are naturally CSRF-resistant (the browser does not auto-attach bearer tokens). But when authentication uses cookies -- which is common and recommended for XSS protection (see AP-03) -- CSRF protection becomes mandatory. Developers switching from bearer-token to cookie-based auth forget this trade-off.
459
+
460
+ **What goes wrong:**
461
+ - **Tesla (2017):** A CSRF vulnerability in Tesla's web interface allowed forged requests to issue remote vehicle commands -- unlock doors, control climate systems. If a Tesla owner visited a malicious site while logged in, the attacker could silently control the vehicle.
462
+ - **Ubiquiti routers:** CSRF in the router admin interface allowed attackers to change DNS settings or push firmware if a user visited a malicious site while authenticated (which was almost always, since users rarely log out of router panels).
463
+
464
+ **The fix:**
465
+ ```python
466
+ # Use framework CSRF middleware
467
+ from flask_wtf.csrf import CSRFProtect
468
+ csrf = CSRFProtect(app)
469
+
470
+ # In templates
471
+ <form method="post">
472
+ {{ csrf_token() }}
473
+ ...
474
+ </form>
475
+
476
+ # For AJAX with cookies, use SameSite attribute
477
+ # Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Strict
478
+ ```
479
+
480
+ **Detection rule:**
481
+ Flag POST/PUT/DELETE endpoints that use cookie authentication but do not validate a CSRF token. Flag cookie `Set-Cookie` headers missing `SameSite=Strict` or `SameSite=Lax`. Flag forms without a hidden CSRF token field.
482
+
483
+ ---
484
+
485
+ ### AP-10: OAuth Misconfiguration
486
+
487
+ **Also known as:** Open redirect in OAuth, redirect URI bypass, state parameter omission
488
+ **Frequency:** Common
489
+ **Severity:** Critical
490
+ **Detection difficulty:** Moderate
491
+
492
+ **What it looks like:**
493
+ ```python
494
+ # Accepting any redirect_uri
495
+ @app.route('/oauth/authorize')
496
+ def authorize():
497
+ redirect_uri = request.args.get('redirect_uri')
498
+ # No validation against a whitelist
499
+ code = generate_auth_code(request.user)
500
+ return redirect(f"{redirect_uri}?code={code}")
501
+
502
+ # Missing state parameter
503
+ def start_oauth():
504
+ return redirect(
505
+ f"https://provider.com/auth?client_id={CLIENT_ID}"
506
+ f"&redirect_uri={CALLBACK_URL}"
507
+ # No &state=<random> parameter -> CSRF on the OAuth flow
508
+ )
509
+ ```
510
+
511
+ **Why developers do it:**
512
+ OAuth 2.0 is a framework, not a protocol -- it has many moving parts and the spec is permissive about implementation details. Developers follow "happy path" tutorials that skip security parameters. Wildcard redirect URIs are convenient during development.
513
+
514
+ **What goes wrong:**
515
+ - **Booking.com (2022):** Salt Labs found flaws in Booking.com's OAuth redirect URI handling that allowed attackers to redirect users to attacker-controlled domains and steal authorization codes.
516
+ - **Expo platform:** Researchers manipulated the `returnURL` parameter to redirect OAuth tokens to attacker-controlled domains, enabling account takeover on any app using Expo's OAuth.
517
+ - **Keycloak (CVE-2023-6927):** Bypass of redirect URI validation via wildcard matching, enabling authorization code theft in all Keycloak versions < 23.0.4.
518
+ - **Spring Security OAuth (CVE-2019-3778):** Open redirect vulnerability in versions prior to 2.3.6 leaking authorization codes.
519
+ - **Gradio (CVE-2026-28415):** Open redirect in OAuth login/logout endpoints in versions < 6.6.0.
520
+
521
+ **The fix:**
522
+ ```python
523
+ ALLOWED_REDIRECT_URIS = {
524
+ "https://app.example.com/callback",
525
+ "https://app.example.com/oauth/callback",
526
+ }
527
+
528
+ @app.route('/oauth/authorize')
529
+ def authorize():
530
+ redirect_uri = request.args.get('redirect_uri')
531
+ if redirect_uri not in ALLOWED_REDIRECT_URIS:
532
+ abort(400, "Invalid redirect_uri")
533
+
534
+ state = generate_csrf_state()
535
+ session['oauth_state'] = state
536
+ # ... include state in the flow and validate on callback
537
+ ```
538
+
539
+ Always use exact-match redirect URI validation (no wildcards, no subdomain matching). Always validate the `state` parameter on callback. Use PKCE for public clients.
540
+
541
+ **Detection rule:**
542
+ Flag OAuth authorize endpoints that do not validate `redirect_uri` against a whitelist. Flag OAuth flows missing the `state` parameter. Flag redirect URI registrations using wildcards (`*`) or broad patterns.
543
+
544
+ ---
545
+
546
+ ### AP-11: Sequential and Guessable IDs for Access Control
547
+
548
+ **Also known as:** Enumerable identifiers, auto-increment exposure, predictable resource IDs
549
+ **Frequency:** Very Common
550
+ **Severity:** High
551
+ **Detection difficulty:** Easy
552
+
553
+ **What it looks like:**
554
+ ```
555
+ GET /api/users/1001/profile
556
+ GET /api/users/1002/profile # Just increment
557
+ GET /api/users/1003/profile # Keep going
558
+
559
+ GET /api/documents/00000001
560
+ GET /api/documents/00000002 # Enumerate all documents
561
+ ```
562
+
563
+ ```python
564
+ # Database uses auto-increment primary keys exposed directly in URLs
565
+ class Invoice(db.Model):
566
+ id = db.Column(db.Integer, primary_key=True, autoincrement=True)
567
+ ```
568
+
569
+ **Why developers do it:**
570
+ Auto-increment integer IDs are the default in every ORM and database. They are simple, indexable, and human-readable. Developers expose them in URLs because REST conventions encourage resource identification via URL path.
571
+
572
+ **What goes wrong:**
573
+ Sequential IDs make IDOR exploitation trivial. An attacker does not need to guess -- they just iterate. Combined with missing authorization checks (AP-05), this pattern enables automated scraping of entire databases. The Snapchat IDOR breach involved sequential user ID manipulation in API requests. Financial applications have been compromised via sequential transaction reference numbers.
574
+
575
+ Even with proper authorization, sequential IDs leak information: total user count, creation rate, and relative account age.
576
+
577
+ **The fix:**
578
+ ```python
579
+ import uuid
580
+
581
+ class Invoice(db.Model):
582
+ id = db.Column(db.String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
583
+ # External ID: 550e8400-e29b-41d4-a716-446655440000
584
+ # Non-sequential, non-guessable
585
+
586
+ # For higher performance, use UUIDv7 (time-ordered but not sequential) or ULID
587
+ # Still enforce authorization checks -- UUIDs are defense-in-depth, not access control
588
+ ```
589
+
590
+ **Detection rule:**
591
+ Flag API endpoints that expose integer IDs in URL paths where the backing model uses auto-increment. Flag endpoints accepting integer path parameters that lack authorization middleware.
592
+
593
+ ---
594
+
595
+ ### AP-12: Not Implementing Account Lockout
596
+
597
+ **Also known as:** Unlimited login attempts, missing brute-force protection, no rate limiting on login
598
+ **Frequency:** Common
599
+ **Severity:** High
600
+ **Detection difficulty:** Easy
601
+
602
+ **What it looks like:**
603
+ ```python
604
+ @app.route('/login', methods=['POST'])
605
+ def login():
606
+ user = User.query.filter_by(email=request.form['email']).first()
607
+ if user and user.check_password(request.form['password']):
608
+ login_user(user)
609
+ return redirect('/dashboard')
610
+ return render_template('login.html', error='Invalid credentials')
611
+ # No attempt counting. No delay. No lockout. No CAPTCHA.
612
+ ```
613
+
614
+ **Why developers do it:**
615
+ Account lockout adds complexity (lockout duration, unlock mechanisms, user communication). Developers fear locking out legitimate users. Rate limiting requires infrastructure (Redis, middleware). In early-stage products, security features are deprioritized.
616
+
617
+ **What goes wrong:**
618
+ Without rate limiting or lockout, attackers can attempt thousands of passwords per second via credential stuffing or brute force. OWASP API Security Top 10 (API2:2023) identifies Broken Authentication as a top risk, specifically noting that "anti-brute force mechanisms should be implemented and should be stricter than regular rate limiting." HackerOne reports document missing rate limits on login endpoints at Weblate and Acronis, among others.
619
+
620
+ **The fix:**
621
+ ```python
622
+ from flask_limiter import Limiter
623
+
624
+ limiter = Limiter(app, key_func=get_remote_address)
625
+
626
+ @app.route('/login', methods=['POST'])
627
+ @limiter.limit("5 per minute") # Rate limit per IP
628
+ def login():
629
+ user = User.query.filter_by(email=request.form['email']).first()
630
+ if user:
631
+ if user.failed_attempts >= 5:
632
+ if user.locked_until and user.locked_until > datetime.utcnow():
633
+ abort(429, "Account temporarily locked")
634
+ user.failed_attempts = 0 # Reset after lockout period
635
+
636
+ if user.check_password(request.form['password']):
637
+ user.failed_attempts = 0
638
+ user.save()
639
+ login_user(user)
640
+ return redirect('/dashboard')
641
+
642
+ user.failed_attempts += 1
643
+ if user.failed_attempts >= 5:
644
+ user.locked_until = datetime.utcnow() + timedelta(minutes=15)
645
+ user.save()
646
+
647
+ # Generic error to prevent username enumeration
648
+ return render_template('login.html', error='Invalid credentials'), 401
649
+ ```
650
+
651
+ **Detection rule:**
652
+ Flag login endpoints that lack rate-limiting middleware. Flag authentication handlers that do not track or check failed attempt counts. Flag absence of rate-limiting libraries in project dependencies.
653
+
654
+ ---
655
+
656
+ ### AP-13: Password Reset Token Reuse
657
+
658
+ **Also known as:** Non-expiring reset tokens, predictable reset tokens, reset link hijacking
659
+ **Frequency:** Occasional
660
+ **Severity:** Critical
661
+ **Detection difficulty:** Moderate
662
+
663
+ **What it looks like:**
664
+ ```python
665
+ def request_password_reset(email):
666
+ user = User.query.filter_by(email=email).first()
667
+ # Token is MD5 of the email -- deterministic, reusable, predictable
668
+ token = hashlib.md5(email.encode()).hexdigest()
669
+ send_email(email, f"https://app.com/reset?token={token}")
670
+
671
+ def reset_password(token, new_password):
672
+ # Token is never invalidated after use
673
+ user = User.query.filter_by(reset_token=token).first()
674
+ user.password = hash_password(new_password)
675
+ user.save()
676
+ # reset_token is still valid -- attacker can use it again
677
+ ```
678
+
679
+ **Why developers do it:**
680
+ Token generation and lifecycle management are treated as an afterthought. Developers generate a token, send it, and check it -- but forget to invalidate it on use, set an expiration, or use a cryptographically random generator.
681
+
682
+ **What goes wrong:**
683
+ - **Vikunja (CVE-2026-28268):** Password reset tokens could be reused indefinitely due to a failure to invalidate tokens upon use and a bug in the token cleanup cron job. An attacker who intercepted a single reset token could perform persistent account takeover at any point in the future.
684
+ - **Mavenlink (HackerOne):** The application generated password reset links based on the `Host` header, allowing attackers to redirect reset links to their domain and steal tokens.
685
+ - Applications using `MD5(email)` or `MD5(timestamp)` as reset tokens allow attackers who understand the generation mechanism to forge valid tokens for any account.
686
+
687
+ **The fix:**
688
+ ```python
689
+ import secrets
690
+ from datetime import datetime, timedelta
691
+
692
+ def request_password_reset(email):
693
+ user = User.query.filter_by(email=email).first()
694
+ if not user:
695
+ return # Don't reveal whether the email exists
696
+
697
+ token = secrets.token_urlsafe(32) # Cryptographically random
698
+ user.reset_token = hash_token(token) # Store hashed, not raw
699
+ user.reset_token_expires = datetime.utcnow() + timedelta(hours=1)
700
+ user.save()
701
+ send_email(email, f"https://app.com/reset?token={token}")
702
+
703
+ def reset_password(token, new_password):
704
+ user = User.query.filter_by(reset_token=hash_token(token)).first()
705
+ if not user or user.reset_token_expires < datetime.utcnow():
706
+ abort(400, "Invalid or expired token")
707
+
708
+ user.password = hash_password(new_password)
709
+ user.reset_token = None # Invalidate immediately
710
+ user.reset_token_expires = None
711
+ user.save()
712
+ ```
713
+
714
+ **Detection rule:**
715
+ Flag password reset handlers that do not nullify the token after use. Flag token generation using `md5`, `sha1`, or `time.time()` as sole entropy sources. Flag reset tokens without expiration timestamps.
716
+
717
+ ---
718
+
719
+ ### AP-14: Missing Multi-Factor Authentication for Sensitive Operations
720
+
721
+ **Also known as:** No MFA, single-factor-only for critical actions, missing step-up authentication
722
+ **Frequency:** Very Common
723
+ **Severity:** High
724
+ **Detection difficulty:** Easy
725
+
726
+ **What it looks like:**
727
+ ```python
728
+ @app.route('/api/account/change-email', methods=['POST'])
729
+ @login_required
730
+ def change_email():
731
+ # Changes email (and thus password reset target) with no re-authentication
732
+ current_user.email = request.json['new_email']
733
+ current_user.save()
734
+ return jsonify({"status": "ok"})
735
+
736
+ @app.route('/api/transfers', methods=['POST'])
737
+ @login_required
738
+ def wire_transfer():
739
+ # Initiates a wire transfer with only a session cookie
740
+ process_wire(request.json)
741
+ return jsonify({"status": "ok"})
742
+ ```
743
+
744
+ **Why developers do it:**
745
+ MFA adds user friction. Product teams push back on requiring re-authentication for "low-friction" user experience. Step-up authentication is architecturally complex -- it requires storing auth levels in the session and prompting conditionally.
746
+
747
+ **What goes wrong:**
748
+ - **Facebook (2018):** Attackers exploited the "View As" feature to steal access tokens for 50 million accounts. Without step-up authentication on sensitive operations, stolen tokens had full account access.
749
+ - **CircleCI (2023):** Malware on an engineer's laptop stole a valid, 2FA-backed SSO session. The session granted full access without re-authentication for sensitive operations.
750
+
751
+ Relying solely on password-based authentication means threat actors need only one credential -- which can be phished, purchased on the dark web, brute-forced, or guessed.
752
+
753
+ **The fix:**
754
+ ```python
755
+ def require_recent_auth(max_age_seconds=300):
756
+ """Require re-authentication for sensitive operations."""
757
+ def decorator(f):
758
+ @wraps(f)
759
+ def wrapper(*args, **kwargs):
760
+ last_auth = session.get('last_auth_time')
761
+ if not last_auth or (time.time() - last_auth) > max_age_seconds:
762
+ return jsonify({"error": "Re-authentication required"}), 403
763
+ return f(*args, **kwargs)
764
+ return wrapper
765
+ return decorator
766
+
767
+ @app.route('/api/account/change-email', methods=['POST'])
768
+ @login_required
769
+ @require_recent_auth(max_age_seconds=300) # Must have re-authed within 5 min
770
+ def change_email():
771
+ current_user.email = request.json['new_email']
772
+ current_user.save()
773
+ ```
774
+
775
+ **Detection rule:**
776
+ Flag endpoints for password change, email change, phone change, MFA change, fund transfer, and API key generation that lack step-up authentication middleware.
777
+
778
+ ---
779
+
780
+ ### AP-15: Excessive Token Lifetimes
781
+
782
+ **Also known as:** Long-lived JWTs, tokens that never expire, week-long sessions
783
+ **Frequency:** Common
784
+ **Severity:** High
785
+ **Detection difficulty:** Easy
786
+
787
+ **What it looks like:**
788
+ ```python
789
+ # Token valid for 30 days
790
+ token = jwt.encode({
791
+ "sub": user.id,
792
+ "exp": datetime.utcnow() + timedelta(days=30)
793
+ }, SECRET_KEY, algorithm="HS256")
794
+
795
+ # Or worse: no expiration at all
796
+ token = jwt.encode({"sub": user.id}, SECRET_KEY, algorithm="HS256")
797
+ ```
798
+
799
+ **Why developers do it:**
800
+ Long-lived tokens reduce the frequency of re-authentication, which improves user experience. Implementing token refresh flows adds complexity (refresh token storage, rotation, revocation). Developers default to "long enough that nobody complains."
801
+
802
+ **What goes wrong:**
803
+ A stolen token with a 30-day lifetime gives an attacker 30 days of access. With JWTs, there is no server-side revocation by default -- the token is valid until it expires. Organizations with proper token lifecycle management experience 47% fewer credential-compromise incidents according to SailPoint research.
804
+
805
+ Azure/Entra ID access tokens cannot be revoked once issued -- the only mitigation is short lifetimes. This architectural constraint has been exploited by attackers who steal tokens and maintain access even after the user changes their password.
806
+
807
+ **The fix:**
808
+ ```python
809
+ # Short-lived access token + long-lived refresh token
810
+ access_token = jwt.encode({
811
+ "sub": user.id,
812
+ "exp": datetime.utcnow() + timedelta(minutes=15), # 15-minute access token
813
+ "type": "access"
814
+ }, SECRET_KEY, algorithm="HS256")
815
+
816
+ refresh_token = secrets.token_urlsafe(32)
817
+ # Store refresh token server-side (database), associated with the user
818
+ # Refresh tokens can be revoked by deleting the database entry
819
+ store_refresh_token(user.id, refresh_token, expires=timedelta(days=7))
820
+ ```
821
+
822
+ **Detection rule:**
823
+ Flag JWT creation where `exp` exceeds 1 hour for access tokens. Flag JWTs without an `exp` claim. Flag refresh tokens with lifetimes exceeding 30 days.
824
+
825
+ ---
826
+
827
+ ### AP-16: Not Revoking Tokens on Password Change
828
+
829
+ **Also known as:** Stale sessions after credential change, zombie tokens, ghost access
830
+ **Frequency:** Common
831
+ **Severity:** High
832
+ **Detection difficulty:** Moderate
833
+
834
+ **What it looks like:**
835
+ ```python
836
+ @app.route('/api/change-password', methods=['POST'])
837
+ @login_required
838
+ def change_password():
839
+ current_user.password = hash_password(request.json['new_password'])
840
+ current_user.save()
841
+ return jsonify({"status": "password changed"})
842
+ # All existing tokens, sessions, and refresh tokens remain valid
843
+ # If the password was changed because the account was compromised,
844
+ # the attacker still has access via existing tokens
845
+ ```
846
+
847
+ **Why developers do it:**
848
+ Session invalidation requires tracking all active sessions/tokens per user. With stateless JWTs, server-side revocation requires maintaining a blocklist or version counter -- which undermines the "stateless" advantage. Developers implement the password change and consider it done.
849
+
850
+ **What goes wrong:**
851
+ - **Cloudflare (2023):** After the Okta breach, Cloudflare was attacked using tokens and service account credentials that had not been rotated post-compromise. The nation-state attacker maintained access because credentials from the prior breach were still valid.
852
+ - **General pattern:** Google explicitly revokes OAuth 2.0 tokens when a user's password changes (documented as a security feature). Many applications do not implement this, leaving a window where compromised sessions persist indefinitely.
853
+
854
+ A user who changes their password because they suspect compromise expects all other sessions to terminate. When they do not, the password change is security theater.
855
+
856
+ **The fix:**
857
+ ```python
858
+ @app.route('/api/change-password', methods=['POST'])
859
+ @login_required
860
+ def change_password():
861
+ current_user.password = hash_password(request.json['new_password'])
862
+ current_user.token_version += 1 # Increment token version
863
+ current_user.save()
864
+
865
+ # Invalidate all refresh tokens
866
+ RefreshToken.query.filter_by(user_id=current_user.id).delete()
867
+
868
+ # Invalidate all sessions
869
+ Session.query.filter_by(user_id=current_user.id).delete()
870
+ db.session.commit()
871
+
872
+ # Issue a new token for the current session only
873
+ new_token = generate_token(current_user)
874
+ return jsonify({"status": "password changed", "token": new_token})
875
+
876
+ # In token verification, check token_version
877
+ def verify_token(token):
878
+ payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
879
+ user = User.query.get(payload['sub'])
880
+ if user.token_version != payload.get('tv'):
881
+ raise InvalidTokenError("Token revoked")
882
+ return payload
883
+ ```
884
+
885
+ **Detection rule:**
886
+ Flag password-change handlers that do not invalidate sessions, refresh tokens, or increment a token version counter. Flag JWT verification logic that does not check a per-user version or revocation list.
887
+
888
+ ---
889
+
890
+ ### AP-17: Role-Based Access Without Resource-Level Checks
891
+
892
+ **Also known as:** Coarse-grained authorization, vertical-only access control, role-is-enough fallacy
893
+ **Frequency:** Common
894
+ **Severity:** High
895
+ **Detection difficulty:** Hard
896
+
897
+ **What it looks like:**
898
+ ```python
899
+ @app.route('/api/projects/<project_id>/settings', methods=['PUT'])
900
+ @login_required
901
+ @require_role('manager') # Checks that user is a manager...
902
+ def update_project_settings(project_id):
903
+ # ...but not that they manage THIS project
904
+ project = Project.query.get(project_id)
905
+ project.settings = request.json
906
+ project.save()
907
+ return jsonify(project.settings)
908
+ ```
909
+
910
+ **Why developers do it:**
911
+ Role-based access control (RBAC) is the most commonly taught authorization model. Frameworks make role checks easy. The mental model is "managers can manage things" rather than "this manager can manage these specific things." Resource-level (or attribute-based) access control requires more data in the authorization decision.
912
+
913
+ **What goes wrong:**
914
+ A manager in Team A can modify settings for Team B's projects. This is horizontal privilege escalation within a role. The fix for AP-05 (IDOR) addresses unauthenticated/unauthorized access, but this pattern is subtler: the user has the right role but not the right scope. This is especially dangerous in multi-tenant SaaS applications where a role in Tenant A should grant zero access to Tenant B.
915
+
916
+ **The fix:**
917
+ ```python
918
+ @app.route('/api/projects/<project_id>/settings', methods=['PUT'])
919
+ @login_required
920
+ @require_role('manager')
921
+ def update_project_settings(project_id):
922
+ # Check resource-level access, not just role
923
+ project = Project.query.get(project_id)
924
+ if not project:
925
+ abort(404)
926
+ if current_user.id not in project.manager_ids:
927
+ abort(403)
928
+ project.settings = request.json
929
+ project.save()
930
+ return jsonify(project.settings)
931
+ ```
932
+
933
+ For complex scenarios, use a policy engine (OPA, Casbin, Cedar) that evaluates `(subject, action, resource)` tuples rather than `(subject, role)` alone.
934
+
935
+ **Detection rule:**
936
+ Flag endpoints that call role-check middleware but do not subsequently verify the relationship between the authenticated user and the specific resource being accessed. Flag authorization logic that does not reference the resource ID.
937
+
938
+ ---
939
+
940
+ ### AP-18: Trusting Client-Provided User Identity
941
+
942
+ **Also known as:** User ID in request body, role from the client, self-declared identity
943
+ **Frequency:** Common
944
+ **Severity:** Critical
945
+ **Detection difficulty:** Moderate
946
+
947
+ **What it looks like:**
948
+ ```python
949
+ @app.route('/api/orders', methods=['POST'])
950
+ def create_order():
951
+ # User ID comes from the request body, not from the authenticated session
952
+ order = Order(
953
+ user_id=request.json['user_id'], # Attacker sets any user_id
954
+ items=request.json['items']
955
+ )
956
+ db.session.add(order)
957
+ db.session.commit()
958
+ ```
959
+
960
+ ```javascript
961
+ // Client sends role claim in request
962
+ fetch('/api/admin/action', {
963
+ method: 'POST',
964
+ body: JSON.stringify({ userId: 42, role: 'admin', action: 'delete_user' })
965
+ });
966
+ ```
967
+
968
+ **Why developers do it:**
969
+ During development, it is convenient to pass the user ID from the client. The developer intends to "fix it later" when auth is fully implemented. In some architectures, the user context is passed between frontend and backend without server-side verification.
970
+
971
+ **What goes wrong:**
972
+ Any user can impersonate any other user by changing the user_id field. CWE-602 and CWE-603 explicitly categorize this as a vulnerability. The OWASP Authorization Cheat Sheet states: "Access control checks must be performed server-side. Authorization and authentication controls must be re-enforced on the server-side."
973
+
974
+ **The fix:**
975
+ ```python
976
+ @app.route('/api/orders', methods=['POST'])
977
+ @login_required
978
+ def create_order():
979
+ order = Order(
980
+ user_id=current_user.id, # Always from the server session, never from the request
981
+ items=request.json['items']
982
+ )
983
+ db.session.add(order)
984
+ db.session.commit()
985
+ ```
986
+
987
+ **Detection rule:**
988
+ Flag request body fields named `user_id`, `userId`, `user`, `role`, or `permissions` that are used in authorization decisions. Flag any endpoint where `request.json['user_id']` or `request.body.userId` is assigned to a model without comparison to the session user.
989
+
990
+ ---
991
+
992
+ ### AP-19: Missing Rate Limiting on Auth Endpoints
993
+
994
+ **Also known as:** Unrestricted authentication, no throttling, open credential stuffing target
995
+ **Frequency:** Very Common
996
+ **Severity:** High
997
+ **Detection difficulty:** Easy
998
+
999
+ **What it looks like:**
1000
+ ```python
1001
+ # All auth endpoints wide open
1002
+ @app.route('/api/login', methods=['POST'])
1003
+ def login():
1004
+ return attempt_login(request.json)
1005
+
1006
+ @app.route('/api/register', methods=['POST'])
1007
+ def register():
1008
+ return create_account(request.json)
1009
+
1010
+ @app.route('/api/forgot-password', methods=['POST'])
1011
+ def forgot_password():
1012
+ return send_reset_email(request.json['email'])
1013
+
1014
+ @app.route('/api/verify-otp', methods=['POST'])
1015
+ def verify_otp():
1016
+ return check_otp(request.json)
1017
+ # Attacker can brute-force 6-digit OTP (1 million combinations) in minutes
1018
+ ```
1019
+
1020
+ **Why developers do it:**
1021
+ Rate limiting is infrastructure, not application logic. Developers expect it to be handled by the API gateway, load balancer, or CDN -- but nobody configures it. Application-level rate limiting requires Redis or similar state storage, which adds deployment complexity.
1022
+
1023
+ **What goes wrong:**
1024
+ OWASP API Security Top 10 (API2:2023 Broken Authentication) explicitly calls out that "anti-brute force mechanisms should be implemented to mitigate credential stuffing, dictionary attacks, and brute force attacks on authentication endpoints." Without rate limiting:
1025
+ - Login endpoints enable credential stuffing at scale.
1026
+ - Password reset endpoints enable token enumeration.
1027
+ - OTP endpoints enable brute-force bypass of MFA (a 6-digit OTP has only 1 million combinations).
1028
+ - Registration endpoints enable mass account creation for spam.
1029
+
1030
+ **The fix:**
1031
+ ```python
1032
+ from flask_limiter import Limiter
1033
+
1034
+ limiter = Limiter(app, key_func=get_remote_address, storage_uri="redis://localhost:6379")
1035
+
1036
+ @app.route('/api/login', methods=['POST'])
1037
+ @limiter.limit("5/minute", key_func=lambda: request.json.get('email', get_remote_address()))
1038
+ def login():
1039
+ return attempt_login(request.json)
1040
+
1041
+ @app.route('/api/forgot-password', methods=['POST'])
1042
+ @limiter.limit("3/hour")
1043
+ def forgot_password():
1044
+ return send_reset_email(request.json['email'])
1045
+
1046
+ @app.route('/api/verify-otp', methods=['POST'])
1047
+ @limiter.limit("5/minute")
1048
+ def verify_otp():
1049
+ return check_otp(request.json)
1050
+ ```
1051
+
1052
+ Rate limit by both IP and account identifier. Use exponential backoff on repeated failures.
1053
+
1054
+ **Detection rule:**
1055
+ Flag authentication endpoints (login, register, forgot-password, verify-otp, verify-mfa) that lack rate-limiting middleware. Flag absence of rate-limiting infrastructure (Redis, Memcached) in deployment configs when cookie/session auth is used.
1056
+
1057
+ ---
1058
+
1059
+ ### AP-20: Logging Sensitive Authentication Data
1060
+
1061
+ **Also known as:** Credentials in logs, PII leakage to observability, debug logging in production
1062
+ **Frequency:** Common
1063
+ **Severity:** High
1064
+ **Detection difficulty:** Moderate
1065
+
1066
+ **What it looks like:**
1067
+ ```python
1068
+ @app.route('/login', methods=['POST'])
1069
+ def login():
1070
+ logger.info(f"Login attempt: email={request.form['email']}, password={request.form['password']}")
1071
+ # Password is now in plaintext in log files, shipped to Splunk/ELK/CloudWatch
1072
+ # Accessible to ops team, log aggregation service, anyone with log access
1073
+
1074
+ user = authenticate(request.form['email'], request.form['password'])
1075
+ if user:
1076
+ token = generate_token(user)
1077
+ logger.info(f"Login success: user={user.id}, token={token}")
1078
+ # Token is now in logs -- anyone with log access can impersonate this user
1079
+ ```
1080
+
1081
+ **Why developers do it:**
1082
+ Debug logging during development includes everything for easy troubleshooting. When code moves to production, nobody audits log statements. Structured logging libraries that auto-serialize request objects can inadvertently capture sensitive fields.
1083
+
1084
+ **What goes wrong:**
1085
+ - **Twitter (2018):** Disclosed that a bug caused user passwords to be written to an internal log in plaintext. All 330 million users were asked to change their passwords.
1086
+ - **Facebook (2019):** Discovered that hundreds of millions of passwords were stored in readable format in internal data storage systems. Engineers had logged passwords during authentication.
1087
+
1088
+ OWASP's logging guidelines explicitly prohibit logging: authentication passwords, session identification values, access tokens, database connection strings, encryption keys, and bank account or payment card holder data.
1089
+
1090
+ **The fix:**
1091
+ ```python
1092
+ import re
1093
+
1094
+ SENSITIVE_FIELDS = {'password', 'token', 'secret', 'api_key', 'authorization', 'cookie', 'ssn', 'credit_card'}
1095
+
1096
+ class SanitizingFormatter(logging.Formatter):
1097
+ def format(self, record):
1098
+ msg = super().format(record)
1099
+ for field in SENSITIVE_FIELDS:
1100
+ msg = re.sub(
1101
+ rf'({field}\s*[=:]\s*)([^\s,;]+)',
1102
+ rf'\1[REDACTED]',
1103
+ msg,
1104
+ flags=re.IGNORECASE
1105
+ )
1106
+ return msg
1107
+
1108
+ # Application logging
1109
+ logger.info(f"Login attempt: email={request.form['email']}")
1110
+ # Never log: passwords, tokens, session IDs, API keys
1111
+ ```
1112
+
1113
+ **Detection rule:**
1114
+ Grep log statements for variables named `password`, `token`, `secret`, `api_key`, `authorization`, `session_id`, `credit_card`. Flag `logger.*` calls that interpolate request body or header fields without a sanitization filter. Flag structured loggers configured to auto-serialize entire request objects.
1115
+
1116
+ ---
1117
+
1118
+ ## Root Cause Analysis
1119
+
1120
+ | Root Cause | Anti-Patterns | Frequency |
1121
+ |---|---|---|
1122
+ | **Treating auth as a one-time setup** | AP-05, AP-06, AP-17 | Very High |
1123
+ | **Prioritizing speed over security** | AP-02, AP-07, AP-12, AP-18 | Very High |
1124
+ | **Misunderstanding crypto primitives** | AP-01, AP-02, AP-04 | High |
1125
+ | **Following outdated tutorials** | AP-03, AP-04, AP-10 | High |
1126
+ | **Missing lifecycle management** | AP-13, AP-15, AP-16 | High |
1127
+ | **Assuming the client is trusted** | AP-06, AP-09, AP-18 | High |
1128
+ | **Security features deferred to "later"** | AP-07, AP-12, AP-14, AP-19 | Very High |
1129
+ | **Confusing authentication with authorization** | AP-05, AP-06, AP-17 | High |
1130
+ | **Insufficient logging hygiene** | AP-20 | Medium |
1131
+ | **Framework defaults accepted uncritically** | AP-08, AP-11 | Medium |
1132
+
1133
+ ---
1134
+
1135
+ ## Self-Check Questions
1136
+
1137
+ Use these questions during code review or architecture review to surface auth anti-patterns:
1138
+
1139
+ 1. **Password storage:** Are passwords hashed with bcrypt, scrypt, or Argon2id? Can you confirm MD5/SHA1/SHA256 are NOT used for password hashing?
1140
+
1141
+ 2. **Token storage:** Where are authentication tokens stored on the client? If localStorage, what is the XSS mitigation strategy? Are cookies marked HttpOnly, Secure, and SameSite?
1142
+
1143
+ 3. **JWT validation:** Does the server explicitly whitelist allowed JWT algorithms? Is `"none"` rejected? Are `exp`, `aud`, and `iss` claims validated?
1144
+
1145
+ 4. **Resource-level authorization:** For every endpoint that takes a resource ID, does the query/check verify the authenticated user has access to that specific resource (not just the right role)?
1146
+
1147
+ 5. **Credential rotation:** What happens to existing sessions and tokens when a user changes their password? Are they invalidated?
1148
+
1149
+ 6. **Secret management:** Are there any API keys, database passwords, or signing secrets in the source code or configuration files checked into version control? Is a pre-commit scanner configured?
1150
+
1151
+ 7. **Rate limiting:** What happens if an attacker sends 10,000 login requests in one minute? What about 10,000 password reset requests? What about 1 million OTP verification attempts?
1152
+
1153
+ 8. **Session lifecycle:** Is the session ID regenerated after login? Does the session expire? Can a user see and terminate other active sessions?
1154
+
1155
+ 9. **OAuth flow:** Are redirect URIs validated against an exact-match whitelist? Is the `state` parameter generated and validated? Is PKCE used for public clients?
1156
+
1157
+ 10. **Reset tokens:** Are password reset tokens cryptographically random, single-use, time-limited, and stored hashed? Can a used token be replayed?
1158
+
1159
+ 11. **MFA coverage:** Which sensitive operations require re-authentication or step-up MFA? Is MFA enforced for password change, email change, API key creation, and fund transfers?
1160
+
1161
+ 12. **Logging:** Do log statements capture passwords, tokens, session IDs, or API keys? Are request objects auto-serialized without field-level filtering?
1162
+
1163
+ 13. **Authorization enforcement:** For every client-side route guard or UI visibility check, is there a corresponding server-side authorization check on the API endpoint?
1164
+
1165
+ 14. **Token lifetime:** What is the access token lifetime? Is it under 1 hour? Are refresh tokens stored server-side and revocable?
1166
+
1167
+ ---
1168
+
1169
+ ## Code Smell Quick Reference
1170
+
1171
+ | Code Smell | Likely Anti-Pattern | Severity |
1172
+ |---|---|---|
1173
+ | `hashlib.md5(password)` or `hashlib.sha1(password)` | AP-02: Weak password hashing | Critical |
1174
+ | `localStorage.setItem('token', ...)` | AP-03: XSS-accessible tokens | High |
1175
+ | `jwt.decode(token, verify=False)` | AP-04: Unverified JWT | Critical |
1176
+ | `jwt.decode(token, algorithms=None)` | AP-04: Algorithm confusion risk | Critical |
1177
+ | `db.query(Model).get(request_param_id)` without user filter | AP-05: IDOR | Critical |
1178
+ | API route with `@login_required` but no role/resource check | AP-06/AP-17: Missing authorization | High |
1179
+ | String literal matching `AKIA*`, `sk_live_*`, `ghp_*` | AP-07: Hardcoded credentials | Critical |
1180
+ | `session['user'] = ...` without `session.regenerate()` | AP-08: Session fixation | High |
1181
+ | POST handler with cookie auth and no CSRF token check | AP-09: Missing CSRF protection | High |
1182
+ | `redirect_uri` accepted from query params without whitelist validation | AP-10: OAuth open redirect | Critical |
1183
+ | Auto-increment integer IDs in API URLs | AP-11: Enumerable identifiers | High |
1184
+ | Login handler without attempt counting or rate-limit decorator | AP-12/AP-19: No brute-force protection | High |
1185
+ | `hashlib.md5(email)` as reset token | AP-13: Predictable reset token | Critical |
1186
+ | Password change endpoint without session invalidation | AP-16: Zombie tokens | High |
1187
+ | `request.json['user_id']` used in data model assignment | AP-18: Trusting client identity | Critical |
1188
+ | `logger.info(f"...password={password}...")` | AP-20: Credentials in logs | High |
1189
+ | `timedelta(days=30)` in JWT `exp` claim | AP-15: Excessive token lifetime | High |
1190
+ | `jwt.encode({...}, SECRET)` without `exp` claim | AP-15: Non-expiring token | High |
1191
+ | Sensitive endpoint without `@require_mfa` or re-auth check | AP-14: Missing step-up auth | High |
1192
+
1193
+ ---
1194
+
1195
+ *Researched: 2026-03-08 | Sources: OWASP Top 10 (2021, 2025), OWASP API Security Top 10, OWASP Cheat Sheet Series, Auth0 JWT vulnerability disclosure (2015), GitGuardian State of Secrets Sprawl (2023, 2024), Have I Been Pwned (Adobe, LinkedIn, RockYou), Portswigger Web Security Academy, PentesterLab JWT Guide, Salt Labs (Booking.com), CVE-2023-6927 (Keycloak), CVE-2026-28415 (Gradio), CVE-2026-28268 (Vikunja), CVE-2019-3778 (Spring Security OAuth), HackerOne reports (Weblate, Acronis, Mavenlink), CWE-602, CWE-603, Schneier on Security, Cybernews (RockYou2024), BleepingComputer, Wiz AI secrets research*