synapse-hub 2.2.4 → 2.4.1

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 (312) hide show
  1. package/.agents/agents/failure-analyst.md +11 -0
  2. package/.agents/agents/live-explorer.md +28 -0
  3. package/.agents/agents/orchestrator.md +23 -4
  4. package/.agents/agents/pom-crafter.md +15 -0
  5. package/.agents/agents/quality-gatekeeper.md +20 -4
  6. package/.agents/agents/test-runner.md +12 -0
  7. package/.agents/rules/communication-style.md +40 -0
  8. package/.agents/rules/context-engineering.md +23 -0
  9. package/.agents/rules/git-push-protocol.md +10 -16
  10. package/.agents/rules/graphify.md +14 -14
  11. package/.agents/rules/mandatory-playwright-live.md +29 -29
  12. package/.agents/rules/mobile_appium_rules.md +132 -0
  13. package/.agents/rules/performance_testing_rules.md +74 -0
  14. package/.agents/rules/systematic-debugging.md +6 -6
  15. package/.agents/skills/agent-orchestration-multi-agent-optimize/SKILL.md +64 -215
  16. package/.agents/skills/agent-orchestration-multi-agent-optimize/examples/swarm_workload_config.json +12 -0
  17. package/.agents/skills/antigravity-test-sentinel/SKILL.md +3 -3
  18. package/.agents/skills/antigravity-test-sentinel/examples/dod_gate_policy.json +11 -0
  19. package/.agents/skills/api-network-mocking-interception/SKILL.md +1 -1
  20. package/.agents/skills/api-network-mocking-interception/examples/network_route_stub.json +9 -0
  21. package/.agents/skills/api-spec-to-e2e-matrix/SKILL.md +9 -9
  22. package/.agents/skills/api-spec-to-e2e-matrix/examples/sample_openapi.json +19 -0
  23. package/.agents/skills/architecture-decision-records/SKILL.md +35 -412
  24. package/.agents/skills/architecture-decision-records/examples/0001-record-architecture-decisions.md +12 -0
  25. package/.agents/skills/auth-session-and-cookie-manager/SKILL.md +1 -1
  26. package/.agents/skills/auth-session-and-cookie-manager/examples/storage_state_sample.json +11 -0
  27. package/.agents/skills/backend-load-and-stress-patterns/SKILL.md +80 -186
  28. package/.agents/skills/backend-load-and-stress-patterns/examples/k6_load_test_sample.js +13 -0
  29. package/.agents/skills/bats-testing-patterns/SKILL.md +1 -1
  30. package/.agents/skills/bats-testing-patterns/examples/cli_runner_test.bats +6 -0
  31. package/.agents/skills/bats-testing-patterns/resources/implementation-playbook.md +614 -614
  32. package/.agents/skills/clean-pom-crafter/SKILL.md +21 -3
  33. package/.agents/skills/clean-pom-crafter/examples/standard_test_case.groovy +28 -28
  34. package/.agents/skills/clean-pom-crafter/references/pom_patterns.groovy +138 -138
  35. package/.agents/skills/code-refactoring-refactor-clean/SKILL.md +2 -2
  36. package/.agents/skills/code-refactoring-refactor-clean/examples/refactored_page_sample.groovy +9 -0
  37. package/.agents/skills/db-state-verifier-and-seeder/SKILL.md +5 -5
  38. package/.agents/skills/db-state-verifier-and-seeder/examples/db_fixture_seed.json +12 -0
  39. package/.agents/skills/e2e-testing-patterns/SKILL.md +1 -1
  40. package/.agents/skills/e2e-testing-patterns/examples/playwright_sharded_config.ts +12 -0
  41. package/.agents/skills/e2e-testing-patterns/resources/implementation-playbook.md +522 -522
  42. package/.agents/skills/failure-autopsy-and-healing/SKILL.md +17 -3
  43. package/.agents/skills/failure-autopsy-and-healing/examples/sample_junit_failure.xml +8 -0
  44. package/.agents/skills/failure-autopsy-and-healing/references/healing_matrix.md +64 -64
  45. package/.agents/skills/failure-autopsy-and-healing/references/qa_investigation_protocol.md +56 -56
  46. package/.agents/skills/failure-autopsy-and-healing/scripts/triage_failures.sh +98 -98
  47. package/.agents/skills/file-and-report-validator/SKILL.md +1 -1
  48. package/.agents/skills/file-and-report-validator/examples/sample_export_schema.json +14 -0
  49. package/.agents/skills/flaky-test-sentinel-and-quarantine/SKILL.md +3 -3
  50. package/.agents/skills/flaky-test-sentinel-and-quarantine/examples/quarantine_registry.json +11 -0
  51. package/.agents/skills/gherkin-to-suite/SKILL.md +103 -0
  52. package/.agents/skills/gherkin-to-suite/examples/checkout.feature +39 -0
  53. package/.agents/skills/gherkin-to-suite/examples/suite_report_sample.json +334 -0
  54. package/.agents/skills/github-actions-templates/SKILL.md +47 -304
  55. package/.agents/skills/github-actions-templates/examples/matrix_test_pipeline.yml +8 -0
  56. package/.agents/skills/graphify-impact-analyzer/SKILL.md +6 -6
  57. package/.agents/skills/graphify-impact-analyzer/examples/dependency_blast_radius.json +8 -0
  58. package/.agents/skills/katalon-cli-runner/SKILL.md +2 -2
  59. package/.agents/skills/katalon-cli-runner/examples/headless_kre_exec.sh +3 -0
  60. package/.agents/skills/katalon-cli-runner/references/groovy-patterns.md +209 -209
  61. package/.agents/skills/katalon-cli-runner/references/katalon-api.md +155 -155
  62. package/.agents/skills/katalon-cli-runner/references/runner_commands.md +67 -67
  63. package/.agents/skills/lam-script-generator/SKILL.md +42 -1
  64. package/.agents/skills/lam-script-generator/examples/user_story_sample.md +8 -0
  65. package/.agents/skills/lean-code-ladder/SKILL.md +83 -0
  66. package/.agents/skills/lean-code-ladder/examples/ladder_walkthrough.md +37 -0
  67. package/.agents/skills/meta-prompt-crafter/SKILL.md +107 -0
  68. package/.agents/skills/meta-prompt-crafter/examples/qa_prompt_template.md +35 -0
  69. package/.agents/skills/persistent-planning-files/SKILL.md +108 -0
  70. package/.agents/skills/persistent-planning-files/examples/task_plan_example.md +56 -0
  71. package/.agents/skills/playwright-live-interaction/SKILL.md +49 -1
  72. package/.agents/skills/playwright-live-interaction/examples/live_probe_script.js +5 -0
  73. package/.agents/skills/playwright-live-interaction/references/advanced_superpowers.md +160 -160
  74. package/.agents/skills/playwright-live-interaction/references/compact_accessibility_snapshots.md +75 -75
  75. package/.agents/skills/playwright-live-interaction/references/recipes.md +388 -388
  76. package/.agents/skills/playwright-live-interaction/references/swarm_orchestration.md +134 -134
  77. package/.agents/skills/playwright-webapp-testing/SKILL.md +93 -0
  78. package/.agents/skills/playwright-webapp-testing/examples/server_lifecycle_playwright.py +63 -0
  79. package/.agents/skills/rbt-manual-testing/SKILL.md +150 -0
  80. package/.agents/skills/rbt-manual-testing/examples/checkout_rbt_manual_suite.md +25 -0
  81. package/.agents/skills/security-dast-sentinel/SKILL.md +54 -135
  82. package/.agents/skills/security-dast-sentinel/examples/dast_scan_config.json +10 -0
  83. package/.agents/skills/self-healing-locator/SKILL.md +24 -10
  84. package/.agents/skills/self-healing-locator/examples/aom_diff_sample.json +16 -0
  85. package/.agents/skills/skillspector-security-guard/SKILL.md +32 -90
  86. package/.agents/skills/skillspector-security-guard/examples/safe_mcp_config.json +12 -0
  87. package/.agents/skills/surgical-coding-principles/SKILL.md +89 -0
  88. package/.agents/skills/surgical-coding-principles/examples/surgical_vs_scope_creep.md +46 -0
  89. package/.agents/skills/synthetic-test-data-generator/SKILL.md +3 -3
  90. package/.agents/skills/synthetic-test-data-generator/examples/idempotent_data_recipe.json +9 -0
  91. package/.agents/skills/systematic-debugging/SKILL.md +100 -0
  92. package/.agents/skills/systematic-debugging/examples/four_phase_case.md +44 -0
  93. package/.agents/skills/test-driven-development/SKILL.md +114 -0
  94. package/.agents/skills/test-driven-development/examples/red_green_refactor_qa.md +78 -0
  95. package/.agents/skills/token-and-context-optimizer/SKILL.md +1 -1
  96. package/.agents/skills/token-and-context-optimizer/examples/distilled_log_sample.txt +3 -0
  97. package/.agents/skills/verification-before-completion/SKILL.md +100 -0
  98. package/.agents/skills/verification-before-completion/examples/evidence_log_example.md +46 -0
  99. package/.agents/skills/visual-regression-sentinel/SKILL.md +21 -1
  100. package/.agents/skills/visual-regression-sentinel/examples/visual_mask_rules.json +9 -0
  101. package/.agents/skills/wcag-audit-patterns/SKILL.md +3 -3
  102. package/.agents/skills/wcag-audit-patterns/examples/wcag_compliance_checklist.json +10 -0
  103. package/.agents/skills/wcag-audit-patterns/resources/implementation-playbook.md +541 -541
  104. package/.agents/skills/web-vitals-and-performance-profiler/SKILL.md +1 -1
  105. package/.agents/skills/web-vitals-and-performance-profiler/examples/cwv_budget_policy.json +6 -0
  106. package/.agents/templates/completion_report.md +57 -57
  107. package/.agents/workflows/analyze_requirement_document.md +18 -0
  108. package/.agents/workflows/export_testcases_excel.md +32 -0
  109. package/.agents/workflows/generate_combinatorial_test_data.md +28 -0
  110. package/.agents/workflows/generate_cross_module_test_plan.md +37 -0
  111. package/.agents/workflows/generate_manual_testcases_rbt.md +56 -0
  112. package/.agents/workflows/generate_requirements_from_website.md +25 -0
  113. package/.agents/workflows/generate_testcases_from_requirements.md +21 -0
  114. package/.agents/workflows/graphify.md +10 -10
  115. package/.agents/workflows/verify.md +2 -2
  116. package/.cursor/rules/language.mdc +7 -7
  117. package/.cursor/rules/synapse-qa.mdc +23 -0
  118. package/.cursorrules +7 -0
  119. package/.pre-commit-hooks.yaml +14 -0
  120. package/.windsurfrules +7 -0
  121. package/AGENTS.md +54 -15
  122. package/CLAUDE.md +35 -12
  123. package/LICENSE +30 -0
  124. package/NOTICE +180 -0
  125. package/README.en.md +331 -0
  126. package/README.md +252 -515
  127. package/action.yml +51 -0
  128. package/benchmarks/qa_eval/README.md +71 -0
  129. package/benchmarks/qa_eval/build_dataset.py +677 -0
  130. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-06_api_contract_drift.json +22 -0
  131. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-07_api_contract_drift.json +22 -0
  132. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-08_api_contract_drift.json +22 -0
  133. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-09_api_contract_drift.json +22 -0
  134. package/benchmarks/qa_eval/dataset/api_contract_drift/qa-challenge-10_api_contract_drift.json +22 -0
  135. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-21_fake_pass_tautology.json +21 -0
  136. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-22_fake_pass_tautology.json +21 -0
  137. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-23_fake_pass_tautology.json +21 -0
  138. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-24_fake_pass_tautology.json +21 -0
  139. package/benchmarks/qa_eval/dataset/fake_pass_tautology/qa-challenge-25_fake_pass_tautology.json +21 -0
  140. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-01_locator_drift.json +23 -0
  141. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-02_locator_drift.json +23 -0
  142. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-03_locator_drift.json +23 -0
  143. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-04_locator_drift.json +23 -0
  144. package/benchmarks/qa_eval/dataset/locator_drift/qa-challenge-05_locator_drift.json +23 -0
  145. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-16_stale_and_mutation.json +22 -0
  146. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-17_stale_and_mutation.json +22 -0
  147. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-18_stale_and_mutation.json +22 -0
  148. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-19_stale_and_mutation.json +22 -0
  149. package/benchmarks/qa_eval/dataset/stale_and_mutation/qa-challenge-20_stale_and_mutation.json +23 -0
  150. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-11_timing_and_race.json +22 -0
  151. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-12_timing_and_race.json +22 -0
  152. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-13_timing_and_race.json +22 -0
  153. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-14_timing_and_race.json +22 -0
  154. package/benchmarks/qa_eval/dataset/timing_and_race/qa-challenge-15_timing_and_race.json +22 -0
  155. package/benchmarks/qa_eval/eval_runner.py +418 -0
  156. package/benchmarks/qa_eval/reports/AB_METHODOLOGY_REPORT.md +83 -0
  157. package/benchmarks/qa_eval/reports/BENCHMARK_REPORT.md +69 -0
  158. package/benchmarks/qa_eval/reports/eval_results.json +105 -0
  159. package/bin/agent-qa.js +16 -4
  160. package/docs/ARCHITECTURE.md +107 -0
  161. package/docs/CI_CD_INTEGRATION.md +94 -0
  162. package/docs/CLI_REFERENCE.md +157 -0
  163. package/docs/CONTRIBUTING.md +91 -0
  164. package/docs/PLUGIN_DEVELOPMENT_GUIDE.md +126 -0
  165. package/docs/TOKEN_QUOTA_PLAYBOOK.md +41 -0
  166. package/docs/adopted-ecosystem-tools.md +87 -0
  167. package/docs/schemas/synapse.config.schema.json +64 -0
  168. package/examples/cypress-ecommerce-suite/README.md +45 -0
  169. package/examples/cypress-ecommerce-suite/cypress/e2e/checkout.cy.js +17 -0
  170. package/examples/cypress-ecommerce-suite/cypress/pages/BasePage.js +29 -0
  171. package/examples/cypress-ecommerce-suite/cypress/pages/CartPage.js +51 -0
  172. package/examples/cypress-ecommerce-suite/cypress.config.js +13 -0
  173. package/examples/cypress-ecommerce-suite/package.json +13 -0
  174. package/examples/katalon-suite/Keywords/pages/BasePage.groovy +19 -0
  175. package/examples/katalon-suite/Keywords/pages/DashboardPage.groovy +29 -0
  176. package/examples/katalon-suite/Keywords/pages/LoginPage.groovy +45 -0
  177. package/examples/katalon-suite/Profiles/default.glbl +12 -0
  178. package/examples/katalon-suite/README.md +18 -0
  179. package/examples/katalon-suite/Scripts/Auth/TC_LoginSuccess/Script1234567890.groovy +12 -0
  180. package/examples/katalon-suite/Scripts/Dashboard/TC_ViewSummary/Script1234567891.groovy +15 -0
  181. package/examples/katalon-suite/katalon_suite.prj +21 -0
  182. package/examples/playwright-crm-suite/README.md +40 -0
  183. package/examples/playwright-crm-suite/package.json +14 -0
  184. package/examples/playwright-crm-suite/pages/BasePage.ts +37 -0
  185. package/examples/playwright-crm-suite/pages/CustomerPage.ts +57 -0
  186. package/examples/playwright-crm-suite/pages/LoginPage.ts +32 -0
  187. package/examples/playwright-crm-suite/playwright.config.ts +27 -0
  188. package/examples/playwright-crm-suite/tests/auth.spec.ts +22 -0
  189. package/examples/playwright-crm-suite/tests/customers.spec.ts +33 -0
  190. package/examples/plugins/sample-custom-plugin/README.md +14 -0
  191. package/examples/plugins/sample-custom-plugin/plugin.json +16 -0
  192. package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/SKILL.md +29 -0
  193. package/examples/plugins/sample-custom-plugin/skills/custom-security-headers-audit/examples/audit_fixture.json +8 -0
  194. package/examples/plugins/sample-custom-plugin/tools/custom_security_headers.py +125 -0
  195. package/examples/practices/README.md +22 -0
  196. package/examples/practices/requirements/crm/requirements_contracts.md +292 -0
  197. package/examples/practices/requirements/crm/requirements_customers.md +462 -0
  198. package/examples/practices/requirements/crm/requirements_login.md +202 -0
  199. package/examples/practices/requirements/crm/requirements_projects.md +396 -0
  200. package/examples/practices/requirements/crm/requirements_tasks.md +343 -0
  201. package/examples/practices/requirements/ecommerce/requirements_category_brand.txt +402 -0
  202. package/examples/practices/requirements/jira/KAN-5/KAN-1/2026-04-03_04-28-27.png +0 -0
  203. package/examples/practices/requirements/jira/KAN-5/KAN-1/KAN-1_requirement.md +52 -0
  204. package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-19.png +0 -0
  205. package/examples/practices/requirements/jira/KAN-5/KAN-4/2026-04-03_04-28-57.png +0 -0
  206. package/examples/practices/requirements/jira/KAN-5/KAN-4/KAN-4_requirement.md +119 -0
  207. package/examples/practices/requirements/jira/KAN-5/KAN-5_overview.md +9 -0
  208. package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-08.png +0 -0
  209. package/examples/practices/requirements/jira/KAN-5/KAN-6/2026-04-03_05-58-23.png +0 -0
  210. package/examples/practices/requirements/jira/KAN-5/KAN-6/KAN-6_requirement.md +32 -0
  211. package/examples/practices/testcases/api/book_management_api_test_cases.md +144 -0
  212. package/examples/practices/testcases/api/user_management_api_test_cases.md +139 -0
  213. package/examples/practices/testcases/crm/test_cases_crm_login.md +148 -0
  214. package/examples/practices/testcases/crm/test_cases_customers.md +156 -0
  215. package/examples/pytest-api-suite/README.md +45 -0
  216. package/examples/pytest-api-suite/conftest.py +31 -0
  217. package/examples/pytest-api-suite/pages/user_api_client.py +36 -0
  218. package/examples/pytest-api-suite/requirements.txt +2 -0
  219. package/examples/pytest-api-suite/tests/test_user_api.py +44 -0
  220. package/examples/selenium-suite/README.md +18 -0
  221. package/examples/selenium-suite/pages/base_page.py +27 -0
  222. package/examples/selenium-suite/pages/login_page.py +29 -0
  223. package/examples/selenium-suite/pages/order_page.py +25 -0
  224. package/examples/selenium-suite/pytest.ini +6 -0
  225. package/examples/selenium-suite/requirements.txt +2 -0
  226. package/examples/selenium-suite/tests/test_login.py +10 -0
  227. package/examples/selenium-suite/tests/test_order.py +10 -0
  228. package/package.json +27 -12
  229. package/scripts/generate_skill_examples.py +244 -0
  230. package/scripts/install.ps1 +112 -0
  231. package/scripts/install_global_plugin.ps1 +94 -0
  232. package/scripts/install_global_plugin.sh +49 -0
  233. package/scripts/lint_skills.ps1 +57 -0
  234. package/scripts/package_dist.py +323 -0
  235. package/scripts/setup.sh +59 -0
  236. package/scripts/update_hub.ps1 +19 -0
  237. package/scripts/update_hub.sh +15 -0
  238. package/skills_manifest.json +209 -6
  239. package/synapse.config.json +30 -0
  240. package/templates/PROJECT_AI_GUIDE_TEMPLATE.md +7 -7
  241. package/templates/PROJECT_STATE_TEMPLATE.md +1 -1
  242. package/templates/ai_bridges/CLAUDE.md +33 -16
  243. package/templates/ai_bridges/copilot-instructions.md +14 -21
  244. package/templates/ai_bridges/cursor_synapse_qa.mdc +17 -20
  245. package/templates/ai_bridges/windsurfrules +21 -21
  246. package/templates/ci/github-actions-synapse.yml +71 -4
  247. package/templates/welcome.html.tmpl +1674 -0
  248. package/tools/.infer_baseline.json +1102 -0
  249. package/tools/TOOLS_RUNBOOK.md +98 -131
  250. package/tools/__init__.py +26 -0
  251. package/tools/agent_canary.py +501 -501
  252. package/tools/analyze_project.py +31 -17
  253. package/tools/api_fuzzer.py +278 -6
  254. package/tools/author_integrity.py +6 -6
  255. package/tools/auto_heal.py +1 -1
  256. package/tools/browser_helpers.js +105 -7
  257. package/tools/bug_reporter.py +31 -1
  258. package/tools/bump_version.py +30 -3
  259. package/tools/cache_resolver.py +248 -14
  260. package/tools/check_env.py +55 -14
  261. package/tools/check_version_sync.py +171 -0
  262. package/tools/ci_healing_hook.py +1 -1
  263. package/tools/cli.py +454 -42
  264. package/tools/count_tokens.py +1257 -0
  265. package/tools/dashboard_generator.py +2638 -0
  266. package/tools/db_sandbox.py +198 -20
  267. package/tools/e2e_flow_prober.ps1 +12 -64
  268. package/tools/e2e_flow_prober.py +99 -0
  269. package/tools/e2e_flow_prober.sh +4 -0
  270. package/tools/flaky_detector.py +4 -3
  271. package/tools/generate_allure_report.ps1 +12 -29
  272. package/tools/generate_allure_report.py +77 -0
  273. package/tools/generate_allure_report.sh +4 -0
  274. package/tools/generate_jmx_plan.py +257 -58
  275. package/tools/generate_test_reports.ps1 +12 -134
  276. package/tools/generate_test_reports.py +133 -0
  277. package/tools/generate_test_reports.sh +4 -0
  278. package/tools/gherkin_suite.py +758 -0
  279. package/tools/git_pre_commit_guard.py +12 -6
  280. package/tools/goldfish_review.py +366 -0
  281. package/tools/heal_pr_automator.py +1 -1
  282. package/tools/heuristic_locator_matcher.py +170 -21
  283. package/tools/infer_linter.py +612 -70
  284. package/tools/lint_ai_docs.py +64 -0
  285. package/tools/matrix_sharder.py +1 -1
  286. package/tools/mcp_excel_server.ps1 +12 -250
  287. package/tools/mcp_excel_server.py +249 -0
  288. package/tools/mcp_excel_server.sh +4 -0
  289. package/tools/mcp_server.py +368 -144
  290. package/tools/md_to_xlsx.py +307 -0
  291. package/tools/micro_mutator.py +302 -69
  292. package/tools/omni_transpiler.py +2 -2
  293. package/tools/output_formatter.py +128 -0
  294. package/tools/pinpoint_failure.py +88 -88
  295. package/tools/project_state.py +2 -2
  296. package/tools/repo_map.py +1 -1
  297. package/tools/safety_firewall.py +196 -45
  298. package/tools/setup_project.py +295 -31
  299. package/tools/skillspector.py +31 -0
  300. package/tools/spec_drift_sentinel.py +439 -0
  301. package/tools/sync_agent_rules.py +248 -0
  302. package/tools/telemetry_correlator.py +45 -0
  303. package/tools/test_impact.py +51 -12
  304. package/tools/tms_sync_bridge.py +21 -5
  305. package/tools/universal_ir.py +3 -0
  306. package/tools/update_sentinel.py +87 -15
  307. package/tools/verify_all.ps1 +13 -0
  308. package/tools/verify_all.py +479 -85
  309. package/tools/visual_diff_canvas.py +1 -0
  310. package/tools/visual_som_helper.py +1 -1
  311. package/tools/watch_mode.py +305 -0
  312. package/tools/welcome_page.py +291 -0
