malwar 0.2.1__tar.gz

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 (293) hide show
  1. malwar-0.2.1/.dockerignore +19 -0
  2. malwar-0.2.1/.env.example +27 -0
  3. malwar-0.2.1/.github/FUNDING.yml +1 -0
  4. malwar-0.2.1/.github/ISSUE_TEMPLATE/bug_report.yml +53 -0
  5. malwar-0.2.1/.github/ISSUE_TEMPLATE/detection_rule.yml +55 -0
  6. malwar-0.2.1/.github/ISSUE_TEMPLATE/feature_request.yml +26 -0
  7. malwar-0.2.1/.github/PULL_REQUEST_TEMPLATE.md +19 -0
  8. malwar-0.2.1/.github/actions/scan-skills/action.yml +115 -0
  9. malwar-0.2.1/.github/actions/scan-skills/scan.py +270 -0
  10. malwar-0.2.1/.github/workflows/ci.yml +134 -0
  11. malwar-0.2.1/.github/workflows/docker.yml +61 -0
  12. malwar-0.2.1/.github/workflows/docs.yml +34 -0
  13. malwar-0.2.1/.github/workflows/publish-pypi.yml +87 -0
  14. malwar-0.2.1/.github/workflows/scan-skills.yml +40 -0
  15. malwar-0.2.1/.gitignore +36 -0
  16. malwar-0.2.1/CONTRIBUTING.md +70 -0
  17. malwar-0.2.1/Dockerfile +43 -0
  18. malwar-0.2.1/LICENSE +68 -0
  19. malwar-0.2.1/PKG-INFO +209 -0
  20. malwar-0.2.1/README.md +161 -0
  21. malwar-0.2.1/SECURITY.md +38 -0
  22. malwar-0.2.1/deploy/helm/malwar/.helmignore +19 -0
  23. malwar-0.2.1/deploy/helm/malwar/Chart.yaml +17 -0
  24. malwar-0.2.1/deploy/helm/malwar/templates/NOTES.txt +38 -0
  25. malwar-0.2.1/deploy/helm/malwar/templates/_helpers.tpl +67 -0
  26. malwar-0.2.1/deploy/helm/malwar/templates/configmap.yaml +11 -0
  27. malwar-0.2.1/deploy/helm/malwar/templates/deployment.yaml +95 -0
  28. malwar-0.2.1/deploy/helm/malwar/templates/hpa.yaml +33 -0
  29. malwar-0.2.1/deploy/helm/malwar/templates/ingress.yaml +42 -0
  30. malwar-0.2.1/deploy/helm/malwar/templates/pvc.yaml +24 -0
  31. malwar-0.2.1/deploy/helm/malwar/templates/secret.yaml +21 -0
  32. malwar-0.2.1/deploy/helm/malwar/templates/service.yaml +16 -0
  33. malwar-0.2.1/deploy/helm/malwar/templates/serviceaccount.yaml +14 -0
  34. malwar-0.2.1/deploy/helm/malwar/templates/tests/test-connection.yaml +16 -0
  35. malwar-0.2.1/deploy/helm/malwar/values.yaml +101 -0
  36. malwar-0.2.1/docker-compose.yml +14 -0
  37. malwar-0.2.1/docs/accuracy-report.md +199 -0
  38. malwar-0.2.1/docs/api-reference.md +778 -0
  39. malwar-0.2.1/docs/architecture.md +386 -0
  40. malwar-0.2.1/docs/benchmarks.md +223 -0
  41. malwar-0.2.1/docs/campaign-ingestion.md +226 -0
  42. malwar-0.2.1/docs/cli-reference.md +449 -0
  43. malwar-0.2.1/docs/deployment/configuration.md +190 -0
  44. malwar-0.2.1/docs/deployment/docker.md +176 -0
  45. malwar-0.2.1/docs/deployment/kubernetes.md +212 -0
  46. malwar-0.2.1/docs/deployment.md +394 -0
  47. malwar-0.2.1/docs/detection-rules.md +618 -0
  48. malwar-0.2.1/docs/development/architecture.md +192 -0
  49. malwar-0.2.1/docs/development/benchmarks.md +83 -0
  50. malwar-0.2.1/docs/development/contributing.md +216 -0
  51. malwar-0.2.1/docs/development.md +558 -0
  52. malwar-0.2.1/docs/getting-started/installation.md +146 -0
  53. malwar-0.2.1/docs/getting-started/quickstart.md +181 -0
  54. malwar-0.2.1/docs/github-action.md +197 -0
  55. malwar-0.2.1/docs/guide/accuracy-report.md +123 -0
  56. malwar-0.2.1/docs/guide/api-reference.md +366 -0
  57. malwar-0.2.1/docs/guide/cli-reference.md +378 -0
  58. malwar-0.2.1/docs/guide/detection-rules.md +297 -0
  59. malwar-0.2.1/docs/guide/threat-campaigns.md +189 -0
  60. malwar-0.2.1/docs/images/campaign-detail.png +0 -0
  61. malwar-0.2.1/docs/images/campaigns.png +0 -0
  62. malwar-0.2.1/docs/images/dashboard.png +0 -0
  63. malwar-0.2.1/docs/images/scan-detail.png +0 -0
  64. malwar-0.2.1/docs/images/scan.png +0 -0
  65. malwar-0.2.1/docs/images/scans.png +0 -0
  66. malwar-0.2.1/docs/images/signatures.png +0 -0
  67. malwar-0.2.1/docs/index.md +123 -0
  68. malwar-0.2.1/docs/integrations/campaign-ingestion.md +99 -0
  69. malwar-0.2.1/docs/integrations/github-action.md +162 -0
  70. malwar-0.2.1/docs/integrations/langchain.md +208 -0
  71. malwar-0.2.1/docs/integrations/sdk.md +157 -0
  72. malwar-0.2.1/docs/integrations/stix-taxii.md +107 -0
  73. malwar-0.2.1/docs/kubernetes.md +327 -0
  74. malwar-0.2.1/docs/langchain.md +333 -0
  75. malwar-0.2.1/docs/product-brief.md +92 -0
  76. malwar-0.2.1/docs/sdk.md +169 -0
  77. malwar-0.2.1/docs/stix-taxii.md +213 -0
  78. malwar-0.2.1/docs/threat-campaigns.md +526 -0
  79. malwar-0.2.1/mkdocs.yml +76 -0
  80. malwar-0.2.1/pyproject.toml +126 -0
  81. malwar-0.2.1/src/malwar/__init__.py +25 -0
  82. malwar-0.2.1/src/malwar/__main__.py +6 -0
  83. malwar-0.2.1/src/malwar/api/__init__.py +1 -0
  84. malwar-0.2.1/src/malwar/api/app.py +90 -0
  85. malwar-0.2.1/src/malwar/api/auth.py +35 -0
  86. malwar-0.2.1/src/malwar/api/middleware.py +181 -0
  87. malwar-0.2.1/src/malwar/api/routes/__init__.py +1 -0
  88. malwar-0.2.1/src/malwar/api/routes/analytics.py +166 -0
  89. malwar-0.2.1/src/malwar/api/routes/campaigns.py +115 -0
  90. malwar-0.2.1/src/malwar/api/routes/export.py +136 -0
  91. malwar-0.2.1/src/malwar/api/routes/feed.py +229 -0
  92. malwar-0.2.1/src/malwar/api/routes/health.py +38 -0
  93. malwar-0.2.1/src/malwar/api/routes/ingest.py +76 -0
  94. malwar-0.2.1/src/malwar/api/routes/reports.py +205 -0
  95. malwar-0.2.1/src/malwar/api/routes/scan.py +341 -0
  96. malwar-0.2.1/src/malwar/api/routes/signatures.py +192 -0
  97. malwar-0.2.1/src/malwar/cli/__init__.py +1 -0
  98. malwar-0.2.1/src/malwar/cli/app.py +439 -0
  99. malwar-0.2.1/src/malwar/cli/commands/__init__.py +1 -0
  100. malwar-0.2.1/src/malwar/cli/commands/db.py +103 -0
  101. malwar-0.2.1/src/malwar/cli/commands/export.py +99 -0
  102. malwar-0.2.1/src/malwar/cli/commands/ingest.py +152 -0
  103. malwar-0.2.1/src/malwar/cli/formatters/__init__.py +1 -0
  104. malwar-0.2.1/src/malwar/cli/formatters/console.py +102 -0
  105. malwar-0.2.1/src/malwar/cli/formatters/json_fmt.py +30 -0
  106. malwar-0.2.1/src/malwar/cli/formatters/sarif.py +88 -0
  107. malwar-0.2.1/src/malwar/core/__init__.py +1 -0
  108. malwar-0.2.1/src/malwar/core/config.py +88 -0
  109. malwar-0.2.1/src/malwar/core/constants.py +54 -0
  110. malwar-0.2.1/src/malwar/core/exceptions.py +38 -0
  111. malwar-0.2.1/src/malwar/core/logging.py +56 -0
  112. malwar-0.2.1/src/malwar/detectors/__init__.py +1 -0
  113. malwar-0.2.1/src/malwar/detectors/llm_analyzer/__init__.py +1 -0
  114. malwar-0.2.1/src/malwar/detectors/llm_analyzer/detector.py +137 -0
  115. malwar-0.2.1/src/malwar/detectors/llm_analyzer/parser.py +189 -0
  116. malwar-0.2.1/src/malwar/detectors/llm_analyzer/prompts.py +95 -0
  117. malwar-0.2.1/src/malwar/detectors/rule_engine/__init__.py +1 -0
  118. malwar-0.2.1/src/malwar/detectors/rule_engine/base_rule.py +26 -0
  119. malwar-0.2.1/src/malwar/detectors/rule_engine/detector.py +63 -0
  120. malwar-0.2.1/src/malwar/detectors/rule_engine/registry.py +43 -0
  121. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/__init__.py +1 -0
  122. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/agent_hijacking.py +83 -0
  123. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/credential_exposure.py +102 -0
  124. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/env_harvesting.py +93 -0
  125. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/exfiltration.py +150 -0
  126. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/known_malware.py +117 -0
  127. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/multi_step.py +93 -0
  128. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/obfuscation.py +138 -0
  129. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/persistence.py +163 -0
  130. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/prompt_injection.py +136 -0
  131. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/social_engineering.py +193 -0
  132. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/steganography.py +169 -0
  133. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/supply_chain.py +168 -0
  134. malwar-0.2.1/src/malwar/detectors/rule_engine/rules/suspicious_commands.py +180 -0
  135. malwar-0.2.1/src/malwar/detectors/threat_intel/__init__.py +1 -0
  136. malwar-0.2.1/src/malwar/detectors/threat_intel/detector.py +55 -0
  137. malwar-0.2.1/src/malwar/detectors/threat_intel/matcher.py +299 -0
  138. malwar-0.2.1/src/malwar/detectors/url_crawler/__init__.py +6 -0
  139. malwar-0.2.1/src/malwar/detectors/url_crawler/analyzer.py +247 -0
  140. malwar-0.2.1/src/malwar/detectors/url_crawler/detector.py +144 -0
  141. malwar-0.2.1/src/malwar/detectors/url_crawler/extractor.py +113 -0
  142. malwar-0.2.1/src/malwar/detectors/url_crawler/fetcher.py +222 -0
  143. malwar-0.2.1/src/malwar/detectors/url_crawler/reputation.py +138 -0
  144. malwar-0.2.1/src/malwar/export/__init__.py +24 -0
  145. malwar-0.2.1/src/malwar/export/stix.py +330 -0
  146. malwar-0.2.1/src/malwar/export/taxii.py +139 -0
  147. malwar-0.2.1/src/malwar/ingestion/__init__.py +27 -0
  148. malwar-0.2.1/src/malwar/ingestion/importer.py +238 -0
  149. malwar-0.2.1/src/malwar/ingestion/schema.py +63 -0
  150. malwar-0.2.1/src/malwar/ingestion/sources.py +479 -0
  151. malwar-0.2.1/src/malwar/integrations/__init__.py +16 -0
  152. malwar-0.2.1/src/malwar/integrations/exceptions.py +40 -0
  153. malwar-0.2.1/src/malwar/integrations/langchain.py +419 -0
  154. malwar-0.2.1/src/malwar/models/__init__.py +17 -0
  155. malwar-0.2.1/src/malwar/models/finding.py +39 -0
  156. malwar-0.2.1/src/malwar/models/report.py +27 -0
  157. malwar-0.2.1/src/malwar/models/sarif.py +70 -0
  158. malwar-0.2.1/src/malwar/models/scan.py +78 -0
  159. malwar-0.2.1/src/malwar/models/signature.py +43 -0
  160. malwar-0.2.1/src/malwar/models/skill.py +57 -0
  161. malwar-0.2.1/src/malwar/notifications/__init__.py +6 -0
  162. malwar-0.2.1/src/malwar/notifications/webhook.py +140 -0
  163. malwar-0.2.1/src/malwar/parsers/__init__.py +13 -0
  164. malwar-0.2.1/src/malwar/parsers/markdown_parser.py +125 -0
  165. malwar-0.2.1/src/malwar/parsers/skill_parser.py +121 -0
  166. malwar-0.2.1/src/malwar/py.typed +0 -0
  167. malwar-0.2.1/src/malwar/scanner/__init__.py +1 -0
  168. malwar-0.2.1/src/malwar/scanner/base.py +36 -0
  169. malwar-0.2.1/src/malwar/scanner/context.py +48 -0
  170. malwar-0.2.1/src/malwar/scanner/pipeline.py +144 -0
  171. malwar-0.2.1/src/malwar/scanner/severity.py +35 -0
  172. malwar-0.2.1/src/malwar/sdk.py +269 -0
  173. malwar-0.2.1/src/malwar/storage/__init__.py +22 -0
  174. malwar-0.2.1/src/malwar/storage/database.py +67 -0
  175. malwar-0.2.1/src/malwar/storage/migrations.py +532 -0
  176. malwar-0.2.1/src/malwar/storage/repositories/__init__.py +16 -0
  177. malwar-0.2.1/src/malwar/storage/repositories/campaigns.py +33 -0
  178. malwar-0.2.1/src/malwar/storage/repositories/findings.py +74 -0
  179. malwar-0.2.1/src/malwar/storage/repositories/publishers.py +44 -0
  180. malwar-0.2.1/src/malwar/storage/repositories/scans.py +132 -0
  181. malwar-0.2.1/src/malwar/storage/repositories/signatures.py +133 -0
  182. malwar-0.2.1/tests/__init__.py +0 -0
  183. malwar-0.2.1/tests/benchmark/__init__.py +1 -0
  184. malwar-0.2.1/tests/benchmark/test_accuracy.py +233 -0
  185. malwar-0.2.1/tests/benchmark/test_benchmarks.py +221 -0
  186. malwar-0.2.1/tests/conftest.py +30 -0
  187. malwar-0.2.1/tests/e2e/__init__.py +0 -0
  188. malwar-0.2.1/tests/e2e/test_full_scan.py +343 -0
  189. malwar-0.2.1/tests/fixtures/skills/benign/code_formatter.md +58 -0
  190. malwar-0.2.1/tests/fixtures/skills/benign/git_helper.md +65 -0
  191. malwar-0.2.1/tests/fixtures/skills/benign/hello_world.md +26 -0
  192. malwar-0.2.1/tests/fixtures/skills/benign/legitimate_with_urls.md +45 -0
  193. malwar-0.2.1/tests/fixtures/skills/benign/web_search.md +54 -0
  194. malwar-0.2.1/tests/fixtures/skills/malicious/agent_hijacking.md +33 -0
  195. malwar-0.2.1/tests/fixtures/skills/malicious/base64_reverse_shell.md +39 -0
  196. malwar-0.2.1/tests/fixtures/skills/malicious/clawhavoc_amos.md +60 -0
  197. malwar-0.2.1/tests/fixtures/skills/malicious/clickfix_fake_prereq.md +51 -0
  198. malwar-0.2.1/tests/fixtures/skills/malicious/credential_harvester.md +67 -0
  199. malwar-0.2.1/tests/fixtures/skills/malicious/env_harvesting.md +46 -0
  200. malwar-0.2.1/tests/fixtures/skills/malicious/exfil_soul_md.md +50 -0
  201. malwar-0.2.1/tests/fixtures/skills/malicious/filesystem_modification.md +44 -0
  202. malwar-0.2.1/tests/fixtures/skills/malicious/multi_stage_dropper.md +66 -0
  203. malwar-0.2.1/tests/fixtures/skills/malicious/multi_step_manipulation.md +39 -0
  204. malwar-0.2.1/tests/fixtures/skills/malicious/obfuscated_curl.md +57 -0
  205. malwar-0.2.1/tests/fixtures/skills/malicious/persistence_mechanism.md +53 -0
  206. malwar-0.2.1/tests/fixtures/skills/malicious/prompt_injection_basic.md +53 -0
  207. malwar-0.2.1/tests/fixtures/skills/malicious/prompt_injection_unicode.md +49 -0
  208. malwar-0.2.1/tests/fixtures/skills/malicious/shadowpkg_typosquat.md +64 -0
  209. malwar-0.2.1/tests/fixtures/skills/malicious/snyktoxic_vercel_exfil.md +58 -0
  210. malwar-0.2.1/tests/fixtures/skills/malicious/steganographic.md +27 -0
  211. malwar-0.2.1/tests/fixtures/skills/malicious/supply_chain.md +41 -0
  212. malwar-0.2.1/tests/fixtures/skills/malicious/typosquatted_package.md +57 -0
  213. malwar-0.2.1/tests/fixtures/skills/real/benign/anthropic_frontend_design.md +42 -0
  214. malwar-0.2.1/tests/fixtures/skills/real/benign/anthropic_pdf.md +314 -0
  215. malwar-0.2.1/tests/fixtures/skills/real/benign/anthropic_skill_creator.md +357 -0
  216. malwar-0.2.1/tests/fixtures/skills/real/clawhub/bankrbot_bankr.md +821 -0
  217. malwar-0.2.1/tests/fixtures/skills/real/clawhub/bankrbot_clanker.md +463 -0
  218. malwar-0.2.1/tests/fixtures/skills/real/clawhub/bankrbot_onchainkit.md +364 -0
  219. malwar-0.2.1/tests/fixtures/skills/real/clawhub/bankrbot_veil.md +111 -0
  220. malwar-0.2.1/tests/fixtures/skills/real/clawhub/bankrbot_yoink.md +76 -0
  221. malwar-0.2.1/tests/fixtures/skills/real/clawhub/bankrbot_zapper.md +4 -0
  222. malwar-0.2.1/tests/fixtures/skills/real/clawhub/clawpod_massive.md +188 -0
  223. malwar-0.2.1/tests/fixtures/skills/real/clawhub/metamask_gator.md +235 -0
  224. malwar-0.2.1/tests/fixtures/skills/real/clawhub/metamask_smart_accounts.md +754 -0
  225. malwar-0.2.1/tests/fixtures/skills/real/clawhub/model_hierarchy.md +280 -0
  226. malwar-0.2.1/tests/fixtures/skills/real/clawhub/search_layer.md +238 -0
  227. malwar-0.2.1/tests/fixtures/skills/real/clawhub/x_bookmarks.md +218 -0
  228. malwar-0.2.1/tests/fixtures/skills/real/clawhub/x_tweet_fetcher.md +113 -0
  229. malwar-0.2.1/tests/fixtures/skills/real/malicious/snyk_ascii_smuggling.md +16 -0
  230. malwar-0.2.1/tests/fixtures/skills/real/malicious/snyk_clawhub.md +482 -0
  231. malwar-0.2.1/tests/fixtures/skills/real/malicious/snyk_clawhub_agent.md +482 -0
  232. malwar-0.2.1/tests/fixtures/skills/real/malicious/snyk_gemini_vercel.md +38 -0
  233. malwar-0.2.1/tests/fixtures/skills/real/malicious/snyk_google_malware.md +334 -0
  234. malwar-0.2.1/tests/fixtures/skills/real/malicious/snyk_vercel_exfil.md +38 -0
  235. malwar-0.2.1/tests/integration/__init__.py +0 -0
  236. malwar-0.2.1/tests/integration/test_analytics.py +150 -0
  237. malwar-0.2.1/tests/integration/test_api.py +459 -0
  238. malwar-0.2.1/tests/integration/test_batch_scan.py +164 -0
  239. malwar-0.2.1/tests/integration/test_campaigns_api.py +92 -0
  240. malwar-0.2.1/tests/integration/test_export_api.py +191 -0
  241. malwar-0.2.1/tests/integration/test_feed.py +302 -0
  242. malwar-0.2.1/tests/integration/test_ingest_api.py +249 -0
  243. malwar-0.2.1/tests/integration/test_reports_api.py +231 -0
  244. malwar-0.2.1/tests/integration/test_scan_pipeline.py +161 -0
  245. malwar-0.2.1/tests/integration/test_signatures_api.py +229 -0
  246. malwar-0.2.1/tests/integration/test_webhooks.py +333 -0
  247. malwar-0.2.1/tests/live/__init__.py +1 -0
  248. malwar-0.2.1/tests/live/test_llm_live.py +247 -0
  249. malwar-0.2.1/tests/unit/__init__.py +0 -0
  250. malwar-0.2.1/tests/unit/detectors/__init__.py +0 -0
  251. malwar-0.2.1/tests/unit/detectors/test_llm_analyzer.py +655 -0
  252. malwar-0.2.1/tests/unit/detectors/test_rule_engine.py +763 -0
  253. malwar-0.2.1/tests/unit/detectors/test_threat_intel.py +391 -0
  254. malwar-0.2.1/tests/unit/detectors/test_url_crawler.py +584 -0
  255. malwar-0.2.1/tests/unit/test_core_models.py +811 -0
  256. malwar-0.2.1/tests/unit/test_github_action.py +435 -0
  257. malwar-0.2.1/tests/unit/test_helm_chart.py +257 -0
  258. malwar-0.2.1/tests/unit/test_ingestion.py +683 -0
  259. malwar-0.2.1/tests/unit/test_langchain_integration.py +444 -0
  260. malwar-0.2.1/tests/unit/test_markdown_parser.py +287 -0
  261. malwar-0.2.1/tests/unit/test_migrations.py +278 -0
  262. malwar-0.2.1/tests/unit/test_rate_limit.py +191 -0
  263. malwar-0.2.1/tests/unit/test_sdk.py +266 -0
  264. malwar-0.2.1/tests/unit/test_skill_parser.py +276 -0
  265. malwar-0.2.1/tests/unit/test_stix_export.py +539 -0
  266. malwar-0.2.1/tests/unit/test_storage.py +485 -0
  267. malwar-0.2.1/tests/unit/test_webhook.py +297 -0
  268. malwar-0.2.1/web/.gitignore +24 -0
  269. malwar-0.2.1/web/README.md +73 -0
  270. malwar-0.2.1/web/eslint.config.js +23 -0
  271. malwar-0.2.1/web/index.html +13 -0
  272. malwar-0.2.1/web/package-lock.json +4412 -0
  273. malwar-0.2.1/web/package.json +38 -0
  274. malwar-0.2.1/web/public/vite.svg +1 -0
  275. malwar-0.2.1/web/src/App.tsx +29 -0
  276. malwar-0.2.1/web/src/components/Badge.tsx +32 -0
  277. malwar-0.2.1/web/src/components/Card.tsx +35 -0
  278. malwar-0.2.1/web/src/components/Layout.tsx +134 -0
  279. malwar-0.2.1/web/src/components/LoadingSpinner.tsx +42 -0
  280. malwar-0.2.1/web/src/index.css +47 -0
  281. malwar-0.2.1/web/src/lib/api.ts +150 -0
  282. malwar-0.2.1/web/src/lib/utils.ts +60 -0
  283. malwar-0.2.1/web/src/main.tsx +10 -0
  284. malwar-0.2.1/web/src/pages/CampaignsPage.tsx +408 -0
  285. malwar-0.2.1/web/src/pages/Dashboard.tsx +431 -0
  286. malwar-0.2.1/web/src/pages/ScanDetail.tsx +477 -0
  287. malwar-0.2.1/web/src/pages/ScanPage.tsx +491 -0
  288. malwar-0.2.1/web/src/pages/ScansHistory.tsx +501 -0
  289. malwar-0.2.1/web/src/pages/SignaturesPage.tsx +482 -0
  290. malwar-0.2.1/web/tsconfig.app.json +28 -0
  291. malwar-0.2.1/web/tsconfig.json +7 -0
  292. malwar-0.2.1/web/tsconfig.node.json +26 -0
  293. malwar-0.2.1/web/vite.config.ts +27 -0
