Report vulnerabilities to: security@plugin.observer
We will acknowledge reports within 48 hours and triage within 7 days. Good-faith security researchers will not face legal action.
Scope: vulnerabilities in the plugin itself, its data handling, or its communication with the plugin.observer API.
When reporting, include: a description of the issue, steps to reproduce, and the affected plugin version.
Supported versions: current release only.
The plugin communicates with a single API server (default: https://plugin.observer, user-configurable in settings). Two endpoints are used:
| Detail | Value |
|---|---|
| Custom headers | None |
| Request body | None |
| Response | { nonce, difficulty, expiry, signature } |
| Purpose | Obtain a challenge token for proof-of-work and request signing |
Request body:
| Field | Type | Example | Description |
|---|---|---|---|
plugins[].id |
string | dataview |
Community plugin ID |
plugins[].version |
string | 0.5.67 |
Installed version |
No other fields are sent. The plugin never transmits vault contents, filenames, note text, user configuration, or any other personal data.
Request headers:
| Header | Description |
|---|---|
Content-Type |
application/json |
x-challenge |
Challenge nonce — binds request to a specific challenge |
x-challenge-expiry |
Challenge expiry timestamp |
x-challenge-sig |
Server-issued signature proving challenge authenticity |
x-challenge-difficulty |
Proof-of-work difficulty level |
x-pow-solution |
Client's proof-of-work solution (SHA-256 with leading zero bits) |
x-request-sig |
HMAC-SHA256 signature over the request body, using a key derived from the challenge nonce and a build-time client secret (two-step HMAC: derive key from nonce, then sign body with derived key) |
Requests are batched at 200 plugins maximum. If more are installed, multiple requests are sent, each with its own challenge.
- Polling: checks the installed plugin list every 10 seconds via a local in-memory comparison — no network request is made unless the list has changed or the previous scan failed
- Scans on change: contacts the API only when a plugin is installed, updated, or removed
- Debounce: 3-second delay after detecting a change before scanning
- Pre-update scan: single-plugin scan when the user installs or updates a plugin (if pre-update warnings are enabled in settings)
- First run: immediate scan on plugin activation
- Manual trigger: "Scan now" command in the command palette
- Transport: all requests use Obsidian's
requestUrlAPI (Electron'snet.request; respects system proxy settings) - Authentication: challenge-response, proof-of-work, and HMAC-SHA256 signing — no user credentials or API keys are transmitted
- Failure behavior: returns empty results and shows an error icon in the status bar; retries on the next poll cycle (10 seconds) if the plugin list is unchanged; no aggressive retry loop or request queuing
From Obsidian:
app.plugins.manifests — a record of installed community plugin IDs, versions, and display names. This is the only data read from the Obsidian runtime beyond the plugin's own settings.
Not collected:
- Vault contents, filenames, or directory structure
- Note text or metadata
- Other plugin configurations or settings
- Obsidian account information
- System information (OS, hardware, etc.)
All local storage uses Obsidian's Plugin.loadData() / Plugin.saveData() API. Data is written to .obsidian/plugins/observer/data.json.
Settings:
| Field | Type | Description |
|---|---|---|
apiUrl |
string | API server URL |
alertThreshold |
number | Score threshold (0–100) for alerts |
preUpdateWarnings |
boolean | Show confirmation before installing flagged plugins |
notificationStyle |
"notice" | "silent" |
How alerts are displayed |
ignoredPlugins |
string[] | Plugin IDs excluded from alerts |
Scan cache:
| Field | Type | Description |
|---|---|---|
results |
ScanResult[] | Scan results: scores, findings, alerts |
scannedAt |
number | Timestamp of last scan (ms) |
No data is encrypted locally — all stored data is non-sensitive (security scores and user preferences).
| Obsidian API | Purpose |
|---|---|
Plugin |
Lifecycle management (load, unload, save data) |
requestUrl |
HTTP requests to the scan API |
Notice |
Toast notifications for scan results |
Modal |
Alert detail modal and pre-update confirmation dialog |
PluginSettingTab / Setting |
Settings UI |
setIcon |
Render shield icons in status bar and modals |
Undocumented API usage:
The plugin accesses app.plugins.manifests (read-only) to list installed plugins, and temporarily replaces app.plugins.installPlugin to show a confirmation dialog before installing plugins with security alerts. Neither of these are part of Obsidian's public API.
- The
installPluginreplacement passes all arguments through to the original function unchanged — it never modifies what gets installed - The original function is restored when the plugin unloads
- If the replacement encounters any error, it falls through to the original function (fail-safe)
- If pre-update warnings are disabled in settings, the original function is called immediately with no interception
Obsidian does not provide an official hook for pre-install interception. This is the only way to warn users before a flagged plugin is installed.
- No
eval(),new Function(), or dynamic code execution - No
innerHTMLor raw HTML injection — all DOM created via Obsidian's safecreateEl/createDivAPI - No direct filesystem access — only Obsidian's
loadData()/saveData() - No WebSocket connections or background workers
- Cryptographic operations use the Web Crypto API (
crypto.subtle) exclusively for proof-of-work (SHA-256) and request signing (HMAC-SHA256) — not for encrypting user data
None. The plugin has no runtime dependencies and no vendored code.
CLIENT_SIGN_KEY is the only secret. It is injected at build time via esbuild's define mechanism and is never present in source control.
- Dev builds use a hardcoded default that matches the local dev server
- Production builds receive the key via a CI environment variable
- Purpose: HMAC request signing only — authenticates requests to the scan API, not user data
Users can verify the built main.js contains no other embedded secrets by searching for the known define patterns: __CLIENT_SIGN_KEY__ and __DEV__.