Major improvement, security handling, file handling +fixes

This commit is contained in:
unknown
2026-05-23 00:13:56 +02:00
parent 2129081599
commit a7b44af91a
25 changed files with 925 additions and 116 deletions

View File

@@ -37,13 +37,20 @@
"max_views": 10,
"current_views": 3,
"allow_download": true,
"created_at": "2024-01-01T00:00:00+00:00"
"created_at": "2024-01-01T00:00:00+00:00",
"total_size": 1234567,
"author": {
"username": "@example",
"user_id": 123456789
}
}
```
- `401 Unauthorized` — Password required but missing or invalid.
- `404 Not Found` — Content does not exist or has been deleted/blacklisted.
- `410 Gone` — Content has reached its maximum view count.
- **Notes:**
- `total_size` is the sum of all file sizes in bytes.
- `author` is gated by the content's `show_author` flag. It is `null` when `show_author = false`, when the uploader's user record is missing, or when the user has no Telegram username (`username` may be `null` even when `user_id` is present).
- If authentication succeeds via the `sc` query parameter, the server sets an HMAC-signed `cgcx_pw` cookie on the response (`Max-Age=3600; SameSite=Strict; HttpOnly; Path=/`).
---

View File

@@ -23,7 +23,7 @@ For a self-hosted, single-tenant service handling encrypted file metadata, **SQL
1. **Operational simplicity**: No separate database server to install, upgrade, or network-secure. A single `.sqlite` file is trivial to back up, replicate, or inspect.
2. **WAL mode performance**: With `PRAGMA journal_mode = WAL`, SQLite handles concurrent readers and a single writer efficiently - enough for a bot + web server pair.
3. **Schema simplicity**: The schema is small (5 tables, 2 migration files). The overhead of a client/server RDBMS is unjustified.
3. **Schema simplicity**: The schema is small (10 tables, 7 migration files). The overhead of a client/server RDBMS is unjustified.
4. **Deployment footprint**: Ideal for running on a small VPS or even an embedded edge device without container orchestration.
If future requirements demand horizontal scaling or heavy analytics, the repository pattern in `cgcx-db` makes it straightforward to swap in PostgreSQL without touching the bot or server code.
@@ -121,6 +121,17 @@ The **cg.cx server** is a trusted party for decryption and delivery. It is not a
---
## Hashing for Deduplication and Blacklist
`cgcx-crypto` computes a **BLAKE3 hash over the ciphertext stream** (including the secretstream header) for tamper detection. This hash is stored per-file in `content_files.encrypted_hash`.
In addition, the file pipeline now computes a **plaintext BLAKE3 hash** during ingestion:
1. A running hash of the plaintext chunks is computed alongside encryption.
2. The resulting `plaintext_hash` is stored in `content_files` and used for deduplication — when identical plaintext is uploaded, the existing encrypted file is reused and its `ref_count` is incremented.
3. A `hash_blacklist` table (migration `007_hash_blacklist.sql`) allows moderators to block re-uploads of known-banned content by its plaintext hash. The pipeline checks this blacklist before storing any new file and rejects blocked content with a `BlockedHash` error.
---
## Future Considerations
- **Client-side decryption**: A future iteration could deliver the wrapped CEK to the browser and decrypt via WebAssembly / libsodium-js. This would remove the server from the trust boundary for delivery.

View File

@@ -11,12 +11,12 @@ All admin commands require the caller to be an **administrator or owner** of the
| Command | Args | Description |
|---------|------|-------------|
| `/reload` | none | Reload moderation lists from disk. |
| `/blacklist_uid` | `<ID>` | Blacklist a user by Telegram ID globally and set their role to `banned`. |
| `/whitelist_uid` | `<ID>` | Remove a user from the global blacklist and restore their role to `user`. |
| `/help` | none | Show the admin help message listing all admin commands. |
| `/blacklist_uid` | `<ID>` | Blacklist a user by Telegram ID globally and set their role to `banned`. **Restricted to configured admin groups.** Shows usage info if the ID argument is missing. |
| `/whitelist_uid` | `<ID>` | Remove a user from the global blacklist and restore their role to `user`. **Restricted to configured admin groups.** Shows usage info if the ID argument is missing. |
| `/help` | none | Show the admin help message listing all admin commands. Properly HTML-escaped. |
| `/get_id` | none | Get the current group chat ID. |
| `/get_id` | `<@username>` | Search administrators in this chat by username. |
| `/get_id` | `<displayname>` | Search members in this chat by display name. |
| `/get_id` | `<@username>` | Search administrators in this chat by username. Results are HTML-escaped. |
| `/get_id` | `<displayname>` | Search members in this chat by display name. Results are HTML-escaped. |
| `/create_submit_forward` | `<dest_chat_id> <review_group_id> [forward_message]` | Create a submission forward link. Bot must be admin in both destination and review groups. |
| `/show_c_forward` | `[page]` | List active forward links for this chat with pagination. |
| `/add_blacklist` | `<user_id>` | Blacklist a user in **all active forwards** for this source chat. |
@@ -102,5 +102,7 @@ Callbacks use the format `v1:<namespace>:<action>[:<id>]`.
| `v1:fwd:approve:{submission_id}` | Approve a forward submission and post it to the destination chat. |
| `v1:fwd:ignore:{submission_id}` | Reject a forward submission. |
| `v1:fwd:blk:{submission_id}` | Blacklist the submitting user from the forward. |
| `v1:fwd:ban:{submission_id}` | Ban the submitter in the destination chat and review group. |
| `v1:fwd:banblk:{submission_id}` | Ban + blacklist the submitter in one action. |
| `v1:fwd:revoke:{forward_id}` | Revoke a forward link. |
| `v1:fwd:page:{page}` | Navigate forward link list pages. |

View File

@@ -141,10 +141,14 @@ Users click the deep link or send:
- Posts a review message to the `review_group_id` with inline buttons:
- `[ Approve ]` → callback `v1:fwd:approve:{submission_id}`
- `[ Ignore ]` → callback `v1:fwd:ignore:{submission_id}`
- `[ Blacklist User ]` → callback `v1:fwd:blk:{submission_id}`
- `[ Blackl. ]` → callback `v1:fwd:blk:{submission_id}`
- `[ Ban ]` → callback `v1:fwd:ban:{submission_id}`
- `[ Ban/BL u. ]` → callback `v1:fwd:banblk:{submission_id}`
- Stores the sent message ID back into `forward_submissions.review_message_id`.
4. **Review** — Moderators in the review group click the buttons to act.
**Note:** Media batching is implemented for both review group presentation and approved destination posts. The bot decrypts and sends up to 10 files per media group, attaching the review text or approval caption to the last item.
---
## Review Actions
@@ -176,6 +180,21 @@ All review callbacks require the clicking user to be an **administrator in the r
3. Sets `forward_submissions.status = 'blacklisted'`.
4. The user is now blocked from using this forward link again (until removed).
### Ban (`v1:fwd:ban`)
1. Bans the submitter in both the destination chat and the review group.
2. Records `ban` punishments in the `punishments` table for both chats.
3. Edits the review message to show `[ BANNED ]` and the moderator ID.
4. Sets `forward_submissions.status = 'banned'`.
### Ban + Blacklist (`v1:fwd:banblk`)
1. Bans the submitter in both the destination chat and the review group (same as `v1:fwd:ban`).
2. Records `ban` punishments in the `punishments` table for both chats.
3. Also adds the submitter to `forward_lists` with `list_type = 'blacklist'` (same as `v1:fwd:blk`).
4. Edits the review message to show `[ BAN/BL ]` and the moderator ID.
5. Sets `forward_submissions.status = 'banblk'`.
---
## Management Commands

View File

@@ -147,3 +147,21 @@ WHERE id = ?2;
This is used both for:
- **Automatic expiration** (`revoked_by = 0`)
- **Manual moderator revocation** (`revoked_by = moderator_user_id`)
---
## Global Ban Configuration
Under `[groups]` in the config, the optional `global_ban` flag (default `false`) controls whether punishment commands (`/sban`, `/smute`, `/mute`, `/pban`) are propagated across all known chats where the bot is an administrator.
```toml
[groups]
admin_group_ids = [-1001234567890]
review_group_ids = [-1009876543210]
global_ban = false
```
- When `global_ban = true`, issuing a punishment in any admin group is intended to apply the same action to every known chat (source chats, destination chats, review groups, and configured `admin_group_ids` / `review_group_ids`) where it has admin rights.
- When `global_ban = false` (default), punishments are local to the group where the command was issued.
**Note:** When `global_ban = true`, the bot propagates the punishment to every configured `admin_group_ids`, `review_group_ids`, and all active forward chats (source, destination, and review groups) where it has administrator rights. Each propagated action is recorded as a separate `punishments` row.