bps-kit 1.0.1 → 1.0.2
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/package.json +1 -1
- package/templates/.agents/agents/backend-specialist.md +263 -0
- package/templates/.agents/agents/code-archaeologist.md +106 -0
- package/templates/.agents/agents/database-architect.md +226 -0
- package/templates/.agents/agents/debugger.md +225 -0
- package/templates/.agents/agents/devops-engineer.md +242 -0
- package/templates/.agents/agents/documentation-writer.md +104 -0
- package/templates/.agents/agents/explorer-agent.md +73 -0
- package/templates/.agents/agents/frontend-specialist.md +593 -0
- package/templates/.agents/agents/game-developer.md +162 -0
- package/templates/.agents/agents/mobile-developer.md +377 -0
- package/templates/.agents/agents/orchestrator.md +416 -0
- package/templates/.agents/agents/penetration-tester.md +188 -0
- package/templates/.agents/agents/performance-optimizer.md +187 -0
- package/templates/.agents/agents/product-manager.md +112 -0
- package/templates/.agents/agents/product-owner.md +95 -0
- package/templates/.agents/agents/project-planner.md +406 -0
- package/templates/.agents/agents/qa-automation-engineer.md +103 -0
- package/templates/.agents/agents/security-auditor.md +170 -0
- package/templates/.agents/agents/seo-specialist.md +111 -0
- package/templates/.agents/agents/test-engineer.md +158 -0
- package/templates/.agents/rules/GEMINI.md +219 -0
- package/templates/.agents/scripts/auto_preview.py +148 -0
- package/templates/.agents/scripts/checklist.py +217 -0
- package/templates/.agents/scripts/session_manager.py +120 -0
- package/templates/.agents/scripts/verify_all.py +327 -0
- package/templates/.agents/workflows/brainstorm.md +113 -0
- package/templates/.agents/workflows/create.md +59 -0
- package/templates/.agents/workflows/debug.md +103 -0
- package/templates/.agents/workflows/deploy.md +176 -0
- package/templates/.agents/workflows/enhance.md +63 -0
- package/templates/.agents/workflows/orchestrate.md +237 -0
- package/templates/.agents/workflows/plan.md +89 -0
- package/templates/.agents/workflows/preview.md +81 -0
- package/templates/.agents/workflows/setup-brain.md +39 -0
- package/templates/.agents/workflows/status.md +86 -0
- package/templates/.agents/workflows/test.md +144 -0
- package/templates/.agents/workflows/ui-ux-pro-max.md +296 -0
- package/templates/skills_normal/api-patterns/scripts/api_validator.py +211 -0
- package/templates/skills_normal/database-design/scripts/schema_validator.py +172 -0
- package/templates/skills_normal/frontend-design/scripts/accessibility_checker.py +183 -0
- package/templates/skills_normal/frontend-design/scripts/ux_audit.py +722 -0
- package/templates/skills_normal/git-pushing/scripts/smart_commit.sh +19 -0
- package/templates/skills_normal/lint-and-validate/scripts/lint_runner.py +184 -0
- package/templates/skills_normal/lint-and-validate/scripts/type_coverage.py +173 -0
- package/templates/skills_normal/performance-profiling/scripts/lighthouse_audit.py +76 -0
- package/templates/skills_normal/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
- package/templates/skills_normal/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
- package/templates/skills_normal/senior-fullstack/scripts/project_scaffolder.py +114 -0
- package/templates/skills_normal/seo-fundamentals/scripts/seo_checker.py +219 -0
- package/templates/skills_normal/testing-patterns/scripts/test_runner.py +219 -0
- package/templates/skills_normal/vulnerability-scanner/scripts/security_scan.py +458 -0
- package/templates/vault/007/scripts/config.py +472 -0
- package/templates/vault/007/scripts/full_audit.py +1306 -0
- package/templates/vault/007/scripts/quick_scan.py +481 -0
- package/templates/vault/007/scripts/requirements.txt +26 -0
- package/templates/vault/007/scripts/scanners/__init__.py +0 -0
- package/templates/vault/007/scripts/scanners/dependency_scanner.py +1305 -0
- package/templates/vault/007/scripts/scanners/injection_scanner.py +1104 -0
- package/templates/vault/007/scripts/scanners/secrets_scanner.py +1008 -0
- package/templates/vault/007/scripts/score_calculator.py +693 -0
- package/templates/vault/agent-orchestrator/scripts/match_skills.py +329 -0
- package/templates/vault/agent-orchestrator/scripts/orchestrate.py +304 -0
- package/templates/vault/agent-orchestrator/scripts/requirements.txt +1 -0
- package/templates/vault/agent-orchestrator/scripts/scan_registry.py +508 -0
- package/templates/vault/ai-studio-image/scripts/config.py +613 -0
- package/templates/vault/ai-studio-image/scripts/generate.py +630 -0
- package/templates/vault/ai-studio-image/scripts/prompt_engine.py +424 -0
- package/templates/vault/ai-studio-image/scripts/requirements.txt +4 -0
- package/templates/vault/ai-studio-image/scripts/templates.py +349 -0
- package/templates/vault/android_ui_verification/scripts/verify_ui.sh +32 -0
- package/templates/vault/apify-audience-analysis/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-brand-reputation-monitoring/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-competitor-intelligence/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-content-analytics/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-ecommerce/reference/scripts/package.json +3 -0
- package/templates/vault/apify-ecommerce/reference/scripts/run_actor.js +369 -0
- package/templates/vault/apify-influencer-discovery/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-lead-generation/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-market-research/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-trend-analysis/reference/scripts/run_actor.js +363 -0
- package/templates/vault/apify-ultimate-scraper/reference/scripts/run_actor.js +363 -0
- package/templates/vault/audio-transcriber/scripts/install-requirements.sh +190 -0
- package/templates/vault/audio-transcriber/scripts/transcribe.py +486 -0
- package/templates/vault/claude-monitor/scripts/api_bench.py +240 -0
- package/templates/vault/claude-monitor/scripts/config.py +69 -0
- package/templates/vault/claude-monitor/scripts/health_check.py +362 -0
- package/templates/vault/claude-monitor/scripts/monitor.py +296 -0
- package/templates/vault/content-creator/scripts/brand_voice_analyzer.py +185 -0
- package/templates/vault/content-creator/scripts/seo_optimizer.py +419 -0
- package/templates/vault/context-agent/scripts/active_context.py +227 -0
- package/templates/vault/context-agent/scripts/compressor.py +149 -0
- package/templates/vault/context-agent/scripts/config.py +69 -0
- package/templates/vault/context-agent/scripts/context_loader.py +155 -0
- package/templates/vault/context-agent/scripts/context_manager.py +302 -0
- package/templates/vault/context-agent/scripts/models.py +103 -0
- package/templates/vault/context-agent/scripts/project_registry.py +132 -0
- package/templates/vault/context-agent/scripts/requirements.txt +6 -0
- package/templates/vault/context-agent/scripts/search.py +115 -0
- package/templates/vault/context-agent/scripts/session_parser.py +206 -0
- package/templates/vault/context-agent/scripts/session_summary.py +319 -0
- package/templates/vault/context-guardian/scripts/context_snapshot.py +229 -0
- package/templates/vault/docx/ooxml/scripts/pack.py +159 -0
- package/templates/vault/docx/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/docx/ooxml/scripts/validate.py +69 -0
- package/templates/vault/docx/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/docx/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/docx/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/docx/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/docx/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/docx/scripts/__init__.py +1 -0
- package/templates/vault/docx/scripts/document.py +1276 -0
- package/templates/vault/docx/scripts/templates/comments.xml +3 -0
- package/templates/vault/docx/scripts/templates/commentsExtended.xml +3 -0
- package/templates/vault/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/templates/vault/docx/scripts/templates/commentsIds.xml +3 -0
- package/templates/vault/docx/scripts/templates/people.xml +3 -0
- package/templates/vault/docx/scripts/utilities.py +374 -0
- package/templates/vault/docx-official/ooxml/scripts/pack.py +159 -0
- package/templates/vault/docx-official/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/docx-official/ooxml/scripts/validate.py +69 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/docx-official/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/docx-official/scripts/__init__.py +1 -0
- package/templates/vault/docx-official/scripts/document.py +1276 -0
- package/templates/vault/docx-official/scripts/templates/comments.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/commentsExtended.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/commentsExtensible.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/commentsIds.xml +3 -0
- package/templates/vault/docx-official/scripts/templates/people.xml +3 -0
- package/templates/vault/docx-official/scripts/utilities.py +374 -0
- package/templates/vault/geo-fundamentals/scripts/geo_checker.py +289 -0
- package/templates/vault/helm-chart-scaffolding/scripts/validate-chart.sh +244 -0
- package/templates/vault/i18n-localization/scripts/i18n_checker.py +241 -0
- package/templates/vault/instagram/scripts/account_setup.py +233 -0
- package/templates/vault/instagram/scripts/analyze.py +221 -0
- package/templates/vault/instagram/scripts/api_client.py +444 -0
- package/templates/vault/instagram/scripts/auth.py +411 -0
- package/templates/vault/instagram/scripts/comments.py +160 -0
- package/templates/vault/instagram/scripts/config.py +111 -0
- package/templates/vault/instagram/scripts/db.py +467 -0
- package/templates/vault/instagram/scripts/export.py +138 -0
- package/templates/vault/instagram/scripts/governance.py +233 -0
- package/templates/vault/instagram/scripts/hashtags.py +114 -0
- package/templates/vault/instagram/scripts/insights.py +170 -0
- package/templates/vault/instagram/scripts/media.py +65 -0
- package/templates/vault/instagram/scripts/messages.py +103 -0
- package/templates/vault/instagram/scripts/profile.py +58 -0
- package/templates/vault/instagram/scripts/publish.py +449 -0
- package/templates/vault/instagram/scripts/requirements.txt +5 -0
- package/templates/vault/instagram/scripts/run_all.py +189 -0
- package/templates/vault/instagram/scripts/schedule.py +189 -0
- package/templates/vault/instagram/scripts/serve_api.py +234 -0
- package/templates/vault/instagram/scripts/templates.py +155 -0
- package/templates/vault/junta-leiloeiros/scripts/db.py +216 -0
- package/templates/vault/junta-leiloeiros/scripts/export.py +137 -0
- package/templates/vault/junta-leiloeiros/scripts/requirements.txt +15 -0
- package/templates/vault/junta-leiloeiros/scripts/run_all.py +190 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/__init__.py +4 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/base_scraper.py +209 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/generic_scraper.py +110 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucap.py +110 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/juceac.py +72 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/juceal.py +72 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/juceb.py +68 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucec.py +63 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucema.py +211 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucemg.py +218 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucep.py +70 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepa.py +74 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepar.py +80 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepe.py +78 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucepi.py +69 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucer.py +256 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucerja.py +170 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucern.py +71 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucesc.py +89 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucesp.py +233 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucetins.py +134 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucis_df.py +63 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/jucisrs.py +299 -0
- package/templates/vault/junta-leiloeiros/scripts/scraper/states.py +99 -0
- package/templates/vault/junta-leiloeiros/scripts/serve_api.py +164 -0
- package/templates/vault/junta-leiloeiros/scripts/web_scraper_fallback.py +233 -0
- package/templates/vault/last30days/scripts/last30days.py +521 -0
- package/templates/vault/last30days/scripts/lib/__init__.py +1 -0
- package/templates/vault/last30days/scripts/lib/cache.py +152 -0
- package/templates/vault/last30days/scripts/lib/dates.py +124 -0
- package/templates/vault/last30days/scripts/lib/dedupe.py +120 -0
- package/templates/vault/last30days/scripts/lib/env.py +149 -0
- package/templates/vault/last30days/scripts/lib/http.py +152 -0
- package/templates/vault/last30days/scripts/lib/models.py +175 -0
- package/templates/vault/last30days/scripts/lib/normalize.py +160 -0
- package/templates/vault/last30days/scripts/lib/openai_reddit.py +230 -0
- package/templates/vault/last30days/scripts/lib/reddit_enrich.py +232 -0
- package/templates/vault/last30days/scripts/lib/render.py +383 -0
- package/templates/vault/last30days/scripts/lib/schema.py +336 -0
- package/templates/vault/last30days/scripts/lib/score.py +311 -0
- package/templates/vault/last30days/scripts/lib/ui.py +324 -0
- package/templates/vault/last30days/scripts/lib/websearch.py +401 -0
- package/templates/vault/last30days/scripts/lib/xai_x.py +217 -0
- package/templates/vault/leiloeiro-avaliacao/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-avaliacao/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-edital/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-edital/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-ia/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-ia/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-juridico/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-juridico/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-mercado/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-mercado/scripts/requirements.txt +1 -0
- package/templates/vault/leiloeiro-risco/scripts/governance.py +106 -0
- package/templates/vault/leiloeiro-risco/scripts/requirements.txt +1 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/database.ts +24 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/db.ts +35 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/index.ts +2 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/migrations.ts +31 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/schema.sql +8 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/index.ts +44 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/routes/todos.ts +155 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/types/index.ts +35 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.css +384 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.tsx +81 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/api/todos.ts +57 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/ConfirmDialog.tsx +26 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/EmptyState.tsx +8 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoForm.tsx +43 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoItem.tsx +36 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoList.tsx +27 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/hooks/useTodos.ts +81 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/index.css +48 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/main.tsx +10 -0
- package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/vite-env.d.ts +1 -0
- package/templates/vault/loki-mode/scripts/export-to-vibe-kanban.sh +178 -0
- package/templates/vault/loki-mode/scripts/loki-wrapper.sh +281 -0
- package/templates/vault/loki-mode/scripts/take-screenshots.js +55 -0
- package/templates/vault/matematico-tao/scripts/complexity_analyzer.py +544 -0
- package/templates/vault/matematico-tao/scripts/dependency_graph.py +538 -0
- package/templates/vault/mcp-builder/scripts/connections.py +151 -0
- package/templates/vault/mcp-builder/scripts/evaluation.py +373 -0
- package/templates/vault/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/templates/vault/mcp-builder/scripts/requirements.txt +2 -0
- package/templates/vault/mobile-design/scripts/mobile_audit.py +670 -0
- package/templates/vault/notebooklm/scripts/__init__.py +81 -0
- package/templates/vault/notebooklm/scripts/ask_question.py +256 -0
- package/templates/vault/notebooklm/scripts/auth_manager.py +358 -0
- package/templates/vault/notebooklm/scripts/browser_session.py +255 -0
- package/templates/vault/notebooklm/scripts/browser_utils.py +107 -0
- package/templates/vault/notebooklm/scripts/cleanup_manager.py +302 -0
- package/templates/vault/notebooklm/scripts/config.py +44 -0
- package/templates/vault/notebooklm/scripts/notebook_manager.py +410 -0
- package/templates/vault/notebooklm/scripts/run.py +102 -0
- package/templates/vault/notebooklm/scripts/setup_environment.py +204 -0
- package/templates/vault/pdf/scripts/check_bounding_boxes.py +70 -0
- package/templates/vault/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/templates/vault/pdf/scripts/check_fillable_fields.py +12 -0
- package/templates/vault/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/templates/vault/pdf/scripts/create_validation_image.py +41 -0
- package/templates/vault/pdf/scripts/extract_form_field_info.py +152 -0
- package/templates/vault/pdf/scripts/fill_fillable_fields.py +114 -0
- package/templates/vault/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/templates/vault/pdf-official/scripts/check_bounding_boxes.py +70 -0
- package/templates/vault/pdf-official/scripts/check_bounding_boxes_test.py +226 -0
- package/templates/vault/pdf-official/scripts/check_fillable_fields.py +12 -0
- package/templates/vault/pdf-official/scripts/convert_pdf_to_images.py +35 -0
- package/templates/vault/pdf-official/scripts/create_validation_image.py +41 -0
- package/templates/vault/pdf-official/scripts/extract_form_field_info.py +152 -0
- package/templates/vault/pdf-official/scripts/fill_fillable_fields.py +114 -0
- package/templates/vault/pdf-official/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/templates/vault/planning-with-files/scripts/check-complete.sh +44 -0
- package/templates/vault/planning-with-files/scripts/init-session.sh +120 -0
- package/templates/vault/pptx/ooxml/scripts/pack.py +159 -0
- package/templates/vault/pptx/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/pptx/ooxml/scripts/validate.py +69 -0
- package/templates/vault/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/pptx/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/pptx/scripts/html2pptx.js +979 -0
- package/templates/vault/pptx/scripts/inventory.py +1020 -0
- package/templates/vault/pptx/scripts/rearrange.py +231 -0
- package/templates/vault/pptx/scripts/replace.py +385 -0
- package/templates/vault/pptx/scripts/thumbnail.py +450 -0
- package/templates/vault/pptx-official/ooxml/scripts/pack.py +159 -0
- package/templates/vault/pptx-official/ooxml/scripts/unpack.py +29 -0
- package/templates/vault/pptx-official/ooxml/scripts/validate.py +69 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/base.py +951 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/docx.py +274 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/vault/pptx-official/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/vault/pptx-official/scripts/html2pptx.js +979 -0
- package/templates/vault/pptx-official/scripts/inventory.py +1020 -0
- package/templates/vault/pptx-official/scripts/rearrange.py +231 -0
- package/templates/vault/pptx-official/scripts/replace.py +385 -0
- package/templates/vault/pptx-official/scripts/thumbnail.py +450 -0
- package/templates/vault/product-manager-toolkit/scripts/customer_interview_analyzer.py +441 -0
- package/templates/vault/product-manager-toolkit/scripts/rice_prioritizer.py +296 -0
- package/templates/vault/prompt-engineering-patterns/scripts/optimize-prompt.py +279 -0
- package/templates/vault/scripts/.skill_cache.json +7538 -0
- package/templates/vault/scripts/skill_search.py +228 -0
- package/templates/vault/senior-architect/scripts/architecture_diagram_generator.py +114 -0
- package/templates/vault/senior-architect/scripts/dependency_analyzer.py +114 -0
- package/templates/vault/senior-architect/scripts/project_architect.py +114 -0
- package/templates/vault/shopify-development/scripts/requirements.txt +19 -0
- package/templates/vault/shopify-development/scripts/shopify_graphql.py +428 -0
- package/templates/vault/shopify-development/scripts/shopify_init.py +441 -0
- package/templates/vault/shopify-development/scripts/tests/test_shopify_init.py +379 -0
- package/templates/vault/skill-creator/scripts/init_skill.py +303 -0
- package/templates/vault/skill-creator/scripts/package_skill.py +110 -0
- package/templates/vault/skill-creator/scripts/quick_validate.py +95 -0
- package/templates/vault/skill-installer/scripts/detect_skills.py +318 -0
- package/templates/vault/skill-installer/scripts/install_skill.py +1708 -0
- package/templates/vault/skill-installer/scripts/package_skill.py +417 -0
- package/templates/vault/skill-installer/scripts/requirements.txt +1 -0
- package/templates/vault/skill-installer/scripts/validate_skill.py +430 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/__init__.py +13 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/code_quality.py +247 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/cross_skill.py +134 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/dependencies.py +121 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/documentation.py +189 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/governance_audit.py +153 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/performance.py +164 -0
- package/templates/vault/skill-sentinel/scripts/analyzers/security.py +189 -0
- package/templates/vault/skill-sentinel/scripts/config.py +158 -0
- package/templates/vault/skill-sentinel/scripts/cost_optimizer.py +146 -0
- package/templates/vault/skill-sentinel/scripts/db.py +354 -0
- package/templates/vault/skill-sentinel/scripts/governance.py +58 -0
- package/templates/vault/skill-sentinel/scripts/recommender.py +228 -0
- package/templates/vault/skill-sentinel/scripts/report_generator.py +224 -0
- package/templates/vault/skill-sentinel/scripts/requirements.txt +1 -0
- package/templates/vault/skill-sentinel/scripts/run_audit.py +290 -0
- package/templates/vault/skill-sentinel/scripts/scanner.py +271 -0
- package/templates/vault/stability-ai/scripts/config.py +266 -0
- package/templates/vault/stability-ai/scripts/generate.py +687 -0
- package/templates/vault/stability-ai/scripts/requirements.txt +4 -0
- package/templates/vault/stability-ai/scripts/styles.py +174 -0
- package/templates/vault/telegram/assets/boilerplate/nodejs/src/bot-client.ts +86 -0
- package/templates/vault/telegram/assets/boilerplate/nodejs/src/handlers.ts +79 -0
- package/templates/vault/telegram/assets/boilerplate/nodejs/src/index.ts +32 -0
- package/templates/vault/telegram/scripts/send_message.py +143 -0
- package/templates/vault/telegram/scripts/setup_project.py +103 -0
- package/templates/vault/telegram/scripts/test_bot.py +144 -0
- package/templates/vault/typescript-expert/scripts/ts_diagnostic.py +203 -0
- package/templates/vault/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/templates/vault/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/templates/vault/ui-ux-pro-max/scripts/core.py +257 -0
- package/templates/vault/ui-ux-pro-max/scripts/design_system.py +487 -0
- package/templates/vault/ui-ux-pro-max/scripts/search.py +76 -0
- package/templates/vault/videodb/scripts/ws_listener.py +204 -0
- package/templates/vault/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
- package/templates/vault/web-artifacts-builder/scripts/init-artifact.sh +322 -0
- package/templates/vault/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
- package/templates/vault/webapp-testing/scripts/with_server.py +106 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/index.ts +125 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/template-manager.ts +67 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/types.ts +216 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/webhook-handler.ts +173 -0
- package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/whatsapp-client.ts +193 -0
- package/templates/vault/whatsapp-cloud-api/scripts/send_test_message.py +137 -0
- package/templates/vault/whatsapp-cloud-api/scripts/setup_project.py +118 -0
- package/templates/vault/whatsapp-cloud-api/scripts/validate_config.py +190 -0
- package/templates/vault/youtube-summarizer/scripts/extract-transcript.py +65 -0
- package/templates/vault/youtube-summarizer/scripts/install-dependencies.sh +28 -0
|
@@ -0,0 +1,1306 @@
|
|
|
1
|
+
"""007 Full Audit -- Comprehensive 6-phase security audit orchestrator.
|
|
2
|
+
|
|
3
|
+
Executes the complete 007 security audit pipeline:
|
|
4
|
+
Phase 1: Surface Mapping -- file inventory, entry points, dependencies
|
|
5
|
+
Phase 2: Threat Modeling Hints -- identify components for STRIDE analysis
|
|
6
|
+
Phase 3: Security Checklist -- run all scanners, compile results
|
|
7
|
+
Phase 4: Red Team Scenarios -- template-based attack scenarios
|
|
8
|
+
Phase 5: Blue Team Recs -- hardening recommendations per finding
|
|
9
|
+
Phase 6: Verdict -- compute score and emit final verdict
|
|
10
|
+
|
|
11
|
+
Generates a comprehensive Markdown report saved to data/reports/ and prints
|
|
12
|
+
a summary to stdout.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
python full_audit.py --target /path/to/project
|
|
16
|
+
python full_audit.py --target /path/to/project --output markdown
|
|
17
|
+
python full_audit.py --target /path/to/project --phase 3 --verbose
|
|
18
|
+
python full_audit.py --target /path/to/project --output json
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import argparse
|
|
22
|
+
import json
|
|
23
|
+
import os
|
|
24
|
+
import re
|
|
25
|
+
import sys
|
|
26
|
+
import time
|
|
27
|
+
from datetime import datetime, timezone
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# Imports from the 007 config hub (same directory)
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
34
|
+
|
|
35
|
+
from config import ( # noqa: E402
|
|
36
|
+
BASE_DIR,
|
|
37
|
+
DATA_DIR,
|
|
38
|
+
REPORTS_DIR,
|
|
39
|
+
SCANNABLE_EXTENSIONS,
|
|
40
|
+
SKIP_DIRECTORIES,
|
|
41
|
+
SCORING_WEIGHTS,
|
|
42
|
+
SCORING_LABELS,
|
|
43
|
+
SEVERITY,
|
|
44
|
+
LIMITS,
|
|
45
|
+
ensure_directories,
|
|
46
|
+
get_verdict,
|
|
47
|
+
get_timestamp,
|
|
48
|
+
log_audit_event,
|
|
49
|
+
setup_logging,
|
|
50
|
+
calculate_weighted_score,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# Import scanners
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent / "scanners"))
|
|
57
|
+
|
|
58
|
+
import secrets_scanner # noqa: E402
|
|
59
|
+
import dependency_scanner # noqa: E402
|
|
60
|
+
import injection_scanner # noqa: E402
|
|
61
|
+
import quick_scan # noqa: E402
|
|
62
|
+
import score_calculator # noqa: E402
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
# Logger
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
logger = setup_logging("007-full-audit")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# =========================================================================
|
|
71
|
+
# RED TEAM SCENARIO TEMPLATES
|
|
72
|
+
# =========================================================================
|
|
73
|
+
# Mapping from finding type/pattern -> attack scenario template.
|
|
74
|
+
|
|
75
|
+
_RED_TEAM_TEMPLATES: dict[str, dict] = {
|
|
76
|
+
# --- Secrets ---
|
|
77
|
+
"secret": {
|
|
78
|
+
"title": "Credential Theft via Leaked Secret",
|
|
79
|
+
"persona": "External attacker / Insider",
|
|
80
|
+
"scenario": (
|
|
81
|
+
"Attacker discovers leaked credential ({pattern}) in {file} "
|
|
82
|
+
"and uses it to gain unauthorized access to the associated "
|
|
83
|
+
"service or resource. Depending on the credential scope, "
|
|
84
|
+
"the attacker may escalate to full account takeover."
|
|
85
|
+
),
|
|
86
|
+
"impact": "Unauthorized access, data exfiltration, lateral movement",
|
|
87
|
+
"difficulty": "Easy (if credential is in public repo) / Medium (if private)",
|
|
88
|
+
},
|
|
89
|
+
# --- Injection ---
|
|
90
|
+
"code_injection": {
|
|
91
|
+
"title": "Remote Code Execution via Code Injection",
|
|
92
|
+
"persona": "Malicious user / Compromised agent",
|
|
93
|
+
"scenario": (
|
|
94
|
+
"Attacker crafts malicious input targeting {pattern} in {file}. "
|
|
95
|
+
"The injected code executes in the server context, allowing "
|
|
96
|
+
"arbitrary command execution, data access, or system compromise."
|
|
97
|
+
),
|
|
98
|
+
"impact": "Full server compromise, data breach, service disruption",
|
|
99
|
+
"difficulty": "Medium",
|
|
100
|
+
},
|
|
101
|
+
"command_injection": {
|
|
102
|
+
"title": "System Compromise via Command Injection",
|
|
103
|
+
"persona": "Malicious user / API abuser",
|
|
104
|
+
"scenario": (
|
|
105
|
+
"Attacker injects OS commands through {pattern} in {file}. "
|
|
106
|
+
"The shell executes attacker-controlled commands, enabling "
|
|
107
|
+
"file access, reverse shells, or privilege escalation."
|
|
108
|
+
),
|
|
109
|
+
"impact": "Full system compromise, lateral movement",
|
|
110
|
+
"difficulty": "Medium",
|
|
111
|
+
},
|
|
112
|
+
"sql_injection": {
|
|
113
|
+
"title": "Data Breach via SQL Injection",
|
|
114
|
+
"persona": "Malicious user / Bot",
|
|
115
|
+
"scenario": (
|
|
116
|
+
"Attacker crafts SQL payload targeting {pattern} in {file}. "
|
|
117
|
+
"The malformed query bypasses authentication, extracts sensitive "
|
|
118
|
+
"data, modifies records, or drops tables."
|
|
119
|
+
),
|
|
120
|
+
"impact": "Data breach, data loss, authentication bypass",
|
|
121
|
+
"difficulty": "Easy to Medium",
|
|
122
|
+
},
|
|
123
|
+
"prompt_injection": {
|
|
124
|
+
"title": "AI Manipulation via Prompt Injection",
|
|
125
|
+
"persona": "Malicious user / Compromised data source",
|
|
126
|
+
"scenario": (
|
|
127
|
+
"Attacker injects adversarial prompt through {pattern} in {file}. "
|
|
128
|
+
"The LLM follows injected instructions, potentially exfiltrating "
|
|
129
|
+
"data, bypassing safety controls, or performing unauthorized actions."
|
|
130
|
+
),
|
|
131
|
+
"impact": "Data leakage, unauthorized actions, reputation damage",
|
|
132
|
+
"difficulty": "Easy to Medium",
|
|
133
|
+
},
|
|
134
|
+
"xss": {
|
|
135
|
+
"title": "User Account Takeover via XSS",
|
|
136
|
+
"persona": "Malicious user",
|
|
137
|
+
"scenario": (
|
|
138
|
+
"Attacker injects JavaScript through {pattern} in {file}. "
|
|
139
|
+
"The script executes in victim browsers, stealing session tokens, "
|
|
140
|
+
"redirecting users, or performing actions on their behalf."
|
|
141
|
+
),
|
|
142
|
+
"impact": "Session hijacking, credential theft, phishing",
|
|
143
|
+
"difficulty": "Easy",
|
|
144
|
+
},
|
|
145
|
+
"ssrf": {
|
|
146
|
+
"title": "Internal Network Scanning via SSRF",
|
|
147
|
+
"persona": "External attacker",
|
|
148
|
+
"scenario": (
|
|
149
|
+
"Attacker manipulates server-side request through {pattern} in {file}. "
|
|
150
|
+
"The server makes requests to internal services, cloud metadata endpoints, "
|
|
151
|
+
"or other internal resources on the attacker's behalf."
|
|
152
|
+
),
|
|
153
|
+
"impact": "Internal network exposure, cloud credential theft, data access",
|
|
154
|
+
"difficulty": "Medium",
|
|
155
|
+
},
|
|
156
|
+
"path_traversal": {
|
|
157
|
+
"title": "Sensitive File Access via Path Traversal",
|
|
158
|
+
"persona": "Malicious user",
|
|
159
|
+
"scenario": (
|
|
160
|
+
"Attacker uses directory traversal sequences (../) through {pattern} "
|
|
161
|
+
"in {file} to access files outside the intended directory, "
|
|
162
|
+
"including configuration files, credentials, or system files."
|
|
163
|
+
),
|
|
164
|
+
"impact": "Credential exposure, configuration leak, source code theft",
|
|
165
|
+
"difficulty": "Easy",
|
|
166
|
+
},
|
|
167
|
+
# --- Dependencies ---
|
|
168
|
+
"dependency": {
|
|
169
|
+
"title": "Supply Chain Attack via Vulnerable Dependency",
|
|
170
|
+
"persona": "Supply chain attacker",
|
|
171
|
+
"scenario": (
|
|
172
|
+
"Attacker compromises a dependency ({pattern}) used in {file}. "
|
|
173
|
+
"Malicious code in the dependency executes during install or runtime, "
|
|
174
|
+
"exfiltrating secrets, installing backdoors, or modifying behavior."
|
|
175
|
+
),
|
|
176
|
+
"impact": "Full compromise, backdoor installation, data exfiltration",
|
|
177
|
+
"difficulty": "Hard (requires compromising upstream package)",
|
|
178
|
+
},
|
|
179
|
+
# --- Auth missing ---
|
|
180
|
+
"no_auth": {
|
|
181
|
+
"title": "Unauthorized Access to Unprotected Endpoints",
|
|
182
|
+
"persona": "Any external attacker / Bot",
|
|
183
|
+
"scenario": (
|
|
184
|
+
"Attacker discovers unprotected API endpoints or routes "
|
|
185
|
+
"with no authentication middleware. Direct access allows "
|
|
186
|
+
"data extraction, modification, or service abuse without credentials."
|
|
187
|
+
),
|
|
188
|
+
"impact": "Data breach, unauthorized actions, resource abuse",
|
|
189
|
+
"difficulty": "Easy",
|
|
190
|
+
},
|
|
191
|
+
# --- Dangerous code ---
|
|
192
|
+
"dangerous_code": {
|
|
193
|
+
"title": "Exploitation of Dangerous Code Pattern",
|
|
194
|
+
"persona": "Malicious user / Insider",
|
|
195
|
+
"scenario": (
|
|
196
|
+
"Attacker exploits dangerous code construct ({pattern}) in {file}. "
|
|
197
|
+
"The construct allows unintended behavior such as arbitrary code "
|
|
198
|
+
"execution, deserialization attacks, or unsafe data processing."
|
|
199
|
+
),
|
|
200
|
+
"impact": "Code execution, data manipulation, service disruption",
|
|
201
|
+
"difficulty": "Medium",
|
|
202
|
+
},
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
# Fallback template for finding types not explicitly mapped
|
|
206
|
+
_RED_TEAM_FALLBACK = {
|
|
207
|
+
"title": "Exploitation of Security Weakness",
|
|
208
|
+
"persona": "Opportunistic attacker",
|
|
209
|
+
"scenario": (
|
|
210
|
+
"Attacker discovers and exploits security weakness ({pattern}) "
|
|
211
|
+
"in {file}. The specific impact depends on the context and "
|
|
212
|
+
"the attacker's capabilities."
|
|
213
|
+
),
|
|
214
|
+
"impact": "Variable -- depends on finding severity and context",
|
|
215
|
+
"difficulty": "Variable",
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# =========================================================================
|
|
220
|
+
# BLUE TEAM RECOMMENDATION TEMPLATES
|
|
221
|
+
# =========================================================================
|
|
222
|
+
|
|
223
|
+
_BLUE_TEAM_TEMPLATES: dict[str, dict] = {
|
|
224
|
+
"secret": {
|
|
225
|
+
"recommendation": (
|
|
226
|
+
"Move secrets to environment variables, a secrets manager (e.g. AWS "
|
|
227
|
+
"Secrets Manager, HashiCorp Vault), or a .env file excluded from "
|
|
228
|
+
"version control. Add a pre-commit hook (e.g. detect-secrets, "
|
|
229
|
+
"gitleaks) to prevent future leaks. Rotate the compromised credential "
|
|
230
|
+
"immediately."
|
|
231
|
+
),
|
|
232
|
+
"priority": "CRITICAL",
|
|
233
|
+
"effort": "Low",
|
|
234
|
+
},
|
|
235
|
+
"code_injection": {
|
|
236
|
+
"recommendation": (
|
|
237
|
+
"Remove all uses of eval(), exec(), and Function(). If dynamic "
|
|
238
|
+
"code execution is absolutely necessary, use a sandboxed environment "
|
|
239
|
+
"(e.g. RestrictedPython, vm2 in strict mode) with allowlisted "
|
|
240
|
+
"operations only. Never pass user input to code execution functions."
|
|
241
|
+
),
|
|
242
|
+
"priority": "CRITICAL",
|
|
243
|
+
"effort": "Medium",
|
|
244
|
+
},
|
|
245
|
+
"command_injection": {
|
|
246
|
+
"recommendation": (
|
|
247
|
+
"Replace os.system(), os.popen(), and subprocess with shell=True "
|
|
248
|
+
"with subprocess.run() using shell=False and a list of arguments. "
|
|
249
|
+
"Never concatenate user input into shell commands. Validate and "
|
|
250
|
+
"sanitize all inputs. Use shlex.quote() if shell is unavoidable."
|
|
251
|
+
),
|
|
252
|
+
"priority": "CRITICAL",
|
|
253
|
+
"effort": "Low to Medium",
|
|
254
|
+
},
|
|
255
|
+
"sql_injection": {
|
|
256
|
+
"recommendation": (
|
|
257
|
+
"Use parameterized queries (placeholders) for ALL database operations. "
|
|
258
|
+
"Never use f-strings, .format(), or string concatenation in SQL. "
|
|
259
|
+
"Use an ORM (SQLAlchemy, Django ORM) when possible. Add input "
|
|
260
|
+
"validation and type checking before database operations."
|
|
261
|
+
),
|
|
262
|
+
"priority": "CRITICAL",
|
|
263
|
+
"effort": "Low",
|
|
264
|
+
},
|
|
265
|
+
"prompt_injection": {
|
|
266
|
+
"recommendation": (
|
|
267
|
+
"Separate system prompts from user content using proper message "
|
|
268
|
+
"structure (system/user/assistant roles). Never concatenate user "
|
|
269
|
+
"input directly into system prompts. Add input sanitization, "
|
|
270
|
+
"output filtering, and content safety guardrails. Limit tool "
|
|
271
|
+
"access and implement output validation."
|
|
272
|
+
),
|
|
273
|
+
"priority": "HIGH",
|
|
274
|
+
"effort": "Medium",
|
|
275
|
+
},
|
|
276
|
+
"xss": {
|
|
277
|
+
"recommendation": (
|
|
278
|
+
"Never set innerHTML or use dangerouslySetInnerHTML with user content. "
|
|
279
|
+
"Use textContent for safe text insertion. Implement Content Security "
|
|
280
|
+
"Policy (CSP) headers. Use template engines with auto-escaping "
|
|
281
|
+
"(Jinja2 with autoescape, React JSX). Sanitize user HTML with "
|
|
282
|
+
"DOMPurify or bleach."
|
|
283
|
+
),
|
|
284
|
+
"priority": "HIGH",
|
|
285
|
+
"effort": "Low to Medium",
|
|
286
|
+
},
|
|
287
|
+
"ssrf": {
|
|
288
|
+
"recommendation": (
|
|
289
|
+
"Implement URL allowlisting for outbound requests. Block requests "
|
|
290
|
+
"to private IP ranges (10.x, 172.16-31.x, 192.168.x), localhost, "
|
|
291
|
+
"and cloud metadata endpoints (169.254.169.254). Validate and "
|
|
292
|
+
"parse URLs before making requests. Use a dedicated HTTP client "
|
|
293
|
+
"with SSRF protections."
|
|
294
|
+
),
|
|
295
|
+
"priority": "HIGH",
|
|
296
|
+
"effort": "Medium",
|
|
297
|
+
},
|
|
298
|
+
"path_traversal": {
|
|
299
|
+
"recommendation": (
|
|
300
|
+
"Use Path.resolve() and verify the resolved path starts with the "
|
|
301
|
+
"expected base directory. Never pass raw user input to open() or "
|
|
302
|
+
"file operations. Use os.path.realpath() followed by a prefix check. "
|
|
303
|
+
"Implement a file access allowlist."
|
|
304
|
+
),
|
|
305
|
+
"priority": "HIGH",
|
|
306
|
+
"effort": "Low",
|
|
307
|
+
},
|
|
308
|
+
"dependency": {
|
|
309
|
+
"recommendation": (
|
|
310
|
+
"Pin all dependency versions with exact versions (not ranges). "
|
|
311
|
+
"Use lock files (pip freeze, package-lock.json, poetry.lock). "
|
|
312
|
+
"Run regular vulnerability scans (safety, npm audit, Snyk). "
|
|
313
|
+
"Remove unused dependencies. Verify package integrity with hashes."
|
|
314
|
+
),
|
|
315
|
+
"priority": "MEDIUM",
|
|
316
|
+
"effort": "Low",
|
|
317
|
+
},
|
|
318
|
+
"dangerous_code": {
|
|
319
|
+
"recommendation": (
|
|
320
|
+
"Replace dangerous constructs with safe alternatives: "
|
|
321
|
+
"pickle -> json, yaml.load -> yaml.safe_load, eval -> ast.literal_eval "
|
|
322
|
+
"(for literals only). Add input validation before any dynamic operation. "
|
|
323
|
+
"Implement proper error handling and type checking."
|
|
324
|
+
),
|
|
325
|
+
"priority": "HIGH",
|
|
326
|
+
"effort": "Low to Medium",
|
|
327
|
+
},
|
|
328
|
+
"no_auth": {
|
|
329
|
+
"recommendation": (
|
|
330
|
+
"Add authentication middleware to all endpoints except public "
|
|
331
|
+
"health checks. Implement RBAC/ABAC for authorization. Use "
|
|
332
|
+
"established auth libraries (Flask-Login, Passport.js, Django auth). "
|
|
333
|
+
"Add rate limiting to prevent brute force attacks."
|
|
334
|
+
),
|
|
335
|
+
"priority": "CRITICAL",
|
|
336
|
+
"effort": "Medium",
|
|
337
|
+
},
|
|
338
|
+
"permission": {
|
|
339
|
+
"recommendation": (
|
|
340
|
+
"Set restrictive file permissions (600 for secrets, 644 for configs, "
|
|
341
|
+
"755 for executables). Never use 777. Run services as non-root users. "
|
|
342
|
+
"Use chown/chmod to enforce ownership."
|
|
343
|
+
),
|
|
344
|
+
"priority": "MEDIUM",
|
|
345
|
+
"effort": "Low",
|
|
346
|
+
},
|
|
347
|
+
"large_file": {
|
|
348
|
+
"recommendation": (
|
|
349
|
+
"Investigate oversized files for accidentally committed binaries, "
|
|
350
|
+
"databases, or data dumps. Add them to .gitignore. Use Git LFS "
|
|
351
|
+
"for legitimate large files."
|
|
352
|
+
),
|
|
353
|
+
"priority": "LOW",
|
|
354
|
+
"effort": "Low",
|
|
355
|
+
},
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
_BLUE_TEAM_FALLBACK = {
|
|
359
|
+
"recommendation": (
|
|
360
|
+
"Review the finding in context and apply the principle of least "
|
|
361
|
+
"privilege. Add input validation, proper error handling, and "
|
|
362
|
+
"logging. Consult OWASP guidelines for the specific vulnerability type."
|
|
363
|
+
),
|
|
364
|
+
"priority": "MEDIUM",
|
|
365
|
+
"effort": "Variable",
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
# =========================================================================
|
|
370
|
+
# PHASE IMPLEMENTATIONS
|
|
371
|
+
# =========================================================================
|
|
372
|
+
|
|
373
|
+
def _phase1_surface_mapping(target: Path, verbose: bool = False) -> dict:
|
|
374
|
+
"""Phase 1: Surface Mapping -- inventory files, entry points, dependencies."""
|
|
375
|
+
logger.info("Phase 1: Surface Mapping")
|
|
376
|
+
|
|
377
|
+
files_by_type: dict[str, int] = {}
|
|
378
|
+
entry_points: list[str] = []
|
|
379
|
+
dependency_files: list[str] = []
|
|
380
|
+
config_files: list[str] = []
|
|
381
|
+
total_files = 0
|
|
382
|
+
|
|
383
|
+
_entry_point_patterns = [
|
|
384
|
+
re.compile(r"""(?i)(?:^main\.py|^app\.py|^server\.py|^index\.\w+|^manage\.py)"""),
|
|
385
|
+
re.compile(r"""(?i)(?:^wsgi\.py|^asgi\.py|^gunicorn|^uvicorn)"""),
|
|
386
|
+
re.compile(r"""(?i)(?:^Dockerfile|^docker-compose)"""),
|
|
387
|
+
re.compile(r"""(?i)(?:\.github[/\\]workflows|Jenkinsfile|\.gitlab-ci)"""),
|
|
388
|
+
]
|
|
389
|
+
|
|
390
|
+
_dep_file_names = {
|
|
391
|
+
"requirements.txt", "requirements-dev.txt", "requirements-test.txt",
|
|
392
|
+
"setup.py", "setup.cfg", "pyproject.toml", "Pipfile", "Pipfile.lock",
|
|
393
|
+
"package.json", "package-lock.json", "yarn.lock", "pnpm-lock.yaml",
|
|
394
|
+
"go.mod", "go.sum", "Cargo.toml", "Cargo.lock",
|
|
395
|
+
"Gemfile", "Gemfile.lock", "composer.json", "composer.lock",
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
_config_extensions = {".json", ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf", ".env"}
|
|
399
|
+
|
|
400
|
+
for root, dirs, filenames in os.walk(target):
|
|
401
|
+
dirs[:] = [d for d in dirs if d not in SKIP_DIRECTORIES]
|
|
402
|
+
|
|
403
|
+
for fname in filenames:
|
|
404
|
+
total_files += 1
|
|
405
|
+
fpath = Path(root) / fname
|
|
406
|
+
suffix = fpath.suffix.lower()
|
|
407
|
+
|
|
408
|
+
# Categorize by extension
|
|
409
|
+
ext_key = suffix if suffix else "(no extension)"
|
|
410
|
+
files_by_type[ext_key] = files_by_type.get(ext_key, 0) + 1
|
|
411
|
+
|
|
412
|
+
# Detect entry points
|
|
413
|
+
for pat in _entry_point_patterns:
|
|
414
|
+
if pat.search(fname) or pat.search(str(fpath)):
|
|
415
|
+
entry_points.append(str(fpath))
|
|
416
|
+
break
|
|
417
|
+
|
|
418
|
+
# Detect dependency files
|
|
419
|
+
if fname.lower() in _dep_file_names:
|
|
420
|
+
dependency_files.append(str(fpath))
|
|
421
|
+
|
|
422
|
+
# Detect config files
|
|
423
|
+
if suffix in _config_extensions or fname.lower().startswith(".env"):
|
|
424
|
+
config_files.append(str(fpath))
|
|
425
|
+
|
|
426
|
+
# Sort by count descending
|
|
427
|
+
sorted_types = sorted(files_by_type.items(), key=lambda x: x[1], reverse=True)
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
"total_files": total_files,
|
|
431
|
+
"files_by_type": dict(sorted_types),
|
|
432
|
+
"entry_points": sorted(set(entry_points)),
|
|
433
|
+
"dependency_files": sorted(set(dependency_files)),
|
|
434
|
+
"config_files": sorted(set(config_files)),
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _phase2_threat_modeling_hints(surface_map: dict, findings: list[dict]) -> dict:
|
|
439
|
+
"""Phase 2: Threat Modeling Hints -- identify components for STRIDE analysis."""
|
|
440
|
+
logger.info("Phase 2: Threat Modeling Hints")
|
|
441
|
+
|
|
442
|
+
components: list[dict] = []
|
|
443
|
+
|
|
444
|
+
# Entry points are high-value STRIDE targets
|
|
445
|
+
for ep in surface_map.get("entry_points", []):
|
|
446
|
+
components.append({
|
|
447
|
+
"component": ep,
|
|
448
|
+
"type": "entry_point",
|
|
449
|
+
"stride_focus": ["Spoofing", "Tampering", "Elevation of Privilege"],
|
|
450
|
+
"reason": "Application entry point -- critical for authentication and authorization",
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
# Dependency files = supply chain
|
|
454
|
+
for dep_file in surface_map.get("dependency_files", []):
|
|
455
|
+
components.append({
|
|
456
|
+
"component": dep_file,
|
|
457
|
+
"type": "dependency_manifest",
|
|
458
|
+
"stride_focus": ["Tampering", "Elevation of Privilege"],
|
|
459
|
+
"reason": "Dependency manifest -- supply chain attack vector",
|
|
460
|
+
})
|
|
461
|
+
|
|
462
|
+
# Config files = information disclosure
|
|
463
|
+
for cfg in surface_map.get("config_files", []):
|
|
464
|
+
components.append({
|
|
465
|
+
"component": cfg,
|
|
466
|
+
"type": "configuration",
|
|
467
|
+
"stride_focus": ["Information Disclosure", "Tampering"],
|
|
468
|
+
"reason": "Configuration file -- may contain secrets or security settings",
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
# Files with critical findings
|
|
472
|
+
critical_files: set[str] = set()
|
|
473
|
+
for f in findings:
|
|
474
|
+
if f.get("severity") in ("CRITICAL", "HIGH"):
|
|
475
|
+
critical_files.add(f.get("file", ""))
|
|
476
|
+
|
|
477
|
+
for cf in sorted(critical_files):
|
|
478
|
+
if cf:
|
|
479
|
+
components.append({
|
|
480
|
+
"component": cf,
|
|
481
|
+
"type": "high_risk_source",
|
|
482
|
+
"stride_focus": [
|
|
483
|
+
"Spoofing", "Tampering", "Repudiation",
|
|
484
|
+
"Information Disclosure", "Denial of Service",
|
|
485
|
+
"Elevation of Privilege",
|
|
486
|
+
],
|
|
487
|
+
"reason": "Source file with CRITICAL/HIGH severity findings",
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
"components_for_stride": components,
|
|
492
|
+
"total_components": len(components),
|
|
493
|
+
"recommendation": (
|
|
494
|
+
"Run a formal STRIDE analysis on each component above. "
|
|
495
|
+
"For each STRIDE category, document: attack vector, impact (1-5), "
|
|
496
|
+
"probability (1-5), and proposed mitigation."
|
|
497
|
+
),
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def _phase3_security_checklist(
|
|
502
|
+
secrets_report: dict,
|
|
503
|
+
dep_report: dict,
|
|
504
|
+
inj_report: dict,
|
|
505
|
+
quick_report: dict,
|
|
506
|
+
) -> dict:
|
|
507
|
+
"""Phase 3: Security Checklist -- compile all scanner results."""
|
|
508
|
+
logger.info("Phase 3: Security Checklist")
|
|
509
|
+
|
|
510
|
+
checklist: list[dict] = []
|
|
511
|
+
|
|
512
|
+
# Secrets check
|
|
513
|
+
secrets_count = secrets_report.get("total_findings", 0)
|
|
514
|
+
checklist.append({
|
|
515
|
+
"check": "No hardcoded secrets in source code",
|
|
516
|
+
"status": "PASS" if secrets_count == 0 else "FAIL",
|
|
517
|
+
"details": f"{secrets_count} secret(s) detected",
|
|
518
|
+
"scanner": "secrets_scanner",
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
# Dependency check
|
|
522
|
+
dep_score = dep_report.get("score", 0)
|
|
523
|
+
dep_count = dep_report.get("total_findings", 0)
|
|
524
|
+
checklist.append({
|
|
525
|
+
"check": "Dependencies are secure and pinned",
|
|
526
|
+
"status": "PASS" if dep_score >= 80 else ("WARN" if dep_score >= 50 else "FAIL"),
|
|
527
|
+
"details": f"{dep_count} finding(s), score={dep_score}",
|
|
528
|
+
"scanner": "dependency_scanner",
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
# Injection check
|
|
532
|
+
inj_count = inj_report.get("total_findings", 0)
|
|
533
|
+
inj_critical = inj_report.get("severity_counts", {}).get("CRITICAL", 0)
|
|
534
|
+
checklist.append({
|
|
535
|
+
"check": "No injection vulnerabilities",
|
|
536
|
+
"status": "PASS" if inj_count == 0 else ("FAIL" if inj_critical > 0 else "WARN"),
|
|
537
|
+
"details": f"{inj_count} finding(s), {inj_critical} CRITICAL",
|
|
538
|
+
"scanner": "injection_scanner",
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
# Quick scan check
|
|
542
|
+
quick_score = quick_report.get("score", 0)
|
|
543
|
+
quick_count = quick_report.get("total_findings", 0)
|
|
544
|
+
checklist.append({
|
|
545
|
+
"check": "No dangerous code patterns",
|
|
546
|
+
"status": "PASS" if quick_score >= 80 else ("WARN" if quick_score >= 50 else "FAIL"),
|
|
547
|
+
"details": f"{quick_count} finding(s), score={quick_score}",
|
|
548
|
+
"scanner": "quick_scan",
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
# Summary counts
|
|
552
|
+
pass_count = sum(1 for c in checklist if c["status"] == "PASS")
|
|
553
|
+
warn_count = sum(1 for c in checklist if c["status"] == "WARN")
|
|
554
|
+
fail_count = sum(1 for c in checklist if c["status"] == "FAIL")
|
|
555
|
+
|
|
556
|
+
return {
|
|
557
|
+
"checklist": checklist,
|
|
558
|
+
"summary": {
|
|
559
|
+
"pass": pass_count,
|
|
560
|
+
"warn": warn_count,
|
|
561
|
+
"fail": fail_count,
|
|
562
|
+
"total": len(checklist),
|
|
563
|
+
},
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _phase4_red_team_scenarios(all_findings: list[dict], auth_score: float) -> dict:
|
|
568
|
+
"""Phase 4: Red Team Scenarios -- generate attack scenarios from findings."""
|
|
569
|
+
logger.info("Phase 4: Red Team Scenarios")
|
|
570
|
+
|
|
571
|
+
scenarios: list[dict] = []
|
|
572
|
+
seen_types: set[str] = set()
|
|
573
|
+
|
|
574
|
+
# Generate scenarios from findings (one per unique type+file combination,
|
|
575
|
+
# capped to keep the report manageable)
|
|
576
|
+
MAX_SCENARIOS = 20
|
|
577
|
+
|
|
578
|
+
# Sort by severity so we get the most critical first
|
|
579
|
+
severity_order = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3, "INFO": 4}
|
|
580
|
+
sorted_findings = sorted(
|
|
581
|
+
all_findings,
|
|
582
|
+
key=lambda f: severity_order.get(f.get("severity", "INFO"), 5),
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
for finding in sorted_findings:
|
|
586
|
+
if len(scenarios) >= MAX_SCENARIOS:
|
|
587
|
+
break
|
|
588
|
+
|
|
589
|
+
# Determine the template key
|
|
590
|
+
finding_type = finding.get("type", "")
|
|
591
|
+
injection_type = finding.get("injection_type", "")
|
|
592
|
+
pattern = finding.get("pattern", "unknown")
|
|
593
|
+
file_path = finding.get("file", "unknown")
|
|
594
|
+
|
|
595
|
+
# Choose best template
|
|
596
|
+
if injection_type and injection_type in _RED_TEAM_TEMPLATES:
|
|
597
|
+
template_key = injection_type
|
|
598
|
+
elif finding_type in _RED_TEAM_TEMPLATES:
|
|
599
|
+
template_key = finding_type
|
|
600
|
+
else:
|
|
601
|
+
template_key = None
|
|
602
|
+
|
|
603
|
+
template = (
|
|
604
|
+
_RED_TEAM_TEMPLATES.get(template_key, _RED_TEAM_FALLBACK)
|
|
605
|
+
if template_key
|
|
606
|
+
else _RED_TEAM_FALLBACK
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
# Deduplicate: one scenario per (template_key, file) pair
|
|
610
|
+
dedup_key = f"{template_key or finding_type}:{file_path}"
|
|
611
|
+
if dedup_key in seen_types:
|
|
612
|
+
continue
|
|
613
|
+
seen_types.add(dedup_key)
|
|
614
|
+
|
|
615
|
+
# Interpolate template
|
|
616
|
+
scenario_text = template["scenario"].format(
|
|
617
|
+
pattern=pattern,
|
|
618
|
+
file=file_path,
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
scenarios.append({
|
|
622
|
+
"title": template["title"],
|
|
623
|
+
"persona": template["persona"],
|
|
624
|
+
"scenario": scenario_text,
|
|
625
|
+
"impact": template["impact"],
|
|
626
|
+
"difficulty": template["difficulty"],
|
|
627
|
+
"severity": finding.get("severity", "MEDIUM"),
|
|
628
|
+
"source_finding": {
|
|
629
|
+
"type": finding_type,
|
|
630
|
+
"pattern": pattern,
|
|
631
|
+
"file": file_path,
|
|
632
|
+
"line": finding.get("line", 0),
|
|
633
|
+
},
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
# Add no-auth scenario if auth score is low
|
|
637
|
+
if auth_score < 40 and "no_auth" not in seen_types:
|
|
638
|
+
template = _RED_TEAM_TEMPLATES["no_auth"]
|
|
639
|
+
scenarios.append({
|
|
640
|
+
"title": template["title"],
|
|
641
|
+
"persona": template["persona"],
|
|
642
|
+
"scenario": template["scenario"],
|
|
643
|
+
"impact": template["impact"],
|
|
644
|
+
"difficulty": template["difficulty"],
|
|
645
|
+
"severity": "HIGH",
|
|
646
|
+
"source_finding": {
|
|
647
|
+
"type": "architectural",
|
|
648
|
+
"pattern": "missing_auth",
|
|
649
|
+
"file": "(project-wide)",
|
|
650
|
+
"line": 0,
|
|
651
|
+
},
|
|
652
|
+
})
|
|
653
|
+
|
|
654
|
+
return {
|
|
655
|
+
"scenarios": scenarios,
|
|
656
|
+
"total_scenarios": len(scenarios),
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
def _phase5_blue_team_recommendations(all_findings: list[dict], auth_score: float) -> dict:
|
|
661
|
+
"""Phase 5: Blue Team Recommendations -- hardening advice per finding type."""
|
|
662
|
+
logger.info("Phase 5: Blue Team Recommendations")
|
|
663
|
+
|
|
664
|
+
recommendations: list[dict] = []
|
|
665
|
+
seen_types: set[str] = set()
|
|
666
|
+
|
|
667
|
+
# Group findings by type for consolidated recommendations
|
|
668
|
+
for finding in all_findings:
|
|
669
|
+
finding_type = finding.get("type", "")
|
|
670
|
+
injection_type = finding.get("injection_type", "")
|
|
671
|
+
|
|
672
|
+
# Choose best template key
|
|
673
|
+
if injection_type and injection_type in _BLUE_TEAM_TEMPLATES:
|
|
674
|
+
rec_key = injection_type
|
|
675
|
+
elif finding_type in _BLUE_TEAM_TEMPLATES:
|
|
676
|
+
rec_key = finding_type
|
|
677
|
+
else:
|
|
678
|
+
rec_key = None
|
|
679
|
+
|
|
680
|
+
if rec_key and rec_key not in seen_types:
|
|
681
|
+
seen_types.add(rec_key)
|
|
682
|
+
template = _BLUE_TEAM_TEMPLATES[rec_key]
|
|
683
|
+
|
|
684
|
+
# Count affected findings
|
|
685
|
+
affected = [
|
|
686
|
+
f for f in all_findings
|
|
687
|
+
if f.get("injection_type", "") == rec_key
|
|
688
|
+
or f.get("type", "") == rec_key
|
|
689
|
+
]
|
|
690
|
+
|
|
691
|
+
recommendations.append({
|
|
692
|
+
"category": rec_key,
|
|
693
|
+
"recommendation": template["recommendation"],
|
|
694
|
+
"priority": template["priority"],
|
|
695
|
+
"effort": template["effort"],
|
|
696
|
+
"affected_findings": len(affected),
|
|
697
|
+
"example_files": sorted(set(
|
|
698
|
+
f.get("file", "") for f in affected[:5]
|
|
699
|
+
)),
|
|
700
|
+
})
|
|
701
|
+
|
|
702
|
+
# Add no-auth recommendation if applicable
|
|
703
|
+
if auth_score < 40 and "no_auth" not in seen_types:
|
|
704
|
+
template = _BLUE_TEAM_TEMPLATES["no_auth"]
|
|
705
|
+
recommendations.append({
|
|
706
|
+
"category": "no_auth",
|
|
707
|
+
"recommendation": template["recommendation"],
|
|
708
|
+
"priority": template["priority"],
|
|
709
|
+
"effort": template["effort"],
|
|
710
|
+
"affected_findings": 0,
|
|
711
|
+
"example_files": [],
|
|
712
|
+
})
|
|
713
|
+
|
|
714
|
+
# Sort by priority (CRITICAL first)
|
|
715
|
+
priority_order = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}
|
|
716
|
+
recommendations.sort(key=lambda r: priority_order.get(r["priority"], 5))
|
|
717
|
+
|
|
718
|
+
return {
|
|
719
|
+
"recommendations": recommendations,
|
|
720
|
+
"total_recommendations": len(recommendations),
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def _phase6_verdict(
|
|
725
|
+
target_str: str,
|
|
726
|
+
all_findings: list[dict],
|
|
727
|
+
source_files: list[Path],
|
|
728
|
+
total_source_files: int,
|
|
729
|
+
secrets_report: dict,
|
|
730
|
+
dep_report: dict,
|
|
731
|
+
inj_report: dict,
|
|
732
|
+
quick_report: dict,
|
|
733
|
+
) -> dict:
|
|
734
|
+
"""Phase 6: Verdict -- compute score and emit final verdict."""
|
|
735
|
+
logger.info("Phase 6: Verdict")
|
|
736
|
+
|
|
737
|
+
domain_scores = score_calculator.compute_domain_scores(
|
|
738
|
+
secrets_findings=secrets_report.get("findings", []),
|
|
739
|
+
injection_findings=inj_report.get("findings", []),
|
|
740
|
+
dependency_report=dep_report,
|
|
741
|
+
quick_findings=quick_report.get("findings", []),
|
|
742
|
+
source_files=source_files,
|
|
743
|
+
total_source_files=total_source_files,
|
|
744
|
+
)
|
|
745
|
+
|
|
746
|
+
final_score = calculate_weighted_score(domain_scores)
|
|
747
|
+
verdict = get_verdict(final_score)
|
|
748
|
+
|
|
749
|
+
return {
|
|
750
|
+
"domain_scores": domain_scores,
|
|
751
|
+
"final_score": final_score,
|
|
752
|
+
"verdict": {
|
|
753
|
+
"label": verdict["label"],
|
|
754
|
+
"description": verdict["description"],
|
|
755
|
+
"emoji": verdict["emoji"],
|
|
756
|
+
},
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
# =========================================================================
|
|
761
|
+
# REPORT GENERATION
|
|
762
|
+
# =========================================================================
|
|
763
|
+
|
|
764
|
+
def _generate_markdown_report(
|
|
765
|
+
target: str,
|
|
766
|
+
phases: dict,
|
|
767
|
+
elapsed: float,
|
|
768
|
+
phases_run: list[int],
|
|
769
|
+
) -> str:
|
|
770
|
+
"""Generate a comprehensive Markdown audit report."""
|
|
771
|
+
lines: list[str] = []
|
|
772
|
+
ts = get_timestamp()
|
|
773
|
+
|
|
774
|
+
lines.append("# 007 -- Full Security Audit Report")
|
|
775
|
+
lines.append("")
|
|
776
|
+
lines.append(f"**Target:** `{target}`")
|
|
777
|
+
lines.append(f"**Timestamp:** {ts}")
|
|
778
|
+
lines.append(f"**Duration:** {elapsed:.2f}s")
|
|
779
|
+
lines.append(f"**Phases executed:** {', '.join(str(p) for p in phases_run)}")
|
|
780
|
+
lines.append("")
|
|
781
|
+
lines.append("---")
|
|
782
|
+
lines.append("")
|
|
783
|
+
|
|
784
|
+
# Phase 1: Surface Mapping
|
|
785
|
+
if 1 in phases_run and "phase1" in phases:
|
|
786
|
+
p1 = phases["phase1"]
|
|
787
|
+
lines.append("## Phase 1: Surface Mapping")
|
|
788
|
+
lines.append("")
|
|
789
|
+
lines.append(f"**Total files:** {p1.get('total_files', 0)}")
|
|
790
|
+
lines.append("")
|
|
791
|
+
|
|
792
|
+
# Files by type
|
|
793
|
+
fbt = p1.get("files_by_type", {})
|
|
794
|
+
if fbt:
|
|
795
|
+
lines.append("### File Types")
|
|
796
|
+
lines.append("")
|
|
797
|
+
lines.append("| Extension | Count |")
|
|
798
|
+
lines.append("|-----------|-------|")
|
|
799
|
+
for ext, count in list(fbt.items())[:20]:
|
|
800
|
+
lines.append(f"| `{ext}` | {count} |")
|
|
801
|
+
lines.append("")
|
|
802
|
+
|
|
803
|
+
# Entry points
|
|
804
|
+
eps = p1.get("entry_points", [])
|
|
805
|
+
if eps:
|
|
806
|
+
lines.append("### Entry Points")
|
|
807
|
+
lines.append("")
|
|
808
|
+
for ep in eps:
|
|
809
|
+
lines.append(f"- `{ep}`")
|
|
810
|
+
lines.append("")
|
|
811
|
+
|
|
812
|
+
# Dependency files
|
|
813
|
+
dfs = p1.get("dependency_files", [])
|
|
814
|
+
if dfs:
|
|
815
|
+
lines.append("### Dependency Files")
|
|
816
|
+
lines.append("")
|
|
817
|
+
for df in dfs:
|
|
818
|
+
lines.append(f"- `{df}`")
|
|
819
|
+
lines.append("")
|
|
820
|
+
|
|
821
|
+
lines.append("---")
|
|
822
|
+
lines.append("")
|
|
823
|
+
|
|
824
|
+
# Phase 2: Threat Modeling Hints
|
|
825
|
+
if 2 in phases_run and "phase2" in phases:
|
|
826
|
+
p2 = phases["phase2"]
|
|
827
|
+
lines.append("## Phase 2: Threat Modeling Hints")
|
|
828
|
+
lines.append("")
|
|
829
|
+
lines.append(f"**Components identified for STRIDE analysis:** {p2.get('total_components', 0)}")
|
|
830
|
+
lines.append("")
|
|
831
|
+
|
|
832
|
+
for comp in p2.get("components_for_stride", [])[:30]:
|
|
833
|
+
lines.append(f"- **`{comp['component']}`** ({comp['type']})")
|
|
834
|
+
lines.append(f" - STRIDE focus: {', '.join(comp['stride_focus'])}")
|
|
835
|
+
lines.append(f" - Reason: {comp['reason']}")
|
|
836
|
+
lines.append("")
|
|
837
|
+
lines.append(f"> {p2.get('recommendation', '')}")
|
|
838
|
+
lines.append("")
|
|
839
|
+
lines.append("---")
|
|
840
|
+
lines.append("")
|
|
841
|
+
|
|
842
|
+
# Phase 3: Security Checklist
|
|
843
|
+
if 3 in phases_run and "phase3" in phases:
|
|
844
|
+
p3 = phases["phase3"]
|
|
845
|
+
summary = p3.get("summary", {})
|
|
846
|
+
lines.append("## Phase 3: Security Checklist")
|
|
847
|
+
lines.append("")
|
|
848
|
+
lines.append(
|
|
849
|
+
f"**Results:** {summary.get('pass', 0)} PASS / "
|
|
850
|
+
f"{summary.get('warn', 0)} WARN / "
|
|
851
|
+
f"{summary.get('fail', 0)} FAIL"
|
|
852
|
+
)
|
|
853
|
+
lines.append("")
|
|
854
|
+
lines.append("| Check | Status | Details | Scanner |")
|
|
855
|
+
lines.append("|-------|--------|---------|---------|")
|
|
856
|
+
for item in p3.get("checklist", []):
|
|
857
|
+
status_icon = {"PASS": "[PASS]", "WARN": "[WARN]", "FAIL": "[FAIL]"}.get(
|
|
858
|
+
item["status"], item["status"]
|
|
859
|
+
)
|
|
860
|
+
lines.append(
|
|
861
|
+
f"| {item['check']} | {status_icon} | {item['details']} | {item['scanner']} |"
|
|
862
|
+
)
|
|
863
|
+
lines.append("")
|
|
864
|
+
lines.append("---")
|
|
865
|
+
lines.append("")
|
|
866
|
+
|
|
867
|
+
# Phase 4: Red Team Scenarios
|
|
868
|
+
if 4 in phases_run and "phase4" in phases:
|
|
869
|
+
p4 = phases["phase4"]
|
|
870
|
+
lines.append("## Phase 4: Red Team Scenarios")
|
|
871
|
+
lines.append("")
|
|
872
|
+
lines.append(f"**Total scenarios:** {p4.get('total_scenarios', 0)}")
|
|
873
|
+
lines.append("")
|
|
874
|
+
|
|
875
|
+
for i, sc in enumerate(p4.get("scenarios", []), start=1):
|
|
876
|
+
lines.append(f"### Scenario {i}: {sc['title']}")
|
|
877
|
+
lines.append("")
|
|
878
|
+
lines.append(f"- **Persona:** {sc['persona']}")
|
|
879
|
+
lines.append(f"- **Severity:** {sc['severity']}")
|
|
880
|
+
lines.append(f"- **Difficulty:** {sc['difficulty']}")
|
|
881
|
+
lines.append(f"- **Impact:** {sc['impact']}")
|
|
882
|
+
lines.append(f"- **Description:** {sc['scenario']}")
|
|
883
|
+
src = sc.get("source_finding", {})
|
|
884
|
+
if src.get("file"):
|
|
885
|
+
lines.append(f"- **Source:** `{src['file']}`:L{src.get('line', 0)} ({src.get('pattern', '')})")
|
|
886
|
+
lines.append("")
|
|
887
|
+
|
|
888
|
+
lines.append("---")
|
|
889
|
+
lines.append("")
|
|
890
|
+
|
|
891
|
+
# Phase 5: Blue Team Recommendations
|
|
892
|
+
if 5 in phases_run and "phase5" in phases:
|
|
893
|
+
p5 = phases["phase5"]
|
|
894
|
+
lines.append("## Phase 5: Blue Team Recommendations")
|
|
895
|
+
lines.append("")
|
|
896
|
+
lines.append(f"**Total recommendations:** {p5.get('total_recommendations', 0)}")
|
|
897
|
+
lines.append("")
|
|
898
|
+
|
|
899
|
+
for rec in p5.get("recommendations", []):
|
|
900
|
+
lines.append(f"### [{rec['priority']}] {rec['category'].replace('_', ' ').title()}")
|
|
901
|
+
lines.append("")
|
|
902
|
+
lines.append(f"**Affected findings:** {rec['affected_findings']}")
|
|
903
|
+
lines.append(f"**Effort:** {rec['effort']}")
|
|
904
|
+
lines.append("")
|
|
905
|
+
lines.append(f"{rec['recommendation']}")
|
|
906
|
+
lines.append("")
|
|
907
|
+
if rec.get("example_files"):
|
|
908
|
+
lines.append("**Example files:**")
|
|
909
|
+
for ef in rec["example_files"]:
|
|
910
|
+
if ef:
|
|
911
|
+
lines.append(f"- `{ef}`")
|
|
912
|
+
lines.append("")
|
|
913
|
+
|
|
914
|
+
lines.append("---")
|
|
915
|
+
lines.append("")
|
|
916
|
+
|
|
917
|
+
# Phase 6: Verdict
|
|
918
|
+
if 6 in phases_run and "phase6" in phases:
|
|
919
|
+
p6 = phases["phase6"]
|
|
920
|
+
domain_scores = p6.get("domain_scores", {})
|
|
921
|
+
final_score = p6.get("final_score", 0)
|
|
922
|
+
verdict = p6.get("verdict", {})
|
|
923
|
+
|
|
924
|
+
lines.append("## Phase 6: Verdict")
|
|
925
|
+
lines.append("")
|
|
926
|
+
lines.append("### Domain Scores")
|
|
927
|
+
lines.append("")
|
|
928
|
+
lines.append("| Domain | Weight | Score |")
|
|
929
|
+
lines.append("|--------|--------|-------|")
|
|
930
|
+
for domain, weight in SCORING_WEIGHTS.items():
|
|
931
|
+
score = domain_scores.get(domain, 0.0)
|
|
932
|
+
label = SCORING_LABELS.get(domain, domain)
|
|
933
|
+
lines.append(f"| {label} | {weight * 100:.0f}% | {score:.1f} |")
|
|
934
|
+
lines.append("")
|
|
935
|
+
|
|
936
|
+
lines.append(f"### Final Score: **{final_score:.1f} / 100**")
|
|
937
|
+
lines.append("")
|
|
938
|
+
lines.append(
|
|
939
|
+
f"### Verdict: **{verdict.get('emoji', '')} {verdict.get('label', 'N/A')}**"
|
|
940
|
+
)
|
|
941
|
+
lines.append("")
|
|
942
|
+
lines.append(f"> {verdict.get('description', '')}")
|
|
943
|
+
lines.append("")
|
|
944
|
+
|
|
945
|
+
lines.append("---")
|
|
946
|
+
lines.append("")
|
|
947
|
+
lines.append("*Generated by 007 -- Licenca para Auditar*")
|
|
948
|
+
lines.append("")
|
|
949
|
+
|
|
950
|
+
return "\n".join(lines)
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
def _generate_text_summary(
|
|
954
|
+
target: str,
|
|
955
|
+
phases: dict,
|
|
956
|
+
elapsed: float,
|
|
957
|
+
phases_run: list[int],
|
|
958
|
+
) -> str:
|
|
959
|
+
"""Generate a concise text summary for stdout."""
|
|
960
|
+
lines: list[str] = []
|
|
961
|
+
|
|
962
|
+
lines.append("=" * 72)
|
|
963
|
+
lines.append(" 007 FULL SECURITY AUDIT -- SUMMARY")
|
|
964
|
+
lines.append("=" * 72)
|
|
965
|
+
lines.append("")
|
|
966
|
+
lines.append(f" Target: {target}")
|
|
967
|
+
lines.append(f" Timestamp: {get_timestamp()}")
|
|
968
|
+
lines.append(f" Duration: {elapsed:.2f}s")
|
|
969
|
+
lines.append(f" Phases: {', '.join(str(p) for p in phases_run)}")
|
|
970
|
+
lines.append("")
|
|
971
|
+
|
|
972
|
+
# Phase 1 summary
|
|
973
|
+
if "phase1" in phases:
|
|
974
|
+
p1 = phases["phase1"]
|
|
975
|
+
lines.append(f" Phase 1 -- Surface: {p1.get('total_files', 0)} files, "
|
|
976
|
+
f"{len(p1.get('entry_points', []))} entry points, "
|
|
977
|
+
f"{len(p1.get('dependency_files', []))} dep files")
|
|
978
|
+
|
|
979
|
+
# Phase 2 summary
|
|
980
|
+
if "phase2" in phases:
|
|
981
|
+
p2 = phases["phase2"]
|
|
982
|
+
lines.append(f" Phase 2 -- Threat Model Hints: "
|
|
983
|
+
f"{p2.get('total_components', 0)} components for STRIDE")
|
|
984
|
+
|
|
985
|
+
# Phase 3 summary
|
|
986
|
+
if "phase3" in phases:
|
|
987
|
+
p3 = phases["phase3"]
|
|
988
|
+
summary = p3.get("summary", {})
|
|
989
|
+
lines.append(
|
|
990
|
+
f" Phase 3 -- Checklist: "
|
|
991
|
+
f"{summary.get('pass', 0)} PASS / "
|
|
992
|
+
f"{summary.get('warn', 0)} WARN / "
|
|
993
|
+
f"{summary.get('fail', 0)} FAIL"
|
|
994
|
+
)
|
|
995
|
+
|
|
996
|
+
# Phase 4 summary
|
|
997
|
+
if "phase4" in phases:
|
|
998
|
+
p4 = phases["phase4"]
|
|
999
|
+
lines.append(f" Phase 4 -- Red Team: {p4.get('total_scenarios', 0)} attack scenarios")
|
|
1000
|
+
|
|
1001
|
+
# Phase 5 summary
|
|
1002
|
+
if "phase5" in phases:
|
|
1003
|
+
p5 = phases["phase5"]
|
|
1004
|
+
lines.append(f" Phase 5 -- Blue Team: {p5.get('total_recommendations', 0)} recommendations")
|
|
1005
|
+
|
|
1006
|
+
# Phase 6 verdict
|
|
1007
|
+
if "phase6" in phases:
|
|
1008
|
+
p6 = phases["phase6"]
|
|
1009
|
+
final_score = p6.get("final_score", 0)
|
|
1010
|
+
verdict = p6.get("verdict", {})
|
|
1011
|
+
lines.append("")
|
|
1012
|
+
lines.append("-" * 72)
|
|
1013
|
+
lines.append(f" FINAL SCORE: {final_score:.1f} / 100")
|
|
1014
|
+
lines.append(f" VERDICT: {verdict.get('emoji', '')} {verdict.get('label', 'N/A')}")
|
|
1015
|
+
lines.append(f" {verdict.get('description', '')}")
|
|
1016
|
+
|
|
1017
|
+
lines.append("=" * 72)
|
|
1018
|
+
lines.append("")
|
|
1019
|
+
|
|
1020
|
+
return "\n".join(lines)
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
# =========================================================================
|
|
1024
|
+
# MAIN ENTRY POINT
|
|
1025
|
+
# =========================================================================
|
|
1026
|
+
|
|
1027
|
+
def run_audit(
|
|
1028
|
+
target_path: str,
|
|
1029
|
+
output_format: str = "text",
|
|
1030
|
+
phases_to_run: str = "all",
|
|
1031
|
+
verbose: bool = False,
|
|
1032
|
+
) -> dict:
|
|
1033
|
+
"""Execute the full 6-phase security audit.
|
|
1034
|
+
|
|
1035
|
+
Args:
|
|
1036
|
+
target_path: Path to the directory to audit.
|
|
1037
|
+
output_format: 'text', 'json', or 'markdown'.
|
|
1038
|
+
phases_to_run: 'all' or a comma-separated list of phase numbers (e.g. '1,3,6').
|
|
1039
|
+
verbose: Enable debug-level logging.
|
|
1040
|
+
|
|
1041
|
+
Returns:
|
|
1042
|
+
JSON-compatible audit report dict.
|
|
1043
|
+
"""
|
|
1044
|
+
if verbose:
|
|
1045
|
+
logger.setLevel("DEBUG")
|
|
1046
|
+
|
|
1047
|
+
ensure_directories()
|
|
1048
|
+
|
|
1049
|
+
target = Path(target_path).resolve()
|
|
1050
|
+
if not target.exists():
|
|
1051
|
+
logger.error("Target path does not exist: %s", target)
|
|
1052
|
+
sys.exit(1)
|
|
1053
|
+
if not target.is_dir():
|
|
1054
|
+
logger.error("Target is not a directory: %s", target)
|
|
1055
|
+
sys.exit(1)
|
|
1056
|
+
|
|
1057
|
+
# Parse phases
|
|
1058
|
+
if phases_to_run == "all":
|
|
1059
|
+
phases_list = [1, 2, 3, 4, 5, 6]
|
|
1060
|
+
else:
|
|
1061
|
+
try:
|
|
1062
|
+
phases_list = sorted(set(int(p.strip()) for p in phases_to_run.split(",")))
|
|
1063
|
+
if not all(1 <= p <= 6 for p in phases_list):
|
|
1064
|
+
logger.error("Phase numbers must be between 1 and 6.")
|
|
1065
|
+
sys.exit(1)
|
|
1066
|
+
except ValueError:
|
|
1067
|
+
logger.error("Invalid --phase value. Use 'all' or comma-separated numbers (1-6).")
|
|
1068
|
+
sys.exit(1)
|
|
1069
|
+
|
|
1070
|
+
logger.info("Starting full audit of %s (phases: %s)", target, phases_list)
|
|
1071
|
+
start_time = time.time()
|
|
1072
|
+
target_str = str(target)
|
|
1073
|
+
|
|
1074
|
+
# ------------------------------------------------------------------
|
|
1075
|
+
# Run scanners if needed (phases 3-6 need scanner data)
|
|
1076
|
+
# ------------------------------------------------------------------
|
|
1077
|
+
need_scanners = any(p in phases_list for p in [3, 4, 5, 6])
|
|
1078
|
+
|
|
1079
|
+
secrets_report: dict = {"findings": [], "score": 100, "total_findings": 0}
|
|
1080
|
+
dep_report: dict = {"findings": [], "score": 100, "total_findings": 0}
|
|
1081
|
+
inj_report: dict = {"findings": [], "score": 100, "total_findings": 0}
|
|
1082
|
+
quick_report: dict = {"findings": [], "score": 100, "total_findings": 0}
|
|
1083
|
+
all_findings: list[dict] = []
|
|
1084
|
+
|
|
1085
|
+
if need_scanners:
|
|
1086
|
+
logger.info("Running scanners for phases %s...", [p for p in phases_list if p >= 3])
|
|
1087
|
+
|
|
1088
|
+
try:
|
|
1089
|
+
secrets_report = secrets_scanner.run_scan(
|
|
1090
|
+
target_path=target_str, output_format="json", verbose=verbose,
|
|
1091
|
+
)
|
|
1092
|
+
except SystemExit:
|
|
1093
|
+
pass
|
|
1094
|
+
|
|
1095
|
+
try:
|
|
1096
|
+
dep_report = dependency_scanner.run_scan(
|
|
1097
|
+
target_path=target_str, output_format="json", verbose=verbose,
|
|
1098
|
+
)
|
|
1099
|
+
except SystemExit:
|
|
1100
|
+
pass
|
|
1101
|
+
|
|
1102
|
+
try:
|
|
1103
|
+
inj_report = injection_scanner.run_scan(
|
|
1104
|
+
target_path=target_str, output_format="json", verbose=verbose,
|
|
1105
|
+
)
|
|
1106
|
+
except SystemExit:
|
|
1107
|
+
pass
|
|
1108
|
+
|
|
1109
|
+
try:
|
|
1110
|
+
quick_report = quick_scan.run_scan(
|
|
1111
|
+
target_path=target_str, output_format="json", verbose=verbose,
|
|
1112
|
+
)
|
|
1113
|
+
except SystemExit:
|
|
1114
|
+
pass
|
|
1115
|
+
|
|
1116
|
+
# Aggregate and deduplicate
|
|
1117
|
+
raw = (
|
|
1118
|
+
secrets_report.get("findings", [])
|
|
1119
|
+
+ dep_report.get("findings", [])
|
|
1120
|
+
+ inj_report.get("findings", [])
|
|
1121
|
+
+ quick_report.get("findings", [])
|
|
1122
|
+
)
|
|
1123
|
+
all_findings = score_calculator._deduplicate_findings(raw)
|
|
1124
|
+
|
|
1125
|
+
# ------------------------------------------------------------------
|
|
1126
|
+
# Collect source files if needed for phase 6
|
|
1127
|
+
# ------------------------------------------------------------------
|
|
1128
|
+
source_files: list[Path] = []
|
|
1129
|
+
total_source_files = 0
|
|
1130
|
+
if 6 in phases_list:
|
|
1131
|
+
source_files = score_calculator._collect_source_files(target)
|
|
1132
|
+
total_source_files = len(source_files)
|
|
1133
|
+
|
|
1134
|
+
# ------------------------------------------------------------------
|
|
1135
|
+
# Execute phases
|
|
1136
|
+
# ------------------------------------------------------------------
|
|
1137
|
+
phases_data: dict = {}
|
|
1138
|
+
|
|
1139
|
+
if 1 in phases_list:
|
|
1140
|
+
phases_data["phase1"] = _phase1_surface_mapping(target, verbose=verbose)
|
|
1141
|
+
|
|
1142
|
+
if 2 in phases_list:
|
|
1143
|
+
# Phase 2 benefits from phase 1 data and findings
|
|
1144
|
+
surface = phases_data.get("phase1") or _phase1_surface_mapping(target, verbose=verbose)
|
|
1145
|
+
phases_data["phase2"] = _phase2_threat_modeling_hints(surface, all_findings)
|
|
1146
|
+
|
|
1147
|
+
if 3 in phases_list:
|
|
1148
|
+
phases_data["phase3"] = _phase3_security_checklist(
|
|
1149
|
+
secrets_report, dep_report, inj_report, quick_report,
|
|
1150
|
+
)
|
|
1151
|
+
|
|
1152
|
+
# Auth score for phases 4 and 5
|
|
1153
|
+
auth_score = 50.0
|
|
1154
|
+
if 6 in phases_list or 4 in phases_list or 5 in phases_list:
|
|
1155
|
+
if source_files:
|
|
1156
|
+
auth_count = score_calculator._count_pattern_matches(
|
|
1157
|
+
source_files, score_calculator._AUTH_PATTERNS,
|
|
1158
|
+
)
|
|
1159
|
+
if auth_count == 0:
|
|
1160
|
+
auth_score = 25.0
|
|
1161
|
+
else:
|
|
1162
|
+
auth_score = score_calculator._score_from_positive_signals(
|
|
1163
|
+
auth_count, total_source_files, base_score=40, max_score=95,
|
|
1164
|
+
)
|
|
1165
|
+
|
|
1166
|
+
if 4 in phases_list:
|
|
1167
|
+
phases_data["phase4"] = _phase4_red_team_scenarios(all_findings, auth_score)
|
|
1168
|
+
|
|
1169
|
+
if 5 in phases_list:
|
|
1170
|
+
phases_data["phase5"] = _phase5_blue_team_recommendations(all_findings, auth_score)
|
|
1171
|
+
|
|
1172
|
+
if 6 in phases_list:
|
|
1173
|
+
phases_data["phase6"] = _phase6_verdict(
|
|
1174
|
+
target_str=target_str,
|
|
1175
|
+
all_findings=all_findings,
|
|
1176
|
+
source_files=source_files,
|
|
1177
|
+
total_source_files=total_source_files,
|
|
1178
|
+
secrets_report=secrets_report,
|
|
1179
|
+
dep_report=dep_report,
|
|
1180
|
+
inj_report=inj_report,
|
|
1181
|
+
quick_report=quick_report,
|
|
1182
|
+
)
|
|
1183
|
+
|
|
1184
|
+
elapsed = time.time() - start_time
|
|
1185
|
+
|
|
1186
|
+
# ------------------------------------------------------------------
|
|
1187
|
+
# Generate and save Markdown report
|
|
1188
|
+
# ------------------------------------------------------------------
|
|
1189
|
+
md_report = _generate_markdown_report(target_str, phases_data, elapsed, phases_list)
|
|
1190
|
+
|
|
1191
|
+
ts_file = datetime.now(timezone.utc).strftime("%Y-%m-%d_%H-%M-%S")
|
|
1192
|
+
report_filename = f"audit_{ts_file}.md"
|
|
1193
|
+
report_path = REPORTS_DIR / report_filename
|
|
1194
|
+
|
|
1195
|
+
try:
|
|
1196
|
+
report_path.write_text(md_report, encoding="utf-8")
|
|
1197
|
+
logger.info("Markdown report saved to %s", report_path)
|
|
1198
|
+
except OSError as exc:
|
|
1199
|
+
logger.warning("Could not save report: %s", exc)
|
|
1200
|
+
|
|
1201
|
+
# ------------------------------------------------------------------
|
|
1202
|
+
# Audit log
|
|
1203
|
+
# ------------------------------------------------------------------
|
|
1204
|
+
verdict_data = phases_data.get("phase6", {}).get("verdict", {})
|
|
1205
|
+
final_score = phases_data.get("phase6", {}).get("final_score", "N/A")
|
|
1206
|
+
|
|
1207
|
+
log_audit_event(
|
|
1208
|
+
action="full_audit",
|
|
1209
|
+
target=target_str,
|
|
1210
|
+
result=f"score={final_score}, verdict={verdict_data.get('label', 'N/A')}",
|
|
1211
|
+
details={
|
|
1212
|
+
"phases_run": phases_list,
|
|
1213
|
+
"total_findings": len(all_findings),
|
|
1214
|
+
"report_path": str(report_path),
|
|
1215
|
+
"duration_seconds": round(elapsed, 3),
|
|
1216
|
+
},
|
|
1217
|
+
)
|
|
1218
|
+
|
|
1219
|
+
# ------------------------------------------------------------------
|
|
1220
|
+
# Build final report dict
|
|
1221
|
+
# ------------------------------------------------------------------
|
|
1222
|
+
full_report = {
|
|
1223
|
+
"report": "full_audit",
|
|
1224
|
+
"target": target_str,
|
|
1225
|
+
"timestamp": get_timestamp(),
|
|
1226
|
+
"duration_seconds": round(elapsed, 3),
|
|
1227
|
+
"phases_run": phases_list,
|
|
1228
|
+
"phases": phases_data,
|
|
1229
|
+
"total_findings": len(all_findings),
|
|
1230
|
+
"findings": all_findings,
|
|
1231
|
+
"report_path": str(report_path),
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
# ------------------------------------------------------------------
|
|
1235
|
+
# Output
|
|
1236
|
+
# ------------------------------------------------------------------
|
|
1237
|
+
if output_format == "json":
|
|
1238
|
+
print(json.dumps(full_report, indent=2, ensure_ascii=False))
|
|
1239
|
+
elif output_format == "markdown":
|
|
1240
|
+
print(md_report)
|
|
1241
|
+
else:
|
|
1242
|
+
print(_generate_text_summary(target_str, phases_data, elapsed, phases_list))
|
|
1243
|
+
print(f" Full report saved to: {report_path}")
|
|
1244
|
+
print("")
|
|
1245
|
+
|
|
1246
|
+
return full_report
|
|
1247
|
+
|
|
1248
|
+
|
|
1249
|
+
# =========================================================================
|
|
1250
|
+
# CLI
|
|
1251
|
+
# =========================================================================
|
|
1252
|
+
|
|
1253
|
+
if __name__ == "__main__":
|
|
1254
|
+
parser = argparse.ArgumentParser(
|
|
1255
|
+
description=(
|
|
1256
|
+
"007 Full Audit -- Comprehensive 6-phase security audit.\n\n"
|
|
1257
|
+
"Phases:\n"
|
|
1258
|
+
" 1: Surface Mapping -- file inventory, entry points, deps\n"
|
|
1259
|
+
" 2: Threat Modeling Hints -- STRIDE analysis targets\n"
|
|
1260
|
+
" 3: Security Checklist -- run all scanners\n"
|
|
1261
|
+
" 4: Red Team Scenarios -- attack scenario generation\n"
|
|
1262
|
+
" 5: Blue Team Recs -- hardening recommendations\n"
|
|
1263
|
+
" 6: Verdict -- scoring and final verdict"
|
|
1264
|
+
),
|
|
1265
|
+
epilog=(
|
|
1266
|
+
"Examples:\n"
|
|
1267
|
+
" python full_audit.py --target ./my-project\n"
|
|
1268
|
+
" python full_audit.py --target ./my-project --output markdown\n"
|
|
1269
|
+
" python full_audit.py --target ./my-project --phase 1,3,6\n"
|
|
1270
|
+
" python full_audit.py --target ./my-project --output json --verbose"
|
|
1271
|
+
),
|
|
1272
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
1273
|
+
)
|
|
1274
|
+
parser.add_argument(
|
|
1275
|
+
"--target",
|
|
1276
|
+
required=True,
|
|
1277
|
+
help="Path to the directory to audit (required).",
|
|
1278
|
+
)
|
|
1279
|
+
parser.add_argument(
|
|
1280
|
+
"--output",
|
|
1281
|
+
choices=["text", "json", "markdown"],
|
|
1282
|
+
default="text",
|
|
1283
|
+
help="Output format: 'text' (default), 'json', or 'markdown'.",
|
|
1284
|
+
)
|
|
1285
|
+
parser.add_argument(
|
|
1286
|
+
"--phase",
|
|
1287
|
+
default="all",
|
|
1288
|
+
help=(
|
|
1289
|
+
"Which phases to run: 'all' (default) or comma-separated numbers "
|
|
1290
|
+
"(e.g. '1,3,6'). Range: 1-6."
|
|
1291
|
+
),
|
|
1292
|
+
)
|
|
1293
|
+
parser.add_argument(
|
|
1294
|
+
"--verbose",
|
|
1295
|
+
action="store_true",
|
|
1296
|
+
default=False,
|
|
1297
|
+
help="Enable verbose/debug logging.",
|
|
1298
|
+
)
|
|
1299
|
+
|
|
1300
|
+
args = parser.parse_args()
|
|
1301
|
+
run_audit(
|
|
1302
|
+
target_path=args.target,
|
|
1303
|
+
output_format=args.output,
|
|
1304
|
+
phases_to_run=args.phase,
|
|
1305
|
+
verbose=args.verbose,
|
|
1306
|
+
)
|