@@ -10,7 +10,7 @@ Executes in sequence:
10
10
  2. Page Class & AST Repository Mapping (tools/repo_map.py)
11
11
  3. Static Defect & Fake-Pass Sentinel (tools/infer_linter.py)
12
12
  4. Data Flow Lifecycle & Teardown Audit (tools/trace_data_flow.py)
13
- 5. Definition of Done 7 Quality Gates (tools/verify_all.py)
13
+ 5. Definition of Done 9 Quality Gates (tools/verify_all.py)
14
14
 
15
15
  Generates & Updates In-Place inside the Target Project:
16
16
  - PROJECT_HEALTH_REPORT.md (Root & reports/ of target project)
@@ -116,23 +116,37 @@ def run_full_analysis(target_repo):
116
116
 
117
117
  # Step 3: Static Defect & Fake-Pass Sentinel
118
118
  print(f"\n{BOLD}[3/5] Scanning Static Defects & Fake-Passes (infer_linter)...{RESET}")
119
- scripts_dir = os.path.join(target_repo, "Scripts")
120
- try:
121
- linter_code = infer_linter.run_infer_linter(target_dir=scripts_dir)
122
- summary["infer_linter"]["exit_code"] = linter_code
123
- except Exception as e:
124
- print(f" ⚠️ infer_linter error: {e}")
119
+ if mode == "HUB":
120
+ print(f" {GREEN}[PASS] Hub Mode: Repository contains QA Hub assets; Static Defect Sentinel applies to target test suites.{RESET}")
121
+ summary["infer_linter"]["exit_code"] = 0
122
+ else:
123
+ scripts_dir = os.path.join(target_repo, "Scripts")
124
+ if not os.path.isdir(scripts_dir):
125
+ for cand in ["tests", "test", "e2e", "specs", "cypress"]:
126
+ cpath = os.path.join(target_repo, cand)
127
+ if os.path.isdir(cpath):
128
+ scripts_dir = cpath
129
+ break
130
+ try:
131
+ linter_code = infer_linter.run_infer_linter(target_dir=scripts_dir)
132
+ summary["infer_linter"]["exit_code"] = linter_code
133
+ except Exception as e:
134
+ print(f" ⚠️ infer_linter error: {e}")
125
135
 
