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,169 @@
1
+ import { z } from "zod";
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+ import bloomFilters from "bloom-filters";
6
+ const { BloomFilter } = bloomFilters;
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
+ // Load legitimate package lists into memory (hash sets for O(1) lookup)
17
+ const LEGITIMATE_PACKAGES = {
18
+ dart: new Set(),
19
+ perl: new Set(),
20
+ raku: new Set(),
21
+ npm: new Set(),
22
+ pypi: new Set(),
23
+ rubygems: new Set(),
24
+ crates: new Set()
25
+ };
26
+
27
+ // Bloom filters for large package lists (memory-efficient probabilistic lookup)
28
+ const BLOOM_FILTERS = {
29
+ npm: null,
30
+ pypi: null,
31
+ rubygems: null
32
+ };
33
+
34
+ // Load package lists on startup
35
+ export function loadPackageLists() {
36
+ const packagesDir = join(__dirname, '..', '..', 'packages');
37
+
38
+ for (const ecosystem of Object.keys(LEGITIMATE_PACKAGES)) {
39
+ const filePath = join(packagesDir, `${ecosystem}.txt`);
40
+ try {
41
+ if (existsSync(filePath)) {
42
+ const content = readFileSync(filePath, 'utf-8');
43
+ const packages = content.split('\n').filter(p => p.trim());
44
+ LEGITIMATE_PACKAGES[ecosystem] = new Set(packages);
45
+ console.error(`Loaded ${packages.length} ${ecosystem} packages`);
46
+ }
47
+ } catch (error) {
48
+ console.error(`Warning: Could not load ${ecosystem} packages: ${error.message}`);
49
+ }
50
+ }
51
+
52
+ // Load bloom filters for large ecosystems (npm, pypi, rubygems)
53
+ for (const ecosystem of Object.keys(BLOOM_FILTERS)) {
54
+ const bloomPath = join(packagesDir, `${ecosystem}-bloom.json`);
55
+ try {
56
+ if (existsSync(bloomPath)) {
57
+ const bloomData = JSON.parse(readFileSync(bloomPath, 'utf-8'));
58
+ BLOOM_FILTERS[ecosystem] = BloomFilter.fromJSON(bloomData);
59
+ console.error(`Loaded ${ecosystem} bloom filter (${bloomData._size} bits)`);
60
+ }
61
+ } catch (error) {
62
+ console.error(`Warning: Could not load ${ecosystem} bloom filter: ${error.message}`);
63
+ }
64
+ }
65
+ }
66
+
67
+ // Check if a package is hallucinated
68
+ export function isHallucinated(packageName, ecosystem) {
69
+ const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
70
+
71
+ // First check Set-based lookup (exact match)
72
+ if (legitPackages && legitPackages.size > 0) {
73
+ return { hallucinated: !legitPackages.has(packageName) };
74
+ }
75
+
76
+ // Fall back to bloom filter for large ecosystems (npm, pypi, rubygems)
77
+ const bloomFilter = BLOOM_FILTERS[ecosystem];
78
+ if (bloomFilter) {
79
+ // Bloom filter: false = definitely not in set, true = probably in set
80
+ const mightExist = bloomFilter.has(packageName);
81
+ return {
82
+ hallucinated: !mightExist,
83
+ bloomFilter: true,
84
+ note: mightExist ? "Package likely exists (bloom filter match)" : "Package not found in bloom filter"
85
+ };
86
+ }
87
+
88
+ return { unknown: true, reason: `No package list loaded for ${ecosystem}` };
89
+ }
90
+
91
+ // Get total packages count for an ecosystem
92
+ export function getTotalPackages(ecosystem) {
93
+ return LEGITIMATE_PACKAGES[ecosystem]?.size || 0;
94
+ }
95
+
96
+ // Get all package stats
97
+ export function getPackageStats() {
98
+ const stats = Object.entries(LEGITIMATE_PACKAGES).map(([ecosystem, packages]) => {
99
+ const bloomFilter = BLOOM_FILTERS[ecosystem];
100
+ const setSize = packages.size;
101
+ const hasBloom = !!bloomFilter;
102
+ return {
103
+ ecosystem,
104
+ packages_loaded: setSize,
105
+ bloom_filter_loaded: hasBloom,
106
+ status: setSize > 0 ? 'ready' : hasBloom ? 'ready (bloom filter)' : 'not loaded'
107
+ };
108
+ });
109
+
110
+ const totalSet = stats.reduce((sum, s) => sum + s.packages_loaded, 0);
111
+ const bloomEcosystems = stats.filter(s => s.bloom_filter_loaded).map(s => s.ecosystem);
112
+
113
+ return {
114
+ package_lists: stats,
115
+ total_packages: totalSet,
116
+ bloom_filter_ecosystems: bloomEcosystems,
117
+ note: bloomEcosystems.length > 0
118
+ ? `Bloom filters provide coverage for: ${bloomEcosystems.join(', ')} (not counted in total_packages)`
119
+ : undefined
120
+ };
121
+ }
122
+
123
+ // Schema for check_package tool
124
+ export const checkPackageSchema = {
125
+ package_name: z.string().describe("The package name to verify"),
126
+ ecosystem: z.enum(["dart", "perl", "raku", "npm", "pypi", "rubygems", "crates"]).describe("The package ecosystem (dart=pub.dev, perl=CPAN, raku=raku.land, npm=npmjs, pypi=PyPI, rubygems=RubyGems, crates=crates.io)")
127
+ };
128
+
129
+ // Handler for check_package tool
130
+ export async function checkPackage({ package_name, ecosystem }) {
131
+ const result = isHallucinated(package_name, ecosystem);
132
+
133
+ if (result.unknown) {
134
+ return {
135
+ content: [{
136
+ type: "text",
137
+ text: JSON.stringify({
138
+ package: package_name,
139
+ ecosystem,
140
+ status: "unknown",
141
+ reason: result.reason,
142
+ suggestion: "Load package list or verify manually at the package registry"
143
+ }, null, 2)
144
+ }]
145
+ };
146
+ }
147
+
148
+ const exists = !result.hallucinated;
149
+ const confidence = result.bloomFilter ? "medium" : "high";
150
+ const totalPackages = getTotalPackages(ecosystem);
151
+
152
+ return {
153
+ content: [{
154
+ type: "text",
155
+ text: JSON.stringify({
156
+ package: package_name,
157
+ ecosystem,
158
+ legitimate: exists,
159
+ hallucinated: !exists,
160
+ confidence,
161
+ bloom_filter: !!result.bloomFilter,
162
+ total_known_packages: totalPackages,
163
+ recommendation: exists
164
+ ? "Package exists in registry - safe to use"
165
+ : "⚠️ POTENTIAL HALLUCINATION - Package not found in registry. Verify before using!"
166
+ }, null, 2)
167
+ }]
168
+ };
169
+ }
@@ -0,0 +1,115 @@
1
+ // src/tools/fix-security.js
2
+ import { z } from "zod";
3
+ import { existsSync, readFileSync } from "fs";
4
+ import { detectLanguage, runAnalyzer, generateFix } from '../utils.js';
5
+
6
+ export const fixSecuritySchema = {
7
+ file_path: z.string().describe("Path to the file to fix"),
8
+ verbosity: z.enum(['minimal', 'compact', 'full']).optional().describe("Response detail level: 'minimal' (summary only), 'compact' (default), 'full' (includes fixed_content)")
9
+ };
10
+
11
+ // Verbosity formatters
12
+ function formatFixMinimal(file_path, fixes) {
13
+ return {
14
+ file: file_path,
15
+ fixes_applied: fixes.length,
16
+ message: fixes.length > 0
17
+ ? `Applied ${fixes.length} fix(es). Use verbosity='compact' for details.`
18
+ : "No fixes needed."
19
+ };
20
+ }
21
+
22
+ function formatFixCompact(file_path, fixes) {
23
+ return {
24
+ file: file_path,
25
+ fixes_applied: fixes.length,
26
+ fixes: fixes.map(f => ({
27
+ line: f.line,
28
+ rule: f.rule,
29
+ description: f.description
30
+ }))
31
+ };
32
+ }
33
+
34
+ function formatFixFull(file_path, fixes, fixedContent) {
35
+ return {
36
+ file: file_path,
37
+ fixes_applied: fixes.length,
38
+ fixes: fixes,
39
+ fixed_content: fixedContent
40
+ };
41
+ }
42
+
43
+ export async function fixSecurity({ file_path, verbosity }) {
44
+ if (!existsSync(file_path)) {
45
+ return {
46
+ content: [{ type: "text", text: JSON.stringify({ error: "File not found" }) }]
47
+ };
48
+ }
49
+
50
+ const issues = runAnalyzer(file_path);
51
+
52
+ if (issues.error || !Array.isArray(issues) || issues.length === 0) {
53
+ return {
54
+ content: [{
55
+ type: "text",
56
+ text: JSON.stringify({
57
+ message: issues.error ? "Error scanning file" : "No security issues found",
58
+ details: issues
59
+ })
60
+ }]
61
+ };
62
+ }
63
+
64
+ // Read and fix the file
65
+ const content = readFileSync(file_path, 'utf-8');
66
+ const lines = content.split('\n');
67
+ const language = detectLanguage(file_path);
68
+ const fixes = [];
69
+
70
+ // Apply fixes (process in reverse order to preserve line numbers)
71
+ const sortedIssues = [...issues].sort((a, b) => b.line - a.line);
72
+
73
+ for (const issue of sortedIssues) {
74
+ const lineIndex = issue.line;
75
+ if (lineIndex >= 0 && lineIndex < lines.length) {
76
+ const originalLine = lines[lineIndex];
77
+ const fix = generateFix(issue, originalLine, language);
78
+
79
+ if (fix.fixed && fix.fixed !== originalLine) {
80
+ lines[lineIndex] = fix.fixed;
81
+ fixes.push({
82
+ line: lineIndex + 1,
83
+ rule: issue.ruleId,
84
+ original: originalLine.trim(),
85
+ fixed: fix.fixed.trim(),
86
+ description: fix.description
87
+ });
88
+ }
89
+ }
90
+ }
91
+
92
+ // Determine verbosity (default: compact)
93
+ const level = verbosity || 'compact';
94
+ const fixedContent = lines.join('\n');
95
+
96
+ let result;
97
+ switch (level) {
98
+ case 'minimal':
99
+ result = formatFixMinimal(file_path, fixes);
100
+ break;
101
+ case 'full':
102
+ result = formatFixFull(file_path, fixes, fixedContent);
103
+ break;
104
+ case 'compact':
105
+ default:
106
+ result = formatFixCompact(file_path, fixes);
107
+ }
108
+
109
+ return {
110
+ content: [{
111
+ type: "text",
112
+ text: JSON.stringify(result, null, 2)
113
+ }]
114
+ };
115
+ }
@@ -0,0 +1,154 @@
1
+ import { z } from "zod";
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { isHallucinated, getTotalPackages } from './check-package.js';
4
+
5
+ // Package import patterns by ecosystem
6
+ const IMPORT_PATTERNS = {
7
+ dart: [
8
+ /import\s+['"]package:([^\/'"]+)/g,
9
+ /dependencies:\s*\n(?:\s+(\w+):\s*[\^~]?[\d.]+\n)+/g
10
+ ],
11
+ perl: [
12
+ /use\s+([\w:]+)/g,
13
+ /require\s+([\w:]+)/g
14
+ ],
15
+ raku: [
16
+ /use\s+([\w:]+)/g,
17
+ /need\s+([\w:]+)/g
18
+ ],
19
+ npm: [
20
+ /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
21
+ /from\s+['"]([^'"]+)['"]/g,
22
+ /import\s+['"]([^'"]+)['"]/g
23
+ ],
24
+ pypi: [
25
+ /^import\s+([\w]+)/gm,
26
+ /^from\s+([\w]+)/gm
27
+ ],
28
+ rubygems: [
29
+ /require\s+['"]([^'"]+)['"]/g,
30
+ /gem\s+['"]([^'"]+)['"]/g,
31
+ /require_relative\s+['"]([^'"]+)['"]/g
32
+ ],
33
+ crates: [
34
+ /use\s+([\w_]+)/g,
35
+ /extern\s+crate\s+([\w_]+)/g,
36
+ /^\s*[\w_-]+\s*=/gm // Cargo.toml dependencies
37
+ ]
38
+ };
39
+
40
+ // Extract package names from code
41
+ export function extractPackages(code, ecosystem) {
42
+ const packages = new Set();
43
+ const patterns = IMPORT_PATTERNS[ecosystem] || [];
44
+
45
+ for (const pattern of patterns) {
46
+ const regex = new RegExp(pattern.source, pattern.flags);
47
+ let match;
48
+ while ((match = regex.exec(code)) !== null) {
49
+ const pkg = match[1];
50
+ if (pkg && !pkg.startsWith('.') && !pkg.startsWith('/')) {
51
+ // Normalize package name (handle scoped packages, subpaths)
52
+ const basePkg = pkg.split('/')[0].replace(/^@/, '');
53
+ packages.add(basePkg);
54
+ }
55
+ }
56
+ }
57
+
58
+ return Array.from(packages);
59
+ }
60
+
61
+ // Schema for scan_packages tool
62
+ export const scanPackagesSchema = {
63
+ file_path: z.string().describe("Path to the file to scan"),
64
+ ecosystem: z.enum(["dart", "perl", "raku", "npm", "pypi", "rubygems", "crates"]).describe("The package ecosystem (dart=pub.dev, perl=CPAN, raku=raku.land, npm=npmjs, pypi=PyPI, rubygems=RubyGems, crates=crates.io)"),
65
+ verbosity: z.enum(['minimal', 'compact', 'full']).optional().describe("Response detail level: 'minimal' (counts only), 'compact' (default), 'full' (all details)")
66
+ };
67
+
68
+ // Handler for scan_packages tool
69
+ export async function scanPackages({ file_path, ecosystem, verbosity }) {
70
+ if (!existsSync(file_path)) {
71
+ return {
72
+ content: [{ type: "text", text: JSON.stringify({ error: "File not found" }) }]
73
+ };
74
+ }
75
+
76
+ const code = readFileSync(file_path, 'utf-8');
77
+ const packages = extractPackages(code, ecosystem);
78
+
79
+ const results = packages.map(pkg => {
80
+ const check = isHallucinated(pkg, ecosystem);
81
+ if (check.unknown) {
82
+ return { package: pkg, status: "unknown", reason: check.reason };
83
+ }
84
+ return {
85
+ package: pkg,
86
+ legitimate: !check.hallucinated,
87
+ hallucinated: check.hallucinated,
88
+ bloom_filter: !!check.bloomFilter,
89
+ confidence: check.bloomFilter ? "medium" : "high"
90
+ };
91
+ });
92
+
93
+ const hallucinated = results.filter(r => r.hallucinated);
94
+ const legitimate = results.filter(r => r.legitimate);
95
+ const unknown = results.filter(r => r.status === "unknown");
96
+ const totalKnown = getTotalPackages(ecosystem);
97
+
98
+ // Determine verbosity (default: compact)
99
+ const level = verbosity || 'compact';
100
+
101
+ const recommendation = hallucinated.length > 0
102
+ ? `Found ${hallucinated.length} potentially hallucinated package(s): ${hallucinated.map(r => r.package).join(', ')}`
103
+ : unknown.length > 0
104
+ ? `${unknown.length} package(s) could not be verified (no data available for ${ecosystem})`
105
+ : "All packages verified as legitimate";
106
+
107
+ let result;
108
+ switch (level) {
109
+ case 'minimal':
110
+ result = {
111
+ file: file_path,
112
+ ecosystem,
113
+ total: packages.length,
114
+ hallucinated: hallucinated.length,
115
+ legitimate: legitimate.length,
116
+ unknown: unknown.length,
117
+ message: recommendation
118
+ };
119
+ break;
120
+ case 'full':
121
+ result = {
122
+ file: file_path,
123
+ ecosystem,
124
+ total_packages_found: packages.length,
125
+ legitimate_count: legitimate.length,
126
+ hallucinated_count: hallucinated.length,
127
+ unknown_count: unknown.length,
128
+ known_packages_in_registry: totalKnown,
129
+ hallucinated_packages: hallucinated.map(r => r.package),
130
+ legitimate_packages: legitimate.map(r => r.package),
131
+ all_results: results,
132
+ recommendation
133
+ };
134
+ break;
135
+ case 'compact':
136
+ default:
137
+ result = {
138
+ file: file_path,
139
+ ecosystem,
140
+ total_packages_found: packages.length,
141
+ hallucinated_count: hallucinated.length,
142
+ hallucinated_packages: hallucinated.map(r => r.package),
143
+ legitimate_count: legitimate.length,
144
+ recommendation
145
+ };
146
+ }
147
+
148
+ return {
149
+ content: [{
150
+ type: "text",
151
+ text: JSON.stringify(result, null, 2)
152
+ }]
153
+ };
154
+ }