mindforge-cc 11.8.2 → 11.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/mindforge/wf-orchestrate-review.md +35 -0
- package/.agent/mindforge/wf-verification-loop.md +45 -0
- package/.agent/mindforge/wf-workflow-optimizer.md +51 -0
- package/.agent/skills/article-illustrator/SKILL.md +7 -7
- package/.agent/skills/arxiv/SKILL.md +6 -6
- package/.agent/skills/code-wiki/SKILL.md +11 -11
- package/.agent/skills/concept-diagrams/SKILL.md +6 -6
- package/.agent/skills/concept-diagrams/examples/apartment-floor-plan-conversion.md +244 -0
- package/.agent/skills/concept-diagrams/examples/automated-password-reset-flow.md +276 -0
- package/.agent/skills/concept-diagrams/examples/autonomous-llm-research-agent-flow.md +240 -0
- package/.agent/skills/concept-diagrams/examples/banana-journey-tree-to-smoothie.md +161 -0
- package/.agent/skills/concept-diagrams/examples/commercial-aircraft-structure.md +209 -0
- package/.agent/skills/concept-diagrams/examples/cpu-ooo-microarchitecture.md +236 -0
- package/.agent/skills/concept-diagrams/examples/electricity-grid-flow.md +182 -0
- package/.agent/skills/concept-diagrams/examples/feature-film-production-pipeline.md +172 -0
- package/.agent/skills/concept-diagrams/examples/hospital-emergency-department-flow.md +165 -0
- package/.agent/skills/concept-diagrams/examples/ml-benchmark-grouped-bar-chart.md +114 -0
- package/.agent/skills/concept-diagrams/examples/place-order-uml-sequence.md +325 -0
- package/.agent/skills/concept-diagrams/examples/smart-city-infrastructure.md +173 -0
- package/.agent/skills/concept-diagrams/examples/smartphone-layer-anatomy.md +154 -0
- package/.agent/skills/concept-diagrams/examples/sn2-reaction-mechanism.md +247 -0
- package/.agent/skills/concept-diagrams/examples/wind-turbine-structure.md +338 -0
- package/.agent/skills/concept-diagrams/templates/template.html +174 -0
- package/.agent/skills/domain-intel/SKILL.md +7 -7
- package/.agent/skills/duckduckgo-search/SKILL.md +17 -17
- package/.agent/skills/github-auth/scripts/gh-env.sh +66 -0
- package/.agent/skills/github-code-review/SKILL.md +9 -9
- package/.agent/skills/github-pr-workflow/SKILL.md +3 -3
- package/.agent/skills/godmode/SKILL.md +8 -8
- package/.agent/skills/jupyter-live-kernel/SKILL.md +3 -3
- package/.agent/skills/kanban-orchestrator/SKILL.md +10 -10
- package/.agent/skills/kanban-worker/SKILL.md +6 -6
- package/.agent/skills/llm-wiki/SKILL.md +7 -7
- package/.agent/skills/mindforge-autonomous/SKILL.md +1 -1
- package/.agent/skills/mindforge-complete-milestone/SKILL.md +1 -1
- package/.agent/skills/mindforge-discuss-phase/SKILL.md +1 -1
- package/.agent/skills/mindforge-do/SKILL.md +1 -1
- package/.agent/skills/mindforge-execute-phase/SKILL.md +1 -1
- package/.agent/skills/mindforge-list-workspaces/SKILL.md +1 -1
- package/.agent/skills/mindforge-manager/SKILL.md +1 -1
- package/.agent/skills/mindforge-neural-orchestrator/SKILL.md +1 -1
- package/.agent/skills/mindforge-new-milestone/SKILL.md +4 -4
- package/.agent/skills/mindforge-new-project/SKILL.md +4 -4
- package/.agent/skills/mindforge-new-workspace/SKILL.md +1 -1
- package/.agent/skills/mindforge-note/SKILL.md +1 -1
- package/.agent/skills/mindforge-plan-phase/SKILL.md +1 -1
- package/.agent/skills/mindforge-profile-user/SKILL.md +1 -1
- package/.agent/skills/mindforge-remove-workspace/SKILL.md +1 -1
- package/.agent/skills/mindforge-research-phase/SKILL.md +1 -1
- package/.agent/skills/mindforge-ui-phase/SKILL.md +1 -1
- package/.agent/skills/mindforge-ui-review/SKILL.md +1 -1
- package/.agent/skills/mindforge-verify-work/SKILL.md +1 -1
- package/.agent/skills/mindforge-workspace-isolated/SKILL.md +1 -1
- package/.agent/skills/node-inspect-debugger/SKILL.md +1 -1
- package/.agent/skills/obsidian/SKILL.md +15 -15
- package/.agent/skills/osint-investigation/SKILL.md +3 -2
- package/.agent/skills/osint-investigation/references/sources/courtlistener.md +98 -0
- package/.agent/skills/osint-investigation/references/sources/gdelt.md +104 -0
- package/.agent/skills/osint-investigation/references/sources/icij-offshore.md +104 -0
- package/.agent/skills/osint-investigation/references/sources/nyc-acris.md +90 -0
- package/.agent/skills/osint-investigation/references/sources/ofac-sdn.md +92 -0
- package/.agent/skills/osint-investigation/references/sources/opencorporates.md +103 -0
- package/.agent/skills/osint-investigation/references/sources/sec-edgar.md +83 -0
- package/.agent/skills/osint-investigation/references/sources/senate-ld.md +89 -0
- package/.agent/skills/osint-investigation/references/sources/usaspending.md +97 -0
- package/.agent/skills/osint-investigation/references/sources/wayback.md +93 -0
- package/.agent/skills/osint-investigation/references/sources/wikipedia.md +107 -0
- package/.agent/skills/osint-investigation/scripts/_http.py +82 -0
- package/.agent/skills/osint-investigation/scripts/_normalize.py +67 -0
- package/.agent/skills/osint-investigation/scripts/build_findings.py +221 -0
- package/.agent/skills/osint-investigation/scripts/entity_resolution.py +228 -0
- package/.agent/skills/osint-investigation/scripts/fetch_courtlistener.py +149 -0
- package/.agent/skills/osint-investigation/scripts/fetch_gdelt.py +161 -0
- package/.agent/skills/osint-investigation/scripts/fetch_icij_offshore.py +234 -0
- package/.agent/skills/osint-investigation/scripts/fetch_nyc_acris.py +203 -0
- package/.agent/skills/osint-investigation/scripts/fetch_ofac_sdn.py +175 -0
- package/.agent/skills/osint-investigation/scripts/fetch_opencorporates.py +191 -0
- package/.agent/skills/osint-investigation/scripts/fetch_sec_edgar.py +184 -0
- package/.agent/skills/osint-investigation/scripts/fetch_senate_ld.py +146 -0
- package/.agent/skills/osint-investigation/scripts/fetch_usaspending.py +170 -0
- package/.agent/skills/osint-investigation/scripts/fetch_wayback.py +142 -0
- package/.agent/skills/osint-investigation/scripts/fetch_wikipedia.py +266 -0
- package/.agent/skills/osint-investigation/scripts/timing_analysis.py +252 -0
- package/.agent/skills/oss-forensics/SKILL.md +10 -9
- package/.agent/skills/oss-forensics/scripts/evidence-store.py +313 -0
- package/.agent/skills/parallel-cli/SKILL.md +2 -2
- package/.agent/skills/pinggy-tunnel/SKILL.md +3 -3
- package/.agent/skills/pixel-art/SKILL.md +2 -2
- package/.agent/skills/plan/SKILL.md +10 -10
- package/.agent/skills/requesting-code-review/SKILL.md +4 -6
- package/.agent/skills/research-paper-writing/SKILL.md +32 -32
- package/.agent/skills/rest-graphql-debug/SKILL.md +31 -32
- package/.agent/skills/s6-container-supervision/SKILL.md +1 -1
- package/.agent/skills/scrapling/SKILL.md +1 -1
- package/.agent/skills/sherlock/SKILL.md +1 -1
- package/.agent/skills/simplify-code/SKILL.md +5 -7
- package/.agent/skills/skill-authoring/SKILL.md +4 -4
- package/.agent/skills/spike/SKILL.md +11 -13
- package/.agent/skills/subagent-driven-development/SKILL.md +7 -11
- package/.agent/skills/systematic-debugging/SKILL.md +13 -14
- package/.agent/skills/test-driven-development/SKILL.md +6 -7
- package/.agent/skills/video-orchestrator/SKILL.md +1 -1
- package/.agent/skills/web-pentest/SKILL.md +7 -7
- package/.mindforge/config.json +3 -2
- package/.mindforge/dynamic-workflows/REGISTRY.md +4 -1
- package/.mindforge/dynamic-workflows/index.json +910 -840
- package/.mindforge/dynamic-workflows/scripts/accessibility-audit.js +88 -90
- package/.mindforge/dynamic-workflows/scripts/ai-model-eval.js +59 -61
- package/.mindforge/dynamic-workflows/scripts/api-contract-test.js +87 -85
- package/.mindforge/dynamic-workflows/scripts/api-migration.js +120 -122
- package/.mindforge/dynamic-workflows/scripts/architecture-modernization.js +84 -86
- package/.mindforge/dynamic-workflows/scripts/code-audit.js +75 -77
- package/.mindforge/dynamic-workflows/scripts/code-explainer.js +99 -101
- package/.mindforge/dynamic-workflows/scripts/competitive-analysis.js +62 -64
- package/.mindforge/dynamic-workflows/scripts/competitive-teardown.js +106 -108
- package/.mindforge/dynamic-workflows/scripts/cost-analysis.js +78 -80
- package/.mindforge/dynamic-workflows/scripts/data-pipeline-validate.js +47 -49
- package/.mindforge/dynamic-workflows/scripts/database-migration.js +85 -87
- package/.mindforge/dynamic-workflows/scripts/debug-detective.js +97 -99
- package/.mindforge/dynamic-workflows/scripts/dependency-health.js +81 -83
- package/.mindforge/dynamic-workflows/scripts/design-system-audit.js +84 -86
- package/.mindforge/dynamic-workflows/scripts/documentation-gen.js +65 -67
- package/.mindforge/dynamic-workflows/scripts/feature-planner.js +78 -80
- package/.mindforge/dynamic-workflows/scripts/incident-response.js +81 -83
- package/.mindforge/dynamic-workflows/scripts/multi-repo-sync.js +49 -51
- package/.mindforge/dynamic-workflows/scripts/mutation-testing.js +113 -115
- package/.mindforge/dynamic-workflows/scripts/onboard-codebase.js +79 -81
- package/.mindforge/dynamic-workflows/scripts/orchestrate-review.js +115 -0
- package/.mindforge/dynamic-workflows/scripts/perf-optimize.js +97 -99
- package/.mindforge/dynamic-workflows/scripts/pr-review.js +61 -63
- package/.mindforge/dynamic-workflows/scripts/refactor-plan.js +88 -90
- package/.mindforge/dynamic-workflows/scripts/release-prep.js +79 -81
- package/.mindforge/dynamic-workflows/scripts/security-hardening.js +117 -119
- package/.mindforge/dynamic-workflows/scripts/security-threat-model.js +119 -121
- package/.mindforge/dynamic-workflows/scripts/tdd-sprint.js +80 -82
- package/.mindforge/dynamic-workflows/scripts/tech-evaluation.js +50 -52
- package/.mindforge/dynamic-workflows/scripts/test-coverage-gap.js +68 -70
- package/.mindforge/dynamic-workflows/scripts/ux-heuristic-audit.js +89 -91
- package/.mindforge/dynamic-workflows/scripts/verification-loop.js +138 -0
- package/.mindforge/dynamic-workflows/scripts/workflow-optimizer.js +136 -0
- package/.mindforge/dynamic-workflows/scripts/writer-reviewer.js +63 -65
- package/.mindforge/memory/sync-manifest.json +1 -1
- package/.mindforge/personas/advisor-researcher.md +2 -2
- package/.mindforge/personas/api-load-tester.md +1 -1
- package/.mindforge/personas/authentication-architect.md +1 -1
- package/.mindforge/personas/backup-recovery-specialist.md +1 -1
- package/.mindforge/personas/build-optimizer.md +1 -1
- package/.mindforge/personas/caching-strategist.md +1 -1
- package/.mindforge/personas/chaos-engineer.md +1 -1
- package/.mindforge/personas/compliance-auditor.md +1 -1
- package/.mindforge/personas/config-management-expert.md +1 -1
- package/.mindforge/personas/coverage-specialist.md +1 -1
- package/.mindforge/personas/debug-specialist.md +2 -2
- package/.mindforge/personas/debugger.md +2 -2
- package/.mindforge/personas/dependency-auditor.md +1 -1
- package/.mindforge/personas/developer.md +1 -1
- package/.mindforge/personas/devops-engineer.md +1 -1
- package/.mindforge/personas/executor.md +1 -1
- package/.mindforge/personas/go-specialist.md +1 -1
- package/.mindforge/personas/incident-commander.md +1 -1
- package/.mindforge/personas/java-specialist.md +1 -1
- package/.mindforge/personas/kubernetes-debugger.md +1 -1
- package/.mindforge/personas/logging-architect.md +1 -1
- package/.mindforge/personas/mf-planner.md +1 -1
- package/.mindforge/personas/mf-researcher.md +1 -1
- package/.mindforge/personas/mf-reviewer.md +1 -1
- package/.mindforge/personas/nyquist-auditor.md +1 -1
- package/.mindforge/personas/observability-engineer.md +1 -1
- package/.mindforge/personas/performance-optimizer.md +1 -1
- package/.mindforge/personas/phase-researcher.md +5 -5
- package/.mindforge/personas/planner.md +1 -1
- package/.mindforge/personas/project-researcher.md +2 -2
- package/.mindforge/personas/python-specialist.md +1 -1
- package/.mindforge/personas/qa-engineer.md +1 -1
- package/.mindforge/personas/release-manager.md +1 -1
- package/.mindforge/personas/research-agent.md +4 -4
- package/.mindforge/personas/roadmapper-extend.md +1 -1
- package/.mindforge/personas/rust-specialist.md +1 -1
- package/.mindforge/personas/security-reviewer.md +1 -1
- package/.mindforge/personas/tech-writer.md +1 -1
- package/.mindforge/personas/typescript-wizard.md +1 -1
- package/.mindforge/personas/ui-researcher.md +1 -1
- package/.mindforge/skills/accessibility/SKILL.md +3 -3
- package/.mindforge/skills/agent-architecture-audit/SKILL.md +11 -0
- package/.mindforge/skills/agent-loops/SKILL.md +1 -1
- package/.mindforge/skills/arxiv/SKILL.md +6 -6
- package/.mindforge/skills/arxiv/scripts/search_arxiv.py +114 -0
- package/.mindforge/skills/code-tour/SKILL.md +2 -2
- package/.mindforge/skills/code-wiki/SKILL.md +11 -11
- package/.mindforge/skills/code-wiki/templates/README.md +31 -0
- package/.mindforge/skills/code-wiki/templates/architecture.md +30 -0
- package/.mindforge/skills/code-wiki/templates/getting-started.md +47 -0
- package/.mindforge/skills/code-wiki/templates/module.md +38 -0
- package/.mindforge/skills/concept-diagrams/SKILL.md +6 -6
- package/.mindforge/skills/concept-diagrams/examples/apartment-floor-plan-conversion.md +244 -0
- package/.mindforge/skills/concept-diagrams/examples/automated-password-reset-flow.md +276 -0
- package/.mindforge/skills/concept-diagrams/examples/autonomous-llm-research-agent-flow.md +240 -0
- package/.mindforge/skills/concept-diagrams/examples/banana-journey-tree-to-smoothie.md +161 -0
- package/.mindforge/skills/concept-diagrams/examples/commercial-aircraft-structure.md +209 -0
- package/.mindforge/skills/concept-diagrams/examples/cpu-ooo-microarchitecture.md +236 -0
- package/.mindforge/skills/concept-diagrams/examples/electricity-grid-flow.md +182 -0
- package/.mindforge/skills/concept-diagrams/examples/feature-film-production-pipeline.md +172 -0
- package/.mindforge/skills/concept-diagrams/examples/hospital-emergency-department-flow.md +165 -0
- package/.mindforge/skills/concept-diagrams/examples/ml-benchmark-grouped-bar-chart.md +114 -0
- package/.mindforge/skills/concept-diagrams/examples/place-order-uml-sequence.md +325 -0
- package/.mindforge/skills/concept-diagrams/examples/smart-city-infrastructure.md +173 -0
- package/.mindforge/skills/concept-diagrams/examples/smartphone-layer-anatomy.md +154 -0
- package/.mindforge/skills/concept-diagrams/examples/sn2-reaction-mechanism.md +247 -0
- package/.mindforge/skills/concept-diagrams/examples/wind-turbine-structure.md +338 -0
- package/.mindforge/skills/concept-diagrams/references/dashboard-patterns.md +43 -0
- package/.mindforge/skills/concept-diagrams/references/infrastructure-patterns.md +144 -0
- package/.mindforge/skills/concept-diagrams/references/physical-shape-cookbook.md +42 -0
- package/.mindforge/skills/concept-diagrams/templates/template.html +174 -0
- package/.mindforge/skills/creative-ideation/references/full-prompt-library.md +110 -0
- package/.mindforge/skills/documentation/SKILL.md +1 -1
- package/.mindforge/skills/domain-intel/SKILL.md +7 -7
- package/.mindforge/skills/domain-intel/scripts/domain_intel.py +397 -0
- package/.mindforge/skills/duckduckgo-search/SKILL.md +17 -17
- package/.mindforge/skills/duckduckgo-search/scripts/duckduckgo.sh +28 -0
- package/.mindforge/skills/environment-management/SKILL.md +1 -1
- package/.mindforge/skills/github-code-review/SKILL.md +9 -9
- package/.mindforge/skills/github-code-review/references/review-output-template.md +74 -0
- package/.mindforge/skills/github-issues/templates/bug-report.md +35 -0
- package/.mindforge/skills/github-issues/templates/feature-request.md +31 -0
- package/.mindforge/skills/github-pr-workflow/SKILL.md +3 -3
- package/.mindforge/skills/github-pr-workflow/references/ci-troubleshooting.md +183 -0
- package/.mindforge/skills/github-pr-workflow/references/conventional-commits.md +71 -0
- package/.mindforge/skills/github-pr-workflow/templates/pr-body-bugfix.md +35 -0
- package/.mindforge/skills/github-pr-workflow/templates/pr-body-feature.md +33 -0
- package/.mindforge/skills/incident-response/SKILL.md +1 -1
- package/.mindforge/skills/jupyter-live-kernel/SKILL.md +3 -3
- package/.mindforge/skills/kanban-orchestrator/SKILL.md +10 -10
- package/.mindforge/skills/kanban-worker/SKILL.md +6 -6
- package/.mindforge/skills/meme-generation/SKILL.md +1 -1
- package/.mindforge/skills/meme-generation/scripts/generate_meme.py +470 -0
- package/.mindforge/skills/meme-generation/scripts/templates.json +97 -0
- package/.mindforge/skills/obsidian/SKILL.md +15 -15
- package/.mindforge/skills/orch-pipeline/SKILL.md +2 -2
- package/.mindforge/skills/osint-investigation/SKILL.md +3 -2
- package/.mindforge/skills/osint-investigation/references/sources/courtlistener.md +98 -0
- package/.mindforge/skills/osint-investigation/references/sources/gdelt.md +104 -0
- package/.mindforge/skills/osint-investigation/references/sources/icij-offshore.md +104 -0
- package/.mindforge/skills/osint-investigation/references/sources/nyc-acris.md +90 -0
- package/.mindforge/skills/osint-investigation/references/sources/ofac-sdn.md +92 -0
- package/.mindforge/skills/osint-investigation/references/sources/opencorporates.md +103 -0
- package/.mindforge/skills/osint-investigation/references/sources/sec-edgar.md +83 -0
- package/.mindforge/skills/osint-investigation/references/sources/senate-ld.md +89 -0
- package/.mindforge/skills/osint-investigation/references/sources/usaspending.md +97 -0
- package/.mindforge/skills/osint-investigation/references/sources/wayback.md +93 -0
- package/.mindforge/skills/osint-investigation/references/sources/wikipedia.md +107 -0
- package/.mindforge/skills/osint-investigation/scripts/_http.py +82 -0
- package/.mindforge/skills/osint-investigation/scripts/_normalize.py +67 -0
- package/.mindforge/skills/osint-investigation/scripts/build_findings.py +221 -0
- package/.mindforge/skills/osint-investigation/scripts/entity_resolution.py +228 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_courtlistener.py +149 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_gdelt.py +161 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_icij_offshore.py +234 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_nyc_acris.py +203 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_ofac_sdn.py +175 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_opencorporates.py +191 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_sec_edgar.py +184 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_senate_ld.py +146 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_usaspending.py +170 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_wayback.py +142 -0
- package/.mindforge/skills/osint-investigation/scripts/fetch_wikipedia.py +266 -0
- package/.mindforge/skills/osint-investigation/scripts/timing_analysis.py +252 -0
- package/.mindforge/skills/osint-investigation/templates/source-template.md +59 -0
- package/.mindforge/skills/oss-forensics/SKILL.md +6 -6
- package/.mindforge/skills/oss-forensics/references/evidence-types.md +89 -0
- package/.mindforge/skills/oss-forensics/references/github-archive-guide.md +184 -0
- package/.mindforge/skills/oss-forensics/references/investigation-templates.md +131 -0
- package/.mindforge/skills/oss-forensics/references/recovery-techniques.md +164 -0
- package/.mindforge/skills/oss-forensics/scripts/evidence-store.py +313 -0
- package/.mindforge/skills/oss-forensics/templates/forensic-report.md +151 -0
- package/.mindforge/skills/oss-forensics/templates/malicious-package-report.md +43 -0
- package/.mindforge/skills/pixel-art/SKILL.md +2 -2
- package/.mindforge/skills/pixel-art/references/palettes.md +49 -0
- package/.mindforge/skills/plan/SKILL.md +10 -10
- package/.mindforge/skills/requesting-code-review/SKILL.md +4 -6
- package/.mindforge/skills/research-paper-writing/SKILL.md +32 -32
- package/.mindforge/skills/research-paper-writing/references/autoreason-methodology.md +394 -0
- package/.mindforge/skills/research-paper-writing/references/checklists.md +434 -0
- package/.mindforge/skills/research-paper-writing/references/citation-workflow.md +563 -0
- package/.mindforge/skills/research-paper-writing/references/experiment-patterns.md +728 -0
- package/.mindforge/skills/research-paper-writing/references/human-evaluation.md +476 -0
- package/.mindforge/skills/research-paper-writing/references/paper-types.md +481 -0
- package/.mindforge/skills/research-paper-writing/references/reviewer-guidelines.md +433 -0
- package/.mindforge/skills/research-paper-writing/references/sources.md +191 -0
- package/.mindforge/skills/research-paper-writing/references/writing-guide.md +474 -0
- package/.mindforge/skills/research-paper-writing/templates/README.md +251 -0
- package/.mindforge/skills/scrapling/SKILL.md +1 -1
- package/.mindforge/skills/security-review/SKILL.md +1 -1
- package/.mindforge/skills/sherlock/SKILL.md +1 -1
- package/.mindforge/skills/simplify-code/SKILL.md +5 -7
- package/.mindforge/skills/spike/SKILL.md +11 -13
- package/.mindforge/skills/subagent-driven-development/SKILL.md +7 -11
- package/.mindforge/skills/subagent-driven-development/references/context-budget-discipline.md +53 -0
- package/.mindforge/skills/subagent-driven-development/references/gates-taxonomy.md +93 -0
- package/.mindforge/skills/systematic-debugging/SKILL.md +13 -14
- package/.mindforge/skills/test-driven-development/SKILL.md +7 -8
- package/.mindforge/skills/testing-standards/SKILL.md +1 -1
- package/.mindforge/skills/web-pentest/SKILL.md +2 -2
- package/.mindforge/skills/web-pentest/references/bypass-techniques.md +133 -0
- package/.mindforge/skills/web-pentest/references/exploitation-techniques.md +204 -0
- package/.mindforge/skills/web-pentest/references/scope-enforcement.md +110 -0
- package/.mindforge/skills/web-pentest/references/vuln-taxonomy.md +81 -0
- package/.mindforge/skills/web-pentest/templates/authorization.md +69 -0
- package/.mindforge/skills/web-pentest/templates/pentest-report.md +178 -0
- package/.mindforge/skills/writing-skills/SKILL.md +4 -3
- package/CHANGELOG.md +35 -0
- package/MINDFORGE.md +3 -3
- package/README.md +9 -7
- package/RELEASENOTES.md +65 -2
- package/SECURITY.md +5 -3
- package/bin/autonomous/auto-runner.js +5 -2
- package/bin/engine/skill-loader.js +4 -0
- package/bin/governance/rbac.js +4 -0
- package/bin/memory/eis-client.js +1 -0
- package/bin/mindforge-cli.js +5 -0
- package/bin/models/bedrock-provider.js +192 -0
- package/bin/models/model-client.js +19 -0
- package/bin/spawn-agent.js +9 -2
- package/docs/commands-reference.md +68 -6
- package/docs/faq.md +38 -1
- package/docs/getting-started.md +27 -12
- package/docs/sdk-reference.md +36 -3
- package/docs/troubleshooting.md +59 -1
- package/docs/user-guide.md +7 -4
- package/package.json +3 -3
- package/.claude/commands/mindforge/skill-tdd.md +0 -53
- package/.claude/commands/mindforge/skills-index.md +0 -118
- package/.claude/commands/mindforge/systematic-debug.md +0 -60
- package/.claude/commands/mindforge/wf-accessibility-audit.md +0 -31
- package/.claude/commands/mindforge/wf-ai-model-eval.md +0 -31
- package/.claude/commands/mindforge/wf-api-migration.md +0 -31
- package/.claude/commands/mindforge/wf-architecture-modernization.md +0 -32
- package/.claude/commands/mindforge/wf-catalog.md +0 -59
- package/.claude/commands/mindforge/wf-code-audit.md +0 -31
- package/.claude/commands/mindforge/wf-code-explainer.md +0 -31
- package/.claude/commands/mindforge/wf-competitive-analysis.md +0 -31
- package/.claude/commands/mindforge/wf-competitive-teardown.md +0 -31
- package/.claude/commands/mindforge/wf-cost-analysis.md +0 -31
- package/.claude/commands/mindforge/wf-data-pipeline-validate.md +0 -31
- package/.claude/commands/mindforge/wf-database-migration.md +0 -31
- package/.claude/commands/mindforge/wf-debug-detective.md +0 -32
- package/.claude/commands/mindforge/wf-design-system-audit.md +0 -31
- package/.claude/commands/mindforge/wf-documentation-gen.md +0 -31
- package/.claude/commands/mindforge/wf-feature-planner.md +0 -31
- package/.claude/commands/mindforge/wf-incident-response.md +0 -31
- package/.claude/commands/mindforge/wf-multi-repo-sync.md +0 -31
- package/.claude/commands/mindforge/wf-mutation-testing.md +0 -31
- package/.claude/commands/mindforge/wf-onboard-codebase.md +0 -31
- package/.claude/commands/mindforge/wf-perf-optimize.md +0 -31
- package/.claude/commands/mindforge/wf-pr-review.md +0 -31
- package/.claude/commands/mindforge/wf-refactor-plan.md +0 -31
- package/.claude/commands/mindforge/wf-release-prep.md +0 -31
- package/.claude/commands/mindforge/wf-security-threat-model.md +0 -31
- package/.claude/commands/mindforge/wf-tdd-sprint.md +0 -31
- package/.claude/commands/mindforge/wf-tech-evaluation.md +0 -31
- package/.claude/commands/mindforge/wf-ux-heuristic-audit.md +0 -31
- package/.claude/commands/mindforge/wf-writer-reviewer.md +0 -30
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Investigation Templates
|
|
2
|
+
|
|
3
|
+
Pre-built hypothesis and investigation templates for common supply chain attack scenarios.
|
|
4
|
+
Each template includes: attack pattern, key evidence to collect, and hypothesis starters.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Template 1: Maintainer Account Compromise
|
|
9
|
+
|
|
10
|
+
**Pattern**: Attacker gains access to a legitimate maintainer account (phishing, credential stuffing)
|
|
11
|
+
and uses it to push malicious code, create backdoored releases, or exfiltrate CI secrets.
|
|
12
|
+
|
|
13
|
+
**Real-world examples**: XZ Utils (2024), Codecov (2021), event-stream (2018)
|
|
14
|
+
|
|
15
|
+
**Key Evidence to Collect**:
|
|
16
|
+
- [ ] Push events from maintainer account outside normal working hours/timezone
|
|
17
|
+
- [ ] Commits adding new dependencies, obfuscated code, or modified build scripts
|
|
18
|
+
- [ ] Release creation immediately after suspicious push (to maximize package distribution)
|
|
19
|
+
- [ ] MemberEvent adding unknown collaborators (attacker adding backup access)
|
|
20
|
+
- [ ] WorkflowRunEvent with unexpected secret access or exfiltration-like behavior
|
|
21
|
+
- [ ] Account login location changes (check social media, conference talks for corroboration)
|
|
22
|
+
|
|
23
|
+
**Hypothesis Starters**:
|
|
24
|
+
```
|
|
25
|
+
[HYPOTHESIS] Actor <HANDLE>'s account was compromised on or around <DATE>,
|
|
26
|
+
based on anomalous commit timing [EV-XXXX] and geographic access patterns [EV-YYYY].
|
|
27
|
+
```
|
|
28
|
+
```
|
|
29
|
+
[HYPOTHESIS] Release <VERSION> was published by the compromised account to push
|
|
30
|
+
malicious code to downstream users, evidenced by the malicious commit [EV-XXXX]
|
|
31
|
+
being added <N> hours before the release [EV-YYYY].
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Template 2: Malicious Dependency Injection
|
|
37
|
+
|
|
38
|
+
**Pattern**: A trusted package is modified to include malicious code in a dependency,
|
|
39
|
+
or a new malicious dependency is injected into an existing package.
|
|
40
|
+
|
|
41
|
+
**Key Evidence to Collect**:
|
|
42
|
+
- [ ] Diff of `package.json`/`requirements.txt`/`go.mod` before and after suspicious commit
|
|
43
|
+
- [ ] The new dependency's publication timestamp vs. the injection commit timestamp
|
|
44
|
+
- [ ] Whether the new dependency exists on npm/PyPI and who owns it
|
|
45
|
+
- [ ] Any obfuscation patterns in the injected dependency code
|
|
46
|
+
- [ ] Install-time scripts (`postinstall`, `setup.py`, etc.) that execute code on install
|
|
47
|
+
|
|
48
|
+
**Hypothesis Starters**:
|
|
49
|
+
```
|
|
50
|
+
[HYPOTHESIS] Commit <SHA> [EV-XXXX] introduced dependency <PACKAGE@VERSION>
|
|
51
|
+
which appears to be a malicious package published by actor <HANDLE> [EV-YYYY],
|
|
52
|
+
designed to execute <BEHAVIOR> during installation.
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Template 3: CI/CD Pipeline Injection
|
|
58
|
+
|
|
59
|
+
**Pattern**: Attacker modifies GitHub Actions workflows to steal secrets, exfiltrate code,
|
|
60
|
+
or inject malicious artifacts into the build output.
|
|
61
|
+
|
|
62
|
+
**Key Evidence to Collect**:
|
|
63
|
+
- [ ] Diff of all `.github/workflows/*.yml` files before/after suspicious period
|
|
64
|
+
- [ ] WorkflowRunEvents triggered by the modified workflows
|
|
65
|
+
- [ ] Any `curl`, `wget`, or network calls added to workflow steps
|
|
66
|
+
- [ ] New or modified `env:` sections referencing `secrets.*`
|
|
67
|
+
- [ ] Artifacts produced by modified workflow runs
|
|
68
|
+
|
|
69
|
+
**Hypothesis Starters**:
|
|
70
|
+
```
|
|
71
|
+
[HYPOTHESIS] Workflow file <FILE> was modified in commit <SHA> [EV-XXXX] to
|
|
72
|
+
exfiltrate repository secrets via <METHOD>, as evidenced by the added network
|
|
73
|
+
call pattern [EV-YYYY].
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Template 4: Typosquatting / Dependency Confusion
|
|
79
|
+
|
|
80
|
+
**Pattern**: Attacker registers a package with a name similar to a popular package
|
|
81
|
+
(or an internal package name) to intercept installs from users who mistype.
|
|
82
|
+
|
|
83
|
+
**Key Evidence to Collect**:
|
|
84
|
+
- [ ] Registration timestamp of the suspicious package on the registry
|
|
85
|
+
- [ ] Package content: does it contain malicious code or is it a stub?
|
|
86
|
+
- [ ] Download statistics for the suspicious package
|
|
87
|
+
- [ ] Names of internal packages that could be targeted (if private repo scope)
|
|
88
|
+
- [ ] Any references to the legitimate package in the malicious one's metadata
|
|
89
|
+
|
|
90
|
+
**Hypothesis Starters**:
|
|
91
|
+
```
|
|
92
|
+
[HYPOTHESIS] Package <MALICIOUS_NAME> was registered on <DATE> [EV-XXXX] to
|
|
93
|
+
typosquat on <LEGITIMATE_NAME>, targeting users who misspell the package name.
|
|
94
|
+
The package contains <BEHAVIOR> [EV-YYYY].
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Template 5: Force-Push History Rewrite (Evidence Erasure)
|
|
100
|
+
|
|
101
|
+
**Pattern**: After a malicious commit is detected (or before wider notice), the attacker
|
|
102
|
+
force-pushes to remove the malicious commit from branch history.
|
|
103
|
+
|
|
104
|
+
**Detection is key** — this template focuses on proving the erasure happened.
|
|
105
|
+
|
|
106
|
+
**Key Evidence to Collect**:
|
|
107
|
+
- [ ] GH Archive PushEvent with `distinct_size=0` (force push indicator) [EV-XXXX]
|
|
108
|
+
- [ ] The SHA of the commit BEFORE the force push (from GH Archive `payload.before`)
|
|
109
|
+
- [ ] Recovery of the erased commit via direct URL or `git fetch origin SHA`
|
|
110
|
+
- [ ] Wayback Machine snapshot of the commit page before erasure
|
|
111
|
+
- [ ] Timeline gap in git log (N commits visible in archive but M < N in current repo)
|
|
112
|
+
|
|
113
|
+
**Hypothesis Starters**:
|
|
114
|
+
```
|
|
115
|
+
[HYPOTHESIS] Actor <HANDLE> force-pushed branch <BRANCH> on <DATE> [EV-XXXX]
|
|
116
|
+
to erase commit <SHA> [EV-YYYY], which contained <MALICIOUS_CONTENT>.
|
|
117
|
+
The erased commit was recovered via <METHOD> [EV-ZZZZ].
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Cross-Cutting Investigation Checklist
|
|
123
|
+
|
|
124
|
+
Apply to every investigation regardless of template:
|
|
125
|
+
|
|
126
|
+
- [ ] Check all contributors for newly created accounts (< 30 days old at time of malicious activity)
|
|
127
|
+
- [ ] Check if any maintainer account changed email in the period (sign of account takeover)
|
|
128
|
+
- [ ] Verify GPG signatures on suspicious commits match known maintainer keys
|
|
129
|
+
- [ ] Check if the repository changed ownership or transferred orgs near the incident
|
|
130
|
+
- [ ] Look for "cleanup" commits immediately after the malicious commit (cover-up pattern)
|
|
131
|
+
- [ ] Check related packages/repos by the same author for similar patterns
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Deleted Content Recovery Techniques
|
|
2
|
+
|
|
3
|
+
## Key Insight: GitHub Never Fully Deletes Force-Pushed Commits
|
|
4
|
+
|
|
5
|
+
Force-pushed commits are removed from the branch history but REMAIN on GitHub's servers until garbage collection runs (which can take weeks to months). This is the foundation of deleted commit recovery.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Method 1: Direct GitHub URL (Fastest — No Auth Required)
|
|
10
|
+
|
|
11
|
+
If you have a commit SHA, access it directly even if it was force-pushed off a branch:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# View commit metadata
|
|
15
|
+
curl -s "https://github.com/OWNER/REPO/commit/SHA"
|
|
16
|
+
|
|
17
|
+
# Download as patch (includes full diff)
|
|
18
|
+
curl -s "https://github.com/OWNER/REPO/commit/SHA.patch" > recovered_commit.patch
|
|
19
|
+
|
|
20
|
+
# Download as diff
|
|
21
|
+
curl -s "https://github.com/OWNER/REPO/commit/SHA.diff" > recovered_commit.diff
|
|
22
|
+
|
|
23
|
+
# Example (Istio credential leak - real incident):
|
|
24
|
+
curl -s "https://github.com/istio/istio/commit/FORCE_PUSHED_SHA.patch"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**When this works**: SHA is known (from GH Archive, Wayback Machine, or `git fsck`)
|
|
28
|
+
**When this fails**: GitHub has already garbage-collected the object (rare, typically 30–90 days post-force-push)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Method 2: GitHub REST API
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Works for commits force-pushed off branches but still on server
|
|
36
|
+
# Note: /commits/SHA may 404, but /git/commits/SHA often succeeds for orphaned commits
|
|
37
|
+
curl -s "https://api.github.com/repos/OWNER/REPO/git/commits/SHA" | jq .
|
|
38
|
+
|
|
39
|
+
# Get the tree (file listing) of a force-pushed commit
|
|
40
|
+
curl -s "https://api.github.com/repos/OWNER/REPO/git/trees/SHA?recursive=1" | jq .
|
|
41
|
+
|
|
42
|
+
# Get a specific file from a force-pushed commit
|
|
43
|
+
curl -s "https://api.github.com/repos/OWNER/REPO/contents/PATH?ref=SHA" | jq .content | base64 -d
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Method 3: Git Fetch by SHA (Local — Requires Clone)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Fetch an orphaned commit directly by SHA into local repo
|
|
52
|
+
cd target_repo
|
|
53
|
+
git fetch origin SHA
|
|
54
|
+
git log FETCH_HEAD -1 # view the commit
|
|
55
|
+
git diff FETCH_HEAD~1 FETCH_HEAD # view the diff
|
|
56
|
+
|
|
57
|
+
# If the SHA was recently force-pushed it will still be fetchable
|
|
58
|
+
# This stops working once GitHub GC runs
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Method 4: Dangling Commits via git fsck
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
cd target_repo
|
|
67
|
+
|
|
68
|
+
# Find all unreachable objects (includes force-pushed commits)
|
|
69
|
+
git fsck --unreachable --no-reflogs 2>&1 | grep "unreachable commit" | awk '{print $3}' > dangling_shas.txt
|
|
70
|
+
|
|
71
|
+
# For each dangling commit, get its metadata
|
|
72
|
+
while read sha; do
|
|
73
|
+
echo "=== $sha ===" >> dangling_details.txt
|
|
74
|
+
git show --stat "$sha" >> dangling_details.txt 2>&1
|
|
75
|
+
done < dangling_shas.txt
|
|
76
|
+
|
|
77
|
+
# Note: dangling objects only exist in LOCAL clone — not the same as GitHub's copies
|
|
78
|
+
# GitHub's copies are accessible via Methods 1-3 until GC runs
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Recovering Deleted GitHub Issues and PRs
|
|
84
|
+
|
|
85
|
+
### Via Wayback Machine CDX API
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Find all archived snapshots of a specific issue
|
|
89
|
+
curl -s "https://web.archive.org/cdx/search/cdx?url=github.com/OWNER/REPO/issues/NUMBER&output=json&limit=50&fl=timestamp,statuscode,original" | python3 -m json.tool
|
|
90
|
+
|
|
91
|
+
# Fetch the best snapshot
|
|
92
|
+
# Use the timestamp from the CDX result:
|
|
93
|
+
# https://web.archive.org/web/TIMESTAMP/https://github.com/OWNER/REPO/issues/NUMBER
|
|
94
|
+
curl -s "https://web.archive.org/web/TIMESTAMP/https://github.com/OWNER/REPO/issues/NUMBER" > issue_NUMBER_archived.html
|
|
95
|
+
|
|
96
|
+
# Find all snapshots of the repo in a date range
|
|
97
|
+
curl -s "https://web.archive.org/cdx/search/cdx?url=github.com/OWNER/REPO*&output=json&from=20240101&to=20240201&limit=200&fl=timestamp,urlkey,statuscode" | python3 -m json.tool
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Via GitHub API (Limited — Only Non-Deleted Content)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Closed issues (not deleted) are retrievable
|
|
104
|
+
curl -s "https://api.github.com/repos/OWNER/REPO/issues?state=closed&per_page=100" | jq '.[].number'
|
|
105
|
+
|
|
106
|
+
# Note: DELETED issues/PRs do NOT appear in the API. Use Wayback Machine or GH Archive for those.
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Via GitHub Archive (For Event History — Not Content)
|
|
110
|
+
|
|
111
|
+
```sql
|
|
112
|
+
-- Find all IssueEvents for a repo in a date range
|
|
113
|
+
SELECT created_at, actor.login, payload.action, payload.issue.number, payload.issue.title
|
|
114
|
+
FROM `githubarchive.day.*`
|
|
115
|
+
WHERE _TABLE_SUFFIX BETWEEN '20240101' AND '20240201'
|
|
116
|
+
AND type = 'IssuesEvent'
|
|
117
|
+
AND repo.name = 'OWNER/REPO'
|
|
118
|
+
ORDER BY created_at
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Recovering Deleted Files from a Known Commit
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# If you have the commit SHA (even force-pushed):
|
|
127
|
+
git show SHA:path/to/file.py > recovered_file.py
|
|
128
|
+
|
|
129
|
+
# Or via API (base64 encoded content):
|
|
130
|
+
curl -s "https://api.github.com/repos/OWNER/REPO/contents/path/to/file.py?ref=SHA" | python3 -c "
|
|
131
|
+
import sys, json, base64
|
|
132
|
+
d = json.load(sys.stdin)
|
|
133
|
+
print(base64.b64decode(d['content']).decode())
|
|
134
|
+
"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Evidence Recording
|
|
140
|
+
|
|
141
|
+
After recovering any deleted content, immediately record it:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
python3 SKILL_DIR/scripts/evidence-store.py --store evidence.json add \
|
|
145
|
+
--source "git fetch origin FORCE_PUSHED_SHA" \
|
|
146
|
+
--content "Recovered commit: FORCE_PUSHED_SHA | Author: attacker@example.com | Date: 2024-01-15 | Added file: malicious.sh" \
|
|
147
|
+
--type git \
|
|
148
|
+
--actor "attacker-handle" \
|
|
149
|
+
--url "https://github.com/OWNER/REPO/commit/FORCE_PUSHED_SHA.patch" \
|
|
150
|
+
--timestamp "2024-01-15T00:00:00Z" \
|
|
151
|
+
--verification single_source \
|
|
152
|
+
--notes "Commit force-pushed off main branch on 2024-01-16. Recovered via direct fetch."
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Recovery Failure Modes
|
|
158
|
+
|
|
159
|
+
| Failure | Cause | Workaround |
|
|
160
|
+
|---------|-------|------------|
|
|
161
|
+
| `git fetch origin SHA` returns "not our ref" | GitHub GC already ran | Try Method 1/2, search Wayback Machine |
|
|
162
|
+
| `github.com/OWNER/REPO/commit/SHA` returns 404 | GC ran or SHA is wrong | Verify SHA via GH Archive; try partial SHA search |
|
|
163
|
+
| Wayback Machine has no snapshots | Page was never crawled by IA | Check `commoncrawl.org`, check Google Cache |
|
|
164
|
+
| BigQuery shows event but no content | GH Archive stores event metadata, not file contents | Recovery only reveals the event occurred, not the content |
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
OSS Forensics Evidence Store Manager
|
|
4
|
+
Manages a JSON-based evidence store for forensic investigations.
|
|
5
|
+
|
|
6
|
+
Commands:
|
|
7
|
+
add - Add a piece of evidence
|
|
8
|
+
list - List all evidence (optionally filter by type or actor)
|
|
9
|
+
verify - Re-check SHA-256 hashes for integrity
|
|
10
|
+
query - Search evidence by keyword
|
|
11
|
+
export - Export evidence as a Markdown table
|
|
12
|
+
summary - Print investigation statistics
|
|
13
|
+
|
|
14
|
+
Usage example:
|
|
15
|
+
python3 evidence-store.py --store evidence.json add \
|
|
16
|
+
--source "git fsck output" --content "dangling commit abc123" \
|
|
17
|
+
--type git --actor "malicious-user" --url "https://github.com/owner/repo/commit/abc123"
|
|
18
|
+
|
|
19
|
+
python3 evidence-store.py --store evidence.json list --type git
|
|
20
|
+
python3 evidence-store.py --store evidence.json verify
|
|
21
|
+
python3 evidence-store.py --store evidence.json export > evidence-table.md
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import json
|
|
25
|
+
import argparse
|
|
26
|
+
import os
|
|
27
|
+
import datetime
|
|
28
|
+
import hashlib
|
|
29
|
+
import sys
|
|
30
|
+
|
|
31
|
+
EVIDENCE_TYPES = [
|
|
32
|
+
"git", # Local git repository data (commits, reflog, fsck)
|
|
33
|
+
"gh_api", # GitHub REST API responses
|
|
34
|
+
"gh_archive", # GitHub Archive / BigQuery query results
|
|
35
|
+
"web_archive", # Wayback Machine snapshots
|
|
36
|
+
"ioc", # Indicator of Compromise (SHA, domain, IP, package name, etc.)
|
|
37
|
+
"analysis", # Derived analysis / cross-source correlation result
|
|
38
|
+
"manual", # Manually noted observation
|
|
39
|
+
"vendor_report", # External security vendor report excerpt
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
VERIFICATION_STATES = ["unverified", "single_source", "multi_source_verified"]
|
|
43
|
+
|
|
44
|
+
IOC_TYPES = [
|
|
45
|
+
"COMMIT_SHA", "FILE_PATH", "API_KEY", "SECRET", "IP_ADDRESS",
|
|
46
|
+
"DOMAIN", "PACKAGE_NAME", "ACTOR_USERNAME", "MALICIOUS_URL",
|
|
47
|
+
"WORKFLOW_FILE", "BRANCH_NAME", "TAG_NAME", "RELEASE_NAME", "OTHER",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _now_iso():
|
|
52
|
+
return datetime.datetime.now(datetime.timezone.utc).isoformat(timespec="seconds") + "Z"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _sha256(content: str) -> str:
|
|
56
|
+
return hashlib.sha256(content.encode("utf-8")).hexdigest()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class EvidenceStore:
|
|
60
|
+
def __init__(self, filepath: str):
|
|
61
|
+
self.filepath = filepath
|
|
62
|
+
self.data = {
|
|
63
|
+
"metadata": {
|
|
64
|
+
"version": "2.0",
|
|
65
|
+
"created_at": _now_iso(),
|
|
66
|
+
"last_updated": _now_iso(),
|
|
67
|
+
"investigation": "",
|
|
68
|
+
"target_repo": "",
|
|
69
|
+
},
|
|
70
|
+
"evidence": [],
|
|
71
|
+
"chain_of_custody": [],
|
|
72
|
+
}
|
|
73
|
+
if os.path.exists(filepath):
|
|
74
|
+
try:
|
|
75
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
76
|
+
self.data = json.load(f)
|
|
77
|
+
except (json.JSONDecodeError, IOError) as e:
|
|
78
|
+
print(f"Error loading evidence store '{filepath}': {e}", file=sys.stderr)
|
|
79
|
+
print("Hint: The file might be corrupted. Check for manual edits or syntax errors.", file=sys.stderr)
|
|
80
|
+
sys.exit(1)
|
|
81
|
+
|
|
82
|
+
def _save(self):
|
|
83
|
+
self.data["metadata"]["last_updated"] = _now_iso()
|
|
84
|
+
with open(self.filepath, "w", encoding="utf-8") as f:
|
|
85
|
+
json.dump(self.data, f, indent=2, ensure_ascii=False)
|
|
86
|
+
|
|
87
|
+
def _next_id(self) -> str:
|
|
88
|
+
return f"EV-{len(self.data['evidence']) + 1:04d}"
|
|
89
|
+
|
|
90
|
+
def add(
|
|
91
|
+
self,
|
|
92
|
+
source: str,
|
|
93
|
+
content: str,
|
|
94
|
+
evidence_type: str,
|
|
95
|
+
actor: str = None,
|
|
96
|
+
url: str = None,
|
|
97
|
+
timestamp: str = None,
|
|
98
|
+
ioc_type: str = None,
|
|
99
|
+
verification: str = "unverified",
|
|
100
|
+
notes: str = None,
|
|
101
|
+
) -> str:
|
|
102
|
+
evidence_id = self._next_id()
|
|
103
|
+
entry = {
|
|
104
|
+
"id": evidence_id,
|
|
105
|
+
"type": evidence_type,
|
|
106
|
+
"source": source,
|
|
107
|
+
"content": content,
|
|
108
|
+
"content_sha256": _sha256(content),
|
|
109
|
+
"actor": actor,
|
|
110
|
+
"url": url,
|
|
111
|
+
"event_timestamp": timestamp,
|
|
112
|
+
"collected_at": _now_iso(),
|
|
113
|
+
"ioc_type": ioc_type,
|
|
114
|
+
"verification": verification,
|
|
115
|
+
"notes": notes,
|
|
116
|
+
}
|
|
117
|
+
self.data["evidence"].append(entry)
|
|
118
|
+
self.data["chain_of_custody"].append({
|
|
119
|
+
"action": "add",
|
|
120
|
+
"evidence_id": evidence_id,
|
|
121
|
+
"timestamp": _now_iso(),
|
|
122
|
+
"source": source,
|
|
123
|
+
})
|
|
124
|
+
self._save()
|
|
125
|
+
return evidence_id
|
|
126
|
+
|
|
127
|
+
def list_evidence(self, filter_type: str = None, filter_actor: str = None):
|
|
128
|
+
results = self.data["evidence"]
|
|
129
|
+
if filter_type:
|
|
130
|
+
results = [e for e in results if e.get("type") == filter_type]
|
|
131
|
+
if filter_actor:
|
|
132
|
+
results = [e for e in results if e.get("actor") == filter_actor]
|
|
133
|
+
return results
|
|
134
|
+
|
|
135
|
+
def verify_integrity(self):
|
|
136
|
+
"""Re-compute SHA-256 for all entries and report mismatches."""
|
|
137
|
+
issues = []
|
|
138
|
+
for entry in self.data["evidence"]:
|
|
139
|
+
expected = _sha256(entry["content"])
|
|
140
|
+
stored = entry.get("content_sha256", "")
|
|
141
|
+
if expected != stored:
|
|
142
|
+
issues.append({
|
|
143
|
+
"id": entry["id"],
|
|
144
|
+
"stored_sha256": stored,
|
|
145
|
+
"computed_sha256": expected,
|
|
146
|
+
})
|
|
147
|
+
return issues
|
|
148
|
+
|
|
149
|
+
def query(self, keyword: str):
|
|
150
|
+
"""Search for keyword in content, source, actor, or url."""
|
|
151
|
+
keyword_lower = keyword.lower()
|
|
152
|
+
return [
|
|
153
|
+
e for e in self.data["evidence"]
|
|
154
|
+
if keyword_lower in (e.get("content", "") or "").lower()
|
|
155
|
+
or keyword_lower in (e.get("source", "") or "").lower()
|
|
156
|
+
or keyword_lower in (e.get("actor", "") or "").lower()
|
|
157
|
+
or keyword_lower in (e.get("url", "") or "").lower()
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
def export_markdown(self) -> str:
|
|
161
|
+
lines = [
|
|
162
|
+
"# Evidence Registry",
|
|
163
|
+
"",
|
|
164
|
+
f"**Store**: `{self.filepath}`",
|
|
165
|
+
f"**Last Updated**: {self.data['metadata'].get('last_updated', 'N/A')}",
|
|
166
|
+
f"**Total Evidence Items**: {len(self.data['evidence'])}",
|
|
167
|
+
"",
|
|
168
|
+
"| ID | Type | Source | Actor | Verification | Event Timestamp | URL |",
|
|
169
|
+
"|----|------|--------|-------|--------------|-----------------|-----|",
|
|
170
|
+
]
|
|
171
|
+
for e in self.data["evidence"]:
|
|
172
|
+
url = e.get("url") or ""
|
|
173
|
+
url_display = f"[link]({url})" if url else ""
|
|
174
|
+
lines.append(
|
|
175
|
+
f"| {e['id']} | {e.get('type','')} | {e.get('source','')} "
|
|
176
|
+
f"| {e.get('actor') or ''} | {e.get('verification','')} "
|
|
177
|
+
f"| {e.get('event_timestamp') or ''} | {url_display} |"
|
|
178
|
+
)
|
|
179
|
+
lines.append("")
|
|
180
|
+
lines.append("## Chain of Custody")
|
|
181
|
+
lines.append("")
|
|
182
|
+
lines.append("| Evidence ID | Action | Timestamp | Source |")
|
|
183
|
+
lines.append("|-------------|--------|-----------|--------|")
|
|
184
|
+
for c in self.data["chain_of_custody"]:
|
|
185
|
+
lines.append(
|
|
186
|
+
f"| {c.get('evidence_id','')} | {c.get('action','')} "
|
|
187
|
+
f"| {c.get('timestamp','')} | {c.get('source','')} |"
|
|
188
|
+
)
|
|
189
|
+
return "\n".join(lines)
|
|
190
|
+
|
|
191
|
+
def summary(self) -> dict:
|
|
192
|
+
by_type = {}
|
|
193
|
+
by_verification = {}
|
|
194
|
+
actors = set()
|
|
195
|
+
for e in self.data["evidence"]:
|
|
196
|
+
t = e.get("type", "unknown")
|
|
197
|
+
by_type[t] = by_type.get(t, 0) + 1
|
|
198
|
+
v = e.get("verification", "unverified")
|
|
199
|
+
by_verification[v] = by_verification.get(v, 0) + 1
|
|
200
|
+
if e.get("actor"):
|
|
201
|
+
actors.add(e["actor"])
|
|
202
|
+
return {
|
|
203
|
+
"total": len(self.data["evidence"]),
|
|
204
|
+
"by_type": by_type,
|
|
205
|
+
"by_verification": by_verification,
|
|
206
|
+
"unique_actors": sorted(actors),
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def main():
|
|
211
|
+
parser = argparse.ArgumentParser(
|
|
212
|
+
description="OSS Forensics Evidence Store Manager v2.0",
|
|
213
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
214
|
+
)
|
|
215
|
+
parser.add_argument("--store", default="evidence.json", help="Path to evidence JSON file (default: evidence.json)")
|
|
216
|
+
|
|
217
|
+
subparsers = parser.add_subparsers(dest="command", metavar="COMMAND")
|
|
218
|
+
|
|
219
|
+
# --- add ---
|
|
220
|
+
add_p = subparsers.add_parser("add", help="Add a new evidence entry")
|
|
221
|
+
add_p.add_argument("--source", required=True, help="Where this evidence came from (e.g. 'git fsck', 'GH API /commits')")
|
|
222
|
+
add_p.add_argument("--content", required=True, help="The evidence content (commit SHA, API response excerpt, etc.)")
|
|
223
|
+
add_p.add_argument("--type", required=True, choices=EVIDENCE_TYPES, dest="evidence_type", help="Evidence type")
|
|
224
|
+
add_p.add_argument("--actor", help="GitHub handle or email of associated actor")
|
|
225
|
+
add_p.add_argument("--url", help="URL to original source")
|
|
226
|
+
add_p.add_argument("--timestamp", help="When the event occurred (ISO 8601)")
|
|
227
|
+
add_p.add_argument("--ioc-type", choices=IOC_TYPES, help="IOC subtype (for --type ioc)")
|
|
228
|
+
add_p.add_argument("--verification", choices=VERIFICATION_STATES, default="unverified")
|
|
229
|
+
add_p.add_argument("--notes", help="Additional investigator notes")
|
|
230
|
+
add_p.add_argument("--quiet", action="store_true", help="Suppress success message")
|
|
231
|
+
|
|
232
|
+
# --- list ---
|
|
233
|
+
list_p = subparsers.add_parser("list", help="List all evidence entries")
|
|
234
|
+
list_p.add_argument("--type", dest="filter_type", choices=EVIDENCE_TYPES, help="Filter by type")
|
|
235
|
+
list_p.add_argument("--actor", dest="filter_actor", help="Filter by actor")
|
|
236
|
+
|
|
237
|
+
# --- verify ---
|
|
238
|
+
subparsers.add_parser("verify", help="Verify SHA-256 integrity of all evidence content")
|
|
239
|
+
|
|
240
|
+
# --- query ---
|
|
241
|
+
query_p = subparsers.add_parser("query", help="Search evidence by keyword")
|
|
242
|
+
query_p.add_argument("keyword", help="Keyword to search for")
|
|
243
|
+
|
|
244
|
+
# --- export ---
|
|
245
|
+
subparsers.add_parser("export", help="Export evidence as a Markdown table (stdout)")
|
|
246
|
+
|
|
247
|
+
# --- summary ---
|
|
248
|
+
subparsers.add_parser("summary", help="Print investigation statistics")
|
|
249
|
+
|
|
250
|
+
args = parser.parse_args()
|
|
251
|
+
|
|
252
|
+
if not args.command:
|
|
253
|
+
parser.print_help()
|
|
254
|
+
sys.exit(0)
|
|
255
|
+
|
|
256
|
+
store = EvidenceStore(args.store)
|
|
257
|
+
|
|
258
|
+
if args.command == "add":
|
|
259
|
+
eid = store.add(
|
|
260
|
+
source=args.source,
|
|
261
|
+
content=args.content,
|
|
262
|
+
evidence_type=args.evidence_type,
|
|
263
|
+
actor=args.actor,
|
|
264
|
+
url=args.url,
|
|
265
|
+
timestamp=args.timestamp,
|
|
266
|
+
ioc_type=args.ioc_type,
|
|
267
|
+
verification=args.verification,
|
|
268
|
+
notes=args.notes,
|
|
269
|
+
)
|
|
270
|
+
if not getattr(args, "quiet", False):
|
|
271
|
+
print(f"✓ Added evidence: {eid}")
|
|
272
|
+
|
|
273
|
+
elif args.command == "list":
|
|
274
|
+
items = store.list_evidence(
|
|
275
|
+
filter_type=getattr(args, "filter_type", None),
|
|
276
|
+
filter_actor=getattr(args, "filter_actor", None),
|
|
277
|
+
)
|
|
278
|
+
if not items:
|
|
279
|
+
print("No evidence found.")
|
|
280
|
+
for e in items:
|
|
281
|
+
actor_str = f" | actor: {e['actor']}" if e.get("actor") else ""
|
|
282
|
+
url_str = f" | {e['url']}" if e.get("url") else ""
|
|
283
|
+
print(f"[{e['id']}] {e['type']:12s} | {e['verification']:20s} | {e['source']}{actor_str}{url_str}")
|
|
284
|
+
|
|
285
|
+
elif args.command == "verify":
|
|
286
|
+
issues = store.verify_integrity()
|
|
287
|
+
if not issues:
|
|
288
|
+
print(f"✓ All {len(store.data['evidence'])} evidence entries passed SHA-256 integrity check.")
|
|
289
|
+
else:
|
|
290
|
+
print(f"✗ {len(issues)} integrity issue(s) detected:")
|
|
291
|
+
for i in issues:
|
|
292
|
+
print(f" [{i['id']}] stored={i['stored_sha256'][:16]}... computed={i['computed_sha256'][:16]}...")
|
|
293
|
+
sys.exit(1)
|
|
294
|
+
|
|
295
|
+
elif args.command == "query":
|
|
296
|
+
results = store.query(args.keyword)
|
|
297
|
+
print(f"Found {len(results)} result(s) for '{args.keyword}':")
|
|
298
|
+
for e in results:
|
|
299
|
+
print(f" [{e['id']}] {e['type']} | {e['source']} | {e['content'][:80]}")
|
|
300
|
+
|
|
301
|
+
elif args.command == "export":
|
|
302
|
+
print(store.export_markdown())
|
|
303
|
+
|
|
304
|
+
elif args.command == "summary":
|
|
305
|
+
s = store.summary()
|
|
306
|
+
print(f"Total evidence items : {s['total']}")
|
|
307
|
+
print(f"By type : {json.dumps(s['by_type'], indent=2)}")
|
|
308
|
+
print(f"By verification : {json.dumps(s['by_verification'], indent=2)}")
|
|
309
|
+
print(f"Unique actors : {s['unique_actors']}")
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
if __name__ == "__main__":
|
|
313
|
+
main()
|