@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,415 +1,415 @@
|
|
|
1
|
-
# Server-Side Request Forgery (SSRF) Prevention Reference
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
SSRF vulnerabilities allow attackers to induce the server-side application to make HTTP requests to an arbitrary domain of the attacker's choosing. This can be used to:
|
|
6
|
-
|
|
7
|
-
- Access internal services not exposed to the internet
|
|
8
|
-
- Read cloud metadata (AWS, GCP, Azure credentials)
|
|
9
|
-
- Scan internal networks
|
|
10
|
-
- Bypass firewalls and access controls
|
|
11
|
-
- Exploit internal services with known vulnerabilities
|
|
12
|
-
|
|
13
|
-
## Attack Scenarios
|
|
14
|
-
|
|
15
|
-
### Cloud Metadata Access (AWS)
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
# Attacker provides URL:
|
|
19
|
-
http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name
|
|
20
|
-
|
|
21
|
-
# Server fetches and returns AWS credentials:
|
|
22
|
-
{
|
|
23
|
-
"AccessKeyId": "ASIA...",
|
|
24
|
-
"SecretAccessKey": "...",
|
|
25
|
-
"Token": "..."
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
### Internal Service Access
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
# Attacker provides URL:
|
|
33
|
-
http://localhost:8080/admin/delete-all
|
|
34
|
-
http://internal-service.local/sensitive-data
|
|
35
|
-
|
|
36
|
-
# Server makes request to internal service that trusts localhost
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Port Scanning
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
# Attacker probes internal network:
|
|
43
|
-
http://192.168.1.1:22 # SSH
|
|
44
|
-
http://192.168.1.1:3306 # MySQL
|
|
45
|
-
http://192.168.1.1:6379 # Redis
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## Prevention Strategies
|
|
51
|
-
|
|
52
|
-
### 1. Input Validation (Allowlist)
|
|
53
|
-
|
|
54
|
-
**Preferred when target hosts are known.**
|
|
55
|
-
|
|
56
|
-
```python
|
|
57
|
-
# VULNERABLE: No validation
|
|
58
|
-
def fetch_url(url):
|
|
59
|
-
return requests.get(url).content
|
|
60
|
-
|
|
61
|
-
# SAFE: Allowlist of permitted domains
|
|
62
|
-
ALLOWED_DOMAINS = {'api.example.com', 'cdn.example.com'}
|
|
63
|
-
|
|
64
|
-
def fetch_url(url):
|
|
65
|
-
parsed = urlparse(url)
|
|
66
|
-
|
|
67
|
-
# Validate scheme
|
|
68
|
-
if parsed.scheme not in ('http', 'https'):
|
|
69
|
-
raise ValueError("Invalid URL scheme")
|
|
70
|
-
|
|
71
|
-
# Validate domain against allowlist
|
|
72
|
-
if parsed.hostname not in ALLOWED_DOMAINS:
|
|
73
|
-
raise ValueError("Domain not allowed")
|
|
74
|
-
|
|
75
|
-
return requests.get(url).content
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### 2. Block Internal Networks (Denylist)
|
|
79
|
-
|
|
80
|
-
**Additional defense layer when allowlist isn't practical.**
|
|
81
|
-
|
|
82
|
-
```python
|
|
83
|
-
import ipaddress
|
|
84
|
-
import socket
|
|
85
|
-
|
|
86
|
-
BLOCKED_RANGES = [
|
|
87
|
-
ipaddress.ip_network('127.0.0.0/8'), # Loopback
|
|
88
|
-
ipaddress.ip_network('10.0.0.0/8'), # Private
|
|
89
|
-
ipaddress.ip_network('172.16.0.0/12'), # Private
|
|
90
|
-
ipaddress.ip_network('192.168.0.0/16'), # Private
|
|
91
|
-
ipaddress.ip_network('169.254.0.0/16'), # Link-local (metadata)
|
|
92
|
-
ipaddress.ip_network('0.0.0.0/8'), # Current network
|
|
93
|
-
ipaddress.ip_network('100.64.0.0/10'), # Shared address space
|
|
94
|
-
ipaddress.ip_network('192.0.0.0/24'), # IETF Protocol
|
|
95
|
-
ipaddress.ip_network('192.0.2.0/24'), # Documentation
|
|
96
|
-
ipaddress.ip_network('198.51.100.0/24'), # Documentation
|
|
97
|
-
ipaddress.ip_network('203.0.113.0/24'), # Documentation
|
|
98
|
-
ipaddress.ip_network('224.0.0.0/4'), # Multicast
|
|
99
|
-
ipaddress.ip_network('240.0.0.0/4'), # Reserved
|
|
100
|
-
]
|
|
101
|
-
|
|
102
|
-
def is_internal_ip(ip_str):
|
|
103
|
-
try:
|
|
104
|
-
ip = ipaddress.ip_address(ip_str)
|
|
105
|
-
return any(ip in network for network in BLOCKED_RANGES)
|
|
106
|
-
except ValueError:
|
|
107
|
-
return True # Invalid IP, block it
|
|
108
|
-
|
|
109
|
-
def validate_url(url):
|
|
110
|
-
parsed = urlparse(url)
|
|
111
|
-
|
|
112
|
-
# Validate scheme
|
|
113
|
-
if parsed.scheme not in ('http', 'https'):
|
|
114
|
-
raise ValueError("Invalid URL scheme")
|
|
115
|
-
|
|
116
|
-
# Resolve hostname to IP
|
|
117
|
-
hostname = parsed.hostname
|
|
118
|
-
if not hostname:
|
|
119
|
-
raise ValueError("Invalid URL")
|
|
120
|
-
|
|
121
|
-
# Check for IP address directly in URL
|
|
122
|
-
try:
|
|
123
|
-
ip = ipaddress.ip_address(hostname)
|
|
124
|
-
if is_internal_ip(str(ip)):
|
|
125
|
-
raise ValueError("Internal IP addresses not allowed")
|
|
126
|
-
except ValueError:
|
|
127
|
-
# It's a hostname, resolve it
|
|
128
|
-
try:
|
|
129
|
-
ip = socket.gethostbyname(hostname)
|
|
130
|
-
if is_internal_ip(ip):
|
|
131
|
-
raise ValueError("Domain resolves to internal IP")
|
|
132
|
-
except socket.gaierror:
|
|
133
|
-
raise ValueError("Could not resolve hostname")
|
|
134
|
-
|
|
135
|
-
return True
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### 3. Disable Redirects
|
|
139
|
-
|
|
140
|
-
```python
|
|
141
|
-
# VULNERABLE: Follows redirects (can bypass IP checks)
|
|
142
|
-
response = requests.get(url, allow_redirects=True)
|
|
143
|
-
# Attacker: http://attacker.com/redirect -> http://169.254.169.254/
|
|
144
|
-
|
|
145
|
-
# SAFE: Don't follow redirects automatically
|
|
146
|
-
response = requests.get(url, allow_redirects=False)
|
|
147
|
-
|
|
148
|
-
# If redirects needed, validate each location
|
|
149
|
-
def safe_fetch(url, max_redirects=5):
|
|
150
|
-
for _ in range(max_redirects):
|
|
151
|
-
validate_url(url) # Validate before each request
|
|
152
|
-
response = requests.get(url, allow_redirects=False)
|
|
153
|
-
|
|
154
|
-
if response.status_code in (301, 302, 303, 307, 308):
|
|
155
|
-
url = response.headers.get('Location')
|
|
156
|
-
if not url:
|
|
157
|
-
raise ValueError("Redirect without Location")
|
|
158
|
-
continue
|
|
159
|
-
|
|
160
|
-
return response
|
|
161
|
-
|
|
162
|
-
raise ValueError("Too many redirects")
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### 4. DNS Rebinding Protection
|
|
166
|
-
|
|
167
|
-
```python
|
|
168
|
-
import socket
|
|
169
|
-
import time
|
|
170
|
-
|
|
171
|
-
def safe_fetch_with_dns_pinning(url):
|
|
172
|
-
parsed = urlparse(url)
|
|
173
|
-
hostname = parsed.hostname
|
|
174
|
-
|
|
175
|
-
# Resolve DNS and pin the IP
|
|
176
|
-
ip = socket.gethostbyname(hostname)
|
|
177
|
-
|
|
178
|
-
# Validate IP is not internal
|
|
179
|
-
if is_internal_ip(ip):
|
|
180
|
-
raise ValueError("Internal IP not allowed")
|
|
181
|
-
|
|
182
|
-
# Make request directly to IP with Host header
|
|
183
|
-
# This prevents DNS rebinding attacks
|
|
184
|
-
modified_url = url.replace(hostname, ip)
|
|
185
|
-
headers = {'Host': hostname}
|
|
186
|
-
|
|
187
|
-
response = requests.get(
|
|
188
|
-
modified_url,
|
|
189
|
-
headers=headers,
|
|
190
|
-
allow_redirects=False,
|
|
191
|
-
verify=True # Still verify TLS with original hostname
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
return response
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### 5. Cloud Metadata Protection
|
|
198
|
-
|
|
199
|
-
#### AWS IMDSv2
|
|
200
|
-
|
|
201
|
-
```bash
|
|
202
|
-
# Require IMDSv2 (token-based) - mitigates SSRF
|
|
203
|
-
aws ec2 modify-instance-metadata-options \
|
|
204
|
-
--instance-id i-1234567890abcdef0 \
|
|
205
|
-
--http-tokens required \
|
|
206
|
-
--http-endpoint enabled
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
```python
|
|
210
|
-
# With IMDSv2, attacker would need two requests:
|
|
211
|
-
# 1. PUT to get token (SSRF usually only does GET)
|
|
212
|
-
# 2. GET with token in header
|
|
213
|
-
|
|
214
|
-
# Block metadata IP regardless
|
|
215
|
-
if '169.254.169.254' in url or '169.254.170.2' in url:
|
|
216
|
-
raise ValueError("Metadata endpoints not allowed")
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
#### GCP
|
|
220
|
-
|
|
221
|
-
```python
|
|
222
|
-
# Block GCP metadata
|
|
223
|
-
BLOCKED_HOSTS = [
|
|
224
|
-
'metadata.google.internal',
|
|
225
|
-
'metadata.google.com',
|
|
226
|
-
'169.254.169.254'
|
|
227
|
-
]
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
#### Azure
|
|
231
|
-
|
|
232
|
-
```python
|
|
233
|
-
# Block Azure metadata
|
|
234
|
-
BLOCKED_HOSTS = [
|
|
235
|
-
'169.254.169.254',
|
|
236
|
-
'management.azure.com'
|
|
237
|
-
]
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## Framework-Specific Mitigations
|
|
243
|
-
|
|
244
|
-
### Python (requests)
|
|
245
|
-
|
|
246
|
-
```python
|
|
247
|
-
from urllib.parse import urlparse
|
|
248
|
-
import requests
|
|
249
|
-
|
|
250
|
-
class SafeRequests:
|
|
251
|
-
@staticmethod
|
|
252
|
-
def get(url, **kwargs):
|
|
253
|
-
validate_url(url)
|
|
254
|
-
kwargs['allow_redirects'] = False
|
|
255
|
-
kwargs['timeout'] = (5, 30) # Connect and read timeout
|
|
256
|
-
return requests.get(url, **kwargs)
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### Node.js
|
|
260
|
-
|
|
261
|
-
```javascript
|
|
262
|
-
const axios = require('axios');
|
|
263
|
-
const url = require('url');
|
|
264
|
-
const dns = require('dns').promises;
|
|
265
|
-
|
|
266
|
-
async function safeFetch(targetUrl) {
|
|
267
|
-
const parsed = new URL(targetUrl);
|
|
268
|
-
|
|
269
|
-
// Validate scheme
|
|
270
|
-
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
|
271
|
-
throw new Error('Invalid scheme');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Resolve and check IP
|
|
275
|
-
const addresses = await dns.lookup(parsed.hostname);
|
|
276
|
-
if (isInternalIP(addresses.address)) {
|
|
277
|
-
throw new Error('Internal IP not allowed');
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return axios.get(targetUrl, {
|
|
281
|
-
maxRedirects: 0,
|
|
282
|
-
timeout: 30000
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
### Java
|
|
288
|
-
|
|
289
|
-
```java
|
|
290
|
-
public class SafeURLConnection {
|
|
291
|
-
private static final Set<String> ALLOWED_PROTOCOLS = Set.of("http", "https");
|
|
292
|
-
|
|
293
|
-
public static URLConnection openConnection(String urlString) throws IOException {
|
|
294
|
-
URL url = new URL(urlString);
|
|
295
|
-
|
|
296
|
-
if (!ALLOWED_PROTOCOLS.contains(url.getProtocol())) {
|
|
297
|
-
throw new SecurityException("Protocol not allowed");
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
InetAddress address = InetAddress.getByName(url.getHost());
|
|
301
|
-
if (isInternalIP(address)) {
|
|
302
|
-
throw new SecurityException("Internal IP not allowed");
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
306
|
-
connection.setInstanceFollowRedirects(false);
|
|
307
|
-
connection.setConnectTimeout(5000);
|
|
308
|
-
connection.setReadTimeout(30000);
|
|
309
|
-
|
|
310
|
-
return connection;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
---
|
|
316
|
-
|
|
317
|
-
## Common Bypass Techniques to Block
|
|
318
|
-
|
|
319
|
-
### URL Encoding
|
|
320
|
-
|
|
321
|
-
```python
|
|
322
|
-
# Bypasses:
|
|
323
|
-
http://169.254.169.254/ # Normal
|
|
324
|
-
http://169%2e254%2e169%2e254/ # URL encoded dots
|
|
325
|
-
http://0251.0376.0251.0376/ # Octal
|
|
326
|
-
http://0xa9fea9fe/ # Hex
|
|
327
|
-
http://2852039166/ # Decimal
|
|
328
|
-
|
|
329
|
-
# Defense: Normalize and decode URL before validation
|
|
330
|
-
from urllib.parse import unquote
|
|
331
|
-
|
|
332
|
-
def normalize_url(url):
|
|
333
|
-
return unquote(url)
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### DNS Rebinding
|
|
337
|
-
|
|
338
|
-
```python
|
|
339
|
-
# Attack: Domain initially resolves to public IP, then internal IP
|
|
340
|
-
# First request: attacker.com -> 1.2.3.4 (passes validation)
|
|
341
|
-
# DNS changes: attacker.com -> 192.168.1.1
|
|
342
|
-
# Second request goes to internal IP
|
|
343
|
-
|
|
344
|
-
# Defense: Pin DNS resolution and re-validate
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
### IPv6
|
|
348
|
-
|
|
349
|
-
```python
|
|
350
|
-
# Bypasses:
|
|
351
|
-
http://[::1]/ # localhost
|
|
352
|
-
http://[::ffff:127.0.0.1]/ # IPv4-mapped IPv6
|
|
353
|
-
http://[0:0:0:0:0:ffff:169.254.169.254]/
|
|
354
|
-
|
|
355
|
-
# Defense: Check both IPv4 and IPv6 ranges
|
|
356
|
-
BLOCKED_RANGES.extend([
|
|
357
|
-
ipaddress.ip_network('::1/128'), # IPv6 loopback
|
|
358
|
-
ipaddress.ip_network('fc00::/7'), # IPv6 private
|
|
359
|
-
ipaddress.ip_network('fe80::/10'), # IPv6 link-local
|
|
360
|
-
])
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
### Alternate Representations
|
|
364
|
-
|
|
365
|
-
```python
|
|
366
|
-
# localhost alternatives:
|
|
367
|
-
localhost
|
|
368
|
-
127.0.0.1
|
|
369
|
-
127.0.0.2 # Any 127.x.x.x is loopback
|
|
370
|
-
2130706433 # Decimal for 127.0.0.1
|
|
371
|
-
0x7f000001 # Hex
|
|
372
|
-
0177.0.0.1 # Octal
|
|
373
|
-
127.1 # Short form
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
---
|
|
377
|
-
|
|
378
|
-
## Grep Patterns for Detection
|
|
379
|
-
|
|
380
|
-
```bash
|
|
381
|
-
# URL fetching functions
|
|
382
|
-
grep -rn "requests\.get\|requests\.post\|urllib\.request\|urlopen\|fetch\|axios" --include="*.py" --include="*.js"
|
|
383
|
-
|
|
384
|
-
# URL from user input
|
|
385
|
-
grep -rn "request\.args\|request\.form\|request\.json\|req\.query\|req\.body" --include="*.py" --include="*.js" | grep -i "url"
|
|
386
|
-
|
|
387
|
-
# Potential SSRF sinks
|
|
388
|
-
grep -rn "curl_exec\|file_get_contents\|fopen\|readfile" --include="*.php"
|
|
389
|
-
|
|
390
|
-
# Missing validation
|
|
391
|
-
grep -rn "requests\.get(url\|fetch(url" --include="*.py" --include="*.js"
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
---
|
|
395
|
-
|
|
396
|
-
## Testing Checklist
|
|
397
|
-
|
|
398
|
-
- [ ] User-controlled URLs validated against allowlist
|
|
399
|
-
- [ ] Internal IP ranges blocked (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
|
|
400
|
-
- [ ] Cloud metadata IPs blocked (169.254.169.254)
|
|
401
|
-
- [ ] IPv6 internal addresses blocked
|
|
402
|
-
- [ ] URL redirects not followed blindly
|
|
403
|
-
- [ ] DNS rebinding protected against
|
|
404
|
-
- [ ] URL encoding/alternate representations handled
|
|
405
|
-
- [ ] IMDSv2 required (AWS environments)
|
|
406
|
-
- [ ] Timeouts configured to prevent DoS
|
|
407
|
-
|
|
408
|
-
---
|
|
409
|
-
|
|
410
|
-
## References
|
|
411
|
-
|
|
412
|
-
- [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
|
|
413
|
-
- [CWE-918: Server-Side Request Forgery](https://cwe.mitre.org/data/definitions/918.html)
|
|
414
|
-
- [AWS IMDSv2 Documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html)
|
|
415
|
-
- [PortSwigger SSRF Guide](https://portswigger.net/web-security/ssrf)
|
|
1
|
+
# Server-Side Request Forgery (SSRF) Prevention Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
SSRF vulnerabilities allow attackers to induce the server-side application to make HTTP requests to an arbitrary domain of the attacker's choosing. This can be used to:
|
|
6
|
+
|
|
7
|
+
- Access internal services not exposed to the internet
|
|
8
|
+
- Read cloud metadata (AWS, GCP, Azure credentials)
|
|
9
|
+
- Scan internal networks
|
|
10
|
+
- Bypass firewalls and access controls
|
|
11
|
+
- Exploit internal services with known vulnerabilities
|
|
12
|
+
|
|
13
|
+
## Attack Scenarios
|
|
14
|
+
|
|
15
|
+
### Cloud Metadata Access (AWS)
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Attacker provides URL:
|
|
19
|
+
http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name
|
|
20
|
+
|
|
21
|
+
# Server fetches and returns AWS credentials:
|
|
22
|
+
{
|
|
23
|
+
"AccessKeyId": "ASIA...",
|
|
24
|
+
"SecretAccessKey": "...",
|
|
25
|
+
"Token": "..."
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Internal Service Access
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Attacker provides URL:
|
|
33
|
+
http://localhost:8080/admin/delete-all
|
|
34
|
+
http://internal-service.local/sensitive-data
|
|
35
|
+
|
|
36
|
+
# Server makes request to internal service that trusts localhost
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Port Scanning
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Attacker probes internal network:
|
|
43
|
+
http://192.168.1.1:22 # SSH
|
|
44
|
+
http://192.168.1.1:3306 # MySQL
|
|
45
|
+
http://192.168.1.1:6379 # Redis
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Prevention Strategies
|
|
51
|
+
|
|
52
|
+
### 1. Input Validation (Allowlist)
|
|
53
|
+
|
|
54
|
+
**Preferred when target hosts are known.**
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
# VULNERABLE: No validation
|
|
58
|
+
def fetch_url(url):
|
|
59
|
+
return requests.get(url).content
|
|
60
|
+
|
|
61
|
+
# SAFE: Allowlist of permitted domains
|
|
62
|
+
ALLOWED_DOMAINS = {'api.example.com', 'cdn.example.com'}
|
|
63
|
+
|
|
64
|
+
def fetch_url(url):
|
|
65
|
+
parsed = urlparse(url)
|
|
66
|
+
|
|
67
|
+
# Validate scheme
|
|
68
|
+
if parsed.scheme not in ('http', 'https'):
|
|
69
|
+
raise ValueError("Invalid URL scheme")
|
|
70
|
+
|
|
71
|
+
# Validate domain against allowlist
|
|
72
|
+
if parsed.hostname not in ALLOWED_DOMAINS:
|
|
73
|
+
raise ValueError("Domain not allowed")
|
|
74
|
+
|
|
75
|
+
return requests.get(url).content
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2. Block Internal Networks (Denylist)
|
|
79
|
+
|
|
80
|
+
**Additional defense layer when allowlist isn't practical.**
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
import ipaddress
|
|
84
|
+
import socket
|
|
85
|
+
|
|
86
|
+
BLOCKED_RANGES = [
|
|
87
|
+
ipaddress.ip_network('127.0.0.0/8'), # Loopback
|
|
88
|
+
ipaddress.ip_network('10.0.0.0/8'), # Private
|
|
89
|
+
ipaddress.ip_network('172.16.0.0/12'), # Private
|
|
90
|
+
ipaddress.ip_network('192.168.0.0/16'), # Private
|
|
91
|
+
ipaddress.ip_network('169.254.0.0/16'), # Link-local (metadata)
|
|
92
|
+
ipaddress.ip_network('0.0.0.0/8'), # Current network
|
|
93
|
+
ipaddress.ip_network('100.64.0.0/10'), # Shared address space
|
|
94
|
+
ipaddress.ip_network('192.0.0.0/24'), # IETF Protocol
|
|
95
|
+
ipaddress.ip_network('192.0.2.0/24'), # Documentation
|
|
96
|
+
ipaddress.ip_network('198.51.100.0/24'), # Documentation
|
|
97
|
+
ipaddress.ip_network('203.0.113.0/24'), # Documentation
|
|
98
|
+
ipaddress.ip_network('224.0.0.0/4'), # Multicast
|
|
99
|
+
ipaddress.ip_network('240.0.0.0/4'), # Reserved
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
def is_internal_ip(ip_str):
|
|
103
|
+
try:
|
|
104
|
+
ip = ipaddress.ip_address(ip_str)
|
|
105
|
+
return any(ip in network for network in BLOCKED_RANGES)
|
|
106
|
+
except ValueError:
|
|
107
|
+
return True # Invalid IP, block it
|
|
108
|
+
|
|
109
|
+
def validate_url(url):
|
|
110
|
+
parsed = urlparse(url)
|
|
111
|
+
|
|
112
|
+
# Validate scheme
|
|
113
|
+
if parsed.scheme not in ('http', 'https'):
|
|
114
|
+
raise ValueError("Invalid URL scheme")
|
|
115
|
+
|
|
116
|
+
# Resolve hostname to IP
|
|
117
|
+
hostname = parsed.hostname
|
|
118
|
+
if not hostname:
|
|
119
|
+
raise ValueError("Invalid URL")
|
|
120
|
+
|
|
121
|
+
# Check for IP address directly in URL
|
|
122
|
+
try:
|
|
123
|
+
ip = ipaddress.ip_address(hostname)
|
|
124
|
+
if is_internal_ip(str(ip)):
|
|
125
|
+
raise ValueError("Internal IP addresses not allowed")
|
|
126
|
+
except ValueError:
|
|
127
|
+
# It's a hostname, resolve it
|
|
128
|
+
try:
|
|
129
|
+
ip = socket.gethostbyname(hostname)
|
|
130
|
+
if is_internal_ip(ip):
|
|
131
|
+
raise ValueError("Domain resolves to internal IP")
|
|
132
|
+
except socket.gaierror:
|
|
133
|
+
raise ValueError("Could not resolve hostname")
|
|
134
|
+
|
|
135
|
+
return True
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 3. Disable Redirects
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
# VULNERABLE: Follows redirects (can bypass IP checks)
|
|
142
|
+
response = requests.get(url, allow_redirects=True)
|
|
143
|
+
# Attacker: http://attacker.com/redirect -> http://169.254.169.254/
|
|
144
|
+
|
|
145
|
+
# SAFE: Don't follow redirects automatically
|
|
146
|
+
response = requests.get(url, allow_redirects=False)
|
|
147
|
+
|
|
148
|
+
# If redirects needed, validate each location
|
|
149
|
+
def safe_fetch(url, max_redirects=5):
|
|
150
|
+
for _ in range(max_redirects):
|
|
151
|
+
validate_url(url) # Validate before each request
|
|
152
|
+
response = requests.get(url, allow_redirects=False)
|
|
153
|
+
|
|
154
|
+
if response.status_code in (301, 302, 303, 307, 308):
|
|
155
|
+
url = response.headers.get('Location')
|
|
156
|
+
if not url:
|
|
157
|
+
raise ValueError("Redirect without Location")
|
|
158
|
+
continue
|
|
159
|
+
|
|
160
|
+
return response
|
|
161
|
+
|
|
162
|
+
raise ValueError("Too many redirects")
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 4. DNS Rebinding Protection
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
import socket
|
|
169
|
+
import time
|
|
170
|
+
|
|
171
|
+
def safe_fetch_with_dns_pinning(url):
|
|
172
|
+
parsed = urlparse(url)
|
|
173
|
+
hostname = parsed.hostname
|
|
174
|
+
|
|
175
|
+
# Resolve DNS and pin the IP
|
|
176
|
+
ip = socket.gethostbyname(hostname)
|
|
177
|
+
|
|
178
|
+
# Validate IP is not internal
|
|
179
|
+
if is_internal_ip(ip):
|
|
180
|
+
raise ValueError("Internal IP not allowed")
|
|
181
|
+
|
|
182
|
+
# Make request directly to IP with Host header
|
|
183
|
+
# This prevents DNS rebinding attacks
|
|
184
|
+
modified_url = url.replace(hostname, ip)
|
|
185
|
+
headers = {'Host': hostname}
|
|
186
|
+
|
|
187
|
+
response = requests.get(
|
|
188
|
+
modified_url,
|
|
189
|
+
headers=headers,
|
|
190
|
+
allow_redirects=False,
|
|
191
|
+
verify=True # Still verify TLS with original hostname
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return response
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 5. Cloud Metadata Protection
|
|
198
|
+
|
|
199
|
+
#### AWS IMDSv2
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Require IMDSv2 (token-based) - mitigates SSRF
|
|
203
|
+
aws ec2 modify-instance-metadata-options \
|
|
204
|
+
--instance-id i-1234567890abcdef0 \
|
|
205
|
+
--http-tokens required \
|
|
206
|
+
--http-endpoint enabled
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
# With IMDSv2, attacker would need two requests:
|
|
211
|
+
# 1. PUT to get token (SSRF usually only does GET)
|
|
212
|
+
# 2. GET with token in header
|
|
213
|
+
|
|
214
|
+
# Block metadata IP regardless
|
|
215
|
+
if '169.254.169.254' in url or '169.254.170.2' in url:
|
|
216
|
+
raise ValueError("Metadata endpoints not allowed")
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
#### GCP
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
# Block GCP metadata
|
|
223
|
+
BLOCKED_HOSTS = [
|
|
224
|
+
'metadata.google.internal',
|
|
225
|
+
'metadata.google.com',
|
|
226
|
+
'169.254.169.254'
|
|
227
|
+
]
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
#### Azure
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
# Block Azure metadata
|
|
234
|
+
BLOCKED_HOSTS = [
|
|
235
|
+
'169.254.169.254',
|
|
236
|
+
'management.azure.com'
|
|
237
|
+
]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Framework-Specific Mitigations
|
|
243
|
+
|
|
244
|
+
### Python (requests)
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
from urllib.parse import urlparse
|
|
248
|
+
import requests
|
|
249
|
+
|
|
250
|
+
class SafeRequests:
|
|
251
|
+
@staticmethod
|
|
252
|
+
def get(url, **kwargs):
|
|
253
|
+
validate_url(url)
|
|
254
|
+
kwargs['allow_redirects'] = False
|
|
255
|
+
kwargs['timeout'] = (5, 30) # Connect and read timeout
|
|
256
|
+
return requests.get(url, **kwargs)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Node.js
|
|
260
|
+
|
|
261
|
+
```javascript
|
|
262
|
+
const axios = require('axios');
|
|
263
|
+
const url = require('url');
|
|
264
|
+
const dns = require('dns').promises;
|
|
265
|
+
|
|
266
|
+
async function safeFetch(targetUrl) {
|
|
267
|
+
const parsed = new URL(targetUrl);
|
|
268
|
+
|
|
269
|
+
// Validate scheme
|
|
270
|
+
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
|
271
|
+
throw new Error('Invalid scheme');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Resolve and check IP
|
|
275
|
+
const addresses = await dns.lookup(parsed.hostname);
|
|
276
|
+
if (isInternalIP(addresses.address)) {
|
|
277
|
+
throw new Error('Internal IP not allowed');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return axios.get(targetUrl, {
|
|
281
|
+
maxRedirects: 0,
|
|
282
|
+
timeout: 30000
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Java
|
|
288
|
+
|
|
289
|
+
```java
|
|
290
|
+
public class SafeURLConnection {
|
|
291
|
+
private static final Set<String> ALLOWED_PROTOCOLS = Set.of("http", "https");
|
|
292
|
+
|
|
293
|
+
public static URLConnection openConnection(String urlString) throws IOException {
|
|
294
|
+
URL url = new URL(urlString);
|
|
295
|
+
|
|
296
|
+
if (!ALLOWED_PROTOCOLS.contains(url.getProtocol())) {
|
|
297
|
+
throw new SecurityException("Protocol not allowed");
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
InetAddress address = InetAddress.getByName(url.getHost());
|
|
301
|
+
if (isInternalIP(address)) {
|
|
302
|
+
throw new SecurityException("Internal IP not allowed");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
306
|
+
connection.setInstanceFollowRedirects(false);
|
|
307
|
+
connection.setConnectTimeout(5000);
|
|
308
|
+
connection.setReadTimeout(30000);
|
|
309
|
+
|
|
310
|
+
return connection;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Common Bypass Techniques to Block
|
|
318
|
+
|
|
319
|
+
### URL Encoding
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
# Bypasses:
|
|
323
|
+
http://169.254.169.254/ # Normal
|
|
324
|
+
http://169%2e254%2e169%2e254/ # URL encoded dots
|
|
325
|
+
http://0251.0376.0251.0376/ # Octal
|
|
326
|
+
http://0xa9fea9fe/ # Hex
|
|
327
|
+
http://2852039166/ # Decimal
|
|
328
|
+
|
|
329
|
+
# Defense: Normalize and decode URL before validation
|
|
330
|
+
from urllib.parse import unquote
|
|
331
|
+
|
|
332
|
+
def normalize_url(url):
|
|
333
|
+
return unquote(url)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### DNS Rebinding
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
# Attack: Domain initially resolves to public IP, then internal IP
|
|
340
|
+
# First request: attacker.com -> 1.2.3.4 (passes validation)
|
|
341
|
+
# DNS changes: attacker.com -> 192.168.1.1
|
|
342
|
+
# Second request goes to internal IP
|
|
343
|
+
|
|
344
|
+
# Defense: Pin DNS resolution and re-validate
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### IPv6
|
|
348
|
+
|
|
349
|
+
```python
|
|
350
|
+
# Bypasses:
|
|
351
|
+
http://[::1]/ # localhost
|
|
352
|
+
http://[::ffff:127.0.0.1]/ # IPv4-mapped IPv6
|
|
353
|
+
http://[0:0:0:0:0:ffff:169.254.169.254]/
|
|
354
|
+
|
|
355
|
+
# Defense: Check both IPv4 and IPv6 ranges
|
|
356
|
+
BLOCKED_RANGES.extend([
|
|
357
|
+
ipaddress.ip_network('::1/128'), # IPv6 loopback
|
|
358
|
+
ipaddress.ip_network('fc00::/7'), # IPv6 private
|
|
359
|
+
ipaddress.ip_network('fe80::/10'), # IPv6 link-local
|
|
360
|
+
])
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Alternate Representations
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
# localhost alternatives:
|
|
367
|
+
localhost
|
|
368
|
+
127.0.0.1
|
|
369
|
+
127.0.0.2 # Any 127.x.x.x is loopback
|
|
370
|
+
2130706433 # Decimal for 127.0.0.1
|
|
371
|
+
0x7f000001 # Hex
|
|
372
|
+
0177.0.0.1 # Octal
|
|
373
|
+
127.1 # Short form
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Grep Patterns for Detection
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
# URL fetching functions
|
|
382
|
+
grep -rn "requests\.get\|requests\.post\|urllib\.request\|urlopen\|fetch\|axios" --include="*.py" --include="*.js"
|
|
383
|
+
|
|
384
|
+
# URL from user input
|
|
385
|
+
grep -rn "request\.args\|request\.form\|request\.json\|req\.query\|req\.body" --include="*.py" --include="*.js" | grep -i "url"
|
|
386
|
+
|
|
387
|
+
# Potential SSRF sinks
|
|
388
|
+
grep -rn "curl_exec\|file_get_contents\|fopen\|readfile" --include="*.php"
|
|
389
|
+
|
|
390
|
+
# Missing validation
|
|
391
|
+
grep -rn "requests\.get(url\|fetch(url" --include="*.py" --include="*.js"
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Testing Checklist
|
|
397
|
+
|
|
398
|
+
- [ ] User-controlled URLs validated against allowlist
|
|
399
|
+
- [ ] Internal IP ranges blocked (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
|
|
400
|
+
- [ ] Cloud metadata IPs blocked (169.254.169.254)
|
|
401
|
+
- [ ] IPv6 internal addresses blocked
|
|
402
|
+
- [ ] URL redirects not followed blindly
|
|
403
|
+
- [ ] DNS rebinding protected against
|
|
404
|
+
- [ ] URL encoding/alternate representations handled
|
|
405
|
+
- [ ] IMDSv2 required (AWS environments)
|
|
406
|
+
- [ ] Timeouts configured to prevent DoS
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## References
|
|
411
|
+
|
|
412
|
+
- [OWASP SSRF Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
|
|
413
|
+
- [CWE-918: Server-Side Request Forgery](https://cwe.mitre.org/data/definitions/918.html)
|
|
414
|
+
- [AWS IMDSv2 Documentation](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html)
|
|
415
|
+
- [PortSwigger SSRF Guide](https://portswigger.net/web-security/ssrf)
|