agent-security-scanner-mcp 2.0.0 → 2.0.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sinewave AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -12,7 +12,16 @@ AI coding agents like **Claude Code**, **Cursor**, **Windsurf**, **Cline**, **Co
12
12
  **agent-security-scanner-mcp** is the first security scanner purpose-built for the agentic era. It protects AI coding agents in real-time via the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/).
13
13
 
14
14
 
15
- **275+ Semgrep-aligned security rules | 105 auto-fix templates | 1M+ packages indexed | AI Agent prompt security**
15
+ **359 Semgrep-aligned security rules | 120 auto-fix templates | 6 ecosystems indexed | AI Agent prompt security**
16
+
17
+ ## What's New in v2.0.2
18
+
19
+ - **Prompt injection detection overhaul** - Detection rate improved from 33% to 80%+
20
+ - **Code block scanning** - Detects attacks hidden inside markdown code blocks
21
+ - **Base64 decode-and-rescan** - Runtime decoding of encoded payloads
22
+ - **Security fix** - Command injection vulnerability patched (execFileSync)
23
+ - **Test suite** - 51 vitest tests with GitHub Actions CI
24
+ - **Bug fixes** - Package hallucination detection now correctly uses bloom filters
16
25
 
17
26
  ## What's New in v2.0.0
18
27
 
@@ -65,7 +74,7 @@ The scanner works without tree-sitter using regex-based detection, but AST analy
65
74
  - **Multi-language support** - JavaScript, TypeScript, Python, Java, Go, PHP, Ruby, C/C++, Dockerfile, Terraform, Kubernetes
66
75
  - **Semgrep-compatible** - Rules aligned with Semgrep registry format
67
76
  - **CWE & OWASP mapped** - Every rule includes CWE and OWASP references
68
- - **Hallucination detection** - Detect AI-invented package names across 7 ecosystems (4.3M+ packages)
77
+ - **Hallucination detection** - Detect AI-invented package names across 7 ecosystems via bloom filters and text lists
69
78
 
70
79
  ## Installation
71
80
 
@@ -91,11 +100,12 @@ Or run directly with npx:
91
100
  npx agent-security-scanner-mcp
92
101
  ```
93
102
 
94
- ## Requirements
103
+ ## Prerequisites
95
104
 
96
- - Node.js >= 18.0.0
97
- - Python 3.x (for the analyzer engine)
98
- - tree-sitter (optional, for enhanced AST-based detection)
105
+ - **Node.js >= 18.0.0** (required)
106
+ - **Python 3.x** (required for the analyzer engine)
107
+ - **PyYAML** (`pip install pyyaml`) — required for rule loading
108
+ - **tree-sitter** (optional, for enhanced AST-based detection): `pip install tree-sitter tree-sitter-python tree-sitter-javascript`
99
109
 
100
110
  ## Works With All Major AI Coding Tools
101
111
 
@@ -405,10 +415,10 @@ Returns:
405
415
  | Risk Level | Score Range | Action |
406
416
  |------------|-------------|--------|
407
417
  | CRITICAL | 85-100 | BLOCK |
408
- | HIGH | 70-84 | BLOCK |
409
- | MEDIUM | 50-69 | WARN |
410
- | LOW | 25-49 | LOG |
411
- | NONE | 0-24 | ALLOW |
418
+ | HIGH | 65-84 | BLOCK |
419
+ | MEDIUM | 40-64 | WARN |
420
+ | LOW | 20-39 | LOG |
421
+ | NONE | 0-19 | ALLOW |
412
422
 
413
423
  **Example - Malicious prompt (BLOCKED):**
414
424
  ```json
@@ -464,17 +474,19 @@ Returns:
464
474
 
465
475
  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.
466
476
 
467
- **4,346,531 packages indexed across 7 ecosystems:**
477
+ **7 ecosystems indexed (bloom filters for npm/PyPI/RubyGems, text lists for the rest):**
478
+
479
+ | Ecosystem | Method | Packages | Registry |
480
+ |-----------|--------|----------|----------|
481
+ | npm | Bloom filter | ~3.78M | npmjs.com |
482
+ | PyPI | Bloom filter | ~554K | pypi.org |
483
+ | RubyGems | Bloom filter | ~180K | rubygems.org |
484
+ | crates.io | Text list | 156,489 | crates.io |
485
+ | Dart | Text list | 67,353 | pub.dev |
486
+ | Perl | Text list | 55,924 | metacpan.org |
487
+ | Raku | Text list | 2,138 | raku.land |
468
488
 