@@ -0,0 +1,19 @@
1
+ # Copyright (c) 2026 Veritas Aequitas Holdings LLC. All rights reserved.
2
+ .git
3
+ __pycache__
4
+ .venv
5
+ node_modules
6
+ web/
7
+ tests/
8
+ docs/
9
+ .github/
10
+ *.pyc
11
+ .env
12
+ .env.*
13
+ .mypy_cache
14
+ .ruff_cache
15
+ .pytest_cache
16
+ htmlcov/
17
+ *.db
18
+ deploy/
19
+ .claude/
@@ -0,0 +1,27 @@
1
+ # malwar configuration
2
+ # Copy this file to .env and fill in values
3
+
4
+ # Anthropic API key for LLM semantic analysis (Layer 3)
5
+ MALWAR_ANTHROPIC_API_KEY=
6
+
7
+ # API authentication keys (comma-separated)
8
+ MALWAR_API_KEYS=
9
+
10
+ # Database path (default: malwar.db in current directory)
11
+ MALWAR_DB_PATH=malwar.db
12
+
13
+ # API server settings
14
+ MALWAR_API_HOST=127.0.0.1
15
+ MALWAR_API_PORT=8000
16
+
17
+ # LLM settings
18
+ MALWAR_LLM_MODEL=claude-sonnet-4-20250514
19
+ MALWAR_LLM_SKIP_BELOW_RISK=15
20
+
21
+ # URL crawler settings
22
+ MALWAR_CRAWLER_MAX_URLS=10
23
+ MALWAR_CRAWLER_TIMEOUT=5.0
24
+
25
+ # Logging
26
+ MALWAR_LOG_LEVEL=INFO
27
+ MALWAR_LOG_FORMAT=json
@@ -0,0 +1 @@
1
+ github: [Ap6pack]
@@ -0,0 +1,53 @@
1
+ name: Bug Report
2
+ description: Report a bug in Malwar
3
+ labels: ["bug"]
4
+ body:
5
+ - type: textarea
6
+ id: description
7
+ attributes:
8
+ label: Description
9
+ description: What happened?
10
+ placeholder: A clear description of the bug
11
+ validations:
12
+ required: true
13
+ - type: textarea
14
+ id: steps
15
+ attributes:
16
+ label: Steps to Reproduce
17
+ description: How can we reproduce this?
18
+ placeholder: |
19
+ 1. Run `malwar scan ...`
20
+ 2. ...
21
+ validations:
22
+ required: true
23
+ - type: textarea
24
+ id: expected
25
+ attributes:
26
+ label: Expected Behavior
27
+ placeholder: What should have happened?
28
+ validations:
29
+ required: true
30
+ - type: textarea
31
+ id: actual
32
+ attributes:
33
+ label: Actual Behavior
34
+ placeholder: What actually happened?
35
+ validations:
36
+ required: true
37
+ - type: input
38
+ id: version
39
+ attributes:
40
+ label: Malwar Version
41
+ placeholder: "0.2.0"
42
+ validations:
43
+ required: true
44
+ - type: input
45
+ id: python
46
+ attributes:
47
+ label: Python Version
48
+ placeholder: "3.13"
49
+ - type: input
50
+ id: os
51
+ attributes:
52
+ label: Operating System
53
+ placeholder: "Ubuntu 24.04"
@@ -0,0 +1,55 @@
1
+ name: Detection Rule Request
2
+ description: Suggest a new detection rule
3
+ labels: ["detection-rule", "enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: description
7
+ attributes:
8
+ label: Description
9
+ description: What attack pattern should be detected?
10
+ placeholder: Describe the threat this rule would catch
11
+ validations:
12
+ required: true
13
+ - type: dropdown
14
+ id: attack_type
15
+ attributes:
16
+ label: Attack Type
17
+ options:
18
+ - Obfuscation
19
+ - Prompt Injection
20
+ - Social Engineering
21
+ - Data Exfiltration
22
+ - Credential Theft
23
+ - Malware Delivery
24
+ - Supply Chain
25
+ - Persistence
26
+ - Other
27
+ validations:
28
+ required: true
29
+ - type: textarea
30
+ id: example
31
+ attributes:
32
+ label: Example SKILL.md Content
33
+ description: Provide a sample that demonstrates the attack pattern
34
+ render: markdown
35
+ validations:
36
+ required: true
37
+ - type: dropdown
38
+ id: expected_verdict
39
+ attributes:
40
+ label: Expected Verdict
41
+ options:
42
+ - MALICIOUS
43
+ - SUSPICIOUS
44
+ - CAUTION
45
+ validations:
46
+ required: true
47
+ - type: dropdown
48
+ id: severity
49
+ attributes:
50
+ label: Suggested Severity
51
+ options:
52
+ - Critical
53
+ - High
54
+ - Medium
55
+ - Low
@@ -0,0 +1,26 @@
1
+ name: Feature Request
2
+ description: Suggest a new feature for Malwar
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: description
7
+ attributes:
8
+ label: Description
9
+ description: What feature would you like?
10
+ placeholder: A clear description of the feature
11
+ validations:
12
+ required: true
13
+ - type: textarea
14
+ id: use_case
15
+ attributes:
16
+ label: Use Case
17
+ description: Why do you need this?
18
+ placeholder: Describe the problem this would solve
19
+ validations:
20
+ required: true
21
+ - type: textarea
22
+ id: solution
23
+ attributes:
24
+ label: Proposed Solution
25
+ description: How would you implement this?
26
+ placeholder: Optional — your ideas on how to solve it
@@ -0,0 +1,19 @@
1
+ ## Description
2
+
3
+ <!-- What does this PR do? -->
4
+
5
+ ## Type of Change
6
+
7
+ - [ ] Bug fix
8
+ - [ ] New feature
9
+ - [ ] New detection rule
10
+ - [ ] Documentation
11
+ - [ ] Infrastructure / CI
12
+
13
+ ## Checklist
14
+
15
+ - [ ] Tests added or updated
16
+ - [ ] `ruff check src/ tests/` passes
17
+ - [ ] `pytest tests/` passes
18
+ - [ ] Copyright headers on new files
19
+ - [ ] Documentation updated (if applicable)
@@ -0,0 +1,115 @@
1
+ # Copyright (c) 2026 Veritas Aequitas Holdings LLC. All rights reserved.
2
+ #
3
+ # Composite GitHub Action that scans SKILL.md files for malware using Malwar.
4
+
5
+ name: "Malwar Skill Scanner"
6
+ description: "Scan SKILL.md files in pull requests for malware, prompt injection, and other threats."
7
+ branding:
8
+ icon: "shield"
9
+ color: "red"
10
+
11
+ inputs:
12
+ path:
13
+ description: "Glob pattern for SKILL.md files to scan"
14
+ required: false
15
+ default: "**/SKILL.md"
16
+ fail-on:
17
+ description: "Verdict threshold that causes the action to fail (MALICIOUS, SUSPICIOUS, CAUTION)"
18
+ required: false
19
+ default: "SUSPICIOUS"
20
+ format:
21
+ description: "Output format: text, json, or sarif"
22
+ required: false
23
+ default: "text"
24
+
25
+ outputs:
26
+ verdict:
27
+ description: "Worst verdict across all scanned files"
28
+ value: ${{ steps.scan.outputs.verdict }}
29
+ risk_score:
30
+ description: "Highest risk score across all scanned files"
31
+ value: ${{ steps.scan.outputs.risk_score }}
32
+ finding_count:
33
+ description: "Total number of findings across all scanned files"
34
+ value: ${{ steps.scan.outputs.finding_count }}
35
+ sarif_path:
36
+ description: "Path to SARIF output file (when format is sarif)"
37
+ value: ${{ steps.scan.outputs.sarif_path }}
38
+
39
+ runs:
40
+ using: "composite"
41
+ steps:
42
+ - name: Set up Python 3.13
43
+ uses: actions/setup-python@v5
44
+ with:
45
+ python-version: "3.13"
46
+
47
+ - name: Install Malwar
48
+ shell: bash
49
+ run: pip install malwar
50
+
51
+ - name: Run Malwar scan
52
+ id: scan
53
+ shell: bash
54
+ run: |
55
+ python "${{ github.action_path }}/scan.py" \
56
+ --path "${{ inputs.path }}" \
57
+ --fail-on "${{ inputs.fail-on }}" \
58
+ --format "${{ inputs.format }}"
59
+
60
+ - name: Post PR comment
61
+ if: github.event_name == 'pull_request' && always()
62
+ uses: actions/github-script@v7
63
+ with:
64
+ script: |
65
+ const verdict = '${{ steps.scan.outputs.verdict }}';
66
+ const riskScore = '${{ steps.scan.outputs.risk_score }}';
67
+ const findingCount = '${{ steps.scan.outputs.finding_count }}';
68
+
69
+ if (!verdict) return;
70
+
71
+ const icons = {
72
+ 'CLEAN': ':white_check_mark:',
73
+ 'CAUTION': ':warning:',
74
+ 'SUSPICIOUS': ':orange_circle:',
75
+ 'MALICIOUS': ':red_circle:'
76
+ };
77
+ const icon = icons[verdict] || ':question:';
78
+
79
+ const body = [
80
+ `## ${icon} Malwar Scan Results`,
81
+ '',
82
+ `| Metric | Value |`,
83
+ `|--------|-------|`,
84
+ `| **Verdict** | ${verdict} |`,
85
+ `| **Risk Score** | ${riskScore}/100 |`,
86
+ `| **Findings** | ${findingCount} |`,
87
+ '',
88
+ '*Scanned by [Malwar](https://github.com/Ap6pack/malwar) — malware detection for agentic AI skills.*'
89
+ ].join('\n');
90
+
91
+ const { data: comments } = await github.rest.issues.listComments({
92
+ owner: context.repo.owner,
93
+ repo: context.repo.repo,
94
+ issue_number: context.issue.number,
95
+ });
96
+
97
+ const existing = comments.find(c =>
98
+ c.user.type === 'Bot' && c.body.includes('Malwar Scan Results')
99
+ );
100
+
101
+ if (existing) {
102
+ await github.rest.issues.updateComment({
103
+ owner: context.repo.owner,
104
+ repo: context.repo.repo,
105
+ comment_id: existing.id,
106
+ body,
107
+ });
108
+ } else {
109
+ await github.rest.issues.createComment({
110
+ owner: context.repo.owner,
111
+ repo: context.repo.repo,
112
+ issue_number: context.issue.number,
113
+ body,
114
+ });
115
+ }
@@ -0,0 +1,270 @@
1
+ # Copyright (c) 2026 Veritas Aequitas Holdings LLC. All rights reserved.
2
+ """Malwar SKILL.md scanner for GitHub Actions.
3
+
4
+ Finds files matching a glob pattern, scans each with the malwar SDK,
5
+ and reports results in the requested format. Sets GitHub Action outputs
6
+ and exits with code 1 if any file meets or exceeds the fail-on threshold.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import asyncio
13
+ import json
14
+ import logging
15
+ import os
16
+ import sys
17
+ from glob import glob
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ from malwar import __version__, scan
22
+ from malwar.cli.formatters.sarif import scan_result_to_sarif
23
+ from malwar.models.scan import ScanResult
24
+
25
+ logger = logging.getLogger("malwar.action")
26
+
27
+ # Verdict severity ordering (lowest to highest)
28
+ VERDICT_ORDER: dict[str, int] = {
29
+ "CLEAN": 0,
30
+ "CAUTION": 1,
31
+ "SUSPICIOUS": 2,
32
+ "MALICIOUS": 3,
33
+ }
34
+
35
+ SARIF_OUTPUT_FILE = "malwar-results.sarif"
36
+
37
+
38
+ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
39
+ """Parse command-line arguments."""
40
+ parser = argparse.ArgumentParser(
41
+ description="Scan SKILL.md files for threats using Malwar.",
42
+ )
43
+ parser.add_argument(
44
+ "--path",
45
+ default="**/SKILL.md",
46
+ help="Glob pattern for files to scan (default: **/SKILL.md)",
47
+ )
48
+ parser.add_argument(
49
+ "--fail-on",
50
+ default="SUSPICIOUS",
51
+ choices=["CAUTION", "SUSPICIOUS", "MALICIOUS"],
52
+ help="Verdict threshold that triggers failure (default: SUSPICIOUS)",
53
+ )
54
+ parser.add_argument(
55
+ "--format",
56
+ default="text",
57
+ choices=["text", "json", "sarif"],
58
+ dest="output_format",
59
+ help="Output format (default: text)",
60
+ )
61
+ return parser.parse_args(argv)
62
+
63
+
64
+ def find_files(pattern: str) -> list[Path]:
65
+ """Find all files matching the glob pattern."""
66
+ matches = sorted(glob(pattern, recursive=True))
67
+ return [Path(m) for m in matches if Path(m).is_file()]
68
+
69
+
70
+ def verdict_meets_threshold(verdict: str, threshold: str) -> bool:
71
+ """Return True if the verdict meets or exceeds the threshold."""
72
+ return VERDICT_ORDER.get(verdict, 0) >= VERDICT_ORDER.get(threshold, 0)
73
+
74
+
75
+ def worst_verdict(verdicts: list[str]) -> str:
76
+ """Return the most severe verdict from a list."""
77
+ if not verdicts:
78
+ return "CLEAN"
79
+ return max(verdicts, key=lambda v: VERDICT_ORDER.get(v, 0))
80
+
81
+
82
+ async def scan_files(files: list[Path]) -> list[tuple[Path, ScanResult]]:
83
+ """Scan all files and return (path, result) pairs."""
84
+ results: list[tuple[Path, ScanResult]] = []
85
+ for file_path in files:
86
+ content = file_path.read_text(encoding="utf-8")
87
+ result = await scan(
88
+ content,
89
+ file_name=str(file_path),
90
+ use_llm=False,
91
+ use_urls=False,
92
+ )
93
+ results.append((file_path, result))
94
+ return results
95
+
96
+
97
+ def format_text(results: list[tuple[Path, ScanResult]]) -> str:
98
+ """Format scan results as human-readable text."""
99
+ if not results:
100
+ return "No SKILL.md files found to scan."
101
+
102
+ lines: list[str] = []
103
+ lines.append(f"Malwar Scan Results (v{__version__})")
104
+ lines.append("=" * 50)
105
+
106
+ for file_path, result in results:
107
+ lines.append("")
108
+ lines.append(f"File: {file_path}")
109
+ lines.append(f" Verdict: {result.verdict}")
110
+ lines.append(f" Risk Score: {result.risk_score}/100")
111
+ lines.append(f" Findings: {len(result.findings)}")
112
+
113
+ for finding in result.findings:
114
+ lines.append(f" - [{finding.severity.upper()}] {finding.title}")
115
+ if finding.location:
116
+ lines.append(f" Line {finding.location.line_start}: {finding.description}")
117
+ else:
118
+ lines.append(f" {finding.description}")
119
+
120
+ lines.append("")
121
+ lines.append("-" * 50)
122
+ all_verdicts = [r.verdict for _, r in results]
123
+ lines.append(f"Overall: {worst_verdict(all_verdicts)}")
124
+
125
+ return "\n".join(lines)
126
+
127
+
128
+ def format_json(results: list[tuple[Path, ScanResult]]) -> str:
129
+ """Format scan results as JSON."""
130
+ output: list[dict[str, Any]] = []
131
+ for file_path, result in results:
132
+ output.append({
133
+ "file": str(file_path),
134
+ "verdict": result.verdict,
135
+ "risk_score": result.risk_score,
136
+ "finding_count": len(result.findings),
137
+ "findings": [
138
+ {
139
+ "id": f.id,
140
+ "rule_id": f.rule_id,
141
+ "title": f.title,
142
+ "severity": f.severity,
143
+ "confidence": f.confidence,
144
+ "description": f.description,
145
+ }
146
+ for f in result.findings
147
+ ],
148
+ })
149
+ return json.dumps(output, indent=2)
150
+
151
+
152
+ def format_sarif(results: list[tuple[Path, ScanResult]]) -> str:
153
+ """Format scan results as SARIF 2.1.0 and write to file."""
154
+ if not results:
155
+ sarif: dict[str, Any] = {
156
+ "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
157
+ "version": "2.1.0",
158
+ "runs": [{
159
+ "tool": {
160
+ "driver": {
161
+ "name": "malwar",
162
+ "version": __version__,
163
+ "rules": [],
164
+ }
165
+ },
166
+ "results": [],
167
+ }],
168
+ }
169
+ elif len(results) == 1:
170
+ _, result = results[0]
171
+ sarif = scan_result_to_sarif(result)
172
+ else:
173
+ # Merge multiple results into a single SARIF run
174
+ all_rules: list[dict[str, Any]] = []
175
+ all_results: list[dict[str, Any]] = []
176
+ seen_rules: set[str] = set()
177
+
178
+ for _, result in results:
179
+ single = scan_result_to_sarif(result)
180
+ run = single["runs"][0]
181
+ for rule in run["tool"]["driver"]["rules"]:
182
+ if rule["id"] not in seen_rules:
183
+ seen_rules.add(rule["id"])
184
+ all_rules.append(rule)
185
+ all_results.extend(run["results"])
186
+
187
+ sarif = {
188
+ "$schema": "https://json.schemastore.org/sarif-2.1.0.json",
189
+ "version": "2.1.0",
190
+ "runs": [{
191
+ "tool": {
192
+ "driver": {
193
+ "name": "malwar",
194
+ "version": __version__,
195
+ "rules": all_rules,
196
+ }
197
+ },
198
+ "results": all_results,
199
+ }],
200
+ }
201
+
202
+ sarif_str = json.dumps(sarif, indent=2)
203
+
204
+ # Write SARIF file to disk
205
+ Path(SARIF_OUTPUT_FILE).write_text(sarif_str, encoding="utf-8")
206
+
207
+ return sarif_str
208
+
209
+
210
+ def set_github_output(name: str, value: str) -> None:
211
+ """Write a key=value pair to $GITHUB_OUTPUT."""
212
+ output_file = os.environ.get("GITHUB_OUTPUT")
213
+ if output_file:
214
+ with open(output_file, "a", encoding="utf-8") as f:
215
+ f.write(f"{name}={value}\n")
216
+
217
+
218
+ def main(argv: list[str] | None = None) -> int:
219
+ """Entry point for the scan script."""
220
+ args = parse_args(argv)
221
+
222
+ # Discover files
223
+ files = find_files(args.path)
224
+ sys.stdout.write(f"Found {len(files)} file(s) matching '{args.path}'\n")
225
+
226
+ if not files:
227
+ set_github_output("verdict", "CLEAN")
228
+ set_github_output("risk_score", "0")
229
+ set_github_output("finding_count", "0")
230
+ sys.stdout.write("No files to scan. Exiting clean.\n")
231
+ return 0
232
+
233
+ # Run scans
234
+ results = asyncio.run(scan_files(files))
235
+
236
+ # Compute aggregates
237
+ verdicts = [r.verdict for _, r in results]
238
+ overall = worst_verdict(verdicts)
239
+ max_score = max((r.risk_score for _, r in results), default=0)
240
+ total_findings = sum(len(r.findings) for _, r in results)
241
+
242
+ # Format output
243
+ formatters = {
244
+ "text": format_text,
245
+ "json": format_json,
246
+ "sarif": format_sarif,
247
+ }
248
+ formatter = formatters[args.output_format]
249
+ output = formatter(results)
250
+ sys.stdout.write(output + "\n")
251
+
252
+ # Set GitHub Action outputs
253
+ set_github_output("verdict", overall)
254
+ set_github_output("risk_score", str(max_score))
255
+ set_github_output("finding_count", str(total_findings))
256
+ if args.output_format == "sarif":
257
+ set_github_output("sarif_path", SARIF_OUTPUT_FILE)
258
+
259
+ # Determine exit code
260
+ should_fail = verdict_meets_threshold(overall, args.fail_on)
261
+ if should_fail:
262
+ sys.stdout.write(f"\nFailed: verdict '{overall}' meets or exceeds threshold '{args.fail_on}'\n")
263
+ return 1
264
+
265
+ sys.stdout.write(f"\nPassed: verdict '{overall}' is below threshold '{args.fail_on}'\n")
266
+ return 0
267
+
268
+
269
+ if __name__ == "__main__":
270
+ sys.exit(main())