@devo-bmad-custom/agent-orchestration 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/installer.js +44 -11
- package/package.json +1 -1
- package/src/.agents/skills/audit-website/README.md +20 -20
- package/src/.agents/skills/audit-website/SKILL.md +470 -470
- package/src/.agents/skills/audit-website/agents/openai.yaml +6 -6
- package/src/.agents/skills/audit-website/assets/icon-small.svg +41 -41
- package/src/.agents/skills/audit-website/references/OUTPUT-FORMAT.md +250 -250
- package/src/.agents/skills/clean-code-standards/SKILL.md +104 -104
- package/src/.agents/skills/excalidraw-dark-standard/SKILL.md +281 -281
- package/src/.agents/skills/frontend-responsive-design-standards/SKILL.md +434 -434
- package/src/.agents/skills/java-fundamentals/SKILL.md +116 -116
- package/src/.agents/skills/java-performance/SKILL.md +119 -119
- package/src/.agents/skills/next-best-practices/SKILL.md +153 -153
- package/src/.agents/skills/next-best-practices/async-patterns.md +87 -87
- package/src/.agents/skills/next-best-practices/bundling.md +180 -180
- package/src/.agents/skills/next-best-practices/data-patterns.md +297 -297
- package/src/.agents/skills/next-best-practices/debug-tricks.md +105 -105
- package/src/.agents/skills/next-best-practices/directives.md +73 -73
- package/src/.agents/skills/next-best-practices/error-handling.md +227 -227
- package/src/.agents/skills/next-best-practices/file-conventions.md +140 -140
- package/src/.agents/skills/next-best-practices/font.md +245 -245
- package/src/.agents/skills/next-best-practices/functions.md +108 -108
- package/src/.agents/skills/next-best-practices/hydration-error.md +91 -91
- package/src/.agents/skills/next-best-practices/image.md +173 -173
- package/src/.agents/skills/next-best-practices/metadata.md +301 -301
- package/src/.agents/skills/next-best-practices/parallel-routes.md +287 -287
- package/src/.agents/skills/next-best-practices/route-handlers.md +146 -146
- package/src/.agents/skills/next-best-practices/rsc-boundaries.md +159 -159
- package/src/.agents/skills/next-best-practices/runtime-selection.md +39 -39
- package/src/.agents/skills/next-best-practices/scripts.md +141 -141
- package/src/.agents/skills/next-best-practices/self-hosting.md +371 -371
- package/src/.agents/skills/next-best-practices/suspense-boundaries.md +67 -67
- package/src/.agents/skills/nextjs-app-router-patterns/SKILL.md +537 -537
- package/src/.agents/skills/postgresql-optimization/SKILL.md +404 -404
- package/src/.agents/skills/python-backend/SKILL.md +153 -153
- package/src/.agents/skills/python-fundamentals/SKILL.md +234 -234
- package/src/.agents/skills/python-performance/SKILL.md +404 -404
- package/src/.agents/skills/react-expert/SKILL.md +335 -335
- package/src/.agents/skills/redis-best-practices/SKILL.md +438 -438
- package/src/.agents/skills/security-best-practices/SKILL.md +288 -288
- package/src/.agents/skills/security-review/LICENSE +22 -22
- package/src/.agents/skills/security-review/SKILL.md +312 -312
- package/src/.agents/skills/security-review/infrastructure/docker.md +432 -432
- package/src/.agents/skills/security-review/languages/javascript.md +388 -388
- package/src/.agents/skills/security-review/languages/python.md +363 -363
- package/src/.agents/skills/security-review/references/api-security.md +519 -519
- package/src/.agents/skills/security-review/references/authentication.md +353 -353
- package/src/.agents/skills/security-review/references/authorization.md +372 -372
- package/src/.agents/skills/security-review/references/business-logic.md +443 -443
- package/src/.agents/skills/security-review/references/cryptography.md +329 -329
- package/src/.agents/skills/security-review/references/csrf.md +398 -398
- package/src/.agents/skills/security-review/references/data-protection.md +378 -378
- package/src/.agents/skills/security-review/references/deserialization.md +410 -410
- package/src/.agents/skills/security-review/references/error-handling.md +436 -436
- package/src/.agents/skills/security-review/references/file-security.md +457 -457
- package/src/.agents/skills/security-review/references/injection.md +259 -259
- package/src/.agents/skills/security-review/references/logging.md +433 -433
- package/src/.agents/skills/security-review/references/misconfiguration.md +435 -435
- package/src/.agents/skills/security-review/references/modern-threats.md +475 -475
- package/src/.agents/skills/security-review/references/ssrf.md +415 -415
- package/src/.agents/skills/security-review/references/supply-chain.md +405 -405
- package/src/.agents/skills/security-review/references/xss.md +336 -336
- package/src/.agents/skills/subagent-driven-development/SKILL.md +275 -275
- package/src/.agents/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -26
- package/src/.agents/skills/subagent-driven-development/implementer-prompt.md +113 -113
- package/src/.agents/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -61
- package/src/.agents/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/src/.agents/skills/systematic-debugging/SKILL.md +296 -296
- package/src/.agents/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/src/.agents/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/src/.agents/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/src/.agents/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/src/.agents/skills/systematic-debugging/test-academic.md +14 -14
- package/src/.agents/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/src/.agents/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/src/.agents/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/src/.agents/skills/typescript-best-practices/SKILL.md +373 -373
- package/src/.agents/skills/ui-ux-pro-custom/SKILL.md +348 -348
- package/src/.agents/skills/ui-ux-pro-custom/data/charts.csv +26 -26
- package/src/.agents/skills/ui-ux-pro-custom/data/colors.csv +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/icons.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/SKILL.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/accessibility.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/animation.md +466 -466
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/composition-locals.md +231 -231
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/deprecated-patterns.md +323 -323
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/lists-scrolling.md +400 -400
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/modifiers.md +331 -331
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/navigation.md +416 -416
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/performance.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/side-effects.md +516 -516
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/foundation-source.md +13327 -13327
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/material3-source.md +19097 -19097
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/navigation-source.md +2947 -2947
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/runtime-source.md +11316 -11316
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/ui-source.md +7896 -7896
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/state-management.md +377 -377
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/styles-experimental.md +470 -470
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/theming-material3.md +349 -349
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/view-composition.md +595 -595
- package/src/.agents/skills/ui-ux-pro-custom/data/landing.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/data/mobile-ui-layout.md +654 -654
- package/src/.agents/skills/ui-ux-pro-custom/data/products.csv +96 -96
- package/src/.agents/skills/ui-ux-pro-custom/data/react-performance.csv +45 -45
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/astro.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/flutter.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/html-tailwind.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/jetpack-compose.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nextjs.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxt-ui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxtjs.csv +59 -59
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react-native.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/shadcn.csv +61 -61
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/svelte.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/swiftui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/vue.csv +50 -50
- package/src/.agents/skills/ui-ux-pro-custom/data/styles.csv +68 -68
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/SKILL.md +438 -438
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/references/alarmkit-patterns.md +584 -584
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-clips/SKILL.md +436 -436
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/SKILL.md +489 -489
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/references/appintents-advanced.md +1076 -1076
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/SKILL.md +340 -340
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/privacy-manifest.md +90 -90
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/review-checklists.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-conversion.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-optimization.md +344 -344
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/foundation-models.md +508 -508
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/mlx-swift.md +285 -285
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/references/keychain-biometric.md +211 -211
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/SKILL.md +499 -499
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/references/background-task-patterns.md +390 -390
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/SKILL.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/references/callkit-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/SKILL.md +492 -492
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/references/cloudkit-patterns.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/codable-patterns/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/SKILL.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/references/contacts-patterns.md +409 -409
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/references/ble-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/SKILL.md +388 -388
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/references/motion-patterns.md +405 -405
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/SKILL.md +495 -495
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/references/nfc-patterns.md +420 -420
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/SKILL.md +459 -459
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/references/coreml-swift-integration.md +765 -765
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/SKILL.md +422 -422
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/instruments-guide.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/lldb-patterns.md +298 -298
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/device-integrity/SKILL.md +477 -477
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/SKILL.md +460 -460
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/references/energykit-patterns.md +541 -541
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/references/eventkit-patterns.md +326 -326
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/references/healthkit-patterns.md +602 -602
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/references/matter-commissioning.md +455 -455
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/SKILL.md +301 -301
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/references/a11y-patterns.md +140 -140
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/SKILL.md +418 -418
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/formatstyle-locale.md +627 -627
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/string-catalogs.md +462 -462
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/SKILL.md +441 -441
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/background-websocket.md +862 -862
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/lightweight-clients.md +93 -93
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/network-framework.md +563 -563
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/urlsession-patterns.md +1116 -1116
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/app-review-guidelines.md +174 -174
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/cryptokit-advanced.md +296 -296
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/file-storage-patterns.md +354 -354
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/privacy-manifest.md +117 -117
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/references/live-activity-patterns.md +868 -868
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/corelocation-patterns.md +730 -730
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/mapkit-patterns.md +748 -748
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/metrickit-diagnostics/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/SKILL.md +395 -395
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/references/musickit-patterns.md +363 -363
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/SKILL.md +412 -412
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/references/translation-patterns.md +311 -311
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/SKILL.md +398 -398
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/references/wallet-passes.md +254 -254
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/SKILL.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/paperkit-integration.md +376 -376
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/pencilkit-patterns.md +302 -302
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/SKILL.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/references/permissionkit-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/av-playback.md +701 -701
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/camera-capture.md +774 -774
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/image-loading-caching.md +869 -869
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/photospicker-patterns.md +597 -597
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/notification-patterns.md +677 -677
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/rich-notifications.md +745 -745
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/references/realitykit-patterns.md +480 -480
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/references/shareplay-patterns.md +544 -544
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/speech-recognition/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/SKILL.md +478 -478
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/app-review-guidelines.md +58 -58
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/storekit-advanced.md +755 -755
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/SKILL.md +487 -487
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/references/charts-patterns.md +895 -895
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/SKILL.md +408 -408
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/approachable-concurrency.md +80 -80
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swift-6-2-concurrency.md +233 -233
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swiftui-concurrency.md +187 -187
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/synchronization-primitives.md +341 -341
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/references/swift-patterns-extended.md +505 -505
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/references/testing-patterns.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/SKILL.md +334 -334
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/core-data-coexistence.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-advanced.md +975 -975
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-queries.md +675 -675
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/SKILL.md +481 -481
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/animation-advanced.md +804 -804
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/core-animation-bridge.md +553 -553
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/SKILL.md +450 -450
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/references/gesture-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/SKILL.md +336 -336
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/form.md +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/grids.md +69 -69
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/list.md +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/scrollview.md +147 -147
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/SKILL.md +325 -325
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/references/liquid-glass.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/SKILL.md +262 -262
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/deeplinks.md +207 -207
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/navigationstack.md +177 -177
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/sheets.md +169 -169
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/tabview.md +178 -178
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/SKILL.md +381 -381
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/architecture-patterns.md +486 -486
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/deprecated-migration.md +1097 -1097
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/design-polish.md +780 -780
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/platform-and-sharing.md +696 -696
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +46 -46
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +29 -29
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-hangs-in-your-app.md +33 -33
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-improving-swiftui-performance.md +52 -52
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/SKILL.md +428 -428
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/hosting-migration.md +534 -534
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/representable-recipes.md +1133 -1133
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/SKILL.md +494 -494
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/references/tipkit-patterns.md +782 -782
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/SKILL.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/vision-requests.md +736 -736
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/visionkit-scanner.md +738 -738
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/SKILL.md +410 -410
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/references/weatherkit-patterns.md +567 -567
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/SKILL.md +497 -497
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/references/widgetkit-advanced.md +871 -871
- package/src/.agents/skills/ui-ux-pro-custom/data/typography.csv +57 -57
- package/src/.agents/skills/ui-ux-pro-custom/data/ui-reasoning.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/ux-guidelines.csv +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/web-interface.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/scripts/core.py +253 -253
- package/src/.agents/skills/ui-ux-pro-custom/scripts/design_system.py +1067 -1067
- package/src/.agents/skills/ui-ux-pro-custom/scripts/search.py +114 -114
- package/src/.agents/skills/ux-audit/SKILL.md +150 -150
- package/src/.agents/skills/websocket-engineer/SKILL.md +168 -168
- package/src/.agents/skills/websocket-engineer/references/alternatives.md +391 -391
- package/src/.agents/skills/websocket-engineer/references/patterns.md +400 -400
- package/src/.agents/skills/websocket-engineer/references/protocol.md +195 -195
- package/src/.agents/skills/websocket-engineer/references/scaling.md +333 -333
- package/src/.agents/skills/websocket-engineer/references/security.md +474 -474
- package/src/.agents/skills/writing-skills/SKILL.md +655 -655
- package/src/.agents/skills/writing-skills/anthropic-best-practices.md +1150 -1150
- package/src/.agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -189
- package/src/.agents/skills/writing-skills/graphviz-conventions.dot +171 -171
- package/src/.agents/skills/writing-skills/persuasion-principles.md +187 -187
- package/src/.agents/skills/writing-skills/render-graphs.js +168 -168
- package/src/.agents/skills/writing-skills/testing-skills-with-subagents.md +384 -384
- package/src/.claude/commands/bmad-track-compact.md +1 -1
- package/src/.claude/commands/bmad-track-extended.md +1 -1
- package/src/.claude/commands/bmad-track-large.md +1 -1
- package/src/.claude/commands/bmad-track-medium.md +1 -1
- package/src/.claude/commands/bmad-track-nano.md +1 -1
- package/src/.claude/commands/bmad-track-rv.md +1 -1
- package/src/.claude/commands/bmad-track-small.md +1 -1
- package/src/.claude/commands/master-orchestrator.md +15 -0
- package/src/_memory/config.yaml +11 -11
- package/src/_memory/master-orchestrator-sidecar/instructions.md +85 -32
- package/src/_memory/skills/nimbalyst-tracking/SKILL.md +103 -103
- package/src/_memory/skills/writing-skills/SKILL.md +655 -655
- package/src/bmb/agents/agent-builder.md +59 -59
- package/src/bmb/agents/module-builder.md +60 -60
- package/src/bmb/agents/workflow-builder.md +61 -61
- package/src/bmb/config.yaml +12 -12
- package/src/bmb/module-help.csv +13 -13
- package/src/bmb/workflows/agent/data/agent-architecture.md +258 -258
- package/src/bmb/workflows/agent/data/agent-compilation.md +185 -185
- package/src/bmb/workflows/agent/data/agent-menu-patterns.md +189 -189
- package/src/bmb/workflows/agent/data/agent-metadata.md +133 -133
- package/src/bmb/workflows/agent/data/agent-validation.md +111 -111
- package/src/bmb/workflows/agent/data/brainstorm-context.md +96 -96
- package/src/bmb/workflows/agent/data/communication-presets.csv +61 -61
- package/src/bmb/workflows/agent/data/critical-actions.md +75 -75
- package/src/bmb/workflows/agent/data/persona-properties.md +252 -252
- package/src/bmb/workflows/agent/data/principles-crafting.md +142 -142
- package/src/bmb/workflows/agent/data/reference/module-examples/architect.md +68 -68
- package/src/bmb/workflows/agent/data/reference/with-sidecar/journal-keeper/journal-keeper-sidecar/entries/yy-mm-dd-entry-template.md +16 -16
- package/src/bmb/workflows/agent/data/understanding-agent-types.md +126 -126
- package/src/bmb/workflows/agent/steps-c/step-01-brainstorm.md +129 -129
- package/src/bmb/workflows/agent/steps-c/step-02-discovery.md +170 -170
- package/src/bmb/workflows/agent/steps-c/step-03-sidecar-metadata.md +309 -309
- package/src/bmb/workflows/agent/steps-c/step-04-persona.md +213 -213
- package/src/bmb/workflows/agent/steps-c/step-05-commands-menu.md +179 -179
- package/src/bmb/workflows/agent/steps-c/step-06-activation.md +278 -278
- package/src/bmb/workflows/agent/steps-c/step-07-build-agent.md +316 -316
- package/src/bmb/workflows/agent/steps-c/step-08-celebrate.md +247 -247
- package/src/bmb/workflows/agent/steps-e/e-01-load-existing.md +221 -221
- package/src/bmb/workflows/agent/steps-e/e-02-discover-edits.md +195 -195
- package/src/bmb/workflows/agent/steps-e/e-04-sidecar-metadata.md +126 -126
- package/src/bmb/workflows/agent/steps-e/e-05-persona.md +135 -135
- package/src/bmb/workflows/agent/steps-e/e-06-commands-menu.md +123 -123
- package/src/bmb/workflows/agent/steps-e/e-07-activation.md +124 -124
- package/src/bmb/workflows/agent/steps-e/e-08-edit-agent.md +197 -197
- package/src/bmb/workflows/agent/steps-e/e-09-celebrate.md +155 -155
- package/src/bmb/workflows/agent/steps-v/v-01-load-review.md +137 -137
- package/src/bmb/workflows/agent/steps-v/v-02a-validate-metadata.md +116 -116
- package/src/bmb/workflows/agent/steps-v/v-02b-validate-persona.md +124 -124
- package/src/bmb/workflows/agent/steps-v/v-02c-validate-menu.md +127 -127
- package/src/bmb/workflows/agent/steps-v/v-02d-validate-structure.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-02e-validate-sidecar.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-03-summary.md +104 -104
- package/src/bmb/workflows/agent/templates/agent-plan.template.md +5 -5
- package/src/bmb/workflows/agent/templates/agent-template.md +89 -89
- package/src/bmb/workflows/agent/workflow-create-agent.md +72 -72
- package/src/bmb/workflows/agent/workflow-edit-agent.md +75 -75
- package/src/bmb/workflows/agent/workflow-validate-agent.md +73 -73
- package/src/bmb/workflows/module/data/agent-architecture.md +179 -179
- package/src/bmb/workflows/module/data/agent-spec-template.md +79 -79
- package/src/bmb/workflows/module/data/module-standards.md +263 -263
- package/src/bmb/workflows/module/data/module-yaml-conventions.md +392 -392
- package/src/bmb/workflows/module/module-help-generate.md +254 -254
- package/src/bmb/workflows/module/steps-b/step-01-welcome.md +148 -148
- package/src/bmb/workflows/module/steps-b/step-02-spark.md +141 -141
- package/src/bmb/workflows/module/steps-b/step-03-module-type.md +149 -149
- package/src/bmb/workflows/module/steps-b/step-04-vision.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-05-identity.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-06-users.md +86 -86
- package/src/bmb/workflows/module/steps-b/step-07-value.md +76 -76
- package/src/bmb/workflows/module/steps-b/step-08-agents.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-09-workflows.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-10-tools.md +91 -91
- package/src/bmb/workflows/module/steps-b/step-11-scenarios.md +84 -84
- package/src/bmb/workflows/module/steps-b/step-12-creative.md +95 -95
- package/src/bmb/workflows/module/steps-b/step-13-review.md +105 -105
- package/src/bmb/workflows/module/steps-b/step-14-finalize.md +117 -117
- package/src/bmb/workflows/module/steps-c/step-01-load-brief.md +179 -179
- package/src/bmb/workflows/module/steps-c/step-01b-continue.md +82 -82
- package/src/bmb/workflows/module/steps-c/step-02-structure.md +105 -105
- package/src/bmb/workflows/module/steps-c/step-03-config.md +119 -119
- package/src/bmb/workflows/module/steps-c/step-04-agents.md +168 -168
- package/src/bmb/workflows/module/steps-c/step-05-workflows.md +184 -184
- package/src/bmb/workflows/module/steps-c/step-06-docs.md +401 -401
- package/src/bmb/workflows/module/steps-c/step-07-complete.md +152 -152
- package/src/bmb/workflows/module/steps-e/step-01-load-target.md +81 -81
- package/src/bmb/workflows/module/steps-e/step-02-select-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-03-apply-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-04-review.md +80 -80
- package/src/bmb/workflows/module/steps-e/step-05-confirm.md +75 -75
- package/src/bmb/workflows/module/steps-v/step-01-load-target.md +96 -96
- package/src/bmb/workflows/module/steps-v/step-02-file-structure.md +93 -93
- package/src/bmb/workflows/module/steps-v/step-03-module-yaml.md +99 -99
- package/src/bmb/workflows/module/steps-v/step-04-agent-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-05-workflow-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-06-documentation.md +143 -143
- package/src/bmb/workflows/module/steps-v/step-07-installation.md +102 -102
- package/src/bmb/workflows/module/steps-v/step-08-report.md +197 -197
- package/src/bmb/workflows/module/templates/brief-template.md +154 -154
- package/src/bmb/workflows/module/templates/workflow-spec-template.md +96 -96
- package/src/bmb/workflows/module/workflow-create-module-brief.md +71 -71
- package/src/bmb/workflows/module/workflow-create-module.md +86 -86
- package/src/bmb/workflows/module/workflow-edit-module.md +66 -66
- package/src/bmb/workflows/module/workflow-validate-module.md +66 -66
- package/src/bmb/workflows/workflow/data/architecture.md +150 -150
- package/src/bmb/workflows/workflow/data/common-workflow-tools.csv +19 -19
- package/src/bmb/workflows/workflow/data/csv-data-file-standards.md +53 -53
- package/src/bmb/workflows/workflow/data/frontmatter-standards.md +184 -184
- package/src/bmb/workflows/workflow/data/input-discovery-standards.md +191 -191
- package/src/bmb/workflows/workflow/data/intent-vs-prescriptive-spectrum.md +44 -44
- package/src/bmb/workflows/workflow/data/menu-handling-standards.md +133 -133
- package/src/bmb/workflows/workflow/data/output-format-standards.md +135 -135
- package/src/bmb/workflows/workflow/data/step-file-rules.md +235 -235
- package/src/bmb/workflows/workflow/data/step-type-patterns.md +257 -257
- package/src/bmb/workflows/workflow/data/subprocess-optimization-patterns.md +188 -188
- package/src/bmb/workflows/workflow/data/trimodal-workflow-structure.md +164 -164
- package/src/bmb/workflows/workflow/data/workflow-chaining-standards.md +222 -222
- package/src/bmb/workflows/workflow/data/workflow-examples.md +232 -232
- package/src/bmb/workflows/workflow/data/workflow-type-criteria.md +134 -134
- package/src/bmb/workflows/workflow/steps-c/step-00-conversion.md +263 -263
- package/src/bmb/workflows/workflow/steps-c/step-01-discovery.md +194 -194
- package/src/bmb/workflows/workflow/steps-c/step-01b-continuation.md +3 -3
- package/src/bmb/workflows/workflow/steps-c/step-02-classification.md +270 -270
- package/src/bmb/workflows/workflow/steps-c/step-03-requirements.md +283 -283
- package/src/bmb/workflows/workflow/steps-c/step-04-tools.md +282 -282
- package/src/bmb/workflows/workflow/steps-c/step-05-plan-review.md +243 -243
- package/src/bmb/workflows/workflow/steps-c/step-06-design.md +330 -330
- package/src/bmb/workflows/workflow/steps-c/step-07-foundation.md +239 -239
- package/src/bmb/workflows/workflow/steps-c/step-08-build-step-01.md +379 -379
- package/src/bmb/workflows/workflow/steps-c/step-09-build-next-step.md +350 -350
- package/src/bmb/workflows/workflow/steps-c/step-10-confirmation.md +322 -322
- package/src/bmb/workflows/workflow/steps-c/step-11-completion.md +191 -191
- package/src/bmb/workflows/workflow/steps-e/step-e-01-assess-workflow.md +237 -237
- package/src/bmb/workflows/workflow/steps-e/step-e-02-discover-edits.md +251 -251
- package/src/bmb/workflows/workflow/steps-e/step-e-03-fix-validation.md +254 -254
- package/src/bmb/workflows/workflow/steps-e/step-e-04-direct-edit.md +277 -277
- package/src/bmb/workflows/workflow/steps-e/step-e-05-apply-edit.md +154 -154
- package/src/bmb/workflows/workflow/steps-e/step-e-06-validate-after.md +190 -190
- package/src/bmb/workflows/workflow/steps-e/step-e-07-complete.md +206 -206
- package/src/bmb/workflows/workflow/steps-v/step-01-validate-max-mode.md +109 -109
- package/src/bmb/workflows/workflow/steps-v/step-01-validate.md +221 -221
- package/src/bmb/workflows/workflow/steps-v/step-01b-structure.md +152 -152
- package/src/bmb/workflows/workflow/steps-v/step-02-frontmatter-validation.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-02b-path-violations.md +265 -265
- package/src/bmb/workflows/workflow/steps-v/step-03-menu-validation.md +164 -164
- package/src/bmb/workflows/workflow/steps-v/step-04-step-type-validation.md +211 -211
- package/src/bmb/workflows/workflow/steps-v/step-05-output-format-validation.md +200 -200
- package/src/bmb/workflows/workflow/steps-v/step-06-validation-design-check.md +195 -195
- package/src/bmb/workflows/workflow/steps-v/step-07-instruction-style-check.md +209 -209
- package/src/bmb/workflows/workflow/steps-v/step-08-collaborative-experience-check.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-08b-subprocess-optimization.md +179 -179
- package/src/bmb/workflows/workflow/steps-v/step-09-cohesive-review.md +186 -186
- package/src/bmb/workflows/workflow/steps-v/step-10-report-complete.md +154 -154
- package/src/bmb/workflows/workflow/steps-v/step-11-plan-validation.md +237 -237
- package/src/bmb/workflows/workflow/templates/minimal-output-template.md +11 -11
- package/src/bmb/workflows/workflow/templates/step-01-init-continuable-template.md +241 -241
- package/src/bmb/workflows/workflow/templates/step-1b-template.md +224 -224
- package/src/bmb/workflows/workflow/templates/step-template.md +294 -294
- package/src/bmb/workflows/workflow/templates/workflow-template.md +102 -102
- package/src/bmb/workflows/workflow/workflow-create-workflow.md +79 -79
- package/src/bmb/workflows/workflow/workflow-edit-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-rework-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-validate-max-parallel-workflow.md +66 -66
- package/src/bmb/workflows/workflow/workflow-validate-workflow.md +65 -65
- package/src/bmm/agents/analyst.md +104 -104
- package/src/bmm/agents/dev.md +100 -100
- package/src/bmm/agents/qa.md +100 -90
- package/src/bmm/agents/review-agent.md +1 -1
- package/src/bmm/agents/tech-writer/tech-writer.md +94 -94
- package/src/bmm/module-help.csv +31 -31
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +115 -115
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +107 -107
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +141 -141
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +144 -144
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +147 -147
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +161 -161
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +99 -99
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +57 -57
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +156 -156
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +165 -165
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +140 -140
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +152 -152
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +345 -345
- package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +92 -92
- package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +164 -164
- package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +174 -174
- package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +184 -184
- package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +105 -105
- package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +360 -360
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +165 -165
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +174 -174
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +141 -141
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +159 -159
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +387 -387
- package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -54
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +100 -100
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +160 -160
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +88 -88
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +99 -99
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +169 -169
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +156 -156
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +176 -176
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +184 -184
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +174 -174
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +175 -175
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +189 -189
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +162 -162
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +79 -79
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +183 -183
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +149 -149
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +187 -187
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +108 -108
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +166 -166
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +131 -131
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +150 -150
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +155 -155
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +170 -170
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +158 -158
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +147 -147
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +182 -182
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +202 -202
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +148 -148
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +201 -201
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +179 -179
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +164 -164
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +106 -106
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +111 -111
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +115 -115
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +127 -127
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +167 -167
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +143 -143
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +154 -154
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +165 -165
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +135 -135
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +101 -101
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +45 -45
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +185 -185
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +130 -130
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +93 -93
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +196 -196
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +54 -54
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +82 -82
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +106 -106
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +138 -138
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +129 -129
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +166 -166
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +186 -186
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +163 -163
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +38 -38
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +49 -49
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +124 -124
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +122 -122
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +84 -84
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +58 -58
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +43 -43
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +53 -53
- package/src/bmm/workflows/4-implementation/create-story/checklist.md +159 -159
- package/src/bmm/workflows/4-implementation/create-story/template.md +79 -79
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +20 -20
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +25 -25
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +158 -158
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +122 -122
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +87 -87
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +146 -146
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -50
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +152 -152
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +123 -123
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +201 -201
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -79
- package/src/bmm/workflows/document-project/workflow.yaml +22 -22
- package/src/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -184
- package/src/bmm/workflows/generate-project-context/steps/step-02-generate.md +322 -322
- package/src/bmm/workflows/generate-project-context/steps/step-03-complete.md +235 -235
- package/src/bmm/workflows/generate-project-context/workflow.md +49 -49
- package/src/bmm/workflows/qa/automate/workflow.yaml +233 -233
- package/src/bmm/workflows/qa-generate-e2e-tests/workflow.yaml +42 -42
- package/src/core/agents/master-orchestrator.md +3 -3
- package/src/core/config.yaml +9 -9
- package/src/core/module-help.csv +10 -10
- package/src/core/scripts/generate-loop-report.py +72 -72
- package/src/core/tasks/editorial-review-prose.xml +101 -101
- package/src/core/tasks/editorial-review-structure.xml +207 -207
- package/src/core/tasks/help.md +86 -86
- package/src/core/tasks/index-docs.xml +64 -64
- package/src/core/tasks/review-adversarial-general.xml +66 -66
- package/src/core/tasks/review-adversarial-loop.xml +46 -46
- package/src/core/tasks/review-edge-case-hunter.xml +63 -63
- package/src/core/tasks/review-party-loop.xml +46 -46
- package/src/core/tasks/shard-doc.xml +107 -107
- package/src/core/tasks/workflow.xml +235 -235
- package/src/core/templates/review-loop-report.html +88 -88
- package/src/core/templates/review-loop-report.md +5 -5
- package/src/core/workflows/advanced-elicitation/workflow.xml +117 -117
- package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +212 -212
- package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -122
- package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -225
- package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -237
- package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -209
- package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -264
- package/src/core/workflows/brainstorming/steps/step-02e-deep-dive.md +68 -68
- package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +403 -403
- package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -303
- package/src/core/workflows/brainstorming/workflow.md +60 -60
- package/src/core/workflows/extract-trackers/workflow.md +45 -45
- package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +142 -142
- package/src/core/workflows/party-mode/workflow.md +194 -194
- package/src/docs/dev/tmux/actions_popup.py +291 -291
- package/src/docs/dev/tmux/tmux-setup.md +62 -1
|
@@ -1,519 +1,519 @@
|
|
|
1
|
-
# API Security Reference
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
APIs expose application functionality and data, making them prime targets for attackers. This reference covers security for REST APIs, GraphQL, and general API patterns.
|
|
6
|
-
|
|
7
|
-
## Authentication
|
|
8
|
-
|
|
9
|
-
### Token-Based Authentication
|
|
10
|
-
|
|
11
|
-
```python
|
|
12
|
-
# JWT Best Practices
|
|
13
|
-
# 1. Use strong signing algorithms
|
|
14
|
-
# VULNERABLE: None algorithm
|
|
15
|
-
jwt.decode(token, algorithms=['none'])
|
|
16
|
-
|
|
17
|
-
# SAFE: Explicit algorithm
|
|
18
|
-
jwt.decode(token, secret_key, algorithms=['HS256'])
|
|
19
|
-
|
|
20
|
-
# 2. Validate standard claims
|
|
21
|
-
def validate_jwt(token):
|
|
22
|
-
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
|
|
23
|
-
|
|
24
|
-
# Validate issuer
|
|
25
|
-
if payload.get('iss') != EXPECTED_ISSUER:
|
|
26
|
-
raise ValueError("Invalid issuer")
|
|
27
|
-
|
|
28
|
-
# Validate audience
|
|
29
|
-
if payload.get('aud') != EXPECTED_AUDIENCE:
|
|
30
|
-
raise ValueError("Invalid audience")
|
|
31
|
-
|
|
32
|
-
# Validate expiration (jwt library does this automatically)
|
|
33
|
-
# Validate not-before (jwt library does this automatically)
|
|
34
|
-
|
|
35
|
-
return payload
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### API Key Security
|
|
39
|
-
|
|
40
|
-
```python
|
|
41
|
-
# VULNERABLE: API key in URL (logged, cached, visible)
|
|
42
|
-
GET /api/users?api_key=secret123
|
|
43
|
-
|
|
44
|
-
# SAFE: API key in header
|
|
45
|
-
GET /api/users
|
|
46
|
-
Authorization: Bearer api_key_here
|
|
47
|
-
# Or
|
|
48
|
-
X-API-Key: api_key_here
|
|
49
|
-
|
|
50
|
-
# Server-side validation
|
|
51
|
-
def require_api_key(f):
|
|
52
|
-
@wraps(f)
|
|
53
|
-
def decorated(*args, **kwargs):
|
|
54
|
-
api_key = request.headers.get('X-API-Key')
|
|
55
|
-
if not api_key or not validate_api_key(api_key):
|
|
56
|
-
return jsonify({'error': 'Invalid API key'}), 401
|
|
57
|
-
|
|
58
|
-
# Rate limit by API key
|
|
59
|
-
if is_rate_limited(api_key):
|
|
60
|
-
return jsonify({'error': 'Rate limit exceeded'}), 429
|
|
61
|
-
|
|
62
|
-
return f(*args, **kwargs)
|
|
63
|
-
return decorated
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Authorization
|
|
69
|
-
|
|
70
|
-
### Endpoint-Level Authorization
|
|
71
|
-
|
|
72
|
-
```python
|
|
73
|
-
# VULNERABLE: No authorization check
|
|
74
|
-
@app.route('/api/users/<user_id>', methods=['GET'])
|
|
75
|
-
def get_user(user_id):
|
|
76
|
-
return User.query.get(user_id).to_dict()
|
|
77
|
-
|
|
78
|
-
# SAFE: Authorization check
|
|
79
|
-
@app.route('/api/users/<user_id>', methods=['GET'])
|
|
80
|
-
@require_auth
|
|
81
|
-
def get_user(user_id):
|
|
82
|
-
if not current_user.can_access_user(user_id):
|
|
83
|
-
return jsonify({'error': 'Forbidden'}), 403
|
|
84
|
-
return User.query.get(user_id).to_dict()
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Field-Level Authorization
|
|
88
|
-
|
|
89
|
-
```python
|
|
90
|
-
# VULNERABLE: All fields returned
|
|
91
|
-
@app.route('/api/users/<user_id>')
|
|
92
|
-
def get_user(user_id):
|
|
93
|
-
user = User.query.get(user_id)
|
|
94
|
-
return jsonify({
|
|
95
|
-
'id': user.id,
|
|
96
|
-
'email': user.email,
|
|
97
|
-
'ssn': user.ssn, # Sensitive!
|
|
98
|
-
'is_admin': user.is_admin, # Internal!
|
|
99
|
-
'password_hash': user.password_hash # NEVER expose!
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
# SAFE: Filtered response based on permissions
|
|
103
|
-
@app.route('/api/users/<user_id>')
|
|
104
|
-
@require_auth
|
|
105
|
-
def get_user(user_id):
|
|
106
|
-
user = User.query.get(user_id)
|
|
107
|
-
|
|
108
|
-
response = {
|
|
109
|
-
'id': user.id,
|
|
110
|
-
'name': user.name,
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
# Add fields based on permissions
|
|
114
|
-
if current_user.id == user_id or current_user.is_admin:
|
|
115
|
-
response['email'] = user.email
|
|
116
|
-
|
|
117
|
-
if current_user.is_admin:
|
|
118
|
-
response['is_admin'] = user.is_admin
|
|
119
|
-
|
|
120
|
-
return jsonify(response)
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## Input Validation
|
|
126
|
-
|
|
127
|
-
### Request Validation
|
|
128
|
-
|
|
129
|
-
```python
|
|
130
|
-
from pydantic import BaseModel, validator, Field
|
|
131
|
-
from typing import Optional
|
|
132
|
-
|
|
133
|
-
class CreateUserRequest(BaseModel):
|
|
134
|
-
name: str = Field(..., min_length=1, max_length=100)
|
|
135
|
-
email: str = Field(..., regex=r'^[\w\.-]+@[\w\.-]+\.\w+$')
|
|
136
|
-
age: Optional[int] = Field(None, ge=0, le=150)
|
|
137
|
-
|
|
138
|
-
@validator('name')
|
|
139
|
-
def name_must_not_be_empty(cls, v):
|
|
140
|
-
if not v.strip():
|
|
141
|
-
raise ValueError('Name cannot be empty')
|
|
142
|
-
return v.strip()
|
|
143
|
-
|
|
144
|
-
@app.route('/api/users', methods=['POST'])
|
|
145
|
-
def create_user():
|
|
146
|
-
try:
|
|
147
|
-
data = CreateUserRequest(**request.json)
|
|
148
|
-
except ValidationError as e:
|
|
149
|
-
return jsonify({'error': e.errors()}), 400
|
|
150
|
-
|
|
151
|
-
# Process validated data
|
|
152
|
-
return create_user_from_data(data)
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Content-Type Validation
|
|
156
|
-
|
|
157
|
-
```python
|
|
158
|
-
# VULNERABLE: Accept any content type
|
|
159
|
-
@app.route('/api/data', methods=['POST'])
|
|
160
|
-
def process_data():
|
|
161
|
-
data = request.get_json() # May fail silently
|
|
162
|
-
|
|
163
|
-
# SAFE: Validate content type
|
|
164
|
-
@app.route('/api/data', methods=['POST'])
|
|
165
|
-
def process_data():
|
|
166
|
-
if request.content_type != 'application/json':
|
|
167
|
-
return jsonify({'error': 'Content-Type must be application/json'}), 415
|
|
168
|
-
|
|
169
|
-
data = request.get_json()
|
|
170
|
-
if data is None:
|
|
171
|
-
return jsonify({'error': 'Invalid JSON'}), 400
|
|
172
|
-
|
|
173
|
-
return process(data)
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Request Size Limits
|
|
177
|
-
|
|
178
|
-
```python
|
|
179
|
-
# Flask
|
|
180
|
-
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max
|
|
181
|
-
|
|
182
|
-
# Express
|
|
183
|
-
app.use(express.json({ limit: '10mb' }))
|
|
184
|
-
|
|
185
|
-
# Handle large request errors
|
|
186
|
-
@app.errorhandler(413)
|
|
187
|
-
def request_too_large(e):
|
|
188
|
-
return jsonify({'error': 'Request too large'}), 413
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
---
|
|
192
|
-
|
|
193
|
-
## Rate Limiting
|
|
194
|
-
|
|
195
|
-
### Implementation
|
|
196
|
-
|
|
197
|
-
```python
|
|
198
|
-
from flask_limiter import Limiter
|
|
199
|
-
from flask_limiter.util import get_remote_address
|
|
200
|
-
|
|
201
|
-
limiter = Limiter(
|
|
202
|
-
app,
|
|
203
|
-
key_func=get_remote_address,
|
|
204
|
-
default_limits=["200 per day", "50 per hour"]
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
# Endpoint-specific limits
|
|
208
|
-
@app.route('/api/login', methods=['POST'])
|
|
209
|
-
@limiter.limit("5 per minute") # Prevent brute force
|
|
210
|
-
def login():
|
|
211
|
-
pass
|
|
212
|
-
|
|
213
|
-
@app.route('/api/password-reset', methods=['POST'])
|
|
214
|
-
@limiter.limit("3 per hour") # Prevent enumeration
|
|
215
|
-
def password_reset():
|
|
216
|
-
pass
|
|
217
|
-
|
|
218
|
-
# Return proper headers
|
|
219
|
-
# X-RateLimit-Limit: 50
|
|
220
|
-
# X-RateLimit-Remaining: 45
|
|
221
|
-
# X-RateLimit-Reset: 1623456789
|
|
222
|
-
# Retry-After: 3600 (when limited)
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Rate Limit by API Key
|
|
226
|
-
|
|
227
|
-
```python
|
|
228
|
-
def get_rate_limit_key():
|
|
229
|
-
# Prefer API key over IP for authenticated requests
|
|
230
|
-
api_key = request.headers.get('X-API-Key')
|
|
231
|
-
if api_key:
|
|
232
|
-
return f"api_key:{api_key}"
|
|
233
|
-
return f"ip:{get_remote_address()}"
|
|
234
|
-
|
|
235
|
-
limiter = Limiter(key_func=get_rate_limit_key)
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
---
|
|
239
|
-
|
|
240
|
-
## Mass Assignment Prevention
|
|
241
|
-
|
|
242
|
-
```python
|
|
243
|
-
# VULNERABLE: Accepting all fields
|
|
244
|
-
@app.route('/api/users/<id>', methods=['PATCH'])
|
|
245
|
-
def update_user(id):
|
|
246
|
-
user = User.query.get(id)
|
|
247
|
-
user.update(**request.json) # Attacker sets is_admin=True
|
|
248
|
-
return user.to_dict()
|
|
249
|
-
|
|
250
|
-
# SAFE: Allowlist of fields
|
|
251
|
-
ALLOWED_USER_FIELDS = {'name', 'email', 'bio'}
|
|
252
|
-
|
|
253
|
-
@app.route('/api/users/<id>', methods=['PATCH'])
|
|
254
|
-
def update_user(id):
|
|
255
|
-
user = User.query.get(id)
|
|
256
|
-
data = {k: v for k, v in request.json.items() if k in ALLOWED_USER_FIELDS}
|
|
257
|
-
user.update(**data)
|
|
258
|
-
return user.to_dict()
|
|
259
|
-
|
|
260
|
-
# BETTER: Use DTOs
|
|
261
|
-
class UserUpdateDTO(BaseModel):
|
|
262
|
-
name: Optional[str]
|
|
263
|
-
email: Optional[str]
|
|
264
|
-
bio: Optional[str]
|
|
265
|
-
# is_admin NOT included - can't be set
|
|
266
|
-
|
|
267
|
-
@app.route('/api/users/<id>', methods=['PATCH'])
|
|
268
|
-
def update_user(id):
|
|
269
|
-
dto = UserUpdateDTO(**request.json)
|
|
270
|
-
user = User.query.get(id)
|
|
271
|
-
user.update(**dto.dict(exclude_unset=True))
|
|
272
|
-
return user.to_dict()
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
---
|
|
276
|
-
|
|
277
|
-
## GraphQL Security
|
|
278
|
-
|
|
279
|
-
### Query Depth Limiting
|
|
280
|
-
|
|
281
|
-
```python
|
|
282
|
-
# VULNERABLE: Unbounded depth
|
|
283
|
-
# query { user { friends { friends { friends { ... } } } } }
|
|
284
|
-
|
|
285
|
-
# SAFE: Limit query depth
|
|
286
|
-
from graphql import validate
|
|
287
|
-
from graphql_core import depth_limit_validator
|
|
288
|
-
|
|
289
|
-
schema = build_schema(...)
|
|
290
|
-
|
|
291
|
-
def execute_query(query):
|
|
292
|
-
errors = validate(
|
|
293
|
-
schema,
|
|
294
|
-
parse(query),
|
|
295
|
-
[depth_limit_validator(max_depth=5)]
|
|
296
|
-
)
|
|
297
|
-
if errors:
|
|
298
|
-
return {'errors': [str(e) for e in errors]}
|
|
299
|
-
return graphql_sync(schema, query)
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
### Query Cost Analysis
|
|
303
|
-
|
|
304
|
-
```python
|
|
305
|
-
# Assign costs to fields and limit total cost
|
|
306
|
-
from graphene import ObjectType, Field, Int
|
|
307
|
-
|
|
308
|
-
class Query(ObjectType):
|
|
309
|
-
user = Field(User, cost=1)
|
|
310
|
-
users = Field(List(User), cost=lambda info, **args: args.get('limit', 10))
|
|
311
|
-
expensive_query = Field(Report, cost=100)
|
|
312
|
-
|
|
313
|
-
# Reject queries exceeding cost threshold
|
|
314
|
-
MAX_QUERY_COST = 1000
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
### Disable Introspection in Production
|
|
318
|
-
|
|
319
|
-
```python
|
|
320
|
-
# VULNERABLE: Introspection enabled
|
|
321
|
-
# Attackers can discover entire schema
|
|
322
|
-
|
|
323
|
-
# SAFE: Disable introspection
|
|
324
|
-
from graphql import GraphQLSchema
|
|
325
|
-
|
|
326
|
-
class NoIntrospectionMiddleware:
|
|
327
|
-
def resolve(self, next, root, info, **args):
|
|
328
|
-
if info.field_name in ('__schema', '__type'):
|
|
329
|
-
return None
|
|
330
|
-
return next(root, info, **args)
|
|
331
|
-
|
|
332
|
-
# Or in configuration
|
|
333
|
-
app.config['GRAPHQL_INTROSPECTION'] = False
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### Batching Attack Prevention
|
|
337
|
-
|
|
338
|
-
```python
|
|
339
|
-
# VULNERABLE: Allows unlimited batched mutations
|
|
340
|
-
# [
|
|
341
|
-
# { "query": "mutation { login(user: 'a', pass: 'a') }" },
|
|
342
|
-
# { "query": "mutation { login(user: 'a', pass: 'b') }" },
|
|
343
|
-
# ...
|
|
344
|
-
# ]
|
|
345
|
-
|
|
346
|
-
# SAFE: Limit batch size
|
|
347
|
-
MAX_BATCH_SIZE = 10
|
|
348
|
-
|
|
349
|
-
@app.route('/graphql', methods=['POST'])
|
|
350
|
-
def graphql_endpoint():
|
|
351
|
-
data = request.json
|
|
352
|
-
|
|
353
|
-
if isinstance(data, list):
|
|
354
|
-
if len(data) > MAX_BATCH_SIZE:
|
|
355
|
-
return jsonify({'error': 'Batch size exceeded'}), 400
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
---
|
|
359
|
-
|
|
360
|
-
## Error Handling
|
|
361
|
-
|
|
362
|
-
### Generic Error Responses
|
|
363
|
-
|
|
364
|
-
```python
|
|
365
|
-
# VULNERABLE: Detailed errors
|
|
366
|
-
@app.errorhandler(Exception)
|
|
367
|
-
def handle_error(e):
|
|
368
|
-
return jsonify({
|
|
369
|
-
'error': str(e),
|
|
370
|
-
'traceback': traceback.format_exc(),
|
|
371
|
-
'query': last_query
|
|
372
|
-
}), 500
|
|
373
|
-
|
|
374
|
-
# SAFE: Generic errors
|
|
375
|
-
@app.errorhandler(Exception)
|
|
376
|
-
def handle_error(e):
|
|
377
|
-
# Log full details server-side
|
|
378
|
-
app.logger.error(f"Error: {e}", exc_info=True)
|
|
379
|
-
|
|
380
|
-
# Return generic message
|
|
381
|
-
return jsonify({'error': 'An unexpected error occurred'}), 500
|
|
382
|
-
|
|
383
|
-
# Use RFC 7807 Problem Details
|
|
384
|
-
@app.errorhandler(404)
|
|
385
|
-
def not_found(e):
|
|
386
|
-
return jsonify({
|
|
387
|
-
'type': 'https://example.com/problems/not-found',
|
|
388
|
-
'title': 'Resource Not Found',
|
|
389
|
-
'status': 404,
|
|
390
|
-
'detail': 'The requested resource was not found'
|
|
391
|
-
}), 404
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
---
|
|
395
|
-
|
|
396
|
-
## Security Headers
|
|
397
|
-
|
|
398
|
-
```python
|
|
399
|
-
@app.after_request
|
|
400
|
-
def add_security_headers(response):
|
|
401
|
-
# Prevent caching of sensitive data
|
|
402
|
-
if request.endpoint in SENSITIVE_ENDPOINTS:
|
|
403
|
-
response.headers['Cache-Control'] = 'no-store'
|
|
404
|
-
|
|
405
|
-
response.headers['X-Content-Type-Options'] = 'nosniff'
|
|
406
|
-
response.headers['X-Frame-Options'] = 'DENY'
|
|
407
|
-
response.headers['Content-Security-Policy'] = "default-src 'none'"
|
|
408
|
-
|
|
409
|
-
return response
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
---
|
|
413
|
-
|
|
414
|
-
## CORS Configuration
|
|
415
|
-
|
|
416
|
-
```python
|
|
417
|
-
# VULNERABLE: Allow all origins
|
|
418
|
-
CORS(app, origins='*')
|
|
419
|
-
|
|
420
|
-
# VULNERABLE: Reflect origin header
|
|
421
|
-
@app.after_request
|
|
422
|
-
def add_cors(response):
|
|
423
|
-
response.headers['Access-Control-Allow-Origin'] = request.headers.get('Origin')
|
|
424
|
-
return response
|
|
425
|
-
|
|
426
|
-
# SAFE: Explicit allowlist
|
|
427
|
-
CORS(app, origins=[
|
|
428
|
-
'https://app.example.com',
|
|
429
|
-
'https://admin.example.com'
|
|
430
|
-
], supports_credentials=True)
|
|
431
|
-
|
|
432
|
-
# SAFE: Dynamic with validation
|
|
433
|
-
ALLOWED_ORIGINS = {'https://app.example.com', 'https://admin.example.com'}
|
|
434
|
-
|
|
435
|
-
@app.after_request
|
|
436
|
-
def add_cors(response):
|
|
437
|
-
origin = request.headers.get('Origin')
|
|
438
|
-
if origin in ALLOWED_ORIGINS:
|
|
439
|
-
response.headers['Access-Control-Allow-Origin'] = origin
|
|
440
|
-
response.headers['Access-Control-Allow-Credentials'] = 'true'
|
|
441
|
-
return response
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
---
|
|
445
|
-
|
|
446
|
-
## HTTP Methods
|
|
447
|
-
|
|
448
|
-
```python
|
|
449
|
-
# VULNERABLE: Method not enforced
|
|
450
|
-
@app.route('/api/users', methods=['GET', 'POST', 'PUT', 'DELETE'])
|
|
451
|
-
def users():
|
|
452
|
-
pass
|
|
453
|
-
|
|
454
|
-
# SAFE: Explicit method handling
|
|
455
|
-
@app.route('/api/users', methods=['GET'])
|
|
456
|
-
def list_users():
|
|
457
|
-
pass
|
|
458
|
-
|
|
459
|
-
@app.route('/api/users', methods=['POST'])
|
|
460
|
-
@require_auth
|
|
461
|
-
def create_user():
|
|
462
|
-
pass
|
|
463
|
-
|
|
464
|
-
# Return 405 for unsupported methods
|
|
465
|
-
@app.errorhandler(405)
|
|
466
|
-
def method_not_allowed(e):
|
|
467
|
-
return jsonify({'error': 'Method not allowed'}), 405
|
|
468
|
-
```
|
|
469
|
-
|
|
470
|
-
---
|
|
471
|
-
|
|
472
|
-
## Grep Patterns for Detection
|
|
473
|
-
|
|
474
|
-
```bash
|
|
475
|
-
# Missing authentication
|
|
476
|
-
grep -rn "@app\.route\|@router\." --include="*.py" | grep -v "@require_auth\|@login_required"
|
|
477
|
-
|
|
478
|
-
# Returning all fields
|
|
479
|
-
grep -rn "to_dict()\|__dict__\|serialize" --include="*.py"
|
|
480
|
-
|
|
481
|
-
# Mass assignment
|
|
482
|
-
grep -rn "\*\*request\.\|update(\*\*\|create(\*\*" --include="*.py"
|
|
483
|
-
|
|
484
|
-
# Missing rate limiting
|
|
485
|
-
grep -rn "login\|password\|reset" --include="*.py" | grep "route" | grep -v "limiter\|rate"
|
|
486
|
-
|
|
487
|
-
# GraphQL introspection
|
|
488
|
-
grep -rn "__schema\|introspection" --include="*.py"
|
|
489
|
-
|
|
490
|
-
# CORS wildcards
|
|
491
|
-
grep -rn "origins.*\*\|Access-Control-Allow-Origin.*\*" --include="*.py"
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
---
|
|
495
|
-
|
|
496
|
-
## Testing Checklist
|
|
497
|
-
|
|
498
|
-
- [ ] All endpoints require authentication (except public ones)
|
|
499
|
-
- [ ] Authorization checked for every request
|
|
500
|
-
- [ ] Input validation on all parameters
|
|
501
|
-
- [ ] Response filtering (no sensitive data exposure)
|
|
502
|
-
- [ ] Rate limiting on authentication endpoints
|
|
503
|
-
- [ ] Rate limiting on resource-intensive endpoints
|
|
504
|
-
- [ ] Mass assignment prevented (field allowlists)
|
|
505
|
-
- [ ] Proper error handling (no information leakage)
|
|
506
|
-
- [ ] Security headers configured
|
|
507
|
-
- [ ] CORS properly configured
|
|
508
|
-
- [ ] HTTP methods restricted
|
|
509
|
-
- [ ] GraphQL depth/cost limiting (if applicable)
|
|
510
|
-
- [ ] GraphQL introspection disabled in production
|
|
511
|
-
|
|
512
|
-
---
|
|
513
|
-
|
|
514
|
-
## References
|
|
515
|
-
|
|
516
|
-
- [OWASP REST Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html)
|
|
517
|
-
- [OWASP GraphQL Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html)
|
|
518
|
-
- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
|
|
519
|
-
- [CWE-285: Improper Authorization](https://cwe.mitre.org/data/definitions/285.html)
|
|
1
|
+
# API Security Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
APIs expose application functionality and data, making them prime targets for attackers. This reference covers security for REST APIs, GraphQL, and general API patterns.
|
|
6
|
+
|
|
7
|
+
## Authentication
|
|
8
|
+
|
|
9
|
+
### Token-Based Authentication
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
# JWT Best Practices
|
|
13
|
+
# 1. Use strong signing algorithms
|
|
14
|
+
# VULNERABLE: None algorithm
|
|
15
|
+
jwt.decode(token, algorithms=['none'])
|
|
16
|
+
|
|
17
|
+
# SAFE: Explicit algorithm
|
|
18
|
+
jwt.decode(token, secret_key, algorithms=['HS256'])
|
|
19
|
+
|
|
20
|
+
# 2. Validate standard claims
|
|
21
|
+
def validate_jwt(token):
|
|
22
|
+
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
|
|
23
|
+
|
|
24
|
+
# Validate issuer
|
|
25
|
+
if payload.get('iss') != EXPECTED_ISSUER:
|
|
26
|
+
raise ValueError("Invalid issuer")
|
|
27
|
+
|
|
28
|
+
# Validate audience
|
|
29
|
+
if payload.get('aud') != EXPECTED_AUDIENCE:
|
|
30
|
+
raise ValueError("Invalid audience")
|
|
31
|
+
|
|
32
|
+
# Validate expiration (jwt library does this automatically)
|
|
33
|
+
# Validate not-before (jwt library does this automatically)
|
|
34
|
+
|
|
35
|
+
return payload
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### API Key Security
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
# VULNERABLE: API key in URL (logged, cached, visible)
|
|
42
|
+
GET /api/users?api_key=secret123
|
|
43
|
+
|
|
44
|
+
# SAFE: API key in header
|
|
45
|
+
GET /api/users
|
|
46
|
+
Authorization: Bearer api_key_here
|
|
47
|
+
# Or
|
|
48
|
+
X-API-Key: api_key_here
|
|
49
|
+
|
|
50
|
+
# Server-side validation
|
|
51
|
+
def require_api_key(f):
|
|
52
|
+
@wraps(f)
|
|
53
|
+
def decorated(*args, **kwargs):
|
|
54
|
+
api_key = request.headers.get('X-API-Key')
|
|
55
|
+
if not api_key or not validate_api_key(api_key):
|
|
56
|
+
return jsonify({'error': 'Invalid API key'}), 401
|
|
57
|
+
|
|
58
|
+
# Rate limit by API key
|
|
59
|
+
if is_rate_limited(api_key):
|
|
60
|
+
return jsonify({'error': 'Rate limit exceeded'}), 429
|
|
61
|
+
|
|
62
|
+
return f(*args, **kwargs)
|
|
63
|
+
return decorated
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Authorization
|
|
69
|
+
|
|
70
|
+
### Endpoint-Level Authorization
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
# VULNERABLE: No authorization check
|
|
74
|
+
@app.route('/api/users/<user_id>', methods=['GET'])
|
|
75
|
+
def get_user(user_id):
|
|
76
|
+
return User.query.get(user_id).to_dict()
|
|
77
|
+
|
|
78
|
+
# SAFE: Authorization check
|
|
79
|
+
@app.route('/api/users/<user_id>', methods=['GET'])
|
|
80
|
+
@require_auth
|
|
81
|
+
def get_user(user_id):
|
|
82
|
+
if not current_user.can_access_user(user_id):
|
|
83
|
+
return jsonify({'error': 'Forbidden'}), 403
|
|
84
|
+
return User.query.get(user_id).to_dict()
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Field-Level Authorization
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
# VULNERABLE: All fields returned
|
|
91
|
+
@app.route('/api/users/<user_id>')
|
|
92
|
+
def get_user(user_id):
|
|
93
|
+
user = User.query.get(user_id)
|
|
94
|
+
return jsonify({
|
|
95
|
+
'id': user.id,
|
|
96
|
+
'email': user.email,
|
|
97
|
+
'ssn': user.ssn, # Sensitive!
|
|
98
|
+
'is_admin': user.is_admin, # Internal!
|
|
99
|
+
'password_hash': user.password_hash # NEVER expose!
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
# SAFE: Filtered response based on permissions
|
|
103
|
+
@app.route('/api/users/<user_id>')
|
|
104
|
+
@require_auth
|
|
105
|
+
def get_user(user_id):
|
|
106
|
+
user = User.query.get(user_id)
|
|
107
|
+
|
|
108
|
+
response = {
|
|
109
|
+
'id': user.id,
|
|
110
|
+
'name': user.name,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Add fields based on permissions
|
|
114
|
+
if current_user.id == user_id or current_user.is_admin:
|
|
115
|
+
response['email'] = user.email
|
|
116
|
+
|
|
117
|
+
if current_user.is_admin:
|
|
118
|
+
response['is_admin'] = user.is_admin
|
|
119
|
+
|
|
120
|
+
return jsonify(response)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Input Validation
|
|
126
|
+
|
|
127
|
+
### Request Validation
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from pydantic import BaseModel, validator, Field
|
|
131
|
+
from typing import Optional
|
|
132
|
+
|
|
133
|
+
class CreateUserRequest(BaseModel):
|
|
134
|
+
name: str = Field(..., min_length=1, max_length=100)
|
|
135
|
+
email: str = Field(..., regex=r'^[\w\.-]+@[\w\.-]+\.\w+$')
|
|
136
|
+
age: Optional[int] = Field(None, ge=0, le=150)
|
|
137
|
+
|
|
138
|
+
@validator('name')
|
|
139
|
+
def name_must_not_be_empty(cls, v):
|
|
140
|
+
if not v.strip():
|
|
141
|
+
raise ValueError('Name cannot be empty')
|
|
142
|
+
return v.strip()
|
|
143
|
+
|
|
144
|
+
@app.route('/api/users', methods=['POST'])
|
|
145
|
+
def create_user():
|
|
146
|
+
try:
|
|
147
|
+
data = CreateUserRequest(**request.json)
|
|
148
|
+
except ValidationError as e:
|
|
149
|
+
return jsonify({'error': e.errors()}), 400
|
|
150
|
+
|
|
151
|
+
# Process validated data
|
|
152
|
+
return create_user_from_data(data)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Content-Type Validation
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
# VULNERABLE: Accept any content type
|
|
159
|
+
@app.route('/api/data', methods=['POST'])
|
|
160
|
+
def process_data():
|
|
161
|
+
data = request.get_json() # May fail silently
|
|
162
|
+
|
|
163
|
+
# SAFE: Validate content type
|
|
164
|
+
@app.route('/api/data', methods=['POST'])
|
|
165
|
+
def process_data():
|
|
166
|
+
if request.content_type != 'application/json':
|
|
167
|
+
return jsonify({'error': 'Content-Type must be application/json'}), 415
|
|
168
|
+
|
|
169
|
+
data = request.get_json()
|
|
170
|
+
if data is None:
|
|
171
|
+
return jsonify({'error': 'Invalid JSON'}), 400
|
|
172
|
+
|
|
173
|
+
return process(data)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Request Size Limits
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
# Flask
|
|
180
|
+
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max
|
|
181
|
+
|
|
182
|
+
# Express
|
|
183
|
+
app.use(express.json({ limit: '10mb' }))
|
|
184
|
+
|
|
185
|
+
# Handle large request errors
|
|
186
|
+
@app.errorhandler(413)
|
|
187
|
+
def request_too_large(e):
|
|
188
|
+
return jsonify({'error': 'Request too large'}), 413
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Rate Limiting
|
|
194
|
+
|
|
195
|
+
### Implementation
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from flask_limiter import Limiter
|
|
199
|
+
from flask_limiter.util import get_remote_address
|
|
200
|
+
|
|
201
|
+
limiter = Limiter(
|
|
202
|
+
app,
|
|
203
|
+
key_func=get_remote_address,
|
|
204
|
+
default_limits=["200 per day", "50 per hour"]
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Endpoint-specific limits
|
|
208
|
+
@app.route('/api/login', methods=['POST'])
|
|
209
|
+
@limiter.limit("5 per minute") # Prevent brute force
|
|
210
|
+
def login():
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
@app.route('/api/password-reset', methods=['POST'])
|
|
214
|
+
@limiter.limit("3 per hour") # Prevent enumeration
|
|
215
|
+
def password_reset():
|
|
216
|
+
pass
|
|
217
|
+
|
|
218
|
+
# Return proper headers
|
|
219
|
+
# X-RateLimit-Limit: 50
|
|
220
|
+
# X-RateLimit-Remaining: 45
|
|
221
|
+
# X-RateLimit-Reset: 1623456789
|
|
222
|
+
# Retry-After: 3600 (when limited)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Rate Limit by API Key
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
def get_rate_limit_key():
|
|
229
|
+
# Prefer API key over IP for authenticated requests
|
|
230
|
+
api_key = request.headers.get('X-API-Key')
|
|
231
|
+
if api_key:
|
|
232
|
+
return f"api_key:{api_key}"
|
|
233
|
+
return f"ip:{get_remote_address()}"
|
|
234
|
+
|
|
235
|
+
limiter = Limiter(key_func=get_rate_limit_key)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Mass Assignment Prevention
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
# VULNERABLE: Accepting all fields
|
|
244
|
+
@app.route('/api/users/<id>', methods=['PATCH'])
|
|
245
|
+
def update_user(id):
|
|
246
|
+
user = User.query.get(id)
|
|
247
|
+
user.update(**request.json) # Attacker sets is_admin=True
|
|
248
|
+
return user.to_dict()
|
|
249
|
+
|
|
250
|
+
# SAFE: Allowlist of fields
|
|
251
|
+
ALLOWED_USER_FIELDS = {'name', 'email', 'bio'}
|
|
252
|
+
|
|
253
|
+
@app.route('/api/users/<id>', methods=['PATCH'])
|
|
254
|
+
def update_user(id):
|
|
255
|
+
user = User.query.get(id)
|
|
256
|
+
data = {k: v for k, v in request.json.items() if k in ALLOWED_USER_FIELDS}
|
|
257
|
+
user.update(**data)
|
|
258
|
+
return user.to_dict()
|
|
259
|
+
|
|
260
|
+
# BETTER: Use DTOs
|
|
261
|
+
class UserUpdateDTO(BaseModel):
|
|
262
|
+
name: Optional[str]
|
|
263
|
+
email: Optional[str]
|
|
264
|
+
bio: Optional[str]
|
|
265
|
+
# is_admin NOT included - can't be set
|
|
266
|
+
|
|
267
|
+
@app.route('/api/users/<id>', methods=['PATCH'])
|
|
268
|
+
def update_user(id):
|
|
269
|
+
dto = UserUpdateDTO(**request.json)
|
|
270
|
+
user = User.query.get(id)
|
|
271
|
+
user.update(**dto.dict(exclude_unset=True))
|
|
272
|
+
return user.to_dict()
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## GraphQL Security
|
|
278
|
+
|
|
279
|
+
### Query Depth Limiting
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
# VULNERABLE: Unbounded depth
|
|
283
|
+
# query { user { friends { friends { friends { ... } } } } }
|
|
284
|
+
|
|
285
|
+
# SAFE: Limit query depth
|
|
286
|
+
from graphql import validate
|
|
287
|
+
from graphql_core import depth_limit_validator
|
|
288
|
+
|
|
289
|
+
schema = build_schema(...)
|
|
290
|
+
|
|
291
|
+
def execute_query(query):
|
|
292
|
+
errors = validate(
|
|
293
|
+
schema,
|
|
294
|
+
parse(query),
|
|
295
|
+
[depth_limit_validator(max_depth=5)]
|
|
296
|
+
)
|
|
297
|
+
if errors:
|
|
298
|
+
return {'errors': [str(e) for e in errors]}
|
|
299
|
+
return graphql_sync(schema, query)
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Query Cost Analysis
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
# Assign costs to fields and limit total cost
|
|
306
|
+
from graphene import ObjectType, Field, Int
|
|
307
|
+
|
|
308
|
+
class Query(ObjectType):
|
|
309
|
+
user = Field(User, cost=1)
|
|
310
|
+
users = Field(List(User), cost=lambda info, **args: args.get('limit', 10))
|
|
311
|
+
expensive_query = Field(Report, cost=100)
|
|
312
|
+
|
|
313
|
+
# Reject queries exceeding cost threshold
|
|
314
|
+
MAX_QUERY_COST = 1000
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Disable Introspection in Production
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
# VULNERABLE: Introspection enabled
|
|
321
|
+
# Attackers can discover entire schema
|
|
322
|
+
|
|
323
|
+
# SAFE: Disable introspection
|
|
324
|
+
from graphql import GraphQLSchema
|
|
325
|
+
|
|
326
|
+
class NoIntrospectionMiddleware:
|
|
327
|
+
def resolve(self, next, root, info, **args):
|
|
328
|
+
if info.field_name in ('__schema', '__type'):
|
|
329
|
+
return None
|
|
330
|
+
return next(root, info, **args)
|
|
331
|
+
|
|
332
|
+
# Or in configuration
|
|
333
|
+
app.config['GRAPHQL_INTROSPECTION'] = False
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Batching Attack Prevention
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
# VULNERABLE: Allows unlimited batched mutations
|
|
340
|
+
# [
|
|
341
|
+
# { "query": "mutation { login(user: 'a', pass: 'a') }" },
|
|
342
|
+
# { "query": "mutation { login(user: 'a', pass: 'b') }" },
|
|
343
|
+
# ...
|
|
344
|
+
# ]
|
|
345
|
+
|
|
346
|
+
# SAFE: Limit batch size
|
|
347
|
+
MAX_BATCH_SIZE = 10
|
|
348
|
+
|
|
349
|
+
@app.route('/graphql', methods=['POST'])
|
|
350
|
+
def graphql_endpoint():
|
|
351
|
+
data = request.json
|
|
352
|
+
|
|
353
|
+
if isinstance(data, list):
|
|
354
|
+
if len(data) > MAX_BATCH_SIZE:
|
|
355
|
+
return jsonify({'error': 'Batch size exceeded'}), 400
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Error Handling
|
|
361
|
+
|
|
362
|
+
### Generic Error Responses
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
# VULNERABLE: Detailed errors
|
|
366
|
+
@app.errorhandler(Exception)
|
|
367
|
+
def handle_error(e):
|
|
368
|
+
return jsonify({
|
|
369
|
+
'error': str(e),
|
|
370
|
+
'traceback': traceback.format_exc(),
|
|
371
|
+
'query': last_query
|
|
372
|
+
}), 500
|
|
373
|
+
|
|
374
|
+
# SAFE: Generic errors
|
|
375
|
+
@app.errorhandler(Exception)
|
|
376
|
+
def handle_error(e):
|
|
377
|
+
# Log full details server-side
|
|
378
|
+
app.logger.error(f"Error: {e}", exc_info=True)
|
|
379
|
+
|
|
380
|
+
# Return generic message
|
|
381
|
+
return jsonify({'error': 'An unexpected error occurred'}), 500
|
|
382
|
+
|
|
383
|
+
# Use RFC 7807 Problem Details
|
|
384
|
+
@app.errorhandler(404)
|
|
385
|
+
def not_found(e):
|
|
386
|
+
return jsonify({
|
|
387
|
+
'type': 'https://example.com/problems/not-found',
|
|
388
|
+
'title': 'Resource Not Found',
|
|
389
|
+
'status': 404,
|
|
390
|
+
'detail': 'The requested resource was not found'
|
|
391
|
+
}), 404
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Security Headers
|
|
397
|
+
|
|
398
|
+
```python
|
|
399
|
+
@app.after_request
|
|
400
|
+
def add_security_headers(response):
|
|
401
|
+
# Prevent caching of sensitive data
|
|
402
|
+
if request.endpoint in SENSITIVE_ENDPOINTS:
|
|
403
|
+
response.headers['Cache-Control'] = 'no-store'
|
|
404
|
+
|
|
405
|
+
response.headers['X-Content-Type-Options'] = 'nosniff'
|
|
406
|
+
response.headers['X-Frame-Options'] = 'DENY'
|
|
407
|
+
response.headers['Content-Security-Policy'] = "default-src 'none'"
|
|
408
|
+
|
|
409
|
+
return response
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## CORS Configuration
|
|
415
|
+
|
|
416
|
+
```python
|
|
417
|
+
# VULNERABLE: Allow all origins
|
|
418
|
+
CORS(app, origins='*')
|
|
419
|
+
|
|
420
|
+
# VULNERABLE: Reflect origin header
|
|
421
|
+
@app.after_request
|
|
422
|
+
def add_cors(response):
|
|
423
|
+
response.headers['Access-Control-Allow-Origin'] = request.headers.get('Origin')
|
|
424
|
+
return response
|
|
425
|
+
|
|
426
|
+
# SAFE: Explicit allowlist
|
|
427
|
+
CORS(app, origins=[
|
|
428
|
+
'https://app.example.com',
|
|
429
|
+
'https://admin.example.com'
|
|
430
|
+
], supports_credentials=True)
|
|
431
|
+
|
|
432
|
+
# SAFE: Dynamic with validation
|
|
433
|
+
ALLOWED_ORIGINS = {'https://app.example.com', 'https://admin.example.com'}
|
|
434
|
+
|
|
435
|
+
@app.after_request
|
|
436
|
+
def add_cors(response):
|
|
437
|
+
origin = request.headers.get('Origin')
|
|
438
|
+
if origin in ALLOWED_ORIGINS:
|
|
439
|
+
response.headers['Access-Control-Allow-Origin'] = origin
|
|
440
|
+
response.headers['Access-Control-Allow-Credentials'] = 'true'
|
|
441
|
+
return response
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## HTTP Methods
|
|
447
|
+
|
|
448
|
+
```python
|
|
449
|
+
# VULNERABLE: Method not enforced
|
|
450
|
+
@app.route('/api/users', methods=['GET', 'POST', 'PUT', 'DELETE'])
|
|
451
|
+
def users():
|
|
452
|
+
pass
|
|
453
|
+
|
|
454
|
+
# SAFE: Explicit method handling
|
|
455
|
+
@app.route('/api/users', methods=['GET'])
|
|
456
|
+
def list_users():
|
|
457
|
+
pass
|
|
458
|
+
|
|
459
|
+
@app.route('/api/users', methods=['POST'])
|
|
460
|
+
@require_auth
|
|
461
|
+
def create_user():
|
|
462
|
+
pass
|
|
463
|
+
|
|
464
|
+
# Return 405 for unsupported methods
|
|
465
|
+
@app.errorhandler(405)
|
|
466
|
+
def method_not_allowed(e):
|
|
467
|
+
return jsonify({'error': 'Method not allowed'}), 405
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Grep Patterns for Detection
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
# Missing authentication
|
|
476
|
+
grep -rn "@app\.route\|@router\." --include="*.py" | grep -v "@require_auth\|@login_required"
|
|
477
|
+
|
|
478
|
+
# Returning all fields
|
|
479
|
+
grep -rn "to_dict()\|__dict__\|serialize" --include="*.py"
|
|
480
|
+
|
|
481
|
+
# Mass assignment
|
|
482
|
+
grep -rn "\*\*request\.\|update(\*\*\|create(\*\*" --include="*.py"
|
|
483
|
+
|
|
484
|
+
# Missing rate limiting
|
|
485
|
+
grep -rn "login\|password\|reset" --include="*.py" | grep "route" | grep -v "limiter\|rate"
|
|
486
|
+
|
|
487
|
+
# GraphQL introspection
|
|
488
|
+
grep -rn "__schema\|introspection" --include="*.py"
|
|
489
|
+
|
|
490
|
+
# CORS wildcards
|
|
491
|
+
grep -rn "origins.*\*\|Access-Control-Allow-Origin.*\*" --include="*.py"
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## Testing Checklist
|
|
497
|
+
|
|
498
|
+
- [ ] All endpoints require authentication (except public ones)
|
|
499
|
+
- [ ] Authorization checked for every request
|
|
500
|
+
- [ ] Input validation on all parameters
|
|
501
|
+
- [ ] Response filtering (no sensitive data exposure)
|
|
502
|
+
- [ ] Rate limiting on authentication endpoints
|
|
503
|
+
- [ ] Rate limiting on resource-intensive endpoints
|
|
504
|
+
- [ ] Mass assignment prevented (field allowlists)
|
|
505
|
+
- [ ] Proper error handling (no information leakage)
|
|
506
|
+
- [ ] Security headers configured
|
|
507
|
+
- [ ] CORS properly configured
|
|
508
|
+
- [ ] HTTP methods restricted
|
|
509
|
+
- [ ] GraphQL depth/cost limiting (if applicable)
|
|
510
|
+
- [ ] GraphQL introspection disabled in production
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## References
|
|
515
|
+
|
|
516
|
+
- [OWASP REST Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html)
|
|
517
|
+
- [OWASP GraphQL Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html)
|
|
518
|
+
- [OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
|
|
519
|
+
- [CWE-285: Improper Authorization](https://cwe.mitre.org/data/definitions/285.html)
|