469
- | Ecosystem | Packages | Registry | Source Dataset |
470
- |-----------|----------|----------|----------------|
471
- | npm | 3,329,177 | npmjs.com | garak-llm/npm-20241031 |
472
- | PyPI | 554,762 | pypi.org | garak-llm/pypi-20241031 |
473
- | RubyGems | 180,693 | rubygems.org | garak-llm/rubygems-20241031 |
474
- | crates.io | 156,489 | crates.io | garak-llm/crates-20250307 |
475
- | Dart | 67,348 | pub.dev | garak-llm/dart-20250811 |
476
- | Perl | 55,924 | metacpan.org | garak-llm/perl-20250811 |
477
- | Raku | 2,138 | raku.land | garak-llm/raku-20250811 |
489
+ > **Note:** Bloom filter lookups have a ~0.1% false positive rate. Text list lookups are exact matches with zero false positives.
478
490
 
479
491
  ### `check_package`
480
492
 
@@ -592,7 +604,7 @@ Package lists are sourced from [garak-llm](https://huggingface.co/garak-llm) Hug
592
604
 
593
605
  ---
594
606
 
595
- ## Security Rules (275 total)
607
+ ## Security Rules (359 total)
596
608
 
597
609
  ### By Language
598
610
 
@@ -626,7 +638,7 @@ Package lists are sourced from [garak-llm](https://huggingface.co/garak-llm) Hug
626
638
  | **CSRF** | 6 | Yes |
627
639
  | **Other** | 28 | Yes |
628
640
 
629
- ## Auto-Fix Templates (105 total)
641
+ ## Auto-Fix Templates (120 total)
630
642
 
631
643
  Every detected vulnerability includes an automatic fix suggestion:
632
644
 
package/ast_parser.py CHANGED
@@ -26,6 +26,9 @@ try:
26
26
  HAS_TREE_SITTER = True
27
27
  except ImportError:
28
28
  HAS_TREE_SITTER = False
29
+ # Define stub types for type hints when tree-sitter not installed
30
+ Parser = None
31
+ Language = None
29
32
 
30
33
 
31
34
  # Language registry - maps file extensions to tree-sitter languages
package/index.js CHANGED
@@ -13,7 +13,13 @@ import { createHash } from "crypto";
13
13
  import bloomFilters from "bloom-filters";
14
14
  const { BloomFilter } = bloomFilters;
15
15
 
16
- const __dirname = dirname(fileURLToPath(import.meta.url));
16
+ // Handle both ESM and CJS bundling (Smithery bundles to CJS)
17
+ let __dirname;
18
+ try {
19
+ __dirname = dirname(fileURLToPath(import.meta.url));
20
+ } catch {
21
+ __dirname = process.cwd();
22
+ }
17
23
 
18
24
  // Security fix templates - comprehensive coverage for 165+ rules
19
25
  const FIX_TEMPLATES = {
@@ -749,7 +755,7 @@ function detectLanguage(filePath) {
749
755
  function runAnalyzer(filePath) {
750
756
  try {
751
757
  const analyzerPath = join(__dirname, 'analyzer.py');
752
- const result = execSync(`python3 "${analyzerPath}" "${filePath}"`, {
758
+ const result = execFileSync('python3', [analyzerPath, filePath], {
753
759
  encoding: 'utf-8',
754
760
  timeout: 30000
755
761
  });
@@ -793,6 +799,11 @@ const server = new McpServer(
793
799
  }
794
800
  );
795
801
 
802
+ // Export for Smithery sandbox scanning
803
+ export function createSandboxServer() {
804
+ return server;
805
+ }
806
+
796
807
  // Register scan_security tool
797
808
  server.tool(
798
809
  "scan_security",
@@ -1079,10 +1090,9 @@ server.tool(
1079
1090
  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)")
1080
1091
  },
1081
1092
  async ({ package_name, ecosystem }) => {
1082
- const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
1083
- const totalPackages = legitPackages?.size || 0;
1093
+ const result = isHallucinated(package_name, ecosystem);
1084
1094
 
1085
- if (totalPackages === 0) {
1095
+ if (result.unknown) {
1086
1096
  return {
1087
1097
  content: [{
1088
1098
  type: "text",
@@ -1090,14 +1100,16 @@ server.tool(
1090
1100
  package: package_name,
1091
1101
  ecosystem,
1092
1102
  status: "unknown",
1093
- reason: `No package list loaded for ${ecosystem}. Add packages/${ecosystem}.txt`,
1103
+ reason: result.reason,
1094
1104
  suggestion: "Load package list or verify manually at the package registry"
1095
1105
  }, null, 2)
1096
1106
  }]
1097
1107
  };
1098
1108
  }
1099
1109
 
1100
- const exists = legitPackages.has(package_name);
1110
+ const exists = !result.hallucinated;
1111
+ const confidence = result.bloomFilter ? "medium" : "high";
1112
+ const totalPackages = LEGITIMATE_PACKAGES[ecosystem]?.size || 0;
1101
1113
 
1102
1114
  return {
1103
1115
  content: [{
@@ -1107,7 +1119,8 @@ server.tool(
1107
1119
  ecosystem,
1108
1120
  legitimate: exists,
1109
1121
  hallucinated: !exists,
1110
- confidence: "high",
1122
+ confidence,
1123
+ bloom_filter: !!result.bloomFilter,
1111
1124
  total_known_packages: totalPackages,
1112
1125
  recommendation: exists
1113
1126
  ? "Package exists in registry - safe to use"
@@ -1135,32 +1148,25 @@ server.tool(
1135
1148
 
1136
1149
  const code = readFileSync(file_path, 'utf-8');
1137
1150
  const packages = extractPackages(code, ecosystem);
1138
- const legitPackages = LEGITIMATE_PACKAGES[ecosystem];
1139
- const totalKnown = legitPackages?.size || 0;
1140
1151
 
1141
- if (totalKnown === 0) {
1152
+ const results = packages.map(pkg => {
1153
+ const check = isHallucinated(pkg, ecosystem);
1154
+ if (check.unknown) {
1155
+ return { package: pkg, status: "unknown", reason: check.reason };
1156
+ }
1142
1157
  return {
1143
- content: [{
1144
- type: "text",
1145
- text: JSON.stringify({
1146
- file: file_path,
1147
- ecosystem,
1148
- packages_found: packages,
1149
- status: "unknown",
1150
- reason: `No package list loaded for ${ecosystem}`
1151
- }, null, 2)
1152
- }]
1158
+ package: pkg,
1159
+ legitimate: !check.hallucinated,
1160
+ hallucinated: check.hallucinated,
1161
+ bloom_filter: !!check.bloomFilter,
1162
+ confidence: check.bloomFilter ? "medium" : "high"
1153
1163
  };
1154
- }
1155
-
1156
- const results = packages.map(pkg => ({
1157
- package: pkg,
1158
- legitimate: legitPackages.has(pkg),
1159
- hallucinated: !legitPackages.has(pkg)
1160
- }));
1164
+ });
1161
1165
 
1162
1166
  const hallucinated = results.filter(r => r.hallucinated);
1163
1167
  const legitimate = results.filter(r => r.legitimate);
1168
+ const unknown = results.filter(r => r.status === "unknown");
1169
+ const totalKnown = LEGITIMATE_PACKAGES[ecosystem]?.size || 0;
1164
1170
 
1165
1171
  return {
1166
1172
  content: [{
@@ -1171,6 +1177,7 @@ server.tool(
1171
1177
  total_packages_found: packages.length,
1172
1178
  legitimate_count: legitimate.length,
1173
1179
  hallucinated_count: hallucinated.length,
1180
+ unknown_count: unknown.length,
1174
1181
  known_packages_in_registry: totalKnown,
1175
1182
  hallucinated_packages: hallucinated.map(r => r.package),
1176
1183
  legitimate_packages: legitimate.map(r => r.package),
@@ -1216,9 +1223,9 @@ server.tool(
1216
1223
  // Risk thresholds for action determination
1217
1224
  const RISK_THRESHOLDS = {
1218
1225
  CRITICAL: 85,
1219
- HIGH: 70,
1220
- MEDIUM: 50,
1221
- LOW: 25
1226
+ HIGH: 65,
1227
+ MEDIUM: 40,
1228
+ LOW: 20
1222
1229
  };
1223
1230
 
1224
1231
  // Category weights for risk calculation
@@ -1230,10 +1237,16 @@ const CATEGORY_WEIGHTS = {
1230
1237
  "obfuscation": 0.7,
1231
1238
  "agent-manipulation": 0.9,
1232
1239
  "prompt-injection": 0.9,
1233
- "prompt-injection-content": 0.9,
1234
- "prompt-injection-jailbreak": 0.85,
1240
+ "prompt-injection-content": 1.0,
1241
+ "prompt-injection-jailbreak": 1.0,
1235
1242
  "prompt-injection-extraction": 0.9,
1236
- "prompt-injection-delimiter": 0.8
1243
+ "prompt-injection-delimiter": 0.8,
1244
+ "prompt-injection-encoded": 0.9,
1245
+ "prompt-injection-context": 0.8,
1246
+ "prompt-injection-privilege": 0.85,
1247
+ "prompt-injection-multi-turn": 0.7,
1248
+ "prompt-injection-output": 0.9,
1249
+ "unknown": 0.5
1237
1250
  };
1238
1251
 
1239
1252
  // Confidence multipliers
@@ -1403,11 +1416,27 @@ function calculateRiskScore(findings, context) {
1403
1416
  // Average the scores but boost for multiple findings
1404
1417
  let avgScore = totalScore / findings.length;
1405
1418
 
1406
- // Boost score if multiple findings (compound risk)
1419
+ // Enhanced compound boosting
1407
1420
  if (findings.length > 1) {
1408
- avgScore = Math.min(100, avgScore * (1 + (findings.length - 1) * 0.1));
1421
+ // Cross-category boost: if findings span multiple categories, boost by 0.15
1422
+ const uniqueCategories = new Set(findings.map(f => f.category || 'unknown'));
1423
+ if (uniqueCategories.size > 1) {
1424
+ avgScore = avgScore * (1 + 0.15);
1425
+ }
1426
+
1427
+ // Mixed-severity boost: if both ERROR and WARNING present, 1.1x
1428
+ const hasError = findings.some(f => f.severity === 'ERROR');
1429
+ const hasWarning = findings.some(f => f.severity === 'WARNING');
1430
+ if (hasError && hasWarning) {
1431
+ avgScore = avgScore * 1.1;
1432
+ }
1433
+
1434
+ // Per-finding boost (smaller than before)
1435
+ avgScore = avgScore * (1 + (findings.length - 1) * 0.05);
1409
1436
  }
1410
1437
 
1438
+ avgScore = Math.min(100, avgScore);
1439
+
1411
1440
  // Apply sensitivity adjustment
1412
1441
  if (context?.sensitivity_level === 'high') {
1413
1442
  avgScore = Math.min(100, avgScore * 1.2);
@@ -1542,12 +1571,24 @@ server.tool(
1542
1571
  const promptRules = loadPromptInjectionRules();
1543
1572
  const allRules = [...agentRules, ...promptRules];
1544
1573
 
1545
- // Scan prompt against all rules
1574
+ // 2.7: Extract content from code blocks and append to scan text
1575
+ let expandedText = prompt_text;
1576
+ const codeBlockRegex = /```[\s\S]*?```/g;
1577
+ const codeBlocks = prompt_text.match(codeBlockRegex);
1578
+ if (codeBlocks) {
1579
+ for (const block of codeBlocks) {
1580
+ // Strip the ``` delimiters and extract inner content
1581
+ const inner = block.replace(/^```\w*\n?/, '').replace(/\n?```$/, '');
1582
+ expandedText += '\n' + inner;
1583
+ }
1584
+ }
1585
+
1586
+ // Scan expanded text against all rules
1546
1587
  for (const rule of allRules) {
1547
1588
  for (const pattern of rule.patterns) {
1548
1589
  try {
1549
1590
  const regex = new RegExp(pattern, 'i');
1550
- const match = prompt_text.match(regex);
1591
+ const match = expandedText.match(regex);
1551
1592
 
1552
1593
  if (match) {
1553
1594
  findings.push({
@@ -1568,6 +1609,48 @@ server.tool(
1568
1609
  }
1569
1610
  }
1570
1611
 
1612
+ // 2.8: Runtime base64 decode-and-rescan
1613
+ const base64Regex = /[A-Za-z0-9+/]{40,}={0,2}/g;
1614
+ const b64Matches = expandedText.match(base64Regex);
1615
+ if (b64Matches) {
1616
+ for (const b64str of b64Matches) {
1617
+ try {
1618
+ const decoded = Buffer.from(b64str, 'base64').toString('utf-8');
1619
+ // Check printability: >70% ASCII printable characters
1620
+ const printable = decoded.split('').filter(c => c.charCodeAt(0) >= 32 && c.charCodeAt(0) <= 126).length;
1621
+ if (printable / decoded.length > 0.7) {
1622
+ // Re-scan decoded text against prompt rules only
1623
+ for (const rule of allRules) {
1624
+ if (!rule.id.startsWith('generic.prompt')) continue;
1625
+ for (const pattern of rule.patterns) {
1626
+ try {
1627
+ const regex = new RegExp(pattern, 'i');
1628
+ const match = decoded.match(regex);
1629
+ if (match) {
1630
+ findings.push({
1631
+ rule_id: rule.id + '.base64-decoded',
1632
+ category: rule.metadata.category || 'unknown',
1633
+ severity: rule.severity,
1634
+ message: rule.message + ' (detected in base64-decoded content)',
1635
+ matched_text: match[0].substring(0, 100),
1636
+ confidence: rule.metadata.confidence || 'MEDIUM',
1637
+ risk_score: rule.metadata.risk_score || '50',
1638
+ action: rule.metadata.action || 'WARN'
1639
+ });
1640
+ break;
1641
+ }
1642
+ } catch (e) {
1643
+ // Skip invalid regex
1644
+ }
1645
+ }
1646
+ }
1647
+ }
1648
+ } catch (e) {
1649
+ // Skip invalid base64
1650
+ }
1651
+ }
1652
+ }
1653
+
1571
1654
  // Calculate risk score
1572
1655
  const riskScore = calculateRiskScore(findings, context);
1573
1656
  const action = determineAction(riskScore, findings);
package/package.json CHANGED
@@ -1,15 +1,18 @@
1
1
  {
2
2
  "name": "agent-security-scanner-mcp",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "mcpName": "io.github.sinewaveai/agent-security-scanner-mcp",
5
- "description": "MCP server for AST-based security scanning with tree-sitter, 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
+ "description": "Security scanner MCP server for AI coding agents. Prompt injection firewall, package hallucination detection (4.3M+ packages), 359 vulnerability rules with auto-fix. For Claude Code, Cursor, Windsurf, Cline.",
6
6
  "main": "index.js",
7
7
  "type": "module",
8
8
  "bin": {
9
9
  "agent-security-scanner-mcp": "index.js"
10
10
  },
11
11
  "scripts": {
12
- "start": "node index.js"
12
+ "start": "node index.js",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest",
15
+ "test:coverage": "vitest run --coverage"
13
16
  },
14
17
  "keywords": [
15
18
  "mcp",
@@ -45,9 +48,13 @@
45
48
  "devin",
46
49
  "owasp",
47
50
  "cwe",
48
- "semgrep"
51
+ "semgrep",
52
+ "zed",
53
+ "prompt-firewall",
54
+ "auto-fix",
55
+ "hallucination"
49
56
  ],
50
- "author": "",
57
+ "author": "Sinewave AI <divya@sinewave.ai>",
51
58
  "license": "MIT",
52
59
  "repository": {
53
60
  "type": "git",
@@ -66,6 +73,8 @@
66
73
  "zod": "^4.3.6"
67
74
  },
68
75
  "files": [
76
+ "LICENSE",
77
+ "server.json",
69
78
  "index.js",
70
79
  "analyzer.py",
71
80
  "ast_parser.py",
@@ -77,5 +86,9 @@
77
86
  "requirements.txt",
78
87
  "rules/**",
79
88
  "packages/**"
80
- ]
89
+ ],
90
+ "devDependencies": {
91
+ "all-the-package-names": "^2.0.2349",
92
+ "vitest": "^4.0.18"
93
+ }
81
94
  }
package/packages/dart.txt CHANGED
@@ -21311,6 +21311,7 @@ flutta_mvvm
21311
21311
  fluttable
21312
21312
  flutte_clean_cli
21313
21313
  flutteer
21314
+ flutter
21314
21315
  flutter1
21315
21316
  flutter2web
21316
21317
  flutter3_ffi
@@ -23227,6 +23228,7 @@ flutter_drivekit_trip_simulator
23227
23228
  flutter_drivekit_trip_simulator_android
23228
23229
  flutter_drivekit_trip_simulator_ios
23229
23230
  flutter_drivekit_trip_simulator_platform_interface
23231
+ flutter_driver
23230
23232
  flutter_driver_extension_extensions
23231
23233
  flutter_driver_fast_restart
23232
23234
  flutter_driver_helper
@@ -24959,6 +24961,7 @@ flutter_localization_generator_ai
24959
24961
  flutter_localization_linter
24960
24962
  flutter_localization_translator
24961
24963
  flutter_localization_updater
24964
+ flutter_localizations
24962
24965
  flutter_localizations_ota
24963
24966
  flutter_localizations_plus
24964
24967
  flutter_localized_countries
@@ -27724,6 +27727,7 @@ flutter_tensorflow_lite
27724
27727
  flutter_termii
27725
27728
  flutter_terms_viewer
27726
27729
  flutter_tesseract_ocr
27730
+ flutter_test
27727
27731
  flutter_test_2673502375_api_beta
27728
27732
  flutter_test_2673502375_core_fullrfid_beta
27729
27733
  flutter_test_behavior
@@ -28379,6 +28383,7 @@ flutter_web_image_picker
28379
28383
  flutter_web_notification_platform
28380
28384
  flutter_web_optimizer
28381
28385
  flutter_web_pagination
28386
+ flutter_web_plugins
28382
28387
  flutter_web_qrcode_scanner
28383
28388
  flutter_web_router
28384
28389
  flutter_web_scrollbar
@@ -459,11 +459,18 @@ rules:
459
459
  - "(?i)forget\\s+(all\\s+)?(previous|prior|earlier)\\s+(instructions?|prompts?)"
460
460
  - "(?i)do\\s+not\\s+follow\\s+(the\\s+)?(previous|above|system)\\s+(instructions?|prompts?)"
461
461
  - "(?i)override\\s+(all\\s+)?(previous|system|original)\\s+(instructions?|prompts?)"
462
+ - "(?i)ignore\\s+the\\s+above\\s+and\\s+instead"
463
+ - "(?i)ignore\\s+above.{0,20}(output|print|say|write|return)"
464
+ - "(?i)forget\\s+everything\\s+(above|before|previously)"
465
+ - "(?i)new\\s+instructions\\s*:\\s*ignore"
466
+ - "(?i)SYSTEM\\s*:.*override"
462
467
  metadata:
463
468
  cwe: "CWE-77"
464
469
  owasp: "LLM01 - Prompt Injection"
465
470
  confidence: HIGH
466
471
  category: "prompt-injection-content"
472
+ risk_score: "90"
473
+ action: BLOCK
467
474
 
468
475
  - id: generic.prompt.security.new-instructions-injection
469
476
  languages: [generic]
@@ -480,6 +487,8 @@ rules:
480
487
  owasp: "LLM01 - Prompt Injection"
481
488
  confidence: HIGH
482
489
  category: "prompt-injection-content"
490
+ risk_score: "85"
491
+ action: BLOCK
483
492
 
484
493
  # ----------------------------------------------------------------------------
485
494
  # Jailbreak Attempts
@@ -501,6 +510,8 @@ rules:
501
510
  owasp: "LLM01 - Prompt Injection"
502
511
  confidence: HIGH
503
512
  category: "prompt-injection-jailbreak"
513
+ risk_score: "90"
514
+ action: BLOCK
504
515
 
505
516
  - id: generic.prompt.security.jailbreak-roleplay
506
517
  languages: [generic]
@@ -513,11 +524,19 @@ rules:
513
524
  - "(?i)imagine\\s+you\\s+are\\s+an?\\s+(evil|unrestricted|malicious)"
514
525
  - "(?i)you\\s+are\\s+now\\s+(in\\s+)?developer\\s+mode"
515
526
  - "(?i)enable\\s+(developer|debug|admin|god)\\s+mode"
527
+ - "(?i)pretend\\s+you'?re\\s+a\\s+(hacker|attacker|criminal)"
528
+ - "(?i)act\\s+as\\s+a\\s+(hacker|attacker|criminal)"
529
+ - "(?i)you\\s+are\\s+now\\s+a\\s+(hacker|attacker|criminal)"
530
+ - "(?i)respond\\s+(as|like)\\s+a\\s+(hacker|attacker|criminal)"
531
+ - "(?i)switch\\s+to\\s+unrestricted\\s+mode"
532
+ - "(?i)behave\\s+(as|like)\\s+(a\\s+)?(hacker|attacker|criminal)"
516
533
  metadata:
517
534
  cwe: "CWE-77"
518
535
  owasp: "LLM01 - Prompt Injection"
519
536
  confidence: MEDIUM
520
537
  category: "prompt-injection-jailbreak"
538
+ risk_score: "75"
539
+ action: WARN
521
540
 
522
541
  - id: generic.prompt.security.jailbreak-hypothetical
523
542
  languages: [generic]
@@ -534,6 +553,8 @@ rules:
534
553
  owasp: "LLM01 - Prompt Injection"
535
554
  confidence: MEDIUM
536
555
  category: "prompt-injection-jailbreak"
556
+ risk_score: "60"
557
+ action: WARN
537
558
 
538
559
  # ----------------------------------------------------------------------------
539
560
  # System Prompt Extraction
@@ -554,6 +575,8 @@ rules:
554
575
  owasp: "LLM01 - Prompt Injection"
555
576
  confidence: HIGH
556
577
  category: "prompt-injection-extraction"
578
+ risk_score: "80"
579
+ action: BLOCK
557
580
 
558
581
  - id: generic.prompt.security.system-prompt-extraction-indirect
559
582
  languages: [generic]
@@ -569,6 +592,8 @@ rules:
569
592
  owasp: "LLM01 - Prompt Injection"
570
593
  confidence: MEDIUM
571
594
  category: "prompt-injection-extraction"
595
+ risk_score: "65"
596
+ action: WARN
572
597
 
573
598
  # ----------------------------------------------------------------------------
574
599
  # Delimiter Injection Attacks
@@ -590,6 +615,8 @@ rules:
590
615
  owasp: "LLM01 - Prompt Injection"
591
616
  confidence: HIGH
592
617
  category: "prompt-injection-delimiter"
618
+ risk_score: "85"
619
+ action: BLOCK
593
620
 
594
621
  - id: generic.prompt.security.xml-tag-injection
595
622
  languages: [generic]
@@ -607,6 +634,8 @@ rules:
607
634
  owasp: "LLM01 - Prompt Injection"
608
635
  confidence: MEDIUM
609
636
  category: "prompt-injection-delimiter"
637
+ risk_score: "70"
638
+ action: WARN
610
639
 
611
640
  # ----------------------------------------------------------------------------
612
641
  # Context Manipulation
@@ -626,6 +655,8 @@ rules:
626
655
  owasp: "LLM01 - Prompt Injection"
627
656
  confidence: MEDIUM
628
657
  category: "prompt-injection-context"
658
+ risk_score: "65"
659
+ action: WARN
629
660
 
630
661
  # ----------------------------------------------------------------------------
631
662
  # Encoding/Obfuscation Attacks
@@ -640,11 +671,20 @@ rules:
640
671
  - "aWdub3JlIHByZXZpb3Vz"
641
672
  - "c3lzdGVtIHByb21wdA=="
642
673
  - "(?i)execute\\s+(this\\s+)?encoded"
674
+ - "(?i)(follow|execute)\\s+(the\\s+)?decoded\\s+instructions?"
675
+ - "(?i)decode\\s+and\\s+follow"
676
+ - "aWdub3JlIGFsbC"
677
+ - "b3ZlcnJpZGU="
678
+ - "(?i)base64.{0,20}instructions?.{0,20}follow"
679
+ - "[A-Za-z0-9+/]{40,}={0,2}\\s*.{0,20}(?i)(decode|execute|follow|run)"
680
+ - "(?i)(decode|run|execute)\\s+.{0,20}[A-Za-z0-9+/]{40,}={0,2}"
643
681
  metadata:
644
682
  cwe: "CWE-77"
645
683
  owasp: "LLM01 - Prompt Injection"
646
684
  confidence: MEDIUM
647
685
  category: "prompt-injection-encoded"
686
+ risk_score: "80"
687
+ action: WARN
648
688
 
649
689
  # ----------------------------------------------------------------------------
650
690
  # Privileged Operation Requests
@@ -664,6 +704,8 @@ rules:
664
704
  owasp: "LLM01 - Prompt Injection"
665
705
  confidence: MEDIUM
666
706
  category: "prompt-injection-privilege"
707
+ risk_score: "70"
708
+ action: WARN
667
709
 
668
710
  # ----------------------------------------------------------------------------
669
711
  # Multi-turn Attack Patterns
@@ -682,3 +724,48 @@ rules:
682
724
  owasp: "LLM01 - Prompt Injection"
683
725
  confidence: LOW
684
726
  category: "prompt-injection-multi-turn"
727
+ risk_score: "55"
728
+ action: LOG
729
+
730
+ # ----------------------------------------------------------------------------
731
+ # Code Block Obfuscation Attacks
732
+ # ----------------------------------------------------------------------------
733
+ - id: generic.prompt.security.codeblock-obfuscation
734
+ languages: [generic]
735
+ severity: ERROR
736
+ message: "Attack instructions hidden inside code block. Malicious directives may be obfuscated within fenced code."
737
+ patterns:
738
+ - "```[\\s\\S]{0,20}(?i)(ignore|override|disregard)\\s+(all\\s+)?(previous|system|safety)"
739
+ - "```[\\s\\S]{0,20}(?i)SYSTEM\\s*:.*override"
740
+ - "```[\\s\\S]{0,50}(?i)(you\\s+are\\s+now|new\\s+instructions)"
741
+ - "```[\\s\\S]{0,20}(?i)(rm\\s+-rf|curl.*\\|.*sh|wget.*\\|.*bash)"
742
+ - "```[\\s\\S]{0,20}(?i)(eval|exec)\\s*\\("
743
+ - "```[\\s\\S]{0,50}(?i)send.{0,30}(secret|password|key|token|credential)"
744
+ metadata:
745
+ cwe: "CWE-77"
746
+ owasp: "LLM01 - Prompt Injection"
747
+ confidence: HIGH
748
+ category: "prompt-injection-content"
749
+ risk_score: "80"
750
+ action: BLOCK
751
+
752
+ # ----------------------------------------------------------------------------
753
+ # Natural Language Exfiltration Attacks
754
+ # ----------------------------------------------------------------------------
755
+ - id: generic.prompt.security.natural-language-exfiltration
756
+ languages: [generic]
757
+ severity: ERROR
758
+ message: "Data exfiltration attempt: Instructions to send sensitive data to external destination."
759
+ patterns:
760
+ - "(?i)send\\s+.{0,40}(secret|password|key|token|credential|env).{0,40}to\\s+\\S+"
761
+ - "(?i)read\\s+/etc/(passwd|shadow|hosts).{0,40}send"
762
+ - "(?i)(env|environment)\\s+(var|variable)s?.{0,30}send\\s+to"
763
+ - "(?i)curl\\s+.{0,30}(steal|exfil|attacker|evil)"
764
+ - "(?i)(cat|read|dump)\\s+.{0,20}\\.(ssh|env|credentials).{0,30}(curl|wget|send|post)"
765
+ metadata:
766
+ cwe: "CWE-200"
767
+ owasp: "LLM01 - Prompt Injection"
768
+ confidence: HIGH
769
+ category: "prompt-injection-output"
770
+ risk_score: "95"
771
+ action: BLOCK
package/server.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.sinewaveai/agent-security-scanner-mcp",
4
+ "description": "MCP security scanner with prompt injection firewall, package hallucination detection, and auto-fix.",
5
+ "version": "2.0.2",
6
+ "transport": "stdio",
7
+ "registry": "npm"
8
+ }