fix: v1.9.6 — Code.gs/CodeFull.gs hardening, goog.script.init unwrap, README rewrite

Server-side (Apps Script) fixes — users replace their Code.gs with assets/apps_script/Code.gs (or CodeFull.gs for full mode) and Manage deployments → ✏️ → New version → Deploy:
- Removed duplicate doGet in Code.gs (HtmlService one was overriding ContentService one due to JS hoisting → every GET to /exec returned a goog.script.init iframe instead of the placeholder HTML)
- CodeFull.gs doGet switched from HtmlService to ContentService (same reason)
- SKIP_HEADERS now strips X-Forwarded-* / Forwarded / Via family — second line of defense to v1.2.9's client-side stripping (#104), in case a misconfigured upstream proxy adds these
- _doBatch fallback when UrlFetchApp.fetchAll() throws as a whole — per-item fetch on safe methods so one bad URL no longer poisons the entire batch (port from masterking32@3094288)

Client-side (Rust) defense-in-depth:
- parse_relay_json now unwraps goog.script.init("...userHtml...") if any deployment returns the iframe-wrapped form (legacy Code.gs, or a redirect that GETs doGet). New extract_apps_script_user_html + decode_js_string_escapes helpers. Tested against a real deployment's doGet response.

Docs:
- README rewritten as short bilingual landing page (English + Persian RTL) targeting normal users; advanced reference moved to docs/guide.md + docs/guide.fa.md.

