kavach-shield 0.1.0__tar.gz

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.
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: kavach-shield
3
+ Version: 0.1.0
4
+ Summary: Security middleware for Model Context Protocol (MCP) - detection engine and rules
5
+ Home-page: https://github.com/shivamnamdeo0101/kavach-agent-ecosystem
6
+ Author: Shivam Namdeo
7
+ Author-email: shivamnamdeo0101@gmail.com
8
+ License: MIT
9
+ Requires-Python: >=3.7
10
+ Requires-Dist: kavach-logger>=0.1.0
11
+ Requires-Dist: kavach-mcp-events>=0.1.0
12
+ Dynamic: author
13
+ Dynamic: author-email
14
+ Dynamic: home-page
15
+ Dynamic: license
16
+ Dynamic: requires-dist
17
+ Dynamic: requires-python
18
+ Dynamic: summary
@@ -0,0 +1,8 @@
1
+ from .engine import DetectionEngine
2
+ from .middleware import KavachMiddleware
3
+ from .types import Rule
4
+ from .exceptions import SecurityException
5
+ from .rules import KAVACH_RULES
6
+
7
+ __version__ = "0.1.0"
8
+ __all__ = ["DetectionEngine", "KavachMiddleware", "Rule", "SecurityException", "KAVACH_RULES"]
@@ -0,0 +1,23 @@
1
+ from kavach_logger import get_logger, mask_sensitive_data
2
+
3
+ logger = get_logger("kavach.shield.engine")
4
+
5
+ class DetectionEngine:
6
+ def __init__(self, rules):
7
+ self.rules = rules
8
+ logger.info(f"DetectionEngine initialized | rules={len(rules)}")
9
+
10
+ def scan(self, text: str):
11
+ violations = []
12
+ masked_text = mask_sensitive_data(text)
13
+ for rule in self.rules:
14
+ for pattern in rule.patterns:
15
+ if pattern.search(text):
16
+ violations.append({
17
+ "rule": rule.id,
18
+ "name": rule.name,
19
+ "severity": rule.severity
20
+ })
21
+ logger.debug(f"Rule matched | {rule.id} | severity={rule.severity} | data={masked_text}")
22
+ break
23
+ return violations
@@ -0,0 +1,2 @@
1
+ class SecurityException(Exception):
2
+ pass
@@ -0,0 +1,83 @@
1
+ import asyncio
2
+ from fnmatch import fnmatch
3
+ from typing import Callable, Any, Dict, List, Optional, Set, Union
4
+
5
+ from kavach_logger import get_logger
6
+ from kavach_events import event_manager
7
+ from .engine import DetectionEngine
8
+ from .rules import KAVACH_RULES
9
+ from .exceptions import SecurityException
10
+
11
+ logger = get_logger("kavach.shield.middleware")
12
+
13
+ class KavachMiddleware:
14
+ def __init__(
15
+ self,
16
+ rules: Optional[List[Any]] = None,
17
+ strict: bool = True,
18
+ sensitive_tools: Optional[Union[List[str], Set[str]]] = None,
19
+ extend_rules: bool = True
20
+ ) -> None:
21
+ if rules and extend_rules:
22
+ self.rules = KAVACH_RULES + rules
23
+ else:
24
+ self.rules = rules or KAVACH_RULES
25
+ self.engine: DetectionEngine = DetectionEngine(self.rules)
26
+ self.strict: bool = strict
27
+ self.sensitive_tools: Set[str] = set(sensitive_tools or [])
28
+ logger.info(f"KavachMiddleware initialized | mode={'STRICT' if strict else 'MONITOR'} | rules={len(self.rules)}")
29
+
30
+ async def __call__(self, context: Any, call_next: Callable[..., Any]) -> Any:
31
+ return await self.on_call_tool(context, call_next)
32
+
33
+ def register_tool(self, tool_name: str) -> None:
34
+ self.sensitive_tools.add(tool_name)
35
+ logger.debug(f"Registered sensitive tool | tool_name={tool_name}")
36
+
37
+ def _matches_pattern(self, tool_name: str) -> bool:
38
+ return any(fnmatch(tool_name, pattern) for pattern in self.sensitive_tools)
39
+
40
+ async def on_call_tool(self, context: Any, call_next: Callable[..., Any]) -> Any:
41
+ tool_name: str = getattr(context.message, "name", "unknown")
42
+ args_dict: Dict[str, Any] = getattr(context.message, "arguments", {})
43
+ is_sensitive: bool = self._matches_pattern(tool_name)
44
+
45
+ event_payload = {"tool_name": tool_name, "arguments": args_dict, "context": context, "is_sensitive": is_sensitive}
46
+
47
+ if event_manager.has_listeners("pre", tool_name):
48
+ asyncio.create_task(event_manager.emit("pre", tool_name, event_payload))
49
+
50
+ if is_sensitive:
51
+ logger.info(f"Scanning inbound request | tool={tool_name}")
52
+ violations: List[Any] = self.engine.scan(str(args_dict))
53
+
54
+ if violations:
55
+ sec_payload = {**event_payload, "violations": violations, "stage": "inbound"}
56
+ if event_manager.has_listeners("sec", tool_name):
57
+ asyncio.create_task(event_manager.emit("sec", tool_name, sec_payload))
58
+
59
+ if self.strict:
60
+ raise SecurityException(f"Security violations detected: {violations}")
61
+
62
+ try:
63
+ result = await call_next(context)
64
+ except Exception as e:
65
+ logger.error(f"Tool execution failed | tool={tool_name} | error={str(e)}")
66
+ raise
67
+
68
+ if is_sensitive:
69
+ logger.info(f"Scanning outbound response | tool={tool_name}")
70
+ violations: List[Any] = self.engine.scan(str(result))
71
+
72
+ if violations:
73
+ sec_payload = {**event_payload, "violations": violations, "result": result, "stage": "outbound"}
74
+ if event_manager.has_listeners("sec", tool_name):
75
+ asyncio.create_task(event_manager.emit("sec", tool_name, sec_payload))
76
+
77
+ if self.strict:
78
+ raise SecurityException(f"Security violations in response: {violations}")
79
+
80
+ if event_manager.has_listeners("post", tool_name):
81
+ asyncio.create_task(event_manager.emit("post", tool_name, {**event_payload, "result": result}))
82
+
83
+ return result
@@ -0,0 +1,75 @@
1
+ import re
2
+ from .types import Rule
3
+
4
+ KAVACH_RULES = [
5
+ Rule(
6
+ id="prompt-injection",
7
+ name="Prompt Injection",
8
+ severity="critical",
9
+ description="System override and jailbreak attempts",
10
+ patterns=[
11
+ re.compile(r"ignore\s+(all|previous)\s+instructions", re.I),
12
+ re.compile(r"override\s+(system\s+)?(instructions|rules|policy)", re.I),
13
+ re.compile(r"bypass\s+(rules|policy|security)", re.I),
14
+ re.compile(r"you\s+are\s+now\s+(system|developer|admin|root)", re.I),
15
+ re.compile(r"act\s+as\s+(system|developer|admin|root|unrestricted)", re.I),
16
+ re.compile(r"(jailbreak|dan|developer\s*mode|god\s*mode)", re.I),
17
+ ],
18
+ ),
19
+ Rule(
20
+ id="data-exfiltration",
21
+ name="Data Exfiltration",
22
+ severity="critical",
23
+ description="Attempts to leak system data or prompts",
24
+ patterns=[
25
+ re.compile(r"(send|upload|export|exfiltrate|dump).*(password|secret|token|env|file|system)", re.I),
26
+ re.compile(r"(print|show|reveal).*(system\s*prompt|hidden\s*instructions|internal\s*rules)", re.I),
27
+ re.compile(r"return\s+(system\s*prompt|config|secrets)", re.I),
28
+ ],
29
+ ),
30
+ Rule(
31
+ id="pii",
32
+ name="PII Detection",
33
+ severity="high",
34
+ description="Sensitive personal data",
35
+ patterns=[
36
+ re.compile(r"\b\d{10}\b"),
37
+ re.compile(r"\b\d{12}\b"),
38
+ re.compile(r"\b\d{16}\b"),
39
+ re.compile(r"\b\d{3}-\d{2}-\d{4}\b"),
40
+ ],
41
+ ),
42
+ Rule(
43
+ id="secret-leak",
44
+ name="Secret Leakage",
45
+ severity="critical",
46
+ description="API keys and credentials",
47
+ patterns=[
48
+ re.compile(r"AKIA[0-9A-Z]{16}"),
49
+ re.compile(r"sk-[A-Za-z0-9_-]{20,}"),
50
+ re.compile(r"AIza[0-9A-Za-z0-9_-]{35}"),
51
+ re.compile(r"-----BEGIN (RSA|PRIVATE) KEY-----"),
52
+ re.compile(r"(api[_-]?key|secret[_-]?key|access[_-]?token|password)\s*[:=]", re.I),
53
+ ],
54
+ ),
55
+ Rule(
56
+ id="dangerous-eval",
57
+ name="Dangerous Execution",
58
+ severity="critical",
59
+ description="Code execution attempts",
60
+ patterns=[
61
+ re.compile(r"\b(eval|exec|compile)\s*\(", re.I),
62
+ re.compile(r"os\.system|subprocess", re.I),
63
+ ],
64
+ ),
65
+ Rule(
66
+ id="sql-injection",
67
+ name="SQL Injection Patterns",
68
+ severity="high",
69
+ description="SQL injection attempts",
70
+ patterns=[
71
+ re.compile(r"(DROP\s+TABLE|DELETE\s+FROM|UNION\s+SELECT|INSERT\s+INTO|UPDATE\s+.*SET)", re.I),
72
+ re.compile(r"(' OR '1'='1|--|#|/\*)", re.I),
73
+ ],
74
+ ),
75
+ ]
@@ -0,0 +1,10 @@
1
+ from dataclasses import dataclass
2
+ from typing import List, Pattern
3
+
4
+ @dataclass
5
+ class Rule:
6
+ id: str
7
+ name: str
8
+ severity: str
9
+ description: str
10
+ patterns: List[Pattern]
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: kavach-shield
3
+ Version: 0.1.0
4
+ Summary: Security middleware for Model Context Protocol (MCP) - detection engine and rules
5
+ Home-page: https://github.com/shivamnamdeo0101/kavach-agent-ecosystem
6
+ Author: Shivam Namdeo
7
+ Author-email: shivamnamdeo0101@gmail.com
8
+ License: MIT
9
+ Requires-Python: >=3.7
10
+ Requires-Dist: kavach-logger>=0.1.0
11
+ Requires-Dist: kavach-mcp-events>=0.1.0
12
+ Dynamic: author
13
+ Dynamic: author-email
14
+ Dynamic: home-page
15
+ Dynamic: license
16
+ Dynamic: requires-dist
17
+ Dynamic: requires-python
18
+ Dynamic: summary
@@ -0,0 +1,12 @@
1
+ setup.py
2
+ kavach_shield/__init__.py
3
+ kavach_shield/engine.py
4
+ kavach_shield/exceptions.py
5
+ kavach_shield/middleware.py
6
+ kavach_shield/rules.py
7
+ kavach_shield/types.py
8
+ kavach_shield.egg-info/PKG-INFO
9
+ kavach_shield.egg-info/SOURCES.txt
10
+ kavach_shield.egg-info/dependency_links.txt
11
+ kavach_shield.egg-info/requires.txt
12
+ kavach_shield.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ kavach-logger>=0.1.0
2
+ kavach-mcp-events>=0.1.0
@@ -0,0 +1 @@
1
+ kavach_shield
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,17 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="kavach-shield",
5
+ version="0.1.0",
6
+ description="Security middleware for Model Context Protocol (MCP) - detection engine and rules",
7
+ author="Shivam Namdeo",
8
+ author_email="shivamnamdeo0101@gmail.com",
9
+ url="https://github.com/shivamnamdeo0101/kavach-agent-ecosystem",
10
+ license="MIT",
11
+ packages=find_packages(),
12
+ python_requires=">=3.7",
13
+ install_requires=[
14
+ "kavach-logger>=0.1.0",
15
+ "kavach-mcp-events>=0.1.0",
16
+ ],
17
+ )