fix(lyrics): avoid shell injection in NetEase requests

This commit is contained in:
h465855hgg
2026-07-17 21:57:02 +08:00
parent 6b96da5a0c
commit 6dacdc7a78
4 changed files with 27 additions and 26 deletions
+5 -5
View File
@@ -12,7 +12,7 @@ highlighting, animated line changes, and configurable online or local sources.
## Requirements
Install `playerctl`, `python3`, `curl`, and `cp` on `PATH`. The active media
Install `playerctl`, `python3`, and `cp` on `PATH`. The active media
player must expose MPRIS metadata for automatic track and playback detection.
Noctalia installs the plugin files; it does not install system packages for you.
@@ -103,7 +103,7 @@ HTTP mode contacts only the configured endpoint. The plugin never reads browser
cookies or player credentials.
The service runs `playerctl` to read and control MPRIS playback, `python3` for the
LRCLIB helper and dynamic-lyric parser, `curl` for the public NetEase API, and
`cp` to preserve temporary local cover files. Query scratch files and downloaded
cover images are written inside the plugin runtime directory. Remote code is
never downloaded or executed.
LRCLIB helper and dynamic-lyric parser, and `cp` to preserve temporary local cover
files. Public NetEase requests use Noctalia's HTTP API. Query scratch files and
downloaded cover images are written inside the plugin runtime directory. Remote
code is never downloaded or executed.
+13 -10
View File
@@ -82,19 +82,18 @@ local function fetchLyricsNetEase(track, embeddedLyrics)
local function tryFetch(query, fallback)
local searchUrl = "https://music.163.com/api/search/get?type=1&s=" .. noctalia.string.urlEncode(query) .. "&limit=5"
local cmd = 'curl -s --max-time 10 --connect-timeout 5 "' .. searchUrl .. '" -H "Referer: https://music.163.com"'
noctalia.runAsync(cmd, function(r1)
noctalia.http({ url = searchUrl, headers = { "Referer: https://music.163.com" } }, function(r1)
if not inFlight then return end
if tk ~= lastTrackKey then inFlight = nil; return end
if r1.exitCode ~= 0 or not r1.stdout or #r1.stdout == 0 then
if not r1.ok or r1.status < 200 or r1.status >= 300 or not r1.body or #r1.body == 0 then
if fallback then fallback()
else inFlight = nil; noctalia.state.set("lyrics", nil) end
return
end
local data = noctalia.json.decode(r1.stdout)
local data = noctalia.json.decode(r1.body)
if not data or not data.result or not data.result.songs or #data.result.songs == 0 then
if fallback then fallback()
else inFlight = nil; noctalia.state.set("lyrics", nil) end
@@ -120,17 +119,21 @@ local function fetchLyricsNetEase(track, embeddedLyrics)
bestMatch = data.result.songs[1]
end
local songId = bestMatch.id
local lyricUrl = "https://music.163.com/api/song/lyric?id=" .. songId .. "&lv=1&kv=1&tv=-1"
local lCmd = 'curl -s --max-time 10 --connect-timeout 5 "' .. lyricUrl .. '" -H "Referer: https://music.163.com"'
local songId = tostring(bestMatch.id or "")
if not songId:match("^%d+$") then
if fallback then fallback()
else inFlight = nil; noctalia.state.set("lyrics", nil) end
return
end
local lyricUrl = "https://music.163.com/api/song/lyric?id=" .. noctalia.string.urlEncode(songId) .. "&lv=1&kv=1&tv=-1"
noctalia.runAsync(lCmd, function(r2)
noctalia.http({ url = lyricUrl, headers = { "Referer: https://music.163.com" } }, function(r2)
if not inFlight then return end
if tk ~= lastTrackKey then inFlight = nil; return end
local lyrics = nil
if r2.exitCode == 0 and r2.stdout and #r2.stdout > 0 then
local ldata = noctalia.json.decode(r2.stdout)
if r2.ok and r2.status >= 200 and r2.status < 300 and r2.body and #r2.body > 0 then
local ldata = noctalia.json.decode(r2.body)
local klyricStr = ""
if ldata and ldata.klyric then
local k = ldata.klyric
+1 -1
View File
@@ -4,7 +4,7 @@ version = "1.1.0"
plugin_api = 3
author = "h465855hgg"
license = "MIT"
dependencies = ["playerctl", "python3", "curl", "cp"]
dependencies = ["playerctl", "python3", "cp"]
tags = ["bar", "service", "music", "media", "animation"]
icon = "music"
description = "Synchronized lyrics with karaoke highlighting, animated transitions, and flexible lyric sources."
+8 -10
View File
@@ -45,11 +45,10 @@ need_command() {
MISSING_COMMANDS=""
need_command playerctl
need_command python3
need_command curl
need_command cp
if [ -z "$MISSING_COMMANDS" ]; then
echo "All runtime commands are installed: playerctl python3 curl cp"
echo "All runtime commands are installed: playerctl python3 cp"
exit 0
fi
@@ -90,28 +89,28 @@ PM="$(detect_pm)"
case "$PM" in
apt)
INSTALL_CMD="$SUDO apt-get update && $SUDO apt-get install -y playerctl python3 curl coreutils"
INSTALL_CMD="$SUDO apt-get update && $SUDO apt-get install -y playerctl python3 coreutils"
;;
dnf)
INSTALL_CMD="$SUDO dnf install -y playerctl python3 curl coreutils"
INSTALL_CMD="$SUDO dnf install -y playerctl python3 coreutils"
;;
pacman)
INSTALL_CMD="$SUDO pacman -S --needed playerctl python curl coreutils"
INSTALL_CMD="$SUDO pacman -S --needed playerctl python coreutils"
;;
zypper)
INSTALL_CMD="$SUDO zypper install -y playerctl python3 curl coreutils"
INSTALL_CMD="$SUDO zypper install -y playerctl python3 coreutils"
;;
apk)
INSTALL_CMD="$SUDO apk add playerctl python3 curl coreutils"
INSTALL_CMD="$SUDO apk add playerctl python3 coreutils"
;;
xbps)
INSTALL_CMD="$SUDO xbps-install -Sy playerctl python3 curl coreutils"
INSTALL_CMD="$SUDO xbps-install -Sy playerctl python3 coreutils"
;;
*)
cat >&2 <<'EOF'
Could not detect a supported package manager.
Install these packages manually with your distribution package manager:
playerctl python3 curl coreutils
playerctl python3 coreutils
EOF
exit 1
;;
@@ -138,7 +137,6 @@ sh -c "$INSTALL_CMD"
MISSING_COMMANDS=""
need_command playerctl
need_command python3
need_command curl
need_command cp
if [ -n "$MISSING_COMMANDS" ]; then