agent-security-scanner-mcp 1.0.2 → 1.1.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A powerful MCP (Model Context Protocol) server for real-time security vulnerability scanning. Integrates with Claude Desktop and Claude Code to automatically detect and fix security issues as you code.
4
4
 
5
- **165 Semgrep-aligned security rules | 105 auto-fix templates | 100% fix coverage**
5
+ **165 Semgrep-aligned security rules | 105 auto-fix templates | 100% fix coverage | Package hallucination detection**
6
6
 
7
7
  ## Features
8
8
 
@@ -11,6 +11,7 @@ A powerful MCP (Model Context Protocol) server for real-time security vulnerabil
11
11
  - **Multi-language support** - JavaScript, TypeScript, Python, Java, Go, Dockerfile
12
12
  - **Semgrep-compatible** - Rules aligned with Semgrep registry format
13
13
  - **CWE & OWASP mapped** - Every rule includes CWE and OWASP references
14
+ - **Hallucination detection** - Detect AI-invented package names (Dart, Perl, Raku)
14
15
 
15
16
  ## Installation
16
17
 
@@ -127,6 +128,115 @@ Returns:
127
128
 
128
129
  List all 105 available auto-fix templates.
129
130
 
131
+ ---
132
+
133
+ ## Package Hallucination Detection
134
+
135
+ Detect AI-hallucinated package names that don't exist in official registries. Prevents supply chain attacks where attackers register fake package names suggested by AI.
136
+
137
+ ### `check_package`
138
+
139
+ Check if a single package name is legitimate or potentially hallucinated.
140
+
141
+ ```
142
+ Parameters:
143
+ package_name (string): The package name to verify
144
+ ecosystem (enum): "dart", "perl", "raku", "npm", "pypi"
145
+
146
+ Returns:
147
+ - legitimate: true/false
148
+ - hallucinated: true/false
149
+ - confidence: "high"
150
+ - recommendation: Action to take
151
+ ```
152
+
153
+ **Example:**
154
+ ```json
155
+ {
156
+ "package": "flutter_animations",
157
+ "ecosystem": "dart",
158
+ "legitimate": true,
159
+ "hallucinated": false,
160
+ "confidence": "high",
161
+ "total_known_packages": 64721,
162
+ "recommendation": "Package exists in registry - safe to use"
163
+ }
164
+ ```
165
+
166
+ ### `scan_packages`
167
+
168
+ Scan a code file and detect all potentially hallucinated package imports.
169
+
170
+ ```
171
+ Parameters:
172
+ file_path (string): Path to the file to scan
173
+ ecosystem (enum): "dart", "perl", "raku", "npm", "pypi"
174
+
175
+ Returns:
176
+ - List of all packages found
177
+ - Which are legitimate vs hallucinated
178
+ - Recommendation
179
+ ```
180
+
181
+ **Example output:**
182
+ ```json
183
+ {
184
+ "file": "/path/to/main.dart",
185
+ "ecosystem": "dart",
186
+ "total_packages_found": 5,
187
+ "legitimate_count": 4,
188
+ "hallucinated_count": 1,
189
+ "hallucinated_packages": ["fake_flutter_pkg"],
190
+ "legitimate_packages": ["flutter", "http", "provider", "shared_preferences"],
191
+ "recommendation": "⚠️ Found 1 potentially hallucinated package(s): fake_flutter_pkg"
192
+ }
193
+ ```
194
+
195
+ ### `list_package_stats`
196
+
197
+ Show statistics about loaded package lists.
198
+
199
+ ```json
200
+ {
201
+ "package_lists": [
202
+ { "ecosystem": "dart", "packages_loaded": 64721, "status": "ready" },
203
+ { "ecosystem": "perl", "packages_loaded": 1000, "status": "ready" },
204
+ { "ecosystem": "raku", "packages_loaded": 2626, "status": "ready" }
205
+ ],
206
+ "total_packages": 68347
207
+ }
208
+ ```
209
+
210
+ ### Adding Custom Package Lists
211
+
212
+ Add your own package lists to `packages/` directory:
213
+
214
+ ```bash
215
+ # Format: one package name per line
216
+ packages/
217
+ ├── dart.txt # 64,721 packages
218
+ ├── perl.txt # 1,000 packages
219
+ ├── raku.txt # 2,626 packages
220
+ ├── npm.txt # Add your own
221
+ └── pypi.txt # Add your own
222
+ ```
223
+
224
+ ### Fetching Package Lists
225
+
226
+ ```bash
227
+ # Using the included script
228
+ cd mcp-server
229
+ pip install datasets
230
+ python scripts/fetch-packages.py
231
+ ```
232
+
233
+ Package lists are sourced from:
234
+ - **Dart:** [dchitimalla1/dart-20250529](https://huggingface.co/datasets/dchitimalla1/dart-20250529)
235
+ - **Perl:** [dchitimalla1/perl-20250530](https://huggingface.co/datasets/dchitimalla1/perl-20250530)
236
+ - **Raku:** [dchitimalla1/raku-20250523](https://huggingface.co/datasets/dchitimalla1/raku-20250523)
237
+
238
+ ---
239
+
130
240
  ## Security Rules (165 total)
131
241
 
132
242
  ### By Language
package/index.js CHANGED
@@ -824,6 +824,235 @@ server.tool(
824
824
  }
825
825
  );
826
826
 
827
+ // ===========================================
828
+ // PACKAGE HALLUCINATION DETECTION
829
+ // ===========================================
830
+
831
+ // Load legitimate package lists into memory (hash sets for O(1) lookup)
832
+ const LEGITIMATE_PACKAGES = {
833
+ dart: new Set(),
834
+ perl: new Set(),
835
+ raku: new Set(),
836
+ npm: new Set(),
837
+ pypi: new Set()
838
+ };
839
+
840
+ // Package import patterns by ecosystem
841
+ const IMPORT_PATTERNS = {
842
+ dart: [
843
+ /import\s+['"]package:([^\/'"]+)/g,
844
+ /dependencies:\s*\n(?:\s+(\w+):\s*[\^~]?[\d.]+\n)+/g
845
+ ],
846
+ perl: [
847
+ /use\s+([\w:]+)/g,
848
+ /require\s+([\w:]+)/g
849
+ ],
850
+ raku: [
851
+ /use\s+([\w:]+)/g,
852
+ /need\s+([\w:]+)/g
853
+ ],
854
+ npm: [
855
+ /require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
856
+ /from\s+['"]([^'"]+)['"]/g,
857
+ /import\s+['"]([^'"]+)['"]/g
858
+ ],
859
+ pypi: [
860
+ /^import\s+([\w]+)/gm,
861
+ /^from\s+([\w]+)/gm
862
+ ]
863
+ };
864
+
865
+ // Load package lists on startup
866
+ function loadPackageLists() {
867
+ const packagesDir = join(__dirname, 'packages');
868
+
869
+ for (const ecosystem of Object.keys(LEGITIMATE_PACKAGES)) {
870
+ const filePath = join(packagesDir, `${ecosystem}.txt`);
871
+ try {
872
+ if (existsSync(filePath)) {
873
+ const content = readFileSync(filePath, 'utf-8');
874
+ const packages = content.split('\n').filter(p => p.trim());
875
+ LEGITIMATE_PACKAGES[ecosystem] = new Set(packages);
876
+ console.error(`Loaded ${packages.length} ${ecosystem} packages`);
877
+ }
878
+ } catch (error) {
879
+ console.error(`Warning: Could not load ${ecosystem} packages: ${error.message}`);
880
+ }
881
+ }
882
+ }
883
+
884
+ // Extract package names from code
885
+ function extractPackages(code, ecosystem) {
886
+ const packages = new Set();
887
+ const patterns = IMPORT_PATTERNS[ecosystem] || [];
888
+
889
+ for (const pattern of patterns) {
890
+ const regex = new RegExp(pattern.source, pattern.flags);
891
+ let match;
892
+ while ((match = regex.exec(code)) !== null) {
893
+ const pkg = match[1];
894
+ if (pkg && !pkg.startsWith('.') && !pkg.startsWith('/')) {
895
+ // Normalize package name (handle scoped packages, subpaths)
896
+ const basePkg = pkg.split('/')[0].replace(/^@/, '');
897
+ packages.add(basePkg);
898
+ }
899
+ }
900
+ }
901
+
902
+ return Array.from(packages);
903
+ }
904
+
905
+ // Check if a package is hallucinated
906
+ function isHallucinated(packageName, ecosystem) {
907
+ const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
908
+ if (!legitPackages || legitPackages.size === 0) {
909
+ return { unknown: true, reason: `No package list loaded for ${ecosystem}` };
910
+ }
911
+ return { hallucinated: !legitPackages.has(packageName) };
912
+ }
913
+
914
+ // Register check_package tool
915
+ server.tool(
916
+ "check_package",
917
+ "Check if a package name is legitimate or potentially hallucinated (AI-invented)",
918
+ {
919
+ package_name: z.string().describe("The package name to verify"),
920
+ ecosystem: z.enum(["dart", "perl", "raku", "npm", "pypi"]).describe("The package ecosystem")
921
+ },
922
+ async ({ package_name, ecosystem }) => {
923
+ const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
924
+ const totalPackages = legitPackages?.size || 0;
925
+
926
+ if (totalPackages === 0) {
927
+ return {
928
+ content: [{
929
+ type: "text",
930
+ text: JSON.stringify({
931
+ package: package_name,
932
+ ecosystem,
933
+ status: "unknown",
934
+ reason: `No package list loaded for ${ecosystem}. Add packages/${ecosystem}.txt`,
935
+ suggestion: "Load package list or verify manually at the package registry"
936
+ }, null, 2)
937
+ }]
938
+ };
939
+ }
940
+
941
+ const exists = legitPackages.has(package_name);
942
+
943
+ return {
944
+ content: [{
945
+ type: "text",
946
+ text: JSON.stringify({
947
+ package: package_name,
948
+ ecosystem,
949
+ legitimate: exists,
950
+ hallucinated: !exists,
951
+ confidence: "high",
952
+ total_known_packages: totalPackages,
953
+ recommendation: exists
954
+ ? "Package exists in registry - safe to use"
955
+ : "⚠️ POTENTIAL HALLUCINATION - Package not found in registry. Verify before using!"
956
+ }, null, 2)
957
+ }]
958
+ };
959
+ }
960
+ );
961
+
962
+ // Register scan_packages tool
963
+ server.tool(
964
+ "scan_packages",
965
+ "Scan code for package imports and check for hallucinated (AI-invented) packages",
966
+ {
967
+ file_path: z.string().describe("Path to the file to scan"),
968
+ ecosystem: z.enum(["dart", "perl", "raku", "npm", "pypi"]).describe("The package ecosystem")
969
+ },
970
+ async ({ file_path, ecosystem }) => {
971
+ if (!existsSync(file_path)) {
972
+ return {
973
+ content: [{ type: "text", text: JSON.stringify({ error: "File not found" }) }]
974
+ };
975
+ }
976
+
977
+ const code = readFileSync(file_path, 'utf-8');
978
+ const packages = extractPackages(code, ecosystem);
979
+ const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
980
+ const totalKnown = legitPackages?.size || 0;
981
+
982
+ if (totalKnown === 0) {
983
+ return {
984
+ content: [{
985
+ type: "text",
986
+ text: JSON.stringify({
987
+ file: file_path,
988
+ ecosystem,
989
+ packages_found: packages,
990
+ status: "unknown",
991
+ reason: `No package list loaded for ${ecosystem}`
992
+ }, null, 2)
993
+ }]
994
+ };
995
+ }
996
+
997
+ const results = packages.map(pkg => ({
998
+ package: pkg,
999
+ legitimate: legitPackages.has(pkg),
1000
+ hallucinated: !legitPackages.has(pkg)
1001
+ }));
1002
+
1003
+ const hallucinated = results.filter(r => r.hallucinated);
1004
+ const legitimate = results.filter(r => r.legitimate);
1005
+
1006
+ return {
1007
+ content: [{
1008
+ type: "text",
1009
+ text: JSON.stringify({
1010
+ file: file_path,
1011
+ ecosystem,
1012
+ total_packages_found: packages.length,
1013
+ legitimate_count: legitimate.length,
1014
+ hallucinated_count: hallucinated.length,
1015
+ known_packages_in_registry: totalKnown,
1016
+ hallucinated_packages: hallucinated.map(r => r.package),
1017
+ legitimate_packages: legitimate.map(r => r.package),
1018
+ all_results: results,
1019
+ recommendation: hallucinated.length > 0
1020
+ ? `⚠️ Found ${hallucinated.length} potentially hallucinated package(s): ${hallucinated.map(r => r.package).join(', ')}`
1021
+ : "✅ All packages verified as legitimate"
1022
+ }, null, 2)
1023
+ }]
1024
+ };
1025
+ }
1026
+ );
1027
+
1028
+ // Register list_package_stats tool
1029
+ server.tool(
1030
+ "list_package_stats",
1031
+ "List statistics about loaded package lists for hallucination detection",
1032
+ {},
1033
+ async () => {
1034
+ const stats = Object.entries(LEGITIMATE_PACKAGES).map(([ecosystem, packages]) => ({
1035
+ ecosystem,
1036
+ packages_loaded: packages.size,
1037
+ status: packages.size > 0 ? "ready" : "not loaded"
1038
+ }));
1039
+
1040
+ return {
1041
+ content: [{
1042
+ type: "text",
1043
+ text: JSON.stringify({
1044
+ package_lists: stats,
1045
+ total_packages: stats.reduce((sum, s) => sum + s.packages_loaded, 0),
1046
+ usage: "Use check_package or scan_packages to detect hallucinated packages"
1047
+ }, null, 2)
1048
+ }]
1049
+ };
1050
+ }
1051
+ );
1052
+
1053
+ // Load package lists on module initialization
1054
+ loadPackageLists();
1055
+
827
1056
  // Start the server with stdio transport
828
1057
  async function main() {
829
1058
  const transport = new StdioServerTransport();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-security-scanner-mcp",
3
- "version": "1.0.2",
4
- "description": "MCP server for security vulnerability scanning - detects SQL injection, XSS, command injection, hardcoded secrets, and more",
3
+ "version": "1.1.0",
4
+ "description": "MCP server for security scanning & package hallucination detection - SQL injection, XSS, secrets, and AI-invented package detection",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "bin": {
@@ -21,7 +21,10 @@
21
21
  "code-analysis",
22
22
  "sql-injection",
23
23
  "xss",
24
- "secrets-detection"
24
+ "secrets-detection",
25
+ "hallucination-detection",
26
+ "package-verification",
27
+ "supply-chain-security"
25
28
  ],
26
29
  "author": "",
27
30
  "license": "MIT",
@@ -43,6 +46,7 @@
43
46
  "files": [
44
47
  "index.js",
45
48
  "analyzer.py",
46
- "rules/**"
49
+ "rules/**",
50
+ "packages/**"
47
51
  ]
48
52
  }