nodeprobe 0.1.3__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.
Files changed (68) hide show
  1. nodeprobe-0.1.3/LICENSE +21 -0
  2. nodeprobe-0.1.3/PKG-INFO +95 -0
  3. nodeprobe-0.1.3/README.md +63 -0
  4. nodeprobe-0.1.3/pyproject.toml +64 -0
  5. nodeprobe-0.1.3/setup.cfg +4 -0
  6. nodeprobe-0.1.3/src/nodeprobe/__init__.py +3 -0
  7. nodeprobe-0.1.3/src/nodeprobe/__main__.py +4 -0
  8. nodeprobe-0.1.3/src/nodeprobe/chains.py +87 -0
  9. nodeprobe-0.1.3/src/nodeprobe/cli.py +440 -0
  10. nodeprobe-0.1.3/src/nodeprobe/contract/__init__.py +18 -0
  11. nodeprobe-0.1.3/src/nodeprobe/contract/address.py +21 -0
  12. nodeprobe-0.1.3/src/nodeprobe/contract/bytecode.py +77 -0
  13. nodeprobe-0.1.3/src/nodeprobe/contract/interfaces.py +72 -0
  14. nodeprobe-0.1.3/src/nodeprobe/contract/proxy.py +104 -0
  15. nodeprobe-0.1.3/src/nodeprobe/contract/sourcify.py +98 -0
  16. nodeprobe-0.1.3/src/nodeprobe/contract_engine.py +485 -0
  17. nodeprobe-0.1.3/src/nodeprobe/data/__init__.py +1 -0
  18. nodeprobe-0.1.3/src/nodeprobe/data/chains_mini.json +1 -0
  19. nodeprobe-0.1.3/src/nodeprobe/engine.py +193 -0
  20. nodeprobe-0.1.3/src/nodeprobe/escalation.py +706 -0
  21. nodeprobe-0.1.3/src/nodeprobe/escalation_contract.py +304 -0
  22. nodeprobe-0.1.3/src/nodeprobe/escalation_web.py +379 -0
  23. nodeprobe-0.1.3/src/nodeprobe/http_client.py +168 -0
  24. nodeprobe-0.1.3/src/nodeprobe/killswitch.py +46 -0
  25. nodeprobe-0.1.3/src/nodeprobe/models.py +114 -0
  26. nodeprobe-0.1.3/src/nodeprobe/multichain/__init__.py +230 -0
  27. nodeprobe-0.1.3/src/nodeprobe/multichain/aptos_engine.py +455 -0
  28. nodeprobe-0.1.3/src/nodeprobe/multichain/common.py +162 -0
  29. nodeprobe-0.1.3/src/nodeprobe/multichain/cosmos_engine.py +348 -0
  30. nodeprobe-0.1.3/src/nodeprobe/multichain/detect.py +110 -0
  31. nodeprobe-0.1.3/src/nodeprobe/multichain/near_engine.py +459 -0
  32. nodeprobe-0.1.3/src/nodeprobe/multichain/solana_engine.py +371 -0
  33. nodeprobe-0.1.3/src/nodeprobe/multichain/starknet_engine.py +418 -0
  34. nodeprobe-0.1.3/src/nodeprobe/multichain/substrate_engine.py +246 -0
  35. nodeprobe-0.1.3/src/nodeprobe/multichain/sui_engine.py +451 -0
  36. nodeprobe-0.1.3/src/nodeprobe/profiles.py +72 -0
  37. nodeprobe-0.1.3/src/nodeprobe/providers.py +41 -0
  38. nodeprobe-0.1.3/src/nodeprobe/report.py +625 -0
  39. nodeprobe-0.1.3/src/nodeprobe/rpc.py +151 -0
  40. nodeprobe-0.1.3/src/nodeprobe/rules/__init__.py +36 -0
  41. nodeprobe-0.1.3/src/nodeprobe/rules/base.py +73 -0
  42. nodeprobe-0.1.3/src/nodeprobe/rules/client.py +187 -0
  43. nodeprobe-0.1.3/src/nodeprobe/rules/identity.py +147 -0
  44. nodeprobe-0.1.3/src/nodeprobe/rules/namespaces.py +336 -0
  45. nodeprobe-0.1.3/src/nodeprobe/rules/tls_http.py +157 -0
  46. nodeprobe-0.1.3/src/nodeprobe/rules/web/__init__.py +23 -0
  47. nodeprobe-0.1.3/src/nodeprobe/rules/web/headers.py +500 -0
  48. nodeprobe-0.1.3/src/nodeprobe/rules/web/tls.py +99 -0
  49. nodeprobe-0.1.3/src/nodeprobe/rules/web/well_known.py +117 -0
  50. nodeprobe-0.1.3/src/nodeprobe/safety.py +133 -0
  51. nodeprobe-0.1.3/src/nodeprobe/scoring.py +42 -0
  52. nodeprobe-0.1.3/src/nodeprobe/web_engine.py +154 -0
  53. nodeprobe-0.1.3/src/nodeprobe.egg-info/PKG-INFO +95 -0
  54. nodeprobe-0.1.3/src/nodeprobe.egg-info/SOURCES.txt +66 -0
  55. nodeprobe-0.1.3/src/nodeprobe.egg-info/dependency_links.txt +1 -0
  56. nodeprobe-0.1.3/src/nodeprobe.egg-info/entry_points.txt +2 -0
  57. nodeprobe-0.1.3/src/nodeprobe.egg-info/requires.txt +5 -0
  58. nodeprobe-0.1.3/src/nodeprobe.egg-info/top_level.txt +1 -0
  59. nodeprobe-0.1.3/tests/test_chainlist_refresh.py +77 -0
  60. nodeprobe-0.1.3/tests/test_contract_escalation.py +151 -0
  61. nodeprobe-0.1.3/tests/test_contract_scanner.py +180 -0
  62. nodeprobe-0.1.3/tests/test_escalation.py +192 -0
  63. nodeprobe-0.1.3/tests/test_header_grading.py +42 -0
  64. nodeprobe-0.1.3/tests/test_header_grading_scan.py +56 -0
  65. nodeprobe-0.1.3/tests/test_multichain.py +576 -0
  66. nodeprobe-0.1.3/tests/test_scanner.py +483 -0
  67. nodeprobe-0.1.3/tests/test_web_escalation.py +88 -0
  68. nodeprobe-0.1.3/tests/test_web_scanner.py +99 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ehsan Hajian
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,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: nodeprobe
3
+ Version: 0.1.3
4
+ Summary: Local CLI security scanner for websites, multi-chain RPC, and EVM smart contracts
5
+ Author: Ehsan Hajian
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ehsanhajian/nodeprobe
8
+ Project-URL: Repository, https://github.com/ehsanhajian/nodeprobe
9
+ Project-URL: Issues, https://github.com/ehsanhajian/nodeprobe/issues
10
+ Project-URL: Releases, https://github.com/ehsanhajian/nodeprobe/releases
11
+ Keywords: security,scanner,cli,rpc,evm,web3,tls
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Topic :: Security
23
+ Classifier: Topic :: System :: Networking
24
+ Requires-Python: >=3.10
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: httpx>=0.27
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8.0; extra == "dev"
30
+ Requires-Dist: pytest-httpx>=0.30; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ # Nodeprobe
34
+
35
+ **Local CLI security scanner for websites, multi-chain RPC nodes, and EVM smart contracts.**
36
+
37
+ Probe HTTP/TLS, JSON-RPC and GraphQL RPC, and contract posture from your terminal. Local-first for infrastructure you operate or are authorized to assess. No account. No cloud. No telemetry.
38
+
39
+ ```bash
40
+ pip install nodeprobe
41
+ nodeprobe web https://example.com
42
+ ```
43
+
44
+ Default profile is **Standard**. Use `--profile Quick` for a fast pass, or `--profile Deep` for a larger budget.
45
+
46
+ ## What it covers
47
+
48
+ | Surface | What Nodeprobe looks at |
49
+ |---|---|
50
+ | **Web** | TLS, security headers (presence + HSTS/CSP policy grading), `security.txt`, `robots.txt`, server disclosure |
51
+ | **RPC** | Protocol **families**: EVM, Solana, Substrate/Polkadot, Cosmos, Aptos, Sui, Starknet, NEAR — auto-detect or `--family` |
52
+ | **Contracts** | EVM code presence, proxies, bytecode heuristics, Sourcify verification (read-only) |
53
+
54
+ EVM networks share one engine. `nodeprobe scan <rpc>` works for any EVM chain. Chain names come from a bundled [Chainlist](https://chainid.network) snapshot; unknown IDs still scan with a generic name.
55
+
56
+ ## Quick start
57
+
58
+ ```bash
59
+ # Website
60
+ nodeprobe web https://example.com
61
+
62
+ # EVM RPC — any EVM chain
63
+ nodeprobe scan https://rpc.example.com
64
+
65
+ # Other protocol families
66
+ nodeprobe rpc https://api.mainnet-beta.solana.com
67
+ nodeprobe substrate https://rpc.polkadot.io
68
+ nodeprobe cosmos https://rpc.cosmos.directory:443
69
+ nodeprobe aptos https://fullnode.mainnet.aptoslabs.com/v1
70
+ nodeprobe sui https://graphql.mainnet.sui.io/graphql
71
+ nodeprobe starknet https://rpc.starknet.lava.build
72
+ nodeprobe near https://rpc.mainnet.near.org
73
+
74
+ # Contract (read-only)
75
+ nodeprobe contract 0x… --rpc https://rpc.example.com --chain 1
76
+ ```
77
+
78
+ Human reports are the default. Use `--html -o report.html` or `--json --pretty` for other formats.
79
+
80
+ ## Safety by design
81
+
82
+ - Blocks SSRF paths: private IPs, localhost, cloud metadata
83
+ - Enforces per-profile request, RPS, and duration budgets
84
+ - Kill switch: touch `/tmp/nodeprobe-kill` or set `NODEPROBE_KILL_SWITCH`
85
+ - Escalation confirms impact — no exploit payloads, no funded transactions
86
+
87
+ **Authorized use only.** Scan systems you own or have permission to assess.
88
+
89
+ ## Docs and source
90
+
91
+ Full examples, screenshots, and development setup: [github.com/ehsanhajian/nodeprobe](https://github.com/ehsanhajian/nodeprobe)
92
+
93
+ ## License
94
+
95
+ [MIT](https://github.com/ehsanhajian/nodeprobe/blob/main/LICENSE)
@@ -0,0 +1,63 @@
1
+ # Nodeprobe
2
+
3
+ **Local CLI security scanner for websites, multi-chain RPC nodes, and EVM smart contracts.**
4
+
5
+ Probe HTTP/TLS, JSON-RPC and GraphQL RPC, and contract posture from your terminal. Local-first for infrastructure you operate or are authorized to assess. No account. No cloud. No telemetry.
6
+
7
+ ```bash
8
+ pip install nodeprobe
9
+ nodeprobe web https://example.com
10
+ ```
11
+
12
+ Default profile is **Standard**. Use `--profile Quick` for a fast pass, or `--profile Deep` for a larger budget.
13
+
14
+ ## What it covers
15
+
16
+ | Surface | What Nodeprobe looks at |
17
+ |---|---|
18
+ | **Web** | TLS, security headers (presence + HSTS/CSP policy grading), `security.txt`, `robots.txt`, server disclosure |
19
+ | **RPC** | Protocol **families**: EVM, Solana, Substrate/Polkadot, Cosmos, Aptos, Sui, Starknet, NEAR — auto-detect or `--family` |
20
+ | **Contracts** | EVM code presence, proxies, bytecode heuristics, Sourcify verification (read-only) |
21
+
22
+ EVM networks share one engine. `nodeprobe scan <rpc>` works for any EVM chain. Chain names come from a bundled [Chainlist](https://chainid.network) snapshot; unknown IDs still scan with a generic name.
23
+
24
+ ## Quick start
25
+
26
+ ```bash
27
+ # Website
28
+ nodeprobe web https://example.com
29
+
30
+ # EVM RPC — any EVM chain
31
+ nodeprobe scan https://rpc.example.com
32
+
33
+ # Other protocol families
34
+ nodeprobe rpc https://api.mainnet-beta.solana.com
35
+ nodeprobe substrate https://rpc.polkadot.io
36
+ nodeprobe cosmos https://rpc.cosmos.directory:443
37
+ nodeprobe aptos https://fullnode.mainnet.aptoslabs.com/v1
38
+ nodeprobe sui https://graphql.mainnet.sui.io/graphql
39
+ nodeprobe starknet https://rpc.starknet.lava.build
40
+ nodeprobe near https://rpc.mainnet.near.org
41
+
42
+ # Contract (read-only)
43
+ nodeprobe contract 0x… --rpc https://rpc.example.com --chain 1
44
+ ```
45
+
46
+ Human reports are the default. Use `--html -o report.html` or `--json --pretty` for other formats.
47
+
48
+ ## Safety by design
49
+
50
+ - Blocks SSRF paths: private IPs, localhost, cloud metadata
51
+ - Enforces per-profile request, RPS, and duration budgets
52
+ - Kill switch: touch `/tmp/nodeprobe-kill` or set `NODEPROBE_KILL_SWITCH`
53
+ - Escalation confirms impact — no exploit payloads, no funded transactions
54
+
55
+ **Authorized use only.** Scan systems you own or have permission to assess.
56
+
57
+ ## Docs and source
58
+
59
+ Full examples, screenshots, and development setup: [github.com/ehsanhajian/nodeprobe](https://github.com/ehsanhajian/nodeprobe)
60
+
61
+ ## License
62
+
63
+ [MIT](https://github.com/ehsanhajian/nodeprobe/blob/main/LICENSE)
@@ -0,0 +1,64 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "nodeprobe"
7
+ version = "0.1.3"
8
+ description = "Local CLI security scanner for websites, multi-chain RPC, and EVM smart contracts"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Ehsan Hajian" }]
14
+ keywords = [
15
+ "security",
16
+ "scanner",
17
+ "cli",
18
+ "rpc",
19
+ "evm",
20
+ "web3",
21
+ "tls",
22
+ ]
23
+ classifiers = [
24
+ "Development Status :: 4 - Beta",
25
+ "Environment :: Console",
26
+ "Intended Audience :: Developers",
27
+ "Intended Audience :: System Administrators",
28
+ "Programming Language :: Python",
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3.10",
31
+ "Programming Language :: Python :: 3.11",
32
+ "Programming Language :: Python :: 3.12",
33
+ "Programming Language :: Python :: 3 :: Only",
34
+ "Topic :: Security",
35
+ "Topic :: System :: Networking",
36
+ ]
37
+ dependencies = [
38
+ "httpx>=0.27",
39
+ ]
40
+
41
+ [project.optional-dependencies]
42
+ dev = [
43
+ "pytest>=8.0",
44
+ "pytest-httpx>=0.30",
45
+ ]
46
+
47
+ [project.urls]
48
+ Homepage = "https://github.com/ehsanhajian/nodeprobe"
49
+ Repository = "https://github.com/ehsanhajian/nodeprobe"
50
+ Issues = "https://github.com/ehsanhajian/nodeprobe/issues"
51
+ Releases = "https://github.com/ehsanhajian/nodeprobe/releases"
52
+
53
+ [project.scripts]
54
+ nodeprobe = "nodeprobe.cli:main"
55
+
56
+ [tool.setuptools.packages.find]
57
+ where = ["src"]
58
+
59
+ [tool.setuptools.package-data]
60
+ nodeprobe = ["data/*.json"]
61
+
62
+ [tool.pytest.ini_options]
63
+ testpaths = ["tests"]
64
+ pythonpath = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Nodeprobe EVM RPC security scanner."""
2
+
3
+ __version__ = "0.1.3"
@@ -0,0 +1,4 @@
1
+ from nodeprobe.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,87 @@
1
+ """EVM chain registry — any chain ID is accepted; names from Chainlist when known."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from dataclasses import dataclass
7
+ from functools import lru_cache
8
+ from importlib import resources
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class ChainInfo:
13
+ chain_id: int
14
+ name: str
15
+ short_name: str
16
+ is_testnet: bool = False
17
+ listed: bool = True
18
+
19
+
20
+ # Backward-compatible alias used by older imports/tests.
21
+ SUPPORTED_CHAINS: dict[int, ChainInfo] = {}
22
+
23
+
24
+ class UnsupportedChainError(ValueError):
25
+ """Deprecated: scans no longer abort on unknown chains.
26
+
27
+ Kept so older callers that catch this exception continue to work.
28
+ """
29
+
30
+ def __init__(self, chain_id: int | None, message: str | None = None):
31
+ self.chain_id = chain_id
32
+ super().__init__(
33
+ message or f"Unsupported or unknown chain ID: {chain_id}."
34
+ )
35
+
36
+
37
+ @lru_cache(maxsize=1)
38
+ def _load_registry() -> dict[int, ChainInfo]:
39
+ text = resources.files("nodeprobe").joinpath("data/chains_mini.json").read_text(
40
+ encoding="utf-8"
41
+ )
42
+ raw = json.loads(text)
43
+ registry: dict[int, ChainInfo] = {}
44
+ for key, value in raw.items():
45
+ chain_id = int(key)
46
+ registry[chain_id] = ChainInfo(
47
+ chain_id=chain_id,
48
+ name=str(value["name"]),
49
+ short_name=str(value.get("short_name") or f"chain-{chain_id}"),
50
+ is_testnet=bool(value.get("is_testnet")),
51
+ listed=True,
52
+ )
53
+ # Populate alias for introspection / tests that inspect the map.
54
+ SUPPORTED_CHAINS.clear()
55
+ SUPPORTED_CHAINS.update(registry)
56
+ return registry
57
+
58
+
59
+ def known_chains() -> dict[int, ChainInfo]:
60
+ return dict(_load_registry())
61
+
62
+
63
+ def resolve_chain(chain_id: int) -> ChainInfo:
64
+ """Resolve any EVM chain ID. Unknown IDs get a generic name and continue scanning."""
65
+ if not isinstance(chain_id, int):
66
+ raise TypeError(f"chain_id must be int, got {type(chain_id)!r}")
67
+ if chain_id < 0:
68
+ raise ValueError(f"chain_id must be non-negative, got {chain_id}")
69
+ info = _load_registry().get(chain_id)
70
+ if info is not None:
71
+ return info
72
+ return ChainInfo(
73
+ chain_id=chain_id,
74
+ name=f"Chain {chain_id}",
75
+ short_name=f"chain-{chain_id}",
76
+ is_testnet=False,
77
+ listed=False,
78
+ )
79
+
80
+
81
+ def parse_hex_or_int(value: str | int) -> int:
82
+ if isinstance(value, int):
83
+ return value
84
+ text = str(value).strip()
85
+ if text.startswith("0x") or text.startswith("0X"):
86
+ return int(text, 16)
87
+ return int(text)