claude-flow-novice 2.15.3 → 2.15.4
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/.claude/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
- package/.claude/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
- package/.claude/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
- package/.claude/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
- package/.claude/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
- package/.claude/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
- package/.claude/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
- package/.claude/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
- package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
- package/.claude/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
- package/.claude/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
- package/.claude/commands/cfn-loop-cli.md +16 -2
- package/.claude/commands/switch-api.md +31 -10
- package/.claude/hooks/cfn-lint-sql-injection.sh +61 -0
- package/.claude/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
- package/.claude/hooks/cfn-pre-edit-security-warning.sh +40 -0
- package/.claude/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
- package/.claude/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
- package/.claude/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
- package/.claude/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
- package/.claude/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
- package/.claude/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +184 -23
- package/.claude/skills/cfn-loop-orchestration/security_utils.sh +24 -0
- package/.claude/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
- package/.claude/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
- package/.claude/skills/cfn-redis-coordination/agent-log.sh +4 -0
- package/.claude/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
- package/.claude/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
- package/.claude/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
- package/.claude/skills/cfn-redis-coordination/get-context.sh +33 -0
- package/.claude/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
- package/.claude/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -0
- package/.claude/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
- package/.claude/skills/cfn-redis-coordination/redis-functions.sh +33 -0
- package/.claude/skills/cfn-redis-coordination/report-completion.sh +24 -31
- package/.claude/skills/cfn-redis-coordination/store-context.sh +4 -0
- package/.claude/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
- package/.claude/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
- package/.claude/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
- package/.claude/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
- package/.claude/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
- package/README.md +116 -475
- package/claude-assets/agents/cfn-dev-team/README.md +103 -0
- package/claude-assets/agents/cfn-dev-team/architecture/goal-planner.md +1 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-frontend-coordinator.md +77 -15
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +355 -6
- package/claude-assets/agents/cfn-dev-team/coordinators/consensus-builder.md +82 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/handoff-coordinator.md +82 -1
- package/claude-assets/agents/cfn-dev-team/coordinators/multi-sprint-coordinator.md +77 -15
- package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +99 -12
- package/claude-assets/agents/cfn-dev-team/dev-ops/github-commit-agent.md +1 -1
- package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +97 -0
- package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +20 -1
- package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +97 -0
- package/claude-assets/agents/cfn-dev-team/developers/backend-developer.md +110 -13
- package/claude-assets/agents/cfn-dev-team/developers/data/data-engineer.md +106 -15
- package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +115 -11
- package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +94 -7
- package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +87 -9
- package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +85 -7
- package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +160 -28
- package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +101 -19
- package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +108 -14
- package/claude-assets/agents/cfn-dev-team/reviewers/{reviewer.md → code-reviewer.md} +95 -8
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +107 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +98 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +95 -7
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +136 -9
- package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +108 -1
- package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +107 -13
- package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +737 -0
- package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +828 -0
- package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +106 -7
- package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +77 -0
- package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +684 -0
- package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +110 -1
- package/claude-assets/agents/cfn-dev-team/testers/tester.md +94 -7
- package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +1 -3
- package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +87 -13
- package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +103 -7
- package/claude-assets/agents/cfn-dev-team/utility/researcher.md +1 -3
- package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +94 -7
- package/claude-assets/agents/docker-coordinators/cfn-docker-v3-coordinator.md +46 -0
- package/claude-assets/agents/project-only-agents/npm-package-specialist.md +1 -1
- package/claude-assets/cfn-extras/skills/advanced-features/cfn-agent-swap/recommend-swap.sh +59 -59
- package/claude-assets/cfn-extras/skills/analytics/cfn-improvement-recommender/recommend-improvements.sh +91 -91
- package/claude-assets/cfn-extras/skills/analytics/cfn-pattern-extraction/extract-patterns.sh +79 -79
- package/claude-assets/cfn-extras/skills/analytics/cfn-retrospective-report/generate-report.sh +100 -100
- package/claude-assets/cfn-extras/skills/analytics/cfn-telemetry/start-telemetry.sh +110 -110
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/add-bullet.sh +145 -145
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/log-merge.sh +67 -67
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/monitor-injection-performance.sh +137 -137
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/optimize-injection-pipeline.sh +168 -168
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/query-reflections.sh +35 -35
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/store-reflection.sh +45 -45
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/track-ab-test.sh +41 -41
- package/claude-assets/cfn-extras/skills/deprecated/cfn-ace-system/update-reflection.sh +41 -41
- package/claude-assets/cfn-extras/skills/deprecated/cfn-cli-setup/validate-cli-environment.sh +191 -191
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/create-campaign.sh +231 -231
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/get-campaign-performance.sh +190 -190
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/pause-campaign.sh +142 -142
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/set-budget.sh +181 -181
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-ad-campaigns/operations/update-bid-strategy.sh +133 -133
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/get-conversation-history.sh +121 -121
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/qualify-lead.sh +156 -156
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/schedule-demo.sh +181 -181
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/send-message.sh +137 -137
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-chatbot-conversations/operations/transfer-to-human.sh +179 -179
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/create-campaign.sh +183 -183
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/get-delivery-status.sh +139 -139
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/opt-out.sh +150 -150
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/schedule-campaign.sh +187 -187
- package/claude-assets/cfn-extras/skills/marketing/cfn-marketing-sms-campaigns/operations/send-sms.sh +181 -181
- package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/test-web-portal-skill.sh +50 -50
- package/claude-assets/cfn-extras/skills/ui-portal/cfn-web-portal/validate-deployment.sh +84 -84
- package/claude-assets/cfn-extras/skills/utility/cfn-environment-sanitization/sanitize-environment.sh +243 -243
- package/claude-assets/commands/cfn-loop-cli.md +16 -2
- package/claude-assets/commands/switch-api.md +31 -10
- package/claude-assets/hooks/cfn-lint-sql-injection.sh +61 -0
- package/claude-assets/hooks/cfn-post-edit-cfn-retrospective.sh +33 -2
- package/claude-assets/hooks/cfn-pre-edit-security-warning.sh +40 -0
- package/claude-assets/hooks/detect-hardcoded-credentials.sh +212 -0
- package/claude-assets/skills/SKILL_TEMPLATE.md +774 -0
- package/claude-assets/skills/agent-lifecycle/execute-lifecycle-hook.sh +84 -113
- package/claude-assets/skills/agent-lifecycle/simple-audit.sh +33 -6
- package/claude-assets/skills/agent-template-generator/SKILL.md +440 -0
- package/claude-assets/skills/agent-template-generator/generate-agent.sh +405 -0
- package/claude-assets/skills/agent-validation-linter/SKILL.md +589 -0
- package/claude-assets/skills/agent-validation-linter/lint-agents.sh +271 -0
- package/claude-assets/skills/bootstrap/bash-fundamentals.md +786 -0
- package/claude-assets/skills/bootstrap/database-connection.md +464 -0
- package/claude-assets/skills/bootstrap/error-handling.md +580 -0
- package/claude-assets/skills/bootstrap/file-operations.md +699 -0
- package/claude-assets/skills/bootstrap/skill-loader.md +616 -0
- package/claude-assets/skills/bootstrap/sqlite-params.sh +287 -0
- package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh +22 -24
- package/claude-assets/skills/cfn-automatic-memory-persistence/test-memory-persistence.sh +17 -16
- package/claude-assets/skills/cfn-deployment/SKILL.md +293 -0
- package/claude-assets/skills/cfn-deployment/execute.sh +21 -0
- package/claude-assets/skills/cfn-docker-agent-spawning/SKILL.md +28 -4
- package/claude-assets/skills/cfn-docker-agent-spawning/spawn-agent.sh +3 -1
- package/claude-assets/skills/cfn-docker-loop-orchestration/orchestrate.sh +224 -20
- package/claude-assets/skills/cfn-environment-sanitization/sanitize-environment.sh +38 -0
- package/claude-assets/skills/cfn-error-batching-strategy/lib/core-functions.sh +47 -47
- package/claude-assets/skills/cfn-file-operations/SKILL.md +290 -0
- package/claude-assets/skills/cfn-file-operations/execute.sh +129 -0
- package/claude-assets/skills/cfn-file-operations/lib/atomic-write.sh +294 -0
- package/claude-assets/skills/cfn-file-operations/lib/lock.sh +361 -0
- package/claude-assets/skills/cfn-file-operations/test.sh +369 -0
- package/claude-assets/skills/cfn-log-operations/SKILL.md +308 -0
- package/claude-assets/skills/cfn-log-operations/execute.sh +420 -0
- package/claude-assets/skills/cfn-log-operations/lib/rotate.sh +406 -0
- package/claude-assets/skills/cfn-log-operations/lib/search.sh +448 -0
- package/claude-assets/skills/cfn-log-operations/test.sh +394 -0
- package/claude-assets/skills/cfn-loop-orchestration/helpers/gate-check.sh +550 -46
- package/claude-assets/skills/cfn-loop-orchestration/helpers/parse-test-results.sh +277 -0
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +184 -23
- package/claude-assets/skills/cfn-loop-orchestration/security_utils.sh +24 -0
- package/claude-assets/skills/cfn-loop-orchestration/test-iteration-context-injection.sh +366 -0
- package/claude-assets/skills/cfn-parameterized-queries/SKILL.md +339 -0
- package/claude-assets/skills/cfn-playbook/query-playbook.sh +19 -15
- package/claude-assets/skills/cfn-playbook/update-playbook.sh +25 -14
- package/claude-assets/skills/cfn-process-instrumentation/instrument-process.sh +44 -0
- package/claude-assets/skills/cfn-promotion/SKILL.md +305 -0
- package/claude-assets/skills/cfn-redis-coordination/CENTRALIZED_REDIS_WRAPPER.md +319 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-log.sh +4 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-log.sh.bak +124 -0
- package/claude-assets/skills/cfn-redis-coordination/agent-recovery.sh +2 -2
- package/claude-assets/skills/cfn-redis-coordination/collect-confidence-scores.sh +30 -0
- package/claude-assets/skills/cfn-redis-coordination/get-context.sh +33 -0
- package/claude-assets/skills/cfn-redis-coordination/get-success-criteria.sh +54 -0
- package/claude-assets/skills/cfn-redis-coordination/invoke-waiting-mode.sh +3 -0
- package/claude-assets/skills/cfn-redis-coordination/redis-cli-wrapper.sh +24 -3
- package/claude-assets/skills/cfn-redis-coordination/redis-functions.sh +33 -0
- package/claude-assets/skills/cfn-redis-coordination/report-completion.sh +24 -31
- package/claude-assets/skills/cfn-redis-coordination/store-context.sh +4 -0
- package/claude-assets/skills/cfn-redis-coordination/store-success-criteria.sh +85 -0
- package/claude-assets/skills/cfn-redis-coordination/update-all-scripts.sh +67 -0
- package/claude-assets/skills/cfn-skill-loader/SKILL.md +466 -0
- package/claude-assets/skills/cfn-skill-loader/execute.sh +344 -0
- package/claude-assets/skills/cfn-sqlite-memory/ttl-cleanup.sh +17 -25
- package/claude-assets/skills/cfn-task-audit/get-audit-data.sh +42 -21
- package/claude-assets/skills/cfn-task-audit/store-task-audit.sh +17 -10
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh +17 -14
- package/claude-assets/skills/cfn-test-runner/detect-regressions.sh.backup-1763392821 +55 -0
- package/claude-assets/skills/cfn-test-runner/store-benchmarks.sh +17 -19
- package/claude-assets/skills/cfn-transparency-middleware/test-e2e.sh +15 -0
- package/claude-assets/skills/cfn-transparency-middleware/tests/input-validation.sh +15 -0
- package/claude-assets/skills/cfn-utilities/SKILL.md +237 -0
- package/claude-assets/skills/cfn-utilities/execute.sh +32 -0
- package/claude-assets/skills/cfn-utilities/lib/errors.sh +56 -0
- package/claude-assets/skills/cfn-utilities/lib/file-ops.sh +164 -0
- package/claude-assets/skills/cfn-utilities/lib/logging.sh +77 -0
- package/claude-assets/skills/cfn-utilities/lib/retry.sh +127 -0
- package/claude-assets/skills/cfn-utilities/test.sh +317 -0
- package/claude-assets/skills/integration/agent-handoff.sh +62 -64
- package/claude-assets/skills/json-validation/SKILL.md +431 -0
- package/claude-assets/skills/json-validation/test-validate-success-criteria.sh +421 -0
- package/claude-assets/skills/json-validation/validate-success-criteria.sh +197 -0
- package/claude-assets/skills/redis-coordination/validate-parameters.sh +34 -0
- package/claude-assets/skills/workflow-codification/DEPLOY_QUICK_REFERENCE.md +106 -0
- package/claude-assets/skills/workflow-codification/PROPAGATE_UPDATE_QUICK_REFERENCE.md +366 -0
- package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh +481 -0
- package/claude-assets/skills/workflow-codification/deploy-approved-skill.sh.backup-1763392820 +512 -0
- package/claude-assets/skills/workflow-codification/lib/security-utils.sh +204 -0
- package/claude-assets/skills/workflow-codification/propagate-skill-update.sh +648 -0
- package/claude-assets/skills/workflow-codification/propagate-skill-update.sh.backup-1763392820 +664 -0
- package/claude-assets/skills/workflow-codification/test-integration.sh +15 -0
- package/claude-assets/skills/workflow-codification/test-metadata-update.sh +350 -0
- package/claude-assets/skills/workflow-codification/track-cost-savings.sh +55 -14
- package/claude-assets/skills/workflow-codification/track-cost-savings.sh.backup-1763392821 +445 -0
- package/claude-assets/skills/workflow-codification/track-edge-case.sh +27 -60
- package/claude-assets/skills/workflow-codification/workflow-codification.db +0 -0
- package/dist/ace/ace-curator.js +10 -2
- package/dist/ace/ace-curator.js.map +1 -1
- package/dist/ace/ace-generator.js +4 -0
- package/dist/ace/ace-generator.js.map +1 -1
- package/dist/ace/ace-reflector.js +1 -1
- package/dist/ace/ace-reflector.js.map +1 -1
- package/dist/ace/context-injection.js +24 -2
- package/dist/ace/context-injection.js.map +1 -1
- package/dist/agents/agent-loader.js +146 -165
- package/dist/agents/agent-loader.js.map +1 -1
- package/dist/agents/task-agent-integration.js +1 -1
- package/dist/agents/task-agent-integration.js.map +1 -1
- package/dist/api/health-endpoints.js +390 -0
- package/dist/api/health-endpoints.js.map +1 -0
- package/dist/cli/agent-executor.js +4 -1
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/cli/agent-prompt-builder.js +89 -1
- package/dist/cli/agent-prompt-builder.js.map +1 -1
- package/dist/cli/agent-spawn.js +130 -37
- package/dist/cli/agent-spawn.js.map +1 -1
- package/dist/cli/skill-cache-validator.js +412 -0
- package/dist/cli/skill-cache-validator.js.map +1 -0
- package/dist/cli/skill-cli.js +991 -0
- package/dist/cli/skill-cli.js.map +1 -0
- package/dist/cli/skill-execution-logger.js +284 -0
- package/dist/cli/skill-execution-logger.js.map +1 -0
- package/dist/cli/skill-loader.js +457 -0
- package/dist/cli/skill-loader.js.map +1 -0
- package/dist/coordination/event-bus.js +2 -2
- package/dist/coordination/event-bus.js.map +1 -1
- package/dist/coordination/fleet-manager.js +1 -1
- package/dist/coordination/fleet-manager.js.map +1 -1
- package/dist/coordination/index.js +23 -9
- package/dist/coordination/index.js.map +1 -1
- package/dist/coordination/types/fleet-manager.types.js.map +1 -1
- package/dist/db/migration-manager.js +483 -0
- package/dist/db/migration-manager.js.map +1 -0
- package/dist/db/skills-query.js +535 -0
- package/dist/db/skills-query.js.map +1 -0
- package/dist/integration/DatabaseHandoff.js +1 -1
- package/dist/integration/DatabaseHandoff.js.map +1 -1
- package/dist/jobs/edge-case-analyzer.js +367 -0
- package/dist/jobs/edge-case-analyzer.js.map +1 -0
- package/dist/jobs/promotion-sla-enforcer.js +288 -0
- package/dist/jobs/promotion-sla-enforcer.js.map +1 -0
- package/dist/lib/agent-output-parser.js.map +1 -1
- package/dist/lib/agent-output-validator.js.map +1 -1
- package/dist/lib/agent-workspace.js +281 -0
- package/dist/lib/agent-workspace.js.map +1 -0
- package/dist/lib/atomic-file-writer.js +377 -0
- package/dist/lib/atomic-file-writer.js.map +1 -0
- package/dist/lib/backup-manager.js +779 -0
- package/dist/lib/backup-manager.js.map +1 -0
- package/dist/lib/checkpoint-manager.js +837 -0
- package/dist/lib/checkpoint-manager.js.map +1 -0
- package/dist/lib/circuit-breaker.js +340 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/completion-signal-handler.js +243 -0
- package/dist/lib/completion-signal-handler.js.map +1 -0
- package/dist/lib/config-manager.js +312 -0
- package/dist/lib/config-manager.js.map +1 -0
- package/dist/lib/config-migrator.js +386 -0
- package/dist/lib/config-migrator.js.map +1 -0
- package/dist/lib/config-validator.js.map +1 -1
- package/dist/lib/correlation-cache.js +311 -0
- package/dist/lib/correlation-cache.js.map +1 -0
- package/dist/lib/correlation.js +263 -0
- package/dist/lib/correlation.js.map +1 -0
- package/dist/lib/database-service/connection-pool-manager.js +520 -0
- package/dist/lib/database-service/connection-pool-manager.js.map +1 -0
- package/dist/lib/database-service/correlation.js +329 -0
- package/dist/lib/database-service/correlation.js.map +1 -0
- package/dist/lib/database-service/errors.js +120 -0
- package/dist/lib/database-service/errors.js.map +1 -0
- package/dist/lib/database-service/index.js +168 -0
- package/dist/lib/database-service/index.js.map +1 -0
- package/dist/lib/database-service/postgres-adapter.js +526 -0
- package/dist/lib/database-service/postgres-adapter.js.map +1 -0
- package/dist/lib/database-service/redis-adapter.js +360 -0
- package/dist/lib/database-service/redis-adapter.js.map +1 -0
- package/dist/lib/database-service/sqlite-adapter.js +544 -0
- package/dist/lib/database-service/sqlite-adapter.js.map +1 -0
- package/dist/lib/database-service/transaction-manager.js +773 -0
- package/dist/lib/database-service/transaction-manager.js.map +1 -0
- package/dist/lib/database-service/types.js +23 -0
- package/dist/lib/database-service/types.js.map +1 -0
- package/dist/lib/deadlock-resolver.js +292 -0
- package/dist/lib/deadlock-resolver.js.map +1 -0
- package/dist/lib/distributed-lock.js +451 -0
- package/dist/lib/distributed-lock.js.map +1 -0
- package/dist/lib/edge-case-deduplicator.js +227 -0
- package/dist/lib/edge-case-deduplicator.js.map +1 -0
- package/dist/lib/encryption-manager.js +322 -0
- package/dist/lib/encryption-manager.js.map +1 -0
- package/dist/lib/error-aggregator.js +234 -0
- package/dist/lib/error-aggregator.js.map +1 -0
- package/dist/lib/errors.js +287 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/file-lock-manager.js +578 -0
- package/dist/lib/file-lock-manager.js.map +1 -0
- package/dist/lib/file-operations.js +367 -0
- package/dist/lib/file-operations.js.map +1 -0
- package/dist/lib/idempotent-write.js +237 -0
- package/dist/lib/idempotent-write.js.map +1 -0
- package/dist/lib/integration-schema-validator.js +522 -0
- package/dist/lib/integration-schema-validator.js.map +1 -0
- package/dist/lib/lock-health-monitor.js +298 -0
- package/dist/lib/lock-health-monitor.js.map +1 -0
- package/dist/lib/log-shipper.js +422 -0
- package/dist/lib/log-shipper.js.map +1 -0
- package/dist/lib/logging.js +146 -0
- package/dist/lib/logging.js.map +1 -0
- package/dist/lib/message-deduplicator.js +439 -0
- package/dist/lib/message-deduplicator.js.map +1 -0
- package/dist/lib/multi-system-query.js +604 -0
- package/dist/lib/multi-system-query.js.map +1 -0
- package/dist/lib/orphan-detector.js +332 -0
- package/dist/lib/orphan-detector.js.map +1 -0
- package/dist/lib/password-generator.js +166 -0
- package/dist/lib/password-generator.js.map +1 -0
- package/dist/lib/path-validator.js +429 -0
- package/dist/lib/path-validator.js.map +1 -0
- package/dist/lib/query-translator.js +905 -0
- package/dist/lib/query-translator.js.map +1 -0
- package/dist/lib/queue-recovery.js +469 -0
- package/dist/lib/queue-recovery.js.map +1 -0
- package/dist/lib/redis-queue-manager.js +512 -0
- package/dist/lib/redis-queue-manager.js.map +1 -0
- package/dist/lib/reflection-archiver.js +272 -0
- package/dist/lib/reflection-archiver.js.map +1 -0
- package/dist/lib/retry-manager.js +453 -0
- package/dist/lib/retry-manager.js.map +1 -0
- package/dist/lib/retry.js +262 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/lib/schema-transform.js +695 -0
- package/dist/lib/schema-transform.js.map +1 -0
- package/dist/lib/schema-validator.js +491 -0
- package/dist/lib/schema-validator.js.map +1 -0
- package/dist/lib/skill-cache.js +297 -0
- package/dist/lib/skill-cache.js.map +1 -0
- package/dist/lib/skill-content-manager.js +337 -0
- package/dist/lib/skill-content-manager.js.map +1 -0
- package/dist/lib/skill-frontmatter-parser.js +237 -0
- package/dist/lib/skill-frontmatter-parser.js.map +1 -0
- package/dist/lib/skill-git-integration.js +275 -0
- package/dist/lib/skill-git-integration.js.map +1 -0
- package/dist/lib/skill-markdown-validator.js +396 -0
- package/dist/lib/skill-markdown-validator.js.map +1 -0
- package/dist/lib/skill-output-parser.js +312 -0
- package/dist/lib/skill-output-parser.js.map +1 -0
- package/dist/lib/unified-query-api.js +467 -0
- package/dist/lib/unified-query-api.js.map +1 -0
- package/dist/middleware/auth-middleware.js +350 -0
- package/dist/middleware/auth-middleware.js.map +1 -0
- package/dist/middleware/schema-validation.js +347 -0
- package/dist/middleware/schema-validation.js.map +1 -0
- package/dist/providers/anthropic-provider.js +1 -1
- package/dist/providers/anthropic-provider.js.map +1 -1
- package/dist/providers/provider-factory.js +2 -2
- package/dist/providers/provider-factory.js.map +1 -1
- package/dist/services/edge-case-analyzer.js +321 -0
- package/dist/services/edge-case-analyzer.js.map +1 -0
- package/dist/services/edge-case-deduplicator.js +266 -0
- package/dist/services/edge-case-deduplicator.js.map +1 -0
- package/dist/services/edge-case-detector.js +337 -0
- package/dist/services/edge-case-detector.js.map +1 -0
- package/dist/services/edge-case-tracker.js +547 -0
- package/dist/services/edge-case-tracker.js.map +1 -0
- package/dist/services/health-check-system.js +586 -0
- package/dist/services/health-check-system.js.map +1 -0
- package/dist/services/metrics-logger.js +412 -0
- package/dist/services/metrics-logger.js.map +1 -0
- package/dist/services/patch-generator.js +378 -0
- package/dist/services/patch-generator.js.map +1 -0
- package/dist/services/patch-validator.js +337 -0
- package/dist/services/patch-validator.js.map +1 -0
- package/dist/services/performance-monitor.js +811 -0
- package/dist/services/performance-monitor.js.map +1 -0
- package/dist/services/promotion-pipeline.js +918 -0
- package/dist/services/promotion-pipeline.js.map +1 -0
- package/dist/services/promotion-validator.js +394 -0
- package/dist/services/promotion-validator.js.map +1 -0
- package/dist/services/reflection-logger.js +388 -0
- package/dist/services/reflection-logger.js.map +1 -0
- package/dist/services/skill-deployment.js +472 -0
- package/dist/services/skill-deployment.js.map +1 -0
- package/dist/services/skill-loader.js +427 -0
- package/dist/services/skill-loader.js.map +1 -0
- package/dist/services/skill-promotion.js +372 -0
- package/dist/services/skill-promotion.js.map +1 -0
- package/dist/services/skill-validator.js +454 -0
- package/dist/services/skill-validator.js.map +1 -0
- package/dist/services/skill-versioning.js +244 -0
- package/dist/services/skill-versioning.js.map +1 -0
- package/dist/services/workspace-supervisor.js +597 -0
- package/dist/services/workspace-supervisor.js.map +1 -0
- package/dist/types/edge-case.js +45 -0
- package/dist/types/edge-case.js.map +1 -0
- package/package.json +201 -177
- package/readme/README.md +19 -4
- package/scripts/backup-cleanup.sh +627 -0
- package/scripts/cleanup-workspaces.sh +412 -0
- package/scripts/cleanup-yaml-configs.sh +141 -0
- package/scripts/deploy-approved-skills.sh +263 -0
- package/scripts/health-check.sh +447 -0
- package/scripts/log-aggregator.sh +554 -0
- package/scripts/log-monitor.sh +629 -0
- package/scripts/manage-agent-workspaces.sh +434 -0
- package/scripts/migrate-schema.sh +533 -0
- package/scripts/promote-staged-skills.sh +423 -0
- package/scripts/verify-no-secrets.sh +88 -35
- package/.claude/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
- package/.claude/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
- package/.claude/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
- package/.claude/skills/agent-lifecycle/SKILL.md +0 -60
- package/.claude/skills/agent-lifecycle/execute-lifecycle-hook.sh +0 -573
- package/.claude/skills/agent-lifecycle/simple-audit.sh +0 -31
- package/.claude/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
- package/README.md.backup_before_replace +0 -781
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/adaptive-coordinator.md.backup +0 -161
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/blocking-coordinator-example.md.backup +0 -728
- package/claude-assets/cfn-extras/agents/deprecated-coordinators/mesh-coordinator.md.backup +0 -131
- package/claude-assets/skills/cfn-agent-spawning/spawn-agent.sh.backup +0 -273
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh.backup +0 -949
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge Case Deduplicator
|
|
3
|
+
*
|
|
4
|
+
* Handles deduplication of edge cases using SHA-256 signatures.
|
|
5
|
+
* Normalizes context to detect similar errors with different IDs, timestamps, or numbers.
|
|
6
|
+
*/ import * as crypto from 'crypto';
|
|
7
|
+
export class EdgeCaseDeduplicator {
|
|
8
|
+
/**
|
|
9
|
+
* Generate SHA-256 signature for edge case deduplication
|
|
10
|
+
*
|
|
11
|
+
* Signature is based on:
|
|
12
|
+
* - Type
|
|
13
|
+
* - Category
|
|
14
|
+
* - Normalized error message
|
|
15
|
+
* - Normalized stack trace
|
|
16
|
+
*
|
|
17
|
+
* @param edgeCase Edge case input
|
|
18
|
+
* @returns SHA-256 signature (64 hex characters)
|
|
19
|
+
*/ generateSignature(edgeCase) {
|
|
20
|
+
const normalizedContext = this.normalizeContext(edgeCase.context);
|
|
21
|
+
const signatureData = {
|
|
22
|
+
type: edgeCase.type,
|
|
23
|
+
category: edgeCase.category,
|
|
24
|
+
context: normalizedContext
|
|
25
|
+
};
|
|
26
|
+
const signatureString = JSON.stringify(signatureData);
|
|
27
|
+
const hash = crypto.createHash('sha256');
|
|
28
|
+
hash.update(signatureString);
|
|
29
|
+
return hash.digest('hex');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Normalize context for deduplication
|
|
33
|
+
*
|
|
34
|
+
* Replaces variable data with placeholders:
|
|
35
|
+
* - Dates/timestamps → {date}
|
|
36
|
+
* - IDs (hex, alphanumeric) → {id}
|
|
37
|
+
* - Numbers → {number}
|
|
38
|
+
* - File paths → normalized paths
|
|
39
|
+
* - Line numbers in stack traces → {line}
|
|
40
|
+
*
|
|
41
|
+
* @param context Edge case context object
|
|
42
|
+
* @returns Normalized JSON string
|
|
43
|
+
*/ normalizeContext(context) {
|
|
44
|
+
const normalized = this.deepNormalize(context);
|
|
45
|
+
return JSON.stringify(normalized, null, 0);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Normalize stack trace for deduplication
|
|
49
|
+
*
|
|
50
|
+
* Removes:
|
|
51
|
+
* - Line numbers
|
|
52
|
+
* - Column numbers
|
|
53
|
+
* - Absolute file paths (keep relative paths)
|
|
54
|
+
* - Memory addresses
|
|
55
|
+
*
|
|
56
|
+
* @param stackTrace Stack trace string
|
|
57
|
+
* @returns Normalized stack trace
|
|
58
|
+
*/ normalizeStackTrace(stackTrace) {
|
|
59
|
+
let normalized = stackTrace;
|
|
60
|
+
// Normalize line and column numbers: ":42:10" → ":{line}:{col}"
|
|
61
|
+
normalized = normalized.replace(/:(\d+):(\d+)/g, ':{line}:{col}');
|
|
62
|
+
// Normalize single line numbers: ":42)" → ":{line})"
|
|
63
|
+
normalized = normalized.replace(/:(\d+)\)/g, ':{line})');
|
|
64
|
+
// Normalize absolute paths to relative
|
|
65
|
+
// /app/src/file.js → src/file.js
|
|
66
|
+
// /home/user/project/file.js → file.js
|
|
67
|
+
normalized = normalized.replace(/\/[a-zA-Z0-9_\-\/]+\//g, (match)=>{
|
|
68
|
+
// Keep only the last 2 path segments
|
|
69
|
+
const segments = match.split('/').filter((s)=>s.length > 0);
|
|
70
|
+
return segments.slice(-2).join('/') + '/';
|
|
71
|
+
});
|
|
72
|
+
// Normalize memory addresses: 0x7f1234567890 → {addr}
|
|
73
|
+
normalized = normalized.replace(/0x[0-9a-f]+/gi, '{addr}');
|
|
74
|
+
return normalized;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Deep normalize an object or string
|
|
78
|
+
*
|
|
79
|
+
* Recursively processes objects and arrays,
|
|
80
|
+
* normalizing strings at leaf nodes.
|
|
81
|
+
*/ deepNormalize(value) {
|
|
82
|
+
if (typeof value === 'string') {
|
|
83
|
+
return this.normalizeString(value);
|
|
84
|
+
}
|
|
85
|
+
if (Array.isArray(value)) {
|
|
86
|
+
return value.map((item)=>this.deepNormalize(item));
|
|
87
|
+
}
|
|
88
|
+
if (typeof value === 'object' && value !== null) {
|
|
89
|
+
const normalized = {};
|
|
90
|
+
// Special handling for error objects
|
|
91
|
+
if (value.message !== undefined) {
|
|
92
|
+
normalized.message = this.normalizeString(value.message);
|
|
93
|
+
}
|
|
94
|
+
if (value.stack !== undefined) {
|
|
95
|
+
normalized.stack = this.normalizeStackTrace(value.stack);
|
|
96
|
+
}
|
|
97
|
+
// Normalize other properties
|
|
98
|
+
for (const key of Object.keys(value)){
|
|
99
|
+
if (key !== 'message' && key !== 'stack') {
|
|
100
|
+
normalized[key] = this.deepNormalize(value[key]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Sort keys for consistent hashing
|
|
104
|
+
const sorted = {};
|
|
105
|
+
Object.keys(normalized).sort().forEach((key)=>{
|
|
106
|
+
sorted[key] = normalized[key];
|
|
107
|
+
});
|
|
108
|
+
return sorted;
|
|
109
|
+
}
|
|
110
|
+
// Numbers, booleans, null stay as-is for non-string normalization
|
|
111
|
+
// but we track them as {number} etc. in strings
|
|
112
|
+
return value;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Normalize a string by replacing variable data with placeholders
|
|
116
|
+
*/ normalizeString(str) {
|
|
117
|
+
let normalized = str;
|
|
118
|
+
// Normalize ISO 8601 dates/timestamps
|
|
119
|
+
// 2025-11-16T13:00:00Z → {date}
|
|
120
|
+
// 2025-11-16T13:00:00.123Z → {date}
|
|
121
|
+
// 2025-11-16 13:00:00 → {date}
|
|
122
|
+
normalized = normalized.replace(/\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z?/g, '{date}');
|
|
123
|
+
// Normalize standalone dates
|
|
124
|
+
// 2025-11-16 → {date}
|
|
125
|
+
normalized = normalized.replace(/\d{4}-\d{2}-\d{2}/g, '{date}');
|
|
126
|
+
// Normalize Unix timestamps (10 digits)
|
|
127
|
+
// 1700000000 → {timestamp}
|
|
128
|
+
normalized = normalized.replace(/\b\d{10}\b/g, '{timestamp}');
|
|
129
|
+
// Normalize UUIDs
|
|
130
|
+
// 550e8400-e29b-41d4-a716-446655440000 → {uuid}
|
|
131
|
+
normalized = normalized.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '{uuid}');
|
|
132
|
+
// Normalize hex IDs (8+ hex characters)
|
|
133
|
+
// abc123def456 → {id}
|
|
134
|
+
normalized = normalized.replace(/\b[a-f0-9]{8,}\b/gi, '{id}');
|
|
135
|
+
// Normalize task/agent IDs with prefixes
|
|
136
|
+
// task-abc123, agent-xyz789, cfn-cli-1234567 → {id}
|
|
137
|
+
normalized = normalized.replace(/(?:task|agent|cfn|job|run|session)-[a-z0-9\-]+/gi, '{id}');
|
|
138
|
+
// Normalize user/resource IDs in paths
|
|
139
|
+
// /api/users/12345 → /api/users/{id}
|
|
140
|
+
// /tasks/abc123/status → /tasks/{id}/status
|
|
141
|
+
normalized = normalized.replace(/\/([a-z0-9\-]+)(?=\/|$)/gi, (match, id)=>{
|
|
142
|
+
// Only replace if it looks like an ID (contains numbers or is hex)
|
|
143
|
+
if (/\d/.test(id) || /^[a-f0-9]+$/i.test(id)) {
|
|
144
|
+
return '/{id}';
|
|
145
|
+
}
|
|
146
|
+
return match;
|
|
147
|
+
});
|
|
148
|
+
// Normalize standalone numbers
|
|
149
|
+
// "Error 500", "timeout after 5000ms", "user 12345" → "Error {number}", etc.
|
|
150
|
+
normalized = normalized.replace(/\b\d+\b/g, '{number}');
|
|
151
|
+
// Normalize memory addresses
|
|
152
|
+
// 0x7f1234567890 → {addr}
|
|
153
|
+
normalized = normalized.replace(/0x[0-9a-f]+/gi, '{addr}');
|
|
154
|
+
// Normalize file paths with line numbers
|
|
155
|
+
// /app/file.js:42 → /app/file.js:{line}
|
|
156
|
+
normalized = normalized.replace(/:(\d+)(?::(\d+))?/g, ':{line}');
|
|
157
|
+
return normalized;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Check if two edge cases are similar based on their signatures
|
|
161
|
+
*
|
|
162
|
+
* @param signature1 First signature
|
|
163
|
+
* @param signature2 Second signature
|
|
164
|
+
* @returns True if signatures match
|
|
165
|
+
*/ areSimilar(signature1, signature2) {
|
|
166
|
+
return signature1 === signature2;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Calculate similarity score between two contexts (for fuzzy matching)
|
|
170
|
+
*
|
|
171
|
+
* This is for future enhancement - currently we use exact signature matching.
|
|
172
|
+
* Could be used for "similar but not identical" edge case detection.
|
|
173
|
+
*
|
|
174
|
+
* @param context1 First context
|
|
175
|
+
* @param context2 Second context
|
|
176
|
+
* @returns Similarity score (0.0 to 1.0)
|
|
177
|
+
*/ calculateSimilarity(context1, context2) {
|
|
178
|
+
const norm1 = this.normalizeContext(context1);
|
|
179
|
+
const norm2 = this.normalizeContext(context2);
|
|
180
|
+
// Simple Jaccard similarity on normalized strings
|
|
181
|
+
const set1 = new Set(norm1.split(/\s+/));
|
|
182
|
+
const set2 = new Set(norm2.split(/\s+/));
|
|
183
|
+
const intersection = new Set(Array.from(set1).filter((x)=>set2.has(x)));
|
|
184
|
+
const union = new Set([
|
|
185
|
+
...Array.from(set1),
|
|
186
|
+
...Array.from(set2)
|
|
187
|
+
]);
|
|
188
|
+
return intersection.size / union.size;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Extract common pattern from multiple edge cases
|
|
192
|
+
*
|
|
193
|
+
* For future pattern detection feature.
|
|
194
|
+
*
|
|
195
|
+
* @param edgeCases Array of edge case inputs
|
|
196
|
+
* @returns Common pattern descriptor
|
|
197
|
+
*/ extractPattern(edgeCases) {
|
|
198
|
+
// Future implementation: extract common error patterns
|
|
199
|
+
// For now, return basic statistics
|
|
200
|
+
const types = new Map();
|
|
201
|
+
const categories = new Map();
|
|
202
|
+
for (const edgeCase of edgeCases){
|
|
203
|
+
types.set(edgeCase.type, (types.get(edgeCase.type) || 0) + 1);
|
|
204
|
+
categories.set(edgeCase.category, (categories.get(edgeCase.category) || 0) + 1);
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
commonType: this.findMostCommon(types),
|
|
208
|
+
commonCategory: this.findMostCommon(categories),
|
|
209
|
+
totalCases: edgeCases.length
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Find most common value in a map
|
|
214
|
+
*/ findMostCommon(map) {
|
|
215
|
+
let maxCount = 0;
|
|
216
|
+
let maxKey = null;
|
|
217
|
+
for (const [key, count] of Array.from(map.entries())){
|
|
218
|
+
if (count > maxCount) {
|
|
219
|
+
maxCount = count;
|
|
220
|
+
maxKey = key;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return maxKey;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
//# sourceMappingURL=edge-case-deduplicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/edge-case-deduplicator.ts"],"sourcesContent":["/**\r\n * Edge Case Deduplicator\r\n *\r\n * Handles deduplication of edge cases using SHA-256 signatures.\r\n * Normalizes context to detect similar errors with different IDs, timestamps, or numbers.\r\n */\r\n\r\nimport * as crypto from 'crypto';\r\nimport { EdgeCaseInput, EdgeCaseType } from '../types/edge-case.js';\r\n\r\nexport class EdgeCaseDeduplicator {\r\n /**\r\n * Generate SHA-256 signature for edge case deduplication\r\n *\r\n * Signature is based on:\r\n * - Type\r\n * - Category\r\n * - Normalized error message\r\n * - Normalized stack trace\r\n *\r\n * @param edgeCase Edge case input\r\n * @returns SHA-256 signature (64 hex characters)\r\n */\r\n public generateSignature(edgeCase: EdgeCaseInput): string {\r\n const normalizedContext = this.normalizeContext(edgeCase.context);\r\n\r\n const signatureData = {\r\n type: edgeCase.type,\r\n category: edgeCase.category,\r\n context: normalizedContext\r\n };\r\n\r\n const signatureString = JSON.stringify(signatureData);\r\n const hash = crypto.createHash('sha256');\r\n hash.update(signatureString);\r\n\r\n return hash.digest('hex');\r\n }\r\n\r\n /**\r\n * Normalize context for deduplication\r\n *\r\n * Replaces variable data with placeholders:\r\n * - Dates/timestamps → {date}\r\n * - IDs (hex, alphanumeric) → {id}\r\n * - Numbers → {number}\r\n * - File paths → normalized paths\r\n * - Line numbers in stack traces → {line}\r\n *\r\n * @param context Edge case context object\r\n * @returns Normalized JSON string\r\n */\r\n public normalizeContext(context: any): string {\r\n const normalized = this.deepNormalize(context);\r\n return JSON.stringify(normalized, null, 0);\r\n }\r\n\r\n /**\r\n * Normalize stack trace for deduplication\r\n *\r\n * Removes:\r\n * - Line numbers\r\n * - Column numbers\r\n * - Absolute file paths (keep relative paths)\r\n * - Memory addresses\r\n *\r\n * @param stackTrace Stack trace string\r\n * @returns Normalized stack trace\r\n */\r\n public normalizeStackTrace(stackTrace: string): string {\r\n let normalized = stackTrace;\r\n\r\n // Normalize line and column numbers: \":42:10\" → \":{line}:{col}\"\r\n normalized = normalized.replace(/:(\\d+):(\\d+)/g, ':{line}:{col}');\r\n\r\n // Normalize single line numbers: \":42)\" → \":{line})\"\r\n normalized = normalized.replace(/:(\\d+)\\)/g, ':{line})');\r\n\r\n // Normalize absolute paths to relative\r\n // /app/src/file.js → src/file.js\r\n // /home/user/project/file.js → file.js\r\n normalized = normalized.replace(/\\/[a-zA-Z0-9_\\-\\/]+\\//g, (match) => {\r\n // Keep only the last 2 path segments\r\n const segments = match.split('/').filter(s => s.length > 0);\r\n return segments.slice(-2).join('/') + '/';\r\n });\r\n\r\n // Normalize memory addresses: 0x7f1234567890 → {addr}\r\n normalized = normalized.replace(/0x[0-9a-f]+/gi, '{addr}');\r\n\r\n return normalized;\r\n }\r\n\r\n /**\r\n * Deep normalize an object or string\r\n *\r\n * Recursively processes objects and arrays,\r\n * normalizing strings at leaf nodes.\r\n */\r\n private deepNormalize(value: any): any {\r\n if (typeof value === 'string') {\r\n return this.normalizeString(value);\r\n }\r\n\r\n if (Array.isArray(value)) {\r\n return value.map(item => this.deepNormalize(item));\r\n }\r\n\r\n if (typeof value === 'object' && value !== null) {\r\n const normalized: any = {};\r\n\r\n // Special handling for error objects\r\n if (value.message !== undefined) {\r\n normalized.message = this.normalizeString(value.message);\r\n }\r\n\r\n if (value.stack !== undefined) {\r\n normalized.stack = this.normalizeStackTrace(value.stack);\r\n }\r\n\r\n // Normalize other properties\r\n for (const key of Object.keys(value)) {\r\n if (key !== 'message' && key !== 'stack') {\r\n normalized[key] = this.deepNormalize(value[key]);\r\n }\r\n }\r\n\r\n // Sort keys for consistent hashing\r\n const sorted: any = {};\r\n Object.keys(normalized).sort().forEach(key => {\r\n sorted[key] = normalized[key];\r\n });\r\n\r\n return sorted;\r\n }\r\n\r\n // Numbers, booleans, null stay as-is for non-string normalization\r\n // but we track them as {number} etc. in strings\r\n return value;\r\n }\r\n\r\n /**\r\n * Normalize a string by replacing variable data with placeholders\r\n */\r\n private normalizeString(str: string): string {\r\n let normalized = str;\r\n\r\n // Normalize ISO 8601 dates/timestamps\r\n // 2025-11-16T13:00:00Z → {date}\r\n // 2025-11-16T13:00:00.123Z → {date}\r\n // 2025-11-16 13:00:00 → {date}\r\n normalized = normalized.replace(/\\d{4}-\\d{2}-\\d{2}[T\\s]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3})?Z?/g, '{date}');\r\n\r\n // Normalize standalone dates\r\n // 2025-11-16 → {date}\r\n normalized = normalized.replace(/\\d{4}-\\d{2}-\\d{2}/g, '{date}');\r\n\r\n // Normalize Unix timestamps (10 digits)\r\n // 1700000000 → {timestamp}\r\n normalized = normalized.replace(/\\b\\d{10}\\b/g, '{timestamp}');\r\n\r\n // Normalize UUIDs\r\n // 550e8400-e29b-41d4-a716-446655440000 → {uuid}\r\n normalized = normalized.replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '{uuid}');\r\n\r\n // Normalize hex IDs (8+ hex characters)\r\n // abc123def456 → {id}\r\n normalized = normalized.replace(/\\b[a-f0-9]{8,}\\b/gi, '{id}');\r\n\r\n // Normalize task/agent IDs with prefixes\r\n // task-abc123, agent-xyz789, cfn-cli-1234567 → {id}\r\n normalized = normalized.replace(/(?:task|agent|cfn|job|run|session)-[a-z0-9\\-]+/gi, '{id}');\r\n\r\n // Normalize user/resource IDs in paths\r\n // /api/users/12345 → /api/users/{id}\r\n // /tasks/abc123/status → /tasks/{id}/status\r\n normalized = normalized.replace(/\\/([a-z0-9\\-]+)(?=\\/|$)/gi, (match, id) => {\r\n // Only replace if it looks like an ID (contains numbers or is hex)\r\n if (/\\d/.test(id) || /^[a-f0-9]+$/i.test(id)) {\r\n return '/{id}';\r\n }\r\n return match;\r\n });\r\n\r\n // Normalize standalone numbers\r\n // \"Error 500\", \"timeout after 5000ms\", \"user 12345\" → \"Error {number}\", etc.\r\n normalized = normalized.replace(/\\b\\d+\\b/g, '{number}');\r\n\r\n // Normalize memory addresses\r\n // 0x7f1234567890 → {addr}\r\n normalized = normalized.replace(/0x[0-9a-f]+/gi, '{addr}');\r\n\r\n // Normalize file paths with line numbers\r\n // /app/file.js:42 → /app/file.js:{line}\r\n normalized = normalized.replace(/:(\\d+)(?::(\\d+))?/g, ':{line}');\r\n\r\n return normalized;\r\n }\r\n\r\n /**\r\n * Check if two edge cases are similar based on their signatures\r\n *\r\n * @param signature1 First signature\r\n * @param signature2 Second signature\r\n * @returns True if signatures match\r\n */\r\n public areSimilar(signature1: string, signature2: string): boolean {\r\n return signature1 === signature2;\r\n }\r\n\r\n /**\r\n * Calculate similarity score between two contexts (for fuzzy matching)\r\n *\r\n * This is for future enhancement - currently we use exact signature matching.\r\n * Could be used for \"similar but not identical\" edge case detection.\r\n *\r\n * @param context1 First context\r\n * @param context2 Second context\r\n * @returns Similarity score (0.0 to 1.0)\r\n */\r\n public calculateSimilarity(context1: any, context2: any): number {\r\n const norm1 = this.normalizeContext(context1);\r\n const norm2 = this.normalizeContext(context2);\r\n\r\n // Simple Jaccard similarity on normalized strings\r\n const set1 = new Set(norm1.split(/\\s+/));\r\n const set2 = new Set(norm2.split(/\\s+/));\r\n\r\n const intersection = new Set(Array.from(set1).filter(x => set2.has(x)));\r\n const union = new Set([...Array.from(set1), ...Array.from(set2)]);\r\n\r\n return intersection.size / union.size;\r\n }\r\n\r\n /**\r\n * Extract common pattern from multiple edge cases\r\n *\r\n * For future pattern detection feature.\r\n *\r\n * @param edgeCases Array of edge case inputs\r\n * @returns Common pattern descriptor\r\n */\r\n public extractPattern(edgeCases: EdgeCaseInput[]): any {\r\n // Future implementation: extract common error patterns\r\n // For now, return basic statistics\r\n\r\n const types = new Map<EdgeCaseType, number>();\r\n const categories = new Map<string, number>();\r\n\r\n for (const edgeCase of edgeCases) {\r\n types.set(edgeCase.type, (types.get(edgeCase.type) || 0) + 1);\r\n categories.set(edgeCase.category, (categories.get(edgeCase.category) || 0) + 1);\r\n }\r\n\r\n return {\r\n commonType: this.findMostCommon(types),\r\n commonCategory: this.findMostCommon(categories),\r\n totalCases: edgeCases.length\r\n };\r\n }\r\n\r\n /**\r\n * Find most common value in a map\r\n */\r\n private findMostCommon<K>(map: Map<K, number>): K | null {\r\n let maxCount = 0;\r\n let maxKey: K | null = null;\r\n\r\n for (const [key, count] of Array.from(map.entries())) {\r\n if (count > maxCount) {\r\n maxCount = count;\r\n maxKey = key;\r\n }\r\n }\r\n\r\n return maxKey;\r\n }\r\n}\r\n"],"names":["crypto","EdgeCaseDeduplicator","generateSignature","edgeCase","normalizedContext","normalizeContext","context","signatureData","type","category","signatureString","JSON","stringify","hash","createHash","update","digest","normalized","deepNormalize","normalizeStackTrace","stackTrace","replace","match","segments","split","filter","s","length","slice","join","value","normalizeString","Array","isArray","map","item","message","undefined","stack","key","Object","keys","sorted","sort","forEach","str","id","test","areSimilar","signature1","signature2","calculateSimilarity","context1","context2","norm1","norm2","set1","Set","set2","intersection","from","x","has","union","size","extractPattern","edgeCases","types","Map","categories","set","get","commonType","findMostCommon","commonCategory","totalCases","maxCount","maxKey","count","entries"],"mappings":"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"}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup Encryption Manager
|
|
3
|
+
*
|
|
4
|
+
* Implements AES-256-GCM encryption for backup files with:
|
|
5
|
+
* - Secure key management via environment variables
|
|
6
|
+
* - Unique IV generation for each backup
|
|
7
|
+
* - HMAC-SHA256 integrity verification
|
|
8
|
+
* - Key rotation support
|
|
9
|
+
* - Backward compatibility detection
|
|
10
|
+
* - Encryption metadata tracking
|
|
11
|
+
*
|
|
12
|
+
* CVSS Mitigation: Addresses CVSS 7.2 (high severity) vulnerability
|
|
13
|
+
* by encrypting sensitive backup data at rest.
|
|
14
|
+
*
|
|
15
|
+
* Security Requirements:
|
|
16
|
+
* - BACKUP_ENCRYPTION_KEY: 32-byte hex encoded AES-256 key (env var)
|
|
17
|
+
* - BACKUP_ENCRYPTION_ENABLED: Set to 'true' to enable encryption (env var)
|
|
18
|
+
* - IV: Cryptographically random, unique per backup
|
|
19
|
+
* - Auth Tag: GCM authentication tag for integrity verification
|
|
20
|
+
* - HMAC: SHA-256 for additional integrity verification
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* const encryptionManager = new EncryptionManager();
|
|
24
|
+
* const encrypted = await encryptionManager.encrypt(buffer, backupId);
|
|
25
|
+
* const decrypted = await encryptionManager.decrypt(encrypted, backupId);
|
|
26
|
+
* const verified = encryptionManager.verifyIntegrity(encrypted);
|
|
27
|
+
*/ import * as crypto from 'crypto';
|
|
28
|
+
import { createLogger } from './logging.js';
|
|
29
|
+
import { createError, ErrorCode } from './errors.js';
|
|
30
|
+
const logger = createLogger('encryption-manager');
|
|
31
|
+
/**
|
|
32
|
+
* Backup Encryption Manager
|
|
33
|
+
*
|
|
34
|
+
* Implements AES-256-GCM encryption with integrity verification
|
|
35
|
+
*/ export class EncryptionManager {
|
|
36
|
+
enabled;
|
|
37
|
+
masterKey = null;
|
|
38
|
+
keyVersion;
|
|
39
|
+
ALGORITHM = 'aes-256-gcm';
|
|
40
|
+
IV_LENGTH = 16;
|
|
41
|
+
AUTH_TAG_LENGTH = 16;
|
|
42
|
+
KEY_LENGTH = 32;
|
|
43
|
+
HMAC_ALGORITHM = 'sha256';
|
|
44
|
+
constructor(config = {}){
|
|
45
|
+
// Check if encryption is enabled
|
|
46
|
+
this.enabled = config.enabled !== undefined ? config.enabled : this.isEncryptionEnabled();
|
|
47
|
+
// Load and validate master key
|
|
48
|
+
if (this.enabled) {
|
|
49
|
+
const keyHex = config.masterKey || process.env.BACKUP_ENCRYPTION_KEY;
|
|
50
|
+
if (!keyHex) {
|
|
51
|
+
throw createError(ErrorCode.VALIDATION_FAILED, 'BACKUP_ENCRYPTION_KEY environment variable is required when encryption is enabled', {
|
|
52
|
+
enabledVia: 'BACKUP_ENCRYPTION_ENABLED env var'
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
this.masterKey = Buffer.from(keyHex, 'hex');
|
|
57
|
+
if (this.masterKey.length !== this.KEY_LENGTH) {
|
|
58
|
+
throw createError(ErrorCode.VALIDATION_FAILED, `Master key must be exactly ${this.KEY_LENGTH} bytes (${this.KEY_LENGTH * 2} hex characters)`, {
|
|
59
|
+
providedLength: this.masterKey.length,
|
|
60
|
+
expectedLength: this.KEY_LENGTH
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
logger.info('Encryption manager initialized with AES-256-GCM', {
|
|
64
|
+
enabled: true,
|
|
65
|
+
algorithm: this.ALGORITHM,
|
|
66
|
+
keyLength: this.KEY_LENGTH,
|
|
67
|
+
ivLength: this.IV_LENGTH,
|
|
68
|
+
authTagLength: this.AUTH_TAG_LENGTH
|
|
69
|
+
});
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (error instanceof Error && 'code' in error) {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
throw createError(ErrorCode.VALIDATION_FAILED, 'Failed to parse BACKUP_ENCRYPTION_KEY. Must be valid hex-encoded 32-byte key.', {
|
|
75
|
+
error: error instanceof Error ? error.message : String(error)
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
logger.info('Encryption manager initialized with encryption disabled', {
|
|
80
|
+
enabled: false,
|
|
81
|
+
enablementRequired: 'Set BACKUP_ENCRYPTION_ENABLED=true and provide BACKUP_ENCRYPTION_KEY to enable encryption'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
this.keyVersion = config.keyVersion || 'v1';
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check if encryption is enabled
|
|
88
|
+
*/ isEnabled() {
|
|
89
|
+
return this.enabled;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Encrypt backup data
|
|
93
|
+
*
|
|
94
|
+
* @param data - Data to encrypt
|
|
95
|
+
* @param backupId - Backup identifier (for logging/tracking)
|
|
96
|
+
* @returns Encrypted backup with metadata
|
|
97
|
+
* @throws Error if encryption is disabled or encryption fails
|
|
98
|
+
*/ async encrypt(data, backupId) {
|
|
99
|
+
if (!this.enabled || !this.masterKey) {
|
|
100
|
+
throw createError(ErrorCode.VALIDATION_FAILED, 'Encryption is not enabled. Enable BACKUP_ENCRYPTION_ENABLED and provide BACKUP_ENCRYPTION_KEY.', {
|
|
101
|
+
backupId
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
// Generate cryptographically random IV
|
|
106
|
+
const iv = crypto.randomBytes(this.IV_LENGTH);
|
|
107
|
+
// Create cipher
|
|
108
|
+
const cipher = crypto.createCipheriv(this.ALGORITHM, this.masterKey, iv);
|
|
109
|
+
// Encrypt data
|
|
110
|
+
let encryptedData = cipher.update(data);
|
|
111
|
+
encryptedData = Buffer.concat([
|
|
112
|
+
encryptedData,
|
|
113
|
+
cipher.final()
|
|
114
|
+
]);
|
|
115
|
+
// Get authentication tag
|
|
116
|
+
const authTag = cipher.getAuthTag();
|
|
117
|
+
// Calculate HMAC for additional integrity verification
|
|
118
|
+
const hmac = crypto.createHmac(this.HMAC_ALGORITHM, this.masterKey).update(Buffer.concat([
|
|
119
|
+
iv,
|
|
120
|
+
encryptedData,
|
|
121
|
+
authTag
|
|
122
|
+
])).digest();
|
|
123
|
+
// Create metadata
|
|
124
|
+
const metadata = {
|
|
125
|
+
algorithm: 'AES-256-GCM',
|
|
126
|
+
iv: iv.toString('hex'),
|
|
127
|
+
authTag: authTag.toString('hex'),
|
|
128
|
+
hmac: hmac.toString('hex'),
|
|
129
|
+
encryptedAt: new Date().toISOString(),
|
|
130
|
+
keyVersion: this.keyVersion
|
|
131
|
+
};
|
|
132
|
+
logger.debug('Backup encrypted successfully', {
|
|
133
|
+
backupId,
|
|
134
|
+
algorithm: this.ALGORITHM,
|
|
135
|
+
dataSize: data.length,
|
|
136
|
+
encryptedSize: encryptedData.length,
|
|
137
|
+
ivLength: iv.length,
|
|
138
|
+
authTagLength: authTag.length
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
data: encryptedData,
|
|
142
|
+
metadata
|
|
143
|
+
};
|
|
144
|
+
} catch (error) {
|
|
145
|
+
logger.error('Backup encryption failed', error instanceof Error ? error : undefined, {
|
|
146
|
+
backupId
|
|
147
|
+
});
|
|
148
|
+
throw createError(ErrorCode.INTERNAL_ERROR, 'Backup encryption failed', {
|
|
149
|
+
backupId,
|
|
150
|
+
error: error instanceof Error ? error.message : String(error)
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Decrypt backup data
|
|
156
|
+
*
|
|
157
|
+
* @param encrypted - Encrypted backup
|
|
158
|
+
* @param backupId - Backup identifier (for logging/tracking)
|
|
159
|
+
* @returns Decryption result with data and integrity verification
|
|
160
|
+
* @throws Error if decryption fails or integrity verification fails
|
|
161
|
+
*/ async decrypt(encrypted, backupId) {
|
|
162
|
+
if (!this.enabled || !this.masterKey) {
|
|
163
|
+
throw createError(ErrorCode.VALIDATION_FAILED, 'Encryption is not enabled. Enable BACKUP_ENCRYPTION_ENABLED and provide BACKUP_ENCRYPTION_KEY.', {
|
|
164
|
+
backupId
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
// Verify HMAC first
|
|
169
|
+
const iv = Buffer.from(encrypted.metadata.iv, 'hex');
|
|
170
|
+
const authTag = Buffer.from(encrypted.metadata.authTag, 'hex');
|
|
171
|
+
const expectedHmac = encrypted.metadata.hmac;
|
|
172
|
+
const calculatedHmac = crypto.createHmac(this.HMAC_ALGORITHM, this.masterKey).update(Buffer.concat([
|
|
173
|
+
iv,
|
|
174
|
+
encrypted.data,
|
|
175
|
+
authTag
|
|
176
|
+
])).digest().toString('hex');
|
|
177
|
+
const integrityVerified = calculatedHmac === expectedHmac;
|
|
178
|
+
if (!integrityVerified) {
|
|
179
|
+
logger.warn('HMAC verification failed during decryption', {
|
|
180
|
+
backupId,
|
|
181
|
+
expectedHmac,
|
|
182
|
+
calculatedHmac
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
// Decrypt data
|
|
186
|
+
const decipher = crypto.createDecipheriv(this.ALGORITHM, this.masterKey, iv);
|
|
187
|
+
decipher.setAuthTag(authTag);
|
|
188
|
+
let decryptedData = decipher.update(encrypted.data);
|
|
189
|
+
decryptedData = Buffer.concat([
|
|
190
|
+
decryptedData,
|
|
191
|
+
decipher.final()
|
|
192
|
+
]);
|
|
193
|
+
logger.debug('Backup decrypted successfully', {
|
|
194
|
+
backupId,
|
|
195
|
+
algorithm: this.ALGORITHM,
|
|
196
|
+
encryptedSize: encrypted.data.length,
|
|
197
|
+
decryptedSize: decryptedData.length,
|
|
198
|
+
integrityVerified
|
|
199
|
+
});
|
|
200
|
+
return {
|
|
201
|
+
data: decryptedData,
|
|
202
|
+
integrityVerified,
|
|
203
|
+
metadata: encrypted.metadata
|
|
204
|
+
};
|
|
205
|
+
} catch (error) {
|
|
206
|
+
logger.error('Backup decryption failed', error instanceof Error ? error : undefined, {
|
|
207
|
+
backupId
|
|
208
|
+
});
|
|
209
|
+
throw createError(ErrorCode.INTERNAL_ERROR, 'Backup decryption failed. Backup may be corrupted or key may be incorrect.', {
|
|
210
|
+
backupId,
|
|
211
|
+
error: error instanceof Error ? error.message : String(error)
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Verify backup integrity without decryption
|
|
217
|
+
*
|
|
218
|
+
* @param encrypted - Encrypted backup
|
|
219
|
+
* @param backupId - Backup identifier (for logging)
|
|
220
|
+
* @returns Whether integrity verification passed
|
|
221
|
+
*/ verifyIntegrity(encrypted, backupId) {
|
|
222
|
+
if (!this.masterKey) {
|
|
223
|
+
logger.error('Cannot verify integrity without master key', {
|
|
224
|
+
backupId
|
|
225
|
+
});
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
const iv = Buffer.from(encrypted.metadata.iv, 'hex');
|
|
230
|
+
const authTag = Buffer.from(encrypted.metadata.authTag, 'hex');
|
|
231
|
+
const expectedHmac = encrypted.metadata.hmac;
|
|
232
|
+
const calculatedHmac = crypto.createHmac(this.HMAC_ALGORITHM, this.masterKey).update(Buffer.concat([
|
|
233
|
+
iv,
|
|
234
|
+
encrypted.data,
|
|
235
|
+
authTag
|
|
236
|
+
])).digest().toString('hex');
|
|
237
|
+
const verified = calculatedHmac === expectedHmac;
|
|
238
|
+
if (!verified) {
|
|
239
|
+
logger.warn('Integrity verification failed', {
|
|
240
|
+
backupId
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
return verified;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
logger.error('Integrity verification error', error instanceof Error ? error : undefined, {
|
|
246
|
+
backupId
|
|
247
|
+
});
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Detect if a backup file is encrypted
|
|
253
|
+
*
|
|
254
|
+
* Useful for backward compatibility - detects if backup is encrypted
|
|
255
|
+
*
|
|
256
|
+
* @param data - File data
|
|
257
|
+
* @returns Whether the data appears to be encrypted
|
|
258
|
+
*/ static isEncrypted(data) {
|
|
259
|
+
// Encrypted data with metadata should have a specific structure
|
|
260
|
+
// For now, we can check if the data is valid JSON metadata (simple heuristic)
|
|
261
|
+
try {
|
|
262
|
+
// Try to detect our encryption format
|
|
263
|
+
// This is a simple heuristic - encrypted data won't be valid text/JSON
|
|
264
|
+
if (data.length < 100) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
// Try to parse as JSON at the end for metadata
|
|
268
|
+
const str = data.toString('utf8', Math.max(0, data.length - 500));
|
|
269
|
+
const match = str.match(/"algorithm"\s*:\s*"AES-256-GCM"/);
|
|
270
|
+
return match !== null;
|
|
271
|
+
} catch {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Generate a new encryption key
|
|
277
|
+
*
|
|
278
|
+
* Useful for key rotation
|
|
279
|
+
*
|
|
280
|
+
* @returns 32-byte hex-encoded AES-256 key
|
|
281
|
+
*/ static generateKey() {
|
|
282
|
+
return crypto.randomBytes(32).toString('hex');
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Export encryption metadata as JSON
|
|
286
|
+
*
|
|
287
|
+
* Useful for storing metadata separately from encrypted data
|
|
288
|
+
*
|
|
289
|
+
* @param encrypted - Encrypted backup
|
|
290
|
+
* @returns JSON stringified metadata
|
|
291
|
+
*/ static exportMetadata(encrypted) {
|
|
292
|
+
return JSON.stringify(encrypted.metadata, null, 2);
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Import encryption metadata from JSON
|
|
296
|
+
*
|
|
297
|
+
* @param metadataJson - JSON stringified metadata
|
|
298
|
+
* @returns Parsed encryption metadata
|
|
299
|
+
*/ static importMetadata(metadataJson) {
|
|
300
|
+
return JSON.parse(metadataJson);
|
|
301
|
+
}
|
|
302
|
+
// ============================================================================
|
|
303
|
+
// Private Helper Methods
|
|
304
|
+
// ============================================================================
|
|
305
|
+
isEncryptionEnabled() {
|
|
306
|
+
const enabled = process.env.BACKUP_ENCRYPTION_ENABLED?.toLowerCase() === 'true';
|
|
307
|
+
return enabled;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Singleton instance
|
|
312
|
+
*/ let defaultManager = null;
|
|
313
|
+
/**
|
|
314
|
+
* Get the default encryption manager instance
|
|
315
|
+
*/ export function getEncryptionManager(config) {
|
|
316
|
+
if (!defaultManager) {
|
|
317
|
+
defaultManager = new EncryptionManager(config);
|
|
318
|
+
}
|
|
319
|
+
return defaultManager;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
//# sourceMappingURL=encryption-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/encryption-manager.ts"],"sourcesContent":["/**\r\n * Backup Encryption Manager\r\n *\r\n * Implements AES-256-GCM encryption for backup files with:\r\n * - Secure key management via environment variables\r\n * - Unique IV generation for each backup\r\n * - HMAC-SHA256 integrity verification\r\n * - Key rotation support\r\n * - Backward compatibility detection\r\n * - Encryption metadata tracking\r\n *\r\n * CVSS Mitigation: Addresses CVSS 7.2 (high severity) vulnerability\r\n * by encrypting sensitive backup data at rest.\r\n *\r\n * Security Requirements:\r\n * - BACKUP_ENCRYPTION_KEY: 32-byte hex encoded AES-256 key (env var)\r\n * - BACKUP_ENCRYPTION_ENABLED: Set to 'true' to enable encryption (env var)\r\n * - IV: Cryptographically random, unique per backup\r\n * - Auth Tag: GCM authentication tag for integrity verification\r\n * - HMAC: SHA-256 for additional integrity verification\r\n *\r\n * Usage:\r\n * const encryptionManager = new EncryptionManager();\r\n * const encrypted = await encryptionManager.encrypt(buffer, backupId);\r\n * const decrypted = await encryptionManager.decrypt(encrypted, backupId);\r\n * const verified = encryptionManager.verifyIntegrity(encrypted);\r\n */\r\n\r\nimport * as crypto from 'crypto';\r\nimport { createLogger } from './logging.js';\r\nimport { createError, ErrorCode } from './errors.js';\r\n\r\nconst logger = createLogger('encryption-manager');\r\n\r\n/**\r\n * Encryption metadata stored with encrypted backup\r\n */\r\nexport interface EncryptionMetadata {\r\n /** Encryption algorithm identifier */\r\n algorithm: 'AES-256-GCM';\r\n /** Initialization Vector (hex encoded) */\r\n iv: string;\r\n /** GCM Authentication Tag (hex encoded) */\r\n authTag: string;\r\n /** HMAC verification tag (hex encoded) */\r\n hmac: string;\r\n /** When encryption was applied */\r\n encryptedAt: string;\r\n /** Master key version/ID for key rotation support */\r\n keyVersion: string;\r\n}\r\n\r\n/**\r\n * Encrypted backup payload\r\n */\r\nexport interface EncryptedBackup {\r\n /** Encrypted file content */\r\n data: Buffer;\r\n /** Encryption metadata */\r\n metadata: EncryptionMetadata;\r\n}\r\n\r\n/**\r\n * Decryption result\r\n */\r\nexport interface DecryptionResult {\r\n /** Decrypted content */\r\n data: Buffer;\r\n /** Whether integrity verification passed */\r\n integrityVerified: boolean;\r\n /** Metadata about the encryption */\r\n metadata: EncryptionMetadata;\r\n}\r\n\r\n/**\r\n * Encryption Manager Configuration\r\n */\r\nexport interface EncryptionConfig {\r\n /** Enable encryption (default: from BACKUP_ENCRYPTION_ENABLED env var) */\r\n enabled?: boolean;\r\n /** Master encryption key (32-byte hex, from BACKUP_ENCRYPTION_KEY env var) */\r\n masterKey?: string;\r\n /** Key version ID for rotation tracking (default: 'v1') */\r\n keyVersion?: string;\r\n}\r\n\r\n/**\r\n * Backup Encryption Manager\r\n *\r\n * Implements AES-256-GCM encryption with integrity verification\r\n */\r\nexport class EncryptionManager {\r\n private enabled: boolean;\r\n private masterKey: Buffer | null = null;\r\n private keyVersion: string;\r\n private readonly ALGORITHM = 'aes-256-gcm';\r\n private readonly IV_LENGTH = 16; // 128 bits\r\n private readonly AUTH_TAG_LENGTH = 16; // 128 bits\r\n private readonly KEY_LENGTH = 32; // 256 bits\r\n private readonly HMAC_ALGORITHM = 'sha256';\r\n\r\n constructor(config: EncryptionConfig = {}) {\r\n // Check if encryption is enabled\r\n this.enabled = config.enabled !== undefined ? config.enabled : this.isEncryptionEnabled();\r\n\r\n // Load and validate master key\r\n if (this.enabled) {\r\n const keyHex = config.masterKey || process.env.BACKUP_ENCRYPTION_KEY;\r\n\r\n if (!keyHex) {\r\n throw createError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'BACKUP_ENCRYPTION_KEY environment variable is required when encryption is enabled',\r\n { enabledVia: 'BACKUP_ENCRYPTION_ENABLED env var' }\r\n );\r\n }\r\n\r\n try {\r\n this.masterKey = Buffer.from(keyHex, 'hex');\r\n\r\n if (this.masterKey.length !== this.KEY_LENGTH) {\r\n throw createError(\r\n ErrorCode.VALIDATION_FAILED,\r\n `Master key must be exactly ${this.KEY_LENGTH} bytes (${this.KEY_LENGTH * 2} hex characters)`,\r\n { providedLength: this.masterKey.length, expectedLength: this.KEY_LENGTH }\r\n );\r\n }\r\n\r\n logger.info('Encryption manager initialized with AES-256-GCM', {\r\n enabled: true,\r\n algorithm: this.ALGORITHM,\r\n keyLength: this.KEY_LENGTH,\r\n ivLength: this.IV_LENGTH,\r\n authTagLength: this.AUTH_TAG_LENGTH,\r\n });\r\n } catch (error) {\r\n if (error instanceof Error && 'code' in error) {\r\n throw error;\r\n }\r\n\r\n throw createError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Failed to parse BACKUP_ENCRYPTION_KEY. Must be valid hex-encoded 32-byte key.',\r\n { error: error instanceof Error ? error.message : String(error) }\r\n );\r\n }\r\n } else {\r\n logger.info('Encryption manager initialized with encryption disabled', {\r\n enabled: false,\r\n enablementRequired:\r\n 'Set BACKUP_ENCRYPTION_ENABLED=true and provide BACKUP_ENCRYPTION_KEY to enable encryption',\r\n });\r\n }\r\n\r\n this.keyVersion = config.keyVersion || 'v1';\r\n }\r\n\r\n /**\r\n * Check if encryption is enabled\r\n */\r\n isEnabled(): boolean {\r\n return this.enabled;\r\n }\r\n\r\n /**\r\n * Encrypt backup data\r\n *\r\n * @param data - Data to encrypt\r\n * @param backupId - Backup identifier (for logging/tracking)\r\n * @returns Encrypted backup with metadata\r\n * @throws Error if encryption is disabled or encryption fails\r\n */\r\n async encrypt(data: Buffer, backupId: string): Promise<EncryptedBackup> {\r\n if (!this.enabled || !this.masterKey) {\r\n throw createError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Encryption is not enabled. Enable BACKUP_ENCRYPTION_ENABLED and provide BACKUP_ENCRYPTION_KEY.',\r\n { backupId }\r\n );\r\n }\r\n\r\n try {\r\n // Generate cryptographically random IV\r\n const iv = crypto.randomBytes(this.IV_LENGTH);\r\n\r\n // Create cipher\r\n const cipher = crypto.createCipheriv(this.ALGORITHM, this.masterKey, iv);\r\n\r\n // Encrypt data\r\n let encryptedData = cipher.update(data);\r\n encryptedData = Buffer.concat([encryptedData, cipher.final()]);\r\n\r\n // Get authentication tag\r\n const authTag = cipher.getAuthTag();\r\n\r\n // Calculate HMAC for additional integrity verification\r\n const hmac = crypto\r\n .createHmac(this.HMAC_ALGORITHM, this.masterKey)\r\n .update(Buffer.concat([iv, encryptedData, authTag]))\r\n .digest();\r\n\r\n // Create metadata\r\n const metadata: EncryptionMetadata = {\r\n algorithm: 'AES-256-GCM',\r\n iv: iv.toString('hex'),\r\n authTag: authTag.toString('hex'),\r\n hmac: hmac.toString('hex'),\r\n encryptedAt: new Date().toISOString(),\r\n keyVersion: this.keyVersion,\r\n };\r\n\r\n logger.debug('Backup encrypted successfully', {\r\n backupId,\r\n algorithm: this.ALGORITHM,\r\n dataSize: data.length,\r\n encryptedSize: encryptedData.length,\r\n ivLength: iv.length,\r\n authTagLength: authTag.length,\r\n });\r\n\r\n return {\r\n data: encryptedData,\r\n metadata,\r\n };\r\n } catch (error) {\r\n logger.error(\r\n 'Backup encryption failed',\r\n error instanceof Error ? error : undefined,\r\n { backupId }\r\n );\r\n\r\n throw createError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Backup encryption failed',\r\n {\r\n backupId,\r\n error: error instanceof Error ? error.message : String(error),\r\n }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Decrypt backup data\r\n *\r\n * @param encrypted - Encrypted backup\r\n * @param backupId - Backup identifier (for logging/tracking)\r\n * @returns Decryption result with data and integrity verification\r\n * @throws Error if decryption fails or integrity verification fails\r\n */\r\n async decrypt(encrypted: EncryptedBackup, backupId: string): Promise<DecryptionResult> {\r\n if (!this.enabled || !this.masterKey) {\r\n throw createError(\r\n ErrorCode.VALIDATION_FAILED,\r\n 'Encryption is not enabled. Enable BACKUP_ENCRYPTION_ENABLED and provide BACKUP_ENCRYPTION_KEY.',\r\n { backupId }\r\n );\r\n }\r\n\r\n try {\r\n // Verify HMAC first\r\n const iv = Buffer.from(encrypted.metadata.iv, 'hex');\r\n const authTag = Buffer.from(encrypted.metadata.authTag, 'hex');\r\n const expectedHmac = encrypted.metadata.hmac;\r\n\r\n const calculatedHmac = crypto\r\n .createHmac(this.HMAC_ALGORITHM, this.masterKey)\r\n .update(Buffer.concat([iv, encrypted.data, authTag]))\r\n .digest()\r\n .toString('hex');\r\n\r\n const integrityVerified = calculatedHmac === expectedHmac;\r\n\r\n if (!integrityVerified) {\r\n logger.warn('HMAC verification failed during decryption', {\r\n backupId,\r\n expectedHmac,\r\n calculatedHmac,\r\n });\r\n }\r\n\r\n // Decrypt data\r\n const decipher = crypto.createDecipheriv(this.ALGORITHM, this.masterKey, iv);\r\n decipher.setAuthTag(authTag);\r\n\r\n let decryptedData = decipher.update(encrypted.data);\r\n decryptedData = Buffer.concat([decryptedData, decipher.final()]);\r\n\r\n logger.debug('Backup decrypted successfully', {\r\n backupId,\r\n algorithm: this.ALGORITHM,\r\n encryptedSize: encrypted.data.length,\r\n decryptedSize: decryptedData.length,\r\n integrityVerified,\r\n });\r\n\r\n return {\r\n data: decryptedData,\r\n integrityVerified,\r\n metadata: encrypted.metadata,\r\n };\r\n } catch (error) {\r\n logger.error(\r\n 'Backup decryption failed',\r\n error instanceof Error ? error : undefined,\r\n { backupId }\r\n );\r\n\r\n throw createError(\r\n ErrorCode.INTERNAL_ERROR,\r\n 'Backup decryption failed. Backup may be corrupted or key may be incorrect.',\r\n {\r\n backupId,\r\n error: error instanceof Error ? error.message : String(error),\r\n }\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Verify backup integrity without decryption\r\n *\r\n * @param encrypted - Encrypted backup\r\n * @param backupId - Backup identifier (for logging)\r\n * @returns Whether integrity verification passed\r\n */\r\n verifyIntegrity(encrypted: EncryptedBackup, backupId?: string): boolean {\r\n if (!this.masterKey) {\r\n logger.error('Cannot verify integrity without master key', { backupId });\r\n return false;\r\n }\r\n\r\n try {\r\n const iv = Buffer.from(encrypted.metadata.iv, 'hex');\r\n const authTag = Buffer.from(encrypted.metadata.authTag, 'hex');\r\n const expectedHmac = encrypted.metadata.hmac;\r\n\r\n const calculatedHmac = crypto\r\n .createHmac(this.HMAC_ALGORITHM, this.masterKey)\r\n .update(Buffer.concat([iv, encrypted.data, authTag]))\r\n .digest()\r\n .toString('hex');\r\n\r\n const verified = calculatedHmac === expectedHmac;\r\n\r\n if (!verified) {\r\n logger.warn('Integrity verification failed', { backupId });\r\n }\r\n\r\n return verified;\r\n } catch (error) {\r\n logger.error('Integrity verification error', error instanceof Error ? error : undefined, {\r\n backupId,\r\n });\r\n\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Detect if a backup file is encrypted\r\n *\r\n * Useful for backward compatibility - detects if backup is encrypted\r\n *\r\n * @param data - File data\r\n * @returns Whether the data appears to be encrypted\r\n */\r\n static isEncrypted(data: Buffer): boolean {\r\n // Encrypted data with metadata should have a specific structure\r\n // For now, we can check if the data is valid JSON metadata (simple heuristic)\r\n try {\r\n // Try to detect our encryption format\r\n // This is a simple heuristic - encrypted data won't be valid text/JSON\r\n if (data.length < 100) {\r\n return false;\r\n }\r\n\r\n // Try to parse as JSON at the end for metadata\r\n const str = data.toString('utf8', Math.max(0, data.length - 500));\r\n const match = str.match(/\"algorithm\"\\s*:\\s*\"AES-256-GCM\"/);\r\n\r\n return match !== null;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Generate a new encryption key\r\n *\r\n * Useful for key rotation\r\n *\r\n * @returns 32-byte hex-encoded AES-256 key\r\n */\r\n static generateKey(): string {\r\n return crypto.randomBytes(32).toString('hex');\r\n }\r\n\r\n /**\r\n * Export encryption metadata as JSON\r\n *\r\n * Useful for storing metadata separately from encrypted data\r\n *\r\n * @param encrypted - Encrypted backup\r\n * @returns JSON stringified metadata\r\n */\r\n static exportMetadata(encrypted: EncryptedBackup): string {\r\n return JSON.stringify(encrypted.metadata, null, 2);\r\n }\r\n\r\n /**\r\n * Import encryption metadata from JSON\r\n *\r\n * @param metadataJson - JSON stringified metadata\r\n * @returns Parsed encryption metadata\r\n */\r\n static importMetadata(metadataJson: string): EncryptionMetadata {\r\n return JSON.parse(metadataJson) as EncryptionMetadata;\r\n }\r\n\r\n // ============================================================================\r\n // Private Helper Methods\r\n // ============================================================================\r\n\r\n private isEncryptionEnabled(): boolean {\r\n const enabled = process.env.BACKUP_ENCRYPTION_ENABLED?.toLowerCase() === 'true';\r\n return enabled;\r\n }\r\n}\r\n\r\n/**\r\n * Singleton instance\r\n */\r\nlet defaultManager: EncryptionManager | null = null;\r\n\r\n/**\r\n * Get the default encryption manager instance\r\n */\r\nexport function getEncryptionManager(config?: EncryptionConfig): EncryptionManager {\r\n if (!defaultManager) {\r\n defaultManager = new EncryptionManager(config);\r\n }\r\n return defaultManager;\r\n}\r\n"],"names":["crypto","createLogger","createError","ErrorCode","logger","EncryptionManager","enabled","masterKey","keyVersion","ALGORITHM","IV_LENGTH","AUTH_TAG_LENGTH","KEY_LENGTH","HMAC_ALGORITHM","config","undefined","isEncryptionEnabled","keyHex","process","env","BACKUP_ENCRYPTION_KEY","VALIDATION_FAILED","enabledVia","Buffer","from","length","providedLength","expectedLength","info","algorithm","keyLength","ivLength","authTagLength","error","Error","message","String","enablementRequired","isEnabled","encrypt","data","backupId","iv","randomBytes","cipher","createCipheriv","encryptedData","update","concat","final","authTag","getAuthTag","hmac","createHmac","digest","metadata","toString","encryptedAt","Date","toISOString","debug","dataSize","encryptedSize","INTERNAL_ERROR","decrypt","encrypted","expectedHmac","calculatedHmac","integrityVerified","warn","decipher","createDecipheriv","setAuthTag","decryptedData","decryptedSize","verifyIntegrity","verified","isEncrypted","str","Math","max","match","generateKey","exportMetadata","JSON","stringify","importMetadata","metadataJson","parse","BACKUP_ENCRYPTION_ENABLED","toLowerCase","defaultManager","getEncryptionManager"],"mappings":"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"}
|