Lidarr is the music automation application in the *arr stack, managing music library downloads with the same architecture as Sonarr and Radarr. Its security profile is nearly identical to its sibling applications: Lidarr's API key provides full access to all indexer credentials, download client credentials, and notification agent secrets; GET /api/v1/indexer returns all configured indexer credentials including private tracker API keys and passkeys; GET /api/v1/downloadclient returns connection details and credentials for every configured download client (qBittorrent, SABnzbd, NZBGet); Lidarr's Import Lists can be configured with Last.fm API tokens and other music metadata service credentials stored in the configuration; and Lidarr's Custom Script notification agent executes arbitrary commands on the host when triggered. This guide covers systematic Lidarr security assessment as part of a complete *arr stack audit.
# Lidarr API — identical architecture to Sonarr and Radarr
LIDARR_URL="https://lidarr.example.com"
API_KEY="your-lidarr-api-key"
# Get system status — confirms API key works and reveals version
curl -s "${LIDARR_URL}/api/v1/system/status?apikey=${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Version: {d.get(\"version\")}')
print(f'Branch: {d.get(\"branch\")}')
print(f'OS: {d.get(\"osName\")}')
print(f'DB: {d.get(\"dbType\")}')
print(f'Runtime version: {d.get(\"runtimeVersion\")}')
" 2>/dev/null
# Get host configuration — reveals authentication settings and base URL
curl -s "${LIDARR_URL}/api/v1/config/host?apikey=${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Auth method: {d.get(\"authenticationMethod\")}')
print(f'Auth required: {d.get(\"authenticationRequired\")}')
print(f'Bind address: {d.get(\"bindAddress\")}')
print(f'Port: {d.get(\"port\")}')
print(f'URL base: {d.get(\"urlBase\")}')
" 2>/dev/null
# Enumerate artists in library — confirms what music is managed
curl -s "${LIDARR_URL}/api/v1/artist?apikey=${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
print(f'Artists in library: {len(d)}')
for a in d[:10]:
print(f' {a.get(\"artistName\")} — {a.get(\"albumCount\",0)} albums, status={a.get(\"status\")}')
" 2>/dev/null
# Lidarr indexers — same credential extraction as Sonarr and Radarr
LIDARR_URL="https://lidarr.example.com"
API_KEY="your-lidarr-api-key"
# Extract all indexer credentials
curl -s "${LIDARR_URL}/api/v1/indexer?apikey=${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
indexers = json.load(sys.stdin)
print(f'Indexers configured: {len(indexers)}')
for idx in indexers:
print(f' [{idx.get(\"id\")}] {idx.get(\"name\")} [{idx.get(\"implementation\")}]')
for field in idx.get('fields',[]):
fname = field.get('name','')
fval = field.get('value','')
if fval and isinstance(fval, str):
sensitive = ['apikey','passkey','apiKey','password','username','cookie']
if any(s.lower() in fname.lower() for s in sensitive):
print(f' CREDENTIAL {fname}: {fval}')
elif fname.lower() in ['baseurl','url']:
print(f' URL: {fval}')
" 2>/dev/null
# Lidarr download clients — qBittorrent, SABnzbd, NZBGet credentials
LIDARR_URL="https://lidarr.example.com"
API_KEY="your-lidarr-api-key"
# Extract all download client credentials
curl -s "${LIDARR_URL}/api/v1/downloadclient?apikey=${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
clients = json.load(sys.stdin)
print(f'Download clients: {len(clients)}')
for c in clients:
print(f' [{c.get(\"id\")}] {c.get(\"name\")} [{c.get(\"implementation\")}] enable={c.get(\"enable\")}')
for field in c.get('fields',[]):
fname = field.get('name','')
fval = field.get('value','')
if fval and fname.lower() in ['host','port','username','password','apikey','urlbase']:
print(f' {fname}: {fval}')
" 2>/dev/null
# Get all notification agents — Custom Script gives code execution path
curl -s "${LIDARR_URL}/api/v1/notification?apikey=${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
notifs = json.load(sys.stdin)
print(f'Notification agents: {len(notifs)}')
for n in notifs:
print(f' [{n.get(\"id\")}] {n.get(\"name\")} [{n.get(\"implementation\")}]')
for field in n.get('fields',[]):
fname = field.get('name','')
fval = field.get('value','')
if fval and isinstance(fval, str):
if fname.lower() in ['path','arguments'] or 'token' in fname.lower() or 'key' in fname.lower():
print(f' {fname}: {fval}')
" 2>/dev/null
| Security Test | Method | Risk |
|---|---|---|
| Indexer credential extraction | GET /api/v1/indexer — returns all configured indexer credentials including private tracker API keys, passkeys, and authentication details; identical architecture to Sonarr and Radarr indexer exposure | High |
| Download client credential exposure | GET /api/v1/downloadclient — returns host, port, username, password, and API key for every configured download client (qBittorrent, SABnzbd, NZBGet, Deluge, Transmission) | High |
| Custom Script notification code execution | GET /api/v1/notification to find Custom Script agents, then POST /api/v1/notification/test — triggers the configured script path on the Lidarr host; code execution with Lidarr service account privileges | Critical (requires API access) |
| Import List API key exposure | GET /api/v1/importlist — returns all configured import lists including Last.fm API token, ListenBrainz token, and credentials for any external music metadata service configured for automatic artist imports | Medium |
| Music library enumeration | GET /api/v1/artist and /api/v1/album — enumerates all artists and albums in the managed music library; reveals content interests and music collection scope | Low |
Ironimo tests Lidarr deployments for the complete credential chain including indexer API key and passkey extraction, download client credential harvesting (qBittorrent, SABnzbd, NZBGet), Custom Script notification agent code execution risk, import list API key exposure, authentication bypass when no credentials are configured, music library enumeration, and config.xml API key access.
Start free scan