ping-shield 1.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.
@@ -0,0 +1,17 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ build/
5
+ dist/
6
+ .venv/
7
+ venv/
8
+ .pytest_cache/
9
+ .mypy_cache/
10
+ .ruff_cache/
11
+ # Never commit credentials
12
+ .pypirc
13
+ config.json
14
+ state.json
15
+
16
+ # Local assistant notes
17
+ CLAUDE.md
@@ -0,0 +1,60 @@
1
+ # Changelog
2
+
3
+ All notable changes to ping-shield are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
6
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.1.0] - 2026-10-31
11
+
12
+ First public release on PyPI.
13
+
14
+ ### Added
15
+
16
+ - Real-time detection: an inotify watcher inside the bot service reports changes to SSH files,
17
+ shell rc files, systemd user units, autostart entries, `environment.d` and extension folders
18
+ about 2 seconds after they happen. Adds `--watch` and the `realtime` config option.
19
+ - Interactive Telegram bot (`--bot`, `ping-shield-bot.service`):
20
+ - alert buttons: ✅ It was me, 🚨 Not me (sends a response checklist), 🔕 Mute keyword;
21
+ - commands: `/status`, `/scan`, `/sources`, `/catalog`, `/addsource`, `/removesource`,
22
+ `/mute`, `/unmute`, `/help`;
23
+ - live progress messages for slow actions;
24
+ - serves only the configured chat.
25
+ - News source management: `--sources`, `--add-source`, `--remove-source`, and a catalog of 17
26
+ sources (security news, official advisories, distro advisories, Telegram channels).
27
+ - Public Telegram channels as sources, read from their `t.me/s/` web preview.
28
+ - Deduplication of stories across sources by article link.
29
+ - `muted_keywords` config option.
30
+
31
+ ### Changed
32
+
33
+ - Alerts are rendered as Telegram HTML instead of MarkdownV2. They gain severity levels, a
34
+ summary of changes, local timestamps, hashtags, a collapsible article summary and inline
35
+ buttons for the article, the Telegram post and NVD entries.
36
+ - The package moved from `src/ping_shield/` to `ping_shield/`.
37
+
38
+ ### Fixed
39
+
40
+ - The crontab check failed under systemd: `NoNewPrivileges=yes` blocked the setuid `crontab`
41
+ binary.
42
+ - `--setup-service` no longer reports its own unit files as a persistence change.
43
+
44
+ ## [1.0.0] - 2026-10-31
45
+
46
+ Initial version (not published to PyPI).
47
+
48
+ ### Added
49
+
50
+ - Monitoring of SSH trust files, the user crontab, systemd user units, autostart, `environment.d`,
51
+ shell rc files and VS Code / Cursor / VSCodium / Windsurf extensions, with SHA-256 baselines and
52
+ exactly-once alerting.
53
+ - Threat-intel scraping of RSS/Atom feeds, filtered by severity and auto-detected environment
54
+ keywords, with deduplication by GUID, title and CVE id.
55
+ - `--init`, `--test`, `--setup-service`, `--remove-service`, `--status` and `--rebaseline`.
56
+ - Secret redaction in diffs sent to Telegram.
57
+
58
+ [Unreleased]: https://github.com/Yosinan/ping-shield/compare/v1.1.0...HEAD
59
+ [1.1.0]: https://github.com/Yosinan/ping-shield/releases/tag/v1.1.0
60
+ [1.0.0]: https://github.com/Yosinan/ping-shield/commit/798093a
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yoseph Zewdu
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,458 @@
1
+ Metadata-Version: 2.5
2
+ Name: ping-shield
3
+ Version: 1.1.0
4
+ Summary: Real-time tripwire for Linux workstations: catches SSH backdoors, persistence and tampered IDE extensions, tracks threat intel from RSS feeds and Telegram channels, and alerts you on Telegram.
5
+ Project-URL: Homepage, https://github.com/Yosinan/ping-shield
6
+ Project-URL: Repository, https://github.com/Yosinan/ping-shield.git
7
+ Project-URL: Issues, https://github.com/Yosinan/ping-shield/issues
8
+ Project-URL: Changelog, https://github.com/Yosinan/ping-shield/blob/main/CHANGELOG.md
9
+ Author-email: Yoseph Zewdu <ymymym4634@gmail.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: backdoor,cursor,inotify,intrusion-detection,monitoring,persistence,realtime,security,ssh,supply-chain,systemd,telegram,threat-intelligence,vscode
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: System Administrators
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.9
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Topic :: Security
26
+ Classifier: Topic :: System :: Monitoring
27
+ Requires-Python: >=3.9
28
+ Description-Content-Type: text/markdown
29
+
30
+ # ping-shield
31
+
32
+ [![PyPI](https://img.shields.io/pypi/v/ping-shield)](https://pypi.org/project/ping-shield/)
33
+ [![Python](https://img.shields.io/pypi/pyversions/ping-shield)](https://pypi.org/project/ping-shield/)
34
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/Yosinan/ping-shield/blob/main/LICENSE)
35
+
36
+ **A real-time tripwire for Linux workstations.** ping-shield watches the places attackers use to keep
37
+ access to your machine: SSH keys, crontabs, systemd user services, autostart entries, shell start-up
38
+ files and VS Code / Cursor extensions. When one of them changes, your phone gets a Telegram alert
39
+ within seconds, with the exact diff. It also follows security news and official advisories (RSS feeds
40
+ and public Telegram channels) and alerts you only about serious threats to software actually
41
+ installed on your machine. Every finding is reported **exactly once**, and you can act on it with
42
+ buttons in Telegram.
43
+
44
+ - **Zero runtime dependencies.** It uses only the Python standard library, so a supply-chain security
45
+ tool doesn't widen its own supply chain.
46
+ - **Real-time.** Changes to SSH keys, auto-start entries and extensions are reported about 2 seconds
47
+ after they happen, with a full scan every 15 minutes as a safety net.
48
+ - **No root required.** Everything runs as your user, configured under `~/.config/ping-shield/`.
49
+ - **Works on any systemd distribution:** Fedora, Ubuntu, Debian, Arch, openSUSE and others. Python 3.9+.
50
+
51
+ ```text
52
+ 🚨 SSH Trust Change
53
+ ~/.ssh/authorized_keys
54
+
55
+ 🖥 Host: laptop
56
+ 🕒 Detected: 31 Oct 2026, 14:05 EAT
57
+ 📊 Changes: 🟢 1 added
58
+
59
+ --- a/~/.ssh/authorized_keys
60
+ +++ b/~/.ssh/authorized_keys
61
+ @@ -1 +1,2 @@
62
+ ssh-ed25519 AAAAC3Nza…Original user@laptop
63
+ +ssh-rsa AAAAB3Nza…EVIL attacker@evil
64
+
65
+ ⚠️ Not you? Investigate immediately: this file controls who can log in to your account over SSH.
66
+
67
+ #ssh #laptop · 🛡 ping-shield
68
+ ```
69
+
70
+ ```text
71
+ 🔴 Critical Threat
72
+
73
+ Official AI Extension on Cursor Marketplace Trojanized with Persistent SSH Backdoor
74
+ ▌ Researchers found a trojanized build of a popular extension… (tap to expand)
75
+
76
+ 🎯 Affects you: Cursor · SSH
77
+ 🏷 Signals: backdoor · CVSS 9.6
78
+ 🔖 CVE: CVE-2026-40123
79
+ 🗞 Source: The Hacker News
80
+ 📅 Published: 31 Oct 2026, 13:42 EAT
81
+
82
+ #threat_intel #cursor · 🛡 ping-shield
83
+ [ 📖 Read full article ]
84
+ [ 🔎 CVE-2026-40123 on NVD ]
85
+ ```
86
+
87
+ Messages use Telegram HTML: diffs appear in syntax-highlighted code blocks, article summaries appear
88
+ in collapsible quotes, and links are inline buttons. Hashtags let you filter alerts by category or host.
89
+
90
+ ---
91
+
92
+ ## Quick start
93
+
94
+ ```bash
95
+ pipx install ping-shield # or: python3 -m pip install --user ping-shield
96
+
97
+ ping-shield --init # paste your Telegram bot token + chat id
98
+ ping-shield --test # end-to-end self-test: two alerts arrive on your phone
99
+ ping-shield --setup-service # scan every 15 minutes + start the interactive bot
100
+ ping-shield --add-source t.me/thehackernews # optional: more sources (see below)
101
+ ```
102
+
103
+ To create a Telegram bot, message [@BotFather](https://t.me/BotFather), send `/newbot`, and copy the
104
+ token. Then open a chat with your new bot and press **Start**. `--init` can find your chat id
105
+ automatically if you leave that prompt blank.
106
+
107
+ ## Commands
108
+
109
+ | Command | What it does |
110
+ |---|---|
111
+ | `ping-shield` / `--run` | Run one scan now (this is what the timer runs). `--only monitor` or `--only intel` limits the scan to one engine. |
112
+ | `--init` | Interactive setup. The token is entered hidden, checked with Telegram, and saved with mode `0600`. |
113
+ | `--test [-y]` | Automated simulation of the full alerting pipeline (see [Self-test](#self-test)). |
114
+ | `--setup-service` | Generates `~/.config/systemd/user/ping-shield.{service,timer}` and `ping-shield-bot.service`, then enables and starts them. Re-run it after upgrading. |
115
+ | `--remove-service` | Stops and deletes the units. |
116
+ | `--sources` | Lists your subscribed sources and the catalog of official sources you can add. |
117
+ | `--add-source SOURCE…` | Subscribes to an RSS/Atom URL, a public Telegram channel (`t.me/name` or `@name`) or a catalog id. |
118
+ | `--remove-source SOURCE…` | Unsubscribes by list number, catalog id, channel or URL. |
119
+ | `--bot` | Runs the interactive Telegram bot and real-time watcher in the foreground. The systemd service normally does this for you. |
120
+ | `--watch` | Runs only the real-time file watcher in the foreground (for systems without the bot service). |
121
+ | `--status` | Shows the config, which targets have a baseline, the detected keywords and the timer state. |
122
+ | `--rebaseline` | Accepts the current state of all monitored files as trusted, without sending alerts. |
123
+ | `-v` / `-vv` | More detailed logging (written to stderr, which goes to the journal under systemd). |
124
+
125
+ Logs: `journalctl --user -u ping-shield.service -u ping-shield-bot.service -f`
126
+
127
+ ## What is monitored
128
+
129
+ | Target id | Location | Why it matters |
130
+ |---|---|---|
131
+ | `ssh.authorized_keys`, `ssh.authorized_keys2` | `~/.ssh/` | A key added here gives someone a login to your machine. ping-shield also flags group- or world-writable files and files owned by another user. |
132
+ | `ssh.rc`, `ssh.environment` | `~/.ssh/` | Code that runs on every SSH login. |
133
+ | `cron.user` | `crontab -l` | The classic persistence mechanism. |
134
+ | `systemd.user` | `~/.config/systemd/user/` | User services and timers that survive reboots. |
135
+ | `autostart` | `~/.config/autostart/` | Programs launched when your desktop session starts. |
136
+ | `environment.d` | `~/.config/environment.d/` | A place to inject `LD_PRELOAD` or a malicious `PATH`. |
137
+ | `shell.*` | `~/.bashrc`, `~/.bashrc.d/`, `~/.profile`, `~/.zshrc`, `config.fish`, … | Code that runs in every new shell. |
138
+ | `ext.vscode`, `ext.cursor`, `ext.vscode-oss`, `ext.windsurf` | `~/.<ide>/extensions/` | Reports installs, removals and updates. It also hashes each extension's `package.json` and entry-point script, so **code modified in place without a version change** is reported as possible tampering. |
139
+
140
+ **Diffs are redacted before they are sent.** Values that look like credentials are replaced with
141
+ `«redacted»` before anything leaves the machine. This covers `export API_KEY=…`, `password: …`,
142
+ `https://user:pass@host`, and GitHub, GitLab, OpenAI, Slack, AWS, Google and Telegram tokens.
143
+
144
+ ## Threat intelligence
145
+
146
+ The default sources are The Hacker News, BleepingComputer and CISA advisories. An item triggers an
147
+ alert only if it matches **both**:
148
+
149
+ 1. a **severity** term, such as `actively exploited`, `zero-day`, `backdoor`, `supply chain`,
150
+ `remote code execution`, `malicious package`, or a CVSS score of 9.0 or higher; **and**
151
+ 2. an **environment** keyword describing *this* machine. These are detected automatically from
152
+ `/etc/os-release` and from the tools on your `PATH`: your distro, OpenSSH, sudo, systemd, Python,
153
+ PyPI, npm, Docker, Podman, VS Code, Cursor, xz/liblzma, and so on. Run `ping-shield --status` to
154
+ see the detected list.
155
+
156
+ ### Adding sources: official feeds and Telegram channels
157
+
158
+ ```bash
159
+ ping-shield --sources # what you have + the catalog (★ = recommended)
160
+ ping-shield --add-source t.me/thehackernews # any public Telegram channel
161
+ ping-shield --add-source fedora-security oss-security # catalog ids
162
+ ping-shield --add-source https://example.org/feed.xml # any RSS/Atom feed
163
+ ping-shield --remove-source 4
164
+ ```
165
+
166
+ You can do the same from Telegram with `/catalog` (tap ➕ to subscribe), `/addsource`, `/sources` and
167
+ `/removesource`.
168
+
169
+ | Group | Catalog ids |
170
+ |---|---|
171
+ | 📰 Security news | `thn`, `bleepingcomputer`, `krebs`, `darkreading` |
172
+ | 🏛 Official advisories | `cisa`, `cisa-news`, `oss-security`, `github-security`, `pypi`, `socket` |
173
+ | 🐧 Distribution advisories | `ubuntu-usn`, `debian-dsa`, `fedora-security`, `arch-asa` (the one for your distro is ★ recommended) |
174
+ | ✈️ Telegram channels | `tg-thehackernews`, `tg-bleepingcomputer`, `tg-cvenotify` |
175
+
176
+ Every new source is fetched once before it's added, to prove it works. Its existing posts are marked
177
+ as already seen, so you are alerted only about posts published after you subscribed.
178
+
179
+ **How Telegram channels work:** Telegram bots can't read channels they don't administer, so
180
+ ping-shield reads a public channel's web preview (`https://t.me/s/<channel>`). This needs no login, API
181
+ key or extra dependency. **Private channels can't be read this way.** When a channel post links to an
182
+ article that also arrives through RSS, both are recognised as the same story by the article link,
183
+ title and CVE id, so you get one alert.
184
+
185
+ ## How detection works
186
+
187
+ ping-shield detects changes in two ways. Both go through the same baseline and exactly-once alerting:
188
+
189
+ | | Real-time watcher | Scheduled scan |
190
+ |---|---|---|
191
+ | **Runs** | Continuously, inside `ping-shield-bot.service` | Every 15 minutes (`ping-shield.timer`), and on `/scan` |
192
+ | **Speed** | Alerts about 2 s after a change | Up to 15 min after a change |
193
+ | **Covers** | SSH files, shell rc files, systemd user units, autostart, `environment.d`, extensions being installed or removed | Everything, including your crontab and code modified deep inside installed extensions |
194
+ | **Cost** | No CPU while idle. Linux (inotify) wakes it only when a watched directory changes. | A few seconds of work per run |
195
+
196
+ The watcher watches the *directories* that contain monitored files. That way it also catches
197
+ editors and attackers that replace a file through a rename. It ignores unrelated files such as
198
+ `.bash_history`, waits for a burst of writes to settle, and then scans only the affected targets.
199
+ Real-time alerts carry a "⚡ Caught: in real time" line. Your crontab can't be watched in real time,
200
+ because it lives in a spool directory only root can read, so the 15-minute scan covers it.
201
+
202
+ ping-shield is a tripwire for the ways attackers keep access to a machine. It isn't an antivirus: it
203
+ doesn't scan every file for malware, and it doesn't watch processes or network traffic.
204
+
205
+ To turn real-time watching off, set `"realtime": false` in `config.json` and run
206
+ `systemctl --user restart ping-shield-bot`.
207
+
208
+ ## Interactive bot
209
+
210
+ `--setup-service` also starts `ping-shield-bot.service`, which listens to your bot. When it's
211
+ running, alerts get action buttons:
212
+
213
+ | Button | What it does |
214
+ |---|---|
215
+ | ✅ **It was me** | Marks the change as expected. The button turns into "Confirmed by &lt;name&gt; · 14:20". |
216
+ | 🚨 **Not me** | Flags the alert and replies with a step-by-step response checklist for SSH, persistence or extension incidents. |
217
+ | 🔕 **Mute "&lt;keyword&gt;"** | Stops news that only matches that keyword (for example, an over-broad `Linux`). Undo with `/unmute`. |
218
+ | 📖 / 💬 / 🔎 | Opens the article, the original Telegram post, or the CVE on NVD. |
219
+
220
+ Chat commands (shown in Telegram's menu): `/status`, `/scan`, `/sources`, `/catalog`, `/addsource`,
221
+ `/removesource`, `/mute`, `/unmute`, `/help`.
222
+
223
+ The bot serves **only the chat id in your config**. Anyone else who finds it is refused, so nobody else
224
+ can run scans, change your sources or press your buttons. If the bot service isn't running, alerts are
225
+ sent without the action buttons, so you never see buttons that do nothing.
226
+
227
+ ## Anti-spam guarantees
228
+
229
+ Everything that has already been seen is cached in `~/.config/ping-shield/state.json` (mode `0600`).
230
+
231
+ - **Monitor:** the first scan records a SHA-256 baseline and sends no alerts. After that, a target
232
+ alerts only when its fingerprint changes. The baseline moves forward **only after Telegram confirms
233
+ delivery**, so if the network is down the alert is retried on the next run. Once an alert is
234
+ delivered, it is never sent again.
235
+ - **Intel:** news items are remembered by GUID. When several outlets or channels carry the same
236
+ story, the copies are matched by normalised title, **article link and CVE id**, so one threat
237
+ produces one alert.
238
+ Conditional GETs (ETag and `Last-Modified`) avoid downloading feeds that haven't changed. At most
239
+ 8 news alerts are sent per run; any extra alerts are deferred to the next run, not dropped.
240
+ - An exclusive lock (`run.lock`) prevents overlapping runs.
241
+
242
+ ## Self-test
243
+
244
+ `ping-shield --test` runs the real detection and alerting code against your real Telegram chat:
245
+
246
+ | Test | What happens |
247
+ |---|---|
248
+ | 0: Telegram | Validates the bot token with `getMe` **before** anything on disk is touched. |
249
+ | 1: SSH monitoring | Appends `# test-alert-trigger-svc-telemetry` to your real `~/.ssh/authorized_keys`, runs the monitor, and checks that an alert with a `diff` code block was delivered. It then checks that a second scan stays silent, and **restores the file byte for byte**. |
250
+ | 2: Threat intel | Injects a simulated "critical zero-day" item that names software found on this machine (for example `Fedora`). It checks that the item is broadcast, that re-processing it is suppressed, and that a harmless control item is filtered out. |
251
+
252
+ The SSH test is safe for these reasons:
253
+
254
+ - `sshd` ignores lines starting with `#`, so the marker grants no access.
255
+ - The file is rewritten in place, so its inode, permissions, owner and SELinux label (`ssh_home_t` on
256
+ Fedora) are preserved.
257
+ - Cleanup runs in a `finally` block, which also runs on Ctrl-C, SIGTERM and SIGHUP.
258
+ - If anything else edits the file during the test, only the marker line is removed.
259
+ - If you have no `~/.ssh/authorized_keys`, a temporary one is created and then deleted.
260
+ - The test refuses to run if the file is a symlink.
261
+ - The simulations use an in-memory state store and hold the run lock. Your real baselines are never
262
+ touched, and a scheduled scan can never see the marker.
263
+
264
+ When stdin is not a terminal, the test refuses to run unless you pass `--yes`.
265
+
266
+ ## Configuration
267
+
268
+ `~/.config/ping-shield/config.json` (`$XDG_CONFIG_HOME` is honoured). Every field is optional except
269
+ the two Telegram fields:
270
+
271
+ ```json
272
+ {
273
+ "telegram_bot_token": "123456789:AA…",
274
+ "telegram_chat_id": "123456789",
275
+ "feeds": [
276
+ "https://feeds.feedburner.com/TheHackersNews",
277
+ "https://www.bleepingcomputer.com/feed/",
278
+ "https://www.cisa.gov/cybersecurity-advisories/cybersecurity-advisories.xml"
279
+ ],
280
+ "keywords": [],
281
+ "extra_keywords": ["PostgreSQL", "Tailscale"],
282
+ "severity_keywords": ["critical", "zero-day", "actively exploited", "backdoor", "…"],
283
+ "extension_dirs": ["~/.vscode/extensions", "~/.cursor/extensions"],
284
+ "disabled_checks": ["shell.zshrc", "ext."],
285
+ "muted_keywords": ["Linux"],
286
+ "news_max_age_days": 3,
287
+ "max_news_alerts_per_run": 8,
288
+ "request_timeout": 20,
289
+ "realtime": true
290
+ }
291
+ ```
292
+
293
+ - `keywords`: leave it empty to auto-detect, or set it to replace detection entirely.
294
+ `extra_keywords` is always added to whichever list is used.
295
+ - `disabled_checks`: target ids, or prefixes ending in `.` (for example `ext.` disables all
296
+ extension checks).
297
+ - Environment overrides for headless or CI use: `PING_SHIELD_TELEGRAM_BOT_TOKEN` and
298
+ `PING_SHIELD_TELEGRAM_CHAT_ID`.
299
+ Note that the systemd timer does **not** read `~/.bashrc`, so variables exported there only affect
300
+ manual runs. To give the scheduled service the variables, add a drop-in, which survives
301
+ re-running `--setup-service`:
302
+
303
+ ```bash
304
+ systemctl --user edit ping-shield.service
305
+ # in the editor that opens:
306
+ [Service]
307
+ Environment=PING_SHIELD_TELEGRAM_BOT_TOKEN=123456789:AA…
308
+ Environment=PING_SHIELD_TELEGRAM_CHAT_ID=123456789
309
+ ```
310
+
311
+ Drop-in files are created world-readable, so `--init` (mode `0600`) is the safer place for the token.
312
+ - `PING_SHIELD_TELEGRAM_API_BASE` points the client at a self-hosted Bot API server. It must use
313
+ `https://` (`http://` is allowed only for loopback addresses).
314
+
315
+ When you legitimately change a monitored file (for example you install a new extension or edit
316
+ `.bashrc`), you will get one alert. That is the intended behaviour. To reset all baselines silently,
317
+ run `ping-shield --rebaseline`.
318
+
319
+ ### Running while logged out
320
+
321
+ User timers run only while you are logged in. To scan around the clock:
322
+
323
+ ```bash
324
+ sudo loginctl enable-linger "$USER"
325
+ ```
326
+
327
+ ### Uninstall
328
+
329
+ ```bash
330
+ ping-shield --remove-service
331
+ pipx uninstall ping-shield
332
+ rm -rf ~/.config/ping-shield
333
+ ```
334
+
335
+ ---
336
+
337
+ ## Development, build and publishing
338
+
339
+ ### 1. Clone and set up a development environment
340
+
341
+ ```bash
342
+ git clone https://github.com/Yosinan/ping-shield.git
343
+ cd ping-shield
344
+ python3 -m venv .venv
345
+ source .venv/bin/activate
346
+ python -m pip install --upgrade pip build twine
347
+ python -m pip install -e . # editable install; the `ping-shield` command is now on PATH
348
+ ping-shield --version
349
+ ```
350
+
351
+ ### 2. Versioning
352
+
353
+ ping-shield follows [Semantic Versioning](https://semver.org/). The version has a single source:
354
+ `__version__` in `ping_shield/__init__.py`.
355
+
356
+ | Bump | When | Example |
357
+ |---|---|---|
358
+ | **MAJOR** `2.0.0` | Something existing users rely on breaks: a CLI flag is removed or renamed, a config field changes meaning, or the state file needs a migration | dropping `--rebaseline` |
359
+ | **MINOR** `1.2.0` | New backwards-compatible features | a new command, check, source type or bot button |
360
+ | **PATCH** `1.1.1` | Backwards-compatible bug fixes only | a parser fix, a wrong exit code |
361
+
362
+ Every change goes under `## [Unreleased]` in [CHANGELOG.md](https://github.com/Yosinan/ping-shield/blob/main/CHANGELOG.md) when it's made. Each release
363
+ gets a git tag `vX.Y.Z` on the commit that sets the version. Never reuse or move a published tag or
364
+ version: PyPI rejects re-uploads, and users may already have pinned it.
365
+
366
+ ### 3. Cut a release
367
+
368
+ ```bash
369
+ # 1. In CHANGELOG.md, rename [Unreleased] to [1.2.0] - YYYY-MM-DD, add a fresh empty
370
+ # [Unreleased] section, and update the compare links at the bottom.
371
+ # 2. Set __version__ = "1.2.0" in ping_shield/__init__.py
372
+ git commit -am "Release 1.2.0"
373
+ git tag -a v1.2.0 -m "ping-shield 1.2.0"
374
+ git push origin main v1.2.0
375
+ ```
376
+
377
+ ### 4. Build the source distribution and wheel
378
+
379
+ ```bash
380
+ rm -rf dist/
381
+ python3 -m build
382
+ ```
383
+
384
+ This creates:
385
+
386
+ ```text
387
+ dist/ping_shield-1.2.0.tar.gz # source distribution (sdist)
388
+ dist/ping_shield-1.2.0-py3-none-any.whl # universal pure-Python wheel
389
+ ```
390
+
391
+ The wheel is tagged `py3-none-any`: it has no compiled code, so the same file installs on every
392
+ Linux distribution and CPU architecture. This is the modern equivalent of a "universal" wheel.
393
+
394
+ Check the artifacts before uploading:
395
+
396
+ ```bash
397
+ python3 -m twine check dist/*
398
+ python3 -m venv /tmp/ps-smoke && /tmp/ps-smoke/bin/pip install dist/*.whl && /tmp/ps-smoke/bin/ping-shield --help
399
+ ```
400
+
401
+ ### 5. Publish to PyPI with twine
402
+
403
+ 1. Create an account on [pypi.org](https://pypi.org) and on [test.pypi.org](https://test.pypi.org),
404
+ and enable 2FA on both.
405
+ 2. Create an **API token** under *Account settings → API tokens*. The first upload needs an
406
+ account-wide token. After that, replace it with a token scoped to the `ping-shield` project.
407
+ 3. Provide the token to twine. The username is always the literal string `__token__`. Choose one
408
+ of these methods:
409
+
410
+ ```bash
411
+ # a) environment variables (good for CI; nothing is written to disk)
412
+ export TWINE_USERNAME=__token__
413
+ export TWINE_PASSWORD=pypi-AgEIcHlwaS5vcmc... # read it from your password manager
414
+
415
+ # b) ~/.pypirc, readable only by you
416
+ cat > ~/.pypirc <<'EOF'
417
+ [pypi]
418
+ username = __token__
419
+ password = pypi-AgEIcHlwaS5vcmc...
420
+ [testpypi]
421
+ repository = https://test.pypi.org/legacy/
422
+ username = __token__
423
+ password = pypi-AgENdGVzdC5weXBp...
424
+ EOF
425
+ chmod 600 ~/.pypirc
426
+ ```
427
+
428
+ 4. Do a dry run on TestPyPI first:
429
+
430
+ ```bash
431
+ python3 -m twine upload --repository testpypi dist/*
432
+ pipx install --index-url https://test.pypi.org/simple/ ping-shield
433
+ ```
434
+
435
+ 5. Publish for real:
436
+
437
+ ```bash
438
+ python3 -m twine upload dist/*
439
+ ```
440
+
441
+ PyPI never lets you re-upload a file with the same version number. If something is wrong, bump the
442
+ version and build again. Once releases are routine, consider switching to
443
+ [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) from GitHub Actions. It removes
444
+ long-lived API tokens completely.
445
+
446
+ ## Security notes
447
+
448
+ - The bot token is stored with mode `0600` in a `0700` directory. If the file's permissions are too
449
+ open, they are tightened on load. The token is scrubbed from all error messages and logs.
450
+ - Feeds are parsed with the standard library's XML parser. Documents that declare XML entities are
451
+ rejected, downloads are capped at 10 MiB, and only `http` and `https` URLs are fetched.
452
+ - ping-shield detects changes. It does not prevent them. An attacker who already runs code as your
453
+ user can tamper with ping-shield itself, so treat a sudden stop in scans (visible with
454
+ `--status` and in the journal) as a signal too.
455
+
456
+ ## License
457
+
458
+ MIT