Skip to content

security: remove bot token from webhook URL, fix no-op framework patching (v0.4.0) - #9

Closed
AhooraZen wants to merge 4 commits into
Andromeda-Collective:mainfrom
AhooraZen:hardening/security-and-correctness-v0.4.0
Closed

security: remove bot token from webhook URL, fix no-op framework patching (v0.4.0)#9
AhooraZen wants to merge 4 commits into
Andromeda-Collective:mainfrom
AhooraZen:hardening/security-and-correctness-v0.4.0

Conversation

@AhooraZen

@AhooraZen AhooraZen commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

No description provided.

AhooraZen and others added 4 commits July 26, 2026 21:09
…hing

Audit of every source file surfaced three classes of problem in a tool whose
whole purpose is working from a network-filtered region.

Security:
- `andro-cfw serverless` registered `/webhook?token=<BOT_TOKEN>` with Telegram.
  That URL is stored in the bot's webhook config and replayed on every update.
  The token is now an encrypted Worker secret and the webhook URL is bare.
- The webhook is authenticated with `setWebhook(secret_token=...)`, verified in
  constant time against `X-Telegram-Bot-Api-Secret-Token`. Previously anyone who
  learned the worker URL could inject fabricated updates.
- The worker no longer trusts a caller-supplied `?token=` query parameter.
- CORS `*` on both origin and headers made every deployment a general-purpose
  CORS bypass for the Bot API. Now opt-in via ALLOWED_ORIGINS.
- Node.js install no longer pipes a downloaded script into `sudo bash` by
  default (ANDRO_CFW_ALLOW_NODESOURCE opts in; ANDRO_CFW_NO_AUTO_INSTALL opts
  out of auto-install entirely).
- Token prompt uses getpass; $TELEGRAM_BOT_TOKEN preferred over argv.
- Key dir and per-account wrangler OAuth dirs are 0700; account labels are
  validated so they cannot escape ~/.andro_cfw/accounts; URL schemes pinned.

Correctness:
- `andro_cfw.patch()` was a no-op for aiogram, python-telegram-bot, pyrogram and
  hydrogram. A bot called it, saw no error, and talked straight to
  api.telegram.org. aiogram and PTB are now patched for real; the MTProto
  clients are removed with an explanatory warning, since an HTTP Bot API proxy
  cannot route them.
- setWebhook success was inferred from HTTP 200; the Bot API returns
  {"ok": false} with status 200 for most rejections.
- Session writes are atomic, so a load-balancer thread can no longer leave an
  undecryptable cfw.session behind.
- The all-workers-down 503 is framed; unframed HTTP/1.1 hung the client.
- Hop-by-hop headers are not relayed, request bodies are capped, stop() closes
  the listening socket, and persistence moved out of the balancer lock.
- The shipped worker template no longer contains a hardcoded demo bot.
- Version is single-sourced from package metadata (the v0.3.2 tag shipped code
  reporting 0.3.0).

Testing: 76 -> 121 tests. The four patch() tests asserted against MagicMock,
which auto-creates any attribute touched, so they passed regardless of behavior.
CI now gates on ruff and mypy and runs on macOS and Windows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI sets ANDRO_CFW_NO_AUTO_INSTALL=1 so the suite can never trigger a system
package install. That short-circuits check_node_toolchain() before the
auto-install branch, so test_check_node_toolchain failed on every CI runner
while passing locally, where the variable is unset.

Clear it in an autouse fixture so these tests exercise the path they name. The
test that covers the opt-out sets the variable explicitly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Windows has no POSIX permission bits -- os.chmod only toggles the read-only
flag, so S_IMODE reports 0o777 / 0o666 regardless. The production code already
treats the chmod as best-effort; only the assertions were platform-specific.

Surfaced by the newly added Windows CI job, which this repo never ran before.
On Windows, access to these paths is governed by the ACL %USERPROFILE% carries.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closes the two findings the previous plan set deferred, and drops plans/.

PERF-04 -- the load balancer buffered whole request and response bodies in
memory. A 50 MB upload plus a few concurrent transfers pinned that much RAM per
request thread.

Responses are now relayed in 64 KB chunks; only the first 512 bytes are held,
which is all the Cloudflare quota-page sniffing needs. Request bodies spool:
in memory up to 1 MB, on disk above it. Spooling rather than streaming off the
client socket is deliberate -- failover replays the same body against the next
worker, so it has to remain seekable.

Two framing cases fell out of streaming and are handled explicitly: urllib
decodes chunked upstream responses, leaving no Content-Length, so those are
delimited with `Connection: close`; and HEAD sends headers with no body.

`_forward` became `_open_upstream`, returning the response unread so the caller
can stream it and owning-close it. Its tests were rewritten accordingly, plus
new coverage for body replay on failover, spill-to-disk, chunked relay, HEAD,
and a client that disconnects mid-download.

DIR-04 -- the worker proxied to a hardcoded api.telegram.org. UPSTREAM_API_ORIGIN
now points it at a self-hosted telegram-bot-api instance, which lifts Telegram's
50 MB upload cap. Only the origin is used, and a malformed or non-http(s) value
falls back to Telegram rather than sending traffic somewhere unintended.

plans/ is removed: every plan in it is now implemented.

133 tests pass; ruff and mypy clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@AhooraZen AhooraZen closed this Jul 26, 2026
@AhooraZen
AhooraZen deleted the hardening/security-and-correctness-v0.4.0 branch July 27, 2026 11:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant