truesignal 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.
- truesignal-0.1.0/.gitignore +8 -0
- truesignal-0.1.0/LICENSE +21 -0
- truesignal-0.1.0/PKG-INFO +179 -0
- truesignal-0.1.0/README.md +144 -0
- truesignal-0.1.0/pyproject.toml +68 -0
- truesignal-0.1.0/src/truesignal/__init__.py +44 -0
- truesignal-0.1.0/src/truesignal/_util.py +28 -0
- truesignal-0.1.0/src/truesignal/cli.py +201 -0
- truesignal-0.1.0/src/truesignal/cli_helpers.py +314 -0
- truesignal-0.1.0/src/truesignal/connectors/__init__.py +48 -0
- truesignal-0.1.0/src/truesignal/connectors/cisa_kev.py +87 -0
- truesignal-0.1.0/src/truesignal/connectors/cloudflare_radar.py +96 -0
- truesignal-0.1.0/src/truesignal/connectors/gdelt.py +106 -0
- truesignal-0.1.0/src/truesignal/connectors/reddit.py +135 -0
- truesignal-0.1.0/src/truesignal/connectors/telegram.py +112 -0
- truesignal-0.1.0/src/truesignal/http.py +96 -0
- truesignal-0.1.0/src/truesignal/provenance/__init__.py +12 -0
- truesignal-0.1.0/src/truesignal/provenance/cache.py +97 -0
- truesignal-0.1.0/src/truesignal/provenance/stamp.py +98 -0
- truesignal-0.1.0/src/truesignal/py.typed +0 -0
- truesignal-0.1.0/src/truesignal/types.py +119 -0
- truesignal-0.1.0/tests/__init__.py +0 -0
- truesignal-0.1.0/tests/conftest.py +31 -0
- truesignal-0.1.0/tests/test_cache.py +75 -0
- truesignal-0.1.0/tests/test_cli.py +132 -0
- truesignal-0.1.0/tests/test_cli_helpers.py +253 -0
- truesignal-0.1.0/tests/test_connectors_cisa_kev.py +98 -0
- truesignal-0.1.0/tests/test_connectors_cloudflare_radar.py +91 -0
- truesignal-0.1.0/tests/test_connectors_gdelt.py +84 -0
- truesignal-0.1.0/tests/test_connectors_reddit.py +124 -0
- truesignal-0.1.0/tests/test_connectors_telegram.py +117 -0
- truesignal-0.1.0/tests/test_no_fabrication.py +275 -0
- truesignal-0.1.0/tests/test_stamp.py +105 -0
truesignal-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rudrendu Paul
|
|
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,179 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: truesignal
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Provenance-first OSINT/security intelligence feed CLI. Every item ships a real source URL, a real timestamp, and an explicit live/fallback flag -- never a fabricated or silently-replayed data point.
|
|
5
|
+
Project-URL: Homepage, https://github.com/RudrenduPaul/truesignal
|
|
6
|
+
Project-URL: Repository, https://github.com/RudrenduPaul/truesignal
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/RudrenduPaul/truesignal/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/RudrenduPaul/truesignal/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Documentation, https://github.com/RudrenduPaul/truesignal/blob/main/docs/getting-started.md
|
|
10
|
+
Project-URL: Author - Rudrendu Paul, https://github.com/RudrenduPaul
|
|
11
|
+
Project-URL: Author - Sourav Nandy, https://github.com/Sourav-nandy-ai
|
|
12
|
+
Author: Rudrendu Paul, Sourav Nandy
|
|
13
|
+
License-Expression: MIT
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Keywords: cisa-kev,cli,cloudflare-radar,cve,gdelt,osint,provenance,security,threat-intelligence
|
|
16
|
+
Classifier: Development Status :: 3 - Alpha
|
|
17
|
+
Classifier: Environment :: Console
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
27
|
+
Classifier: Topic :: Internet
|
|
28
|
+
Classifier: Topic :: Security
|
|
29
|
+
Requires-Python: >=3.9
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: build<2,>=1.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest<9,>=7.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: twine<7,>=5.0; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# truesignal (Python)
|
|
37
|
+
|
|
38
|
+
A provenance-first OSINT/security intelligence feed. Every item ships a real
|
|
39
|
+
source URL, a real upstream timestamp, and an explicit `live`/`fallback`
|
|
40
|
+
flag -- never a fabricated or silently-replayed data point.
|
|
41
|
+
|
|
42
|
+
[](https://pypi.org/project/truesignal/)
|
|
43
|
+
[](https://github.com/RudrenduPaul/truesignal/blob/main/LICENSE)
|
|
44
|
+
[](https://pypi.org/project/truesignal/)
|
|
45
|
+
[](https://github.com/RudrenduPaul/truesignal/actions/workflows/ci.yml)
|
|
46
|
+
|
|
47
|
+
## Why this exists
|
|
48
|
+
|
|
49
|
+
Personal OSINT/security feeds are easy to build and easy to get wrong in one
|
|
50
|
+
specific way: silently showing stale or synthetic data as if it were
|
|
51
|
+
current. TrueSignal's answer is structural, not a documentation promise --
|
|
52
|
+
every connector's failure path is enforced by a dedicated test suite to
|
|
53
|
+
return either a real cached item honestly labeled `fallback` with its real
|
|
54
|
+
age, or nothing at all. This package is the Python distribution -- a
|
|
55
|
+
genuine, independent port of the npm package, not a wrapper around the
|
|
56
|
+
Node binary.
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install truesignal
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
or with [uv](https://docs.astral.sh/uv/):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
uv add truesignal
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
No separate install step, no external binary to fetch. The complementary
|
|
71
|
+
JS/TS distribution installs the same way on the npm side:
|
|
72
|
+
`npm install -g truesignal-cli` (or `npx truesignal-cli init` to run it once
|
|
73
|
+
without installing) -- see the
|
|
74
|
+
[project README](https://github.com/RudrenduPaul/truesignal#readme) for
|
|
75
|
+
that package. Both are first-class, maintained together; neither is
|
|
76
|
+
deprecated in favor of the other.
|
|
77
|
+
|
|
78
|
+
## Quickstart
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
truesignal init
|
|
82
|
+
truesignal feed
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`init` reports which connectors are ready right now -- CISA-KEV and GDELT
|
|
86
|
+
need no configuration -- and which environment variables are still missing
|
|
87
|
+
for the rest (Cloudflare Radar, Reddit, Telegram). `feed` pulls from every
|
|
88
|
+
configured connector.
|
|
89
|
+
|
|
90
|
+
Or call the library directly (the agent-native path):
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from truesignal import ALL_CONNECTORS
|
|
94
|
+
|
|
95
|
+
for connector in ALL_CONNECTORS:
|
|
96
|
+
if connector.requires_config and not connector.is_configured():
|
|
97
|
+
continue
|
|
98
|
+
for item in connector.fetch_items():
|
|
99
|
+
print(item.status, item.source, item.url)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## How it works
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
connector.fetch_items()
|
|
106
|
+
-> fetch_with_fallback(source, fetch_live)
|
|
107
|
+
-> upstream API call succeeds -> stamp_live() -> write_cache() -> real "live" items
|
|
108
|
+
-> upstream API call fails -> read_cache() -> stamp_fallback() -> real "fallback"
|
|
109
|
+
items with an honest fallback_age_seconds, or []
|
|
110
|
+
if nothing has ever been cached
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Every connector implements the same `Connector` interface
|
|
114
|
+
(`truesignal/types.py`) and calls `fetch_with_fallback` instead of touching
|
|
115
|
+
the network directly -- that single function is what gives every connector
|
|
116
|
+
its live/fallback/nothing guarantee. See
|
|
117
|
+
[docs/concepts.md](https://github.com/RudrenduPaul/truesignal/blob/main/docs/concepts.md)
|
|
118
|
+
for the full data model and what each of the five connectors actually
|
|
119
|
+
returns.
|
|
120
|
+
|
|
121
|
+
## No-fabrication guarantee
|
|
122
|
+
|
|
123
|
+
Every connector's live, fallback, and empty-cache-failure path is covered
|
|
124
|
+
by a dedicated pytest suite (`tests/test_no_fabrication.py`), ported
|
|
125
|
+
directly from the TypeScript suite's `no-fabrication.test.ts`: it proves,
|
|
126
|
+
for every connector, that a failed upstream fetch never produces
|
|
127
|
+
synthetic, randomized, or silently-relabeled-as-current data -- only a real
|
|
128
|
+
cached fallback item with an honest age, or nothing. A companion static
|
|
129
|
+
check scans every connector source file in `src/truesignal/connectors/`
|
|
130
|
+
for forbidden patterns (`random.random()`, a fake-data library,
|
|
131
|
+
`datetime.now()` used to construct an item's timestamp).
|
|
132
|
+
|
|
133
|
+
## CLI command reference
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
usage: truesignal [-h] [--version] {init,feed,verify} ...
|
|
137
|
+
|
|
138
|
+
Commands:
|
|
139
|
+
init [--json] Check which connectors are ready right now.
|
|
140
|
+
feed [--source NAME] [--json] Pull the current feed from every configured
|
|
141
|
+
connector, or one connector with --source.
|
|
142
|
+
verify <item-id> [--json] Re-fetch the source named in <item-id> and
|
|
143
|
+
confirm its current provenance.
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Exit codes: `0` success, `1` general error, `2` no connectors configured,
|
|
147
|
+
`3` network error (a configured connector's fetch failed with no fallback
|
|
148
|
+
data), `4` a malformed or unknown `verify` item id -- documented in full in
|
|
149
|
+
[docs/concepts.md](https://github.com/RudrenduPaul/truesignal/blob/main/docs/concepts.md).
|
|
150
|
+
|
|
151
|
+
## Security
|
|
152
|
+
|
|
153
|
+
No connector ever `eval()`s, `exec()`s, or dynamically imports anything
|
|
154
|
+
read from an upstream response -- responses are only ever parsed as JSON
|
|
155
|
+
and mapped into typed fields. Credentials are read only from real
|
|
156
|
+
environment variables (`CLOUDFLARE_RADAR_API_TOKEN`, `REDDIT_CLIENT_ID`,
|
|
157
|
+
`REDDIT_CLIENT_SECRET`, `TELEGRAM_BOT_TOKEN`) -- never accepted as a CLI
|
|
158
|
+
flag, never auto-loaded from a `.env` file. To report a vulnerability, see
|
|
159
|
+
[SECURITY.md](https://github.com/RudrenduPaul/truesignal/blob/main/SECURITY.md).
|
|
160
|
+
**Honest note**: this project does not currently publish SLSA provenance,
|
|
161
|
+
Sigstore signatures, or an SBOM, and has no OpenSSF Scorecard badge set up
|
|
162
|
+
-- none of that infrastructure exists yet for either distribution, so it
|
|
163
|
+
isn't claimed here.
|
|
164
|
+
|
|
165
|
+
## Contributing
|
|
166
|
+
|
|
167
|
+
See [CONTRIBUTING.md](https://github.com/RudrenduPaul/truesignal/blob/main/CONTRIBUTING.md)
|
|
168
|
+
for the full guide, covering both the TypeScript and Python codebases.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
cd python
|
|
172
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
173
|
+
pip install -e ".[dev]"
|
|
174
|
+
pytest
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
MIT, see [LICENSE](https://github.com/RudrenduPaul/truesignal/blob/main/LICENSE).
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# truesignal (Python)
|
|
2
|
+
|
|
3
|
+
A provenance-first OSINT/security intelligence feed. Every item ships a real
|
|
4
|
+
source URL, a real upstream timestamp, and an explicit `live`/`fallback`
|
|
5
|
+
flag -- never a fabricated or silently-replayed data point.
|
|
6
|
+
|
|
7
|
+
[](https://pypi.org/project/truesignal/)
|
|
8
|
+
[](https://github.com/RudrenduPaul/truesignal/blob/main/LICENSE)
|
|
9
|
+
[](https://pypi.org/project/truesignal/)
|
|
10
|
+
[](https://github.com/RudrenduPaul/truesignal/actions/workflows/ci.yml)
|
|
11
|
+
|
|
12
|
+
## Why this exists
|
|
13
|
+
|
|
14
|
+
Personal OSINT/security feeds are easy to build and easy to get wrong in one
|
|
15
|
+
specific way: silently showing stale or synthetic data as if it were
|
|
16
|
+
current. TrueSignal's answer is structural, not a documentation promise --
|
|
17
|
+
every connector's failure path is enforced by a dedicated test suite to
|
|
18
|
+
return either a real cached item honestly labeled `fallback` with its real
|
|
19
|
+
age, or nothing at all. This package is the Python distribution -- a
|
|
20
|
+
genuine, independent port of the npm package, not a wrapper around the
|
|
21
|
+
Node binary.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install truesignal
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
or with [uv](https://docs.astral.sh/uv/):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv add truesignal
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
No separate install step, no external binary to fetch. The complementary
|
|
36
|
+
JS/TS distribution installs the same way on the npm side:
|
|
37
|
+
`npm install -g truesignal-cli` (or `npx truesignal-cli init` to run it once
|
|
38
|
+
without installing) -- see the
|
|
39
|
+
[project README](https://github.com/RudrenduPaul/truesignal#readme) for
|
|
40
|
+
that package. Both are first-class, maintained together; neither is
|
|
41
|
+
deprecated in favor of the other.
|
|
42
|
+
|
|
43
|
+
## Quickstart
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
truesignal init
|
|
47
|
+
truesignal feed
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`init` reports which connectors are ready right now -- CISA-KEV and GDELT
|
|
51
|
+
need no configuration -- and which environment variables are still missing
|
|
52
|
+
for the rest (Cloudflare Radar, Reddit, Telegram). `feed` pulls from every
|
|
53
|
+
configured connector.
|
|
54
|
+
|
|
55
|
+
Or call the library directly (the agent-native path):
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from truesignal import ALL_CONNECTORS
|
|
59
|
+
|
|
60
|
+
for connector in ALL_CONNECTORS:
|
|
61
|
+
if connector.requires_config and not connector.is_configured():
|
|
62
|
+
continue
|
|
63
|
+
for item in connector.fetch_items():
|
|
64
|
+
print(item.status, item.source, item.url)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## How it works
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
connector.fetch_items()
|
|
71
|
+
-> fetch_with_fallback(source, fetch_live)
|
|
72
|
+
-> upstream API call succeeds -> stamp_live() -> write_cache() -> real "live" items
|
|
73
|
+
-> upstream API call fails -> read_cache() -> stamp_fallback() -> real "fallback"
|
|
74
|
+
items with an honest fallback_age_seconds, or []
|
|
75
|
+
if nothing has ever been cached
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Every connector implements the same `Connector` interface
|
|
79
|
+
(`truesignal/types.py`) and calls `fetch_with_fallback` instead of touching
|
|
80
|
+
the network directly -- that single function is what gives every connector
|
|
81
|
+
its live/fallback/nothing guarantee. See
|
|
82
|
+
[docs/concepts.md](https://github.com/RudrenduPaul/truesignal/blob/main/docs/concepts.md)
|
|
83
|
+
for the full data model and what each of the five connectors actually
|
|
84
|
+
returns.
|
|
85
|
+
|
|
86
|
+
## No-fabrication guarantee
|
|
87
|
+
|
|
88
|
+
Every connector's live, fallback, and empty-cache-failure path is covered
|
|
89
|
+
by a dedicated pytest suite (`tests/test_no_fabrication.py`), ported
|
|
90
|
+
directly from the TypeScript suite's `no-fabrication.test.ts`: it proves,
|
|
91
|
+
for every connector, that a failed upstream fetch never produces
|
|
92
|
+
synthetic, randomized, or silently-relabeled-as-current data -- only a real
|
|
93
|
+
cached fallback item with an honest age, or nothing. A companion static
|
|
94
|
+
check scans every connector source file in `src/truesignal/connectors/`
|
|
95
|
+
for forbidden patterns (`random.random()`, a fake-data library,
|
|
96
|
+
`datetime.now()` used to construct an item's timestamp).
|
|
97
|
+
|
|
98
|
+
## CLI command reference
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
usage: truesignal [-h] [--version] {init,feed,verify} ...
|
|
102
|
+
|
|
103
|
+
Commands:
|
|
104
|
+
init [--json] Check which connectors are ready right now.
|
|
105
|
+
feed [--source NAME] [--json] Pull the current feed from every configured
|
|
106
|
+
connector, or one connector with --source.
|
|
107
|
+
verify <item-id> [--json] Re-fetch the source named in <item-id> and
|
|
108
|
+
confirm its current provenance.
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Exit codes: `0` success, `1` general error, `2` no connectors configured,
|
|
112
|
+
`3` network error (a configured connector's fetch failed with no fallback
|
|
113
|
+
data), `4` a malformed or unknown `verify` item id -- documented in full in
|
|
114
|
+
[docs/concepts.md](https://github.com/RudrenduPaul/truesignal/blob/main/docs/concepts.md).
|
|
115
|
+
|
|
116
|
+
## Security
|
|
117
|
+
|
|
118
|
+
No connector ever `eval()`s, `exec()`s, or dynamically imports anything
|
|
119
|
+
read from an upstream response -- responses are only ever parsed as JSON
|
|
120
|
+
and mapped into typed fields. Credentials are read only from real
|
|
121
|
+
environment variables (`CLOUDFLARE_RADAR_API_TOKEN`, `REDDIT_CLIENT_ID`,
|
|
122
|
+
`REDDIT_CLIENT_SECRET`, `TELEGRAM_BOT_TOKEN`) -- never accepted as a CLI
|
|
123
|
+
flag, never auto-loaded from a `.env` file. To report a vulnerability, see
|
|
124
|
+
[SECURITY.md](https://github.com/RudrenduPaul/truesignal/blob/main/SECURITY.md).
|
|
125
|
+
**Honest note**: this project does not currently publish SLSA provenance,
|
|
126
|
+
Sigstore signatures, or an SBOM, and has no OpenSSF Scorecard badge set up
|
|
127
|
+
-- none of that infrastructure exists yet for either distribution, so it
|
|
128
|
+
isn't claimed here.
|
|
129
|
+
|
|
130
|
+
## Contributing
|
|
131
|
+
|
|
132
|
+
See [CONTRIBUTING.md](https://github.com/RudrenduPaul/truesignal/blob/main/CONTRIBUTING.md)
|
|
133
|
+
for the full guide, covering both the TypeScript and Python codebases.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
cd python
|
|
137
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
138
|
+
pip install -e ".[dev]"
|
|
139
|
+
pytest
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## License
|
|
143
|
+
|
|
144
|
+
MIT, see [LICENSE](https://github.com/RudrenduPaul/truesignal/blob/main/LICENSE).
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "truesignal"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Provenance-first OSINT/security intelligence feed CLI. Every item ships a real source URL, a real timestamp, and an explicit live/fallback flag -- never a fabricated or silently-replayed data point."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Rudrendu Paul" },
|
|
14
|
+
{ name = "Sourav Nandy" },
|
|
15
|
+
]
|
|
16
|
+
keywords = ["osint", "security", "threat-intelligence", "cisa-kev", "cve", "cli", "provenance", "cloudflare-radar", "gdelt"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Environment :: Console",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.9",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Topic :: Security",
|
|
30
|
+
"Topic :: Internet",
|
|
31
|
+
]
|
|
32
|
+
dependencies = []
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = [
|
|
36
|
+
"pytest>=7.0,<9",
|
|
37
|
+
"build>=1.0,<2",
|
|
38
|
+
"twine>=5.0,<7",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://github.com/RudrenduPaul/truesignal"
|
|
43
|
+
Repository = "https://github.com/RudrenduPaul/truesignal"
|
|
44
|
+
"Bug Tracker" = "https://github.com/RudrenduPaul/truesignal/issues"
|
|
45
|
+
Changelog = "https://github.com/RudrenduPaul/truesignal/blob/main/CHANGELOG.md"
|
|
46
|
+
Documentation = "https://github.com/RudrenduPaul/truesignal/blob/main/docs/getting-started.md"
|
|
47
|
+
"Author - Rudrendu Paul" = "https://github.com/RudrenduPaul"
|
|
48
|
+
"Author - Sourav Nandy" = "https://github.com/Sourav-nandy-ai"
|
|
49
|
+
|
|
50
|
+
[project.scripts]
|
|
51
|
+
truesignal = "truesignal.cli:main"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["src/truesignal"]
|
|
55
|
+
|
|
56
|
+
[tool.hatch.build.targets.sdist]
|
|
57
|
+
include = [
|
|
58
|
+
"src/truesignal",
|
|
59
|
+
"tests",
|
|
60
|
+
"README.md",
|
|
61
|
+
"LICENSE",
|
|
62
|
+
]
|
|
63
|
+
exclude = [
|
|
64
|
+
"examples",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Library entry point -- import truesignal's connectors and provenance layer programmatically.
|
|
3
|
+
|
|
4
|
+
from truesignal import ALL_CONNECTORS, get_connector
|
|
5
|
+
|
|
6
|
+
for connector in ALL_CONNECTORS:
|
|
7
|
+
for item in connector.fetch_items():
|
|
8
|
+
print(item.status, item.url)
|
|
9
|
+
|
|
10
|
+
This is the Python port of the truesignal-cli npm package
|
|
11
|
+
(https://www.npmjs.com/package/truesignal-cli). Both distributions ship the same five source
|
|
12
|
+
connectors (CISA-KEV, Cloudflare Radar, Reddit, Telegram, GDELT) and the same no-fabrication
|
|
13
|
+
provenance guarantee; see https://github.com/RudrenduPaul/truesignal for the canonical
|
|
14
|
+
documentation and the original TypeScript source.
|
|
15
|
+
"""
|
|
16
|
+
from .connectors import ALL_CONNECTORS, get_connector
|
|
17
|
+
from .provenance import (
|
|
18
|
+
fetch_with_fallback,
|
|
19
|
+
get_cache_dir,
|
|
20
|
+
read_cache,
|
|
21
|
+
stamp_fallback,
|
|
22
|
+
stamp_live,
|
|
23
|
+
write_cache,
|
|
24
|
+
)
|
|
25
|
+
from .types import Connector, ConnectorNotConfiguredError, FeedItem, ItemStatus, UnstampedItem
|
|
26
|
+
|
|
27
|
+
__version__ = "0.1.0"
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"Connector",
|
|
31
|
+
"FeedItem",
|
|
32
|
+
"ItemStatus",
|
|
33
|
+
"UnstampedItem",
|
|
34
|
+
"ConnectorNotConfiguredError",
|
|
35
|
+
"ALL_CONNECTORS",
|
|
36
|
+
"get_connector",
|
|
37
|
+
"fetch_with_fallback",
|
|
38
|
+
"stamp_live",
|
|
39
|
+
"stamp_fallback",
|
|
40
|
+
"read_cache",
|
|
41
|
+
"write_cache",
|
|
42
|
+
"get_cache_dir",
|
|
43
|
+
"__version__",
|
|
44
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Small internal helpers shared across connectors and the provenance layer. Not part of the
|
|
2
|
+
public API (hence the leading underscore) -- see truesignal/__init__.py for the public surface."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def format_iso8601(dt: datetime) -> str:
|
|
9
|
+
"""
|
|
10
|
+
Formats a datetime as a JavaScript-``Date.prototype.toISOString()``-equivalent string:
|
|
11
|
+
always UTC, always exactly 3 millisecond digits, always a trailing "Z". Naive datetimes are
|
|
12
|
+
assumed to already be UTC (every caller in this codebase constructs them that way).
|
|
13
|
+
"""
|
|
14
|
+
if dt.tzinfo is None:
|
|
15
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
16
|
+
dt = dt.astimezone(timezone.utc)
|
|
17
|
+
milliseconds = dt.microsecond // 1000
|
|
18
|
+
return dt.strftime("%Y-%m-%dT%H:%M:%S") + f".{milliseconds:03d}Z"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def parse_iso8601(value: str) -> datetime:
|
|
22
|
+
"""Parses a real upstream ISO-8601 timestamp (with or without a trailing 'Z') into an aware
|
|
23
|
+
UTC datetime. Raises ValueError on anything that isn't a real, parseable instant."""
|
|
24
|
+
normalized = value[:-1] + "+00:00" if value.endswith("Z") else value
|
|
25
|
+
dt = datetime.fromisoformat(normalized)
|
|
26
|
+
if dt.tzinfo is None:
|
|
27
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
28
|
+
return dt.astimezone(timezone.utc)
|