@wazir-dev/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,1158 @@
|
|
|
1
|
+
# Cross-Site Scripting (XSS) Security Expertise Module
|
|
2
|
+
|
|
3
|
+
> **Purpose:** Reference module for AI agents during planning and implementation to prevent XSS vulnerabilities.
|
|
4
|
+
> **Last Updated:** 2026-03-08
|
|
5
|
+
> **OWASP Classification:** A03:2021 - Injection | CWE-79
|
|
6
|
+
> **Severity:** High to Critical (CVSS 6.1 - 9.6 depending on context)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Threat Landscape
|
|
11
|
+
|
|
12
|
+
### 1.1 XSS Types
|
|
13
|
+
|
|
14
|
+
**Reflected XSS (Non-Persistent)**
|
|
15
|
+
The malicious script is embedded in a URL or request parameter and reflected back in the server response without proper encoding. The victim must click a crafted link. This remains the most commonly reported XSS variant in bug bounty programs.
|
|
16
|
+
|
|
17
|
+
**Stored XSS (Persistent)**
|
|
18
|
+
The malicious payload is persisted in the application's data store (database, file system, cache) and served to every user who views the affected content. Stored XSS is the most dangerous variant because it requires no victim interaction beyond normal browsing and can affect all users simultaneously.
|
|
19
|
+
|
|
20
|
+
**DOM-Based XSS**
|
|
21
|
+
The vulnerability exists entirely in client-side code. The server response is benign, but client-side JavaScript processes attacker-controlled data (URL fragments, `postMessage` data, `localStorage`) and writes it to a dangerous sink (`innerHTML`, `document.write`). DOM XSS has become the predominant threat in modern single-page applications (SPAs) because the exploit bypasses server-side protections entirely.
|
|
22
|
+
|
|
23
|
+
**Mutation XSS (mXSS)**
|
|
24
|
+
The payload is initially benign and passes sanitization, but the browser's HTML parser mutates the DOM structure during rendering, transforming safe markup into executable script. mXSS exploits the gap between how sanitizers parse HTML and how browsers actually render it. This is an active area of research with new bypasses discovered regularly:
|
|
25
|
+
|
|
26
|
+
- **CVE-2024-47875:** DOMPurify <= 3.1.0 bypass via deep DOM nesting causing node flattening (discovered by @IcesFont, April 2024).
|
|
27
|
+
- **CVE-2025-26791:** DOMPurify < 3.2.4 bypass via regex parsing bug allowing mXSS at render time.
|
|
28
|
+
- **CVE-2024-52595:** lxml_html_clean bypass via `<noscript>` tag interpretation differences between sanitizer and browser.
|
|
29
|
+
|
|
30
|
+
### 1.2 Real-World Breaches
|
|
31
|
+
|
|
32
|
+
**British Airways Magecart Attack (2018)**
|
|
33
|
+
Attackers compromised BA's network via stolen third-party supplier credentials, then injected 22 lines of malicious JavaScript into the payment page. The script copied payment card data (name, address, card number, CVV) to an attacker-controlled domain. 429,612 individuals were affected, including full card details for 244,000 customers. The UK Information Commissioner's Office (ICO) fined BA 20 million GBP. This attack demonstrated how a single JavaScript injection point can enable massive financial data exfiltration.
|
|
34
|
+
*Source: ICO Penalty Notice, October 2020; RiskIQ/Magecart research*
|
|
35
|
+
|
|
36
|
+
**Fortnite Account Takeover (2019)**
|
|
37
|
+
Check Point researchers discovered XSS vulnerabilities in Epic Games' legacy subdomains. An attacker could craft a link that, when clicked, would capture the victim's authentication token via an OAuth flow -- no credentials required. The attacker gained full account access: purchasing in-game currency, accessing contacts, and eavesdropping on conversations. Over 200 million registered accounts were potentially at risk. Epic Games was subsequently hit with a class-action lawsuit.
|
|
38
|
+
*Source: Check Point Research, January 2019*
|
|
39
|
+
|
|
40
|
+
**eBay Stored XSS (2015-2017)**
|
|
41
|
+
eBay allowed sellers to include custom HTML/JavaScript in auction listing descriptions. Attackers injected malicious scripts that redirected buyers to spoofed login pages, stealing credentials. Compromised accounts were then used to create more fraudulent high-value listings (primarily vehicles). Some malicious listings persisted for over six weeks before removal. The vulnerability was repeatedly exploited across multiple years.
|
|
42
|
+
*Source: Netcraft Research, 2017; Bitdefender analysis*
|
|
43
|
+
|
|
44
|
+
### 1.3 Current Trends (2024-2025)
|
|
45
|
+
|
|
46
|
+
- **DOM XSS dominance:** With the shift to SPAs, DOM-based XSS has overtaken reflected XSS as the primary client-side threat vector.
|
|
47
|
+
- **Scale:** 22,254 CVEs were reported by mid-2024 (30% increase from 2023). Microsoft MSRC alone mitigated over 970 XSS cases since January 2024.
|
|
48
|
+
- **Supply chain XSS:** Attackers compromise third-party scripts (analytics, ads, chat widgets) to inject XSS into thousands of sites simultaneously.
|
|
49
|
+
- **Framework escape hatches:** Despite auto-escaping in React/Vue/Angular, developers routinely use unsafe APIs (`dangerouslySetInnerHTML`, `v-html`, `bypassSecurityTrust*`) that reintroduce XSS.
|
|
50
|
+
- **AI-assisted attacks:** 93% of security executives anticipated daily AI-driven attacks by mid-2025, with XSS as a component in 78% of predicted attack vectors.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 2. Core Security Principles
|
|
55
|
+
|
|
56
|
+
### 2.1 Output Encoding (Context-Specific)
|
|
57
|
+
|
|
58
|
+
The single most important XSS defense. Data must be encoded for the specific rendering context:
|
|
59
|
+
|
|
60
|
+
| Context | Encoding Method | Example |
|
|
61
|
+
|---------|----------------|---------|
|
|
62
|
+
| HTML body | HTML entity encoding | `<` `>` `&` `"` `'` |
|
|
63
|
+
| HTML attribute | Attribute encoding + quoting | Always quote attribute values |
|
|
64
|
+
| JavaScript | JavaScript hex/unicode encoding | `\x3C` `\u003C` |
|
|
65
|
+
| URL parameter | Percent encoding | `%3C` `%3E` `%22` |
|
|
66
|
+
| CSS value | CSS hex encoding | `\3C` `\3E` |
|
|
67
|
+
|
|
68
|
+
**Why it matters:** Most XSS vulnerabilities arise not because input was not validated, but because output was not properly encoded for its rendering context. A value safe in HTML body context can be dangerous in a JavaScript string or URL context.
|
|
69
|
+
|
|
70
|
+
### 2.2 Content Security Policy (CSP)
|
|
71
|
+
|
|
72
|
+
CSP is a browser-enforced HTTP header that restricts which scripts can execute, which resources can load, and where data can be sent. It acts as a defense-in-depth layer -- it does not prevent the vulnerability itself but limits the damage an attacker can achieve.
|
|
73
|
+
|
|
74
|
+
**Why it matters:** Even if an XSS vulnerability exists, a strict CSP can prevent the injected script from executing, loading external resources, or exfiltrating data.
|
|
75
|
+
|
|
76
|
+
### 2.3 Trusted Types
|
|
77
|
+
|
|
78
|
+
A browser API (CSP directive) that prevents DOM XSS by requiring all values assigned to dangerous sinks (`innerHTML`, `document.write`) to pass through a developer-defined policy function first. The browser blocks any assignment that has not been processed by a Trusted Types policy.
|
|
79
|
+
|
|
80
|
+
**Why it matters:** Trusted Types shift XSS prevention from post-hoc detection to compile-time/runtime enforcement. They make it impossible to accidentally pass unsanitized strings to dangerous DOM APIs.
|
|
81
|
+
|
|
82
|
+
### 2.4 Input Validation
|
|
83
|
+
|
|
84
|
+
Validate all input against strict allowlists (expected format, length, character set). Input validation alone is NOT sufficient to prevent XSS -- it is a first layer, not a complete defense. Always combine with output encoding.
|
|
85
|
+
|
|
86
|
+
**Why it matters:** Reduces the attack surface by rejecting obviously malicious input early, but cannot catch all encoding-based bypass techniques.
|
|
87
|
+
|
|
88
|
+
### 2.5 HTTPOnly / Secure / SameSite Cookies
|
|
89
|
+
|
|
90
|
+
- **HTTPOnly:** Prevents JavaScript from reading session cookies via `document.cookie`, blocking the most common XSS exploitation path (session hijacking).
|
|
91
|
+
- **Secure:** Ensures cookies are only sent over HTTPS, preventing interception.
|
|
92
|
+
- **SameSite=Lax/Strict:** Limits cross-origin cookie transmission, reducing CSRF and some XSS exploitation chains.
|
|
93
|
+
|
|
94
|
+
**Why it matters:** Even if XSS exists, HTTPOnly cookies prevent attackers from stealing session tokens, significantly limiting impact.
|
|
95
|
+
|
|
96
|
+
### 2.6 Defense-in-Depth Layering
|
|
97
|
+
|
|
98
|
+
No single defense is sufficient. Effective XSS prevention requires:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
Input Validation --> Output Encoding --> CSP --> Trusted Types --> HTTPOnly Cookies
|
|
102
|
+
(first layer) (primary defense) (fallback) (DOM protection) (impact reduction)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 3. Implementation Patterns
|
|
108
|
+
|
|
109
|
+
### 3.1 Context-Specific Output Encoding
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// VULNERABLE: No encoding
|
|
113
|
+
function renderUserName(name: string): string {
|
|
114
|
+
return `<span>${name}</span>`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// SECURE: HTML entity encoding
|
|
118
|
+
function renderUserName(name: string): string {
|
|
119
|
+
return `<span>${htmlEncode(name)}</span>`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function htmlEncode(str: string): string {
|
|
123
|
+
return str
|
|
124
|
+
.replace(/&/g, '&')
|
|
125
|
+
.replace(/</g, '<')
|
|
126
|
+
.replace(/>/g, '>')
|
|
127
|
+
.replace(/"/g, '"')
|
|
128
|
+
.replace(/'/g, ''');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// For JavaScript context
|
|
132
|
+
function jsEncode(str: string): string {
|
|
133
|
+
return str.replace(/[^\w. ]/gi, (c) => {
|
|
134
|
+
return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// For URL parameter context
|
|
139
|
+
function urlEncode(str: string): string {
|
|
140
|
+
return encodeURIComponent(str);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 3.2 Content Security Policy (Nonce-Based)
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// Server-side: Generate unique nonce per request
|
|
148
|
+
import crypto from 'crypto';
|
|
149
|
+
|
|
150
|
+
function generateCSPNonce(): string {
|
|
151
|
+
return crypto.randomBytes(16).toString('base64');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Express middleware example
|
|
155
|
+
function cspMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
156
|
+
const nonce = generateCSPNonce();
|
|
157
|
+
res.locals.cspNonce = nonce;
|
|
158
|
+
|
|
159
|
+
res.setHeader('Content-Security-Policy', [
|
|
160
|
+
`script-src 'nonce-${nonce}' 'strict-dynamic'`,
|
|
161
|
+
`object-src 'none'`,
|
|
162
|
+
`base-uri 'none'`,
|
|
163
|
+
`style-src 'self' 'nonce-${nonce}'`,
|
|
164
|
+
`img-src 'self' data: https:`,
|
|
165
|
+
`font-src 'self'`,
|
|
166
|
+
`connect-src 'self'`,
|
|
167
|
+
`frame-ancestors 'none'`,
|
|
168
|
+
`form-action 'self'`,
|
|
169
|
+
`report-uri /csp-report`,
|
|
170
|
+
`report-to csp-endpoint`,
|
|
171
|
+
].join('; '));
|
|
172
|
+
|
|
173
|
+
next();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// In HTML template, use the nonce on every script tag
|
|
177
|
+
// <script nonce="<%= cspNonce %>">...</script>
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Critical CSP rules:**
|
|
181
|
+
- Never use `unsafe-inline` or `unsafe-eval` in production.
|
|
182
|
+
- Use `strict-dynamic` to allow trusted scripts to load child scripts without individual nonces.
|
|
183
|
+
- Generate a cryptographically random nonce (minimum 128 bits) per request. Never reuse nonces.
|
|
184
|
+
- Deploy in `Content-Security-Policy-Report-Only` first, monitor violations, then enforce.
|
|
185
|
+
- Never accept nonce values from user input or query parameters.
|
|
186
|
+
|
|
187
|
+
### 3.3 Trusted Types Implementation
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// Define a Trusted Types policy
|
|
191
|
+
if (window.trustedTypes && window.trustedTypes.createPolicy) {
|
|
192
|
+
const sanitizerPolicy = trustedTypes.createPolicy('app-sanitizer', {
|
|
193
|
+
createHTML: (input: string): string => {
|
|
194
|
+
return DOMPurify.sanitize(input, {
|
|
195
|
+
RETURN_TRUSTED_TYPE: false,
|
|
196
|
+
});
|
|
197
|
+
},
|
|
198
|
+
createScriptURL: (input: string): string => {
|
|
199
|
+
const url = new URL(input, window.location.origin);
|
|
200
|
+
if (url.origin === window.location.origin) {
|
|
201
|
+
return url.toString();
|
|
202
|
+
}
|
|
203
|
+
throw new TypeError(`Blocked script URL: ${input}`);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// CSP header to enforce Trusted Types
|
|
209
|
+
// Content-Security-Policy: require-trusted-types-for 'script'; trusted-types app-sanitizer
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Deployment strategy:**
|
|
213
|
+
1. Start with report-only: `Content-Security-Policy-Report-Only: require-trusted-types-for 'script'`
|
|
214
|
+
2. Identify all DOM sinks in your application that trigger violations.
|
|
215
|
+
3. Create policies that sanitize input for each sink.
|
|
216
|
+
4. Switch to enforcement mode once all violations are resolved.
|
|
217
|
+
|
|
218
|
+
### 3.4 DOMPurify Sanitization
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import DOMPurify from 'dompurify';
|
|
222
|
+
|
|
223
|
+
// Basic usage -- secure default
|
|
224
|
+
const cleanHTML = DOMPurify.sanitize(dirtyHTML);
|
|
225
|
+
|
|
226
|
+
// Profile-based configuration (recommended starting point)
|
|
227
|
+
const cleanHTML = DOMPurify.sanitize(dirtyHTML, {
|
|
228
|
+
USE_PROFILES: { html: true },
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Strict configuration for user-generated content
|
|
232
|
+
const cleanHTML = DOMPurify.sanitize(dirtyHTML, {
|
|
233
|
+
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br', 'ul', 'ol', 'li', 'a'],
|
|
234
|
+
ALLOWED_ATTR: ['href', 'title', 'target'],
|
|
235
|
+
ALLOW_DATA_ATTR: false,
|
|
236
|
+
ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// CRITICAL: Keep DOMPurify updated -- mXSS bypasses are found regularly
|
|
240
|
+
// CVE-2024-47875 (v3.1.0), CVE-2025-26791 (v3.2.4)
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**DOMPurify anti-patterns to avoid:**
|
|
244
|
+
- Never modify sanitized output after sanitization (voids the protection).
|
|
245
|
+
- Never use overly permissive `ALLOWED_URI_REGEXP` (can allow `javascript:` URIs).
|
|
246
|
+
- Never add `script` or `style` to `ALLOWED_TAGS`.
|
|
247
|
+
- Never pass sanitized HTML through another library that may re-parse it.
|
|
248
|
+
- Always pin and regularly update the DOMPurify version.
|
|
249
|
+
|
|
250
|
+
### 3.5 Framework Auto-Escaping
|
|
251
|
+
|
|
252
|
+
**React (JSX auto-escaping):**
|
|
253
|
+
```tsx
|
|
254
|
+
// SAFE: React auto-escapes JSX expressions
|
|
255
|
+
function UserGreeting({ name }: { name: string }) {
|
|
256
|
+
return <h1>Hello, {name}</h1>; // Auto-escaped
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// VULNERABLE: dangerouslySetInnerHTML bypasses auto-escaping
|
|
260
|
+
function UserBio({ bioHtml }: { bioHtml: string }) {
|
|
261
|
+
return <div dangerouslySetInnerHTML={{ __html: bioHtml }} />; // XSS risk
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// SECURE: Sanitize before using dangerouslySetInnerHTML
|
|
265
|
+
function UserBio({ bioHtml }: { bioHtml: string }) {
|
|
266
|
+
const cleanHtml = DOMPurify.sanitize(bioHtml, {
|
|
267
|
+
USE_PROFILES: { html: true },
|
|
268
|
+
});
|
|
269
|
+
return <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />;
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**Vue (template auto-escaping):**
|
|
274
|
+
```vue
|
|
275
|
+
<!-- SAFE: Double-brace interpolation auto-escapes -->
|
|
276
|
+
<template>
|
|
277
|
+
<p>{{ userInput }}</p>
|
|
278
|
+
</template>
|
|
279
|
+
|
|
280
|
+
<!-- VULNERABLE: v-html renders raw HTML -->
|
|
281
|
+
<template>
|
|
282
|
+
<div v-html="userContent"></div>
|
|
283
|
+
</template>
|
|
284
|
+
|
|
285
|
+
<!-- SECURE: Sanitize in computed property -->
|
|
286
|
+
<script setup lang="ts">
|
|
287
|
+
import DOMPurify from 'dompurify';
|
|
288
|
+
import { computed } from 'vue';
|
|
289
|
+
|
|
290
|
+
const props = defineProps<{ userContent: string }>();
|
|
291
|
+
const safeContent = computed(() => DOMPurify.sanitize(props.userContent));
|
|
292
|
+
</script>
|
|
293
|
+
<template>
|
|
294
|
+
<div v-html="safeContent"></div>
|
|
295
|
+
</template>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**Angular (DomSanitizer):**
|
|
299
|
+
```typescript
|
|
300
|
+
// Angular sanitizes by default. NEVER bypass without sanitization:
|
|
301
|
+
// VULNERABLE:
|
|
302
|
+
this.domSanitizer.bypassSecurityTrustHtml(userInput);
|
|
303
|
+
|
|
304
|
+
// SECURE: Use DOMPurify before bypassing Angular sanitizer
|
|
305
|
+
import DOMPurify from 'dompurify';
|
|
306
|
+
|
|
307
|
+
const clean = DOMPurify.sanitize(userInput);
|
|
308
|
+
this.safeHtml = this.domSanitizer.bypassSecurityTrustHtml(clean);
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## 4. Vulnerability Catalog
|
|
314
|
+
|
|
315
|
+
### V-01: Reflected XSS via URL Parameters
|
|
316
|
+
**CWE:** CWE-79 (Reflected)
|
|
317
|
+
**Risk:** High
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// VULNERABLE: Search query reflected without encoding
|
|
321
|
+
app.get('/search', (req, res) => {
|
|
322
|
+
const query = req.query.q;
|
|
323
|
+
res.send(`<h1>Results for: ${query}</h1>`);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// SECURE: HTML-encode before reflecting
|
|
327
|
+
app.get('/search', (req, res) => {
|
|
328
|
+
const query = htmlEncode(String(req.query.q || ''));
|
|
329
|
+
res.send(`<h1>Results for: ${query}</h1>`);
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### V-02: Stored XSS in User-Generated Content
|
|
334
|
+
**CWE:** CWE-79 (Stored)
|
|
335
|
+
**Risk:** Critical
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
// VULNERABLE: Storing and rendering raw user HTML
|
|
339
|
+
async function saveComment(content: string): Promise<void> {
|
|
340
|
+
await db.comments.insert({ content }); // Raw HTML stored
|
|
341
|
+
}
|
|
342
|
+
function renderComment(content: string): string {
|
|
343
|
+
return `<div class="comment">${content}</div>`; // Rendered unsanitized
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// SECURE: Sanitize on output (preferred) or on input
|
|
347
|
+
function renderComment(content: string): string {
|
|
348
|
+
const clean = DOMPurify.sanitize(content, { USE_PROFILES: { html: true } });
|
|
349
|
+
return `<div class="comment">${clean}</div>`;
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### V-03: DOM XSS via innerHTML
|
|
354
|
+
**CWE:** CWE-79 (DOM-based)
|
|
355
|
+
**Risk:** High
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// VULNERABLE: Setting innerHTML with user-controlled data
|
|
359
|
+
const userInput = new URLSearchParams(location.search).get('name');
|
|
360
|
+
document.getElementById('greeting')!.innerHTML = `Hello, ${userInput}`;
|
|
361
|
+
|
|
362
|
+
// SECURE: Use textContent for text-only rendering
|
|
363
|
+
document.getElementById('greeting')!.textContent = `Hello, ${userInput}`;
|
|
364
|
+
|
|
365
|
+
// SECURE: If HTML is needed, sanitize first
|
|
366
|
+
document.getElementById('greeting')!.innerHTML =
|
|
367
|
+
`Hello, ${DOMPurify.sanitize(userInput)}`;
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### V-04: DOM XSS via document.write
|
|
371
|
+
**CWE:** CWE-79 (DOM-based)
|
|
372
|
+
**Risk:** High
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
// VULNERABLE: document.write with user data
|
|
376
|
+
document.write('<div>' + location.hash.slice(1) + '</div>');
|
|
377
|
+
|
|
378
|
+
// SECURE: Never use document.write. Use DOM APIs instead.
|
|
379
|
+
const container = document.createElement('div');
|
|
380
|
+
container.textContent = decodeURIComponent(location.hash.slice(1));
|
|
381
|
+
document.body.appendChild(container);
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### V-05: XSS via javascript: URLs
|
|
385
|
+
**CWE:** CWE-79
|
|
386
|
+
**Risk:** High
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
// VULNERABLE: User-controlled href without protocol validation
|
|
390
|
+
function UserLink({ url }: { url: string }) {
|
|
391
|
+
return <a href={url}>Visit</a>;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// SECURE: Validate URL protocol
|
|
395
|
+
function UserLink({ url }: { url: string }) {
|
|
396
|
+
const safeUrl = sanitizeUrl(url);
|
|
397
|
+
return <a href={safeUrl}>Visit</a>;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function sanitizeUrl(url: string): string {
|
|
401
|
+
try {
|
|
402
|
+
const parsed = new URL(url);
|
|
403
|
+
if (['http:', 'https:', 'mailto:'].includes(parsed.protocol)) {
|
|
404
|
+
return parsed.toString();
|
|
405
|
+
}
|
|
406
|
+
} catch {
|
|
407
|
+
// Invalid URL
|
|
408
|
+
}
|
|
409
|
+
return '#';
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### V-06: XSS in SVG and MathML Elements
|
|
414
|
+
**CWE:** CWE-79
|
|
415
|
+
**Risk:** High
|
|
416
|
+
**Reference:** CVE-2025-66412 (Angular SVG/MathML XSS)
|
|
417
|
+
|
|
418
|
+
SVG elements can contain `<script>` tags and event handlers. MathML elements can reference external resources. Both can be used as XSS vectors when user content is rendered as SVG/MathML.
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
// VULNERABLE: Rendering user-provided SVG
|
|
422
|
+
element.innerHTML = userSvgContent;
|
|
423
|
+
|
|
424
|
+
// SECURE: Sanitize with SVG-aware configuration
|
|
425
|
+
const cleanSvg = DOMPurify.sanitize(userSvgContent, {
|
|
426
|
+
USE_PROFILES: { svg: true },
|
|
427
|
+
ADD_TAGS: ['use'],
|
|
428
|
+
FORBID_TAGS: ['script'],
|
|
429
|
+
FORBID_ATTR: ['onload', 'onerror', 'onclick'],
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### V-07: Mutation XSS (mXSS) Through Sanitizer Bypass
|
|
434
|
+
**CWE:** CWE-79
|
|
435
|
+
**Risk:** Critical
|
|
436
|
+
|
|
437
|
+
mXSS occurs when the browser's parser mutates sanitized HTML into executable content. The sanitizer sees safe markup, but the browser renders it differently.
|
|
438
|
+
|
|
439
|
+
**Defense:**
|
|
440
|
+
- Keep sanitization libraries updated to the latest version.
|
|
441
|
+
- Use DOMPurify >= 3.2.4 (patches CVE-2025-26791).
|
|
442
|
+
- Prefer `textContent` over `innerHTML` when possible.
|
|
443
|
+
- Monitor DOMPurify's GitHub for new advisories.
|
|
444
|
+
- Consider Trusted Types as an additional enforcement layer.
|
|
445
|
+
|
|
446
|
+
### V-08: XSS via CSS Injection
|
|
447
|
+
**CWE:** CWE-79
|
|
448
|
+
**Risk:** Medium
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
// VULNERABLE: User-controlled CSS values
|
|
452
|
+
element.style.cssText = `background: ${userInput}`;
|
|
453
|
+
|
|
454
|
+
// SECURE: Validate CSS values against allowlist
|
|
455
|
+
const SAFE_COLORS = /^#[0-9a-f]{3,6}$/i;
|
|
456
|
+
if (SAFE_COLORS.test(userInput)) {
|
|
457
|
+
element.style.backgroundColor = userInput;
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### V-09: postMessage XSS
|
|
462
|
+
**CWE:** CWE-79 (DOM-based)
|
|
463
|
+
**Risk:** High
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
// VULNERABLE: No origin validation on message handler
|
|
467
|
+
window.addEventListener('message', (event) => {
|
|
468
|
+
document.getElementById('output')!.innerHTML = event.data.html;
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// SECURE: Validate origin and sanitize data
|
|
472
|
+
window.addEventListener('message', (event) => {
|
|
473
|
+
if (event.origin !== 'https://trusted-domain.com') {
|
|
474
|
+
return; // Reject messages from untrusted origins
|
|
475
|
+
}
|
|
476
|
+
const clean = DOMPurify.sanitize(String(event.data.html));
|
|
477
|
+
document.getElementById('output')!.innerHTML = clean;
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// IMPORTANT: Never use indexOf() or search() for origin validation
|
|
481
|
+
// VULNERABLE: event.origin.indexOf('trusted-domain.com')
|
|
482
|
+
// Can be bypassed with: trusted-domain.com.attacker.com
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### V-10: XSS via Template Injection (Server-Side)
|
|
486
|
+
**CWE:** CWE-79
|
|
487
|
+
**Risk:** Critical
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
// VULNERABLE: String concatenation in server templates
|
|
491
|
+
app.get('/profile', (req, res) => {
|
|
492
|
+
res.send(`<div>${req.query.bio}</div>`);
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// SECURE: Use template engine with auto-escaping
|
|
496
|
+
// EJS: <%= userBio %> (auto-escapes)
|
|
497
|
+
// Pug: p= userBio (auto-escapes)
|
|
498
|
+
// Handlebars: {{userBio}} (auto-escapes)
|
|
499
|
+
// NEVER use unescaped output: <%- %>, !{}, {{{}}}, v-html
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### V-11: XSS via JSON Injection in Script Tags
|
|
503
|
+
**CWE:** CWE-79
|
|
504
|
+
**Risk:** High
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
// VULNERABLE: Embedding user data in inline script
|
|
508
|
+
app.get('/page', (req, res) => {
|
|
509
|
+
const userData = getUserData(req.user.id);
|
|
510
|
+
res.send(`
|
|
511
|
+
<script>
|
|
512
|
+
window.__DATA__ = ${JSON.stringify(userData)};
|
|
513
|
+
</script>
|
|
514
|
+
`);
|
|
515
|
+
// If userData contains </script>, the script block breaks
|
|
516
|
+
// and attacker-controlled HTML/JS can follow
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// SECURE: Encode closing tags and dangerous sequences
|
|
520
|
+
function safeJsonForScript(data: unknown): string {
|
|
521
|
+
return JSON.stringify(data)
|
|
522
|
+
.replace(/</g, '\\u003c')
|
|
523
|
+
.replace(/>/g, '\\u003e')
|
|
524
|
+
.replace(/&/g, '\\u0026')
|
|
525
|
+
.replace(/\u2028/g, '\\u2028')
|
|
526
|
+
.replace(/\u2029/g, '\\u2029');
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### V-12: XSS via File Upload (SVG, HTML)
|
|
531
|
+
**CWE:** CWE-79 (Stored)
|
|
532
|
+
**Risk:** High
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
// VULNERABLE: Serving uploaded SVG files from same origin
|
|
536
|
+
app.use('/uploads', express.static('uploads'));
|
|
537
|
+
// An uploaded SVG with embedded script runs in the application's origin
|
|
538
|
+
|
|
539
|
+
// SECURE: Serve user uploads from a separate domain or with
|
|
540
|
+
// Content-Disposition: attachment header
|
|
541
|
+
app.get('/uploads/:file', (req, res) => {
|
|
542
|
+
res.setHeader('Content-Disposition', 'attachment');
|
|
543
|
+
res.setHeader('Content-Type', 'application/octet-stream');
|
|
544
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
545
|
+
res.sendFile(path.join(uploadsDir, req.params.file));
|
|
546
|
+
});
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### V-13: XSS via Dynamic Code Execution
|
|
550
|
+
**CWE:** CWE-95 (Code Injection)
|
|
551
|
+
**Risk:** Critical
|
|
552
|
+
|
|
553
|
+
Dynamic code execution APIs such as `eval()`, `setTimeout()` with string arguments, and `Function()` constructor with dynamic strings are extremely dangerous when used with any user-controlled input. These APIs should NEVER be used with untrusted data.
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
// SECURE alternative: Use function references with setTimeout
|
|
557
|
+
setTimeout(() => processData(userData), 1000);
|
|
558
|
+
|
|
559
|
+
// SECURE alternative: Use JSON.parse for data deserialization
|
|
560
|
+
const parsed = JSON.parse(trustedJsonString);
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### V-14: XSS via HTTP Header Injection
|
|
564
|
+
**CWE:** CWE-113
|
|
565
|
+
**Risk:** Medium
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
// VULNERABLE: User input in redirect URL without validation
|
|
569
|
+
res.redirect(req.query.returnUrl);
|
|
570
|
+
|
|
571
|
+
// SECURE: Validate redirect targets
|
|
572
|
+
function safeRedirect(url: string, allowedHosts: string[]): string {
|
|
573
|
+
try {
|
|
574
|
+
const parsed = new URL(url, 'https://self.example.com');
|
|
575
|
+
if (allowedHosts.includes(parsed.hostname)) {
|
|
576
|
+
return parsed.pathname + parsed.search;
|
|
577
|
+
}
|
|
578
|
+
} catch {}
|
|
579
|
+
return '/';
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
### V-15: XSS via WebSocket Messages
|
|
584
|
+
**CWE:** CWE-79 (DOM-based)
|
|
585
|
+
**Risk:** High
|
|
586
|
+
|
|
587
|
+
```typescript
|
|
588
|
+
// VULNERABLE: Rendering WebSocket messages as HTML
|
|
589
|
+
socket.onmessage = (event) => {
|
|
590
|
+
chatDiv.innerHTML += `<p>${event.data}</p>`;
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
// SECURE: Use textContent or sanitize
|
|
594
|
+
socket.onmessage = (event) => {
|
|
595
|
+
const p = document.createElement('p');
|
|
596
|
+
p.textContent = event.data;
|
|
597
|
+
chatDiv.appendChild(p);
|
|
598
|
+
};
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
---
|
|
602
|
+
|
|
603
|
+
## 5. Security Checklist
|
|
604
|
+
|
|
605
|
+
### Output Encoding & Rendering
|
|
606
|
+
- [ ] All dynamic output is context-encoded (HTML, JS, URL, CSS) before rendering
|
|
607
|
+
- [ ] Template engines are configured with auto-escaping enabled by default
|
|
608
|
+
- [ ] No use of raw/unescaped output directives without explicit sanitization
|
|
609
|
+
- [ ] JSON data embedded in `<script>` tags is encoded to prevent `</script>` breakout
|
|
610
|
+
- [ ] User-controlled URLs are validated against protocol allowlist (`http:`, `https:`, `mailto:`)
|
|
611
|
+
|
|
612
|
+
### Input Validation
|
|
613
|
+
- [ ] All input is validated for expected type, length, format, and character set
|
|
614
|
+
- [ ] Input validation uses allowlists (not blocklists) for acceptable values
|
|
615
|
+
- [ ] File uploads are validated for type, size, and content; never served from same origin as app
|
|
616
|
+
|
|
617
|
+
### Content Security Policy
|
|
618
|
+
- [ ] CSP header is deployed with nonce-based `script-src` and `strict-dynamic`
|
|
619
|
+
- [ ] `unsafe-inline` and `unsafe-eval` are not present in CSP directives
|
|
620
|
+
- [ ] `object-src 'none'` and `base-uri 'none'` are set
|
|
621
|
+
- [ ] CSP violation reporting is configured (`report-uri` / `report-to`)
|
|
622
|
+
- [ ] CSP was tested with Google CSP Evaluator before deployment
|
|
623
|
+
|
|
624
|
+
### Trusted Types
|
|
625
|
+
- [ ] Trusted Types enforcement is deployed or in report-only mode
|
|
626
|
+
- [ ] All DOM sink assignments go through defined Trusted Types policies
|
|
627
|
+
- [ ] Trusted Types violations are monitored and triaged
|
|
628
|
+
|
|
629
|
+
### Cookie Security
|
|
630
|
+
- [ ] Session cookies have `HttpOnly` flag set
|
|
631
|
+
- [ ] Session cookies have `Secure` flag set
|
|
632
|
+
- [ ] Session cookies use `SameSite=Lax` or `SameSite=Strict`
|
|
633
|
+
|
|
634
|
+
### Sanitization
|
|
635
|
+
- [ ] DOMPurify (or equivalent) is used for all user-supplied HTML rendering
|
|
636
|
+
- [ ] DOMPurify is updated to latest version (check for mXSS advisories)
|
|
637
|
+
- [ ] Sanitized output is not modified or re-parsed after sanitization
|
|
638
|
+
- [ ] SVG and MathML content is sanitized with appropriate profiles
|
|
639
|
+
|
|
640
|
+
### Framework-Specific
|
|
641
|
+
- [ ] React: No `dangerouslySetInnerHTML` without DOMPurify sanitization
|
|
642
|
+
- [ ] Vue: No `v-html` without DOMPurify sanitization
|
|
643
|
+
- [ ] Angular: No `bypassSecurityTrust*` without DOMPurify sanitization
|
|
644
|
+
- [ ] Server templates: Auto-escaping enabled, no raw output with user data
|
|
645
|
+
|
|
646
|
+
### DOM Security
|
|
647
|
+
- [ ] No use of `document.write()` or `document.writeln()`
|
|
648
|
+
- [ ] No use of dynamic code execution APIs with string arguments from user input
|
|
649
|
+
- [ ] `innerHTML` assignments use sanitized content only; `textContent` preferred for text
|
|
650
|
+
- [ ] `postMessage` handlers validate `event.origin` with strict equality (not `indexOf`)
|
|
651
|
+
- [ ] WebSocket message content is not rendered as HTML
|
|
652
|
+
|
|
653
|
+
### Headers & Configuration
|
|
654
|
+
- [ ] `X-Content-Type-Options: nosniff` is set on all responses
|
|
655
|
+
- [ ] `X-Frame-Options: DENY` or CSP `frame-ancestors 'none'` is set
|
|
656
|
+
- [ ] User-uploaded files are served with `Content-Disposition: attachment`
|
|
657
|
+
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
## 6. Tools & Automation
|
|
661
|
+
|
|
662
|
+
### Static Analysis
|
|
663
|
+
|
|
664
|
+
**Semgrep**
|
|
665
|
+
Semgrep provides 20,000+ rules including XSS-specific patterns. It detects `dangerouslySetInnerHTML`, `v-html`, `innerHTML` assignments, dynamic code execution, and missing CSP headers. Custom rules can be written to match project-specific XSS patterns.
|
|
666
|
+
|
|
667
|
+
```bash
|
|
668
|
+
# Run Semgrep with security ruleset
|
|
669
|
+
semgrep --config p/javascript --config p/typescript --config p/react
|
|
670
|
+
semgrep --config p/xss # XSS-specific rules
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
**ESLint Security Plugins**
|
|
674
|
+
- `eslint-plugin-security`: Detects dynamic code execution, `innerHTML`, `document.write` usage.
|
|
675
|
+
- `eslint-plugin-no-unsanitized`: Mozilla's plugin that flags `innerHTML`, `outerHTML`, `insertAdjacentHTML`, and `document.write` with unsanitized content.
|
|
676
|
+
- `@microsoft/eslint-plugin-sdl`: Microsoft's SDL rules including XSS prevention patterns.
|
|
677
|
+
|
|
678
|
+
```json
|
|
679
|
+
{
|
|
680
|
+
"plugins": ["security", "no-unsanitized"],
|
|
681
|
+
"rules": {
|
|
682
|
+
"security/detect-eval-with-expression": "error",
|
|
683
|
+
"no-unsanitized/property": "error",
|
|
684
|
+
"no-unsanitized/method": "error"
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Dynamic Analysis & Testing
|
|
690
|
+
|
|
691
|
+
**Burp Suite**
|
|
692
|
+
Burp Scanner includes comprehensive XSS detection covering reflected, stored, and DOM-based XSS. The active scanner fuzzes parameters with context-aware payloads and detects responses where injected content is rendered without encoding.
|
|
693
|
+
|
|
694
|
+
**OWASP ZAP**
|
|
695
|
+
Open-source alternative to Burp with active and passive XSS scanning. Can be integrated into CI/CD pipelines via the ZAP Docker image.
|
|
696
|
+
|
|
697
|
+
**Browser DevTools -- Trusted Types Violations**
|
|
698
|
+
Chrome DevTools reports Trusted Types violations in the console. Enable report-only CSP header to discover all DOM sinks in your application before enforcement:
|
|
699
|
+
```
|
|
700
|
+
Content-Security-Policy-Report-Only: require-trusted-types-for 'script'
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### CSP Evaluation
|
|
704
|
+
|
|
705
|
+
**Google CSP Evaluator** (https://csp-evaluator.withgoogle.com/)
|
|
706
|
+
Analyzes CSP headers and identifies weaknesses (missing directives, overly permissive sources, `unsafe-inline` usage). Should be used before deploying any CSP policy.
|
|
707
|
+
|
|
708
|
+
### Dependency Auditing
|
|
709
|
+
|
|
710
|
+
```bash
|
|
711
|
+
# Check for known vulnerabilities in DOMPurify and other dependencies
|
|
712
|
+
npm audit
|
|
713
|
+
# Automated patching
|
|
714
|
+
npm audit fix
|
|
715
|
+
# Monitor DOMPurify specifically
|
|
716
|
+
npm ls dompurify
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
## 7. Platform-Specific Guidance
|
|
722
|
+
|
|
723
|
+
### 7.1 React Applications
|
|
724
|
+
|
|
725
|
+
**Default protections:** JSX auto-escapes all embedded expressions before DOM insertion. Values are converted to strings and HTML entities are encoded.
|
|
726
|
+
|
|
727
|
+
**Known escape hatches (XSS vectors):**
|
|
728
|
+
- `dangerouslySetInnerHTML`: Bypasses all React escaping. Must be paired with DOMPurify.
|
|
729
|
+
- `ref.current.innerHTML = ...`: Direct DOM manipulation bypasses React's virtual DOM escaping.
|
|
730
|
+
- `href={userUrl}`: Accepts `javascript:` URLs. Validate protocol.
|
|
731
|
+
- `src` attributes on `<iframe>`, `<script>`: Can load attacker-controlled content.
|
|
732
|
+
- Server-side rendering (SSR): If user data is serialized into `<script>` tags for hydration, it can break out of the JSON context.
|
|
733
|
+
|
|
734
|
+
**React-specific rule:** Treat every use of `dangerouslySetInnerHTML` as a security-critical code path requiring review and DOMPurify sanitization.
|
|
735
|
+
|
|
736
|
+
### 7.2 Vue Applications
|
|
737
|
+
|
|
738
|
+
**Default protections:** Double-brace interpolation (`{{ }}`) auto-escapes HTML.
|
|
739
|
+
|
|
740
|
+
**Known escape hatches:**
|
|
741
|
+
- `v-html` directive: Renders raw HTML. Must be paired with DOMPurify.
|
|
742
|
+
- Server-side template mixing: If Vue templates are generated server-side with user data, template injection can occur.
|
|
743
|
+
- Dynamic component rendering with user-controlled component names.
|
|
744
|
+
|
|
745
|
+
**Vue-specific rule:** Ban `v-html` in ESLint config (`vue/no-v-html`) and require explicit exemption with DOMPurify.
|
|
746
|
+
|
|
747
|
+
### 7.3 Angular Applications
|
|
748
|
+
|
|
749
|
+
**Default protections:** Angular sanitizes HTML, styles, URLs, and resource URLs by default. The `DomSanitizer` service strips dangerous content automatically.
|
|
750
|
+
|
|
751
|
+
**Known escape hatches:**
|
|
752
|
+
- `bypassSecurityTrustHtml()`: Marks content as safe, skipping sanitization.
|
|
753
|
+
- `bypassSecurityTrustScript()`: Marks script content as safe.
|
|
754
|
+
- `bypassSecurityTrustUrl()`: Marks URLs as safe.
|
|
755
|
+
- `bypassSecurityTrustResourceUrl()`: Marks resource URLs as safe.
|
|
756
|
+
- SVG animation attributes (`<animate>`, `<set>`) can bypass sanitization for `href`/`xlink:href` (CVE-2025-66412).
|
|
757
|
+
|
|
758
|
+
**Angular-specific rule:** Audit all uses of `bypassSecurityTrust*`. Each should have a corresponding DOMPurify call or strict validation.
|
|
759
|
+
|
|
760
|
+
### 7.4 Server-Rendered Applications
|
|
761
|
+
|
|
762
|
+
**Template engine configuration:**
|
|
763
|
+
- EJS: `<%= %>` auto-escapes; `<%- %>` outputs raw (never use with user data).
|
|
764
|
+
- Pug/Jade: `= ` escapes; `!= ` outputs raw.
|
|
765
|
+
- Handlebars: `{{ }}` escapes; `{{{ }}}` outputs raw.
|
|
766
|
+
- Jinja2: Auto-escapes by default (when `autoescape=True`).
|
|
767
|
+
- Twig: Auto-escapes by default. `|raw` filter disables (never use with user data).
|
|
768
|
+
|
|
769
|
+
**Server-rendered rule:** Enable auto-escaping globally. Grep the codebase for raw output patterns and audit each occurrence.
|
|
770
|
+
|
|
771
|
+
### 7.5 Mobile WebViews
|
|
772
|
+
|
|
773
|
+
**iOS WKWebView:**
|
|
774
|
+
- Disable JavaScript if not needed: `configuration.preferences.javaScriptEnabled = false`.
|
|
775
|
+
- Use `loadHTMLString` with sanitized content only.
|
|
776
|
+
- Implement `WKNavigationDelegate` to validate URLs before navigation.
|
|
777
|
+
- Never expose native APIs to JavaScript via `WKScriptMessageHandler` without strict input validation.
|
|
778
|
+
|
|
779
|
+
**Android WebView:**
|
|
780
|
+
- Disable JavaScript if not needed: `webView.settings.javaScriptEnabled = false`.
|
|
781
|
+
- Never use `addJavascriptInterface` without `@JavascriptInterface` annotation (Android 4.2+).
|
|
782
|
+
- Set `WebViewClient.shouldOverrideUrlLoading` to validate navigation URLs.
|
|
783
|
+
- Use `WebSettings.setAllowFileAccess(false)` to prevent file:// access.
|
|
784
|
+
|
|
785
|
+
**WebView rule:** WebViews that render user content must apply the same XSS defenses as web applications: CSP, sanitization, and output encoding.
|
|
786
|
+
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
## 8. Incident Patterns
|
|
790
|
+
|
|
791
|
+
### 8.1 XSS Attack Chain: Injection to Account Takeover
|
|
792
|
+
|
|
793
|
+
```
|
|
794
|
+
1. RECONNAISSANCE
|
|
795
|
+
Attacker identifies input field rendered without encoding
|
|
796
|
+
(search box, comment field, profile bio, URL parameter)
|
|
797
|
+
|
|
798
|
+
2. INJECTION
|
|
799
|
+
Attacker submits payload via the vulnerable input
|
|
800
|
+
(stored: save to DB; reflected: craft URL; DOM: manipulate fragment)
|
|
801
|
+
|
|
802
|
+
3. EXECUTION
|
|
803
|
+
Victim's browser executes the injected script
|
|
804
|
+
in the application's origin context
|
|
805
|
+
|
|
806
|
+
4. EXFILTRATION
|
|
807
|
+
Script steals session token (document.cookie if not HTTPOnly),
|
|
808
|
+
captures keystrokes, or reads sensitive DOM content
|
|
809
|
+
|
|
810
|
+
5. ACCOUNT TAKEOVER
|
|
811
|
+
Attacker uses stolen session token to impersonate victim,
|
|
812
|
+
change credentials, exfiltrate data, or escalate privileges
|
|
813
|
+
|
|
814
|
+
6. LATERAL MOVEMENT
|
|
815
|
+
If victim is admin, attacker may access admin panels,
|
|
816
|
+
modify application code, or compromise other users
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
### 8.2 WAF Detection Patterns
|
|
820
|
+
|
|
821
|
+
Web Application Firewalls detect XSS through:
|
|
822
|
+
- **Signature matching:** Known patterns like `<script>`, `onerror=`, `javascript:`.
|
|
823
|
+
- **Heuristic analysis:** Detecting encoded variants, mixed case, and obfuscation.
|
|
824
|
+
- **Behavioral analysis:** Identifying unusual sequences of special characters in parameters.
|
|
825
|
+
|
|
826
|
+
**WAF limitations:** WAFs are a supplementary defense, not a primary one. Skilled attackers can bypass WAFs through encoding tricks, mutation XSS, DOM-only payloads (never reach the server), and context-specific evasion. Never rely on a WAF as your sole XSS defense.
|
|
827
|
+
|
|
828
|
+
### 8.3 CSP Violation Reporting
|
|
829
|
+
|
|
830
|
+
```typescript
|
|
831
|
+
// Server-side CSP report endpoint
|
|
832
|
+
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
|
|
833
|
+
const report = req.body['csp-report'];
|
|
834
|
+
logger.warn('CSP Violation', {
|
|
835
|
+
blockedUri: report['blocked-uri'],
|
|
836
|
+
violatedDirective: report['violated-directive'],
|
|
837
|
+
documentUri: report['document-uri'],
|
|
838
|
+
sourceFile: report['source-file'],
|
|
839
|
+
lineNumber: report['line-number'],
|
|
840
|
+
columnNumber: report['column-number'],
|
|
841
|
+
});
|
|
842
|
+
res.status(204).end();
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
// Reporting-Endpoints header (newer API)
|
|
846
|
+
// Reporting-Endpoints: csp-endpoint="https://example.com/csp-report"
|
|
847
|
+
// Content-Security-Policy: ...; report-to csp-endpoint
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
**Triage CSP reports:**
|
|
851
|
+
- High priority: Violations from your own origin (possible XSS or misconfiguration).
|
|
852
|
+
- Medium priority: Violations from known third-party scripts (may need nonce/hash).
|
|
853
|
+
- Low priority: Violations from browser extensions (common, usually benign).
|
|
854
|
+
|
|
855
|
+
### 8.4 Monitoring Indicators of XSS Exploitation
|
|
856
|
+
|
|
857
|
+
- Unusual outbound requests from client to unknown domains (data exfiltration).
|
|
858
|
+
- Spike in CSP violation reports from a specific page or endpoint.
|
|
859
|
+
- Session tokens appearing in server logs from unexpected IP addresses.
|
|
860
|
+
- User reports of unexpected redirects or login prompts.
|
|
861
|
+
- Anomalous DOM mutations detected by MutationObserver-based monitoring.
|
|
862
|
+
|
|
863
|
+
---
|
|
864
|
+
|
|
865
|
+
## 9. Compliance & Standards
|
|
866
|
+
|
|
867
|
+
### 9.1 OWASP A03:2021 - Injection
|
|
868
|
+
|
|
869
|
+
XSS falls under OWASP Top 10 A03:2021 (Injection), which covers cross-site scripting alongside SQL injection, command injection, and other injection flaws. CWE-79 is the primary weakness identifier for XSS.
|
|
870
|
+
|
|
871
|
+
**Key OWASP requirements:**
|
|
872
|
+
- Use safe APIs that parameterize queries and avoid interpreters.
|
|
873
|
+
- Validate all input on the server side.
|
|
874
|
+
- Escape/encode output for the specific rendering context.
|
|
875
|
+
- Use CSP as a defense-in-depth measure.
|
|
876
|
+
|
|
877
|
+
### 9.2 CWE-79 and Related Weaknesses
|
|
878
|
+
|
|
879
|
+
| CWE ID | Description |
|
|
880
|
+
|--------|-------------|
|
|
881
|
+
| CWE-79 | Improper Neutralization of Input During Web Page Generation (XSS) |
|
|
882
|
+
| CWE-80 | Improper Neutralization of Script-Related HTML Tags |
|
|
883
|
+
| CWE-83 | Improper Neutralization of Script in Attributes |
|
|
884
|
+
| CWE-84 | Improper Neutralization of Encoded URI Schemes |
|
|
885
|
+
| CWE-85 | Doubled Character XSS Manipulations |
|
|
886
|
+
| CWE-86 | Improper Neutralization of Invalid Characters in Identifiers |
|
|
887
|
+
| CWE-87 | Improper Neutralization of Alternate XSS Syntax |
|
|
888
|
+
| CWE-95 | Improper Neutralization of Directives in Dynamically Evaluated Code |
|
|
889
|
+
|
|
890
|
+
### 9.3 PCI-DSS Requirements
|
|
891
|
+
|
|
892
|
+
PCI-DSS v4.0 requires:
|
|
893
|
+
- **Requirement 6.2.4:** Software engineering techniques prevent common software attacks, including XSS (injection flaws).
|
|
894
|
+
- **Requirement 6.3.1:** Security vulnerabilities are identified and managed, including public security vulnerability databases (CVEs).
|
|
895
|
+
- **Requirement 6.4.1:** Public-facing web applications are protected against attacks (WAF or code review for XSS).
|
|
896
|
+
- **Requirement 6.4.2:** Automated technical solutions to detect and prevent web-based attacks, including XSS, for public-facing web applications.
|
|
897
|
+
|
|
898
|
+
**PCI-DSS compliance note:** Organizations handling payment card data must demonstrate XSS prevention controls. The British Airways breach (Magecart) resulted in both regulatory fines and PCI-DSS compliance consequences.
|
|
899
|
+
|
|
900
|
+
### 9.4 Additional Standards
|
|
901
|
+
|
|
902
|
+
- **NIST SP 800-53:** SI-10 (Information Input Validation), SI-15 (Information Output Filtering).
|
|
903
|
+
- **ISO 27001:** A.14.2.5 (Secure system engineering principles), including XSS prevention in SDLC.
|
|
904
|
+
- **SOC 2:** CC6.1 (Logical and Physical Access Controls) -- XSS can be a vector for unauthorized access.
|
|
905
|
+
|
|
906
|
+
---
|
|
907
|
+
|
|
908
|
+
## 10. Code Examples
|
|
909
|
+
|
|
910
|
+
### 10.1 Complete CSP Implementation (Express.js + TypeScript)
|
|
911
|
+
|
|
912
|
+
```typescript
|
|
913
|
+
import crypto from 'crypto';
|
|
914
|
+
import { Request, Response, NextFunction } from 'express';
|
|
915
|
+
|
|
916
|
+
// Middleware: Generate nonce and set CSP headers
|
|
917
|
+
export function cspMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
918
|
+
const nonce = crypto.randomBytes(16).toString('base64');
|
|
919
|
+
res.locals.cspNonce = nonce;
|
|
920
|
+
|
|
921
|
+
const policy = [
|
|
922
|
+
`default-src 'self'`,
|
|
923
|
+
`script-src 'nonce-${nonce}' 'strict-dynamic' https:`,
|
|
924
|
+
`style-src 'self' 'nonce-${nonce}'`,
|
|
925
|
+
`img-src 'self' data: https:`,
|
|
926
|
+
`font-src 'self'`,
|
|
927
|
+
`connect-src 'self'`,
|
|
928
|
+
`media-src 'self'`,
|
|
929
|
+
`object-src 'none'`,
|
|
930
|
+
`base-uri 'none'`,
|
|
931
|
+
`form-action 'self'`,
|
|
932
|
+
`frame-ancestors 'none'`,
|
|
933
|
+
`upgrade-insecure-requests`,
|
|
934
|
+
`report-uri /api/csp-report`,
|
|
935
|
+
].join('; ');
|
|
936
|
+
|
|
937
|
+
res.setHeader('Content-Security-Policy', policy);
|
|
938
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
939
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
940
|
+
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
941
|
+
|
|
942
|
+
next();
|
|
943
|
+
}
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
### 10.2 DOMPurify with Trusted Types (Browser)
|
|
947
|
+
|
|
948
|
+
```typescript
|
|
949
|
+
import DOMPurify from 'dompurify';
|
|
950
|
+
|
|
951
|
+
// Create a Trusted Types policy backed by DOMPurify
|
|
952
|
+
let sanitizePolicy: TrustedTypePolicy | null = null;
|
|
953
|
+
|
|
954
|
+
if (typeof window !== 'undefined' && window.trustedTypes?.createPolicy) {
|
|
955
|
+
sanitizePolicy = window.trustedTypes.createPolicy('dompurify', {
|
|
956
|
+
createHTML: (input: string) =>
|
|
957
|
+
DOMPurify.sanitize(input, {
|
|
958
|
+
USE_PROFILES: { html: true },
|
|
959
|
+
RETURN_TRUSTED_TYPE: false,
|
|
960
|
+
}) as string,
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Safe HTML rendering function
|
|
965
|
+
export function renderSanitizedHTML(
|
|
966
|
+
element: HTMLElement,
|
|
967
|
+
untrustedHTML: string,
|
|
968
|
+
): void {
|
|
969
|
+
if (sanitizePolicy) {
|
|
970
|
+
element.innerHTML = sanitizePolicy.createHTML(untrustedHTML) as unknown as string;
|
|
971
|
+
} else {
|
|
972
|
+
element.innerHTML = DOMPurify.sanitize(untrustedHTML, {
|
|
973
|
+
USE_PROFILES: { html: true },
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// Safe text rendering (preferred when HTML is not needed)
|
|
979
|
+
export function renderText(element: HTMLElement, untrustedText: string): void {
|
|
980
|
+
element.textContent = untrustedText;
|
|
981
|
+
}
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
### 10.3 Context-Specific Encoding Utility (TypeScript)
|
|
985
|
+
|
|
986
|
+
```typescript
|
|
987
|
+
/**
|
|
988
|
+
* Context-specific output encoding utilities.
|
|
989
|
+
* Use the appropriate encoder for each rendering context.
|
|
990
|
+
*/
|
|
991
|
+
export const XSSEncoder = {
|
|
992
|
+
/**
|
|
993
|
+
* HTML entity encoding for content placed in HTML body.
|
|
994
|
+
* Use for: <div>{encoded}</div>
|
|
995
|
+
*/
|
|
996
|
+
html(str: string): string {
|
|
997
|
+
const map: Record<string, string> = {
|
|
998
|
+
'&': '&',
|
|
999
|
+
'<': '<',
|
|
1000
|
+
'>': '>',
|
|
1001
|
+
'"': '"',
|
|
1002
|
+
"'": ''',
|
|
1003
|
+
'/': '/',
|
|
1004
|
+
};
|
|
1005
|
+
return str.replace(/[&<>"'/]/g, (ch) => map[ch]);
|
|
1006
|
+
},
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* Attribute encoding. Always quote the attribute value.
|
|
1010
|
+
* Use for: <div title="{encoded}">
|
|
1011
|
+
*/
|
|
1012
|
+
attr(str: string): string {
|
|
1013
|
+
return str.replace(/[^\w.\-]/g, (ch) => {
|
|
1014
|
+
return `&#x${ch.charCodeAt(0).toString(16).padStart(2, '0')};`;
|
|
1015
|
+
});
|
|
1016
|
+
},
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* JavaScript string encoding.
|
|
1020
|
+
* Use for: var x = '{encoded}';
|
|
1021
|
+
*/
|
|
1022
|
+
js(str: string): string {
|
|
1023
|
+
return str.replace(/[^\w ]/g, (ch) => {
|
|
1024
|
+
return `\\u${ch.charCodeAt(0).toString(16).padStart(4, '0')}`;
|
|
1025
|
+
});
|
|
1026
|
+
},
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* URL parameter encoding.
|
|
1030
|
+
* Use for: /search?q={encoded}
|
|
1031
|
+
*/
|
|
1032
|
+
url(str: string): string {
|
|
1033
|
+
return encodeURIComponent(str);
|
|
1034
|
+
},
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* CSS value encoding.
|
|
1038
|
+
* Use for: style="color: {encoded}"
|
|
1039
|
+
*/
|
|
1040
|
+
css(str: string): string {
|
|
1041
|
+
return str.replace(/[^\w]/g, (ch) => {
|
|
1042
|
+
return `\\${ch.charCodeAt(0).toString(16)} `;
|
|
1043
|
+
});
|
|
1044
|
+
},
|
|
1045
|
+
} as const;
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
### 10.4 React Component: Secure Rich Text Renderer
|
|
1049
|
+
|
|
1050
|
+
```tsx
|
|
1051
|
+
import DOMPurify from 'dompurify';
|
|
1052
|
+
import { useMemo } from 'react';
|
|
1053
|
+
|
|
1054
|
+
interface RichTextProps {
|
|
1055
|
+
content: string;
|
|
1056
|
+
allowedTags?: string[];
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* Secure rich text renderer that sanitizes HTML before rendering.
|
|
1061
|
+
* Uses DOMPurify with strict defaults.
|
|
1062
|
+
*/
|
|
1063
|
+
export function RichText({ content, allowedTags }: RichTextProps) {
|
|
1064
|
+
const sanitizedContent = useMemo(() => {
|
|
1065
|
+
const config: DOMPurify.Config = {
|
|
1066
|
+
USE_PROFILES: { html: true },
|
|
1067
|
+
ALLOWED_TAGS: allowedTags ?? [
|
|
1068
|
+
'b', 'i', 'em', 'strong', 'p', 'br', 'ul', 'ol', 'li',
|
|
1069
|
+
'a', 'h1', 'h2', 'h3', 'h4', 'blockquote', 'code', 'pre',
|
|
1070
|
+
],
|
|
1071
|
+
ALLOWED_ATTR: ['href', 'title', 'target', 'rel'],
|
|
1072
|
+
ALLOW_DATA_ATTR: false,
|
|
1073
|
+
ADD_ATTR: ['target'],
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
// Force all links to open in new tab with noopener
|
|
1077
|
+
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
|
|
1078
|
+
if (node.tagName === 'A') {
|
|
1079
|
+
node.setAttribute('target', '_blank');
|
|
1080
|
+
node.setAttribute('rel', 'noopener noreferrer');
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
const clean = DOMPurify.sanitize(content, config);
|
|
1085
|
+
DOMPurify.removeHook('afterSanitizeAttributes');
|
|
1086
|
+
return clean;
|
|
1087
|
+
}, [content, allowedTags]);
|
|
1088
|
+
|
|
1089
|
+
return (
|
|
1090
|
+
<div
|
|
1091
|
+
className="rich-text"
|
|
1092
|
+
dangerouslySetInnerHTML={{ __html: sanitizedContent }}
|
|
1093
|
+
/>
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
### 10.5 URL Validation Utility
|
|
1099
|
+
|
|
1100
|
+
```typescript
|
|
1101
|
+
/**
|
|
1102
|
+
* Validates and sanitizes URLs to prevent javascript: and data: XSS.
|
|
1103
|
+
* Returns the sanitized URL or a safe fallback.
|
|
1104
|
+
*/
|
|
1105
|
+
export function sanitizeUrl(
|
|
1106
|
+
url: string,
|
|
1107
|
+
options: {
|
|
1108
|
+
allowedProtocols?: string[];
|
|
1109
|
+
allowedHosts?: string[];
|
|
1110
|
+
fallback?: string;
|
|
1111
|
+
} = {},
|
|
1112
|
+
): string {
|
|
1113
|
+
const {
|
|
1114
|
+
allowedProtocols = ['http:', 'https:', 'mailto:'],
|
|
1115
|
+
allowedHosts,
|
|
1116
|
+
fallback = '#',
|
|
1117
|
+
} = options;
|
|
1118
|
+
|
|
1119
|
+
try {
|
|
1120
|
+
const parsed = new URL(url, window.location.origin);
|
|
1121
|
+
|
|
1122
|
+
if (!allowedProtocols.includes(parsed.protocol)) {
|
|
1123
|
+
return fallback;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
if (allowedHosts && !allowedHosts.includes(parsed.hostname)) {
|
|
1127
|
+
return fallback;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
return parsed.toString();
|
|
1131
|
+
} catch {
|
|
1132
|
+
return fallback;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
```
|
|
1136
|
+
|
|
1137
|
+
---
|
|
1138
|
+
|
|
1139
|
+
## References
|
|
1140
|
+
|
|
1141
|
+
- OWASP XSS Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
|
|
1142
|
+
- OWASP DOM-Based XSS Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html
|
|
1143
|
+
- OWASP A03:2021 Injection: https://owasp.org/Top10/2021/A03_2021-Injection/
|
|
1144
|
+
- CWE-79: https://cwe.mitre.org/data/definitions/79.html
|
|
1145
|
+
- Trusted Types API (MDN): https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API
|
|
1146
|
+
- Trusted Types (web.dev): https://web.dev/trusted-types/
|
|
1147
|
+
- DOMPurify: https://github.com/cure53/DOMPurify
|
|
1148
|
+
- Google CSP Evaluator: https://csp-evaluator.withgoogle.com/
|
|
1149
|
+
- Strict CSP (Google): https://csp.withgoogle.com/docs/strict-csp.html
|
|
1150
|
+
- British Airways ICO Fine: https://www.theregister.com/2020/10/16/british_airways_ico_fine_20m/
|
|
1151
|
+
- Fortnite Vulnerability (Check Point): https://research.checkpoint.com/2019/hacking-fortnite/
|
|
1152
|
+
- eBay Stored XSS (Netcraft): https://www.netcraft.com/blog/hackers-still-exploiting-ebays-stored-xss-vulnerabilities-in-2017
|
|
1153
|
+
- Microsoft MSRC on XSS: https://www.microsoft.com/en-us/msrc/blog/2025/09/why-xss-still-matters-msrcs-perspective-on-a-25-year-old-threat
|
|
1154
|
+
- DOMPurify CVE-2024-47875: https://portswigger.net/research/bypassing-dompurify-again-with-mutation-xss
|
|
1155
|
+
- DOMPurify CVE-2025-26791: https://www.cve.news/cve-2025-26791/
|
|
1156
|
+
- Angular SVG/MathML XSS CVE-2025-66412: https://github.com/advisories/GHSA-v4hv-rgfq-gp49
|
|
1157
|
+
- Vue Security Guide: https://vuejs.org/guide/best-practices/security
|
|
1158
|
+
- CSP (MDN): https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP
|