Files
Panama/config/dot/quickshell/modules/settings/SettingsButton.qml
T
Gabriel Brown 8e93f08977 Show what the keyring holds, without showing what it holds
Managing a stored credential meant installing Seahorse. The keyring
rows on Privacy could say whether it was locked and nothing about what
was in it.

Four rules, each pinned by a contract, because each is a way this could
leak the thing it exists to protect:

Listing never reads values. Enumerating reports labels and attributes;
it does not ask the keyring to hand over what it is protecting.

A secret never reaches a command line. /proc makes argv readable by
every process on this machine, so a password passed as an argument is
published to all of them. The helper reads the value in process and
writes it to wl-copy on stdin.

A secret never reaches an error message, a log, or a QML property. An
exception raised while holding a password does not get to choose what
text is printed, so the clipboard tool's stderr is discarded rather
than echoed.

Forgetting one is irreversible, so the first press asks and the second
does it, and the confirming button is the only one wearing danger.

The list is collapsed until asked for: opening Privacy should not
enumerate someone's passwords as a side effect. A copied value clears
itself about a minute later, but only if the clipboard still holds it --
the guard compares a SHA-256, so the waiting process never has the
password.

Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
2026-08-19 11:14:32 -04:00

51 lines
1.6 KiB
QML

import QtQuick
import qs.config
Rectangle {
id: root
property string text: ""
// "normal", "accent", or "danger". Danger is for an action that cannot be
// undone -- it never initiates one on a single press, it marks the press
// that confirms it, so the confirming button cannot be mistaken for the
// Cancel sitting next to it.
property string tone: "normal"
property bool enabled: true
signal clicked
implicitWidth: label.implicitWidth + 22
implicitHeight: 31
radius: 9
opacity: enabled ? 1 : 0.45
color: {
if (tone === "accent")
return mouse.containsMouse ? Theme.mix(Theme.accent, Theme.fg, 0.12) : Theme.accent;
if (tone === "danger")
return mouse.containsMouse ? Theme.alpha(Theme.danger, 0.22) : Theme.alpha(Theme.danger, 0.12);
return mouse.containsMouse ? Theme.alpha(Theme.fg, 0.13) : Theme.alpha(Theme.fg, 0.075);
}
border.width: tone === "accent" ? 0 : 1
border.color: tone === "danger" ? Theme.alpha(Theme.danger, 0.45) : Theme.alpha(Theme.fg, 0.08)
Text {
id: label
anchors.centerIn: parent
text: root.text
color: root.tone === "accent"
? Theme.bgDark
: (root.tone === "danger" ? Theme.danger : Theme.fg)
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
}
MouseArea {
id: mouse
anchors.fill: parent
enabled: root.enabled
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.clicked()
}
}