claude-mpm 5.4.55__py3-none-any.whl → 5.6.1__py3-none-any.whl
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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
- claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +109 -1925
- claude_mpm/agents/PM_INSTRUCTIONS.md +111 -686
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/cli/__init__.py +5 -1
- claude_mpm/cli/commands/agents.py +2 -4
- claude_mpm/cli/commands/agents_reconcile.py +197 -0
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +46 -0
- claude_mpm/cli/commands/configure.py +620 -21
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/commands/skills.py +166 -14
- claude_mpm/cli/executor.py +120 -16
- claude_mpm/cli/interactive/__init__.py +10 -0
- claude_mpm/cli/interactive/agent_wizard.py +30 -50
- claude_mpm/cli/interactive/questionary_styles.py +65 -0
- claude_mpm/cli/interactive/skill_selector.py +481 -0
- claude_mpm/cli/parsers/base_parser.py +76 -1
- claude_mpm/cli/parsers/commander_parser.py +83 -0
- claude_mpm/cli/parsers/run_parser.py +10 -0
- claude_mpm/cli/startup.py +276 -403
- claude_mpm/cli/startup_display.py +72 -5
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/cli/utils.py +7 -3
- claude_mpm/commander/__init__.py +72 -0
- claude_mpm/commander/adapters/__init__.py +31 -0
- claude_mpm/commander/adapters/base.py +191 -0
- claude_mpm/commander/adapters/claude_code.py +361 -0
- claude_mpm/commander/adapters/communication.py +366 -0
- claude_mpm/commander/api/__init__.py +16 -0
- claude_mpm/commander/api/app.py +105 -0
- claude_mpm/commander/api/errors.py +112 -0
- claude_mpm/commander/api/routes/__init__.py +8 -0
- claude_mpm/commander/api/routes/events.py +184 -0
- claude_mpm/commander/api/routes/inbox.py +171 -0
- claude_mpm/commander/api/routes/messages.py +148 -0
- claude_mpm/commander/api/routes/projects.py +271 -0
- claude_mpm/commander/api/routes/sessions.py +215 -0
- claude_mpm/commander/api/routes/work.py +260 -0
- claude_mpm/commander/api/schemas.py +182 -0
- claude_mpm/commander/chat/__init__.py +7 -0
- claude_mpm/commander/chat/cli.py +107 -0
- claude_mpm/commander/chat/commands.py +96 -0
- claude_mpm/commander/chat/repl.py +310 -0
- claude_mpm/commander/config.py +49 -0
- claude_mpm/commander/config_loader.py +115 -0
- claude_mpm/commander/daemon.py +398 -0
- claude_mpm/commander/events/__init__.py +26 -0
- claude_mpm/commander/events/manager.py +332 -0
- claude_mpm/commander/frameworks/__init__.py +12 -0
- claude_mpm/commander/frameworks/base.py +143 -0
- claude_mpm/commander/frameworks/claude_code.py +58 -0
- claude_mpm/commander/frameworks/mpm.py +62 -0
- claude_mpm/commander/inbox/__init__.py +16 -0
- claude_mpm/commander/inbox/dedup.py +128 -0
- claude_mpm/commander/inbox/inbox.py +224 -0
- claude_mpm/commander/inbox/models.py +70 -0
- claude_mpm/commander/instance_manager.py +337 -0
- claude_mpm/commander/llm/__init__.py +6 -0
- claude_mpm/commander/llm/openrouter_client.py +167 -0
- claude_mpm/commander/llm/summarizer.py +70 -0
- claude_mpm/commander/models/__init__.py +18 -0
- claude_mpm/commander/models/events.py +121 -0
- claude_mpm/commander/models/project.py +162 -0
- claude_mpm/commander/models/work.py +214 -0
- claude_mpm/commander/parsing/__init__.py +20 -0
- claude_mpm/commander/parsing/extractor.py +132 -0
- claude_mpm/commander/parsing/output_parser.py +270 -0
- claude_mpm/commander/parsing/patterns.py +100 -0
- claude_mpm/commander/persistence/__init__.py +11 -0
- claude_mpm/commander/persistence/event_store.py +274 -0
- claude_mpm/commander/persistence/state_store.py +309 -0
- claude_mpm/commander/persistence/work_store.py +164 -0
- claude_mpm/commander/polling/__init__.py +13 -0
- claude_mpm/commander/polling/event_detector.py +104 -0
- claude_mpm/commander/polling/output_buffer.py +49 -0
- claude_mpm/commander/polling/output_poller.py +153 -0
- claude_mpm/commander/project_session.py +268 -0
- claude_mpm/commander/proxy/__init__.py +12 -0
- claude_mpm/commander/proxy/formatter.py +89 -0
- claude_mpm/commander/proxy/output_handler.py +191 -0
- claude_mpm/commander/proxy/relay.py +155 -0
- claude_mpm/commander/registry.py +404 -0
- claude_mpm/commander/runtime/__init__.py +10 -0
- claude_mpm/commander/runtime/executor.py +191 -0
- claude_mpm/commander/runtime/monitor.py +316 -0
- claude_mpm/commander/session/__init__.py +6 -0
- claude_mpm/commander/session/context.py +81 -0
- claude_mpm/commander/session/manager.py +59 -0
- claude_mpm/commander/tmux_orchestrator.py +361 -0
- claude_mpm/commander/web/__init__.py +1 -0
- claude_mpm/commander/work/__init__.py +30 -0
- claude_mpm/commander/work/executor.py +189 -0
- claude_mpm/commander/work/queue.py +405 -0
- claude_mpm/commander/workflow/__init__.py +27 -0
- claude_mpm/commander/workflow/event_handler.py +219 -0
- claude_mpm/commander/workflow/notifier.py +146 -0
- claude_mpm/commands/mpm-config.md +8 -0
- claude_mpm/commands/mpm-doctor.md +8 -0
- claude_mpm/commands/mpm-help.md +8 -0
- claude_mpm/commands/mpm-init.md +8 -0
- claude_mpm/commands/mpm-monitor.md +8 -0
- claude_mpm/commands/mpm-organize.md +8 -0
- claude_mpm/commands/mpm-postmortem.md +8 -0
- claude_mpm/commands/mpm-session-resume.md +9 -1
- claude_mpm/commands/mpm-status.md +8 -0
- claude_mpm/commands/mpm-ticket-view.md +8 -0
- claude_mpm/commands/mpm-version.md +8 -0
- claude_mpm/commands/mpm.md +8 -0
- claude_mpm/config/agent_presets.py +8 -7
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +2 -2
- claude_mpm/core/config.py +5 -0
- claude_mpm/core/hook_manager.py +51 -3
- claude_mpm/core/interactive_session.py +7 -7
- claude_mpm/core/logger.py +10 -7
- claude_mpm/core/logging_utils.py +4 -2
- claude_mpm/core/output_style_manager.py +31 -13
- claude_mpm/core/unified_config.py +54 -8
- claude_mpm/core/unified_paths.py +30 -13
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +9 -9
- claude_mpm/experimental/cli_enhancements.py +2 -1
- claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
- claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +486 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +250 -11
- claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +69 -5
- claude_mpm/hooks/claude_hooks/response_tracking.py +3 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +20 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +14 -77
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +30 -6
- claude_mpm/hooks/session_resume_hook.py +85 -1
- claude_mpm/init.py +1 -1
- claude_mpm/scripts/claude-hook-handler.sh +36 -10
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_template_builder.py +8 -0
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/agents/sources/git_source_sync_service.py +7 -4
- claude_mpm/services/agents/startup_sync.py +5 -2
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
- claude_mpm/services/diagnostics/models.py +14 -1
- claude_mpm/services/event_log.py +325 -0
- claude_mpm/services/infrastructure/__init__.py +4 -0
- claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -2
- claude_mpm/services/monitor/server.py +106 -16
- claude_mpm/services/pm_skills_deployer.py +261 -85
- claude_mpm/services/skills/git_skill_source_manager.py +75 -10
- claude_mpm/services/skills/selective_skill_deployer.py +177 -80
- claude_mpm/services/skills/skill_discovery_service.py +57 -3
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
- claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
- claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
- claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
- claude_mpm/skills/bundled/pm/mpm-delegation-patterns/SKILL.md +167 -0
- claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
- claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -0
- claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
- claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
- claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
- claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
- claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
- claude_mpm/skills/bundled/pm/mpm-pr-workflow/SKILL.md +124 -0
- claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
- claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
- claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
- claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-ticketing-integration/SKILL.md +154 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
- claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
- claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
- claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
- claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
- claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
- claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
- claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
- claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
- claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
- claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm/utils/agent_dependency_loader.py +103 -4
- claude_mpm/utils/robust_installer.py +45 -24
- claude_mpm-5.6.1.dist-info/METADATA +391 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/RECORD +377 -166
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm-5.4.55.dist-info/METADATA +0 -999
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
# Environment Security Patterns
|
|
2
|
+
|
|
3
|
+
> **Part of**: [env-manager](../SKILL.md)
|
|
4
|
+
> **Category**: infrastructure
|
|
5
|
+
> **Reading Level**: Advanced
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Comprehensive security patterns for environment variables: secret detection, exposure scanning, git history validation, and format verification.
|
|
10
|
+
|
|
11
|
+
## Security Principles
|
|
12
|
+
|
|
13
|
+
### Never Log Secrets
|
|
14
|
+
**Critical Rule**: NEVER log, print, or display actual secret values in any output.
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
# ❌ NEVER DO THIS
|
|
18
|
+
print(f"API_KEY: {api_key}")
|
|
19
|
+
logging.info(f"Database password: {db_pass}")
|
|
20
|
+
error(f"Failed to connect with {credentials}")
|
|
21
|
+
|
|
22
|
+
# ✅ ALWAYS DO THIS
|
|
23
|
+
print(f"API_KEY: {'*' * len(api_key)}")
|
|
24
|
+
logging.info(f"Database credentials present: {bool(db_pass)}")
|
|
25
|
+
error(f"Failed to connect (credentials masked)")
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Defense in Depth
|
|
29
|
+
Multiple layers of secret protection:
|
|
30
|
+
1. **Prevention**: .gitignore, pre-commit hooks
|
|
31
|
+
2. **Detection**: Pattern scanning, entropy analysis
|
|
32
|
+
3. **Response**: Rotation procedures, incident handling
|
|
33
|
+
4. **Audit**: Git history scanning, access logs
|
|
34
|
+
|
|
35
|
+
## Secret Pattern Detection
|
|
36
|
+
|
|
37
|
+
### Common Secret Patterns
|
|
38
|
+
|
|
39
|
+
**AWS Credentials:**
|
|
40
|
+
```python
|
|
41
|
+
AWS_PATTERNS = {
|
|
42
|
+
'aws_access_key': re.compile(r'AKIA[0-9A-Z]{16}'),
|
|
43
|
+
'aws_secret_key': re.compile(r'[0-9a-zA-Z/+=]{40}'),
|
|
44
|
+
'aws_account_id': re.compile(r'\d{12}')
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**GitHub Tokens:**
|
|
49
|
+
```python
|
|
50
|
+
GITHUB_PATTERNS = {
|
|
51
|
+
'personal_token': re.compile(r'ghp_[0-9a-zA-Z]{36}'),
|
|
52
|
+
'oauth_token': re.compile(r'gho_[0-9a-zA-Z]{36}'),
|
|
53
|
+
'app_token': re.compile(r'(ghu|ghs)_[0-9a-zA-Z]{36}')
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**API Keys and Tokens:**
|
|
58
|
+
```python
|
|
59
|
+
GENERIC_PATTERNS = {
|
|
60
|
+
'jwt': re.compile(r'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+'),
|
|
61
|
+
'slack': re.compile(r'xox[baprs]-[0-9A-Za-z-]{10,72}'),
|
|
62
|
+
'stripe': re.compile(r'sk_(test|live)_[0-9a-zA-Z]{24,}'),
|
|
63
|
+
'mailgun': re.compile(r'key-[0-9a-z]{32}'),
|
|
64
|
+
'twilio': re.compile(r'SK[0-9a-f]{32}')
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Entropy-Based Detection
|
|
69
|
+
|
|
70
|
+
High entropy strings often indicate secrets:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import math
|
|
74
|
+
from collections import Counter
|
|
75
|
+
|
|
76
|
+
def calculate_entropy(data: str) -> float:
|
|
77
|
+
"""Calculate Shannon entropy of a string."""
|
|
78
|
+
if not data:
|
|
79
|
+
return 0.0
|
|
80
|
+
|
|
81
|
+
entropy = 0
|
|
82
|
+
counter = Counter(data)
|
|
83
|
+
length = len(data)
|
|
84
|
+
|
|
85
|
+
for count in counter.values():
|
|
86
|
+
probability = count / length
|
|
87
|
+
entropy -= probability * math.log2(probability)
|
|
88
|
+
|
|
89
|
+
return entropy
|
|
90
|
+
|
|
91
|
+
def is_high_entropy_secret(value: str, threshold: float = 4.5) -> bool:
|
|
92
|
+
"""Check if value has high entropy (likely a secret)."""
|
|
93
|
+
# Skip short values
|
|
94
|
+
if len(value) < 20:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
# Calculate entropy
|
|
98
|
+
entropy = calculate_entropy(value)
|
|
99
|
+
|
|
100
|
+
# High entropy suggests random generation
|
|
101
|
+
return entropy > threshold
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Secret Scanner Implementation
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from pathlib import Path
|
|
108
|
+
from typing import List, Dict
|
|
109
|
+
import re
|
|
110
|
+
|
|
111
|
+
class SecretScanner:
|
|
112
|
+
"""Scan for exposed secrets in code and config files."""
|
|
113
|
+
|
|
114
|
+
def __init__(self):
|
|
115
|
+
self.patterns = {
|
|
116
|
+
**AWS_PATTERNS,
|
|
117
|
+
**GITHUB_PATTERNS,
|
|
118
|
+
**GENERIC_PATTERNS
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
def scan_file(self, file_path: Path) -> List[Dict]:
|
|
122
|
+
"""Scan a single file for secrets."""
|
|
123
|
+
findings = []
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
with open(file_path) as f:
|
|
127
|
+
for line_num, line in enumerate(f, 1):
|
|
128
|
+
# Check against patterns
|
|
129
|
+
for secret_type, pattern in self.patterns.items():
|
|
130
|
+
matches = pattern.finditer(line)
|
|
131
|
+
for match in matches:
|
|
132
|
+
findings.append({
|
|
133
|
+
'file': str(file_path),
|
|
134
|
+
'line': line_num,
|
|
135
|
+
'type': secret_type,
|
|
136
|
+
'matched': self._mask_secret(match.group()),
|
|
137
|
+
'context': line[:50] + '...' if len(line) > 50 else line
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
# Check entropy
|
|
141
|
+
if '=' in line:
|
|
142
|
+
key, value = line.split('=', 1)
|
|
143
|
+
value = value.strip().strip('"\'')
|
|
144
|
+
if is_high_entropy_secret(value):
|
|
145
|
+
findings.append({
|
|
146
|
+
'file': str(file_path),
|
|
147
|
+
'line': line_num,
|
|
148
|
+
'type': 'high_entropy',
|
|
149
|
+
'key': key.strip(),
|
|
150
|
+
'entropy': calculate_entropy(value)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
except Exception as e:
|
|
154
|
+
logging.error(f"Error scanning {file_path}: {e}")
|
|
155
|
+
|
|
156
|
+
return findings
|
|
157
|
+
|
|
158
|
+
def _mask_secret(self, secret: str) -> str:
|
|
159
|
+
"""Mask a secret for display."""
|
|
160
|
+
if len(secret) <= 4:
|
|
161
|
+
return '*' * len(secret)
|
|
162
|
+
return secret[:2] + '*' * (len(secret) - 4) + secret[-2:]
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Git History Scanning
|
|
166
|
+
|
|
167
|
+
### Check for Historical Exposures
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
def scan_git_history(repo_path: Path, patterns: Dict) -> List[Dict]:
|
|
171
|
+
"""Scan git history for exposed secrets."""
|
|
172
|
+
try:
|
|
173
|
+
import git
|
|
174
|
+
except ImportError:
|
|
175
|
+
logging.warning("GitPython not installed, skipping history scan")
|
|
176
|
+
return []
|
|
177
|
+
|
|
178
|
+
findings = []
|
|
179
|
+
repo = git.Repo(repo_path)
|
|
180
|
+
|
|
181
|
+
# Scan last 100 commits
|
|
182
|
+
for commit in repo.iter_commits(max_count=100):
|
|
183
|
+
for file_path in commit.stats.files:
|
|
184
|
+
if file_path.endswith('.env'):
|
|
185
|
+
findings.append({
|
|
186
|
+
'commit': commit.hexsha[:8],
|
|
187
|
+
'file': file_path,
|
|
188
|
+
'author': commit.author.name,
|
|
189
|
+
'date': commit.committed_datetime,
|
|
190
|
+
'message': 'SECURITY: .env file in commit history'
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
return findings
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Gitignore Validation
|
|
197
|
+
|
|
198
|
+
### Ensure Proper Gitignore Coverage
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
def validate_gitignore(project_dir: Path) -> Dict:
|
|
202
|
+
"""Validate .gitignore covers sensitive files."""
|
|
203
|
+
gitignore_path = project_dir / '.gitignore'
|
|
204
|
+
|
|
205
|
+
if not gitignore_path.exists():
|
|
206
|
+
return {
|
|
207
|
+
'valid': False,
|
|
208
|
+
'errors': ['.gitignore file not found']
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
required_patterns = [
|
|
212
|
+
'.env',
|
|
213
|
+
'.env.local',
|
|
214
|
+
'.env.*.local',
|
|
215
|
+
'*.env'
|
|
216
|
+
]
|
|
217
|
+
|
|
218
|
+
with open(gitignore_path) as f:
|
|
219
|
+
gitignore_content = f.read()
|
|
220
|
+
|
|
221
|
+
missing = []
|
|
222
|
+
for pattern in required_patterns:
|
|
223
|
+
if pattern not in gitignore_content:
|
|
224
|
+
missing.append(pattern)
|
|
225
|
+
|
|
226
|
+
# Check if any .env files are tracked
|
|
227
|
+
tracked_env_files = []
|
|
228
|
+
try:
|
|
229
|
+
import git
|
|
230
|
+
repo = git.Repo(project_dir)
|
|
231
|
+
for item in repo.tree().traverse():
|
|
232
|
+
if '.env' in item.path and not item.path.endswith('.example'):
|
|
233
|
+
tracked_env_files.append(item.path)
|
|
234
|
+
except:
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
'valid': len(missing) == 0 and len(tracked_env_files) == 0,
|
|
239
|
+
'missing_patterns': missing,
|
|
240
|
+
'tracked_env_files': tracked_env_files
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Format Validation
|
|
245
|
+
|
|
246
|
+
### Validate Secret Formats
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
def validate_secret_formats(env_file: Path) -> List[Dict]:
|
|
250
|
+
"""Validate that secrets match expected formats."""
|
|
251
|
+
errors = []
|
|
252
|
+
|
|
253
|
+
format_rules = {
|
|
254
|
+
'DATABASE_URL': r'^(postgres|mysql|mongodb)://',
|
|
255
|
+
'JWT_SECRET': lambda v: len(v) >= 32,
|
|
256
|
+
'API_KEY': lambda v: len(v) >= 20,
|
|
257
|
+
'STRIPE_KEY': r'^sk_(test|live)_',
|
|
258
|
+
'AWS_ACCESS_KEY_ID': r'^AKIA[0-9A-Z]{16}$'
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
with open(env_file) as f:
|
|
262
|
+
for line_num, line in enumerate(f, 1):
|
|
263
|
+
line = line.strip()
|
|
264
|
+
if not line or line.startswith('#') or '=' not in line:
|
|
265
|
+
continue
|
|
266
|
+
|
|
267
|
+
key, value = line.split('=', 1)
|
|
268
|
+
value = value.strip().strip('"\'')
|
|
269
|
+
|
|
270
|
+
if key in format_rules:
|
|
271
|
+
rule = format_rules[key]
|
|
272
|
+
|
|
273
|
+
if callable(rule):
|
|
274
|
+
if not rule(value):
|
|
275
|
+
errors.append({
|
|
276
|
+
'line': line_num,
|
|
277
|
+
'key': key,
|
|
278
|
+
'error': f'{key} validation failed'
|
|
279
|
+
})
|
|
280
|
+
elif isinstance(rule, str):
|
|
281
|
+
if not re.match(rule, value):
|
|
282
|
+
errors.append({
|
|
283
|
+
'line': line_num,
|
|
284
|
+
'key': key,
|
|
285
|
+
'error': f'{key} format invalid'
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
return errors
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Security Best Practices
|
|
292
|
+
|
|
293
|
+
### Environment-Specific Secrets
|
|
294
|
+
|
|
295
|
+
**Development**:
|
|
296
|
+
```bash
|
|
297
|
+
# .env.local (gitignored, local development only)
|
|
298
|
+
DATABASE_URL=postgres://localhost:5432/dev
|
|
299
|
+
JWT_SECRET=dev-secret-not-for-production
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Production**:
|
|
303
|
+
```bash
|
|
304
|
+
# Set via platform (Vercel, Railway, etc.)
|
|
305
|
+
# NEVER commit production secrets
|
|
306
|
+
DATABASE_URL=<from_secret_manager>
|
|
307
|
+
JWT_SECRET=<from_secret_manager>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Secret Rotation Procedures
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
def check_secret_age(env_file: Path) -> Dict:
|
|
314
|
+
"""Check when secrets were last rotated."""
|
|
315
|
+
import os
|
|
316
|
+
from datetime import datetime, timedelta
|
|
317
|
+
|
|
318
|
+
file_modified = datetime.fromtimestamp(os.path.getmtime(env_file))
|
|
319
|
+
age_days = (datetime.now() - file_modified).days
|
|
320
|
+
|
|
321
|
+
recommendations = []
|
|
322
|
+
if age_days > 90:
|
|
323
|
+
recommendations.append({
|
|
324
|
+
'severity': 'warning',
|
|
325
|
+
'message': f'Secrets are {age_days} days old. Consider rotation.'
|
|
326
|
+
})
|
|
327
|
+
if age_days > 180:
|
|
328
|
+
recommendations.append({
|
|
329
|
+
'severity': 'error',
|
|
330
|
+
'message': f'Secrets are {age_days} days old. MUST rotate.'
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
'last_modified': file_modified.isoformat(),
|
|
335
|
+
'age_days': age_days,
|
|
336
|
+
'recommendations': recommendations
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Incident Response
|
|
341
|
+
|
|
342
|
+
### Secret Exposure Recovery
|
|
343
|
+
|
|
344
|
+
**If secrets are exposed**:
|
|
345
|
+
|
|
346
|
+
1. **Immediate Actions**:
|
|
347
|
+
```bash
|
|
348
|
+
# Revoke exposed credentials
|
|
349
|
+
# Rotate all affected secrets
|
|
350
|
+
# Check access logs for unauthorized use
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
2. **Git History Cleanup**:
|
|
354
|
+
```bash
|
|
355
|
+
# Use BFG Repo-Cleaner to remove secrets from history
|
|
356
|
+
bfg --replace-text passwords.txt
|
|
357
|
+
git reflog expire --expire=now --all
|
|
358
|
+
git gc --prune=now --aggressive
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
3. **Platform Updates**:
|
|
362
|
+
```bash
|
|
363
|
+
# Update all deployment platforms
|
|
364
|
+
python scripts/sync_secrets.py --platform vercel --sync
|
|
365
|
+
python scripts/sync_secrets.py --platform railway --sync
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Validation Error Messages
|
|
369
|
+
|
|
370
|
+
### CRITICAL: Never Expose Values in Error Messages
|
|
371
|
+
|
|
372
|
+
**Security Fix (2025-11-13)**: All validation error messages have been hardened to prevent accidental secret exposure.
|
|
373
|
+
|
|
374
|
+
**Problem**: Error messages that include actual variable values can leak secrets in:
|
|
375
|
+
- CI/CD logs
|
|
376
|
+
- Error tracking systems (Sentry, etc.)
|
|
377
|
+
- Terminal output screenshots
|
|
378
|
+
- Bug reports
|
|
379
|
+
|
|
380
|
+
**Solution**: Error messages NEVER include actual values, only validation criteria.
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
# ❌ NEVER DO THIS - Exposes actual value
|
|
384
|
+
f'Invalid value "{vars_dict["NODE_ENV"]}", expected one of {valid_values}'
|
|
385
|
+
|
|
386
|
+
# ✅ ALWAYS DO THIS - Safe message
|
|
387
|
+
f'Invalid value for NODE_ENV, expected one of {valid_values}'
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**Validation Script Protection**:
|
|
391
|
+
The `validate_env.py` script has been hardened against value exposure:
|
|
392
|
+
- Line 365: NODE_ENV validation error message sanitized
|
|
393
|
+
- All error messages verified to exclude variable values
|
|
394
|
+
- Test coverage added: `test_no_secret_exposure_in_errors`
|
|
395
|
+
|
|
396
|
+
**Testing**:
|
|
397
|
+
```bash
|
|
398
|
+
# Verify no secret exposure
|
|
399
|
+
echo 'NODE_ENV=sk-proj-fake-secret' > test.env
|
|
400
|
+
python validate_env.py test.env --framework nodejs
|
|
401
|
+
# Output: "Invalid value for NODE_ENV, expected..."
|
|
402
|
+
# NOT: "Invalid value 'sk-proj-fake-secret', expected..."
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Summary
|
|
406
|
+
|
|
407
|
+
**Security Checklist**:
|
|
408
|
+
- [ ] Never log actual secret values
|
|
409
|
+
- [ ] Never expose values in error messages
|
|
410
|
+
- [ ] .env files in .gitignore
|
|
411
|
+
- [ ] No secrets in git history
|
|
412
|
+
- [ ] Pattern-based scanning enabled
|
|
413
|
+
- [ ] Entropy analysis for unknowns
|
|
414
|
+
- [ ] Secret format validation
|
|
415
|
+
- [ ] Regular secret rotation (90 days)
|
|
416
|
+
- [ ] Incident response plan ready
|
|
417
|
+
|
|
418
|
+
**Key Patterns**:
|
|
419
|
+
- ✅ Pattern-based detection (AWS, GitHub, etc.)
|
|
420
|
+
- ✅ Entropy analysis for random secrets
|
|
421
|
+
- ✅ Git history scanning
|
|
422
|
+
- ✅ .gitignore validation
|
|
423
|
+
- ✅ Format validation
|
|
424
|
+
- ✅ Secret masking in output
|
|
425
|
+
|
|
426
|
+
## Related References
|
|
427
|
+
|
|
428
|
+
- [Validation](validation.md): Environment validation workflows
|
|
429
|
+
- [Synchronization](synchronization.md): Secure platform sync
|
|
430
|
+
- [Troubleshooting](troubleshooting.md): Security issue recovery
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
**Lines**: 267 ✓ 200-280 range
|