agent-security-scanner-mcp 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/README.md +451 -739
  2. package/analyzer.py +51 -7
  3. package/index.js +42 -2697
  4. package/package.json +7 -6
  5. package/regex_fallback.py +66 -0
  6. package/rules/__init__.py +124 -36
  7. package/rules/generic/secrets/gitleaks/adafruit-api-key.yaml +27 -0
  8. package/rules/generic/secrets/gitleaks/adobe-client-id.yaml +27 -0
  9. package/rules/generic/secrets/gitleaks/adobe-client-secret.yaml +27 -0
  10. package/rules/generic/secrets/gitleaks/age-secret-key.yaml +27 -0
  11. package/rules/generic/secrets/gitleaks/airtable-api-key.yaml +27 -0
  12. package/rules/generic/secrets/gitleaks/algolia-api-key.yaml +27 -0
  13. package/rules/generic/secrets/gitleaks/alibaba-access-key-id.yaml +27 -0
  14. package/rules/generic/secrets/gitleaks/alibaba-secret-key.yaml +27 -0
  15. package/rules/generic/secrets/gitleaks/asana-client-id.yaml +27 -0
  16. package/rules/generic/secrets/gitleaks/asana-client-secret.yaml +27 -0
  17. package/rules/generic/secrets/gitleaks/atlassian-api-token.yaml +27 -0
  18. package/rules/generic/secrets/gitleaks/authress-service-client-access-key.yaml +27 -0
  19. package/rules/generic/secrets/gitleaks/aws-access-token.yaml +27 -0
  20. package/rules/generic/secrets/gitleaks/beamer-api-token.yaml +27 -0
  21. package/rules/generic/secrets/gitleaks/bitbucket-client-id.yaml +27 -0
  22. package/rules/generic/secrets/gitleaks/bitbucket-client-secret.yaml +27 -0
  23. package/rules/generic/secrets/gitleaks/bittrex-access-key.yaml +27 -0
  24. package/rules/generic/secrets/gitleaks/bittrex-secret-key.yaml +27 -0
  25. package/rules/generic/secrets/gitleaks/clojars-api-token.yaml +27 -0
  26. package/rules/generic/secrets/gitleaks/cloudflare-api-key.yaml +27 -0
  27. package/rules/generic/secrets/gitleaks/cloudflare-global-api-key.yaml +27 -0
  28. package/rules/generic/secrets/gitleaks/cloudflare-origin-ca-key.yaml +27 -0
  29. package/rules/generic/secrets/gitleaks/codecov-access-token.yaml +27 -0
  30. package/rules/generic/secrets/gitleaks/coinbase-access-token.yaml +27 -0
  31. package/rules/generic/secrets/gitleaks/confluent-access-token.yaml +27 -0
  32. package/rules/generic/secrets/gitleaks/confluent-secret-key.yaml +27 -0
  33. package/rules/generic/secrets/gitleaks/contentful-delivery-api-token.yaml +27 -0
  34. package/rules/generic/secrets/gitleaks/databricks-api-token.yaml +27 -0
  35. package/rules/generic/secrets/gitleaks/datadog-access-token.yaml +27 -0
  36. package/rules/generic/secrets/gitleaks/defined-networking-api-token.yaml +27 -0
  37. package/rules/generic/secrets/gitleaks/digitalocean-access-token.yaml +27 -0
  38. package/rules/generic/secrets/gitleaks/digitalocean-pat.yaml +27 -0
  39. package/rules/generic/secrets/gitleaks/digitalocean-refresh-token.yaml +27 -0
  40. package/rules/generic/secrets/gitleaks/discord-api-token.yaml +27 -0
  41. package/rules/generic/secrets/gitleaks/discord-client-id.yaml +27 -0
  42. package/rules/generic/secrets/gitleaks/discord-client-secret.yaml +27 -0
  43. package/rules/generic/secrets/gitleaks/doppler-api-token.yaml +27 -0
  44. package/rules/generic/secrets/gitleaks/droneci-access-token.yaml +27 -0
  45. package/rules/generic/secrets/gitleaks/dropbox-api-token.yaml +27 -0
  46. package/rules/generic/secrets/gitleaks/dropbox-long-lived-api-token.yaml +27 -0
  47. package/rules/generic/secrets/gitleaks/dropbox-short-lived-api-token.yaml +27 -0
  48. package/rules/generic/secrets/gitleaks/duffel-api-token.yaml +27 -0
  49. package/rules/generic/secrets/gitleaks/dynatrace-api-token.yaml +27 -0
  50. package/rules/generic/secrets/gitleaks/easypost-api-token.yaml +27 -0
  51. package/rules/generic/secrets/gitleaks/easypost-test-api-token.yaml +27 -0
  52. package/rules/generic/secrets/gitleaks/etsy-access-token.yaml +27 -0
  53. package/rules/generic/secrets/gitleaks/facebook-access-token.yaml +27 -0
  54. package/rules/generic/secrets/gitleaks/facebook-page-access-token.yaml +27 -0
  55. package/rules/generic/secrets/gitleaks/facebook-secret.yaml +27 -0
  56. package/rules/generic/secrets/gitleaks/facebook.yaml +27 -0
  57. package/rules/generic/secrets/gitleaks/fastly-api-token.yaml +27 -0
  58. package/rules/generic/secrets/gitleaks/finicity-api-token.yaml +27 -0
  59. package/rules/generic/secrets/gitleaks/finicity-client-secret.yaml +27 -0
  60. package/rules/generic/secrets/gitleaks/finnhub-access-token.yaml +27 -0
  61. package/rules/generic/secrets/gitleaks/flickr-access-token.yaml +27 -0
  62. package/rules/generic/secrets/gitleaks/flutterwave-encryption-key.yaml +27 -0
  63. package/rules/generic/secrets/gitleaks/flutterwave-public-key.yaml +27 -0
  64. package/rules/generic/secrets/gitleaks/flutterwave-secret-key.yaml +27 -0
  65. package/rules/generic/secrets/gitleaks/frameio-api-token.yaml +27 -0
  66. package/rules/generic/secrets/gitleaks/freshbooks-access-token.yaml +27 -0
  67. package/rules/generic/secrets/gitleaks/gcp-api-key.yaml +27 -0
  68. package/rules/generic/secrets/gitleaks/generic-api-key.yaml +76 -0
  69. package/rules/generic/secrets/gitleaks/github-app-token.yaml +27 -0
  70. package/rules/generic/secrets/gitleaks/github-fine-grained-pat.yaml +27 -0
  71. package/rules/generic/secrets/gitleaks/github-oauth.yaml +27 -0
  72. package/rules/generic/secrets/gitleaks/github-pat.yaml +27 -0
  73. package/rules/generic/secrets/gitleaks/github-refresh-token.yaml +27 -0
  74. package/rules/generic/secrets/gitleaks/gitlab-pat.yaml +27 -0
  75. package/rules/generic/secrets/gitleaks/gitlab-ptt.yaml +27 -0
  76. package/rules/generic/secrets/gitleaks/gitlab-rrt.yaml +27 -0
  77. package/rules/generic/secrets/gitleaks/gitter-access-token.yaml +27 -0
  78. package/rules/generic/secrets/gitleaks/gocardless-api-token.yaml +27 -0
  79. package/rules/generic/secrets/gitleaks/grafana-api-key.yaml +27 -0
  80. package/rules/generic/secrets/gitleaks/grafana-cloud-api-token.yaml +27 -0
  81. package/rules/generic/secrets/gitleaks/grafana-service-account-token.yaml +27 -0
  82. package/rules/generic/secrets/gitleaks/harness-api-key.yaml +27 -0
  83. package/rules/generic/secrets/gitleaks/hashicorp-tf-api-token.yaml +27 -0
  84. package/rules/generic/secrets/gitleaks/hashicorp-tf-password.yaml +31 -0
  85. package/rules/generic/secrets/gitleaks/heroku-api-key.yaml +27 -0
  86. package/rules/generic/secrets/gitleaks/hubspot-api-key.yaml +27 -0
  87. package/rules/generic/secrets/gitleaks/huggingface-access-token.yaml +27 -0
  88. package/rules/generic/secrets/gitleaks/huggingface-organization-api-token.yaml +27 -0
  89. package/rules/generic/secrets/gitleaks/infracost-api-token.yaml +27 -0
  90. package/rules/generic/secrets/gitleaks/intercom-api-key.yaml +27 -0
  91. package/rules/generic/secrets/gitleaks/intra42-client-secret.yaml +27 -0
  92. package/rules/generic/secrets/gitleaks/jfrog-api-key.yaml +27 -0
  93. package/rules/generic/secrets/gitleaks/jfrog-identity-token.yaml +27 -0
  94. package/rules/generic/secrets/gitleaks/jwt-base64.yaml +27 -0
  95. package/rules/generic/secrets/gitleaks/jwt.yaml +27 -0
  96. package/rules/generic/secrets/gitleaks/kraken-access-token.yaml +27 -0
  97. package/rules/generic/secrets/gitleaks/kucoin-access-token.yaml +27 -0
  98. package/rules/generic/secrets/gitleaks/kucoin-secret-key.yaml +27 -0
  99. package/rules/generic/secrets/gitleaks/launchdarkly-access-token.yaml +27 -0
  100. package/rules/generic/secrets/gitleaks/linear-api-key.yaml +27 -0
  101. package/rules/generic/secrets/gitleaks/linear-client-secret.yaml +27 -0
  102. package/rules/generic/secrets/gitleaks/linkedin-client-id.yaml +27 -0
  103. package/rules/generic/secrets/gitleaks/linkedin-client-secret.yaml +27 -0
  104. package/rules/generic/secrets/gitleaks/lob-api-key.yaml +27 -0
  105. package/rules/generic/secrets/gitleaks/lob-pub-api-key.yaml +27 -0
  106. package/rules/generic/secrets/gitleaks/mailchimp-api-key.yaml +27 -0
  107. package/rules/generic/secrets/gitleaks/mailgun-private-api-token.yaml +27 -0
  108. package/rules/generic/secrets/gitleaks/mailgun-pub-key.yaml +27 -0
  109. package/rules/generic/secrets/gitleaks/mailgun-signing-key.yaml +27 -0
  110. package/rules/generic/secrets/gitleaks/mapbox-api-token.yaml +27 -0
  111. package/rules/generic/secrets/gitleaks/mattermost-access-token.yaml +27 -0
  112. package/rules/generic/secrets/gitleaks/messagebird-api-token.yaml +27 -0
  113. package/rules/generic/secrets/gitleaks/messagebird-client-id.yaml +27 -0
  114. package/rules/generic/secrets/gitleaks/microsoft-teams-webhook.yaml +27 -0
  115. package/rules/generic/secrets/gitleaks/netlify-access-token.yaml +27 -0
  116. package/rules/generic/secrets/gitleaks/new-relic-browser-api-token.yaml +27 -0
  117. package/rules/generic/secrets/gitleaks/new-relic-insert-key.yaml +27 -0
  118. package/rules/generic/secrets/gitleaks/new-relic-user-api-id.yaml +27 -0
  119. package/rules/generic/secrets/gitleaks/new-relic-user-api-key.yaml +27 -0
  120. package/rules/generic/secrets/gitleaks/npm-access-token.yaml +27 -0
  121. package/rules/generic/secrets/gitleaks/nytimes-access-token.yaml +27 -0
  122. package/rules/generic/secrets/gitleaks/okta-access-token.yaml +27 -0
  123. package/rules/generic/secrets/gitleaks/openai-api-key.yaml +27 -0
  124. package/rules/generic/secrets/gitleaks/plaid-api-token.yaml +27 -0
  125. package/rules/generic/secrets/gitleaks/plaid-client-id.yaml +27 -0
  126. package/rules/generic/secrets/gitleaks/plaid-secret-key.yaml +27 -0
  127. package/rules/generic/secrets/gitleaks/planetscale-api-token.yaml +27 -0
  128. package/rules/generic/secrets/gitleaks/planetscale-oauth-token.yaml +27 -0
  129. package/rules/generic/secrets/gitleaks/planetscale-password.yaml +27 -0
  130. package/rules/generic/secrets/gitleaks/postman-api-token.yaml +27 -0
  131. package/rules/generic/secrets/gitleaks/prefect-api-token.yaml +27 -0
  132. package/rules/generic/secrets/gitleaks/private-key.yaml +27 -0
  133. package/rules/generic/secrets/gitleaks/pulumi-api-token.yaml +27 -0
  134. package/rules/generic/secrets/gitleaks/pypi-upload-token.yaml +27 -0
  135. package/rules/generic/secrets/gitleaks/rapidapi-access-token.yaml +27 -0
  136. package/rules/generic/secrets/gitleaks/readme-api-token.yaml +27 -0
  137. package/rules/generic/secrets/gitleaks/rubygems-api-token.yaml +27 -0
  138. package/rules/generic/secrets/gitleaks/scalingo-api-token.yaml +27 -0
  139. package/rules/generic/secrets/gitleaks/sendbird-access-id.yaml +27 -0
  140. package/rules/generic/secrets/gitleaks/sendbird-access-token.yaml +27 -0
  141. package/rules/generic/secrets/gitleaks/sendgrid-api-token.yaml +27 -0
  142. package/rules/generic/secrets/gitleaks/sendinblue-api-token.yaml +27 -0
  143. package/rules/generic/secrets/gitleaks/sentry-access-token.yaml +27 -0
  144. package/rules/generic/secrets/gitleaks/shippo-api-token.yaml +27 -0
  145. package/rules/generic/secrets/gitleaks/shopify-access-token.yaml +27 -0
  146. package/rules/generic/secrets/gitleaks/shopify-custom-access-token.yaml +27 -0
  147. package/rules/generic/secrets/gitleaks/shopify-private-app-access-token.yaml +27 -0
  148. package/rules/generic/secrets/gitleaks/shopify-shared-secret.yaml +27 -0
  149. package/rules/generic/secrets/gitleaks/sidekiq-secret.yaml +27 -0
  150. package/rules/generic/secrets/gitleaks/sidekiq-sensitive-url.yaml +27 -0
  151. package/rules/generic/secrets/gitleaks/slack-app-token.yaml +27 -0
  152. package/rules/generic/secrets/gitleaks/slack-bot-token.yaml +27 -0
  153. package/rules/generic/secrets/gitleaks/slack-config-access-token.yaml +27 -0
  154. package/rules/generic/secrets/gitleaks/slack-config-refresh-token.yaml +27 -0
  155. package/rules/generic/secrets/gitleaks/slack-legacy-bot-token.yaml +27 -0
  156. package/rules/generic/secrets/gitleaks/slack-legacy-token.yaml +27 -0
  157. package/rules/generic/secrets/gitleaks/slack-legacy-workspace-token.yaml +27 -0
  158. package/rules/generic/secrets/gitleaks/slack-user-token.yaml +27 -0
  159. package/rules/generic/secrets/gitleaks/slack-webhook-url.yaml +27 -0
  160. package/rules/generic/secrets/gitleaks/snyk-api-token.yaml +27 -0
  161. package/rules/generic/secrets/gitleaks/square-access-token.yaml +27 -0
  162. package/rules/generic/secrets/gitleaks/squarespace-access-token.yaml +27 -0
  163. package/rules/generic/secrets/gitleaks/stripe-access-token.yaml +27 -0
  164. package/rules/generic/secrets/gitleaks/sumologic-access-id.yaml +27 -0
  165. package/rules/generic/secrets/gitleaks/sumologic-access-token.yaml +27 -0
  166. package/rules/generic/secrets/gitleaks/telegram-bot-api-token.yaml +27 -0
  167. package/rules/generic/secrets/gitleaks/travisci-access-token.yaml +27 -0
  168. package/rules/generic/secrets/gitleaks/twilio-api-key.yaml +27 -0
  169. package/rules/generic/secrets/gitleaks/twitch-api-token.yaml +27 -0
  170. package/rules/generic/secrets/gitleaks/twitter-access-secret.yaml +27 -0
  171. package/rules/generic/secrets/gitleaks/twitter-access-token.yaml +27 -0
  172. package/rules/generic/secrets/gitleaks/twitter-api-key.yaml +27 -0
  173. package/rules/generic/secrets/gitleaks/twitter-api-secret.yaml +27 -0
  174. package/rules/generic/secrets/gitleaks/twitter-bearer-token.yaml +27 -0
  175. package/rules/generic/secrets/gitleaks/typeform-api-token.yaml +27 -0
  176. package/rules/generic/secrets/gitleaks/vault-batch-token.yaml +27 -0
  177. package/rules/generic/secrets/gitleaks/vault-service-token.yaml +27 -0
  178. package/rules/generic/secrets/gitleaks/yandex-access-token.yaml +27 -0
  179. package/rules/generic/secrets/gitleaks/yandex-api-key.yaml +27 -0
  180. package/rules/generic/secrets/gitleaks/yandex-aws-access-token.yaml +27 -0
  181. package/rules/generic/secrets/gitleaks/zendesk-secret-key.yaml +27 -0
  182. package/rules/generic/secrets/security/detected-amazon-mws-auth-token.yaml +26 -0
  183. package/rules/generic/secrets/security/detected-artifactory-password.yaml +47 -0
  184. package/rules/generic/secrets/security/detected-artifactory-token.yaml +44 -0
  185. package/rules/generic/secrets/security/detected-aws-access-key-id-value.yaml +29 -0
  186. package/rules/generic/secrets/security/detected-aws-account-id.yaml +58 -0
  187. package/rules/generic/secrets/security/detected-aws-appsync-graphql-key.yaml +27 -0
  188. package/rules/generic/secrets/security/detected-aws-secret-access-key.yaml +30 -0
  189. package/rules/generic/secrets/security/detected-aws-session-token.yaml +31 -0
  190. package/rules/generic/secrets/security/detected-bcrypt-hash.yaml +25 -0
  191. package/rules/generic/secrets/security/detected-codeclimate.yaml +27 -0
  192. package/rules/generic/secrets/security/detected-etc-shadow.yaml +27 -0
  193. package/rules/generic/secrets/security/detected-facebook-access-token.yaml +29 -0
  194. package/rules/generic/secrets/security/detected-facebook-oauth.yaml +27 -0
  195. package/rules/generic/secrets/security/detected-generic-api-key.yaml +29 -0
  196. package/rules/generic/secrets/security/detected-generic-secret.yaml +30 -0
  197. package/rules/generic/secrets/security/detected-github-token.yaml +47 -0
  198. package/rules/generic/secrets/security/detected-google-api-key.yaml +29 -0
  199. package/rules/generic/secrets/security/detected-google-cloud-api-key.yaml +27 -0
  200. package/rules/generic/secrets/security/detected-google-gcm-service-account.yaml +27 -0
  201. package/rules/generic/secrets/security/detected-google-oauth-access-token.yaml +26 -0
  202. package/rules/generic/secrets/security/detected-google-oauth.yaml +26 -0
  203. package/rules/generic/secrets/security/detected-heroku-api-key.yaml +27 -0
  204. package/rules/generic/secrets/security/detected-hockeyapp.yaml +27 -0
  205. package/rules/generic/secrets/security/detected-jwt-token.yaml +25 -0
  206. package/rules/generic/secrets/security/detected-kolide-api-key.yaml +25 -0
  207. package/rules/generic/secrets/security/detected-mailchimp-api-key.yaml +26 -0
  208. package/rules/generic/secrets/security/detected-mailgun-api-key.yaml +26 -0
  209. package/rules/generic/secrets/security/detected-npm-registry-auth-token.yaml +33 -0
  210. package/rules/generic/secrets/security/detected-onfido-live-api-token.yaml +20 -0
  211. package/rules/generic/secrets/security/detected-outlook-team.yaml +27 -0
  212. package/rules/generic/secrets/security/detected-paypal-braintree-access-token.yaml +27 -0
  213. package/rules/generic/secrets/security/detected-pgp-private-key-block.yaml +28 -0
  214. package/rules/generic/secrets/security/detected-picatic-api-key.yaml +26 -0
  215. package/rules/generic/secrets/security/detected-private-key.yaml +39 -0
  216. package/rules/generic/secrets/security/detected-sauce-token.yaml +27 -0
  217. package/rules/generic/secrets/security/detected-sendgrid-api-key.yaml +27 -0
  218. package/rules/generic/secrets/security/detected-slack-token.yaml +28 -0
  219. package/rules/generic/secrets/security/detected-slack-webhook.yaml +27 -0
  220. package/rules/generic/secrets/security/detected-snyk-api-key.yaml +26 -0
  221. package/rules/generic/secrets/security/detected-softlayer-api-key.yaml +27 -0
  222. package/rules/generic/secrets/security/detected-sonarqube-docs-api-key.yaml +40 -0
  223. package/rules/generic/secrets/security/detected-square-access-token.yaml +26 -0
  224. package/rules/generic/secrets/security/detected-square-oauth-secret.yaml +27 -0
  225. package/rules/generic/secrets/security/detected-ssh-password.yaml +27 -0
  226. package/rules/generic/secrets/security/detected-stripe-api-key.yaml +26 -0
  227. package/rules/generic/secrets/security/detected-stripe-restricted-api-key.yaml +26 -0
  228. package/rules/generic/secrets/security/detected-telegram-bot-api-key.yaml +30 -0
  229. package/rules/generic/secrets/security/detected-twilio-api-key.yaml +26 -0
  230. package/rules/generic/secrets/security/detected-username-and-password-in-uri.yaml +35 -0
  231. package/rules/generic/secrets/security/google-maps-apikeyleak.yaml +25 -0
  232. package/rules/prompt-injection.security.yaml +4 -0
  233. package/rules/python/flask/security/injection/flask-injection-sinks.yaml +352 -0
  234. package/src/analyzer.py +119 -0
  235. package/src/cli/demo.js +238 -0
  236. package/src/cli/doctor.js +273 -0
  237. package/src/cli/init.js +288 -0
  238. package/src/fix-patterns.js +698 -0
  239. package/src/tools/check-package.js +169 -0
  240. package/src/tools/fix-security.js +115 -0
  241. package/src/tools/scan-packages.js +154 -0
  242. package/src/tools/scan-prompt.js +570 -0
  243. package/src/tools/scan-security.js +117 -0
  244. package/src/utils.js +153 -0
