fix(lyrics): secure credential request handling
This commit is contained in:
+4
-2
@@ -18,7 +18,8 @@ Install these commands on `PATH`:
|
|||||||
- `playerctl`: read and control MPRIS players.
|
- `playerctl`: read and control MPRIS players.
|
||||||
- `python3`: run the unified lyric-source adapter and dynamic lyric parser.
|
- `python3`: run the unified lyric-source adapter and dynamic lyric parser.
|
||||||
- `cp`: preserve local MPRIS artwork in the plugin cache.
|
- `cp`: preserve local MPRIS artwork in the plugin cache.
|
||||||
- `chmod`: restrict temporary credential request files to the current user.
|
- `chmod`: secures the temporary request directory before credentials are
|
||||||
|
written.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -106,7 +107,8 @@ Noctalia currently exposes these as normal string settings, not secret fields.
|
|||||||
Spotify, Apple Music, Musixmatch, and Qishui credentials may therefore be stored
|
Spotify, Apple Music, Musixmatch, and Qishui credentials may therefore be stored
|
||||||
in plaintext in Noctalia's settings. The plugin never scans browser cookies,
|
in plaintext in Noctalia's settings. The plugin never scans browser cookies,
|
||||||
never logs credential values, and deletes its temporary credential request file
|
never logs credential values, and deletes its temporary credential request file
|
||||||
as soon as the source adapter reads it.
|
as soon as the source adapter reads it. The service applies mode `0700` to the
|
||||||
|
request directory before writing any credential-bearing file.
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ local coverInFlight = nil
|
|||||||
local pluginDir = noctalia.pluginDir() or "/tmp"
|
local pluginDir = noctalia.pluginDir() or "/tmp"
|
||||||
local cacheDir = pluginDir .. "/.cache"
|
local cacheDir = pluginDir .. "/.cache"
|
||||||
noctalia.mkdirAll(cacheDir)
|
noctalia.mkdirAll(cacheDir)
|
||||||
|
local requestDir = cacheDir .. "/requests"
|
||||||
|
noctalia.mkdirAll(requestDir)
|
||||||
local krcTmp = cacheDir .. "/krc.tmp"
|
local krcTmp = cacheDir .. "/krc.tmp"
|
||||||
local lyricsSource = noctalia.getConfig("lyrics_source") or "auto"
|
local lyricsSource = noctalia.getConfig("lyrics_source") or "auto"
|
||||||
local lyricsSources = noctalia.getConfig("lyrics_sources") or {
|
local lyricsSources = noctalia.getConfig("lyrics_sources") or {
|
||||||
@@ -32,8 +34,8 @@ local currentEmbeddedLyrics = ""
|
|||||||
local currentPlayerInstance = ""
|
local currentPlayerInstance = ""
|
||||||
local currentArtUrl = ""
|
local currentArtUrl = ""
|
||||||
|
|
||||||
for _, name in ipairs(noctalia.listDir(cacheDir) or {}) do
|
for _, name in ipairs(noctalia.listDir(requestDir) or {}) do
|
||||||
if name:match("^source_request_.*%.json$") then noctalia.removeFile(cacheDir .. "/" .. name) end
|
if name:match("^source_request_.*%.json$") then noctalia.removeFile(requestDir .. "/" .. name) end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function normalizePatterns(value)
|
local function normalizePatterns(value)
|
||||||
@@ -279,7 +281,7 @@ local function fetchLyricsNetEase(track, embeddedLyrics)
|
|||||||
if not ok then klyricStr = "" end
|
if not ok then klyricStr = "" end
|
||||||
end
|
end
|
||||||
if klyricStr ~= "" then
|
if klyricStr ~= "" then
|
||||||
local py = 'python3 "' .. noctalia.pluginDir() .. '/krc_decode.py" "' .. krcTmp .. '"'
|
local py = "python3 " .. shellQuote(pluginDir .. "/krc_decode.py") .. " " .. shellQuote(krcTmp)
|
||||||
noctalia.runAsync(py, function(r3)
|
noctalia.runAsync(py, function(r3)
|
||||||
if inFlight ~= flight then return end
|
if inFlight ~= flight then return end
|
||||||
if tk ~= lastTrackKey then inFlight = nil; return end
|
if tk ~= lastTrackKey then inFlight = nil; return end
|
||||||
@@ -357,7 +359,7 @@ local function fetchLyricsNetEase(track, embeddedLyrics)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function runPy(script, cb)
|
local function runPy(script, cb)
|
||||||
local py = 'python3 "' .. dir .. "/" .. script .. '" "' .. qTmp .. '"'
|
local py = "python3 " .. shellQuote(dir .. "/" .. script) .. " " .. shellQuote(qTmp)
|
||||||
noctalia.runAsync(py, function(r)
|
noctalia.runAsync(py, function(r)
|
||||||
if inFlight ~= flight then return end
|
if inFlight ~= flight then return end
|
||||||
if tk ~= lastTrackKey then inFlight = nil; return end
|
if tk ~= lastTrackKey then inFlight = nil; return end
|
||||||
@@ -436,7 +438,7 @@ local function fetchLyricsNetEase(track, embeddedLyrics)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local requestPath = cacheDir .. "/source_request_" .. tostring(fetchGeneration) .. ".json"
|
local requestPath = requestDir .. "/source_request_" .. tostring(fetchGeneration) .. ".json"
|
||||||
local sources = normalizedSources()
|
local sources = normalizedSources()
|
||||||
local request = {
|
local request = {
|
||||||
track = track,
|
track = track,
|
||||||
@@ -470,12 +472,21 @@ local function fetchLyricsNetEase(track, embeddedLyrics)
|
|||||||
request.source = source
|
request.source = source
|
||||||
request.credentials = credentialsFor(source)
|
request.credentials = credentialsFor(source)
|
||||||
local encoded = noctalia.json.encode(request)
|
local encoded = noctalia.json.encode(request)
|
||||||
if not encoded or not noctalia.writeFile(requestPath, encoded) then
|
if not encoded then
|
||||||
trySource(index + 1)
|
trySource(index + 1)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local command = "chmod 600 " .. shellQuote(requestPath) .. " && python3 "
|
|
||||||
.. shellQuote(pluginDir .. "/lyric_sources.py") .. " " .. shellQuote(requestPath)
|
-- Secure the containing directory before any credentials are written.
|
||||||
|
local secured = noctalia.runAsync("chmod 700 " .. shellQuote(requestDir), function(chmodResult)
|
||||||
|
if inFlight ~= flight or tk ~= lastTrackKey then return end
|
||||||
|
if chmodResult.exitCode ~= 0 or not noctalia.writeFile(requestPath, encoded) then
|
||||||
|
trySource(index + 1)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local command = "python3 " .. shellQuote(pluginDir .. "/lyric_sources.py")
|
||||||
|
.. " " .. shellQuote(requestPath)
|
||||||
local started = noctalia.runAsync(command, function(result)
|
local started = noctalia.runAsync(command, function(result)
|
||||||
noctalia.removeFile(requestPath)
|
noctalia.removeFile(requestPath)
|
||||||
if inFlight ~= flight or tk ~= lastTrackKey then return end
|
if inFlight ~= flight or tk ~= lastTrackKey then return end
|
||||||
@@ -494,6 +505,10 @@ local function fetchLyricsNetEase(track, embeddedLyrics)
|
|||||||
noctalia.removeFile(requestPath)
|
noctalia.removeFile(requestPath)
|
||||||
trySource(index + 1)
|
trySource(index + 1)
|
||||||
end
|
end
|
||||||
|
end, 5000)
|
||||||
|
if not secured then
|
||||||
|
trySource(index + 1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
trySource(1)
|
trySource(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user