pysealer 0.2.0__cp313-cp313t-musllinux_1_2_x86_64.whl

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.
pysealer/setup.py ADDED
@@ -0,0 +1,137 @@
1
+ """Setup the storage of the pysealer keypair in a .env file."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional
6
+ from dotenv import load_dotenv, set_key
7
+ from pysealer import generate_keypair
8
+
9
+
10
+ def _find_env_file() -> Path:
11
+ """
12
+ Search for .env file starting from current directory and walking up to parent directories.
13
+ Also checks PYSEALER_ENV_PATH environment variable.
14
+
15
+ Returns:
16
+ Path: Path to the .env file
17
+
18
+ Raises:
19
+ FileNotFoundError: If no .env file is found
20
+ """
21
+ # First check if PYSEALER_ENV_PATH environment variable is set
22
+ env_path_var = os.getenv("PYSEALER_ENV_PATH")
23
+ if env_path_var:
24
+ env_path = Path(env_path_var)
25
+ if env_path.exists():
26
+ return env_path
27
+
28
+ # Start from current working directory and search upward
29
+ current = Path.cwd()
30
+
31
+ # Check current directory and all parent directories up to root
32
+ for parent in [current] + list(current.parents):
33
+ env_file = parent / '.env'
34
+ if env_file.exists():
35
+ return env_file
36
+
37
+ # If not found, return the default location (current directory)
38
+ # This will be used in error messages
39
+ return Path.cwd() / '.env'
40
+
41
+ def setup_keypair(env_path: Optional[str | Path] = None):
42
+ """
43
+ Generate and store keypair securely.
44
+
45
+ Args:
46
+ env_path: Optional path to .env file. If None, creates in current directory.
47
+ """
48
+ # Determine .env location
49
+ if env_path is None:
50
+ env_path = Path.cwd() / '.env'
51
+ else:
52
+ env_path = Path(env_path)
53
+
54
+ # Check if keys already exist
55
+ if env_path.exists():
56
+ load_dotenv(env_path)
57
+ existing_private = os.getenv("PYSEALER_PRIVATE_KEY")
58
+ existing_public = os.getenv("PYSEALER_PUBLIC_KEY")
59
+
60
+ if existing_private or existing_public:
61
+ raise ValueError(f"Keys already exist in {env_path} Cannot overwrite existing keys.")
62
+
63
+ # Create .env if it doesn't exist
64
+ env_path.touch(exist_ok=True)
65
+
66
+ # Generate keypair using the Rust function
67
+ private_key_hex, public_key_hex = generate_keypair()
68
+
69
+ # Store keys in .env file
70
+ set_key(str(env_path), "PYSEALER_PRIVATE_KEY", private_key_hex)
71
+ set_key(str(env_path), "PYSEALER_PUBLIC_KEY", public_key_hex)
72
+
73
+ return private_key_hex, public_key_hex
74
+
75
+
76
+ def get_public_key(env_path: Optional[str | Path] = None) -> str:
77
+ """
78
+ Retrieve the public key from the .env file.
79
+
80
+ Args:
81
+ env_path: Optional path to .env file. If None, searches from current directory upward.
82
+
83
+ Returns:
84
+ str: The public key hex string, or None if not found.
85
+ """
86
+ # Determine .env location
87
+ if env_path is None:
88
+ env_path = _find_env_file()
89
+ else:
90
+ env_path = Path(env_path)
91
+
92
+ # Check if .env exists
93
+ if not env_path.exists():
94
+ raise FileNotFoundError(f"No .env file found at {env_path}. Run setup_keypair() first.")
95
+
96
+ # Load environment variables from .env
97
+ load_dotenv(env_path)
98
+
99
+ # Get public key
100
+ public_key = os.getenv("PYSEALER_PUBLIC_KEY")
101
+
102
+ if public_key is None:
103
+ raise ValueError(f"PYSEALER_PUBLIC_KEY not found in {env_path}. Run setup_keypair() first.")
104
+
105
+ return public_key
106
+
107
+
108
+ def get_private_key(env_path: Optional[str | Path] = None) -> str:
109
+ """
110
+ Retrieve the private key from the .env file.
111
+
112
+ Args:
113
+ env_path: Optional path to .env file. If None, searches from current directory upward.
114
+
115
+ Returns:
116
+ str: The private key hex string, or None if not found.
117
+ """
118
+ # Determine .env location
119
+ if env_path is None:
120
+ env_path = _find_env_file()
121
+ else:
122
+ env_path = Path(env_path)
123
+
124
+ # Check if .env exists
125
+ if not env_path.exists():
126
+ raise FileNotFoundError(f"No .env file found at {env_path}. Run setup_keypair() first.")
127
+
128
+ # Load environment variables from .env
129
+ load_dotenv(env_path)
130
+
131
+ # Get private key
132
+ private_key = os.getenv("PYSEALER_PRIVATE_KEY")
133
+
134
+ if private_key is None:
135
+ raise ValueError(f"PYSEALER_PRIVATE_KEY not found in {env_path}. Run setup_keypair() first.")
136
+
137
+ return private_key
@@ -0,0 +1,171 @@
1
+ Metadata-Version: 2.4
2
+ Name: pysealer
3
+ Version: 0.2.0
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.8
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Rust
14
+ Classifier: Programming Language :: Python :: Implementation :: CPython
15
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
16
+ Classifier: Topic :: Security
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Dist: python-dotenv>=1.0.0
19
+ Requires-Dist: typer>=0.9.0
20
+ Requires-Dist: pygithub>=2.1.1
21
+ Requires-Dist: pynacl>=1.5.0
22
+ Requires-Dist: gitpython>=3.1.0
23
+ Requires-Dist: pytest>=7.0.0 ; extra == 'test'
24
+ Requires-Dist: pytest-cov>=4.0.0 ; extra == 'test'
25
+ Requires-Dist: pytest-asyncio>=0.21.0 ; extra == 'test'
26
+ Provides-Extra: test
27
+ License-File: LICENSE
28
+ Summary: Cryptographically sign Python functions and classes for defense-in-depth security
29
+ Keywords: rust,python,decorator,cryptography
30
+ Author: Aidan Dyga
31
+ License: MIT
32
+ Requires-Python: >=3.8
33
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
34
+ Project-URL: Issues, https://github.com/MCP-Security-Research/pysealer/issues
35
+ Project-URL: Repository, https://github.com/MCP-Security-Research/pysealer
36
+
37
+ # pysealer
38
+
39
+ Cryptographically sign Python functions and classes for defense-in-depth security
40
+
41
+ > 💡 **code version controls code**
42
+
43
+ - 🦀 Built with the [maturin build system](https://www.maturin.rs/) for easy Rust-Python packaging
44
+ - 🔗 [PyO3](https://pyo3.rs/v0.27.1/index.html) bindings for seamless Python-Rust integration
45
+ - 🔏 [Ed25519](https://docs.rs/ed25519-dalek/latest/ed25519_dalek/) signatures to ensure code integrity and authorship
46
+ - 🖥️ [Typer](https://typer.tiangolo.com/) for a clean and user-friendly command line interface
47
+
48
+ Pysealer helps you maintain code integrity by automatically adding cryptographic signatures to your Python functions and classes. Each function or class receives a unique decorator containing a cryptographic signature that verifies both authorship and integrity, making it easy to detect unauthorized code modifications.
49
+
50
+ ## Table of Contents
51
+
52
+ 1. [Getting Started](#getting-started)
53
+ 2. [Usage](#usage)
54
+ 3. [How It Works](#how-it-works)
55
+ 4. [Model Context Protocol (MCP) Security Use Cases](#model-context-protocol-mcp-security-use-cases)
56
+ 5. [Contributing](#contributing)
57
+ 6. [License](#license)
58
+
59
+ ## Getting Started
60
+
61
+ ```shell
62
+ pip install pysealer
63
+ # or
64
+ uv pip install pysealer
65
+ ```
66
+
67
+ ## Usage
68
+
69
+ ```shell
70
+ pysealer init [ENV_FILE] # Initialize the pysealer tool by generating and saving keys to an ENV_FILE (default: .env)
71
+ pysealer decorate <file.py>... # Add cryptographic decorators to all functions/classes in one or more .py files
72
+ pysealer check <file.py>... # Verify the integrity and validity of pysealer decorators in one or more .py files
73
+ pysealer remove <file.py>... # Remove all pysealer decorators from one or more .py files
74
+ pysealer --help # Show all available commands and options
75
+ ```
76
+
77
+ ## How It Works
78
+
79
+ Pysealer works by automatically injecting cryptographic decorators into your Python functions and classes. Here's how the process works:
80
+
81
+ ### Step-by-Step Example
82
+
83
+ Suppose you have a file `fibonacci.py`:
84
+
85
+ ```python
86
+ def fibonacci(n):
87
+ if n <= 0:
88
+ return 0
89
+ elif n == 1:
90
+ return 1
91
+ else:
92
+ return fibonacci(n-1) + fibonacci(n-2)
93
+ ```
94
+
95
+ #### 1. Decorate the file
96
+
97
+ ```shell
98
+ pysealer decorate examples/fibonacci.py
99
+
100
+ Successfully added decorators to 1 file:
101
+ ✓ /path/to/examples/fibonacci.py
102
+ ```
103
+
104
+ ```python
105
+ @pysealer._GnCLaWr9B6TD524JZ3v1CENXmo5Drwfgvc9arVagbghQ6hMH4Aqc8whs3Tf57pkTjsAVNDybviW9XG5Eu3JSP6T()
106
+ def fibonacci(n):
107
+ if n <= 0:
108
+ return 0
109
+ elif n == 1:
110
+ return 1
111
+ else:
112
+ return fibonacci(n-1) + fibonacci(n-2)
113
+ ```
114
+
115
+ #### 2. Check integrity
116
+
117
+ ```shell
118
+ pysealer check examples/fibonacci.py
119
+
120
+ All decorators are valid in 1 file:
121
+ ✓ /path/to/examples/fibonacci.py: 1 decorators valid
122
+ ```
123
+
124
+ #### 3. Tamper with the code (change return 0 to return 42)
125
+
126
+ ```python
127
+ @pysealer._GnCLaWr9B6TD524JZ3v1CENXmo5Drwfgvc9arVagbghQ6hMH4Aqc8whs3Tf57pkTjsAVNDybviW9XG5Eu3JSP6T()
128
+ def fibonacci(n):
129
+ if n <= 0:
130
+ return 42
131
+ elif n == 1:
132
+ return 1
133
+ else:
134
+ return fibonacci(n-1) + fibonacci(n-2)
135
+ ```
136
+
137
+ #### 4. Check again
138
+
139
+ ```shell
140
+ pysealer check examples/fibonacci.py
141
+
142
+ 1/1 decorators failed verification across 1 file:
143
+ ✗ /path/to/examples/fibonacci.py: 1/1 decorators failed
144
+ ```
145
+
146
+ ## Model Context Protocol (MCP) Security Use Cases
147
+
148
+ One use case of Pysealer is to protect MCP servers from upstream attacks by cryptographically signing tool functions and their docstrings. Since LLMs rely on docstrings to understand tool behavior, attackers can inject malicious instructions or create fake tools that mimic legitimate ones. Pysealer's signatures ensure tool authenticity and detect tampering because any modification to code or docstrings breaks the signature and flags compromised tools.
149
+
150
+ - **Detect Version Control Changes**
151
+ - Automatically detect unauthorized code modifications through cryptographic signatures
152
+ - Each function's decorator contains a signature based on its code and docstring
153
+ - Any mismatch between code and signature is immediately flagged
154
+
155
+ - **Defense-in-Depth for Source Control**
156
+ - Add an additional security layer to version control systems
157
+ - Complement existing security measures with cryptographic verification
158
+ - Reduce risk through multiple layers of protection
159
+
160
+ ## Contributing
161
+
162
+ **🙌 Contributions are welcome!**
163
+
164
+ If you have suggestions, bug reports, or want to help improve Pysealer, feel free to open an [issue](https://github.com/MCP-Security-Research/pysealer/issues) or submit a pull request.
165
+
166
+ All ideas and contributions are appreciated—thanks for helping make pysealer better!
167
+
168
+ ## License
169
+
170
+ Pysealer is licensed under the MIT License. See [LICENSE](LICENSE) for details.
171
+
@@ -0,0 +1,15 @@
1
+ pysealer/__init__.py,sha256=iKSceDzfR6fiNPSoKYAhvllIYpxfWXRlOK6TxDbfxbc,944
2
+ pysealer/_pysealer.cpython-313t-x86_64-linux-musl.so,sha256=EL3SNcEMqWiwLkBbJ4r4-Iz2LV1JIXiLeevGNIhOvsQ,678201
3
+ pysealer/add_decorators.py,sha256=-Jq1no002PfFnYeYVQsIGptzZpqVpoopox7RVV5QbtU,8750
4
+ pysealer/check_decorators.py,sha256=LdcVBWS_PbuqJvXNBkOYSaPRiDOMvAlA5SZhjkxGGk8,7203
5
+ pysealer/cli.py,sha256=FMS3Xir3gcwKLLFeInZR6tBg6QhipKMzD5PGrCDp3hY,13837
6
+ pysealer/dummy_decorators.py,sha256=9ORNnMZ6lT5e1gU4Dt9lifAEDfjJ1wCXGiOUfMiqP-w,2807
7
+ pysealer/github_secrets.py,sha256=7SQP1k97vcLsPQqLOBEt-XiSep4dOIHPQI8NCuR-xFg,6120
8
+ pysealer/remove_decorators.py,sha256=v1VpxFid8kqqoj-NuWSWWzCoYUwl1XVYyGdTE8BLbeI,3276
9
+ pysealer/setup.py,sha256=M8wgv4GeVbbC_t0a8PBD5Hn6piqGTFIUTlo1ds6JtAc,4206
10
+ pysealer-0.2.0.dist-info/METADATA,sha256=SnUho5P0sJqiD52V14bZ0Ahh568mJrJxaQKpNkfYfCM,6229
11
+ pysealer-0.2.0.dist-info/WHEEL,sha256=D22VRV7HYPuteMjBjPavK5Qg_RIk0oJeUDr6juBgrp4,109
12
+ pysealer-0.2.0.dist-info/entry_points.txt,sha256=DUDRyFGp10a8FMaLUFE6X94kCD2dciDY66ISXuUySmA,45
13
+ pysealer-0.2.0.dist-info/licenses/LICENSE,sha256=FtcYsMyXNwAqNhOMxR3TwptCUnwb5coRK_UQyWGLJnc,1067
14
+ pysealer.libs/libgcc_s-6d2d9dc8.so.1,sha256=K3sghe0OS1atTYJSB8m0plft2csyIC36q20Ii8UMudc,536145
15
+ pysealer-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.11.5)
3
+ Root-Is-Purelib: false
4
+ Tag: cp313-cp313t-musllinux_1_2_x86_64
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pysealer=pysealer.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Aidan Dyga
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.
Binary file