netallion-mcp-lite 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.
- netallion_mcp_lite-0.1.0/LICENSE +21 -0
- netallion_mcp_lite-0.1.0/MANIFEST.in +11 -0
- netallion_mcp_lite-0.1.0/PKG-INFO +123 -0
- netallion_mcp_lite-0.1.0/PRIVACY.md +39 -0
- netallion_mcp_lite-0.1.0/README.md +97 -0
- netallion_mcp_lite-0.1.0/pyproject.toml +56 -0
- netallion_mcp_lite-0.1.0/setup.cfg +4 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite/__init__.py +19 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite/py.typed +0 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite/redaction.py +30 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite/rules.py +330 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite/scanner.py +72 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite/server.py +289 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite.egg-info/PKG-INFO +123 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite.egg-info/SOURCES.txt +17 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite.egg-info/dependency_links.txt +1 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite.egg-info/entry_points.txt +2 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite.egg-info/requires.txt +1 -0
- netallion_mcp_lite-0.1.0/src/netallion_mcp_lite.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Netallion
|
|
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.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include PRIVACY.md
|
|
3
|
+
include LICENSE
|
|
4
|
+
recursive-include src/netallion_mcp_lite *.py
|
|
5
|
+
recursive-include src/netallion_mcp_lite py.typed
|
|
6
|
+
# Never ship tests, scripts, or internal docs — they legitimately reference the
|
|
7
|
+
# proprietary engine by name (as search terms / firewall assertions) and must
|
|
8
|
+
# not end up in the public artifact.
|
|
9
|
+
prune tests
|
|
10
|
+
prune scripts
|
|
11
|
+
prune docs
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: netallion-mcp-lite
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Free, local, IP-safe MCP server for AI-development security — commodity secret & credential checks from Netallion. Part of Agentic Development Governance.
|
|
5
|
+
Author-email: Netallion <hello@netallion.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://www.netallion.ai
|
|
8
|
+
Project-URL: Documentation, https://www.netallion.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/netallion/netallion-mcp-lite
|
|
10
|
+
Keywords: mcp,model-context-protocol,security,secrets,credentials,ai,agentic,governance,devsecops
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Security
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: mcp>=1.0.0
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# Netallion MCP Lite
|
|
28
|
+
|
|
29
|
+
**Free, local, IP-safe AI-development security — an MCP server from Netallion.**
|
|
30
|
+
|
|
31
|
+
Netallion MCP Lite is a small [Model Context Protocol](https://modelcontextprotocol.io) server that helps you and your AI coding assistants avoid the most common mistake in AI-assisted development: leaking a credential. It flags a curated set of **commodity, high-confidence** secrets and credentials (AWS, GitHub, Stripe, OpenAI, private keys, database URIs, and more) in text and files.
|
|
32
|
+
|
|
33
|
+
It runs **entirely on your machine**. No account. No data sent to Netallion. Detected secrets are **redacted** before results are returned, so they are safe to hand back into an AI assistant's context.
|
|
34
|
+
|
|
35
|
+
Part of Netallion's **Agentic Development Governance** approach — secret detection is one capability, not the whole story.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## What it is (and isn't)
|
|
40
|
+
|
|
41
|
+
| | **Netallion MCP Lite** (this package) | **Netallion AI Assurance** (the platform) |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| Price | Free | Commercial (free trial) |
|
|
44
|
+
| Runs | Locally, no account | SaaS + IDE + CI |
|
|
45
|
+
| Detection | ~27 commodity, high-confidence checks | Advanced detection well beyond commodity patterns |
|
|
46
|
+
| AI/MCP governance | — | AI provider governance, MCP governance |
|
|
47
|
+
| Policy & visibility | — | Central policy management, org-wide visibility |
|
|
48
|
+
| Compliance | — | Compliance and audit evidence |
|
|
49
|
+
| Data | Nothing leaves your machine | Enterprise controls |
|
|
50
|
+
|
|
51
|
+
MCP Lite is deliberately limited. It is a genuinely useful local utility **and** an on-ramp: if you want advanced detection, AI/MCP governance, central policy, and audit evidence across your organisation, that lives in **[Netallion AI Assurance](https://www.netallion.ai)**.
|
|
52
|
+
|
|
53
|
+
This package does **not** contain or depend on Netallion's proprietary detection engine.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Install & run
|
|
58
|
+
|
|
59
|
+
Requires Python 3.10+.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install netallion-mcp-lite
|
|
63
|
+
# or, zero-install:
|
|
64
|
+
uvx netallion-mcp-lite
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
It speaks MCP over stdio. Add it to any MCP-compatible client (Claude, Cursor, Windsurf, VS Code, …):
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"netallion-mcp-lite": {
|
|
73
|
+
"command": "netallion-mcp-lite"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Optionally confine `scan_file` to a specific directory:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"mcpServers": {
|
|
84
|
+
"netallion-mcp-lite": {
|
|
85
|
+
"command": "netallion-mcp-lite",
|
|
86
|
+
"env": { "NETALLION_MCP_LITE_WORKSPACE_ROOT": "/path/to/your/project" }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Tools
|
|
93
|
+
|
|
94
|
+
| Tool | What it does |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `scan_text` | Scan a string for commodity secrets; returns redacted findings (rule, severity, line/column, remediation). |
|
|
97
|
+
| `scan_file` | Scan a file **inside the workspace root**; sensitive files (`.env`, keys) are refused; findings redacted. |
|
|
98
|
+
| `list_supported_checks` | List the commodity checks (no secrets). |
|
|
99
|
+
| `about` | About the tool, privacy posture, and Lite vs AI Assurance. |
|
|
100
|
+
|
|
101
|
+
Run `list_supported_checks` to see the full set. Findings never include the full secret value.
|
|
102
|
+
|
|
103
|
+
## Privacy
|
|
104
|
+
|
|
105
|
+
Local-only. No telemetry. No source, file contents, prompts, or secrets are sent anywhere. See [PRIVACY.md](PRIVACY.md).
|
|
106
|
+
|
|
107
|
+
## Scope & limitations
|
|
108
|
+
|
|
109
|
+
- Commodity, high-confidence formats only — it will not catch obfuscated, encoded, or org-specific secrets. That is by design; advanced detection is part of Netallion AI Assurance.
|
|
110
|
+
- `scan_file` reads only within a single workspace root and refuses obviously-sensitive files.
|
|
111
|
+
- No generic high-entropy scanning (it produces noise without advanced technique).
|
|
112
|
+
|
|
113
|
+
We make no claims about detection rates, false-positive rates, customer adoption, certifications, or regulatory compliance for this free utility.
|
|
114
|
+
|
|
115
|
+
## Links
|
|
116
|
+
|
|
117
|
+
- Netallion AI Assurance — https://www.netallion.ai
|
|
118
|
+
- VS Code / Cursor / Windsurf extension — [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=netallion.netallion-ai-assurance) · [Open VSX](https://open-vsx.org/extension/netallion/netallion-ai-assurance)
|
|
119
|
+
- Azure Marketplace — [Netallion AI Assurance](https://marketplace.microsoft.com/en-us/product/netallion.netallion-ai-assurance)
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Privacy & Data Flow — Netallion MCP Lite
|
|
2
|
+
|
|
3
|
+
**Short version: everything runs locally. Nothing leaves your machine.**
|
|
4
|
+
|
|
5
|
+
## What happens when you use it
|
|
6
|
+
|
|
7
|
+
- `scan_text` and `scan_file` run entirely in-process using local regular
|
|
8
|
+
expressions. The text/file you scan is **never transmitted anywhere**.
|
|
9
|
+
- No source code, file contents, prompts, secrets, or credentials are sent to
|
|
10
|
+
Netallion or any third party.
|
|
11
|
+
- Detected secret values are **redacted** before results are returned, so even
|
|
12
|
+
the tool's own output does not contain full credentials.
|
|
13
|
+
|
|
14
|
+
## Telemetry
|
|
15
|
+
|
|
16
|
+
- **None.** This package sends no usage analytics, no pings, no crash reports.
|
|
17
|
+
- There is no network client in the core scanner.
|
|
18
|
+
|
|
19
|
+
## Network
|
|
20
|
+
|
|
21
|
+
- Core scanning makes **no network calls**.
|
|
22
|
+
- The only time anything is fetched from the network is at *install* time, when
|
|
23
|
+
your package manager downloads this package and its single public dependency
|
|
24
|
+
(the MCP SDK) from your configured package index.
|
|
25
|
+
|
|
26
|
+
## Data retention
|
|
27
|
+
|
|
28
|
+
- The tool stores nothing. It holds the text you pass it in memory only for the
|
|
29
|
+
duration of a scan.
|
|
30
|
+
|
|
31
|
+
## Future optional features
|
|
32
|
+
|
|
33
|
+
- If any optional outbound feature is ever added (for example, connecting to
|
|
34
|
+
Netallion AI Assurance), it will be **explicitly opt-in**, off by default, and
|
|
35
|
+
documented separately. The core Lite scanner will always work fully offline.
|
|
36
|
+
|
|
37
|
+
## Contact
|
|
38
|
+
|
|
39
|
+
Questions: https://www.netallion.ai
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Netallion MCP Lite
|
|
2
|
+
|
|
3
|
+
**Free, local, IP-safe AI-development security — an MCP server from Netallion.**
|
|
4
|
+
|
|
5
|
+
Netallion MCP Lite is a small [Model Context Protocol](https://modelcontextprotocol.io) server that helps you and your AI coding assistants avoid the most common mistake in AI-assisted development: leaking a credential. It flags a curated set of **commodity, high-confidence** secrets and credentials (AWS, GitHub, Stripe, OpenAI, private keys, database URIs, and more) in text and files.
|
|
6
|
+
|
|
7
|
+
It runs **entirely on your machine**. No account. No data sent to Netallion. Detected secrets are **redacted** before results are returned, so they are safe to hand back into an AI assistant's context.
|
|
8
|
+
|
|
9
|
+
Part of Netallion's **Agentic Development Governance** approach — secret detection is one capability, not the whole story.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What it is (and isn't)
|
|
14
|
+
|
|
15
|
+
| | **Netallion MCP Lite** (this package) | **Netallion AI Assurance** (the platform) |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Price | Free | Commercial (free trial) |
|
|
18
|
+
| Runs | Locally, no account | SaaS + IDE + CI |
|
|
19
|
+
| Detection | ~27 commodity, high-confidence checks | Advanced detection well beyond commodity patterns |
|
|
20
|
+
| AI/MCP governance | — | AI provider governance, MCP governance |
|
|
21
|
+
| Policy & visibility | — | Central policy management, org-wide visibility |
|
|
22
|
+
| Compliance | — | Compliance and audit evidence |
|
|
23
|
+
| Data | Nothing leaves your machine | Enterprise controls |
|
|
24
|
+
|
|
25
|
+
MCP Lite is deliberately limited. It is a genuinely useful local utility **and** an on-ramp: if you want advanced detection, AI/MCP governance, central policy, and audit evidence across your organisation, that lives in **[Netallion AI Assurance](https://www.netallion.ai)**.
|
|
26
|
+
|
|
27
|
+
This package does **not** contain or depend on Netallion's proprietary detection engine.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Install & run
|
|
32
|
+
|
|
33
|
+
Requires Python 3.10+.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install netallion-mcp-lite
|
|
37
|
+
# or, zero-install:
|
|
38
|
+
uvx netallion-mcp-lite
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
It speaks MCP over stdio. Add it to any MCP-compatible client (Claude, Cursor, Windsurf, VS Code, …):
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"netallion-mcp-lite": {
|
|
47
|
+
"command": "netallion-mcp-lite"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Optionally confine `scan_file` to a specific directory:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"netallion-mcp-lite": {
|
|
59
|
+
"command": "netallion-mcp-lite",
|
|
60
|
+
"env": { "NETALLION_MCP_LITE_WORKSPACE_ROOT": "/path/to/your/project" }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Tools
|
|
67
|
+
|
|
68
|
+
| Tool | What it does |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `scan_text` | Scan a string for commodity secrets; returns redacted findings (rule, severity, line/column, remediation). |
|
|
71
|
+
| `scan_file` | Scan a file **inside the workspace root**; sensitive files (`.env`, keys) are refused; findings redacted. |
|
|
72
|
+
| `list_supported_checks` | List the commodity checks (no secrets). |
|
|
73
|
+
| `about` | About the tool, privacy posture, and Lite vs AI Assurance. |
|
|
74
|
+
|
|
75
|
+
Run `list_supported_checks` to see the full set. Findings never include the full secret value.
|
|
76
|
+
|
|
77
|
+
## Privacy
|
|
78
|
+
|
|
79
|
+
Local-only. No telemetry. No source, file contents, prompts, or secrets are sent anywhere. See [PRIVACY.md](PRIVACY.md).
|
|
80
|
+
|
|
81
|
+
## Scope & limitations
|
|
82
|
+
|
|
83
|
+
- Commodity, high-confidence formats only — it will not catch obfuscated, encoded, or org-specific secrets. That is by design; advanced detection is part of Netallion AI Assurance.
|
|
84
|
+
- `scan_file` reads only within a single workspace root and refuses obviously-sensitive files.
|
|
85
|
+
- No generic high-entropy scanning (it produces noise without advanced technique).
|
|
86
|
+
|
|
87
|
+
We make no claims about detection rates, false-positive rates, customer adoption, certifications, or regulatory compliance for this free utility.
|
|
88
|
+
|
|
89
|
+
## Links
|
|
90
|
+
|
|
91
|
+
- Netallion AI Assurance — https://www.netallion.ai
|
|
92
|
+
- VS Code / Cursor / Windsurf extension — [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=netallion.netallion-ai-assurance) · [Open VSX](https://open-vsx.org/extension/netallion/netallion-ai-assurance)
|
|
93
|
+
- Azure Marketplace — [Netallion AI Assurance](https://marketplace.microsoft.com/en-us/product/netallion.netallion-ai-assurance)
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "netallion-mcp-lite"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Free, local, IP-safe MCP server for AI-development security — commodity secret & credential checks from Netallion. Part of Agentic Development Governance."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Netallion", email = "hello@netallion.ai" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"mcp",
|
|
15
|
+
"model-context-protocol",
|
|
16
|
+
"security",
|
|
17
|
+
"secrets",
|
|
18
|
+
"credentials",
|
|
19
|
+
"ai",
|
|
20
|
+
"agentic",
|
|
21
|
+
"governance",
|
|
22
|
+
"devsecops",
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 4 - Beta",
|
|
26
|
+
"Intended Audience :: Developers",
|
|
27
|
+
"License :: OSI Approved :: MIT License",
|
|
28
|
+
"Operating System :: OS Independent",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Topic :: Security",
|
|
34
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
# IP FIREWALL: the ONLY runtime dependency is the public MCP SDK.
|
|
38
|
+
# Never add the proprietary Netallion detection engine or any private
|
|
39
|
+
# Netallion package here. The firewall test and CI enforce this.
|
|
40
|
+
dependencies = [
|
|
41
|
+
"mcp>=1.0.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.urls]
|
|
45
|
+
Homepage = "https://www.netallion.ai"
|
|
46
|
+
Documentation = "https://www.netallion.ai"
|
|
47
|
+
Repository = "https://github.com/netallion/netallion-mcp-lite"
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
netallion-mcp-lite = "netallion_mcp_lite.server:main"
|
|
51
|
+
|
|
52
|
+
[tool.setuptools.packages.find]
|
|
53
|
+
where = ["src"]
|
|
54
|
+
|
|
55
|
+
[tool.setuptools.package-data]
|
|
56
|
+
netallion_mcp_lite = ["py.typed"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Netallion MCP Lite — free, local, IP-safe AI-development security.
|
|
2
|
+
|
|
3
|
+
A standalone Model Context Protocol server that scans text and files for a
|
|
4
|
+
curated set of *commodity*, high-confidence secret/credential formats — the
|
|
5
|
+
kind that are already publicly documented across the ecosystem. It runs fully
|
|
6
|
+
locally, sends nothing to Netallion, and requires no account.
|
|
7
|
+
|
|
8
|
+
This package is intentionally isolated from Netallion's proprietary detection
|
|
9
|
+
engine. It is a developer utility and an on-ramp to Netallion AI Assurance —
|
|
10
|
+
the enterprise Agentic Development Governance platform — not a reimplementation
|
|
11
|
+
of it. See README.md for the Lite vs AI Assurance distinction.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
from netallion_mcp_lite.rules import RULES
|
|
17
|
+
from netallion_mcp_lite.scanner import Finding, scan_text
|
|
18
|
+
|
|
19
|
+
__all__ = ["RULES", "Finding", "scan_text", "__version__"]
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Redaction helpers.
|
|
2
|
+
|
|
3
|
+
CRITICAL: results from this package are intended to be handed back into an LLM
|
|
4
|
+
context (that is what an MCP tool result is). We therefore never return a full
|
|
5
|
+
secret value. Every detected secret is masked before it leaves the scanner.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def redact(secret: str) -> str:
|
|
12
|
+
"""Mask a secret for safe display.
|
|
13
|
+
|
|
14
|
+
Short values are fully masked; longer values keep a tiny prefix/suffix so a
|
|
15
|
+
human can correlate the finding without the value ever being usable.
|
|
16
|
+
"""
|
|
17
|
+
s = secret.strip()
|
|
18
|
+
if not s:
|
|
19
|
+
return ""
|
|
20
|
+
n = len(s)
|
|
21
|
+
if n <= 8:
|
|
22
|
+
return "*" * n
|
|
23
|
+
# Reveal only a small prefix/suffix — never more than ~1/3 of the value, and
|
|
24
|
+
# for short values (9-11 chars) reveal a prefix only.
|
|
25
|
+
head_len = 4 if n >= 16 else 2
|
|
26
|
+
tail_len = 2 if n >= 12 else 0
|
|
27
|
+
head = s[:head_len]
|
|
28
|
+
tail = s[-tail_len:] if tail_len else ""
|
|
29
|
+
stars = min(n - head_len - tail_len, 12)
|
|
30
|
+
return f"{head}{'*' * stars}{tail}"
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"""Commodity detection rules for Netallion MCP Lite.
|
|
2
|
+
|
|
3
|
+
INTENTIONALLY PUBLIC. Every rule here matches a credential/secret format that
|
|
4
|
+
is already widely documented in open tools and vendor docs (AWS, GitHub, Stripe,
|
|
5
|
+
etc.). None of this logic is derived from, copied from, or dependent on
|
|
6
|
+
Netallion's proprietary detection engine. The differentiated technology —
|
|
7
|
+
the full pattern corpus, BPE detection, live verifiers, FP classification, and
|
|
8
|
+
runtime AI-threat rules — lives exclusively in Netallion AI Assurance and is
|
|
9
|
+
NOT present in this package.
|
|
10
|
+
|
|
11
|
+
Design test applied to every rule: "Would we be comfortable publishing this
|
|
12
|
+
exact detection logic permanently on GitHub for competitors to copy?" Only
|
|
13
|
+
rules that pass are included. High-entropy/BPE-style generic detection is
|
|
14
|
+
deliberately excluded — it cannot be done well without proprietary technique.
|
|
15
|
+
|
|
16
|
+
Patterns are written to be simple and linear-time (no nested/ambiguous
|
|
17
|
+
quantifiers) to avoid catastrophic backtracking.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import re
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
|
|
25
|
+
Severity = str # "critical" | "high" | "medium" | "low"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class Rule:
|
|
30
|
+
"""A single commodity detection rule."""
|
|
31
|
+
|
|
32
|
+
id: str
|
|
33
|
+
name: str
|
|
34
|
+
severity: Severity
|
|
35
|
+
description: str
|
|
36
|
+
pattern: re.Pattern[str]
|
|
37
|
+
remediation: str
|
|
38
|
+
# 1-based index of the capture group holding the secret to redact; 0 = whole match.
|
|
39
|
+
secret_group: int = 0
|
|
40
|
+
tags: tuple[str, ...] = field(default_factory=tuple)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
_ROTATE = (
|
|
44
|
+
"Revoke/rotate this credential at the provider, remove it from the code or "
|
|
45
|
+
"config, and load it from a secret manager or environment variable instead."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# Rules — commodity, high-confidence formats only.
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
RULES: tuple[Rule, ...] = (
|
|
52
|
+
Rule(
|
|
53
|
+
id="aws-access-key-id",
|
|
54
|
+
name="AWS Access Key ID",
|
|
55
|
+
severity="high",
|
|
56
|
+
description="Amazon Web Services access key identifier (AKIA/ASIA prefix).",
|
|
57
|
+
pattern=re.compile(r"\b(?:AKIA|ASIA)[0-9A-Z]{16}\b"),
|
|
58
|
+
remediation="Deactivate the key in AWS IAM and issue a replacement. " + _ROTATE,
|
|
59
|
+
tags=("cloud", "aws"),
|
|
60
|
+
),
|
|
61
|
+
Rule(
|
|
62
|
+
id="aws-secret-access-key",
|
|
63
|
+
name="AWS Secret Access Key (assignment)",
|
|
64
|
+
severity="high",
|
|
65
|
+
description="A 40-char AWS secret access key assigned to an aws_secret_access_key field.",
|
|
66
|
+
pattern=re.compile(
|
|
67
|
+
r"(?i)aws_secret_access_key\s*[:=]\s*[\"']?([A-Za-z0-9/+]{40})[\"']?"
|
|
68
|
+
),
|
|
69
|
+
remediation="Rotate the AWS secret access key immediately in IAM. " + _ROTATE,
|
|
70
|
+
secret_group=1,
|
|
71
|
+
tags=("cloud", "aws"),
|
|
72
|
+
),
|
|
73
|
+
Rule(
|
|
74
|
+
id="github-pat-classic",
|
|
75
|
+
name="GitHub Personal Access Token (classic)",
|
|
76
|
+
severity="high",
|
|
77
|
+
description="GitHub classic personal access token (ghp_ prefix).",
|
|
78
|
+
pattern=re.compile(r"\bghp_[A-Za-z0-9]{36}\b"),
|
|
79
|
+
remediation="Revoke the token in GitHub Developer settings. " + _ROTATE,
|
|
80
|
+
tags=("vcs", "github"),
|
|
81
|
+
),
|
|
82
|
+
Rule(
|
|
83
|
+
id="github-oauth-token",
|
|
84
|
+
name="GitHub OAuth Access Token",
|
|
85
|
+
severity="high",
|
|
86
|
+
description="GitHub OAuth access token (gho_ prefix).",
|
|
87
|
+
pattern=re.compile(r"\bgho_[A-Za-z0-9]{36}\b"),
|
|
88
|
+
remediation=_ROTATE,
|
|
89
|
+
tags=("vcs", "github"),
|
|
90
|
+
),
|
|
91
|
+
Rule(
|
|
92
|
+
id="github-app-token",
|
|
93
|
+
name="GitHub App Token",
|
|
94
|
+
severity="high",
|
|
95
|
+
description="GitHub app user-to-server / server-to-server token (ghu_/ghs_ prefix).",
|
|
96
|
+
pattern=re.compile(r"\b(?:ghu|ghs)_[A-Za-z0-9]{36}\b"),
|
|
97
|
+
remediation=_ROTATE,
|
|
98
|
+
tags=("vcs", "github"),
|
|
99
|
+
),
|
|
100
|
+
Rule(
|
|
101
|
+
id="github-refresh-token",
|
|
102
|
+
name="GitHub Refresh Token",
|
|
103
|
+
severity="medium",
|
|
104
|
+
description="GitHub refresh token (ghr_ prefix).",
|
|
105
|
+
pattern=re.compile(r"\bghr_[A-Za-z0-9]{36}\b"),
|
|
106
|
+
remediation=_ROTATE,
|
|
107
|
+
tags=("vcs", "github"),
|
|
108
|
+
),
|
|
109
|
+
Rule(
|
|
110
|
+
id="github-fine-grained-pat",
|
|
111
|
+
name="GitHub Fine-Grained PAT",
|
|
112
|
+
severity="high",
|
|
113
|
+
description="GitHub fine-grained personal access token (github_pat_ prefix).",
|
|
114
|
+
pattern=re.compile(r"\bgithub_pat_[A-Za-z0-9_]{82}\b"),
|
|
115
|
+
remediation="Revoke the fine-grained token in GitHub Developer settings. " + _ROTATE,
|
|
116
|
+
tags=("vcs", "github"),
|
|
117
|
+
),
|
|
118
|
+
Rule(
|
|
119
|
+
id="gitlab-pat",
|
|
120
|
+
name="GitLab Personal Access Token",
|
|
121
|
+
severity="high",
|
|
122
|
+
description="GitLab personal access token (glpat- prefix).",
|
|
123
|
+
pattern=re.compile(r"\bglpat-[A-Za-z0-9_\-]{20}\b"),
|
|
124
|
+
remediation="Revoke the token in GitLab access-token settings. " + _ROTATE,
|
|
125
|
+
tags=("vcs", "gitlab"),
|
|
126
|
+
),
|
|
127
|
+
Rule(
|
|
128
|
+
id="openai-api-key",
|
|
129
|
+
name="OpenAI API Key",
|
|
130
|
+
severity="high",
|
|
131
|
+
description="OpenAI API key (sk- prefix, incl. sk-proj-/sk-svcacct- with _/- in the body).",
|
|
132
|
+
# Two precise forms: a known modern prefix (proj-/svcacct-/admin-) with a
|
|
133
|
+
# long body, OR the classic long all-alphanumeric key. Matches real keys
|
|
134
|
+
# without flagging ordinary sk-<kebab-case> identifiers.
|
|
135
|
+
pattern=re.compile(
|
|
136
|
+
r"\bsk-(?:proj|svcacct|admin)-[A-Za-z0-9_-]{20,}\b"
|
|
137
|
+
r"|\bsk-[A-Za-z0-9]{32,}\b"
|
|
138
|
+
),
|
|
139
|
+
remediation="Revoke the key in the OpenAI dashboard. " + _ROTATE,
|
|
140
|
+
tags=("ai", "openai"),
|
|
141
|
+
),
|
|
142
|
+
Rule(
|
|
143
|
+
id="anthropic-api-key",
|
|
144
|
+
name="Anthropic API Key",
|
|
145
|
+
severity="high",
|
|
146
|
+
description="Anthropic API key (sk-ant- prefix).",
|
|
147
|
+
pattern=re.compile(r"\bsk-ant-[A-Za-z0-9_\-]{80,}\b"),
|
|
148
|
+
remediation="Revoke the key in the Anthropic console. " + _ROTATE,
|
|
149
|
+
tags=("ai", "anthropic"),
|
|
150
|
+
),
|
|
151
|
+
Rule(
|
|
152
|
+
id="google-api-key",
|
|
153
|
+
name="Google API Key",
|
|
154
|
+
severity="high",
|
|
155
|
+
description="Google API key (AIza prefix).",
|
|
156
|
+
pattern=re.compile(r"\bAIza[0-9A-Za-z_\-]{35}\b"),
|
|
157
|
+
remediation="Regenerate the key in the Google Cloud console. " + _ROTATE,
|
|
158
|
+
tags=("cloud", "google"),
|
|
159
|
+
),
|
|
160
|
+
Rule(
|
|
161
|
+
id="slack-token",
|
|
162
|
+
name="Slack Token",
|
|
163
|
+
severity="high",
|
|
164
|
+
description="Slack API token (xoxb/xoxa/xoxp/xoxr/xoxs prefix).",
|
|
165
|
+
pattern=re.compile(r"\bxox[baprs]-[A-Za-z0-9\-]{10,}\b"),
|
|
166
|
+
remediation="Revoke the token in Slack app settings. " + _ROTATE,
|
|
167
|
+
tags=("saas", "slack"),
|
|
168
|
+
),
|
|
169
|
+
Rule(
|
|
170
|
+
id="slack-webhook-url",
|
|
171
|
+
name="Slack Incoming Webhook URL",
|
|
172
|
+
severity="medium",
|
|
173
|
+
description="Slack incoming webhook URL (hooks.slack.com/services/...).",
|
|
174
|
+
pattern=re.compile(
|
|
175
|
+
r"https://hooks\.slack\.com/services/T[A-Za-z0-9_]+/B[A-Za-z0-9_]+/[A-Za-z0-9_]{20,}"
|
|
176
|
+
),
|
|
177
|
+
remediation="Delete and recreate the webhook in Slack. " + _ROTATE,
|
|
178
|
+
tags=("saas", "slack"),
|
|
179
|
+
),
|
|
180
|
+
Rule(
|
|
181
|
+
id="stripe-live-secret-key",
|
|
182
|
+
name="Stripe Live Secret Key",
|
|
183
|
+
severity="critical",
|
|
184
|
+
description="Stripe live-mode secret/restricted key (sk_live_/rk_live_).",
|
|
185
|
+
pattern=re.compile(r"\b(?:sk|rk)_live_[0-9a-zA-Z]{24,}\b"),
|
|
186
|
+
remediation="Roll the key in the Stripe dashboard immediately. " + _ROTATE,
|
|
187
|
+
tags=("payments", "stripe"),
|
|
188
|
+
),
|
|
189
|
+
Rule(
|
|
190
|
+
id="stripe-test-secret-key",
|
|
191
|
+
name="Stripe Test Secret Key",
|
|
192
|
+
severity="low",
|
|
193
|
+
description="Stripe test-mode secret/restricted key (sk_test_/rk_test_).",
|
|
194
|
+
pattern=re.compile(r"\b(?:sk|rk)_test_[0-9a-zA-Z]{24,}\b"),
|
|
195
|
+
remediation="Test-mode key — still remove from source and roll if unsure. " + _ROTATE,
|
|
196
|
+
tags=("payments", "stripe"),
|
|
197
|
+
),
|
|
198
|
+
Rule(
|
|
199
|
+
id="twilio-api-key",
|
|
200
|
+
name="Twilio API Key SID",
|
|
201
|
+
severity="high",
|
|
202
|
+
description="Twilio API key SID (SK + 32 hex).",
|
|
203
|
+
pattern=re.compile(r"\bSK[0-9a-fA-F]{32}\b"),
|
|
204
|
+
remediation="Delete the API key in the Twilio console. " + _ROTATE,
|
|
205
|
+
tags=("saas", "twilio"),
|
|
206
|
+
),
|
|
207
|
+
Rule(
|
|
208
|
+
id="twilio-account-sid",
|
|
209
|
+
name="Twilio Account SID",
|
|
210
|
+
severity="medium",
|
|
211
|
+
description="Twilio account SID (AC + 32 hex).",
|
|
212
|
+
pattern=re.compile(r"\bAC[0-9a-fA-F]{32}\b"),
|
|
213
|
+
remediation="Account SID is an identifier; ensure the paired auth token is not exposed.",
|
|
214
|
+
tags=("saas", "twilio"),
|
|
215
|
+
),
|
|
216
|
+
Rule(
|
|
217
|
+
id="sendgrid-api-key",
|
|
218
|
+
name="SendGrid API Key",
|
|
219
|
+
severity="high",
|
|
220
|
+
description="SendGrid API key (SG. prefix).",
|
|
221
|
+
pattern=re.compile(r"\bSG\.[A-Za-z0-9_\-]{22}\.[A-Za-z0-9_\-]{43}\b"),
|
|
222
|
+
remediation="Delete the key in SendGrid settings. " + _ROTATE,
|
|
223
|
+
tags=("saas", "sendgrid"),
|
|
224
|
+
),
|
|
225
|
+
Rule(
|
|
226
|
+
id="npm-access-token",
|
|
227
|
+
name="npm Access Token",
|
|
228
|
+
severity="high",
|
|
229
|
+
description="npm access token (npm_ prefix).",
|
|
230
|
+
pattern=re.compile(r"\bnpm_[A-Za-z0-9]{36}\b"),
|
|
231
|
+
remediation="Revoke the token in your npm account settings. " + _ROTATE,
|
|
232
|
+
tags=("package", "npm"),
|
|
233
|
+
),
|
|
234
|
+
Rule(
|
|
235
|
+
id="pem-private-key",
|
|
236
|
+
name="Private Key (PEM header)",
|
|
237
|
+
severity="critical",
|
|
238
|
+
description="A PEM-encoded private key block header.",
|
|
239
|
+
pattern=re.compile(
|
|
240
|
+
r"-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY-----"
|
|
241
|
+
),
|
|
242
|
+
remediation="Treat the key as compromised: rotate the key pair and revoke any certs. " + _ROTATE,
|
|
243
|
+
tags=("crypto", "private-key"),
|
|
244
|
+
),
|
|
245
|
+
Rule(
|
|
246
|
+
id="jwt",
|
|
247
|
+
name="JSON Web Token",
|
|
248
|
+
# Low severity: JWTs are structural (not inherently secret), so this is a
|
|
249
|
+
# low-confidence signal. Severity communicates that confidence for now.
|
|
250
|
+
severity="low",
|
|
251
|
+
description="A JWT (three base64url segments, header starts with eyJ).",
|
|
252
|
+
pattern=re.compile(r"\beyJ[A-Za-z0-9_\-]{10,}\.eyJ[A-Za-z0-9_\-]{10,}\.[A-Za-z0-9_\-]{10,}\b"),
|
|
253
|
+
remediation="If this token grants access, invalidate the session/signing key. " + _ROTATE,
|
|
254
|
+
tags=("token", "jwt"),
|
|
255
|
+
),
|
|
256
|
+
Rule(
|
|
257
|
+
id="db-connection-uri",
|
|
258
|
+
name="Database Connection URI with Credentials",
|
|
259
|
+
severity="high",
|
|
260
|
+
description="A database URI with an embedded username:password (postgres/mysql/mongodb/redis/amqp).",
|
|
261
|
+
pattern=re.compile(
|
|
262
|
+
r"\b(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp)://[^\s:@/]+:[^\s:@/]+@[^\s/]+"
|
|
263
|
+
),
|
|
264
|
+
remediation="Rotate the database password and move the URI to a secret manager. " + _ROTATE,
|
|
265
|
+
tags=("database",),
|
|
266
|
+
),
|
|
267
|
+
Rule(
|
|
268
|
+
id="authorization-bearer",
|
|
269
|
+
name="Authorization Bearer Token",
|
|
270
|
+
severity="medium",
|
|
271
|
+
description="An Authorization header assigning a Bearer token.",
|
|
272
|
+
pattern=re.compile(
|
|
273
|
+
r"(?i)authorization\s*[:=]\s*[\"']?bearer\s+([A-Za-z0-9\-._~+/]{20,}={0,2})"
|
|
274
|
+
),
|
|
275
|
+
remediation="Rotate the bearer token/credential it represents. " + _ROTATE,
|
|
276
|
+
secret_group=1,
|
|
277
|
+
tags=("token",),
|
|
278
|
+
),
|
|
279
|
+
Rule(
|
|
280
|
+
id="generic-api-key-assignment",
|
|
281
|
+
name="Generic API Key / Token Assignment",
|
|
282
|
+
# Low severity/confidence: also matches placeholders/examples.
|
|
283
|
+
severity="low",
|
|
284
|
+
description="A quoted value assigned to an api_key / secret_key / access_token style field.",
|
|
285
|
+
pattern=re.compile(
|
|
286
|
+
r"(?i)\b(?:api[_-]?key|secret[_-]?key|access[_-]?token|auth[_-]?token)\b\s*[:=]\s*[\"']([A-Za-z0-9\-_]{16,})[\"']"
|
|
287
|
+
),
|
|
288
|
+
remediation="Confirm whether this is a live secret; if so, rotate it. " + _ROTATE,
|
|
289
|
+
secret_group=1,
|
|
290
|
+
tags=("generic",),
|
|
291
|
+
),
|
|
292
|
+
Rule(
|
|
293
|
+
id="generic-password-assignment",
|
|
294
|
+
name="Hardcoded Password Assignment",
|
|
295
|
+
severity="low",
|
|
296
|
+
description="A quoted value assigned to a password / passwd / pwd field.",
|
|
297
|
+
pattern=re.compile(
|
|
298
|
+
r"(?i)\b(?:password|passwd|pwd)\b\s*[:=]\s*[\"']([^\"']{6,})[\"']"
|
|
299
|
+
),
|
|
300
|
+
remediation="Do not hardcode passwords; load from a secret manager and rotate if real.",
|
|
301
|
+
secret_group=1,
|
|
302
|
+
tags=("generic",),
|
|
303
|
+
),
|
|
304
|
+
Rule(
|
|
305
|
+
id="azure-storage-account-key",
|
|
306
|
+
name="Azure Storage Account Key",
|
|
307
|
+
severity="high",
|
|
308
|
+
description="An Azure Storage AccountKey= value (88-char base64).",
|
|
309
|
+
pattern=re.compile(r"(?i)AccountKey\s*=\s*([A-Za-z0-9+/]{86,}==)"),
|
|
310
|
+
remediation="Regenerate the storage account key in the Azure portal. " + _ROTATE,
|
|
311
|
+
secret_group=1,
|
|
312
|
+
tags=("cloud", "azure"),
|
|
313
|
+
),
|
|
314
|
+
Rule(
|
|
315
|
+
id="telegram-bot-token",
|
|
316
|
+
name="Telegram Bot Token",
|
|
317
|
+
severity="medium",
|
|
318
|
+
description="A Telegram bot token (numeric id : 35-char secret).",
|
|
319
|
+
pattern=re.compile(r"\b\d{8,10}:[A-Za-z0-9_\-]{35}\b"),
|
|
320
|
+
remediation="Revoke the bot token via BotFather (/revoke). " + _ROTATE,
|
|
321
|
+
tags=("saas", "telegram"),
|
|
322
|
+
),
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def rule_by_id(rule_id: str) -> Rule | None:
|
|
327
|
+
for rule in RULES:
|
|
328
|
+
if rule.id == rule_id:
|
|
329
|
+
return rule
|
|
330
|
+
return None
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Local scanning engine for Netallion MCP Lite.
|
|
2
|
+
|
|
3
|
+
Runs entirely in-process. No network calls, no telemetry, no dependency on the
|
|
4
|
+
Netallion platform. Matched secrets are redacted before being returned.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import asdict, dataclass
|
|
10
|
+
|
|
11
|
+
from netallion_mcp_lite.redaction import redact
|
|
12
|
+
from netallion_mcp_lite.rules import RULES
|
|
13
|
+
|
|
14
|
+
# Cap how much text we scan per call to keep behaviour predictable and fast.
|
|
15
|
+
MAX_SCAN_CHARS = 5_000_000 # ~5M characters
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class Finding:
|
|
20
|
+
rule_id: str
|
|
21
|
+
rule_name: str
|
|
22
|
+
severity: str
|
|
23
|
+
line: int
|
|
24
|
+
column: int
|
|
25
|
+
redacted_preview: str
|
|
26
|
+
remediation: str
|
|
27
|
+
|
|
28
|
+
def as_dict(self) -> dict:
|
|
29
|
+
return asdict(self)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _secret_from_match(match, secret_group: int) -> str:
|
|
33
|
+
if secret_group and match.lastindex and secret_group <= match.lastindex:
|
|
34
|
+
value = match.group(secret_group)
|
|
35
|
+
if value is not None:
|
|
36
|
+
return value
|
|
37
|
+
return match.group(0)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def scan_text(text: str, source: str = "text") -> list[Finding]:
|
|
41
|
+
"""Scan text line by line and return redacted findings, ordered by location."""
|
|
42
|
+
if not text:
|
|
43
|
+
return []
|
|
44
|
+
if len(text) > MAX_SCAN_CHARS:
|
|
45
|
+
text = text[:MAX_SCAN_CHARS]
|
|
46
|
+
|
|
47
|
+
findings: list[Finding] = []
|
|
48
|
+
for line_no, line in enumerate(text.splitlines(), start=1):
|
|
49
|
+
for rule in RULES:
|
|
50
|
+
for match in rule.pattern.finditer(line):
|
|
51
|
+
secret = _secret_from_match(match, rule.secret_group)
|
|
52
|
+
findings.append(
|
|
53
|
+
Finding(
|
|
54
|
+
rule_id=rule.id,
|
|
55
|
+
rule_name=rule.name,
|
|
56
|
+
severity=rule.severity,
|
|
57
|
+
line=line_no,
|
|
58
|
+
column=match.start() + 1,
|
|
59
|
+
redacted_preview=redact(secret),
|
|
60
|
+
remediation=rule.remediation,
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
findings.sort(key=lambda f: (f.line, f.column, f.rule_id))
|
|
64
|
+
return findings
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def summarize(findings: list[Finding]) -> dict[str, int]:
|
|
68
|
+
"""Count findings by severity."""
|
|
69
|
+
counts: dict[str, int] = {}
|
|
70
|
+
for f in findings:
|
|
71
|
+
counts[f.severity] = counts.get(f.severity, 0) + 1
|
|
72
|
+
return counts
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""Netallion MCP Lite — standards-compliant MCP server (stdio).
|
|
2
|
+
|
|
3
|
+
Exposes four tools: scan_text, scan_file, list_supported_checks, about.
|
|
4
|
+
Everything runs locally; no secrets or source ever leave the machine, and all
|
|
5
|
+
detected secret material is redacted before being returned to the caller.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from mcp.server import Server
|
|
16
|
+
from mcp.server.stdio import stdio_server
|
|
17
|
+
from mcp.types import TextContent, Tool
|
|
18
|
+
|
|
19
|
+
from netallion_mcp_lite import __version__
|
|
20
|
+
from netallion_mcp_lite.rules import RULES
|
|
21
|
+
from netallion_mcp_lite.scanner import scan_text as _scan_text
|
|
22
|
+
from netallion_mcp_lite.scanner import summarize
|
|
23
|
+
|
|
24
|
+
# Files we refuse to read even inside the workspace — reading them would defeat
|
|
25
|
+
# the point (and their contents would flow back into an LLM context).
|
|
26
|
+
_BLOCKED_NAMES = {".env", "id_rsa", "id_dsa", "id_ecdsa", "id_ed25519", "credentials"}
|
|
27
|
+
_BLOCKED_SUFFIXES = (".env", ".key", ".pem", ".p12", ".pfx", ".keystore")
|
|
28
|
+
# Example/template env files hold placeholders, not real secrets — safe to scan.
|
|
29
|
+
_ENV_EXAMPLE_NAMES = {".env.example", ".env.sample", ".env.template", ".env.dist"}
|
|
30
|
+
_MAX_FILE_BYTES = 10_000_000 # 10 MB
|
|
31
|
+
|
|
32
|
+
_ATTRIBUTION = {
|
|
33
|
+
"product": "Netallion MCP Lite",
|
|
34
|
+
"tagline": "Free, local, IP-safe AI-development security.",
|
|
35
|
+
"learn_more": "https://www.netallion.ai",
|
|
36
|
+
"vs_code_extension": (
|
|
37
|
+
"https://marketplace.visualstudio.com/items?itemName=netallion.netallion-ai-assurance"
|
|
38
|
+
),
|
|
39
|
+
"azure_marketplace": (
|
|
40
|
+
"https://marketplace.microsoft.com/en-us/product/netallion.netallion-ai-assurance"
|
|
41
|
+
),
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _allowed_root() -> Path:
|
|
46
|
+
"""The single directory tree scan_file may read.
|
|
47
|
+
|
|
48
|
+
Defaults to the process working directory (the opened workspace). Override
|
|
49
|
+
with NETALLION_MCP_LITE_WORKSPACE_ROOT for explicit confinement.
|
|
50
|
+
"""
|
|
51
|
+
root = os.environ.get("NETALLION_MCP_LITE_WORKSPACE_ROOT")
|
|
52
|
+
return Path(root).resolve() if root else Path.cwd().resolve()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _root_is_too_broad(root: Path) -> bool:
|
|
56
|
+
"""A filesystem anchor ('/', 'C:\\') or a home directory is too broad to be a
|
|
57
|
+
safe allowlist — a globally-installed server launched with such a cwd would
|
|
58
|
+
otherwise expose ~/.aws, ~/.ssh, etc.
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
home = Path.home().resolve()
|
|
62
|
+
except (RuntimeError, OSError):
|
|
63
|
+
home = None
|
|
64
|
+
return root == Path(root.anchor) or (
|
|
65
|
+
home is not None and (root == home or root in home.parents)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _is_within(path: Path, root: Path) -> bool:
|
|
70
|
+
try:
|
|
71
|
+
path.relative_to(root)
|
|
72
|
+
return True
|
|
73
|
+
except ValueError:
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _err(message: str) -> list[TextContent]:
|
|
78
|
+
return [TextContent(type="text", text=json.dumps({"error": message}))]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _handle_scan_text(arguments: dict[str, Any]) -> list[TextContent]:
|
|
82
|
+
text = arguments.get("text", "")
|
|
83
|
+
source = arguments.get("source", "text")
|
|
84
|
+
if not isinstance(text, str) or not text:
|
|
85
|
+
return [TextContent(type="text", text=json.dumps({"findings": [], "count": 0}))]
|
|
86
|
+
|
|
87
|
+
findings = _scan_text(text, source=str(source))
|
|
88
|
+
payload = {
|
|
89
|
+
"source": str(source),
|
|
90
|
+
"count": len(findings),
|
|
91
|
+
"summary": summarize(findings),
|
|
92
|
+
"findings": [f.as_dict() for f in findings],
|
|
93
|
+
"note": "Commodity checks only (Netallion MCP Lite). Secret values are redacted.",
|
|
94
|
+
}
|
|
95
|
+
return [TextContent(type="text", text=json.dumps(payload, indent=2))]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _handle_scan_file(arguments: dict[str, Any]) -> list[TextContent]:
|
|
99
|
+
file_path = arguments.get("path", "")
|
|
100
|
+
if not file_path or not isinstance(file_path, str):
|
|
101
|
+
return _err("No path provided")
|
|
102
|
+
|
|
103
|
+
# .resolve() collapses '..' AND resolves symlinks, so an in-workspace symlink
|
|
104
|
+
# pointing outside resolves to its real (out-of-root) target and is rejected.
|
|
105
|
+
path = Path(file_path).resolve()
|
|
106
|
+
root = _allowed_root()
|
|
107
|
+
|
|
108
|
+
if _root_is_too_broad(root):
|
|
109
|
+
return _err(
|
|
110
|
+
"scan_file disabled: workspace root is too broad (filesystem/home root). "
|
|
111
|
+
"Set NETALLION_MCP_LITE_WORKSPACE_ROOT to the workspace directory."
|
|
112
|
+
)
|
|
113
|
+
if not _is_within(path, root):
|
|
114
|
+
return _err("Access denied: path is outside the allowed workspace root")
|
|
115
|
+
name = path.name
|
|
116
|
+
if (
|
|
117
|
+
name in _BLOCKED_NAMES
|
|
118
|
+
# .env, .env.local, .env.production, ... but allow .env.example/.sample/etc.
|
|
119
|
+
or (name.startswith(".env") and name not in _ENV_EXAMPLE_NAMES)
|
|
120
|
+
or any(name.endswith(s) for s in _BLOCKED_SUFFIXES)
|
|
121
|
+
):
|
|
122
|
+
return _err("Access denied: sensitive file type is not read by scan_file")
|
|
123
|
+
if not path.exists():
|
|
124
|
+
return _err(f"File not found: {file_path}")
|
|
125
|
+
if not path.is_file():
|
|
126
|
+
return _err(f"Not a file: {file_path}")
|
|
127
|
+
if path.stat().st_size > _MAX_FILE_BYTES:
|
|
128
|
+
return _err("File too large (>10MB)")
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
text = path.read_text(errors="replace")
|
|
132
|
+
except OSError as exc:
|
|
133
|
+
return _err(f"Cannot read file: {exc}")
|
|
134
|
+
|
|
135
|
+
findings = _scan_text(text, source=str(path))
|
|
136
|
+
payload = {
|
|
137
|
+
"path": str(path),
|
|
138
|
+
"count": len(findings),
|
|
139
|
+
"summary": summarize(findings),
|
|
140
|
+
"findings": [f.as_dict() for f in findings],
|
|
141
|
+
"note": "Commodity checks only (Netallion MCP Lite). Secret values are redacted.",
|
|
142
|
+
}
|
|
143
|
+
return [TextContent(type="text", text=json.dumps(payload, indent=2))]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _handle_list_supported_checks() -> list[TextContent]:
|
|
147
|
+
payload = {
|
|
148
|
+
"count": len(RULES),
|
|
149
|
+
"checks": [
|
|
150
|
+
{
|
|
151
|
+
"id": r.id,
|
|
152
|
+
"name": r.name,
|
|
153
|
+
"severity": r.severity,
|
|
154
|
+
"description": r.description,
|
|
155
|
+
"tags": list(r.tags),
|
|
156
|
+
}
|
|
157
|
+
for r in RULES
|
|
158
|
+
],
|
|
159
|
+
"note": (
|
|
160
|
+
"These are commodity, publicly-documented credential formats. "
|
|
161
|
+
"Netallion AI Assurance adds advanced detection, AI/MCP governance, "
|
|
162
|
+
"policy management, and compliance evidence."
|
|
163
|
+
),
|
|
164
|
+
}
|
|
165
|
+
return [TextContent(type="text", text=json.dumps(payload, indent=2))]
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _handle_about() -> list[TextContent]:
|
|
169
|
+
payload = {
|
|
170
|
+
**_ATTRIBUTION,
|
|
171
|
+
"version": __version__,
|
|
172
|
+
"what_this_is": (
|
|
173
|
+
"Netallion MCP Lite is a free, local MCP server that flags a curated set "
|
|
174
|
+
"of commodity, high-confidence secrets and credentials as you and your AI "
|
|
175
|
+
"assistants work. It runs entirely on your machine — no account, no data "
|
|
176
|
+
"sent to Netallion."
|
|
177
|
+
),
|
|
178
|
+
"lite_vs_ai_assurance": {
|
|
179
|
+
"netallion_mcp_lite": [
|
|
180
|
+
"Free and local; no account required",
|
|
181
|
+
"~27 commodity, high-confidence credential checks",
|
|
182
|
+
"No source/secrets ever uploaded",
|
|
183
|
+
],
|
|
184
|
+
"netallion_ai_assurance": [
|
|
185
|
+
"Enterprise Agentic Development Governance platform",
|
|
186
|
+
"Advanced detection beyond commodity patterns",
|
|
187
|
+
"AI provider governance and MCP governance",
|
|
188
|
+
"Central policy management and organisational visibility",
|
|
189
|
+
"Compliance and audit evidence",
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
"privacy": "Local-only scanning. No telemetry. See PRIVACY.md.",
|
|
193
|
+
}
|
|
194
|
+
return [TextContent(type="text", text=json.dumps(payload, indent=2))]
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _dispatch(name: str, arguments: dict[str, Any]) -> list[TextContent]:
|
|
198
|
+
"""Route a tool call by name. Module-level so the unknown-tool path is testable."""
|
|
199
|
+
if name == "scan_text":
|
|
200
|
+
return _handle_scan_text(arguments)
|
|
201
|
+
if name == "scan_file":
|
|
202
|
+
return _handle_scan_file(arguments)
|
|
203
|
+
if name == "list_supported_checks":
|
|
204
|
+
return _handle_list_supported_checks()
|
|
205
|
+
if name == "about":
|
|
206
|
+
return _handle_about()
|
|
207
|
+
return _err(f"Unknown tool: {name}")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def create_server() -> Server:
|
|
211
|
+
server: Server = Server("netallion-mcp-lite")
|
|
212
|
+
|
|
213
|
+
@server.list_tools()
|
|
214
|
+
async def list_tools() -> list[Tool]:
|
|
215
|
+
return [
|
|
216
|
+
Tool(
|
|
217
|
+
name="scan_text",
|
|
218
|
+
description=(
|
|
219
|
+
"Scan text locally for commodity secrets and credentials "
|
|
220
|
+
"(AWS/GitHub/Stripe/etc.). Returns redacted findings with rule id, "
|
|
221
|
+
"severity, location, and remediation. Nothing is uploaded."
|
|
222
|
+
),
|
|
223
|
+
inputSchema={
|
|
224
|
+
"type": "object",
|
|
225
|
+
"properties": {
|
|
226
|
+
"text": {"type": "string", "description": "Text to scan."},
|
|
227
|
+
"source": {
|
|
228
|
+
"type": "string",
|
|
229
|
+
"description": "Optional label for the source (e.g. filename).",
|
|
230
|
+
"default": "text",
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
"required": ["text"],
|
|
234
|
+
},
|
|
235
|
+
),
|
|
236
|
+
Tool(
|
|
237
|
+
name="scan_file",
|
|
238
|
+
description=(
|
|
239
|
+
"Scan a file inside the current workspace for commodity secrets. "
|
|
240
|
+
"Confined to the workspace root; sensitive files (.env, keys) are "
|
|
241
|
+
"refused. Returns redacted findings."
|
|
242
|
+
),
|
|
243
|
+
inputSchema={
|
|
244
|
+
"type": "object",
|
|
245
|
+
"properties": {
|
|
246
|
+
"path": {
|
|
247
|
+
"type": "string",
|
|
248
|
+
"description": "Path to a file within the workspace root.",
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"required": ["path"],
|
|
252
|
+
},
|
|
253
|
+
),
|
|
254
|
+
Tool(
|
|
255
|
+
name="list_supported_checks",
|
|
256
|
+
description="List the commodity checks this Lite server supports (no secrets).",
|
|
257
|
+
inputSchema={"type": "object", "properties": {}},
|
|
258
|
+
),
|
|
259
|
+
Tool(
|
|
260
|
+
name="about",
|
|
261
|
+
description=(
|
|
262
|
+
"About Netallion MCP Lite, privacy posture, and how it compares to "
|
|
263
|
+
"Netallion AI Assurance."
|
|
264
|
+
),
|
|
265
|
+
inputSchema={"type": "object", "properties": {}},
|
|
266
|
+
),
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
@server.call_tool()
|
|
270
|
+
async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
|
|
271
|
+
return _dispatch(name, arguments)
|
|
272
|
+
|
|
273
|
+
return server
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
async def run_server() -> None:
|
|
277
|
+
server = create_server()
|
|
278
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
279
|
+
await server.run(read_stream, write_stream, server.create_initialization_options())
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def main() -> None:
|
|
283
|
+
import asyncio
|
|
284
|
+
|
|
285
|
+
asyncio.run(run_server())
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
if __name__ == "__main__":
|
|
289
|
+
main()
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: netallion-mcp-lite
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Free, local, IP-safe MCP server for AI-development security — commodity secret & credential checks from Netallion. Part of Agentic Development Governance.
|
|
5
|
+
Author-email: Netallion <hello@netallion.ai>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://www.netallion.ai
|
|
8
|
+
Project-URL: Documentation, https://www.netallion.ai
|
|
9
|
+
Project-URL: Repository, https://github.com/netallion/netallion-mcp-lite
|
|
10
|
+
Keywords: mcp,model-context-protocol,security,secrets,credentials,ai,agentic,governance,devsecops
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Security
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: mcp>=1.0.0
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# Netallion MCP Lite
|
|
28
|
+
|
|
29
|
+
**Free, local, IP-safe AI-development security — an MCP server from Netallion.**
|
|
30
|
+
|
|
31
|
+
Netallion MCP Lite is a small [Model Context Protocol](https://modelcontextprotocol.io) server that helps you and your AI coding assistants avoid the most common mistake in AI-assisted development: leaking a credential. It flags a curated set of **commodity, high-confidence** secrets and credentials (AWS, GitHub, Stripe, OpenAI, private keys, database URIs, and more) in text and files.
|
|
32
|
+
|
|
33
|
+
It runs **entirely on your machine**. No account. No data sent to Netallion. Detected secrets are **redacted** before results are returned, so they are safe to hand back into an AI assistant's context.
|
|
34
|
+
|
|
35
|
+
Part of Netallion's **Agentic Development Governance** approach — secret detection is one capability, not the whole story.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## What it is (and isn't)
|
|
40
|
+
|
|
41
|
+
| | **Netallion MCP Lite** (this package) | **Netallion AI Assurance** (the platform) |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| Price | Free | Commercial (free trial) |
|
|
44
|
+
| Runs | Locally, no account | SaaS + IDE + CI |
|
|
45
|
+
| Detection | ~27 commodity, high-confidence checks | Advanced detection well beyond commodity patterns |
|
|
46
|
+
| AI/MCP governance | — | AI provider governance, MCP governance |
|
|
47
|
+
| Policy & visibility | — | Central policy management, org-wide visibility |
|
|
48
|
+
| Compliance | — | Compliance and audit evidence |
|
|
49
|
+
| Data | Nothing leaves your machine | Enterprise controls |
|
|
50
|
+
|
|
51
|
+
MCP Lite is deliberately limited. It is a genuinely useful local utility **and** an on-ramp: if you want advanced detection, AI/MCP governance, central policy, and audit evidence across your organisation, that lives in **[Netallion AI Assurance](https://www.netallion.ai)**.
|
|
52
|
+
|
|
53
|
+
This package does **not** contain or depend on Netallion's proprietary detection engine.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Install & run
|
|
58
|
+
|
|
59
|
+
Requires Python 3.10+.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install netallion-mcp-lite
|
|
63
|
+
# or, zero-install:
|
|
64
|
+
uvx netallion-mcp-lite
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
It speaks MCP over stdio. Add it to any MCP-compatible client (Claude, Cursor, Windsurf, VS Code, …):
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"netallion-mcp-lite": {
|
|
73
|
+
"command": "netallion-mcp-lite"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Optionally confine `scan_file` to a specific directory:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"mcpServers": {
|
|
84
|
+
"netallion-mcp-lite": {
|
|
85
|
+
"command": "netallion-mcp-lite",
|
|
86
|
+
"env": { "NETALLION_MCP_LITE_WORKSPACE_ROOT": "/path/to/your/project" }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Tools
|
|
93
|
+
|
|
94
|
+
| Tool | What it does |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `scan_text` | Scan a string for commodity secrets; returns redacted findings (rule, severity, line/column, remediation). |
|
|
97
|
+
| `scan_file` | Scan a file **inside the workspace root**; sensitive files (`.env`, keys) are refused; findings redacted. |
|
|
98
|
+
| `list_supported_checks` | List the commodity checks (no secrets). |
|
|
99
|
+
| `about` | About the tool, privacy posture, and Lite vs AI Assurance. |
|
|
100
|
+
|
|
101
|
+
Run `list_supported_checks` to see the full set. Findings never include the full secret value.
|
|
102
|
+
|
|
103
|
+
## Privacy
|
|
104
|
+
|
|
105
|
+
Local-only. No telemetry. No source, file contents, prompts, or secrets are sent anywhere. See [PRIVACY.md](PRIVACY.md).
|
|
106
|
+
|
|
107
|
+
## Scope & limitations
|
|
108
|
+
|
|
109
|
+
- Commodity, high-confidence formats only — it will not catch obfuscated, encoded, or org-specific secrets. That is by design; advanced detection is part of Netallion AI Assurance.
|
|
110
|
+
- `scan_file` reads only within a single workspace root and refuses obviously-sensitive files.
|
|
111
|
+
- No generic high-entropy scanning (it produces noise without advanced technique).
|
|
112
|
+
|
|
113
|
+
We make no claims about detection rates, false-positive rates, customer adoption, certifications, or regulatory compliance for this free utility.
|
|
114
|
+
|
|
115
|
+
## Links
|
|
116
|
+
|
|
117
|
+
- Netallion AI Assurance — https://www.netallion.ai
|
|
118
|
+
- VS Code / Cursor / Windsurf extension — [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=netallion.netallion-ai-assurance) · [Open VSX](https://open-vsx.org/extension/netallion/netallion-ai-assurance)
|
|
119
|
+
- Azure Marketplace — [Netallion AI Assurance](https://marketplace.microsoft.com/en-us/product/netallion.netallion-ai-assurance)
|
|
120
|
+
|
|
121
|
+
## License
|
|
122
|
+
|
|
123
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
PRIVACY.md
|
|
4
|
+
README.md
|
|
5
|
+
pyproject.toml
|
|
6
|
+
src/netallion_mcp_lite/__init__.py
|
|
7
|
+
src/netallion_mcp_lite/py.typed
|
|
8
|
+
src/netallion_mcp_lite/redaction.py
|
|
9
|
+
src/netallion_mcp_lite/rules.py
|
|
10
|
+
src/netallion_mcp_lite/scanner.py
|
|
11
|
+
src/netallion_mcp_lite/server.py
|
|
12
|
+
src/netallion_mcp_lite.egg-info/PKG-INFO
|
|
13
|
+
src/netallion_mcp_lite.egg-info/SOURCES.txt
|
|
14
|
+
src/netallion_mcp_lite.egg-info/dependency_links.txt
|
|
15
|
+
src/netallion_mcp_lite.egg-info/entry_points.txt
|
|
16
|
+
src/netallion_mcp_lite.egg-info/requires.txt
|
|
17
|
+
src/netallion_mcp_lite.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
mcp>=1.0.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
netallion_mcp_lite
|