126
136
  # Step 4: Data Flow Lifecycle & Teardown Hygiene
127
137
  print(f"\n{BOLD}[4/5] Auditing Synthetic Data Lifecycle & Teardown (trace_data_flow)...{RESET}")
128
- try:
129
- data_flow_code = trace_data_flow.run_data_flow_tracer(target_dir=scripts_dir, repo_root=target_repo)
130
- summary["data_flow"]["exit_code"] = data_flow_code
131
- except Exception as e:
132
- print(f" ⚠️ trace_data_flow error: {e}")
138
+ if mode == "HUB":
139
+ print(f" {GREEN}[PASS] Hub Mode: Repository contains QA Hub assets; Data Flow Tracer applies to target test suites.{RESET}")
140
+ summary["data_flow"]["exit_code"] = 0
141
+ else:
142
+ try:
143
+ data_flow_code = trace_data_flow.run_data_flow_tracer(target_dir=scripts_dir, repo_root=target_repo)
144
+ summary["data_flow"]["exit_code"] = data_flow_code
145
+ except Exception as e:
146
+ print(f" ⚠️ trace_data_flow error: {e}")
133
147
 
134
- # Step 5: 7 Quality Gates DoD Verification
135
- print(f"\n{BOLD}[5/5] Verifying 7 Quality Gates DoD (verify_all)...{RESET}")
148
+ # Step 5: 9 Quality Gates DoD Verification
149
+ print(f"\n{BOLD}[5/5] Verifying 9 Quality Gates DoD (verify_all)...{RESET}")
136
150
  gates_passed = True