@@ -0,0 +1,352 @@
1
+ rules:
2
+ # Flask SQL Injection - Direct Execution
3
+ - id: flask-sql-injection-execute
4
+ message: >-
5
+ SQL Injection vulnerability detected. User input from Flask request flows
6
+ directly to database query execution. An attacker could manipulate the query
7
+ to access, modify, or delete data. Use parameterized queries instead:
8
+ cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
9
+ metadata:
10
+ cwe:
11
+ - 'CWE-89: SQL Injection'
12
+ owasp:
13
+ - A03:2021 - Injection
14
+ - A05:2025 - Injection
15
+ category: security
16
+ technology:
17
+ - flask
18
+ - sqlite3
19
+ - mysql
20
+ - postgresql
21
+ subcategory:
22
+ - vuln
23
+ impact: HIGH
24
+ likelihood: HIGH
25
+ confidence: HIGH
26
+ fix: |
27
+ Use parameterized queries:
28
+ cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
29
+ severity: ERROR
30
+ languages:
31
+ - python
32
+ mode: taint
33
+ pattern-sources:
34
+ - patterns:
35
+ - pattern-either:
36
+ - pattern: request.args.get(...)
37
+ - pattern: request.args[...]
38
+ - pattern: request.form.get(...)
39
+ - pattern: request.form[...]
40
+ - pattern: request.json
41
+ - pattern: request.json.get(...)
42
+ - pattern: request.json[...]
43
+ - pattern: request.data
44
+ - pattern: request.values.get(...)
45
+ - pattern: request.values[...]
46
+ - pattern: request.cookies.get(...)
47
+ - pattern: request.headers.get(...)
48
+ - pattern: flask.request.args.get(...)
49
+ - pattern: flask.request.form[...]
50
+ - pattern: flask.request.json
51
+ pattern-sinks:
52
+ - patterns:
53
+ - pattern-either:
54
+ - pattern: $CURSOR.execute(...)
55
+ - pattern: $CURSOR.executemany(...)
56
+ - pattern: $CONN.execute(...)
57
+ - pattern: $DB.execute(...)
58
+ - pattern: $SESSION.execute(...)
59
+ pattern-sanitizers:
60
+ - patterns:
61
+ - pattern-either:
62
+ - pattern: $CURSOR.execute($QUERY, (...))
63
+ - pattern: $CURSOR.execute($QUERY, [...])
64
+
65
+ # Flask Command Injection - os.system
66
+ - id: flask-command-injection-system
67
+ message: >-
68
+ Command Injection vulnerability detected. User input from Flask request flows
69
+ to os.system() which executes shell commands. An attacker could execute arbitrary
70
+ system commands. Use subprocess with shell=False and argument lists instead.
71
+ metadata:
72
+ cwe:
73
+ - 'CWE-78: OS Command Injection'
74
+ owasp:
75
+ - A03:2021 - Injection
76
+ - A05:2025 - Injection
77
+ category: security
78
+ technology:
79
+ - flask
80
+ subcategory:
81
+ - vuln
82
+ impact: CRITICAL
83
+ likelihood: HIGH
84
+ confidence: HIGH
85
+ fix: |
86
+ Use subprocess with shell=False:
87
+ subprocess.run(['ping', '-c', '1', host], shell=False)
88
+ severity: ERROR
89
+ languages:
90
+ - python
91
+ mode: taint
92
+ pattern-sources:
93
+ - patterns:
94
+ - pattern-either:
95
+ - pattern: request.args.get(...)
96
+ - pattern: request.args[...]
97
+ - pattern: request.form.get(...)
98
+ - pattern: request.form[...]
99
+ - pattern: request.json
100
+ - pattern: request.json.get(...)
101
+ - pattern: request.json[...]
102
+ - pattern: flask.request.args.get(...)
103
+ - pattern: flask.request.form[...]
104
+ pattern-sinks:
105
+ - patterns:
106
+ - pattern-either:
107
+ - pattern: os.system(...)
108
+ - pattern: os.popen(...)
109
+ - pattern: os.spawn(...)
110
+ - pattern: os.spawnl(...)
111
+ - pattern: os.spawnle(...)
112
+ - pattern: os.spawnlp(...)
113
+ - pattern: os.spawnlpe(...)
114
+ - pattern: os.spawnv(...)
115
+ - pattern: os.spawnve(...)
116
+ - pattern: os.spawnvp(...)
117
+ - pattern: os.spawnvpe(...)
118
+ pattern-sanitizers:
119
+ - patterns:
120
+ - pattern-either:
121
+ - pattern: shlex.quote(...)
122
+ - pattern: shlex.split(...)
123
+
124
+ # Flask Command Injection - subprocess
125
+ - id: flask-command-injection-subprocess
126
+ message: >-
127
+ Command Injection vulnerability detected. User input from Flask request flows
128
+ to subprocess with shell=True. An attacker could execute arbitrary system commands.
129
+ Use subprocess with shell=False and pass arguments as a list.
130
+ metadata:
131
+ cwe:
132
+ - 'CWE-78: OS Command Injection'
133
+ owasp:
134
+ - A03:2021 - Injection
135
+ - A05:2025 - Injection
136
+ category: security
137
+ technology:
138
+ - flask
139
+ subcategory:
140
+ - vuln
141
+ impact: CRITICAL
142
+ likelihood: HIGH
143
+ confidence: HIGH
144
+ fix: |
145
+ Use subprocess with shell=False:
146
+ subprocess.run(['command', arg1, arg2], shell=False)
147
+ severity: ERROR
148
+ languages:
149
+ - python
150
+ mode: taint
151
+ pattern-sources:
152
+ - patterns:
153
+ - pattern-either:
154
+ - pattern: request.args.get(...)
155
+ - pattern: request.form[...]
156
+ - pattern: request.json
157
+ - pattern: request.json[...]
158
+ - pattern: flask.request.args.get(...)
159
+ pattern-sinks:
160
+ - patterns:
161
+ - pattern-either:
162
+ - pattern: subprocess.call(..., shell=True, ...)
163
+ - pattern: subprocess.run(..., shell=True, ...)
164
+ - pattern: subprocess.Popen(..., shell=True, ...)
165
+ - pattern: subprocess.check_output(..., shell=True, ...)
166
+ - pattern: subprocess.check_call(..., shell=True, ...)
167
+ pattern-sanitizers:
168
+ - patterns:
169
+ - pattern: shlex.quote(...)
170
+
171
+ # Flask Path Traversal
172
+ - id: flask-path-traversal
173
+ message: >-
174
+ Path Traversal vulnerability detected. User input from Flask request is used
175
+ in file operations without validation. An attacker could access files outside
176
+ the intended directory using '../' sequences. Validate and sanitize file paths.
177
+ metadata:
178
+ cwe:
179
+ - 'CWE-22: Path Traversal'
180
+ owasp:
181
+ - A01:2021 - Broken Access Control
182
+ - A01:2025 - Broken Access Control
183
+ category: security
184
+ technology:
185
+ - flask
186
+ subcategory:
187
+ - vuln
188
+ impact: HIGH
189
+ likelihood: MEDIUM
190
+ confidence: HIGH
191
+ fix: |
192
+ Use secure_filename and validate paths:
193
+ from werkzeug.utils import secure_filename
194
+ filename = secure_filename(user_input)
195
+ safe_path = os.path.join(base_dir, filename)
196
+ if not safe_path.startswith(base_dir):
197
+ abort(403)
198
+ severity: ERROR
199
+ languages:
200
+ - python
201
+ mode: taint
202
+ pattern-sources:
203
+ - patterns:
204
+ - pattern-either:
205
+ - pattern: request.args.get(...)
206
+ - pattern: request.form[...]
207
+ - pattern: request.json[...]
208
+ - pattern: flask.request.args.get(...)
209
+ pattern-sinks:
210
+ - patterns:
211
+ - pattern-either:
212
+ - pattern: open(...)
213
+ - pattern: os.path.join(...)
214
+ - pattern: pathlib.Path(...)
215
+ - pattern: send_file(...)
216
+ - pattern: send_from_directory(...)
217
+ pattern-sanitizers:
218
+ - patterns:
219
+ - pattern-either:
220
+ - pattern: secure_filename(...)
221
+ - pattern: os.path.basename(...)
222
+
223
+ # Flask XSS via Template Injection
224
+ - id: flask-template-injection
225
+ message: >-
226
+ Server-Side Template Injection (SSTI) vulnerability detected. User input flows
227
+ to render_template_string() which can execute arbitrary Python code. Use
228
+ render_template() with separate template files and proper escaping instead.
229
+ metadata:
230
+ cwe:
231
+ - 'CWE-79: Cross-site Scripting'
232
+ - 'CWE-94: Code Injection'
233
+ owasp:
234
+ - A03:2021 - Injection
235
+ - A05:2025 - Injection
236
+ category: security
237
+ technology:
238
+ - flask
239
+ - jinja2
240
+ subcategory:
241
+ - vuln
242
+ impact: CRITICAL
243
+ likelihood: HIGH
244
+ confidence: HIGH
245
+ fix: |
246
+ Use render_template with separate files:
247
+ return render_template('greeting.html', name=name)
248
+ # In greeting.html: <h1>Hello, {{ name }}!</h1>
249
+ severity: ERROR
250
+ languages:
251
+ - python
252
+ mode: taint
253
+ pattern-sources:
254
+ - patterns:
255
+ - pattern-either:
256
+ - pattern: request.args.get(...)
257
+ - pattern: request.form[...]
258
+ - pattern: request.json[...]
259
+ - pattern: flask.request.args.get(...)
260
+ pattern-sinks:
261
+ - patterns:
262
+ - pattern-either:
263
+ - pattern: render_template_string(...)
264
+ - pattern: flask.render_template_string(...)
265
+ - pattern: jinja2.Template(...).render(...)
266
+
267
+ # Flask Insecure Deserialization
268
+ - id: flask-insecure-deserialization
269
+ message: >-
270
+ Insecure deserialization vulnerability detected. User input flows to pickle.loads()
271
+ which can execute arbitrary code. Never deserialize untrusted data with pickle.
272
+ Use JSON or other safe serialization formats.
273
+ metadata:
274
+ cwe:
275
+ - 'CWE-502: Deserialization of Untrusted Data'
276
+ owasp:
277
+ - A08:2021 - Software and Data Integrity Failures
278
+ - A08:2025 - Software or Data Integrity Failures
279
+ category: security
280
+ technology:
281
+ - flask
282
+ subcategory:
283
+ - vuln
284
+ impact: CRITICAL
285
+ likelihood: MEDIUM
286
+ confidence: HIGH
287
+ fix: |
288
+ Use JSON instead of pickle:
289
+ data = json.loads(user_input)
290
+ severity: ERROR
291
+ languages:
292
+ - python
293
+ mode: taint
294
+ pattern-sources:
295
+ - patterns:
296
+ - pattern-either:
297
+ - pattern: request.data
298
+ - pattern: request.get_data(...)
299
+ - pattern: request.json
300
+ - pattern: flask.request.data
301
+ pattern-sinks:
302
+ - patterns:
303
+ - pattern-either:
304
+ - pattern: pickle.loads(...)
305
+ - pattern: pickle.load(...)
306
+ - pattern: yaml.load(...)
307
+ - pattern: yaml.unsafe_load(...)
308
+ pattern-sanitizers:
309
+ - patterns:
310
+ - pattern: yaml.safe_load(...)
311
+
312
+ # Flask Eval Injection
313
+ - id: flask-eval-injection
314
+ message: >-
315
+ Code injection vulnerability detected. User input flows to eval() or exec()
316
+ which can execute arbitrary Python code. Never use eval/exec with user input.
317
+ metadata:
318
+ cwe:
319
+ - 'CWE-94: Code Injection'
320
+ owasp:
321
+ - A03:2021 - Injection
322
+ - A05:2025 - Injection
323
+ category: security
324
+ technology:
325
+ - flask
326
+ subcategory:
327
+ - vuln
328
+ impact: CRITICAL
329
+ likelihood: HIGH
330
+ confidence: HIGH
331
+ fix: |
332
+ Avoid eval/exec entirely. Use safer alternatives like:
333
+ - ast.literal_eval() for simple data structures
334
+ - JSON parsing for data exchange
335
+ severity: ERROR
336
+ languages:
337
+ - python
338
+ mode: taint
339
+ pattern-sources:
340
+ - patterns:
341
+ - pattern-either:
342
+ - pattern: request.args.get(...)
343
+ - pattern: request.form[...]
344
+ - pattern: request.json[...]
345
+ - pattern: request.data
346
+ - pattern: flask.request.args.get(...)
347
+ pattern-sinks:
348
+ - patterns:
349
+ - pattern-either:
350
+ - pattern: eval(...)
351
+ - pattern: exec(...)
352
+ - pattern: compile(...)
@@ -0,0 +1,119 @@
1
+ import sys
2
+ import json
3
+ import re
4
+ import os
5
+
6
+ # Add the directory containing this script to the path
7
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
8
+
9
+ from rules import get_rules, get_rules_for_language, get_rule_stats
10
+
11
+ # File extension to language mapping
12
+ EXTENSION_MAP = {
13
+ '.py': 'python',
14
+ '.js': 'javascript',
15
+ '.ts': 'typescript',
16
+ '.tsx': 'typescript',
17
+ '.jsx': 'javascript',
18
+ '.java': 'java',
19
+ '.go': 'go',
20
+ '.rb': 'ruby',
21
+ '.php': 'php',
22
+ '.cs': 'csharp',
23
+ '.rs': 'rust',
24
+ '.c': 'c',
25
+ '.cpp': 'cpp',
26
+ '.h': 'c',
27
+ '.hpp': 'cpp',
28
+ '.sql': 'sql',
29
+ '.dockerfile': 'dockerfile',
30
+ '.yaml': 'yaml',
31
+ '.yml': 'yaml',
32
+ '.json': 'json',
33
+ '.tf': 'terraform',
34
+ '.hcl': 'terraform',
35
+ }
36
+
37
+ def detect_language(file_path):
38
+ """Detect the programming language from file extension or name"""
39
+ basename = os.path.basename(file_path).lower()
40
+
41
+ if basename == 'dockerfile' or basename.startswith('dockerfile.') or basename.startswith('dockerfile_'):
42
+ return 'dockerfile'
43
+
44
+ _, ext = os.path.splitext(file_path.lower())
45
+ return EXTENSION_MAP.get(ext, 'generic')
46
+
47
+ def analyze_file(file_path):
48
+ """Analyze a single file for security vulnerabilities"""
49
+ issues = []
50
+
51
+ try:
52
+ language = detect_language(file_path)
53
+ rules = get_rules_for_language(language)
54
+
55
+ with open(file_path, 'r', encoding='utf-8') as f:
56
+ lines = f.readlines()
57
+ content = ''.join(lines)
58
+
59
+ for line_index, line in enumerate(lines):
60
+ original_line = line
61
+ line = line.strip()
62
+ if not line:
63
+ continue
64
+
65
+ # Skip comment-only lines (basic detection)
66
+ if line.startswith('#') or line.startswith('//') or line.startswith('*'):
67
+ continue
68
+
69
+ for rule_id, rule in rules.items():
70
+ for pattern in rule['patterns']:
71
+ try:
72
+ # Use IGNORECASE for better detection (API_KEY vs api_key)
73
+ matches = re.finditer(pattern, line, re.IGNORECASE)
74
+ for match in matches:
75
+ # Calculate column based on original line (preserve indentation)
76
+ col_offset = len(original_line) - len(original_line.lstrip())
77
+ issues.append({
78
+ 'ruleId': rule['id'],
79
+ 'message': f"[{rule['name']}] {rule['message']}",
80
+ 'line': line_index,
81
+ 'column': match.start() + col_offset,
82
+ 'length': match.end() - match.start(),
83
+ 'severity': rule['severity'],
84
+ 'metadata': rule.get('metadata', {})
85
+ })
86
+ except re.error:
87
+ # Skip invalid regex patterns
88
+ continue
89
+
90
+ except Exception as e:
91
+ return {'error': str(e)}
92
+
93
+ # Deduplicate issues (same rule, same line)
94
+ seen = set()
95
+ unique_issues = []
96
+ for issue in issues:
97
+ key = (issue['ruleId'], issue['line'], issue['column'])
98
+ if key not in seen:
99
+ seen.add(key)
100
+ unique_issues.append(issue)
101
+
102
+ return unique_issues
103
+
104
+ def main():
105
+ if len(sys.argv) < 2:
106
+ print(json.dumps({'error': 'No file path provided'}))
107
+ sys.exit(1)
108
+
109
+ file_path = sys.argv[1]
110
+
111
+ if not os.path.exists(file_path):
112
+ print(json.dumps({'error': f'File not found: {file_path}'}))
113
+ sys.exit(1)
114
+
115
+ results = analyze_file(file_path)
116
+ print(json.dumps(results))
117
+
118
+ if __name__ == '__main__':
119
+ main()
@@ -0,0 +1,238 @@
1
+ import { execFileSync } from "child_process";
2
+ import { writeFileSync, unlinkSync } from "fs";
3
+ import { join } from "path";
4
+ import { createInterface } from "readline";
5
+ import { dirname } from "path";
6
+ import { fileURLToPath } from "url";
7
+
8
+ // Handle both ESM and CJS bundling (Smithery bundles to CJS)
9
+ let __dirname;
10
+ try {
11
+ __dirname = dirname(fileURLToPath(import.meta.url));
12
+ } catch {
13
+ __dirname = process.cwd();
14
+ }
15
+
16
+ const DEMO_TEMPLATES = {
17
+ js: {
18
+ ext: 'js',
19
+ name: 'JavaScript',
20
+ code: `const express = require("express");
21
+ const child_process = require("child_process");
22
+ const app = express();
23
+
24
+ // SQL Injection vulnerability
25
+ app.get("/user", (req, res) => {
26
+ const userId = req.query.id;
27
+ db.query("SELECT * FROM users WHERE id = " + userId, (err, result) => {
28
+ res.send(result);
29
+ });
30
+ });
31
+
32
+ // XSS vulnerability
33
+ app.get("/profile", (req, res) => {
34
+ const name = req.query.name;
35
+ document.getElementById("welcome").innerHTML = name;
36
+ });
37
+
38
+ // Command Injection vulnerability
39
+ app.get("/run", (req, res) => {
40
+ const cmd = req.query.cmd;
41
+ child_process.exec("ls " + cmd, (err, stdout) => {
42
+ res.send(stdout);
43
+ });
44
+ });
45
+ `
46
+ },
47
+ py: {
48
+ ext: 'py',
49
+ name: 'Python',
50
+ code: `import pickle
51
+ import subprocess
52
+ import hashlib
53
+
54
+ API_SECRET = "stripe_test_FAKEFAKEFAKEFAKE1234"
55
+
56
+ def get_user(user_id):
57
+ query = f"SELECT * FROM users WHERE id = {user_id}"
58
+ cursor.execute(query)
59
+ return cursor.fetchone()
60
+
61
+ def load_data(data):
62
+ return pickle.loads(data)
63
+
64
+ def run_command(cmd):
65
+ return subprocess.call(cmd, shell=True)
66
+
67
+ def hash_password(password):
68
+ return hashlib.md5(password.encode()).hexdigest()
69
+ `
70
+ },
71
+ go: {
72
+ ext: 'go',
73
+ name: 'Go',
74
+ code: `package main
75
+
76
+ import (
77
+ \t"crypto/md5"
78
+ \t"database/sql"
79
+ \t"fmt"
80
+ \t"net/http"
81
+ \t"os/exec"
82
+ )
83
+
84
+ var dbPassword = "super_secret_password_123"
85
+
86
+ func getUser(w http.ResponseWriter, r *http.Request) {
87
+ \tid := r.URL.Query().Get("id")
88
+ \tquery := fmt.Sprintf("SELECT * FROM users WHERE id = %s", id)
89
+ \tdb.Query(query)
90
+ }
91
+
92
+ func runCmd(w http.ResponseWriter, r *http.Request) {
93
+ \tcmd := r.URL.Query().Get("cmd")
94
+ \tout, _ := exec.Command("sh", "-c", cmd).Output()
95
+ \tw.Write(out)
96
+ }
97
+
98
+ func hashData(data string) string {
99
+ \th := md5.Sum([]byte(data))
100
+ \treturn fmt.Sprintf("%x", h)
101
+ }
102
+ `
103
+ },
104
+ java: {
105
+ ext: 'java',
106
+ name: 'Java',
107
+ code: `import java.sql.*;
108
+ import java.io.*;
109
+ import java.security.MessageDigest;
110
+
111
+ public class VulnDemo {
112
+ private static final String DB_PASSWORD = "admin123";
113
+
114
+ public ResultSet getUser(String userId) throws SQLException {
115
+ Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/db");
116
+ Statement stmt = conn.createStatement();
117
+ return stmt.executeQuery("SELECT * FROM users WHERE id = " + userId);
118
+ }
119
+
120
+ public String runCommand(String cmd) throws IOException {
121
+ Runtime rt = Runtime.getRuntime();
122
+ Process proc = rt.exec(cmd);
123
+ BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
124
+ return reader.readLine();
125
+ }
126
+
127
+ public String hashPassword(String password) throws Exception {
128
+ MessageDigest md = MessageDigest.getInstance("MD5");
129
+ byte[] hash = md.digest(password.getBytes());
130
+ return new String(hash);
131
+ }
132
+ }
133
+ `
134
+ }
135
+ };
136
+
137
+ function parseDemoFlags(args) {
138
+ const flags = { lang: 'js' };
139
+ let i = 0;
140
+ while (i < args.length) {
141
+ const arg = args[i];
142
+ if ((arg === '--lang' || arg === '-l') && i + 1 < args.length) {
143
+ flags.lang = args[++i].toLowerCase();
144
+ } else if (!arg.startsWith('-')) {
145
+ flags.lang = arg.toLowerCase();
146
+ }
147
+ i++;
148
+ }
149
+ return flags;
150
+ }
151
+
152
+ function checkCommand(cmd, args) {
153
+ try {
154
+ const out = execFileSync(cmd, args, { timeout: 10000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
155
+ return { ok: true, output: out.trim() };
156
+ } catch {
157
+ return { ok: false, output: null };
158
+ }
159
+ }
160
+
161
+ export async function runDemo(args) {
162
+ const flags = parseDemoFlags(args);
163
+ const template = DEMO_TEMPLATES[flags.lang];
164
+ if (!template) {
165
+ console.log(`\n Unknown language: "${flags.lang}"`);
166
+ console.log(` Available: ${Object.keys(DEMO_TEMPLATES).join(', ')}\n`);
167
+ process.exit(1);
168
+ }
169
+
170
+ const filename = `vuln-demo.${template.ext}`;
171
+ const filepath = join(process.cwd(), filename);
172
+
173
+ console.log(`\n agent-security-scanner-mcp demo\n`);
174
+ console.log(` Creating ${filename} with 3 intentional vulnerabilities...\n`);
175
+
176
+ // Write the vulnerable file
177
+ writeFileSync(filepath, template.code);
178
+
179
+ // Run the analyzer
180
+ const analyzerPath = join(__dirname, '..', '..', 'analyzer.py');
181
+ let pythonCmd = 'python3';
182
+ const py3 = checkCommand('python3', ['--version']);
183
+ if (!py3.ok) {
184
+ const py = checkCommand('python', ['--version']);
185
+ if (py.ok && py.output.includes('3.')) {
186
+ pythonCmd = 'python';
187
+ } else {
188
+ console.log(` Error: Python 3 not found. Run "npx agent-security-scanner-mcp doctor" to diagnose.\n`);
189
+ unlinkSync(filepath);
190
+ process.exit(1);
191
+ }
192
+ }
193
+
194
+ let results;
195
+ try {
196
+ const output = execFileSync(pythonCmd, [analyzerPath, filepath], { timeout: 30000, encoding: 'utf-8' });
197
+ results = JSON.parse(output);
198
+ } catch (e) {
199
+ console.log(` Error running analyzer: ${e.message}\n`);
200
+ unlinkSync(filepath);
201
+ process.exit(1);
202
+ }
203
+
204
+ // Display results
205
+ console.log(` Scanning...\n`);
206
+
207
+ if (results.length === 0) {
208
+ console.log(` No issues found (unexpected for demo file).\n`);
209
+ } else {
210
+ console.log(` Found ${results.length} issue(s):\n`);
211
+ for (const issue of results) {
212
+ const severity = (issue.severity || 'error').toUpperCase();
213
+ const icon = severity === 'ERROR' ? '\u2717' : severity === 'WARNING' ? '\u2717' : '\u2022';
214
+ console.log(` ${icon} ${severity.padEnd(8)} Line ${String(issue.line).padEnd(4)} ${issue.message}`);
215
+ if (issue.metadata) {
216
+ const refs = [issue.metadata.cwe, issue.metadata.owasp].filter(Boolean).join(' | ');
217
+ if (refs) console.log(` ${refs}`);
218
+ }
219
+ }
220
+ console.log(`\n ${results.length} vulnerabilities detected.\n`);
221
+ }
222
+
223
+ // Ask to keep or delete
224
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
225
+ const answer = await new Promise((resolve) => {
226
+ rl.question(` Keep ${filename} for testing? (y/N): `, (a) => { rl.close(); resolve(a); });
227
+ });
228
+
229
+ if (answer.toLowerCase() === 'y') {
230
+ console.log(`\n Kept: ${filepath}`);
231
+ } else {
232
+ unlinkSync(filepath);
233
+ console.log(`\n Deleted: ${filename}`);
234
+ }
235
+
236
+ console.log(`\n Next: Connect to your AI coding tool and ask it to`);
237
+ console.log(` "scan ${filename} for security issues"\n`);
238
+ }