Tests: 3 new regression tests. 176 lib + 33 tunnel-node tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
therealaleph
2026-05-01 19:00:13 +03:00
parent d336bd39e5
commit cbb08468bc
9 changed files with 1430 additions and 739 deletions
+18
View File
@@ -0,0 +1,18 @@
<!-- see docs/changelog/v1.1.0.md for the file format: Persian, then `---`, then English. -->
• Code.gs / CodeFull.gs hardening + باگ‌فیکس (هیچ تغییری در کانفیگ کاربر لازم نیست — فقط Code.gs خودتان را با [`assets/apps_script/Code.gs`](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/blob/main/assets/apps_script/Code.gs) (یا `CodeFull.gs` برای حالت full) جایگزین کنید + در Apps Script editor: `Manage deployments → ✏️ → Version: New version → Deploy`. Deployment ID همان قبلی می‌ماند):
- **`Code.gs` doGet تکراری حذف شد**: نسخه‌ای که با `HtmlService.createHtmlOutput` تعریف شده بود به‌خاطر hoisting جاوااسکریپت روی نسخهٔ صحیح `ContentService` overwrite می‌کرد. در نتیجه هر GET به URL deployment پاسخ سندباکس `goog.script.init` iframe برمی‌گرداند به‌جای HTML پلیس‌هولدر ساده. این برای ترافیک معمولی POST تأثیری نداشت ولی در زنجیرهٔ redirect که با GET پی می‌گیریم می‌توانست باگ ظاهر شود.
- **`CodeFull.gs` `doGet` به `ContentService` تغییر کرد** (قبلاً `HtmlService` بود) — به همان دلیل بالا.
- **هدرهای IP-leak در `SKIP_HEADERS` اضافه شد** (`X-Forwarded-For`, `X-Forwarded-Host`, `X-Forwarded-Proto`, `X-Forwarded-Port`, `X-Real-IP`, `Forwarded`, `Via`) — در صورت misconfigured بودن یک پروکسی upstream سمت کاربر، IP واقعی کاربر دیگر در leg دوم سرور به مقصد نشت نمی‌کند. لایهٔ دفاع دوم به stripping سمت کلاینت v1.2.9 (#104).
- **`_doBatch` دارای fallback شد**: اگر `UrlFetchApp.fetchAll()` به‌عنوان یک کل throw کند (مثلاً یک URL بد همه را poison کند)، حالا برای متدهای امن (GET / HEAD / OPTIONS) per-item fetch می‌کند به‌جای صفر کردن کل پاسخ batch. port از `masterking32/MasterHttpRelayVPN@3094288`.
`parse_relay_json` (سمت Rust): unwrapper برای `goog.script.init("...userHtml...")` اضافه شد — اگر هر deployment‌ای پاسخ HtmlService-wrapped برگرداند (legacy Code.gs قبل از v1.9.6، یا redirect که doGet را GET بزند)، client حالا JSON داخلی را استخراج می‌کند به‌جای `key must be a string at line 2 column 1` fail کردن. در مقابل پاسخ doGet واقعی deployment کاربر تست شده — UTF-8 با `\xNN` byte-escape را درست decode می‌کند.
• README بازنویسی شد: نسخهٔ کوتاه دوزبانه (انگلیسی + فارسی RTL) برای کاربر معمولی + راهنمای کامل پیشرفته در [`docs/guide.md`](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/blob/main/docs/guide.md) و [`docs/guide.fa.md`](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/blob/main/docs/guide.fa.md). جدا کردن "راه‌اندازی ۵ دقیقه‌ای" از "همهٔ گزینه‌ها و troubleshooting" راهنما را خیلی قابل‌فهم‌تر کرد، خصوصاً برای کاربرانی که می‌خواهند فقط شروع کنند.
• تست: ۳ regression test جدید برای `extract_apps_script_user_html` + `decode_js_string_escapes` + `parse_relay_json` end-to-end. **۱۷۶ lib test + ۳۳ tunnel-node test همه pass.**
---
• Code.gs / CodeFull.gs hardening + bug fixes (no client config change needed — just replace your own Code.gs with [`assets/apps_script/Code.gs`](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/blob/main/assets/apps_script/Code.gs) (or `CodeFull.gs` for full mode) and in the Apps Script editor: `Manage deployments → ✏️ → Version: New version → Deploy`. Your Deployment ID stays the same):
- **Removed duplicate `doGet` in `Code.gs`**: a second copy declared with `HtmlService.createHtmlOutput` was silently overriding the correct `ContentService` one due to JS function hoisting. Result: every GET to the deployment URL was returning the `goog.script.init` sandbox iframe instead of the simple placeholder HTML. Did not affect normal POST traffic, but could surface during redirect chains we GET-follow.
- **`CodeFull.gs` `doGet` switched to `ContentService`** (was `HtmlService`) — same reason as above.
- **Added IP-leak headers to `SKIP_HEADERS`** (`X-Forwarded-For`, `X-Forwarded-Host`, `X-Forwarded-Proto`, `X-Forwarded-Port`, `X-Real-IP`, `Forwarded`, `Via`) — if a misconfigured upstream proxy on the user side adds these, the user's real IP no longer leaks to the destination on the server-side leg. Second line of defense to v1.2.9's client-side stripping (#104).
- **`_doBatch` got a fallback path**: if `UrlFetchApp.fetchAll()` throws as a whole (e.g. one bad URL poisons the batch), it now per-item-fetches safe methods (GET / HEAD / OPTIONS) instead of zeroing the entire batch's responses. Ported from `masterking32/MasterHttpRelayVPN@3094288`.
`parse_relay_json` (Rust client): added unwrapper for `goog.script.init("...userHtml...")` iframe — if any deployment ever returns an HtmlService-wrapped response (legacy Code.gs prior to v1.9.6, or a redirect that GET-hits doGet), the client now extracts the inner JSON instead of failing with `key must be a string at line 2 column 1`. Tested against a real user deployment's actual doGet output — correctly decodes UTF-8 with `\xNN` byte-escapes.
• Rewrote the README: short bilingual landing page (English + Persian RTL) for normal users, with the full advanced reference moved to [`docs/guide.md`](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/blob/main/docs/guide.md) and [`docs/guide.fa.md`](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/blob/main/docs/guide.fa.md). Splitting "5-minute quick start" from "every option + troubleshooting" makes the docs much more approachable, especially for users who just want to get running.
• Tests: 3 new regression tests for `extract_apps_script_user_html` + `decode_js_string_escapes` + `parse_relay_json` end-to-end. **176 lib tests + 33 tunnel-node tests all passing.**