Sync config from arch
- hypr/apps.lua - hypr/autostart.lua - hypr/envs.lua - hypr/hyprland.lua - hypr/hyprsunset.conf - hypr/input.lua - hypr/looknfeel.lua - hypr/omasettings.lua - hypr/xdph.conf - omarchy/branding/about.txt - omarchy/branding/screensaver.txt - omarchy/extensions/omarchy-menu.jsonc - omarchy/hooks/battery-low.d/play-warning-sound.sample - omarchy/hooks/font-set.d/show-font-notification.sample - omarchy/hooks/post-boot.d/weather.sample - omarchy/hooks/post-update.d/install-voxtype.hook - omarchy/hooks/post-update.d/setup-agent.hook - omarchy/hooks/post-update.d/setup-fingerprint.hook - omarchy/hooks/post-update.d/show-update-notification.sample - omarchy/hooks/pre-refresh-pacman.d/add-custom-repo.sample - omarchy/hooks/theme-set.d/show-theme-notification.sample - omarchy/shell.json - omarchy/shell.toml - omarchy/theme.name - omarchy/themes/azure-glow/README.md - omarchy/themes/azure-glow/alacritty.toml - omarchy/themes/azure-glow/btop.theme - omarchy/themes/azure-glow/hyprland.conf - omarchy/themes/azure-glow/hyprlock.conf - omarchy/themes/azure-glow/icons.theme - … 269 more
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env node
|
||||
// Two places where data the panel did not write reaches the system: the
|
||||
// session-handoff file path, and a vault item's URI on its way to xdg-open.
|
||||
//
|
||||
// node tests/handoff-urls.test.js
|
||||
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const { execFileSync } = require("child_process")
|
||||
const os = require("os")
|
||||
const Model = {}
|
||||
new Function("exports", fs.readFileSync(path.join(__dirname, "..", "BitwardenModel.js"), "utf8")
|
||||
.replace(/^\.pragma library\s*$/m, "") + `
|
||||
exports.terminalLoginCommand = terminalLoginCommand
|
||||
exports.sessionHandoffReadCommand = sessionHandoffReadCommand
|
||||
exports.normalizeOpenableUrl = normalizeOpenableUrl
|
||||
`)(Model)
|
||||
|
||||
let pass = 0
|
||||
const failures = []
|
||||
const check = (l, ok, d) => ok ? pass++ : failures.push(`${l}\n ${d}`)
|
||||
|
||||
// --- the session handoff file never falls back to a shared directory --------
|
||||
// The key is written by a terminal and read by the panel. A world-writable
|
||||
// fallback would let another user pre-create the directory and collect it.
|
||||
|
||||
const login = Model.terminalLoginCommand("login")[2]
|
||||
const unlock = Model.terminalLoginCommand("unlock")[2]
|
||||
const read = Model.sessionHandoffReadCommand(true)[2]
|
||||
|
||||
for (const [label, script] of [["terminal login", login], ["terminal unlock", unlock], ["handoff read", read]]) {
|
||||
check(`${label} never falls back to /tmp`, !script.includes("/tmp"), script)
|
||||
check(`${label} puts the key under XDG_RUNTIME_DIR`, script.includes("XDG_RUNTIME_DIR"), script)
|
||||
}
|
||||
|
||||
check("the write side refuses to run without a runtime dir, rather than defaulting",
|
||||
/XDG_RUNTIME_DIR:\?/.test(login), login)
|
||||
check("mkdir and chmod are both checked, so a directory that is not ours aborts",
|
||||
login.includes('mkdir -p "$d" || exit 1') && login.includes('chmod 700 "$d" || exit 1'), login)
|
||||
check("the write side refuses a symlinked handoff directory",
|
||||
login.includes('[ ! -L "$d" ]'), login)
|
||||
check("umask is set before the directory is created, not after",
|
||||
login.indexOf("umask 077") < login.indexOf("mkdir"), login)
|
||||
check("the read side exits quietly instead, since it runs on every refresh",
|
||||
read.includes('[ -n "$d" ] || exit 0') && !/XDG_RUNTIME_DIR:\?/.test(read), read)
|
||||
check("the read side never follows a symlinked directory or handoff file",
|
||||
read.includes('[ ! -L "$d" ]') && read.includes('[ ! -L "$f" ]'), read)
|
||||
|
||||
// Actually run the two scripts to prove the behaviour, with `bw` stubbed.
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "qsbw-handoff-"))
|
||||
const bin = path.join(tmp, "bin")
|
||||
fs.mkdirSync(bin)
|
||||
fs.writeFileSync(path.join(bin, "bw"), "#!/usr/bin/env bash\necho STUBSESSIONKEY\n", { mode: 0o755 })
|
||||
|
||||
const runInner = (script, env) => {
|
||||
try {
|
||||
// stderr is swallowed: the no-runtime-dir case is *meant* to complain there.
|
||||
return { out: execFileSync("bash", ["-c", script], { env, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim(), code: 0 }
|
||||
} catch (e) {
|
||||
return { out: String(e.stdout || "").trim(), code: e.status }
|
||||
}
|
||||
}
|
||||
|
||||
const noRuntime = { PATH: bin + ":" + process.env.PATH, HOME: tmp }
|
||||
const withRuntime = { ...noRuntime, XDG_RUNTIME_DIR: tmp }
|
||||
|
||||
// The inner script is what the terminal runs; pull it out of the quoted wrapper.
|
||||
const innerLogin = login.match(/omarchy launch terminal -e bash -c '(.*)' \|\| alacritty/)[1].replace(/'\\''/g, "'")
|
||||
|
||||
const denied = runInner(innerLogin, noRuntime)
|
||||
check("with no runtime dir the login script exits non-zero and writes no key",
|
||||
denied.code !== 0 && !fs.existsSync(path.join(tmp, "qs-bitwarden-cli", "session-handoff")),
|
||||
`exit ${denied.code}`)
|
||||
|
||||
const readNoRuntime = runInner(read, noRuntime)
|
||||
check("with no runtime dir the read is silent and successful",
|
||||
readNoRuntime.code === 0 && readNoRuntime.out === "", JSON.stringify(readNoRuntime))
|
||||
|
||||
// A pre-existing symlink must not redirect either side to a persistent or
|
||||
// less-protected directory. XDG_RUNTIME_DIR is private, but refusing the
|
||||
// redirect also makes a stale/misconfigured runtime fail closed.
|
||||
const handoffDir = path.join(tmp, "qs-bitwarden-cli")
|
||||
const redirectedDir = path.join(tmp, "redirected")
|
||||
fs.mkdirSync(redirectedDir)
|
||||
fs.symlinkSync(redirectedDir, handoffDir)
|
||||
const symlinkWrite = runInner(innerLogin.replace(/omarchy-shell[^;]*;/, "true;").replace(/read -p[^;]*;?/, ""), withRuntime)
|
||||
check("a symlinked handoff directory makes terminal login fail closed",
|
||||
symlinkWrite.code !== 0 && !fs.existsSync(path.join(redirectedDir, "session-handoff")),
|
||||
`exit ${symlinkWrite.code}`)
|
||||
fs.writeFileSync(path.join(redirectedDir, "session-handoff"), "REDIRECTED")
|
||||
const symlinkRead = runInner(read, withRuntime)
|
||||
check("the panel does not read through a symlinked handoff directory",
|
||||
symlinkRead.out === "", JSON.stringify(symlinkRead))
|
||||
fs.unlinkSync(handoffDir)
|
||||
|
||||
// With a runtime dir, the round trip works and the directory is private.
|
||||
fs.mkdirSync(path.join(tmp, "qs-bitwarden-cli"), { recursive: true, mode: 0o755 })
|
||||
runInner(innerLogin.replace(/omarchy-shell[^;]*;/, "true;").replace(/read -p[^;]*;?/, ""), withRuntime)
|
||||
check("the handoff directory ends up private to the user",
|
||||
(fs.statSync(handoffDir).mode & 0o777) === 0o700,
|
||||
"0" + (fs.statSync(handoffDir).mode & 0o777).toString(8))
|
||||
|
||||
const roundTrip = runInner(read, withRuntime)
|
||||
check("the panel reads the key back", roundTrip.out === "STUBSESSIONKEY", JSON.stringify(roundTrip))
|
||||
const secondRead = runInner(read, withRuntime)
|
||||
check("and the key is consumed, so a second read gets nothing",
|
||||
secondRead.out === "", JSON.stringify(secondRead))
|
||||
|
||||
fs.rmSync(tmp, { recursive: true, force: true })
|
||||
|
||||
// --- only web links are handed to xdg-open ----------------------------------
|
||||
// A vault item's URI is data, and an org-shared item can be written by others.
|
||||
|
||||
const opens = (u) => Model.normalizeOpenableUrl(u)
|
||||
|
||||
for (const [input, expected] of [
|
||||
["https://example.com", "https://example.com"],
|
||||
["http://example.com/login", "http://example.com/login"],
|
||||
["HTTPS://Example.COM", "HTTPS://Example.COM"],
|
||||
["example.com", "https://example.com"],
|
||||
["example.com:8443/login", "https://example.com:8443/login"],
|
||||
["localhost:3000", "https://localhost:3000"],
|
||||
["192.168.1.10:8006", "https://192.168.1.10:8006"]
|
||||
]) {
|
||||
const r = opens(input)
|
||||
check(`${input} opens as ${expected}`, r.ok && r.url === expected, JSON.stringify(r))
|
||||
}
|
||||
|
||||
for (const [input, scheme] of [
|
||||
["file:///etc/passwd", "file"],
|
||||
["ftp://files.example.com", "ftp"],
|
||||
["javascript:alert(1)", "javascript"],
|
||||
["data:text/html,<script>x</script>", "data"],
|
||||
["mailto:someone@example.com", "mailto"],
|
||||
["vnc://10.0.0.1", "vnc"],
|
||||
["custom-app-handler://do-something", "custom-app-handler"]
|
||||
]) {
|
||||
const r = opens(input)
|
||||
check(`${input} is refused`, !r.ok && r.scheme === scheme, JSON.stringify(r))
|
||||
}
|
||||
|
||||
check("an empty URI is refused without naming a scheme",
|
||||
!opens("").ok && opens("").scheme === "", JSON.stringify(opens("")))
|
||||
check("whitespace is trimmed rather than https-ified",
|
||||
opens(" https://example.com ").url === "https://example.com", JSON.stringify(opens(" https://example.com ")))
|
||||
check("an ambiguous backslash web URL is refused instead of parsed differently by the browser",
|
||||
!opens("https://evil.example\\@trusted.example").ok
|
||||
&& opens("https://evil.example\\@trusted.example").reason === "ambiguous",
|
||||
JSON.stringify(opens("https://evil.example\\@trusted.example")))
|
||||
|
||||
console.log(`${pass} passed, ${failures.length} failed`)
|
||||
if (failures.length) { console.error("\nFAILURES:\n " + failures.join("\n ")); process.exit(1) }
|
||||
Reference in New Issue
Block a user