137
151
  gates = [
138
152
  ("Gate 1: UTF-8 No BOM", verify_all.gate_1_bom_check),
@@ -172,7 +186,7 @@ def run_full_analysis(target_repo):
172
186
  | **Page Classes & Helpers** | `{summary['repo_map'].get('classes_count', 0)}` classes (`{summary['repo_map'].get('methods_count', 0)}` methods) | ✅ INDEXED |
173
187
  | **Static Defect Sentinel** | `infer_linter` exit `{summary['infer_linter'].get('exit_code', 0)}` | {'✅ CLEAN' if summary['infer_linter'].get('exit_code', 0) == 0 else '❌ CRITICAL'} |
174
188
  | **Lifecycle & Teardown** | `trace_data_flow` exit `{summary['data_flow'].get('exit_code', 0)}` | ✅ AUDITED |
175
- | **7 Quality Gates DoD** | `{summary['gates_status']}` | {'✅ 100% PASS' if gates_passed else '❌ BLOCKED'} |
189
+ | **9 Quality Gates DoD** | `{summary['gates_status']}` | {'✅ 100% PASS' if gates_passed else '❌ BLOCKED'} |
176
190
 
177
191
  ---
178
192
 
@@ -193,7 +207,7 @@ def run_full_analysis(target_repo):
193
207
  > 1. `PROJECT_HEALTH_REPORT.md` (immediate architecture status, warnings, and DoD readiness).
194
208
  > 2. `repo_map.txt` (instant lookup for Page Classes, methods, and parameters in < 600 tokens).
195
209
  > - **Post-Audit Git Push (Mandatory):** Once analysis completes, stage, commit, and push updated in-project artifacts to git (`git add PROJECT_HEALTH_REPORT.md repo_map.txt && git commit -m "docs(audit): update project health report and AST repo map" && git push origin <branch>`).
196
- > - Whenever the user requests **"analyze project"** or **"project audit"**, re-run `agent-qa analyze` to update this file in-place.
210
+ > - Whenever the user requests **"analyze project"** or **"project audit"**, re-run `npx synapse-hub analyze` to update this file in-place.
197
211
  """
198
212
  with open(report_in_reports, "w", encoding="utf-8") as f:
199
213
  f.write(md_content)
@@ -211,7 +225,7 @@ def run_full_analysis(target_repo):
211
225
  print(f"{BOLD}{CYAN}======================================================================{RESET}")
212
226
  print(f" Elapsed Time : {elapsed:.2f}s")
213
227
  print(f" Page Classes Indexed : {summary['repo_map'].get('classes_count', 0)} classes")
214
- print(f" 7 Quality Gates DoD : {GREEN + '100% PASS' if gates_passed else RED + 'FAILED'}{RESET}")
228
+ print(f" 9 Quality Gates DoD : {GREEN + '100% PASS' if gates_passed else RED + 'FAILED'}{RESET}")
215
229
  print(f" In-Project Report Path : {report_in_root if os.path.abspath(target_repo).lower() != os.path.abspath(HUB_ROOT).lower() else report_in_reports}")
216
230
  if os.path.abspath(target_repo).lower() != os.path.abspath(HUB_ROOT).lower():
217
231
  print(f" {YELLOW}💡 Post-Analysis Action: Push updated files to Git:{RESET}")
@@ -61,6 +61,26 @@ STACKTRACE_LEAK_PATTERNS = [
61
61
  re.compile(r'ODBC SQL Server Driver', re.IGNORECASE),
62
62
  ]
63
63
 
64
+ # Severity taxonomy (concept borrowed from OWASP ZAP's alert risk levels,
65
+ # Apache-2.0 - re-expressed for the fuzz oracle). Each alarm class maps to a
66
+ # fixed risk level so CI can gate on CRITICAL findings alone.
67
+ ALARM_SEVERITY = {
68
+ "HTTP_": "HIGH", # HTTP_500/502/503_SERVER_CRASH
69
+ "STACKTRACE_LEAK": "CRITICAL",
70
+ "SLA_TIMEOUT": "LOW",
71
+ "AUTH_BYPASS": "CRITICAL",
72
+ "CONNECTION_ERROR": "MEDIUM",
73
+ }
74
+ SEVERITY_LEVELS = ["INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"]
75
+
76
+
77
+ def classify_alarm_severity(alarm: str) -> str:
78
+ """Maps an alarm string onto the ZAP-style severity taxonomy."""
79
+ for prefix, severity in ALARM_SEVERITY.items():
80
+ if alarm.startswith(prefix):
81
+ return severity
82
+ return "INFO"
83
+
64
84
  GREEN = "\033[92m"
65
85
  RED = "\033[91m"
66
86
  YELLOW = "\033[93m"
@@ -79,21 +99,108 @@ class ApiFuzzer:
79
99
  headers: Optional[Dict[str, str]] = None,
80
100
  timeout_sec: float = 10.0,
81
101
  sla_ms: float = 3000.0,
82
- verify_ssl: bool = False
102
+ verify_ssl: bool = True,
103
+ insecure: bool = False
83
104
  ):
84
105
  self.base_url = base_url.rstrip("/")
85
106
  self.spec_path = spec_path_or_url
86
107
  self.custom_headers = headers or {"Content-Type": "application/json", "User-Agent": "AgentQA-Fuzzer/2.0"}
87
108
  self.timeout_sec = timeout_sec
88
109
  self.sla_ms = sla_ms
89
- self.verify_ssl = verify_ssl
90
-
91
- # Prepare SSL context (support self-signed staging certificates)
110
+ self.insecure = insecure or (not verify_ssl)
111
+ self.verify_ssl = not self.insecure
112
+
113
+ # Environment Allowlist Guard (F2 Safety Discipline)
114
+ # Prevents accidental fuzzing or stress bombardment against production environments
115
+ parsed = urllib.parse.urlparse(self.base_url)
116
+ host = (parsed.hostname or "").lower().strip()
117
+ is_safe_env = self._is_safe_non_prod_host(host)
118
+ if host and not is_safe_env and not os.environ.get("SYNAPSE_ALLOW_PROD_FUZZ"):
119
+ raise PermissionError(
120
+ f"[SAFETY FIREWALL - REJECTED] Target host '{host}' is not in the allowed non-production environment allowlist "
121
+ "(localhost, private IP, .local/.internal, or verified dev/staging/qa/test subdomain). "
122
+ "Set SYNAPSE_ALLOW_PROD_FUZZ=1 only if authorized."
123
+ )
124
+
125
+ # Prepare SSL context (support self-signed staging certificates via explicit --insecure flag)
92
126
  self.ssl_context = ssl.create_default_context()
93
- if not verify_ssl:
127
+ if self.insecure:
128
+ print(" ⚠️ [SECURITY NOTICE] TLS certificate verification is explicitly disabled (--insecure).")
94
129
  self.ssl_context.check_hostname = False
95
130
  self.ssl_context.verify_mode = ssl.CERT_NONE
96
131
 
132
+ @staticmethod
133
+ def _is_safe_non_prod_host(host: str) -> bool:
134
+ """
135
+ Validates whether a hostname strictly belongs to a local, private, or non-production environment.
136
+ Prevents substring false positives on production domains like 'devex.com', 'contest.gov', or 'attestation-bank.com'.
137
+ """
138
+ if not host:
139
+ return False
140
+
141
+ host = host.lower().strip()
142
+
143
+ # 0. User-specified explicit host allowlist via SYNAPSE_ALLOWED_HOSTS
144
+ allowed_env = os.environ.get("SYNAPSE_ALLOWED_HOSTS", "").strip()
145
+ if allowed_env:
146
+ for item in allowed_env.split(","):
147
+ pattern = item.strip().lower()
148
+ if pattern:
149
+ if pattern.startswith("*.") and (host.endswith(pattern[1:]) or host == pattern[2:]):
150
+ return True
151
+ if host == pattern:
152
+ return True
153
+
154
+ # 1. Exact loopback / special names
155
+ if host in ("localhost", "127.0.0.1", "::1", "0.0.0.0"):
156
+ return True
157
+
158
+ # 2. Check IP address (loopback or private RFC 1918, disallowing link-local metadata like 169.254.169.254)
159
+ try:
160
+ import ipaddress
161
+ ip = ipaddress.ip_address(host)
162
+ if ip.is_link_local or ip in ipaddress.ip_network("169.254.0.0/16"):
163
+ return False
164
+ return ip.is_loopback or ip.is_private
165
+ except ValueError:
166
+ pass
167
+
168
+ # 3. Special-use TLDs (RFC 6761 / RFC 2606)
169
+ if any(host.endswith(tld) for tld in (".local", ".localhost", ".internal", ".test")):
170
+ return True
171
+
172
+ labels = host.split('.')
173
+
174
+ # 4. Reject production markers anywhere in hostname (e.g. prod-test.bank.com)
175
+ if any(re.search(r'(?:^|[-_])(prod|production)(?:$|[-_])', lbl) for lbl in labels):
176
+ return False
177
+
178
+ # 5. Prevent domain spoofing (e.g. bank.com.test.evil.io where public TLD appears in subdomains)
179
+ common_public_tlds = {
180
+ 'com', 'org', 'net', 'edu', 'gov', 'mil', 'io', 'co', 'vn', 'uk', 'de',
181
+ 'fr', 'jp', 'cn', 'ru', 'br', 'in', 'ca', 'au', 'info', 'biz'
182
+ }
183
+ if any(lbl in common_public_tlds for lbl in labels[:-2]):
184
+ return False
185
+
186
+ # 6. Apex domains (e.g. bank-test.com, test.com) cannot be assumed non-prod without explicit config
187
+ if len(labels) < 3:
188
+ return False
189
+
190
+ # 7. Subdomain verification (must be in leading subdomains preceding the root domain)
191
+ subdomain_labels = labels[:-2]
192
+ safe_exact_tokens = {"dev", "development", "staging", "qa", "test", "sandbox", "uat", "local", "internal"}
193
+ safe_sub_regex = re.compile(
194
+ r"^(dev|development|staging|qa|test|sandbox|uat)(?:[-_][a-zA-Z0-9]+)*$|^(?:[a-zA-Z0-9]+[-_])+(dev|development|staging|qa|test|sandbox|uat)(?:[-_][a-zA-Z0-9]+)*$",
195
+ re.IGNORECASE
196
+ )
197
+
198
+ for sub in subdomain_labels:
199
+ if sub in safe_exact_tokens or safe_sub_regex.match(sub):
200
+ return True
201
+
202
+ return False
203
+
97
204
  def load_spec(self) -> Dict[str, Any]:
98
205
  """Loads OpenAPI or Swagger specification from file or URL."""
99
206
  if not self.spec_path:
@@ -275,6 +382,29 @@ class ApiFuzzer:
275
382
  "headers": mut_headers
276
383
  })
277
384
 
385
+ # 4. Catalog-Driven Vectors via tools/fuzz_payloads.py (XSS polyglots,
386
+ # emoji surrogates, numeric boundaries). Formerly a dead import -
387
+ # now wired into generation so the catalog actually fires.
388
+ if get_field_fuzz_scenarios is not None:
389
+ existing_names = {m["name"] for m in mutations}
390
+ for p_name, p_schema in props.items():
391
+ p_type = p_schema.get("type", "string")
392
+ for scen in get_field_fuzz_scenarios(p_name, p_type):
393
+ mut_body = dict(valid_body)
394
+ mut_body[p_name] = scen["value"]
395
+ name = f"{method} {path} - {scen['scenario']}"
396
+ if name in existing_names:
397
+ continue
398
+ existing_names.add(name)
399
+ mutations.append({
400
+ "name": name,
401
+ "type": scen["category"],
402
+ "path": path,
403
+ "method": method,
404
+ "body": mut_body,
405
+ "headers": self.custom_headers
406
+ })
407
+
278
408
  return mutations
279
409
 
280
410
  def execute_request(self, mutation: Dict[str, Any], dry_run: bool = False) -> Dict[str, Any]:
@@ -295,6 +425,7 @@ class ApiFuzzer:
295
425
  "status_code": 400 if mutation["type"] != "BASELINE" else 200,
296
426
  "duration_ms": 42.5,
297
427
  "verdict": "PASS",
428
+ "severity": "INFO",
298
429
  "alarms": [],
299
430
  "response_preview": '{"status": "handled_by_dry_run"}'
300
431
  }
@@ -354,7 +485,18 @@ class ApiFuzzer:
354
485
  if mutation["type"] == "AUTH_OMISSION" and status_code in (200, 201):
355
486
  alarms.append("AUTH_BYPASS_VULNERABILITY: Endpoint returned 200 OK without Authorization header")
356
487
 
488
+ # Alarm 5: Target unreachable - an endpoint that could not be tested
489
+ # must NEVER be reported as PASS (Truth Anchor discipline).
490
+ if status_code < 0:
491
+ alarms.append("CONNECTION_ERROR: Target unreachable or transport failure - endpoint could not be verified")
492
+
357
493
  verdict = "FAIL" if alarms else "PASS"
494
+ severity = "INFO"
495
+ if alarms:
496
+ severity = max(
497
+ (classify_alarm_severity(a) for a in alarms),
498
+ key=lambda s: SEVERITY_LEVELS.index(s)
499
+ )
358
500
 
359
501
  return {
360
502
  "name": mutation["name"],
@@ -364,6 +506,7 @@ class ApiFuzzer:
364
506
  "status_code": status_code,
365
507
  "duration_ms": duration_ms,
366
508
  "verdict": verdict,
509
+ "severity": severity,
367
510
  "alarms": alarms,
368
511
  "response_preview": response_body[:300]
369
512
  }
@@ -404,11 +547,13 @@ class ApiFuzzer:
404
547
  scorecard = {
405
548
  "base_url": self.base_url,
406
549
  "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
550
+ "mode": "ACTIVE",
407
551
  "total_mutations": len(all_mutations),
408
552
  "passed": len(all_mutations) - total_failures,
409
553
  "failed": total_failures,
410
554
  "sla_ms": self.sla_ms,
411
555
  "dry_run": dry_run,
556
+ "severity_summary": self._severity_summary(results),
412
557
  "results": results
413
558
  }
414
559
 
@@ -419,6 +564,7 @@ class ApiFuzzer:
419
564
  print(f" Mutations Executed: {scorecard['total_mutations']}")
420
565
  print(f" Clean Handled : {GREEN}{scorecard['passed']}{RESET}")
421
566
  print(f" Defects Detected : {RED if total_failures > 0 else GREEN}{scorecard['failed']}{RESET}")
567
+ print(f" Severity Mix : {scorecard['severity_summary']}")
422
568
  if total_failures == 0:
423
569
  print(f" Verdict : {GREEN}[PASS] API resilient against input fuzzing & chaos.{RESET}")
424
570
  else:
@@ -434,6 +580,117 @@ class ApiFuzzer:
434
580
 
435
581
  return scorecard
436
582
 
583
+ @staticmethod
584
+ def _severity_summary(results: List[Dict[str, Any]]) -> Dict[str, int]:
585
+ """Counts findings per severity level (ZAP-style risk mix)."""
586
+ summary = {level: 0 for level in SEVERITY_LEVELS}
587
+ for res in results:
588
+ sev = res.get("severity", "INFO")
589
+ if sev in summary:
590
+ summary[sev] += 1
591
+ return summary
592
+
593
+ def run_passive_scan(self) -> Dict[str, Any]:
594
+ """Offline spec-level audit (no HTTP dispatched).
595
+
596
+ Concept borrowed from OWASP ZAP's passive scanning (Apache-2.0):
597
+ findings are pre-flight advisories derived purely from the OpenAPI/
598
+ Swagger document - unreachable endpoints cannot fake a pass here
599
+ because nothing is dispatched at all.
600
+ """
601
+ spec = self.load_spec()
602
+ has_global_security = bool(spec.get("security"))
603
+ findings: List[Dict[str, Any]] = []
604
+
605
+ for path, methods in spec.get("paths", {}).items():
606
+ for method, op in methods.items():
607
+ if method.lower() not in ("get", "post", "put", "patch", "delete"):
608
+ continue
609
+ op_security = op.get("security") if isinstance(op, dict) else None
610
+ is_protected = has_global_security or bool(op_security)
611
+ label = f"{method.upper()} {path}"
612
+
613
+ if not is_protected and method.lower() in ("post", "put", "patch", "delete"):
614
+ severity = "HIGH" if re.search(r"admin|internal|user", path, re.IGNORECASE) else "MEDIUM"
615
+ findings.append({
616
+ "finding": f"AUTH_NOT_DECLARED: Mutating endpoint declares no security requirement",
617
+ "severity": severity,
618
+ "target": label,
619
+ "recommendation": "Declare securitySchemes/security for this operation or document why it is public.",
620
+ })
621
+
622
+ if not is_protected and re.search(r"admin|internal", path, re.IGNORECASE):
623
+ findings.append({
624
+ "finding": "SENSITIVE_PATH_UNPROTECTED: Administrative/internal path is publicly declared",
625
+ "severity": "HIGH",
626
+ "target": label,
627
+ "recommendation": "Gate this path behind an authentication scheme and network policy.",
628
+ })
629
+
630
+ body = op.get("requestBody", {}) if isinstance(op, dict) else {}
631
+ content = body.get("content", {}).get("application/json", {}) if isinstance(body, dict) else {}
632
+ schema = content.get("schema", {})
633
+ props = schema.get("properties", {}) if isinstance(schema, dict) else {}
634
+ unbounded = [
635
+ p_name for p_name, p_schema in props.items()
636
+ if isinstance(p_schema, dict)
637
+ and p_schema.get("type") == "string"
638
+ and not any(k in p_schema for k in ("maxLength", "pattern", "enum", "format"))
639
+ ]
640
+ if unbounded:
641
+ findings.append({
642
+ "finding": f"NO_INPUT_VALIDATION_DECLARED: unbounded string props {unbounded[:5]}",
643
+ "severity": "LOW",
644
+ "target": label,
645
+ "recommendation": "Add maxLength/pattern/enum constraints so the contract rejects oversized input.",
646
+ })
647
+
648
+ if not spec.get("info") and spec.get("paths"):
649
+ findings.append({
650
+ "finding": "SPEC_METADATA_MISSING: OpenAPI info block absent",
651
+ "severity": "INFO",
652
+ "target": self.spec_path or "inline-spec",
653
+ "recommendation": "Document title/version/contact so scan reports are traceable to an owner.",
654
+ })
655
+
656
+ severity_summary = {level: 0 for level in SEVERITY_LEVELS}
657
+ for f in findings:
658
+ severity_summary[f["severity"]] += 1
659
+
660
+ blocking = severity_summary["HIGH"] + severity_summary["CRITICAL"]
661
+
662
+ print(f"\n{BOLD}{CYAN}======================================================================{RESET}")
663
+ print(f"{BOLD}{CYAN} API PASSIVE SCAN (Offline Spec Audit - Zero Traffic) {RESET}")
664
+ print(f"{BOLD}{CYAN}======================================================================{RESET}")
665
+ print(f" Spec Source : {self.spec_path or 'Default Archetype'}")
666
+ print(f" Findings : {YELLOW if findings else GREEN}{len(findings)}{RESET}")
667
+ print(f" Severity Mix : {severity_summary}")
668
+ if blocking:
669
+ print(f" Verdict : {RED}[FAIL] {blocking} HIGH/CRITICAL pre-flight finding(s).{RESET}")
670
+ else:
671
+ print(f" Verdict : {GREEN}[PASS] No HIGH/CRITICAL spec-level finding.{RESET}")
672
+ print(f"======================================================================\n")
673
+ for f in findings:
674
+ print(f" - [{f['severity']}] {f['target']} - {f['finding']}")
675
+ print(f" 👉 {f['recommendation']}")
676
+
677
+ os.makedirs(REPORTS_DIR, exist_ok=True)
678
+ report_path = os.path.join(REPORTS_DIR, "api_fuzz_report.json")
679
+ scorecard = {
680
+ "base_url": self.base_url,
681
+ "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
682
+ "mode": "PASSIVE",
683
+ "spec_source": self.spec_path or "Default Archetype",
684
+ "total_findings": len(findings),
685
+ "severity_summary": severity_summary,
686
+ "findings": findings,
687
+ }
688
+ with open(report_path, "w", encoding="utf-8") as f:
689
+ json.dump(scorecard, f, indent=2)
690
+ print(f"\nFull telemetry report saved to: {report_path}")
691
+
692
+ return scorecard
693
+
437
694
 
438
695
  def main():
439
696
  parser = argparse.ArgumentParser(description="Enterprise Autonomous API Fuzzer & Chaos Breaker")
@@ -441,6 +698,9 @@ def main():
441
698
  parser.add_argument("--spec", default=None, help="Path or URL to OpenAPI/Swagger JSON specification")
442
699
  parser.add_argument("--token", default=None, help="Bearer token for authenticated endpoints")
443
700
  parser.add_argument("--sla-ms", type=float, default=3000.0, help="SLA latency ceiling in ms (default: 3000ms)")
701
+ parser.add_argument("--mode", choices=["active", "passive"], default="active",
702
+ help="active = live fuzzing (default); passive = offline spec audit, zero traffic")
703
+ parser.add_argument("--insecure", action="store_true", help="Explicitly bypass TLS certificate verification (for staging/self-signed certs)")
444
704
  parser.add_argument("--dry-run", action="store_true", help="Simulate mutation generation without live requests")
445
705
  parser.add_argument("--json", action="store_true", help="Output summary in JSON format")
446
706
 
@@ -454,13 +714,25 @@ def main():
454
714
  base_url=args.base_url,
455
715
  spec_path_or_url=args.spec,
456
716
  headers=headers,
457
- sla_ms=args.sla_ms
717
+ sla_ms=args.sla_ms,
718
+ insecure=args.insecure
458
719
  )
459
720
 
721
+ if args.mode == "passive":
722
+ scorecard = fuzzer.run_passive_scan()
723
+ if args.json:
724
+ print(json.dumps(scorecard, indent=2))
725
+ severity_dict = scorecard.get("severity_summary", {})
726
+ blocking = int(severity_dict.get("HIGH", 0)) + int(severity_dict.get("CRITICAL", 0))
727
+ return 1 if blocking > 0 else 0
728
+
460
729
  scorecard = fuzzer.run_fuzz_suite(dry_run=args.dry_run)
461
730
  if args.json:
462
731
  print(json.dumps(scorecard, indent=2))
463
732
 
733
+ critical = scorecard["severity_summary"].get("CRITICAL", 0)
734
+ if critical > 0:
735
+ return 1
464
736
  return 1 if scorecard["failed"] > 0 else 0
465
737
 
466
738
 
@@ -4,9 +4,9 @@
4
4
  # Project: Synapse-Hub (https://github.com/TeeVee998/Synapse-Hub)
5
5
 
6
6
  """
7
- tools/author_integrity.py — Author Attribution & Anti-Tamper Integrity Guard
7
+ tools/author_integrity.py — Author Attribution & MIT License Compliance Guard
8
8
  -----------------------------------------------------------------------------
9
- Verifies the cryptographic and textual provenance of Synapse Hub.
9
+ Verifies the author provenance and copyright attribution of Synapse Hub.
10
10
  Enforces the mandatory Attribution condition of the MIT License:
11
11
  "The above copyright notice and this permission notice shall be included
12
12
  in all copies or substantial portions of the Software."
@@ -48,7 +48,7 @@ def get_canonical_watermark() -> str:
48
48
 
49
49
 
50
50
  def verify_author_fingerprint() -> bool:
51
- """Cryptographic validation that the canonical watermark matches the SHA-256 fingerprint."""
51
+ """Validates that the canonical author attribution metadata matches the reference integrity hash."""
52
52
  watermark = get_canonical_watermark()
53
53
  computed = hashlib.sha256(watermark.encode("utf-8")).hexdigest()
54
54
  return computed == CANONICAL_AUTHOR_SIG
@@ -56,9 +56,9 @@ def verify_author_fingerprint() -> bool:
56
56
 
57
57
  def assert_integrity_or_raise(repo_root: Optional[str] = None) -> bool:
58
58
  """
59
- Enforces author integrity and cryptographic provenance.
60
- If tampered with, immediately halts execution by raising a RuntimeError.
61
- Under MIT License, the original copyright notice in LICENSE is mandatory.
59
+ Enforces author attribution and MIT License provenance in accordance with open-source licensing.
60
+ Under the MIT License, preserving the original copyright notice in LICENSE is mandatory.
61
+ If mandatory LICENSE copyright notice is missing, halts execution to protect author attribution.
62
62
  """
63
63
  if not verify_author_fingerprint():
64
64
  raise RuntimeError(
@@ -85,7 +85,7 @@ def diagnose_and_plan_heal(target_dir, junit_xml_path=None):
85
85
 
86
86
  # 2. Query 0-token memory cache & 3-layer heuristic matcher
87
87
  print(f"{BOLD}[1/2] Checking 3-Layer Heuristic Matcher & Memory Cache...{RESET}")
88
- cache_solution = cache_resolver.query_cache(err_sig, threshold=0.35)
88
+ cache_solution = cache_resolver.query_solution(err_sig, threshold=0.35)
89
89
 
90
90
  # 2b. Query Episodic Reflexion Memory (Top 2 historical lessons)
91
91
  lessons = cache_resolver.query_lessons(f"{test_name} {err_sig}", limit=2)
@@ -13,7 +13,7 @@
13
13
  (function () {
14
14
  'use strict';
15
15
 
16
- if (window.__qa && window.__qa.__version === '2.2.4') {
16
+ if (window.__qa && window.__qa.__version === '2.4.1') {
17
17
  return window.__qa;
18
18
  }
19
19
 
@@ -47,7 +47,7 @@
47
47
  }
48
48
 
49
49
  const qa = {
50
- __version: '2.2.4',
50
+ __version: '2.4.1',
51
51
 
52
52
  /**
53
53
  * 1. Smart Action Batching
@@ -193,13 +193,20 @@
193
193
  */
194
194
  exportSession: function () {
195
195
  const storage = {};
196
- for (let i = 0; i < localStorage.length; i++) {
197
- const key = localStorage.key(i);
198
- storage[key] = localStorage.getItem(key);
196
+ try {
197
+ const ls = (typeof window !== "undefined" && window.localStorage) || (typeof localStorage !== "undefined" ? localStorage : null);
198
+ if (ls) {
199
+ for (let i = 0; i < ls.length; i++) {
200
+ const key = ls.key(i);
201
+ if (key) storage[key] = ls.getItem(key);
202
+ }
203
+ }
204
+ } catch (e) {
205
+ // Ignore cross-origin security errors on localStorage access
199
206
  }
200
207
  return {
201
- url: window.location.href,
202
- cookies: document.cookie,
208
+ url: (typeof window !== "undefined" && window.location && window.location.href) || "",
209
+ cookies: (typeof document !== "undefined" && document.cookie) || "",
203
210
  localStorage: storage
204
211
  };
205
212
  },
@@ -505,6 +512,97 @@
505
512
  return aomNodes;
506
513
  },
507
514
 
515
+ /**
516
+ * 8. ARIA Snapshot (Playwright-MCP-style YAML serialization)
517
+ * Serializes the visible accessibility tree into the ARIA snapshot text
518
+ * format that MCP-style agents consume: `- role "name" [state] [ref=eN]`.
519
+ * (Format borrowed and adapted from microsoft/playwright-mcp, Apache-2.0.)
520
+ * @param {Element|Document} [root=document]
521
+ * @param {number} [maxDepth=6]
522
+ * @returns {string} YAML-style ARIA snapshot text (one node per line)
523
+ */
524
+ ariaSnapshot: function (root = document, maxDepth = 6) {
525
+ const lines = [];
526
+ let refCounter = 0;
527
+
528
+ function getRole(el) {
529
+ const explicitRole = el.getAttribute('role');
530
+ if (explicitRole) return explicitRole.toLowerCase();
531
+ const tag = el.tagName.toLowerCase();
532
+ if (tag === 'button') return 'button';
533
+ if (tag === 'a' && el.hasAttribute('href')) return 'link';
534
+ if (tag === 'input') {
535
+ const type = (el.getAttribute('type') || 'text').toLowerCase();
536
+ if (['checkbox', 'radio', 'button', 'submit'].includes(type)) return type;
537
+ return 'textbox';
538
+ }
539
+ if (tag === 'select') return 'combobox';
540
+ if (tag === 'textarea') return 'textbox';
541
+ if (tag === 'h1' || tag === 'h2' || tag === 'h3' || tag === 'h4' || tag === 'h5' || tag === 'h6') return 'heading';
542
+ if (tag === 'img') return 'img';
543
+ if (tag === 'ul' || tag === 'ol') return 'list';
544
+ if (tag === 'li') return 'listitem';
545
+ return null;
546
+ }
547
+
548
+ function getAccessibleName(el) {
549
+ return (
550
+ el.getAttribute('aria-label') ||
551
+ el.getAttribute('aria-placeholder') ||
552
+ el.getAttribute('placeholder') ||
553
+ el.getAttribute('title') ||
554
+ (el.innerText ? el.innerText.trim().slice(0, 80).replace(/\s+/g, ' ') : '') ||
555
+ el.getAttribute('alt') ||
556
+ ''
557
+ );
558
+ }
559
+
560
+ function isVisible(el) {
561
+ if (!el || el.nodeType !== 1) return false;
562
+ const style = window.getComputedStyle(el);
563
+ if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') return false;
564
+ const rect = el.getBoundingClientRect();
565
+ return rect.width > 0 && rect.height > 0;
566
+ }
567
+
568
+ function walk(node, depth) {
569
+ if (depth > maxDepth || !node) return;
570
+ const children = node.children || [];
571
+ for (let i = 0; i < children.length; i++) {
572
+ const child = children[i];
573
+ if (!isVisible(child)) continue;
574
+
575
+ const role = getRole(child);
576
+ if (role) {
577
+ refCounter += 1;
578
+ const rawName = getAccessibleName(child);
579
+ const namePart = rawName ? ' "' + rawName.replace(/"/g, "'") + '"' : '';
580
+ let states = '';
581
+ if (child.disabled || child.getAttribute('aria-disabled') === 'true') states += ' [disabled]';
582
+ if (child.getAttribute('aria-checked') === 'true') states += ' [checked]';
583
+ if (child.getAttribute('aria-expanded') !== null) {
584
+ states += child.getAttribute('aria-expanded') === 'true' ? ' [expanded]' : ' [collapsed]';
585
+ }
586
+ if (child.getAttribute('aria-selected') === 'true') states += ' [selected]';
587
+ const tagLower = child.tagName.toLowerCase();
588
+ const level = (role === 'heading' && /^h[1-6]$/.test(tagLower)) ? ' [level=' + tagLower[1] + ']' : '';
589
+ const testId = child.getAttribute('data-testid') || child.getAttribute('data-cy') || '';
590
+ const idPart = testId ? ' [testid=' + testId + ']' : '';
591
+ lines.push(' '.repeat(depth - 1) + '- ' + role + namePart + level + states + ' [ref=e' + refCounter + ']' + idPart);
592
+ }
593
+
594
+ // Pierce Shadow DOM
595
+ if (child.shadowRoot) {
596
+ walk(child.shadowRoot, depth + 1);
597
+ }
598
+ walk(child, depth + 1);
599
+ }
600
+ }
601
+
602
+ walk(root === document ? document.body : root, 1);
603
+ return lines.join('\n');
604
+ },
605
+
508
606
  /**
509
607
  * 8. Dynamic Stability Probe (Event-Driven Wait Weaver)
510
608
  * Replaces flaky static sleeps (Thread.sleep / cy.wait) by dynamically probing: