fix(pairing): warn that pairing links grant daemon access (#3734)

A pairing offer remains valid across daemon restarts, so remove the incorrect rotation guidance and warn users where the durable credential is displayed.
This commit is contained in:
Mohamed Boudra
2026-08-23 12:32:21 +02:00
committed by GitHub
parent 5123b23f41
commit 8d92e1cc17
14 changed files with 27 additions and 3 deletions
@@ -113,6 +113,11 @@ export async function enableRelayAndExpectOffer(page: Page): Promise<void> {
export async function expectPairingOffer(page: Page): Promise<void> {
await expect(page.getByRole("img", { name: "Pairing QR code" })).toBeVisible();
await expect(page.getByRole("textbox", { name: "Pairing link" })).toHaveValue(/#offer=/);
await expect(
page.getByRole("alert").filter({
hasText: "Treat this pairing link like a password. Anyone with it can access this daemon.",
}),
).toBeVisible();
await expect(page.getByRole("button", { name: "Copy", exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: "Enable relay", exact: true })).toHaveCount(0);
}
@@ -262,6 +262,7 @@ function PairingOffer(props: PairDeviceBodyProps & { offer: { url: string } }) {
{props.copied ? t("pairing.device.copied") : t("pairing.device.copy")}
</Button>
</View>
<Alert variant="warning" description={t("pairing.device.securityWarning")} />
</View>
);
}
+2
View File
@@ -1630,6 +1630,8 @@ export const ar: TranslationResources = {
updateRequired: "حدّث المضيف لتمكين التتابع من Paseo Desktop.",
unavailable: "عرض الاقتران غير متاح.",
hint: "قم بمسح رمز QR هذا باستخدام Paseo على هاتفك، أو انسخ الرابط أدناه.",
securityWarning:
"تعامل مع رابط الاقتران هذا ككلمة مرور. يمكن لأي شخص يملكه الوصول إلى هذا البرنامج الخفي.",
qrUnavailable: "رمز QR غير متاح.",
qrAccessibility: "رمز QR للإقران",
retry: "أعد المحاولة",
+2
View File
@@ -1640,6 +1640,8 @@ export const en = {
updateRequired: "Update the host to enable relay from Paseo Desktop.",
unavailable: "Pairing offer unavailable.",
hint: "Scan this QR code with Paseo on your phone, or copy the link below.",
securityWarning:
"Treat this pairing link like a password. Anyone with it can access this daemon.",
qrUnavailable: "QR code unavailable.",
qrAccessibility: "Pairing QR code",
retry: "Retry",
+2
View File
@@ -1676,6 +1676,8 @@ export const es: TranslationResources = {
updateRequired: "Actualice el host para habilitar el relé desde Paseo Desktop.",
unavailable: "Oferta de maridaje no disponible.",
hint: "Escanee este códigoQRconPaseoen su teléfono o copie el enlace a continuación.",
securityWarning:
"Trata este enlace de emparejamiento como una contraseña. Cualquiera que lo tenga puede acceder a este daemon.",
qrUnavailable: "CódigoQRno disponible.",
qrAccessibility: "Código QR de emparejamiento",
retry: "Rever",
+2
View File
@@ -1680,6 +1680,8 @@ export const fr: TranslationResources = {
updateRequired: "Mettez à jour l’hôte pour activer le relais depuis Paseo Desktop.",
unavailable: "Offre de jumelage indisponible.",
hint: "Scannez ce codeQRavecPaseosur votre téléphone ou copiez le lien ci-dessous.",
securityWarning:
"Traitez ce lien d’association comme un mot de passe. Toute personne qui le possède peut accéder à ce daemon.",
qrUnavailable: "CodeQRindisponible.",
qrAccessibility: "Code QR de couplage",
retry: "Réessayer",
+2
View File
@@ -1647,6 +1647,8 @@ export const ja: TranslationResources = {
updateRequired: "Paseo Desktop からリレーを有効にするにはホストを更新してください。",
unavailable: "ペアリングオファーが利用できません。",
hint: "スマートフォンのPaseoでこのQRコードをスキャンするか、以下のリンクをコピーしてください。",
securityWarning:
"このペアリングリンクはパスワードと同様に扱ってください。リンクを知っている人は誰でもこのデーモンにアクセスできます。",
qrUnavailable: "QRコードが利用できません。",
qrAccessibility: "ペアリング QR コード",
retry: "再試行",
+2
View File
@@ -1640,6 +1640,8 @@ export const ko: TranslationResources = {
updateRequired: "Paseo 데스크톱에서 릴레이를 활성화하려면 호스트를 업데이트하세요.",
unavailable: "페어링 정보를 사용할 수 없습니다.",
hint: "휴대폰의 Paseo로 이 QR 코드를 스캔하거나 아래 링크를 복사하세요.",
securityWarning:
"이 페어링 링크는 비밀번호처럼 취급하세요. 링크를 가진 사람은 누구나 이 데몬에 접근할 수 있습니다.",
qrUnavailable: "QR 코드를 사용할 수 없습니다.",
qrAccessibility: "페어링 QR 코드",
retry: "다시 시도",
+2
View File
@@ -1662,6 +1662,8 @@ export const ptBR: TranslationResources = {
updateRequired: "Atualize o host para ativar o relay pelo Paseo Desktop.",
unavailable: "Oferta de pareamento indisponível.",
hint: "Escaneie este QR code com o Paseo no seu celular ou copie o link abaixo.",
securityWarning:
"Trate este link de pareamento como uma senha. Qualquer pessoa com o link pode acessar este daemon.",
qrUnavailable: "QR code indisponível.",
qrAccessibility: "Código QR de pareamento",
retry: "Tentar novamente",
+2
View File
@@ -1660,6 +1660,8 @@ export const ru: TranslationResources = {
updateRequired: "Обновите хост, чтобы включить ретранслятор из Paseo Desktop.",
unavailable: "Данные для сопряжения недоступны.",
hint: "Отсканируйте этот QR-код с помощью Paseo на телефоне или скопируйте ссылку ниже.",
securityWarning:
"Обращайтесь с этой ссылкой для сопряжения как с паролем. Любой, у кого она есть, может получить доступ к этому демону.",
qrUnavailable: "QR-код недоступен.",
qrAccessibility: "QR-код сопряжения",
retry: "Повторить попытку",
+1
View File
@@ -1610,6 +1610,7 @@ export const zhCN: TranslationResources = {
updateRequired: "请更新主机,以便从 Paseo Desktop 启用中继。",
unavailable: "配对 offer 不可用。",
hint: "用手机上的 Paseo 扫描此二维码,或复制下方链接。",
securityWarning: "请像保管密码一样保管此配对链接。任何获得此链接的人都可以访问此守护进程。",
qrUnavailable: "二维码不可用。",
qrAccessibility: "配对二维码",
retry: "重试",
+3
View File
@@ -10,6 +10,9 @@ describe("formatPairingInstructions", () => {
expect(output).toContain(QR);
expect(output.split("\n")).toContain(URL);
expect(output).toContain(
"Treat this pairing link like a password. Anyone with it can access this daemon.",
);
});
it("does not print a QR that would reach the terminal edge", () => {
+1 -1
View File
@@ -33,5 +33,5 @@ function formatQr(qr: string | null, columns: number | undefined): string {
}
export function formatPairingInstructions({ url, qr, columns }: PairingInstructions): string {
return `\nScan to pair:\n${formatQr(qr, columns)}\n\nPairing link:\n${url}\n`;
return `\nScan to pair:\n${formatQr(qr, columns)}\n\nPairing link:\n${url}\n\nTreat this pairing link like a password. Anyone with it can access this daemon.\n`;
}
-2
View File
@@ -52,8 +52,6 @@ The daemon requires a valid cryptographic handshake before processing any comman
The QR code or pairing link is the trust anchor. It contains the daemon's public key, which is required to establish the encrypted connection. Treat it like a password, don't share it publicly.
If you believe a pairing offer has been compromised, restart the daemon to generate a new session ID and rotate the relay pairing.
## Direct connections
By default, the daemon listens on `127.0.0.1:6767` (localhost only). This is safe for local CLI usage but not reachable from your phone or other devices.