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.
Files changed (368) hide show
  1. package/package.json +1 -1
  2. package/templates/.agents/agents/backend-specialist.md +263 -0
  3. package/templates/.agents/agents/code-archaeologist.md +106 -0
  4. package/templates/.agents/agents/database-architect.md +226 -0
  5. package/templates/.agents/agents/debugger.md +225 -0
  6. package/templates/.agents/agents/devops-engineer.md +242 -0
  7. package/templates/.agents/agents/documentation-writer.md +104 -0
  8. package/templates/.agents/agents/explorer-agent.md +73 -0
  9. package/templates/.agents/agents/frontend-specialist.md +593 -0
  10. package/templates/.agents/agents/game-developer.md +162 -0
  11. package/templates/.agents/agents/mobile-developer.md +377 -0
  12. package/templates/.agents/agents/orchestrator.md +416 -0
  13. package/templates/.agents/agents/penetration-tester.md +188 -0
  14. package/templates/.agents/agents/performance-optimizer.md +187 -0
  15. package/templates/.agents/agents/product-manager.md +112 -0
  16. package/templates/.agents/agents/product-owner.md +95 -0
  17. package/templates/.agents/agents/project-planner.md +406 -0
  18. package/templates/.agents/agents/qa-automation-engineer.md +103 -0
  19. package/templates/.agents/agents/security-auditor.md +170 -0
  20. package/templates/.agents/agents/seo-specialist.md +111 -0
  21. package/templates/.agents/agents/test-engineer.md +158 -0
  22. package/templates/.agents/rules/GEMINI.md +219 -0
  23. package/templates/.agents/scripts/auto_preview.py +148 -0
  24. package/templates/.agents/scripts/checklist.py +217 -0
  25. package/templates/.agents/scripts/session_manager.py +120 -0
  26. package/templates/.agents/scripts/verify_all.py +327 -0
  27. package/templates/.agents/workflows/brainstorm.md +113 -0
  28. package/templates/.agents/workflows/create.md +59 -0
  29. package/templates/.agents/workflows/debug.md +103 -0
  30. package/templates/.agents/workflows/deploy.md +176 -0
  31. package/templates/.agents/workflows/enhance.md +63 -0
  32. package/templates/.agents/workflows/orchestrate.md +237 -0
  33. package/templates/.agents/workflows/plan.md +89 -0
  34. package/templates/.agents/workflows/preview.md +81 -0
  35. package/templates/.agents/workflows/setup-brain.md +39 -0
  36. package/templates/.agents/workflows/status.md +86 -0
  37. package/templates/.agents/workflows/test.md +144 -0
  38. package/templates/.agents/workflows/ui-ux-pro-max.md +296 -0
  39. package/templates/skills_normal/api-patterns/scripts/api_validator.py +211 -0
  40. package/templates/skills_normal/database-design/scripts/schema_validator.py +172 -0
  41. package/templates/skills_normal/frontend-design/scripts/accessibility_checker.py +183 -0
  42. package/templates/skills_normal/frontend-design/scripts/ux_audit.py +722 -0
  43. package/templates/skills_normal/git-pushing/scripts/smart_commit.sh +19 -0
  44. package/templates/skills_normal/lint-and-validate/scripts/lint_runner.py +184 -0
  45. package/templates/skills_normal/lint-and-validate/scripts/type_coverage.py +173 -0
  46. package/templates/skills_normal/performance-profiling/scripts/lighthouse_audit.py +76 -0
  47. package/templates/skills_normal/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
  48. package/templates/skills_normal/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
  49. package/templates/skills_normal/senior-fullstack/scripts/project_scaffolder.py +114 -0
  50. package/templates/skills_normal/seo-fundamentals/scripts/seo_checker.py +219 -0
  51. package/templates/skills_normal/testing-patterns/scripts/test_runner.py +219 -0
  52. package/templates/skills_normal/vulnerability-scanner/scripts/security_scan.py +458 -0
  53. package/templates/vault/007/scripts/config.py +472 -0
  54. package/templates/vault/007/scripts/full_audit.py +1306 -0
  55. package/templates/vault/007/scripts/quick_scan.py +481 -0
  56. package/templates/vault/007/scripts/requirements.txt +26 -0
  57. package/templates/vault/007/scripts/scanners/__init__.py +0 -0
  58. package/templates/vault/007/scripts/scanners/dependency_scanner.py +1305 -0
  59. package/templates/vault/007/scripts/scanners/injection_scanner.py +1104 -0
  60. package/templates/vault/007/scripts/scanners/secrets_scanner.py +1008 -0
  61. package/templates/vault/007/scripts/score_calculator.py +693 -0
  62. package/templates/vault/agent-orchestrator/scripts/match_skills.py +329 -0
  63. package/templates/vault/agent-orchestrator/scripts/orchestrate.py +304 -0
  64. package/templates/vault/agent-orchestrator/scripts/requirements.txt +1 -0
  65. package/templates/vault/agent-orchestrator/scripts/scan_registry.py +508 -0
  66. package/templates/vault/ai-studio-image/scripts/config.py +613 -0
  67. package/templates/vault/ai-studio-image/scripts/generate.py +630 -0
  68. package/templates/vault/ai-studio-image/scripts/prompt_engine.py +424 -0
  69. package/templates/vault/ai-studio-image/scripts/requirements.txt +4 -0
  70. package/templates/vault/ai-studio-image/scripts/templates.py +349 -0
  71. package/templates/vault/android_ui_verification/scripts/verify_ui.sh +32 -0
  72. package/templates/vault/apify-audience-analysis/reference/scripts/run_actor.js +363 -0
  73. package/templates/vault/apify-brand-reputation-monitoring/reference/scripts/run_actor.js +363 -0
  74. package/templates/vault/apify-competitor-intelligence/reference/scripts/run_actor.js +363 -0
  75. package/templates/vault/apify-content-analytics/reference/scripts/run_actor.js +363 -0
  76. package/templates/vault/apify-ecommerce/reference/scripts/package.json +3 -0
  77. package/templates/vault/apify-ecommerce/reference/scripts/run_actor.js +369 -0
  78. package/templates/vault/apify-influencer-discovery/reference/scripts/run_actor.js +363 -0
  79. package/templates/vault/apify-lead-generation/reference/scripts/run_actor.js +363 -0
  80. package/templates/vault/apify-market-research/reference/scripts/run_actor.js +363 -0
  81. package/templates/vault/apify-trend-analysis/reference/scripts/run_actor.js +363 -0
  82. package/templates/vault/apify-ultimate-scraper/reference/scripts/run_actor.js +363 -0
  83. package/templates/vault/audio-transcriber/scripts/install-requirements.sh +190 -0
  84. package/templates/vault/audio-transcriber/scripts/transcribe.py +486 -0
  85. package/templates/vault/claude-monitor/scripts/api_bench.py +240 -0
  86. package/templates/vault/claude-monitor/scripts/config.py +69 -0
  87. package/templates/vault/claude-monitor/scripts/health_check.py +362 -0
  88. package/templates/vault/claude-monitor/scripts/monitor.py +296 -0
  89. package/templates/vault/content-creator/scripts/brand_voice_analyzer.py +185 -0
  90. package/templates/vault/content-creator/scripts/seo_optimizer.py +419 -0
  91. package/templates/vault/context-agent/scripts/active_context.py +227 -0
  92. package/templates/vault/context-agent/scripts/compressor.py +149 -0
  93. package/templates/vault/context-agent/scripts/config.py +69 -0
  94. package/templates/vault/context-agent/scripts/context_loader.py +155 -0
  95. package/templates/vault/context-agent/scripts/context_manager.py +302 -0
  96. package/templates/vault/context-agent/scripts/models.py +103 -0
  97. package/templates/vault/context-agent/scripts/project_registry.py +132 -0
  98. package/templates/vault/context-agent/scripts/requirements.txt +6 -0
  99. package/templates/vault/context-agent/scripts/search.py +115 -0
  100. package/templates/vault/context-agent/scripts/session_parser.py +206 -0
  101. package/templates/vault/context-agent/scripts/session_summary.py +319 -0
  102. package/templates/vault/context-guardian/scripts/context_snapshot.py +229 -0
  103. package/templates/vault/docx/ooxml/scripts/pack.py +159 -0
  104. package/templates/vault/docx/ooxml/scripts/unpack.py +29 -0
  105. package/templates/vault/docx/ooxml/scripts/validate.py +69 -0
  106. package/templates/vault/docx/ooxml/scripts/validation/__init__.py +15 -0
  107. package/templates/vault/docx/ooxml/scripts/validation/base.py +951 -0
  108. package/templates/vault/docx/ooxml/scripts/validation/docx.py +274 -0
  109. package/templates/vault/docx/ooxml/scripts/validation/pptx.py +315 -0
  110. package/templates/vault/docx/ooxml/scripts/validation/redlining.py +279 -0
  111. package/templates/vault/docx/scripts/__init__.py +1 -0
  112. package/templates/vault/docx/scripts/document.py +1276 -0
  113. package/templates/vault/docx/scripts/templates/comments.xml +3 -0
  114. package/templates/vault/docx/scripts/templates/commentsExtended.xml +3 -0
  115. package/templates/vault/docx/scripts/templates/commentsExtensible.xml +3 -0
  116. package/templates/vault/docx/scripts/templates/commentsIds.xml +3 -0
  117. package/templates/vault/docx/scripts/templates/people.xml +3 -0
  118. package/templates/vault/docx/scripts/utilities.py +374 -0
  119. package/templates/vault/docx-official/ooxml/scripts/pack.py +159 -0
  120. package/templates/vault/docx-official/ooxml/scripts/unpack.py +29 -0
  121. package/templates/vault/docx-official/ooxml/scripts/validate.py +69 -0
  122. package/templates/vault/docx-official/ooxml/scripts/validation/__init__.py +15 -0
  123. package/templates/vault/docx-official/ooxml/scripts/validation/base.py +951 -0
  124. package/templates/vault/docx-official/ooxml/scripts/validation/docx.py +274 -0
  125. package/templates/vault/docx-official/ooxml/scripts/validation/pptx.py +315 -0
  126. package/templates/vault/docx-official/ooxml/scripts/validation/redlining.py +279 -0
  127. package/templates/vault/docx-official/scripts/__init__.py +1 -0
  128. package/templates/vault/docx-official/scripts/document.py +1276 -0
  129. package/templates/vault/docx-official/scripts/templates/comments.xml +3 -0
  130. package/templates/vault/docx-official/scripts/templates/commentsExtended.xml +3 -0
  131. package/templates/vault/docx-official/scripts/templates/commentsExtensible.xml +3 -0
  132. package/templates/vault/docx-official/scripts/templates/commentsIds.xml +3 -0
  133. package/templates/vault/docx-official/scripts/templates/people.xml +3 -0
  134. package/templates/vault/docx-official/scripts/utilities.py +374 -0
  135. package/templates/vault/geo-fundamentals/scripts/geo_checker.py +289 -0
  136. package/templates/vault/helm-chart-scaffolding/scripts/validate-chart.sh +244 -0
  137. package/templates/vault/i18n-localization/scripts/i18n_checker.py +241 -0
  138. package/templates/vault/instagram/scripts/account_setup.py +233 -0
  139. package/templates/vault/instagram/scripts/analyze.py +221 -0
  140. package/templates/vault/instagram/scripts/api_client.py +444 -0
  141. package/templates/vault/instagram/scripts/auth.py +411 -0
  142. package/templates/vault/instagram/scripts/comments.py +160 -0
  143. package/templates/vault/instagram/scripts/config.py +111 -0
  144. package/templates/vault/instagram/scripts/db.py +467 -0
  145. package/templates/vault/instagram/scripts/export.py +138 -0
  146. package/templates/vault/instagram/scripts/governance.py +233 -0
  147. package/templates/vault/instagram/scripts/hashtags.py +114 -0
  148. package/templates/vault/instagram/scripts/insights.py +170 -0
  149. package/templates/vault/instagram/scripts/media.py +65 -0
  150. package/templates/vault/instagram/scripts/messages.py +103 -0
  151. package/templates/vault/instagram/scripts/profile.py +58 -0
  152. package/templates/vault/instagram/scripts/publish.py +449 -0
  153. package/templates/vault/instagram/scripts/requirements.txt +5 -0
  154. package/templates/vault/instagram/scripts/run_all.py +189 -0
  155. package/templates/vault/instagram/scripts/schedule.py +189 -0
  156. package/templates/vault/instagram/scripts/serve_api.py +234 -0
  157. package/templates/vault/instagram/scripts/templates.py +155 -0
  158. package/templates/vault/junta-leiloeiros/scripts/db.py +216 -0
  159. package/templates/vault/junta-leiloeiros/scripts/export.py +137 -0
  160. package/templates/vault/junta-leiloeiros/scripts/requirements.txt +15 -0
  161. package/templates/vault/junta-leiloeiros/scripts/run_all.py +190 -0
  162. package/templates/vault/junta-leiloeiros/scripts/scraper/__init__.py +4 -0
  163. package/templates/vault/junta-leiloeiros/scripts/scraper/base_scraper.py +209 -0
  164. package/templates/vault/junta-leiloeiros/scripts/scraper/generic_scraper.py +110 -0
  165. package/templates/vault/junta-leiloeiros/scripts/scraper/jucap.py +110 -0
  166. package/templates/vault/junta-leiloeiros/scripts/scraper/juceac.py +72 -0
  167. package/templates/vault/junta-leiloeiros/scripts/scraper/juceal.py +72 -0
  168. package/templates/vault/junta-leiloeiros/scripts/scraper/juceb.py +68 -0
  169. package/templates/vault/junta-leiloeiros/scripts/scraper/jucec.py +63 -0
  170. package/templates/vault/junta-leiloeiros/scripts/scraper/jucema.py +211 -0
  171. package/templates/vault/junta-leiloeiros/scripts/scraper/jucemg.py +218 -0
  172. package/templates/vault/junta-leiloeiros/scripts/scraper/jucep.py +70 -0
  173. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepa.py +74 -0
  174. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepar.py +80 -0
  175. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepe.py +78 -0
  176. package/templates/vault/junta-leiloeiros/scripts/scraper/jucepi.py +69 -0
  177. package/templates/vault/junta-leiloeiros/scripts/scraper/jucer.py +256 -0
  178. package/templates/vault/junta-leiloeiros/scripts/scraper/jucerja.py +170 -0
  179. package/templates/vault/junta-leiloeiros/scripts/scraper/jucern.py +71 -0
  180. package/templates/vault/junta-leiloeiros/scripts/scraper/jucesc.py +89 -0
  181. package/templates/vault/junta-leiloeiros/scripts/scraper/jucesp.py +233 -0
  182. package/templates/vault/junta-leiloeiros/scripts/scraper/jucetins.py +134 -0
  183. package/templates/vault/junta-leiloeiros/scripts/scraper/jucis_df.py +63 -0
  184. package/templates/vault/junta-leiloeiros/scripts/scraper/jucisrs.py +299 -0
  185. package/templates/vault/junta-leiloeiros/scripts/scraper/states.py +99 -0
  186. package/templates/vault/junta-leiloeiros/scripts/serve_api.py +164 -0
  187. package/templates/vault/junta-leiloeiros/scripts/web_scraper_fallback.py +233 -0
  188. package/templates/vault/last30days/scripts/last30days.py +521 -0
  189. package/templates/vault/last30days/scripts/lib/__init__.py +1 -0
  190. package/templates/vault/last30days/scripts/lib/cache.py +152 -0
  191. package/templates/vault/last30days/scripts/lib/dates.py +124 -0
  192. package/templates/vault/last30days/scripts/lib/dedupe.py +120 -0
  193. package/templates/vault/last30days/scripts/lib/env.py +149 -0
  194. package/templates/vault/last30days/scripts/lib/http.py +152 -0
  195. package/templates/vault/last30days/scripts/lib/models.py +175 -0
  196. package/templates/vault/last30days/scripts/lib/normalize.py +160 -0
  197. package/templates/vault/last30days/scripts/lib/openai_reddit.py +230 -0
  198. package/templates/vault/last30days/scripts/lib/reddit_enrich.py +232 -0
  199. package/templates/vault/last30days/scripts/lib/render.py +383 -0
  200. package/templates/vault/last30days/scripts/lib/schema.py +336 -0
  201. package/templates/vault/last30days/scripts/lib/score.py +311 -0
  202. package/templates/vault/last30days/scripts/lib/ui.py +324 -0
  203. package/templates/vault/last30days/scripts/lib/websearch.py +401 -0
  204. package/templates/vault/last30days/scripts/lib/xai_x.py +217 -0
  205. package/templates/vault/leiloeiro-avaliacao/scripts/governance.py +106 -0
  206. package/templates/vault/leiloeiro-avaliacao/scripts/requirements.txt +1 -0
  207. package/templates/vault/leiloeiro-edital/scripts/governance.py +106 -0
  208. package/templates/vault/leiloeiro-edital/scripts/requirements.txt +1 -0
  209. package/templates/vault/leiloeiro-ia/scripts/governance.py +106 -0
  210. package/templates/vault/leiloeiro-ia/scripts/requirements.txt +1 -0
  211. package/templates/vault/leiloeiro-juridico/scripts/governance.py +106 -0
  212. package/templates/vault/leiloeiro-juridico/scripts/requirements.txt +1 -0
  213. package/templates/vault/leiloeiro-mercado/scripts/governance.py +106 -0
  214. package/templates/vault/leiloeiro-mercado/scripts/requirements.txt +1 -0
  215. package/templates/vault/leiloeiro-risco/scripts/governance.py +106 -0
  216. package/templates/vault/leiloeiro-risco/scripts/requirements.txt +1 -0
  217. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/database.ts +24 -0
  218. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/db.ts +35 -0
  219. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/index.ts +2 -0
  220. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/migrations.ts +31 -0
  221. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/db/schema.sql +8 -0
  222. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/index.ts +44 -0
  223. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/routes/todos.ts +155 -0
  224. package/templates/vault/loki-mode/examples/todo-app-generated/backend/src/types/index.ts +35 -0
  225. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.css +384 -0
  226. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/App.tsx +81 -0
  227. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/api/todos.ts +57 -0
  228. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/ConfirmDialog.tsx +26 -0
  229. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/EmptyState.tsx +8 -0
  230. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoForm.tsx +43 -0
  231. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoItem.tsx +36 -0
  232. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/components/TodoList.tsx +27 -0
  233. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/hooks/useTodos.ts +81 -0
  234. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/index.css +48 -0
  235. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/main.tsx +10 -0
  236. package/templates/vault/loki-mode/examples/todo-app-generated/frontend/src/vite-env.d.ts +1 -0
  237. package/templates/vault/loki-mode/scripts/export-to-vibe-kanban.sh +178 -0
  238. package/templates/vault/loki-mode/scripts/loki-wrapper.sh +281 -0
  239. package/templates/vault/loki-mode/scripts/take-screenshots.js +55 -0
  240. package/templates/vault/matematico-tao/scripts/complexity_analyzer.py +544 -0
  241. package/templates/vault/matematico-tao/scripts/dependency_graph.py +538 -0
  242. package/templates/vault/mcp-builder/scripts/connections.py +151 -0
  243. package/templates/vault/mcp-builder/scripts/evaluation.py +373 -0
  244. package/templates/vault/mcp-builder/scripts/example_evaluation.xml +22 -0
  245. package/templates/vault/mcp-builder/scripts/requirements.txt +2 -0
  246. package/templates/vault/mobile-design/scripts/mobile_audit.py +670 -0
  247. package/templates/vault/notebooklm/scripts/__init__.py +81 -0
  248. package/templates/vault/notebooklm/scripts/ask_question.py +256 -0
  249. package/templates/vault/notebooklm/scripts/auth_manager.py +358 -0
  250. package/templates/vault/notebooklm/scripts/browser_session.py +255 -0
  251. package/templates/vault/notebooklm/scripts/browser_utils.py +107 -0
  252. package/templates/vault/notebooklm/scripts/cleanup_manager.py +302 -0
  253. package/templates/vault/notebooklm/scripts/config.py +44 -0
  254. package/templates/vault/notebooklm/scripts/notebook_manager.py +410 -0
  255. package/templates/vault/notebooklm/scripts/run.py +102 -0
  256. package/templates/vault/notebooklm/scripts/setup_environment.py +204 -0
  257. package/templates/vault/pdf/scripts/check_bounding_boxes.py +70 -0
  258. package/templates/vault/pdf/scripts/check_bounding_boxes_test.py +226 -0
  259. package/templates/vault/pdf/scripts/check_fillable_fields.py +12 -0
  260. package/templates/vault/pdf/scripts/convert_pdf_to_images.py +35 -0
  261. package/templates/vault/pdf/scripts/create_validation_image.py +41 -0
  262. package/templates/vault/pdf/scripts/extract_form_field_info.py +152 -0
  263. package/templates/vault/pdf/scripts/fill_fillable_fields.py +114 -0
  264. package/templates/vault/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  265. package/templates/vault/pdf-official/scripts/check_bounding_boxes.py +70 -0
  266. package/templates/vault/pdf-official/scripts/check_bounding_boxes_test.py +226 -0
  267. package/templates/vault/pdf-official/scripts/check_fillable_fields.py +12 -0
  268. package/templates/vault/pdf-official/scripts/convert_pdf_to_images.py +35 -0
  269. package/templates/vault/pdf-official/scripts/create_validation_image.py +41 -0
  270. package/templates/vault/pdf-official/scripts/extract_form_field_info.py +152 -0
  271. package/templates/vault/pdf-official/scripts/fill_fillable_fields.py +114 -0
  272. package/templates/vault/pdf-official/scripts/fill_pdf_form_with_annotations.py +108 -0
  273. package/templates/vault/planning-with-files/scripts/check-complete.sh +44 -0
  274. package/templates/vault/planning-with-files/scripts/init-session.sh +120 -0
  275. package/templates/vault/pptx/ooxml/scripts/pack.py +159 -0
  276. package/templates/vault/pptx/ooxml/scripts/unpack.py +29 -0
  277. package/templates/vault/pptx/ooxml/scripts/validate.py +69 -0
  278. package/templates/vault/pptx/ooxml/scripts/validation/__init__.py +15 -0
  279. package/templates/vault/pptx/ooxml/scripts/validation/base.py +951 -0
  280. package/templates/vault/pptx/ooxml/scripts/validation/docx.py +274 -0
  281. package/templates/vault/pptx/ooxml/scripts/validation/pptx.py +315 -0
  282. package/templates/vault/pptx/ooxml/scripts/validation/redlining.py +279 -0
  283. package/templates/vault/pptx/scripts/html2pptx.js +979 -0
  284. package/templates/vault/pptx/scripts/inventory.py +1020 -0
  285. package/templates/vault/pptx/scripts/rearrange.py +231 -0
  286. package/templates/vault/pptx/scripts/replace.py +385 -0
  287. package/templates/vault/pptx/scripts/thumbnail.py +450 -0
  288. package/templates/vault/pptx-official/ooxml/scripts/pack.py +159 -0
  289. package/templates/vault/pptx-official/ooxml/scripts/unpack.py +29 -0
  290. package/templates/vault/pptx-official/ooxml/scripts/validate.py +69 -0
  291. package/templates/vault/pptx-official/ooxml/scripts/validation/__init__.py +15 -0
  292. package/templates/vault/pptx-official/ooxml/scripts/validation/base.py +951 -0
  293. package/templates/vault/pptx-official/ooxml/scripts/validation/docx.py +274 -0
  294. package/templates/vault/pptx-official/ooxml/scripts/validation/pptx.py +315 -0
  295. package/templates/vault/pptx-official/ooxml/scripts/validation/redlining.py +279 -0
  296. package/templates/vault/pptx-official/scripts/html2pptx.js +979 -0
  297. package/templates/vault/pptx-official/scripts/inventory.py +1020 -0
  298. package/templates/vault/pptx-official/scripts/rearrange.py +231 -0
  299. package/templates/vault/pptx-official/scripts/replace.py +385 -0
  300. package/templates/vault/pptx-official/scripts/thumbnail.py +450 -0
  301. package/templates/vault/product-manager-toolkit/scripts/customer_interview_analyzer.py +441 -0
  302. package/templates/vault/product-manager-toolkit/scripts/rice_prioritizer.py +296 -0
  303. package/templates/vault/prompt-engineering-patterns/scripts/optimize-prompt.py +279 -0
  304. package/templates/vault/scripts/.skill_cache.json +7538 -0
  305. package/templates/vault/scripts/skill_search.py +228 -0
  306. package/templates/vault/senior-architect/scripts/architecture_diagram_generator.py +114 -0
  307. package/templates/vault/senior-architect/scripts/dependency_analyzer.py +114 -0
  308. package/templates/vault/senior-architect/scripts/project_architect.py +114 -0
  309. package/templates/vault/shopify-development/scripts/requirements.txt +19 -0
  310. package/templates/vault/shopify-development/scripts/shopify_graphql.py +428 -0
  311. package/templates/vault/shopify-development/scripts/shopify_init.py +441 -0
  312. package/templates/vault/shopify-development/scripts/tests/test_shopify_init.py +379 -0
  313. package/templates/vault/skill-creator/scripts/init_skill.py +303 -0
  314. package/templates/vault/skill-creator/scripts/package_skill.py +110 -0
  315. package/templates/vault/skill-creator/scripts/quick_validate.py +95 -0
  316. package/templates/vault/skill-installer/scripts/detect_skills.py +318 -0
  317. package/templates/vault/skill-installer/scripts/install_skill.py +1708 -0
  318. package/templates/vault/skill-installer/scripts/package_skill.py +417 -0
  319. package/templates/vault/skill-installer/scripts/requirements.txt +1 -0
  320. package/templates/vault/skill-installer/scripts/validate_skill.py +430 -0
  321. package/templates/vault/skill-sentinel/scripts/analyzers/__init__.py +13 -0
  322. package/templates/vault/skill-sentinel/scripts/analyzers/code_quality.py +247 -0
  323. package/templates/vault/skill-sentinel/scripts/analyzers/cross_skill.py +134 -0
  324. package/templates/vault/skill-sentinel/scripts/analyzers/dependencies.py +121 -0
  325. package/templates/vault/skill-sentinel/scripts/analyzers/documentation.py +189 -0
  326. package/templates/vault/skill-sentinel/scripts/analyzers/governance_audit.py +153 -0
  327. package/templates/vault/skill-sentinel/scripts/analyzers/performance.py +164 -0
  328. package/templates/vault/skill-sentinel/scripts/analyzers/security.py +189 -0
  329. package/templates/vault/skill-sentinel/scripts/config.py +158 -0
  330. package/templates/vault/skill-sentinel/scripts/cost_optimizer.py +146 -0
  331. package/templates/vault/skill-sentinel/scripts/db.py +354 -0
  332. package/templates/vault/skill-sentinel/scripts/governance.py +58 -0
  333. package/templates/vault/skill-sentinel/scripts/recommender.py +228 -0
  334. package/templates/vault/skill-sentinel/scripts/report_generator.py +224 -0
  335. package/templates/vault/skill-sentinel/scripts/requirements.txt +1 -0
  336. package/templates/vault/skill-sentinel/scripts/run_audit.py +290 -0
  337. package/templates/vault/skill-sentinel/scripts/scanner.py +271 -0
  338. package/templates/vault/stability-ai/scripts/config.py +266 -0
  339. package/templates/vault/stability-ai/scripts/generate.py +687 -0
  340. package/templates/vault/stability-ai/scripts/requirements.txt +4 -0
  341. package/templates/vault/stability-ai/scripts/styles.py +174 -0
  342. package/templates/vault/telegram/assets/boilerplate/nodejs/src/bot-client.ts +86 -0
  343. package/templates/vault/telegram/assets/boilerplate/nodejs/src/handlers.ts +79 -0
  344. package/templates/vault/telegram/assets/boilerplate/nodejs/src/index.ts +32 -0
  345. package/templates/vault/telegram/scripts/send_message.py +143 -0
  346. package/templates/vault/telegram/scripts/setup_project.py +103 -0
  347. package/templates/vault/telegram/scripts/test_bot.py +144 -0
  348. package/templates/vault/typescript-expert/scripts/ts_diagnostic.py +203 -0
  349. package/templates/vault/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
  350. package/templates/vault/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
  351. package/templates/vault/ui-ux-pro-max/scripts/core.py +257 -0
  352. package/templates/vault/ui-ux-pro-max/scripts/design_system.py +487 -0
  353. package/templates/vault/ui-ux-pro-max/scripts/search.py +76 -0
  354. package/templates/vault/videodb/scripts/ws_listener.py +204 -0
  355. package/templates/vault/web-artifacts-builder/scripts/bundle-artifact.sh +54 -0
  356. package/templates/vault/web-artifacts-builder/scripts/init-artifact.sh +322 -0
  357. package/templates/vault/web-artifacts-builder/scripts/shadcn-components.tar.gz +0 -0
  358. package/templates/vault/webapp-testing/scripts/with_server.py +106 -0
  359. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/index.ts +125 -0
  360. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/template-manager.ts +67 -0
  361. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/types.ts +216 -0
  362. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/webhook-handler.ts +173 -0
  363. package/templates/vault/whatsapp-cloud-api/assets/boilerplate/nodejs/src/whatsapp-client.ts +193 -0
  364. package/templates/vault/whatsapp-cloud-api/scripts/send_test_message.py +137 -0
  365. package/templates/vault/whatsapp-cloud-api/scripts/setup_project.py +118 -0
  366. package/templates/vault/whatsapp-cloud-api/scripts/validate_config.py +190 -0
  367. package/templates/vault/youtube-summarizer/scripts/extract-transcript.py +65 -0
  368. package/templates/vault/youtube-summarizer/scripts/install-dependencies.sh +28 -0
