sentro 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.
- sentro-0.1.0/.gitignore +20 -0
- sentro-0.1.0/.idea/.gitignore +3 -0
- sentro-0.1.0/.idea/inspectionProfiles/Project_Default.xml +7 -0
- sentro-0.1.0/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- sentro-0.1.0/.idea/misc.xml +7 -0
- sentro-0.1.0/.idea/modules.xml +8 -0
- sentro-0.1.0/.idea/safe-pip-install.iml +18 -0
- sentro-0.1.0/.idea/vcs.xml +6 -0
- sentro-0.1.0/.idea/workspace.xml +43 -0
- sentro-0.1.0/.pypirc +5 -0
- sentro-0.1.0/PKG-INFO +230 -0
- sentro-0.1.0/PUBLISH.md +100 -0
- sentro-0.1.0/README.md +204 -0
- sentro-0.1.0/pypitoken.text +5 -0
- sentro-0.1.0/pyproject.toml +57 -0
- sentro-0.1.0/src/sentro/__init__.py +3 -0
- sentro-0.1.0/src/sentro/__main__.py +4 -0
- sentro-0.1.0/src/sentro/_version.py +1 -0
- sentro-0.1.0/src/sentro/cli.py +195 -0
- sentro-0.1.0/src/sentro/config.py +133 -0
- sentro-0.1.0/src/sentro/data/popular_packages.txt +259 -0
- sentro-0.1.0/src/sentro/extraction/__init__.py +0 -0
- sentro-0.1.0/src/sentro/extraction/extractor.py +27 -0
- sentro-0.1.0/src/sentro/extraction/sdist_extractor.py +91 -0
- sentro-0.1.0/src/sentro/extraction/wheel_extractor.py +54 -0
- sentro-0.1.0/src/sentro/installer.py +169 -0
- sentro-0.1.0/src/sentro/models.py +70 -0
- sentro-0.1.0/src/sentro/orchestrator.py +140 -0
- sentro-0.1.0/src/sentro/pypi/__init__.py +0 -0
- sentro-0.1.0/src/sentro/pypi/client.py +84 -0
- sentro-0.1.0/src/sentro/pypi/downloader.py +70 -0
- sentro-0.1.0/src/sentro/reporting/__init__.py +0 -0
- sentro-0.1.0/src/sentro/reporting/json_reporter.py +41 -0
- sentro-0.1.0/src/sentro/reporting/reporter.py +22 -0
- sentro-0.1.0/src/sentro/reporting/text_reporter.py +95 -0
- sentro-0.1.0/src/sentro/scanners/__init__.py +0 -0
- sentro-0.1.0/src/sentro/scanners/base.py +48 -0
- sentro-0.1.0/src/sentro/scanners/dependency_confusion.py +56 -0
- sentro-0.1.0/src/sentro/scanners/malicious_code.py +332 -0
- sentro-0.1.0/src/sentro/scanners/metadata.py +133 -0
- sentro-0.1.0/src/sentro/scanners/obfuscation.py +173 -0
- sentro-0.1.0/src/sentro/scanners/setup_hooks.py +158 -0
- sentro-0.1.0/src/sentro/scanners/typosquatting.py +112 -0
- sentro-0.1.0/tests/conftest.py +151 -0
- sentro-0.1.0/tests/fixtures/pypi_responses/new_package.json +37 -0
- sentro-0.1.0/tests/fixtures/pypi_responses/requests.json +62 -0
- sentro-0.1.0/tests/test_cli.py +98 -0
- sentro-0.1.0/tests/test_config.py +86 -0
- sentro-0.1.0/tests/test_extractor.py +48 -0
- sentro-0.1.0/tests/test_installer.py +65 -0
- sentro-0.1.0/tests/test_integration.py +39 -0
- sentro-0.1.0/tests/test_models.py +34 -0
- sentro-0.1.0/tests/test_pipeline.py +61 -0
- sentro-0.1.0/tests/test_reporter.py +88 -0
- sentro-0.1.0/tests/test_scanner_dependency_confusion.py +55 -0
- sentro-0.1.0/tests/test_scanner_malicious_code.py +103 -0
- sentro-0.1.0/tests/test_scanner_metadata.py +115 -0
- sentro-0.1.0/tests/test_scanner_obfuscation.py +72 -0
- sentro-0.1.0/tests/test_scanner_setup_hooks.py +81 -0
- sentro-0.1.0/tests/test_scanner_typosquatting.py +49 -0
- sentro-0.1.0/uv.lock +524 -0
sentro-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<component name="InspectionProjectProfileManager">
|
|
2
|
+
<profile version="1.0">
|
|
3
|
+
<option name="myName" value="Project Default" />
|
|
4
|
+
<inspection_tool class="PyArgumentListInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
5
|
+
<inspection_tool class="PyStatementEffectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
|
6
|
+
</profile>
|
|
7
|
+
</component>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="Black">
|
|
4
|
+
<option name="sdkName" value="uv (safe-pip-install)" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="ProjectRootManager" version="2" project-jdk-name="uv (safe-pip-install)" project-jdk-type="Python SDK" />
|
|
7
|
+
</project>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/safe-pip-install.iml" filepath="$PROJECT_DIR$/.idea/safe-pip-install.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="PYTHON_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$">
|
|
5
|
+
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
6
|
+
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
|
7
|
+
</content>
|
|
8
|
+
<orderEntry type="jdk" jdkName="uv (safe-pip-install)" jdkType="Python SDK" />
|
|
9
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
10
|
+
</component>
|
|
11
|
+
<component name="PyDocumentationSettings">
|
|
12
|
+
<option name="format" value="PLAIN" />
|
|
13
|
+
<option name="myDocStringFormat" value="Plain" />
|
|
14
|
+
</component>
|
|
15
|
+
<component name="TestRunnerService">
|
|
16
|
+
<option name="PROJECT_TEST_RUNNER" value="py.test" />
|
|
17
|
+
</component>
|
|
18
|
+
</module>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ChangeListManager">
|
|
4
|
+
<list default="true" id="b83261b5-2f82-4a61-8ff1-8fb46c8264ce" name="Changes" comment="" />
|
|
5
|
+
<option name="SHOW_DIALOG" value="false" />
|
|
6
|
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
7
|
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
8
|
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
9
|
+
</component>
|
|
10
|
+
<component name="Git.Settings">
|
|
11
|
+
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
|
|
12
|
+
</component>
|
|
13
|
+
<component name="ProjectColorInfo">{
|
|
14
|
+
"associatedIndex": 1
|
|
15
|
+
}</component>
|
|
16
|
+
<component name="ProjectId" id="3CFzClefo4gqasflutg5HAmFyGK" />
|
|
17
|
+
<component name="ProjectViewState">
|
|
18
|
+
<option name="hideEmptyMiddlePackages" value="true" />
|
|
19
|
+
<option name="showLibraryContents" value="true" />
|
|
20
|
+
</component>
|
|
21
|
+
<component name="PropertiesComponent"><![CDATA[{
|
|
22
|
+
"keyToString": {
|
|
23
|
+
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
|
24
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
25
|
+
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
|
26
|
+
"RunOnceActivity.git.unshallow": "true",
|
|
27
|
+
"dart.analysis.tool.window.visible": "false",
|
|
28
|
+
"git-widget-placeholder": "origin",
|
|
29
|
+
"last_opened_file_path": "/home/ckdev/development/sentro",
|
|
30
|
+
"show.migrate.to.gradle.popup": "false"
|
|
31
|
+
}
|
|
32
|
+
}]]></component>
|
|
33
|
+
<component name="TaskManager">
|
|
34
|
+
<task active="true" id="Default" summary="Default task">
|
|
35
|
+
<changelist id="b83261b5-2f82-4a61-8ff1-8fb46c8264ce" name="Changes" comment="" />
|
|
36
|
+
<created>1775995982467</created>
|
|
37
|
+
<option name="number" value="Default" />
|
|
38
|
+
<option name="presentableId" value="Default" />
|
|
39
|
+
<updated>1775995982467</updated>
|
|
40
|
+
</task>
|
|
41
|
+
<servers />
|
|
42
|
+
</component>
|
|
43
|
+
</project>
|
sentro-0.1.0/.pypirc
ADDED
sentro-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sentro
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A security-conscious pip wrapper that scans packages for malicious code before installation
|
|
5
|
+
Author-email: Solvyx <dev@solvyx.dev>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: malware-detection,package-manager,pip,security,supply-chain
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Security
|
|
15
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: click>=8.1
|
|
18
|
+
Requires-Dist: packaging>=24.0
|
|
19
|
+
Requires-Dist: rich>=13.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest-mock>=3.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: responses>=0.25; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# sentro
|
|
28
|
+
|
|
29
|
+
Built by [Solvyx.dev](https://solvyx.dev) — a pip wrapper that scans Python packages for malicious code before installing them.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
sentro install requests
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
╭──────────────────────── sentro scan ─────────────────────────╮
|
|
37
|
+
│ Package : requests 2.31.0 │
|
|
38
|
+
│ PyPI : verified │
|
|
39
|
+
│ Risk : SAFE (score 0/100) │
|
|
40
|
+
╰─────────────────────────────────────────────────────────────────╯
|
|
41
|
+
No issues found.
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Why
|
|
47
|
+
|
|
48
|
+
Supply-chain attacks on PyPI are getting more frequent. Attackers publish packages with names one typo away from `requests` or `numpy`, or shadow internal package names to trigger dependency confusion. The malicious code runs at install time inside `setup.py`, or silently on first `import`.
|
|
49
|
+
|
|
50
|
+
`sentro` downloads and scans a package before pip ever touches it.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## What it detects
|
|
55
|
+
|
|
56
|
+
| Check | Examples |
|
|
57
|
+
|---|---|
|
|
58
|
+
| **Malicious code** | `eval()` / `exec()` at module level, `os.system()`, `subprocess(shell=True)`, socket connections to hardcoded IPs |
|
|
59
|
+
| **Install hooks** | Dangerous calls in `setup.py` that run unconditionally at install time, `cmdclass` overrides, dynamic `install_requires` |
|
|
60
|
+
| **Obfuscation** | `exec(base64.b64decode(...))` chains, high-entropy string constants, `marshal.loads` payloads |
|
|
61
|
+
| **Typosquatting** | Names similar to popular packages (`reqeusts`, `numpy-dev`), Unicode homoglyphs |
|
|
62
|
+
| **Dependency confusion** | Package names that shadow Python stdlib modules (`json`, `os`, `urllib`) |
|
|
63
|
+
| **Metadata signals** | Package age under 7 days, very low download count, missing author/homepage |
|
|
64
|
+
|
|
65
|
+
Each finding contributes to a risk score (0–100). The overall verdict is **SAFE**, **WARNING**, or **DANGER**.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install sentro
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Requires Python 3.11+.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Usage
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Scan and install
|
|
83
|
+
sentro install requests
|
|
84
|
+
|
|
85
|
+
# Scan only — don't install
|
|
86
|
+
sentro install requests --no-install
|
|
87
|
+
|
|
88
|
+
# Block installation if anything scores DANGER
|
|
89
|
+
sentro install requests --strict
|
|
90
|
+
|
|
91
|
+
# Pin a version
|
|
92
|
+
sentro install "requests==2.28.0"
|
|
93
|
+
|
|
94
|
+
# Multiple packages
|
|
95
|
+
sentro install requests flask sqlalchemy
|
|
96
|
+
|
|
97
|
+
# JSON output (for CI pipelines)
|
|
98
|
+
sentro install requests --no-install --output-format json
|
|
99
|
+
|
|
100
|
+
# See which installer was detected
|
|
101
|
+
sentro detect-installer
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Flags
|
|
107
|
+
|
|
108
|
+
| Flag | Description |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `--strict` | Exit 1 and block install if any package scores DANGER |
|
|
111
|
+
| `--no-install` | Scan only, don't install |
|
|
112
|
+
| `--skip-scan` | Skip scanning, forward directly to the installer |
|
|
113
|
+
| `--output-format text\|json` | Output format (default: `text`) |
|
|
114
|
+
| `--installer pip\|uv\|conda\|mamba\|poetry\|pipenv\|pdm\|auto` | Installer to use (default: `auto`) |
|
|
115
|
+
| `--config PATH` | Path to a TOML config file |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Installer detection
|
|
120
|
+
|
|
121
|
+
`sentro` auto-detects your package manager so the install step uses the right tool.
|
|
122
|
+
|
|
123
|
+
| Installer | Detected when |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `uv` | `uv` is on PATH and a virtual env is active |
|
|
126
|
+
| `conda` / `mamba` | `CONDA_DEFAULT_ENV` or `CONDA_PREFIX` is set |
|
|
127
|
+
| `poetry` | `pyproject.toml` has `[tool.poetry]` and `poetry` is on PATH |
|
|
128
|
+
| `pipenv` | `Pipfile` exists and `pipenv` is on PATH |
|
|
129
|
+
| `pdm` | `pyproject.toml` has `[tool.pdm]` and `pdm` is on PATH |
|
|
130
|
+
| `pip` | Fallback |
|
|
131
|
+
|
|
132
|
+
Override with `--installer pip` or set `SENTRO_INSTALLER=pip`.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Configuration
|
|
137
|
+
|
|
138
|
+
Create a `.sentro.toml` in your project root (or add `[tool.sentro]` to `pyproject.toml`):
|
|
139
|
+
|
|
140
|
+
```toml
|
|
141
|
+
[sentro]
|
|
142
|
+
strict = true
|
|
143
|
+
|
|
144
|
+
[sentro.thresholds]
|
|
145
|
+
warning = 30
|
|
146
|
+
danger = 70
|
|
147
|
+
|
|
148
|
+
whitelist_packages = ["requests", "numpy"]
|
|
149
|
+
scanners_disabled = ["metadata"]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Config is merged from multiple sources in this order (last wins):
|
|
153
|
+
|
|
154
|
+
1. `~/.config/sentro/config.toml` — user-level defaults
|
|
155
|
+
2. `pyproject.toml` `[tool.sentro]` in the current directory
|
|
156
|
+
3. `.sentro.toml` in the current directory
|
|
157
|
+
4. `--config PATH` flag
|
|
158
|
+
5. `SENTRO_*` environment variables
|
|
159
|
+
6. CLI flags
|
|
160
|
+
|
|
161
|
+
### Environment variables
|
|
162
|
+
|
|
163
|
+
| Variable | Effect |
|
|
164
|
+
|---|---|
|
|
165
|
+
| `SENTRO_STRICT=true` | Enable strict mode |
|
|
166
|
+
| `SENTRO_INSTALLER=pip` | Force a specific installer |
|
|
167
|
+
| `SENTRO_OUTPUT_FORMAT=json` | JSON output |
|
|
168
|
+
| `SENTRO_DANGER_THRESHOLD=50` | Override the DANGER score threshold |
|
|
169
|
+
| `SENTRO_WARNING_THRESHOLD=20` | Override the WARNING score threshold |
|
|
170
|
+
| `SENTRO_WHITELIST=requests,numpy` | Comma-separated whitelist |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## CI usage
|
|
175
|
+
|
|
176
|
+
```yaml
|
|
177
|
+
# GitHub Actions example
|
|
178
|
+
- name: Scan dependencies
|
|
179
|
+
run: |
|
|
180
|
+
pip install sentro
|
|
181
|
+
sentro install -r requirements.txt --strict --output-format json > scan.json
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The `--strict` flag makes the step fail if any package scores DANGER. The JSON output can be parsed or stored as an artifact.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## False positives
|
|
189
|
+
|
|
190
|
+
Some legitimate packages use dynamic imports or `eval()` inside functions (template engines, plugin loaders, REPLs). `sentro` tries to distinguish:
|
|
191
|
+
|
|
192
|
+
- `eval()` / `exec()` **at module level** → DANGER (runs unconditionally on import)
|
|
193
|
+
- `eval()` / `exec()` **inside a function** → WARNING (may be legitimate)
|
|
194
|
+
- `__import__()` **inside a function** → not flagged (standard plugin-loader pattern)
|
|
195
|
+
|
|
196
|
+
If a package you trust keeps triggering warnings, add it to `whitelist_packages` in your config.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Development
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
git clone https://github.com/solvyx-dev/sentro
|
|
204
|
+
cd sentro
|
|
205
|
+
pip install -e ".[dev]"
|
|
206
|
+
|
|
207
|
+
# Run tests (no network required)
|
|
208
|
+
pytest -m "not integration"
|
|
209
|
+
|
|
210
|
+
# Run with coverage
|
|
211
|
+
pytest --cov=sentro --cov-report=term-missing
|
|
212
|
+
|
|
213
|
+
# Run integration tests (requires internet)
|
|
214
|
+
pytest -m integration
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Roadmap
|
|
220
|
+
|
|
221
|
+
- [ ] `requirements.txt` file scanning (`sentro install -r requirements.txt`)
|
|
222
|
+
- [ ] Support for other languages (npm, cargo, gem)
|
|
223
|
+
- [ ] GitHub Action
|
|
224
|
+
- [ ] Cache scan results to avoid re-scanning unchanged packages
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT — built and maintained by [Solvyx.dev](https://solvyx.dev)
|
sentro-0.1.0/PUBLISH.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# PyPI Publishing Checklist
|
|
2
|
+
|
|
3
|
+
## Status: Ready to publish
|
|
4
|
+
|
|
5
|
+
Name confirmed available on PyPI: **`sentro`**
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What is already done
|
|
10
|
+
|
|
11
|
+
- [x] Package name `sentro` available on PyPI
|
|
12
|
+
- [x] `README.md` written
|
|
13
|
+
- [x] `pyproject.toml` complete — author: Solvyx, dev@solvyx.dev
|
|
14
|
+
- [x] 91 unit tests passing
|
|
15
|
+
- [x] `build` and `twine` installed
|
|
16
|
+
- [x] All references updated to `sentro`
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Publishing steps
|
|
21
|
+
|
|
22
|
+
### 1. Create a PyPI account
|
|
23
|
+
https://pypi.org/account/register
|
|
24
|
+
|
|
25
|
+
### 2. Create an API token
|
|
26
|
+
https://pypi.org/manage/account/token/
|
|
27
|
+
|
|
28
|
+
Set scope to "Entire account" for the first upload, then restrict it to the `sentro` project.
|
|
29
|
+
|
|
30
|
+
### 3. Configure credentials
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Option A — ~/.pypirc file
|
|
34
|
+
[pypi]
|
|
35
|
+
username = __token__
|
|
36
|
+
password = pypi-xxxxxxxxxxxxxxxx
|
|
37
|
+
|
|
38
|
+
# Option B — env vars (better for CI)
|
|
39
|
+
export TWINE_USERNAME=__token__
|
|
40
|
+
export TWINE_PASSWORD=pypi-xxxxxxxxxxxxxxxx
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 4. Build
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
cd /home/ckdev/development/sentro
|
|
47
|
+
python -m build
|
|
48
|
+
# produces:
|
|
49
|
+
# dist/sentro-0.1.0-py3-none-any.whl
|
|
50
|
+
# dist/sentro-0.1.0.tar.gz
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 5. Test on TestPyPI first
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
twine upload --repository testpypi dist/*
|
|
57
|
+
# verify at: https://test.pypi.org/project/sentro/
|
|
58
|
+
|
|
59
|
+
pip install --index-url https://test.pypi.org/simple/ sentro
|
|
60
|
+
sentro --version
|
|
61
|
+
sentro install requests --no-install
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 6. Upload to PyPI
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
twine upload dist/*
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 7. Verify live
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install sentro
|
|
74
|
+
sentro --version
|
|
75
|
+
sentro install requests --no-install
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Bumping the version
|
|
81
|
+
|
|
82
|
+
When ready to release a new version:
|
|
83
|
+
|
|
84
|
+
1. Update `pyproject.toml` → `version = "x.x.x"`
|
|
85
|
+
2. Update `src/sentro/_version.py` → `__version__ = "x.x.x"`
|
|
86
|
+
3. Build and upload again
|
|
87
|
+
|
|
88
|
+
Current version: `0.1.0`
|
|
89
|
+
|
|
90
|
+
Before bumping to `1.0.0` consider:
|
|
91
|
+
- `requirements.txt` file scanning (`sentro install -r requirements.txt`)
|
|
92
|
+
- Broader real-world package testing
|
|
93
|
+
- Stable config format
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Notes
|
|
98
|
+
|
|
99
|
+
- `PUBLISH.md` is dev-only — it won't be included in the PyPI distribution
|
|
100
|
+
- The GitHub repo should be at `https://github.com/solvyx-dev/sentro` before publishing
|
sentro-0.1.0/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# sentro
|
|
2
|
+
|
|
3
|
+
Built by [Solvyx.dev](https://solvyx.dev) — a pip wrapper that scans Python packages for malicious code before installing them.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
sentro install requests
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
╭──────────────────────── sentro scan ─────────────────────────╮
|
|
11
|
+
│ Package : requests 2.31.0 │
|
|
12
|
+
│ PyPI : verified │
|
|
13
|
+
│ Risk : SAFE (score 0/100) │
|
|
14
|
+
╰─────────────────────────────────────────────────────────────────╯
|
|
15
|
+
No issues found.
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Why
|
|
21
|
+
|
|
22
|
+
Supply-chain attacks on PyPI are getting more frequent. Attackers publish packages with names one typo away from `requests` or `numpy`, or shadow internal package names to trigger dependency confusion. The malicious code runs at install time inside `setup.py`, or silently on first `import`.
|
|
23
|
+
|
|
24
|
+
`sentro` downloads and scans a package before pip ever touches it.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## What it detects
|
|
29
|
+
|
|
30
|
+
| Check | Examples |
|
|
31
|
+
|---|---|
|
|
32
|
+
| **Malicious code** | `eval()` / `exec()` at module level, `os.system()`, `subprocess(shell=True)`, socket connections to hardcoded IPs |
|
|
33
|
+
| **Install hooks** | Dangerous calls in `setup.py` that run unconditionally at install time, `cmdclass` overrides, dynamic `install_requires` |
|
|
34
|
+
| **Obfuscation** | `exec(base64.b64decode(...))` chains, high-entropy string constants, `marshal.loads` payloads |
|
|
35
|
+
| **Typosquatting** | Names similar to popular packages (`reqeusts`, `numpy-dev`), Unicode homoglyphs |
|
|
36
|
+
| **Dependency confusion** | Package names that shadow Python stdlib modules (`json`, `os`, `urllib`) |
|
|
37
|
+
| **Metadata signals** | Package age under 7 days, very low download count, missing author/homepage |
|
|
38
|
+
|
|
39
|
+
Each finding contributes to a risk score (0–100). The overall verdict is **SAFE**, **WARNING**, or **DANGER**.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install sentro
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Requires Python 3.11+.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Scan and install
|
|
57
|
+
sentro install requests
|
|
58
|
+
|
|
59
|
+
# Scan only — don't install
|
|
60
|
+
sentro install requests --no-install
|
|
61
|
+
|
|
62
|
+
# Block installation if anything scores DANGER
|
|
63
|
+
sentro install requests --strict
|
|
64
|
+
|
|
65
|
+
# Pin a version
|
|
66
|
+
sentro install "requests==2.28.0"
|
|
67
|
+
|
|
68
|
+
# Multiple packages
|
|
69
|
+
sentro install requests flask sqlalchemy
|
|
70
|
+
|
|
71
|
+
# JSON output (for CI pipelines)
|
|
72
|
+
sentro install requests --no-install --output-format json
|
|
73
|
+
|
|
74
|
+
# See which installer was detected
|
|
75
|
+
sentro detect-installer
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Flags
|
|
81
|
+
|
|
82
|
+
| Flag | Description |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `--strict` | Exit 1 and block install if any package scores DANGER |
|
|
85
|
+
| `--no-install` | Scan only, don't install |
|
|
86
|
+
| `--skip-scan` | Skip scanning, forward directly to the installer |
|
|
87
|
+
| `--output-format text\|json` | Output format (default: `text`) |
|
|
88
|
+
| `--installer pip\|uv\|conda\|mamba\|poetry\|pipenv\|pdm\|auto` | Installer to use (default: `auto`) |
|
|
89
|
+
| `--config PATH` | Path to a TOML config file |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Installer detection
|
|
94
|
+
|
|
95
|
+
`sentro` auto-detects your package manager so the install step uses the right tool.
|
|
96
|
+
|
|
97
|
+
| Installer | Detected when |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `uv` | `uv` is on PATH and a virtual env is active |
|
|
100
|
+
| `conda` / `mamba` | `CONDA_DEFAULT_ENV` or `CONDA_PREFIX` is set |
|
|
101
|
+
| `poetry` | `pyproject.toml` has `[tool.poetry]` and `poetry` is on PATH |
|
|
102
|
+
| `pipenv` | `Pipfile` exists and `pipenv` is on PATH |
|
|
103
|
+
| `pdm` | `pyproject.toml` has `[tool.pdm]` and `pdm` is on PATH |
|
|
104
|
+
| `pip` | Fallback |
|
|
105
|
+
|
|
106
|
+
Override with `--installer pip` or set `SENTRO_INSTALLER=pip`.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Configuration
|
|
111
|
+
|
|
112
|
+
Create a `.sentro.toml` in your project root (or add `[tool.sentro]` to `pyproject.toml`):
|
|
113
|
+
|
|
114
|
+
```toml
|
|
115
|
+
[sentro]
|
|
116
|
+
strict = true
|
|
117
|
+
|
|
118
|
+
[sentro.thresholds]
|
|
119
|
+
warning = 30
|
|
120
|
+
danger = 70
|
|
121
|
+
|
|
122
|
+
whitelist_packages = ["requests", "numpy"]
|
|
123
|
+
scanners_disabled = ["metadata"]
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Config is merged from multiple sources in this order (last wins):
|
|
127
|
+
|
|
128
|
+
1. `~/.config/sentro/config.toml` — user-level defaults
|
|
129
|
+
2. `pyproject.toml` `[tool.sentro]` in the current directory
|
|
130
|
+
3. `.sentro.toml` in the current directory
|
|
131
|
+
4. `--config PATH` flag
|
|
132
|
+
5. `SENTRO_*` environment variables
|
|
133
|
+
6. CLI flags
|
|
134
|
+
|
|
135
|
+
### Environment variables
|
|
136
|
+
|
|
137
|
+
| Variable | Effect |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `SENTRO_STRICT=true` | Enable strict mode |
|
|
140
|
+
| `SENTRO_INSTALLER=pip` | Force a specific installer |
|
|
141
|
+
| `SENTRO_OUTPUT_FORMAT=json` | JSON output |
|
|
142
|
+
| `SENTRO_DANGER_THRESHOLD=50` | Override the DANGER score threshold |
|
|
143
|
+
| `SENTRO_WARNING_THRESHOLD=20` | Override the WARNING score threshold |
|
|
144
|
+
| `SENTRO_WHITELIST=requests,numpy` | Comma-separated whitelist |
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## CI usage
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
# GitHub Actions example
|
|
152
|
+
- name: Scan dependencies
|
|
153
|
+
run: |
|
|
154
|
+
pip install sentro
|
|
155
|
+
sentro install -r requirements.txt --strict --output-format json > scan.json
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The `--strict` flag makes the step fail if any package scores DANGER. The JSON output can be parsed or stored as an artifact.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## False positives
|
|
163
|
+
|
|
164
|
+
Some legitimate packages use dynamic imports or `eval()` inside functions (template engines, plugin loaders, REPLs). `sentro` tries to distinguish:
|
|
165
|
+
|
|
166
|
+
- `eval()` / `exec()` **at module level** → DANGER (runs unconditionally on import)
|
|
167
|
+
- `eval()` / `exec()` **inside a function** → WARNING (may be legitimate)
|
|
168
|
+
- `__import__()` **inside a function** → not flagged (standard plugin-loader pattern)
|
|
169
|
+
|
|
170
|
+
If a package you trust keeps triggering warnings, add it to `whitelist_packages` in your config.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
git clone https://github.com/solvyx-dev/sentro
|
|
178
|
+
cd sentro
|
|
179
|
+
pip install -e ".[dev]"
|
|
180
|
+
|
|
181
|
+
# Run tests (no network required)
|
|
182
|
+
pytest -m "not integration"
|
|
183
|
+
|
|
184
|
+
# Run with coverage
|
|
185
|
+
pytest --cov=sentro --cov-report=term-missing
|
|
186
|
+
|
|
187
|
+
# Run integration tests (requires internet)
|
|
188
|
+
pytest -m integration
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Roadmap
|
|
194
|
+
|
|
195
|
+
- [ ] `requirements.txt` file scanning (`sentro install -r requirements.txt`)
|
|
196
|
+
- [ ] Support for other languages (npm, cargo, gem)
|
|
197
|
+
- [ ] GitHub Action
|
|
198
|
+
- [ ] Cache scan results to avoid re-scanning unchanged packages
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT — built and maintained by [Solvyx.dev](https://solvyx.dev)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#pypi_token
|
|
2
|
+
pypi-AgEIcHlwaS5vcmcCJDU4NWVmNWE1LTJlNDgtNDkwNC05ZTJiLTg5ZmEyZGEwNmFjNwACKlszLCIzYjNhMDQ4NS00YzIyLTRmZWMtYmZhYS1kYjJlOTYwYzRjYWMiXQAABiDp1kxUluuyn5czGGcGgE7A3oZSX4pYSH-lILOF35JLlA
|
|
3
|
+
|
|
4
|
+
#pytest
|
|
5
|
+
pypi-AgENdGVzdC5weXBpLm9yZwIkYmJiYjJiYjItZmQ0NC00ODEwLTg2MzUtODY5M2E2NTRmMmRmAAIqWzMsImQ0ZjJiYWVkLWMzZWMtNDJhOC1hZGNjLTgzOWVjMDYxOTQ4NyJdAAAGIDrSZxLBJkzqeh6h47AmWXOAkuxOgILfO7xg-b4YQ7VL
|