agent-security-scanner-mcp 1.4.3 → 1.4.5
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 +18 -8
- package/index.js +97 -30
- package/package.json +1 -1
- package/packages/pypi-bloom.json +1 -0
- package/packages/rubygems-bloom.json +1 -0
- package/packages/npm-bloom.json +0 -1
- package/packages/pypi.txt +0 -554762
- package/packages/rubygems.txt +0 -180693
package/README.md
CHANGED
|
@@ -2,16 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
A powerful MCP (Model Context Protocol) server for real-time security vulnerability scanning. Integrates with Claude Desktop, Claude Code, OpenCode.ai, Kilo Code, and any MCP-compatible client to automatically detect and fix security issues as you code.
|
|
4
4
|
|
|
5
|
-
**275+ Semgrep-aligned security rules | 105 auto-fix templates |
|
|
5
|
+
**275+ Semgrep-aligned security rules | 105 auto-fix templates | 1M+ packages indexed | AI Agent prompt security**
|
|
6
6
|
|
|
7
|
-
## What's New in v1.4.
|
|
7
|
+
## What's New in v1.4.5
|
|
8
8
|
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **Ruby & Rust support** - Coverage for RubyGems and crates.io
|
|
14
|
-
- **garak-llm datasets** - Using official package snapshots from Hugging Face
|
|
9
|
+
- **92% smaller package** - Only 2.7 MB (down from 84 MB)
|
|
10
|
+
- **6 ecosystems included** - PyPI, RubyGems, crates.io, pub.dev, CPAN, raku.land
|
|
11
|
+
- **npm available separately** - Use `agent-security-scanner-mcp-full` for npm support (adds 7.6 MB)
|
|
12
|
+
- **Bloom Filters** - Efficient storage for large package lists
|
|
15
13
|
|
|
16
14
|
## What's New in v1.3.0
|
|
17
15
|
|
|
@@ -40,10 +38,22 @@ A powerful MCP (Model Context Protocol) server for real-time security vulnerabil
|
|
|
40
38
|
|
|
41
39
|
## Installation
|
|
42
40
|
|
|
41
|
+
### Default Package (Lightweight - 2.7 MB)
|
|
42
|
+
|
|
43
43
|
```bash
|
|
44
44
|
npm install -g agent-security-scanner-mcp
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
Includes hallucination detection for: **PyPI, RubyGems, crates.io, pub.dev, CPAN, raku.land** (1M+ packages)
|
|
48
|
+
|
|
49
|
+
### Full Package (With npm - 8.7 MB)
|
|
50
|
+
|
|
51
|
+
If you need **npm/JavaScript hallucination detection** (3.3M packages):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install -g agent-security-scanner-mcp-full
|
|
55
|
+
```
|
|
56
|
+
|
|
47
57
|
Or run directly with npx:
|
|
48
58
|
|
|
49
59
|
```bash
|
package/index.js
CHANGED
|
@@ -941,14 +941,25 @@ const LEGITIMATE_PACKAGES = {
|
|
|
941
941
|
dart: new Set(),
|
|
942
942
|
perl: new Set(),
|
|
943
943
|
raku: new Set(),
|
|
944
|
-
npm: null,
|
|
945
|
-
pypi:
|
|
946
|
-
rubygems:
|
|
944
|
+
npm: null, // Uses Bloom Filter
|
|
945
|
+
pypi: null, // Uses Bloom Filter
|
|
946
|
+
rubygems: null, // Uses Bloom Filter
|
|
947
947
|
crates: new Set()
|
|
948
948
|
};
|
|
949
949
|
|
|
950
|
-
// Bloom
|
|
951
|
-
|
|
950
|
+
// Bloom Filters for large ecosystems (npm, pypi, rubygems)
|
|
951
|
+
const BLOOM_FILTERS = {
|
|
952
|
+
npm: null,
|
|
953
|
+
pypi: null,
|
|
954
|
+
rubygems: null
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
// Package counts for bloom filter ecosystems (filter doesn't store count)
|
|
958
|
+
const BLOOM_COUNTS = {
|
|
959
|
+
npm: 3329177,
|
|
960
|
+
pypi: 554762,
|
|
961
|
+
rubygems: 180693
|
|
962
|
+
};
|
|
952
963
|
|
|
953
964
|
// Package import patterns by ecosystem
|
|
954
965
|
const IMPORT_PATTERNS = {
|
|
@@ -989,21 +1000,23 @@ const IMPORT_PATTERNS = {
|
|
|
989
1000
|
function loadPackageLists() {
|
|
990
1001
|
const packagesDir = join(__dirname, 'packages');
|
|
991
1002
|
|
|
992
|
-
// Load
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1003
|
+
// Load Bloom Filter ecosystems (npm, pypi, rubygems)
|
|
1004
|
+
for (const ecosystem of Object.keys(BLOOM_FILTERS)) {
|
|
1005
|
+
try {
|
|
1006
|
+
const bloomPath = join(packagesDir, `${ecosystem}-bloom.json`);
|
|
1007
|
+
if (existsSync(bloomPath)) {
|
|
1008
|
+
const bloomData = JSON.parse(readFileSync(bloomPath, 'utf-8'));
|
|
1009
|
+
BLOOM_FILTERS[ecosystem] = BloomFilter.fromJSON(bloomData);
|
|
1010
|
+
console.error(`Loaded ${ecosystem} Bloom Filter (${BLOOM_COUNTS[ecosystem].toLocaleString()} packages)`);
|
|
1011
|
+
}
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
console.error(`Warning: Could not load ${ecosystem} Bloom Filter: ${error.message}`);
|
|
999
1014
|
}
|
|
1000
|
-
} catch (error) {
|
|
1001
|
-
console.error(`Warning: Could not load npm Bloom Filter: ${error.message}`);
|
|
1002
1015
|
}
|
|
1003
1016
|
|
|
1004
|
-
// Load other ecosystems using regular Set
|
|
1017
|
+
// Load other ecosystems using regular Set (smaller lists)
|
|
1005
1018
|
for (const ecosystem of Object.keys(LEGITIMATE_PACKAGES)) {
|
|
1006
|
-
if (ecosystem
|
|
1019
|
+
if (BLOOM_FILTERS.hasOwnProperty(ecosystem)) continue; // Uses Bloom Filter
|
|
1007
1020
|
|
|
1008
1021
|
const filePath = join(packagesDir, `${ecosystem}.txt`);
|
|
1009
1022
|
try {
|
|
@@ -1017,6 +1030,11 @@ function loadPackageLists() {
|
|
|
1017
1030
|
console.error(`Warning: Could not load ${ecosystem} packages: ${error.message}`);
|
|
1018
1031
|
}
|
|
1019
1032
|
}
|
|
1033
|
+
|
|
1034
|
+
// Note about npm if not loaded
|
|
1035
|
+
if (!BLOOM_FILTERS.npm) {
|
|
1036
|
+
console.error(`npm: not included (use agent-security-scanner-mcp-full for npm support)`);
|
|
1037
|
+
}
|
|
1020
1038
|
}
|
|
1021
1039
|
|
|
1022
1040
|
// Extract package names from code
|
|
@@ -1042,27 +1060,33 @@ function extractPackages(code, ecosystem) {
|
|
|
1042
1060
|
|
|
1043
1061
|
// Check if a package exists in the package list
|
|
1044
1062
|
function packageExists(packageName, ecosystem) {
|
|
1045
|
-
|
|
1046
|
-
|
|
1063
|
+
// Bloom Filter ecosystems
|
|
1064
|
+
if (BLOOM_FILTERS.hasOwnProperty(ecosystem)) {
|
|
1065
|
+
return BLOOM_FILTERS[ecosystem] ? BLOOM_FILTERS[ecosystem].has(packageName) : false;
|
|
1047
1066
|
}
|
|
1067
|
+
// Set-based ecosystems
|
|
1048
1068
|
const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
|
|
1049
1069
|
return legitPackages ? legitPackages.has(packageName) : false;
|
|
1050
1070
|
}
|
|
1051
1071
|
|
|
1052
1072
|
// Get package count for an ecosystem
|
|
1053
1073
|
function getPackageCount(ecosystem) {
|
|
1054
|
-
|
|
1055
|
-
|
|
1074
|
+
// Bloom Filter ecosystems
|
|
1075
|
+
if (BLOOM_FILTERS.hasOwnProperty(ecosystem)) {
|
|
1076
|
+
return BLOOM_FILTERS[ecosystem] ? BLOOM_COUNTS[ecosystem] : 0;
|
|
1056
1077
|
}
|
|
1078
|
+
// Set-based ecosystems
|
|
1057
1079
|
const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
|
|
1058
1080
|
return legitPackages ? legitPackages.size : 0;
|
|
1059
1081
|
}
|
|
1060
1082
|
|
|
1061
1083
|
// Check if ecosystem is loaded
|
|
1062
1084
|
function isEcosystemLoaded(ecosystem) {
|
|
1063
|
-
|
|
1064
|
-
|
|
1085
|
+
// Bloom Filter ecosystems
|
|
1086
|
+
if (BLOOM_FILTERS.hasOwnProperty(ecosystem)) {
|
|
1087
|
+
return BLOOM_FILTERS[ecosystem] !== null;
|
|
1065
1088
|
}
|
|
1089
|
+
// Set-based ecosystems
|
|
1066
1090
|
const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
|
|
1067
1091
|
return legitPackages && legitPackages.size > 0;
|
|
1068
1092
|
}
|
|
@@ -1084,6 +1108,22 @@ server.tool(
|
|
|
1084
1108
|
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)")
|
|
1085
1109
|
},
|
|
1086
1110
|
async ({ package_name, ecosystem }) => {
|
|
1111
|
+
// Check if npm is requested but not available
|
|
1112
|
+
if (ecosystem === 'npm' && !BLOOM_FILTERS.npm) {
|
|
1113
|
+
return {
|
|
1114
|
+
content: [{
|
|
1115
|
+
type: "text",
|
|
1116
|
+
text: JSON.stringify({
|
|
1117
|
+
package: package_name,
|
|
1118
|
+
ecosystem,
|
|
1119
|
+
status: "unavailable",
|
|
1120
|
+
reason: "npm hallucination detection not included in default package (saves 7.6 MB)",
|
|
1121
|
+
suggestion: "Use 'agent-security-scanner-mcp-full' for npm support, or verify manually at npmjs.com"
|
|
1122
|
+
}, null, 2)
|
|
1123
|
+
}]
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1087
1127
|
const totalPackages = getPackageCount(ecosystem);
|
|
1088
1128
|
|
|
1089
1129
|
if (!isEcosystemLoaded(ecosystem)) {
|
|
@@ -1094,8 +1134,8 @@ server.tool(
|
|
|
1094
1134
|
package: package_name,
|
|
1095
1135
|
ecosystem,
|
|
1096
1136
|
status: "unknown",
|
|
1097
|
-
reason: `No package list loaded for ${ecosystem}
|
|
1098
|
-
suggestion: "
|
|
1137
|
+
reason: `No package list loaded for ${ecosystem}`,
|
|
1138
|
+
suggestion: "Verify manually at the package registry"
|
|
1099
1139
|
}, null, 2)
|
|
1100
1140
|
}]
|
|
1101
1141
|
};
|
|
@@ -1137,6 +1177,22 @@ server.tool(
|
|
|
1137
1177
|
};
|
|
1138
1178
|
}
|
|
1139
1179
|
|
|
1180
|
+
// Check if npm is requested but not available
|
|
1181
|
+
if (ecosystem === 'npm' && !BLOOM_FILTERS.npm) {
|
|
1182
|
+
return {
|
|
1183
|
+
content: [{
|
|
1184
|
+
type: "text",
|
|
1185
|
+
text: JSON.stringify({
|
|
1186
|
+
file: file_path,
|
|
1187
|
+
ecosystem,
|
|
1188
|
+
status: "unavailable",
|
|
1189
|
+
reason: "npm hallucination detection not included in default package (saves 7.6 MB)",
|
|
1190
|
+
suggestion: "Use 'agent-security-scanner-mcp-full' for npm support, or verify manually at npmjs.com"
|
|
1191
|
+
}, null, 2)
|
|
1192
|
+
}]
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1140
1196
|
const code = readFileSync(file_path, 'utf-8');
|
|
1141
1197
|
const packages = extractPackages(code, ecosystem);
|
|
1142
1198
|
const totalKnown = getPackageCount(ecosystem);
|
|
@@ -1194,12 +1250,23 @@ server.tool(
|
|
|
1194
1250
|
{},
|
|
1195
1251
|
async () => {
|
|
1196
1252
|
const ecosystems = ['npm', 'pypi', 'rubygems', 'crates', 'dart', 'perl', 'raku'];
|
|
1197
|
-
const stats = ecosystems.map(ecosystem =>
|
|
1198
|
-
ecosystem
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1253
|
+
const stats = ecosystems.map(ecosystem => {
|
|
1254
|
+
const loaded = isEcosystemLoaded(ecosystem);
|
|
1255
|
+
let status = loaded ? "ready" : "not loaded";
|
|
1256
|
+
let storage = BLOOM_FILTERS.hasOwnProperty(ecosystem) ? "bloom filter" : "hash set";
|
|
1257
|
+
|
|
1258
|
+
// npm is not included in default package
|
|
1259
|
+
if (ecosystem === 'npm' && !loaded) {
|
|
1260
|
+
status = "not included (use -full package)";
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
return {
|
|
1264
|
+
ecosystem,
|
|
1265
|
+
packages_loaded: loaded ? getPackageCount(ecosystem) : 0,
|
|
1266
|
+
status,
|
|
1267
|
+
storage
|
|
1268
|
+
};
|
|
1269
|
+
});
|
|
1203
1270
|
|
|
1204
1271
|
return {
|
|
1205
1272
|
content: [{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-security-scanner-mcp",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.5",
|
|
4
4
|
"description": "MCP server for security scanning, AI agent prompt security & package hallucination detection. Works with Claude Desktop, Claude Code, OpenCode, Kilo Code. Detects SQL injection, XSS, secrets, prompt attacks, and AI-invented packages.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|