@@ -0,0 +1,1008 @@
1
+ """007 Secrets Scanner -- Deep scanner for secrets and credentials.
2
+
3
+ Goes deeper than quick_scan by performing entropy analysis, base64 detection,
4
+ context-aware false positive reduction, and targeted scanning of sensitive
5
+ file types (.env, config files, shell scripts, Docker, CI/CD).
6
+
7
+ Usage:
8
+ python secrets_scanner.py --target /path/to/project
9
+ python secrets_scanner.py --target /path/to/project --output json --verbose
10
+ python secrets_scanner.py --target /path/to/project --include-low
11
+ """
12
+
13
+ import argparse
14
+ import base64
15
+ import json
16
+ import math
17
+ import os
18
+ import re
19
+ import sys
20
+ import time
21
+ from pathlib import Path
22
+
23
+ # ---------------------------------------------------------------------------
24
+ # Import from the 007 config hub (parent directory)
25
+ # ---------------------------------------------------------------------------
26
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
27
+
28
+ import config # noqa: E402
29
+
30
+ # ---------------------------------------------------------------------------
31
+ # Logger
32
+ # ---------------------------------------------------------------------------
33
+ logger = config.setup_logging("007-secrets-scanner")
34
+
35
+ # ---------------------------------------------------------------------------
36
+ # Additional patterns beyond config.SECRET_PATTERNS
37
+ # ---------------------------------------------------------------------------
38
+ # Each entry: (pattern_name, compiled_regex, severity)
39
+
40
+ _EXTRA_PATTERN_DEFS = [
41
+ # URLs with embedded credentials (http://user:pass@host)
42
+ (
43
+ "url_embedded_credentials",
44
+ r"""https?://[^:\s]+:[^@\s]+@[^\s/]+""",
45
+ "HIGH",
46
+ ),
47
+ # Stripe keys
48
+ (
49
+ "stripe_key",
50
+ r"""(?:sk|pk)_(?:live|test)_[A-Za-z0-9]{20,}""",
51
+ "CRITICAL",
52
+ ),
53
+ # Google API key
54
+ (
55
+ "google_api_key",
56
+ r"""AIza[0-9A-Za-z\-_]{35}""",
57
+ "HIGH",
58
+ ),
59
+ # Twilio Account SID / Auth Token
60
+ (
61
+ "twilio_key",
62
+ r"""(?:AC[a-f0-9]{32}|SK[a-f0-9]{32})""",
63
+ "HIGH",
64
+ ),
65
+ # Heroku API key
66
+ (
67
+ "heroku_api_key",
68
+ r"""(?i)heroku[_-]?api[_-]?key\s*[:=]\s*['\"]\S{8,}['\"]""",
69
+ "HIGH",
70
+ ),
71
+ # SendGrid API key
72
+ (
73
+ "sendgrid_key",
74
+ r"""SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}""",
75
+ "CRITICAL",
76
+ ),
77
+ # npm token
78
+ (
79
+ "npm_token",
80
+ r"""(?:npm_)[A-Za-z0-9]{36}""",
81
+ "CRITICAL",
82
+ ),
83
+ # Generic connection string (ODBC / ADO style)
84
+ (
85
+ "connection_string",
86
+ r"""(?i)(?:connectionstring|conn_str)\s*[:=]\s*['\"][^'\"]{10,}['\"]""",
87
+ "HIGH",
88
+ ),
89
+ # JWT tokens (three base64 segments separated by dots)
90
+ (
91
+ "jwt_token",
92
+ r"""eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}""",
93
+ "MEDIUM",
94
+ ),
95
+ # Azure storage key
96
+ (
97
+ "azure_storage_key",
98
+ r"""(?i)(?:accountkey|storage[_-]?key)\s*[:=]\s*['\"]\S{44,}['\"]""",
99
+ "CRITICAL",
100
+ ),
101
+ ]
102
+
103
+ EXTRA_PATTERNS = [
104
+ (name, re.compile(pattern), severity)
105
+ for name, pattern, severity in _EXTRA_PATTERN_DEFS
106
+ ]
107
+
108
+ # Combined pattern set: config patterns first, then extras
109
+ ALL_SECRET_PATTERNS = list(config.SECRET_PATTERNS) + EXTRA_PATTERNS
110
+
111
+
112
+ # ---------------------------------------------------------------------------
113
+ # Targeted file categories for deep scanning
114
+ # ---------------------------------------------------------------------------
115
+
116
+ # .env variants -- always scanned regardless of SCANNABLE_EXTENSIONS
117
+ ENV_FILE_PATTERNS = {
118
+ ".env", ".env.local", ".env.production", ".env.staging",
119
+ ".env.development", ".env.test", ".env.example", ".env.sample",
120
+ ".env.defaults", ".env.template",
121
+ }
122
+
123
+ CONFIG_EXTENSIONS = {".json", ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf"}
124
+
125
+ SHELL_EXTENSIONS = {".sh", ".bash", ".zsh", ".ps1", ".bat", ".cmd"}
126
+
127
+ DOCKER_PREFIXES = ("Dockerfile", "dockerfile", "docker-compose")
128
+
129
+ CICD_PATTERNS = {
130
+ ".github/workflows",
131
+ ".gitlab-ci.yml",
132
+ "Jenkinsfile",
133
+ ".circleci/config.yml",
134
+ ".travis.yml",
135
+ "azure-pipelines.yml",
136
+ "bitbucket-pipelines.yml",
137
+ }
138
+
139
+ PRIVATE_KEY_EXTENSIONS = {".pem", ".key", ".p12", ".pfx", ".jks", ".keystore"}
140
+
141
+ # Files that are test fixtures -- lower severity or skip
142
+ _TEST_FILE_PATTERNS = re.compile(
143
+ r"""(?i)(?:^test_|_test\.py$|\.test\.[jt]sx?$|\.spec\.[jt]sx?$|__tests__|fixtures?[/\\])"""
144
+ )
145
+
146
+ # Placeholder / example value patterns -- these are NOT real secrets
147
+ _PLACEHOLDER_PATTERN = re.compile(
148
+ r"""(?i)(?:example|placeholder|changeme|xxx+|your[_-]?key[_-]?here|"""
149
+ r"""insert[_-]?here|replace[_-]?me|todo|fixme|dummy|fake|sample|test123|"""
150
+ r"""sk_test_|pk_test_)"""
151
+ )
152
+
153
+
154
+ # ---------------------------------------------------------------------------
155
+ # Entropy calculation
156
+ # ---------------------------------------------------------------------------
157
+
158
+ def shannon_entropy(s: str) -> float:
159
+ """Calculate Shannon entropy of a string.
160
+
161
+ Higher entropy indicates more randomness, which may suggest a secret/token.
162
+ Typical English text: ~3.5-4.0 bits. Random tokens: ~4.5-6.0 bits.
163
+
164
+ Args:
165
+ s: Input string.
166
+
167
+ Returns:
168
+ Shannon entropy in bits. Returns 0.0 for empty strings.
169
+ """
170
+ if not s:
171
+ return 0.0
172
+
173
+ length = len(s)
174
+ freq: dict[str, int] = {}
175
+ for ch in s:
176
+ freq[ch] = freq.get(ch, 0) + 1
177
+
178
+ entropy = 0.0
179
+ for count in freq.values():
180
+ probability = count / length
181
+ if probability > 0:
182
+ entropy -= probability * math.log2(probability)
183
+
184
+ return entropy
185
+
186
+
187
+ # ---------------------------------------------------------------------------
188
+ # Base64 detection
189
+ # ---------------------------------------------------------------------------
190
+
191
+ _BASE64_RE = re.compile(
192
+ r"""[A-Za-z0-9+/]{20,}={0,2}"""
193
+ )
194
+
195
+ _BASE64_URL_RE = re.compile(
196
+ r"""[A-Za-z0-9_-]{20,}"""
197
+ )
198
+
199
+
200
+ def _check_base64_secret(token: str) -> bool:
201
+ """Check if a base64-looking string decodes to something high-entropy.
202
+
203
+ Args:
204
+ token: A candidate base64 string.
205
+
206
+ Returns:
207
+ True if the decoded content has high entropy (likely a secret).
208
+ """
209
+ # Pad if needed for standard base64
210
+ padded = token + "=" * (-len(token) % 4)
211
+ try:
212
+ decoded = base64.b64decode(padded, validate=True)
213
+ decoded_str = decoded.decode("ascii", errors="replace")
214
+ # Only flag if decoded content is also high entropy
215
+ return shannon_entropy(decoded_str) > 4.0 and len(decoded) >= 12
216
+ except Exception:
217
+ return False
218
+
219
+
220
+ # ---------------------------------------------------------------------------
221
+ # Hardcoded IP detection
222
+ # ---------------------------------------------------------------------------
223
+
224
+ _IP_RE = re.compile(
225
+ r"""\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b"""
226
+ )
227
+
228
+ _SAFE_IP_PREFIXES = (
229
+ "127.", # localhost
230
+ "0.", # unspecified
231
+ "10.", # private class A
232
+ "192.168.", # private class C
233
+ "169.254.", # link-local
234
+ "255.", # broadcast
235
+ )
236
+
237
+
238
+ def _is_private_or_localhost(ip: str) -> bool:
239
+ """Return True if IP is localhost, private range, or otherwise safe."""
240
+ if ip.startswith(_SAFE_IP_PREFIXES):
241
+ return True
242
+ # 172.16.0.0 - 172.31.255.255 (private class B)
243
+ parts = ip.split(".")
244
+ try:
245
+ if parts[0] == "172" and 16 <= int(parts[1]) <= 31:
246
+ return True
247
+ except (IndexError, ValueError):
248
+ pass
249
+ return False
250
+
251
+
252
+ # ---------------------------------------------------------------------------
253
+ # Context-aware false positive reduction
254
+ # ---------------------------------------------------------------------------
255
+
256
+ _COMMENT_LINE_RE = re.compile(
257
+ r"""^\s*(?:#|//|/\*|\*|;|rem\b|@rem\b)""", re.IGNORECASE
258
+ )
259
+
260
+ _MARKDOWN_CODE_FENCE = re.compile(r"""^\s*```""")
261
+
262
+
263
+ def _is_comment_line(line: str) -> bool:
264
+ """Return True if the line appears to be a comment."""
265
+ return bool(_COMMENT_LINE_RE.match(line))
266
+
267
+
268
+ def _is_test_file(filepath: Path) -> bool:
269
+ """Return True if the file is a test fixture / test file."""
270
+ return bool(_TEST_FILE_PATTERNS.search(filepath.name)) or bool(
271
+ _TEST_FILE_PATTERNS.search(str(filepath))
272
+ )
273
+
274
+
275
+ def _is_placeholder_value(line: str) -> bool:
276
+ """Return True if the matched line contains placeholder/example values."""
277
+ return bool(_PLACEHOLDER_PATTERN.search(line))
278
+
279
+
280
+ def _is_env_example(filepath: Path) -> bool:
281
+ """Return True if the file is a .env.example or similar template."""
282
+ name = filepath.name.lower()
283
+ return name in (".env.example", ".env.sample", ".env.template", ".env.defaults")
284
+
285
+
286
+ def _classify_file(filepath: Path) -> str:
287
+ """Classify a file into a category for reporting.
288
+
289
+ Returns one of: 'env', 'config', 'shell', 'docker', 'cicd',
290
+ 'private_key', 'source', 'other'.
291
+ """
292
+ name = filepath.name.lower()
293
+ suffix = filepath.suffix.lower()
294
+
295
+ # .env variants
296
+ if name.startswith(".env") or name in ENV_FILE_PATTERNS:
297
+ return "env"
298
+
299
+ # Private key files
300
+ if suffix in PRIVATE_KEY_EXTENSIONS:
301
+ return "private_key"
302
+
303
+ # Config files
304
+ if suffix in CONFIG_EXTENSIONS:
305
+ return "config"
306
+
307
+ # Shell scripts
308
+ if suffix in SHELL_EXTENSIONS:
309
+ return "shell"
310
+
311
+ # Docker files
312
+ if any(name.startswith(prefix) for prefix in DOCKER_PREFIXES):
313
+ return "docker"
314
+
315
+ # CI/CD files
316
+ filepath_str = str(filepath).replace("\\", "/")
317
+ for cicd_pattern in CICD_PATTERNS:
318
+ if cicd_pattern in filepath_str:
319
+ return "cicd"
320
+
321
+ # Source code
322
+ if suffix in config.SCANNABLE_EXTENSIONS:
323
+ return "source"
324
+
325
+ return "other"
326
+
327
+
328
+ # ---------------------------------------------------------------------------
329
+ # File collection (deeper than quick_scan)
330
+ # ---------------------------------------------------------------------------
331
+
332
+ def _should_scan_file(filepath: Path) -> bool:
333
+ """Determine if a file should be included in the deep scan.
334
+
335
+ More inclusive than quick_scan: also picks up .env variants, Docker files,
336
+ CI/CD files, and private key files even if their extension is not in
337
+ SCANNABLE_EXTENSIONS.
338
+ """
339
+ name = filepath.name.lower()
340
+ suffix = filepath.suffix.lower()
341
+
342
+ # Always scan .env variants
343
+ if name.startswith(".env"):
344
+ return True
345
+
346
+ # Always scan private key files (we detect their presence, not content)
347
+ if suffix in PRIVATE_KEY_EXTENSIONS:
348
+ return True
349
+
350
+ # Always scan Docker files
351
+ if any(name.startswith(prefix) for prefix in DOCKER_PREFIXES):
352
+ return True
353
+
354
+ # Always scan CI/CD files
355
+ filepath_str = str(filepath).replace("\\", "/")
356
+ for cicd_pattern in CICD_PATTERNS:
357
+ if cicd_pattern in filepath_str or name == Path(cicd_pattern).name:
358
+ return True
359
+
360
+ # Standard scannable extensions
361
+ for ext in config.SCANNABLE_EXTENSIONS:
362
+ if name.endswith(ext):
363
+ return True
364
+ if suffix in config.SCANNABLE_EXTENSIONS:
365
+ return True
366
+
367
+ return False
368
+
369
+
370
+ def collect_files(target: Path) -> list[Path]:
371
+ """Walk *target* recursively and return files for deep scanning.
372
+
373
+ Respects SKIP_DIRECTORIES but is more inclusive on file types.
374
+ """
375
+ files: list[Path] = []
376
+ max_files = config.LIMITS["max_files_per_scan"]
377
+
378
+ for root, dirs, filenames in os.walk(target):
379
+ dirs[:] = [d for d in dirs if d not in config.SKIP_DIRECTORIES]
380
+
381
+ for fname in filenames:
382
+ if len(files) >= max_files:
383
+ logger.warning(
384
+ "Reached max_files_per_scan limit (%d). Stopping.", max_files
385
+ )
386
+ return files
387
+
388
+ fpath = Path(root) / fname
389
+ if _should_scan_file(fpath):
390
+ files.append(fpath)
391
+
392
+ return files
393
+
394
+
395
+ # ---------------------------------------------------------------------------
396
+ # Core scanning logic
397
+ # ---------------------------------------------------------------------------
398
+
399
+ def _redact(text: str, keep: int = 6) -> str:
400
+ """Return a redacted version of *text*, keeping only the first few chars."""
401
+ text = text.strip()
402
+ if len(text) <= keep:
403
+ return text
404
+ return text[:keep] + "****"
405
+
406
+
407
+ def _snippet(line: str, match_start: int, context: int = 50) -> str:
408
+ """Extract a short redacted snippet around the match position."""
409
+ start = max(0, match_start - context // 2)
410
+ end = min(len(line), match_start + context)
411
+ raw = line[start:end].strip()
412
+ return _redact(raw)
413
+
414
+
415
+ def scan_file(filepath: Path, verbose: bool = False) -> list[dict]:
416
+ """Perform deep secret scanning on a single file.
417
+
418
+ Applies pattern matching, entropy analysis, base64 detection,
419
+ URL credential detection, IP detection, and context-aware filtering.
420
+
421
+ Returns a list of finding dicts.
422
+ """
423
+ findings: list[dict] = []
424
+ max_findings = config.LIMITS["max_findings_per_file"]
425
+ file_str = str(filepath)
426
+ file_category = _classify_file(filepath)
427
+ is_test = _is_test_file(filepath)
428
+ is_env_ex = _is_env_example(filepath)
429
+
430
+ # --- Private key file detection (by extension, not content) ---
431
+ if filepath.suffix.lower() in PRIVATE_KEY_EXTENSIONS:
432
+ sev = "MEDIUM" if is_test else "CRITICAL"
433
+ findings.append({
434
+ "type": "secret",
435
+ "pattern": "private_key_file",
436
+ "severity": sev,
437
+ "file": file_str,
438
+ "line": 0,
439
+ "snippet": f"Private key file detected: {filepath.name}",
440
+ "category": file_category,
441
+ })
442
+ # Still scan content if readable
443
+ # (fall through)
444
+
445
+ # --- File size check ---
446
+ try:
447
+ size = filepath.stat().st_size
448
+ except OSError:
449
+ return findings
450
+
451
+ if size > config.LIMITS["max_file_size_bytes"]:
452
+ if verbose:
453
+ logger.debug("Skipping oversized file: %s (%d bytes)", filepath, size)
454
+ return findings
455
+
456
+ # --- Read content ---
457
+ try:
458
+ text = filepath.read_text(encoding="utf-8", errors="replace")
459
+ except OSError as exc:
460
+ if verbose:
461
+ logger.debug("Cannot read %s: %s", filepath, exc)
462
+ return findings
463
+
464
+ lines = text.splitlines()
465
+ in_markdown_code_block = False
466
+
467
+ for line_num, line in enumerate(lines, start=1):
468
+ if len(findings) >= max_findings:
469
+ break
470
+
471
+ stripped = line.strip()
472
+ if not stripped:
473
+ continue
474
+
475
+ # Track markdown code fences for context-aware filtering
476
+ if _MARKDOWN_CODE_FENCE.match(stripped):
477
+ in_markdown_code_block = not in_markdown_code_block
478
+ continue
479
+
480
+ # Context-aware filters
481
+ is_comment = _is_comment_line(stripped)
482
+ is_placeholder = _is_placeholder_value(stripped)
483
+
484
+ # --- Pattern matching (config + extra patterns) ---
485
+ for pattern_name, regex, severity in ALL_SECRET_PATTERNS:
486
+ m = regex.search(line)
487
+ if not m:
488
+ continue
489
+
490
+ # Apply false positive reduction
491
+ skip = False
492
+ adjusted_severity = severity
493
+
494
+ if is_comment and not file_category == "env":
495
+ # Comments in source code are usually not real secrets
496
+ # But comments in .env files might still be sensitive
497
+ skip = True
498
+
499
+ if in_markdown_code_block:
500
+ skip = True
501
+
502
+ if is_placeholder:
503
+ skip = True
504
+
505
+ if is_test:
506
+ # Lower severity for test files
507
+ sev_weight = config.SEVERITY.get(severity, 1)
508
+ if sev_weight >= config.SEVERITY["HIGH"]:
509
+ adjusted_severity = "MEDIUM"
510
+ elif sev_weight >= config.SEVERITY["MEDIUM"]:
511
+ adjusted_severity = "LOW"
512
+
513
+ if is_env_ex:
514
+ # .env.example should have placeholders, not real values
515
+ # If pattern matches, it might be a real secret leaked into example
516
+ if not is_placeholder:
517
+ adjusted_severity = "MEDIUM" # flag but lower severity
518
+ else:
519
+ skip = True # placeholder in example = expected
520
+
521
+ if skip:
522
+ continue
523
+
524
+ findings.append({
525
+ "type": "secret",
526
+ "pattern": pattern_name,
527
+ "severity": adjusted_severity,
528
+ "file": file_str,
529
+ "line": line_num,
530
+ "snippet": _snippet(line, m.start()),
531
+ "category": file_category,
532
+ })
533
+
534
+ # --- High entropy string detection ---
535
+ # Look for quoted strings or assignment values 16+ chars
536
+ for token_match in re.finditer(r"""['"]([^'"]{16,})['\"]""", line):
537
+ if len(findings) >= max_findings:
538
+ break
539
+
540
+ token = token_match.group(1)
541
+ ent = shannon_entropy(token)
542
+
543
+ if ent > 4.5:
544
+ # Skip if already caught by pattern matching
545
+ # (crude check: see if any finding on this line already)
546
+ already_found = any(
547
+ f["file"] == file_str and f["line"] == line_num
548
+ for f in findings
549
+ )
550
+ if already_found:
551
+ continue
552
+
553
+ if is_comment or in_markdown_code_block or is_placeholder:
554
+ continue
555
+
556
+ sev = "MEDIUM"
557
+ if ent > 5.0:
558
+ sev = "HIGH"
559
+ if is_test:
560
+ sev = "LOW"
561
+
562
+ findings.append({
563
+ "type": "secret",
564
+ "pattern": "high_entropy_string",
565
+ "severity": sev,
566
+ "file": file_str,
567
+ "line": line_num,
568
+ "snippet": _redact(token),
569
+ "category": file_category,
570
+ "entropy": round(ent, 2),
571
+ })
572
+
573
+ # --- Base64-encoded secret detection ---
574
+ for b64_match in _BASE64_RE.finditer(line):
575
+ if len(findings) >= max_findings:
576
+ break
577
+
578
+ token = b64_match.group(0)
579
+ if len(token) < 20:
580
+ continue
581
+
582
+ # Skip if already caught
583
+ already_found = any(
584
+ f["file"] == file_str and f["line"] == line_num
585
+ for f in findings
586
+ )
587
+ if already_found:
588
+ continue
589
+
590
+ if is_comment or in_markdown_code_block or is_placeholder:
591
+ continue
592
+
593
+ if _check_base64_secret(token):
594
+ sev = "MEDIUM" if is_test else "HIGH"
595
+ findings.append({
596
+ "type": "secret",
597
+ "pattern": "base64_encoded_secret",
598
+ "severity": sev,
599
+ "file": file_str,
600
+ "line": line_num,
601
+ "snippet": _redact(token),
602
+ "category": file_category,
603
+ })
604
+
605
+ # --- URL with embedded credentials ---
606
+ # Already handled by pattern, but double-check for non-standard schemes
607
+ # (covered by url_embedded_credentials pattern)
608
+
609
+ # --- Hardcoded IP detection ---
610
+ for ip_match in _IP_RE.finditer(line):
611
+ if len(findings) >= max_findings:
612
+ break
613
+
614
+ ip = ip_match.group(1)
615
+ if _is_private_or_localhost(ip):
616
+ continue
617
+
618
+ # Validate it looks like a real IP (each octet 0-255)
619
+ parts = ip.split(".")
620
+ try:
621
+ if not all(0 <= int(p) <= 255 for p in parts):
622
+ continue
623
+ except ValueError:
624
+ continue
625
+
626
+ if is_comment or in_markdown_code_block:
627
+ continue
628
+
629
+ sev = "LOW"
630
+ if is_test:
631
+ continue # Skip IPs in test files entirely
632
+
633
+ findings.append({
634
+ "type": "hardcoded_ip",
635
+ "pattern": "hardcoded_public_ip",
636
+ "severity": sev,
637
+ "file": file_str,
638
+ "line": line_num,
639
+ "snippet": ip,
640
+ "category": file_category,
641
+ })
642
+
643
+ return findings
644
+
645
+
646
+ # ---------------------------------------------------------------------------
647
+ # Aggregation and scoring
648
+ # ---------------------------------------------------------------------------
649
+
650
+ SCORE_DEDUCTIONS = {
651
+ "CRITICAL": 10,
652
+ "HIGH": 5,
653
+ "MEDIUM": 2,
654
+ "LOW": 1,
655
+ "INFO": 0,
656
+ }
657
+
658
+
659
+ def aggregate_by_severity(findings: list[dict]) -> dict[str, int]:
660
+ """Count findings per severity level."""
661
+ counts: dict[str, int] = {sev: 0 for sev in config.SEVERITY}
662
+ for f in findings:
663
+ sev = f.get("severity", "INFO")
664
+ if sev in counts:
665
+ counts[sev] += 1
666
+ return counts
667
+
668
+
669
+ def aggregate_by_pattern(findings: list[dict]) -> dict[str, int]:
670
+ """Count findings per pattern type."""
671
+ counts: dict[str, int] = {}
672
+ for f in findings:
673
+ pattern = f.get("pattern", "unknown")
674
+ counts[pattern] = counts.get(pattern, 0) + 1
675
+ return counts
676
+
677
+
678
+ def aggregate_by_category(findings: list[dict]) -> dict[str, int]:
679
+ """Count findings per file category."""
680
+ counts: dict[str, int] = {}
681
+ for f in findings:
682
+ cat = f.get("category", "other")
683
+ counts[cat] = counts.get(cat, 0) + 1
684
+ return counts
685
+
686
+
687
+ def compute_score(findings: list[dict]) -> int:
688
+ """Compute a secrets score starting at 100, deducting by severity."""
689
+ score = 100
690
+ for f in findings:
691
+ deduction = SCORE_DEDUCTIONS.get(f["severity"], 0)
692
+ score -= deduction
693
+ return max(0, score)
694
+
695
+
696
+ # ---------------------------------------------------------------------------
697
+ # Report formatters
698
+ # ---------------------------------------------------------------------------
699
+
700
+ def format_text_report(
701
+ target: str,
702
+ total_files: int,
703
+ findings: list[dict],
704
+ severity_counts: dict[str, int],
705
+ pattern_counts: dict[str, int],
706
+ category_counts: dict[str, int],
707
+ score: int,
708
+ verdict: dict,
709
+ elapsed: float,
710
+ include_low: bool = False,
711
+ ) -> str:
712
+ """Build a human-readable text report grouped by severity, then file."""
713
+ lines: list[str] = []
714
+
715
+ lines.append("=" * 72)
716
+ lines.append(" 007 SECRETS SCANNER -- DEEP SCAN REPORT")
717
+ lines.append("=" * 72)
718
+ lines.append("")
719
+
720
+ # Metadata
721
+ lines.append(f" Target: {target}")
722
+ lines.append(f" Timestamp: {config.get_timestamp()}")
723
+ lines.append(f" Duration: {elapsed:.2f}s")
724
+ lines.append(f" Files scanned: {total_files}")
725
+ lines.append(f" Total findings: {len(findings)}")
726
+ lines.append("")
727
+
728
+ # Severity breakdown
729
+ lines.append("-" * 72)
730
+ lines.append(" FINDINGS BY SEVERITY")
731
+ lines.append("-" * 72)
732
+ for sev in ("CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"):
733
+ count = severity_counts.get(sev, 0)
734
+ bar = "#" * min(count, 40)
735
+ lines.append(f" {sev:<10} {count:>5} {bar}")
736
+ lines.append("")
737
+
738
+ # Pattern type breakdown
739
+ if pattern_counts:
740
+ lines.append("-" * 72)
741
+ lines.append(" FINDINGS BY TYPE")
742
+ lines.append("-" * 72)
743
+ sorted_patterns = sorted(pattern_counts.items(), key=lambda x: x[1], reverse=True)
744
+ for pattern_name, count in sorted_patterns[:20]:
745
+ lines.append(f" {pattern_name:<35} {count:>5}")
746
+ lines.append("")
747
+
748
+ # Category breakdown
749
+ if category_counts:
750
+ lines.append("-" * 72)
751
+ lines.append(" FINDINGS BY FILE CATEGORY")
752
+ lines.append("-" * 72)
753
+ sorted_cats = sorted(category_counts.items(), key=lambda x: x[1], reverse=True)
754
+ for cat_name, count in sorted_cats:
755
+ lines.append(f" {cat_name:<20} {count:>5}")
756
+ lines.append("")
757
+
758
+ # Findings grouped by severity, then by file
759
+ min_severity = config.SEVERITY["LOW"] if include_low else config.SEVERITY["MEDIUM"]
760
+
761
+ displayed = [
762
+ f for f in findings
763
+ if config.SEVERITY.get(f.get("severity", "INFO"), 0) >= min_severity
764
+ ]
765
+
766
+ if displayed:
767
+ # Group by severity
768
+ by_severity: dict[str, list[dict]] = {}
769
+ for f in displayed:
770
+ sev = f.get("severity", "INFO")
771
+ by_severity.setdefault(sev, []).append(f)
772
+
773
+ for sev in ("CRITICAL", "HIGH", "MEDIUM", "LOW"):
774
+ sev_findings = by_severity.get(sev, [])
775
+ if not sev_findings:
776
+ continue
777
+
778
+ lines.append("-" * 72)
779
+ lines.append(f" [{sev}] FINDINGS ({len(sev_findings)})")
780
+ lines.append("-" * 72)
781
+
782
+ # Sub-group by file
783
+ by_file: dict[str, list[dict]] = {}
784
+ for f in sev_findings:
785
+ by_file.setdefault(f["file"], []).append(f)
786
+
787
+ for filepath, file_findings in sorted(by_file.items()):
788
+ lines.append(f" {filepath}")
789
+ for f in sorted(file_findings, key=lambda x: x.get("line", 0)):
790
+ loc = f"L{f['line']}" if f.get("line") else ""
791
+ snippet_part = f" [{f['snippet']}]" if f.get("snippet") else ""
792
+ entropy_part = f" (entropy={f['entropy']})" if f.get("entropy") else ""
793
+ lines.append(
794
+ f" {loc:>6} {f['pattern']}{snippet_part}{entropy_part}"
795
+ )
796
+ lines.append("")
797
+ else:
798
+ lines.append(" No findings above the display threshold.")
799
+ lines.append("")
800
+
801
+ # Score and verdict
802
+ lines.append("=" * 72)
803
+ lines.append(f" SECRETS SCORE: {score} / 100")
804
+ lines.append(f" VERDICT: {verdict['emoji']} {verdict['label']}")
805
+ lines.append(f" {verdict['description']}")
806
+ lines.append("=" * 72)
807
+ lines.append("")
808
+
809
+ return "\n".join(lines)
810
+
811
+
812
+ def build_json_report(
813
+ target: str,
814
+ total_files: int,
815
+ findings: list[dict],
816
+ severity_counts: dict[str, int],
817
+ pattern_counts: dict[str, int],
818
+ category_counts: dict[str, int],
819
+ score: int,
820
+ verdict: dict,
821
+ elapsed: float,
822
+ ) -> dict:
823
+ """Build a structured JSON-serializable report dict."""
824
+ return {
825
+ "scan": "secrets_scanner",
826
+ "target": target,
827
+ "timestamp": config.get_timestamp(),
828
+ "duration_seconds": round(elapsed, 3),
829
+ "total_files_scanned": total_files,
830
+ "total_findings": len(findings),
831
+ "severity_counts": severity_counts,
832
+ "pattern_counts": pattern_counts,
833
+ "category_counts": category_counts,
834
+ "score": score,
835
+ "verdict": {
836
+ "label": verdict["label"],
837
+ "description": verdict["description"],
838
+ "emoji": verdict["emoji"],
839
+ },
840
+ "findings": findings,
841
+ }
842
+
843
+
844
+ # ---------------------------------------------------------------------------
845
+ # Main entry point
846
+ # ---------------------------------------------------------------------------
847
+
848
+ def run_scan(
849
+ target_path: str,
850
+ output_format: str = "text",
851
+ verbose: bool = False,
852
+ include_low: bool = False,
853
+ ) -> dict:
854
+ """Execute the deep secrets scan and return the report dict.
855
+
856
+ Also prints the report to stdout in the requested format.
857
+
858
+ Args:
859
+ target_path: Path to the directory to scan.
860
+ output_format: 'text' or 'json'.
861
+ verbose: Enable debug-level logging.
862
+ include_low: Include LOW severity findings in text output.
863
+
864
+ Returns:
865
+ JSON-compatible report dict.
866
+ """
867
+ if verbose:
868
+ logger.setLevel("DEBUG")
869
+
870
+ config.ensure_directories()
871
+
872
+ target = Path(target_path).resolve()
873
+ if not target.exists():
874
+ logger.error("Target path does not exist: %s", target)
875
+ sys.exit(1)
876
+ if not target.is_dir():
877
+ logger.error("Target is not a directory: %s", target)
878
+ sys.exit(1)
879
+
880
+ logger.info("Starting deep secrets scan of %s", target)
881
+ start_time = time.time()
882
+
883
+ # Collect files
884
+ files = collect_files(target)
885
+ total_files = len(files)
886
+ logger.info("Collected %d files for deep scanning", total_files)
887
+
888
+ # Scan each file
889
+ all_findings: list[dict] = []
890
+ max_report = config.LIMITS["max_report_findings"]
891
+
892
+ for fpath in files:
893
+ if len(all_findings) >= max_report:
894
+ logger.warning(
895
+ "Reached max_report_findings limit (%d). Truncating.", max_report
896
+ )
897
+ break
898
+
899
+ file_findings = scan_file(fpath, verbose=verbose)
900
+ remaining = max_report - len(all_findings)
901
+ all_findings.extend(file_findings[:remaining])
902
+
903
+ elapsed = time.time() - start_time
904
+ logger.info(
905
+ "Deep scan complete: %d files, %d findings in %.2fs",
906
+ total_files, len(all_findings), elapsed,
907
+ )
908
+
909
+ # Aggregation
910
+ severity_counts = aggregate_by_severity(all_findings)
911
+ pattern_counts = aggregate_by_pattern(all_findings)
912
+ category_counts = aggregate_by_category(all_findings)
913
+ score = compute_score(all_findings)
914
+ verdict = config.get_verdict(score)
915
+
916
+ # Audit log
917
+ config.log_audit_event(
918
+ action="secrets_scan",
919
+ target=str(target),
920
+ result=f"score={score}, findings={len(all_findings)}, verdict={verdict['label']}",
921
+ details={
922
+ "total_files": total_files,
923
+ "severity_counts": severity_counts,
924
+ "pattern_counts": pattern_counts,
925
+ "category_counts": category_counts,
926
+ "duration_seconds": round(elapsed, 3),
927
+ },
928
+ )
929
+
930
+ # Build report
931
+ report = build_json_report(
932
+ target=str(target),
933
+ total_files=total_files,
934
+ findings=all_findings,
935
+ severity_counts=severity_counts,
936
+ pattern_counts=pattern_counts,
937
+ category_counts=category_counts,
938
+ score=score,
939
+ verdict=verdict,
940
+ elapsed=elapsed,
941
+ )
942
+
943
+ # Output
944
+ if output_format == "json":
945
+ print(json.dumps(report, indent=2, ensure_ascii=False))
946
+ else:
947
+ print(format_text_report(
948
+ target=str(target),
949
+ total_files=total_files,
950
+ findings=all_findings,
951
+ severity_counts=severity_counts,
952
+ pattern_counts=pattern_counts,
953
+ category_counts=category_counts,
954
+ score=score,
955
+ verdict=verdict,
956
+ elapsed=elapsed,
957
+ include_low=include_low,
958
+ ))
959
+
960
+ return report
961
+
962
+
963
+ # ---------------------------------------------------------------------------
964
+ # CLI
965
+ # ---------------------------------------------------------------------------
966
+
967
+ if __name__ == "__main__":
968
+ parser = argparse.ArgumentParser(
969
+ description="007 Secrets Scanner -- Deep scanner for secrets and credentials.",
970
+ epilog=(
971
+ "Examples:\n"
972
+ " python secrets_scanner.py --target ./my-project\n"
973
+ " python secrets_scanner.py --target ./my-project --output json\n"
974
+ " python secrets_scanner.py --target ./my-project --verbose --include-low"
975
+ ),
976
+ formatter_class=argparse.RawDescriptionHelpFormatter,
977
+ )
978
+ parser.add_argument(
979
+ "--target",
980
+ required=True,
981
+ help="Path to the directory to scan (required).",
982
+ )
983
+ parser.add_argument(
984
+ "--output",
985
+ choices=["text", "json"],
986
+ default="text",
987
+ help="Output format: 'text' (default) or 'json'.",
988
+ )
989
+ parser.add_argument(
990
+ "--verbose",
991
+ action="store_true",
992
+ default=False,
993
+ help="Enable verbose/debug logging.",
994
+ )
995
+ parser.add_argument(
996
+ "--include-low",
997
+ action="store_true",
998
+ default=False,
999
+ help="Include LOW severity findings in text output (hidden by default).",
1000
+ )
1001
+
1002
+ args = parser.parse_args()
1003
+ run_scan(
1004
+ target_path=args.target,
1005
+ output_format=args.output,
1006
+ verbose=args.verbose,
1007
+ include_low=args.include_low,
1008
+ )