V0.1.1 release, close to actual release. Bug & security fixes/improvements.
This commit is contained in:
166
agent4_batch4.md
Normal file
166
agent4_batch4.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Batch 4 QA Report
|
||||
|
||||
**Date:** 2026-05-24
|
||||
**Scope:** Workspace build validation, Telegram API constraint analysis, regression checklist
|
||||
**Blockers:** None
|
||||
|
||||
---
|
||||
|
||||
## 1. Build Results
|
||||
|
||||
| Check | Result | Details |
|
||||
|---|---|---|
|
||||
| `cargo check --workspace` | ✅ PASS | Finished in 5.38s, all crates compile cleanly |
|
||||
| `cargo test --workspace` | ✅ PASS | All test suites pass (0 unit tests present across crates, all doc-tests pass) |
|
||||
| `cd frontend && npm run build` | ✅ PASS | Built in 1.44s. Warning: `lib-BKGKj-wr.js` (497 kB) and `index-5C1xoqEL.js` (1,038 kB) exceed 500 kB after minification. This is a Vite chunk-size warning, not a build failure. |
|
||||
|
||||
---
|
||||
|
||||
## 2. Telegram API Constraint Analysis
|
||||
|
||||
### 2.1 Caption Length — ⚠️ FLAGGED
|
||||
|
||||
**Location:** `crates/cgcx-bot/src/main.rs`, `handle_forward_callback`, `"approve"` action (~line 1810)
|
||||
|
||||
**Code:**
|
||||
```rust
|
||||
let caption = format!(
|
||||
"{}\n\nSubmitted by: {}\nDirect link: <code>{}</code>\nForward link: <code>{}</code>",
|
||||
escape_html(&forward_def.forward_message),
|
||||
author_line,
|
||||
link,
|
||||
forward_link
|
||||
);
|
||||
```
|
||||
|
||||
**Analysis:**
|
||||
- `forward_def.forward_message` is stored as `TEXT NOT NULL DEFAULT ''` in SQLite (`migrations/003_forward_system.sql`). SQLite `TEXT` has no practical length limit.
|
||||
- The fixed parts of the caption (`Submitted by: …`, `Direct link: …`, `Forward link: …`) add ~60–120 characters.
|
||||
- `author_line` can be up to ~60 characters (escaped username + ID).
|
||||
- `link` is ~80–120 characters (base URL + 12-char CXID + 12-char password).
|
||||
- `forward_link` is ~70–100 characters (`t.me/{bot}?start=submitfwdid{code}`).
|
||||
- **Telegram API limit for captions:** 1,024 characters.
|
||||
|
||||
**Risk:** If an admin sets a `forward_message` longer than ~800 characters, the total caption will exceed 1,024 characters. Telegram will reject the `send_media_group` or `send_message` call with a `Bad Request: MEDIA_CAPTION_TOO_LONG` error. The code does not truncate or validate caption length before sending.
|
||||
|
||||
**Recommendation:** Truncate `forward_def.forward_message` to a safe limit (e.g., 700 chars) before interpolating into the caption, or split into a separate text message if the message is long.
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Media Type Handling — 📋 NOTED
|
||||
|
||||
**Locations:**
|
||||
- `finalize_upload`, review-group media batch (~line 1340)
|
||||
- `handle_forward_callback`, destination media batch (~line 1850)
|
||||
|
||||
**Code:**
|
||||
```rust
|
||||
let media = if mime_type.starts_with("image/") {
|
||||
InputMedia::Photo(InputMediaPhoto::new(input_file))
|
||||
} else {
|
||||
InputMedia::Document(InputMediaDocument::new(input_file))
|
||||
};
|
||||
```
|
||||
|
||||
**Analysis:**
|
||||
- Images are correctly sent as `InputMediaPhoto`.
|
||||
- **All non-image files are sent as `InputMediaDocument`**, regardless of MIME type.
|
||||
|
||||
**Impact:**
|
||||
- **Video files** (`video/mp4`, etc.) lose native Telegram playback UI (no inline player, no duration badge, no thumbnail generation).
|
||||
- **Audio files** (`audio/mpeg`, etc.) lose native audio player UI.
|
||||
- Telegram treats them as generic documents, which degrades UX in review and destination groups.
|
||||
|
||||
**Recommendation:** Map MIME types more precisely:
|
||||
| MIME prefix | Current | Better |
|
||||
|---|---|---|
|
||||
| `image/*` | `InputMediaPhoto` | ✅ Keep |
|
||||
| `video/*` | `InputMediaDocument` | `InputMediaVideo` |
|
||||
| `audio/*` | `InputMediaDocument` | `InputMediaAudio` |
|
||||
| other | `InputMediaDocument` | ✅ Keep |
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Memory Usage — 📋 NOTED
|
||||
|
||||
**Locations:**
|
||||
- `finalize_upload`, review-group decryption (~line 1320)
|
||||
- `handle_forward_callback`, destination decryption (~line 1830)
|
||||
|
||||
**Code pattern:**
|
||||
```rust
|
||||
match cgcx_crypto::decrypt_bytes(&ciphertext, &file.encrypted_key_wrapped, &ctx.master_key) {
|
||||
Ok(bytes) => decrypted.push((file.mime_type.clone(), bytes)),
|
||||
...
|
||||
}
|
||||
...
|
||||
let input_file = InputFile::memory(bytes.clone());
|
||||
```
|
||||
|
||||
**Analysis:**
|
||||
1. `tokio::fs::read(&file.stored_path)` loads the entire encrypted file into memory as `ciphertext`.
|
||||
2. `decrypt_bytes` decrypts in-memory and returns a new `Vec<u8>` (`bytes`). At this point, two copies of the file exist in RAM (ciphertext + plaintext).
|
||||
3. `InputFile::memory(bytes.clone())` clones the plaintext bytes again for the `InputMedia` struct. Now three copies may exist transiently.
|
||||
4. Files are batched in chunks of 10 (`decrypted.chunks(10)`), so up to 10 files are held in memory simultaneously.
|
||||
|
||||
**Risk:** For large uploads (e.g., a 100 MB video), this can easily exhaust RAM, especially on constrained hosts or when multiple submissions are processed concurrently. The bot does not stream or chunk-decrypt files.
|
||||
|
||||
**Recommendation:** Consider streaming decryption to temporary files and using `InputFile::file(path)` instead of `InputFile::memory(bytes)`. This keeps only one copy on disk instead of multiple copies in RAM.
|
||||
|
||||
---
|
||||
|
||||
## 3. Regression Checklist — Batch 4
|
||||
|
||||
Use this checklist before merging or deploying Batch 4 changes:
|
||||
|
||||
### Build & Compile
|
||||
- [ ] `cargo check --workspace` passes with zero errors
|
||||
- [ ] `cargo test --workspace` passes (all suites green)
|
||||
- [ ] `cd frontend && npm run build` produces `dist/` without errors
|
||||
- [ ] No new compiler warnings introduced in `cgcx-bot`
|
||||
|
||||
### Bot Runtime
|
||||
- [ ] Bot starts successfully and connects to Telegram (`get_me` succeeds)
|
||||
- [ ] InMemStorage dialogue state machine transitions correctly (Start → TermsPending → MainMenu → UploadStaging → UploadOptions → UploadFinalizing)
|
||||
- [ ] Service message cleanup works in groups/channels and is silently skipped in private chats
|
||||
- [ ] Punishment expiration timer revokes bans/mutes after duration elapses
|
||||
- [ ] Global ban propagation (`propagate_punishment`) only runs when `config.groups.global_ban == true`
|
||||
|
||||
### Upload & Submission Flow
|
||||
- [ ] Staging accepts media, documents, and text up to `max_batch_size`
|
||||
- [ ] Upload options (destroy, download, password, show_author) toggle correctly
|
||||
- [ ] `finalize_upload` respects `max_total_batch_bytes` limit
|
||||
- [ ] Disk-space check (`fs2::available_space`) blocks uploads when temp space < 2× batch size
|
||||
- [ ] Blocked-hash detection (`CgcxError::BlockedHash`) aborts upload and cleans up
|
||||
|
||||
### Forward & Review System
|
||||
- [ ] `/create_submit_forward` validates bot admin status in both destination and review groups
|
||||
- [ ] Submission links (`?start=submitfwdid{code}`) work and enforce allow-lists
|
||||
- [ ] Review message is sent to review group with correct inline keyboard
|
||||
- [ ] **Approve action sends media batch to destination without `MEDIA_CAPTION_TOO_LONG` error**
|
||||
- [ ] Approve action sends DM confirmation to submitter with posted link
|
||||
- [ ] Ignore/Ban/Blacklist/Ban+Blacklist callbacks update review message and submitter correctly
|
||||
- [ ] **Media batching handles >10 files by splitting into multiple `send_media_group` calls**
|
||||
|
||||
### Admin Commands
|
||||
- [ ] `/reload` refreshes moderation lists
|
||||
- [ ] `/blacklist_uid`, `/whitelist_uid` update DB and moderation engine
|
||||
- [ ] `/sban`, `/smute`, `/mute`, `/pban`, `/kick` resolve target user and apply restrictions
|
||||
- [ ] `/rmute`, `/rban` revoke active punishments
|
||||
- [ ] `/get_id` returns chat ID or searches admins by username/display name
|
||||
|
||||
### Security & Stability
|
||||
- [ ] Panic hook logs location and message
|
||||
- [ ] `CatchPanicLayer`-swallowed panics are traceable via logs
|
||||
- [ ] 8MB thread stack prevents stack overflow during dptree dispatch
|
||||
|
||||
---
|
||||
|
||||
## 4. Blockers
|
||||
|
||||
**No critical blockers.** All builds pass.
|
||||
|
||||
**Non-blocking issues identified:**
|
||||
1. **Caption length risk** — can cause Telegram API rejection on approval; should be mitigated before relying on forward system in production.
|
||||
2. **Media type mapping** — video/audio UX is degraded; nice-to-have improvement.
|
||||
3. **Memory usage** — large files may cause OOM during forward review/approval; should be monitored or mitigated for production load.
|
||||
Reference in New Issue
Block a user