Skip to content

[Security][High] Account deletion without re-auth, server-controlled nid injection chain, and FormData token handling fragility #66

@numbers-official

Description

@numbers-official

Overview

Deep audit (2026-03-27) identified 3 new high-severity security findings not covered by existing issues.


Finding 1: Account Deletion Without Re-authentication

File: src/services/AuthService.ts, lines 175-178

The deleteAccount() method sends a DELETE /auth/users/me/ request using only the stored auth token, with no re-authentication or password confirmation. If an attacker gains temporary access to the stored token (e.g., via another extension, compromised machine, Chrome debugger), they can permanently delete the user's account with a single API call.

async deleteAccount(): Promise<void> {
    await this.apiClient.deleteWithAuth('/auth/users/me/');
    this.clearAuth();
}

Impact: Irreversible account destruction with no additional verification gate.

Fix: Require current_password in the DELETE request body (Djoser standard). Add a multi-step confirmation UI.


Finding 2: Server-Controlled nid Interpolated into URLs Without Sanitization

File: src/services/UploadService.ts, line 359; src/share/share.tsx, lines 21-36

After upload, the API response's nid value is directly interpolated into a URL used to open share.html:

const nid = result.id || result.cid || result.nid;
const shareUrl = chrome.runtime.getURL(`share.html?nid=${nid}`);

The nid is server-controlled. In share.tsx, this unsanitized nid flows into innerHTML:

const verifyUrl = `https://asset.captureapp.xyz/${nid}`;
document.getElementById('root')!.innerHTML = `
    <div class="verify-link">
      <a href="${verifyUrl}" target="_blank">${verifyUrl}</a>
    </div>`;

A crafted nid like "><img src=x onerror=alert(1)> executes JS in the extension's origin context.

Impact: XSS in extension context via compromised API response (distinct from #10 which describes the general share page XSS; this identifies the specific server-response attack vector).

Fix:

  • Validate nid with /^[a-zA-Z0-9_-]+$/ before use
  • Use encodeURIComponent(nid) in URL construction
  • Replace innerHTML with DOM API methods (textContent, createElement, setAttribute)

Finding 3: FormData Auth Token Handling with Cross-Context instanceof Fragility

File: src/services/ApiClient.ts, lines 71-83 and 128-137

buildAuthHeaders() always sets Content-Type: application/json. For FormData uploads, request() deletes this header based on body instanceof FormData. However, instanceof can fail across different global scopes (e.g., after service worker restart), causing:

  • Auth token sent with wrong Content-Type
  • Token potentially exposed in server error responses

Fix: Use duck-typing (body && typeof body.append === 'function') instead of instanceof. Move Content-Type decision into a dedicated uploadWithAuth method.


Generated by Heart Beat with Omni

Metadata

Metadata

Labels

priority:highHigh prioritysecuritySecurity vulnerability or concern

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions