Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion scripts/verify-ui.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ try {
check('encryption lands on the result page', onResult)

const shareLinkGone = await page2.evaluate(() => !window.__sb.text().includes('Copy Share Link'))
check('Copy Share Link button is removed', shareLinkGone)
check('share link not offered for local-only encryption (no pastebin link)', shareLinkGone)

await page2.evaluate(() => window.__sb.clickButton('Decrypt'))
const resultDecryptDialog = await page2.evaluate(() => window.__sb.waitForText('Decryption Key'))
Expand Down Expand Up @@ -463,9 +463,73 @@ try {
await shot(page2, '9-open-plain-paste')
check('opening a plain paste loads its content into the editor', opened)
}

} else {
console.log('⏭ PASTEBIN_API_KEY not set — skipping the live paste-link checks')
}

// ── 6. Copy Share Link → securebin.org viewer URL (offline via seeded history)
await sw.evaluate(async (encText) => {
await chrome.storage.local.set({
history: [{
id: 'sharetest1',
action: 'Encrypt to Pastebin',
pastebinLink: 'https://pastebin.com/SHARE123',
key: 'share-verify-pass',
encText,
encMode: 'AES-GCM',
keyLength: 16,
date: Date.now(),
title: 'Share Test 8814',
format: 'text',
privacy: '1',
expiry: 'N',
}],
})
}, ciphertext)

const page3 = await browser.newPage()
await page3.goto('https://pastebin.com/doc_api', { waitUntil: 'networkidle2' })
await page3.evaluate(pageHelpers)
const sl = await injectAndSend('https://pastebin.com/doc_api', { type: 'SB_OPEN' })
check('panel opens for the share-link flow', !sl.error, sl.error)

await page3.evaluate(async () => {
const start = Date.now()
while (Date.now() - start < 4000) {
const nav = window.__sb.buttons().find(b => b.title === 'Pastes')
if (nav) { nav.click(); return }
await new Promise(r => setTimeout(r, 100))
}
})
// History rows are labeled by their pastebin link
const historyShown = await page3.evaluate(() => window.__sb.waitForText('SHARE123'))
check('seeded history item appears', historyShown)

await page3.evaluate(() => {
const leaf = [...(window.__sb.root()?.querySelectorAll('*') ?? [])]
.reverse()
.find(el => el.childElementCount === 0 && (el.textContent ?? '').includes('SHARE123'))
leaf?.click()
})
const shareBtnShown = await page3.evaluate(() => window.__sb.waitForText('Copy Share Link'))
check('result page offers Copy Share Link', shareBtnShown)

try {
await browser.defaultBrowserContext().overridePermissions('https://pastebin.com', ['clipboard-read', 'clipboard-write', 'clipboard-sanitized-write'])
} catch {
await browser.defaultBrowserContext().overridePermissions('https://pastebin.com', ['clipboard-read', 'clipboard-write'])
}
await page3.bringToFront()
await page3.evaluate(() => window.__sb.clickButton('Copy Share Link'))
await new Promise(r => setTimeout(r, 400))
const copied = await page3.evaluate(() => navigator.clipboard.readText().catch(() => ''))
await shot(page3, '10-share-link')
check(
'share link is the securebin.org viewer URL with the key in the fragment',
copied === 'https://securebin.org/SHARE123#key=share-verify-pass',
copied || 'clipboard empty',
)
} finally {
await browser.close()
}
Expand Down
34 changes: 34 additions & 0 deletions src/routes/Result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default function Result() {
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)
const [deleting, setDeleting] = useState(false)
const [deleteError, setDeleteError] = useState('')
const [shareCopied, setShareCopied] = useState(false)
const [contentCopied, setContentCopied] = useState(false)
const [postDropdownOpen, setPostDropdownOpen] = useState(false)

Expand Down Expand Up @@ -90,6 +91,18 @@ export default function Result() {
navigate('/home')
}

const handleCopyShareLink = async () => {
if (!hasPastebin || !item.key) return
// Opens in the securebin.org viewer (same path as pastebin.com); the
// passkey rides in the fragment, which browsers never send to servers —
// decryption happens in the recipient's browser
const pasteKey = extractPasteKey(item.pastebinLink)
const shareLink = `https://securebin.org/${pasteKey}#key=${encodeURIComponent(item.key)}`
await navigator.clipboard.writeText(shareLink)
setShareCopied(true)
setTimeout(() => setShareCopied(false), 2000)
}

// Load draft content into editor with the chosen action, then navigate to editor
const handleOpenInEditor = (action?: EditorAction) => {
const text = item.encText ?? ''
Expand Down Expand Up @@ -202,6 +215,27 @@ export default function Result() {
{/* Passkey */}
{item.key && <CopyBox label="Passkey" value={item.key} masked />}

{/* Share link — one URL that fetches AND decrypts in the recipient's browser */}
{hasPastebin && item.key && (
<div className="space-y-1">
<button
onClick={handleCopyShareLink}
className={cn(
'w-full flex items-center justify-center gap-2 py-2.5 rounded-xl border text-sm font-medium transition-all',
shareCopied
? 'border-success/30 bg-success/5 text-success'
: 'border-border text-text-secondary hover:bg-surface-hover',
)}
>
{shareCopied ? <Check size={14} /> : <Copy size={14} />}
{shareCopied ? 'Share link copied!' : 'Copy Share Link'}
</button>
<p className="text-[11px] text-text-muted/70 leading-snug text-center">
Link + passkey in one URL — decrypts on securebin.org, no extension needed. Anyone with the link can read the paste.
</p>
</div>
)}

{/* Actions */}
<div className="pt-2 space-y-2">
<p className="text-[11px] font-semibold text-text-muted uppercase tracking-[0.06em]">Actions</p>
Expand Down