fix: reveal mode broken + missing await + select rendering — v2.0.10

Bug 1 - Reveal mode not working (all browsers):
sessionSubstitutions stored "ademo demo" (full name) as key, but
buildRevealPairs looked up "ademo" and "demo" individually. No match,
no reveal pairs, reveal did nothing. Fixed by also storing individual
words from multi-word replacements so both "ademo demo" AND "ademo"
AND "demo" are in the map.

Bug 2 - Missing await on _handleDecryptedMeta (sync.js):
Two call sites returned the Promise instead of the resolved value.
Downstream code checking decResult.data got undefined. Added await.

Bug 3 - Profile selector broken by safeHTML (popup.js):
DOMParser.parseFromString wraps content in <html><body> which
mangles <option> elements when moved to a <select>. Replaced with
new Option() DOM API which creates proper option elements.

https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
This commit is contained in:
Claude
2026-03-27 12:41:47 +00:00
parent 97e37e65ac
commit 85fbc6c4d0
7 changed files with 33 additions and 13 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Silent Send",
"version": "2.0.9",
"version": "2.0.10",
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
"browser_specific_settings": {
"gecko": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "Silent Send",
"version": "2.0.9",
"version": "2.0.10",
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
"permissions": [
"storage",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "silent-send",
"version": "2.0.9",
"version": "2.0.10",
"private": true,
"license": "BSL-1.1",
"description": "Browser extension that substitutes personal data before sending to AI services",
+16 -2
View File
@@ -784,11 +784,25 @@
// Record what was actually substituted so reveal knows
for (const r of replacements) {
if (r.replaced && r.original) {
// Store with lowercase key for lookup, but preserve original case
// Store full replacement with lowercase key for lookup
sessionSubstitutions.set(r.replaced.toLowerCase(), {
original: r.original,
replaced: r.replaced, // preserve original case
replaced: r.replaced,
});
// Also store individual words so reveal pairs match identity entries
// e.g. "Ademo Demo" → store "ademo" and "demo" separately
const replacedWords = r.replaced.split(/\s+/);
const originalWords = r.original.split(/\s+/);
if (replacedWords.length > 1) {
for (let i = 0; i < replacedWords.length; i++) {
if (replacedWords[i] && originalWords[i]) {
sessionSubstitutions.set(replacedWords[i].toLowerCase(), {
original: originalWords[i],
replaced: replacedWords[i],
});
}
}
}
}
}
+2 -2
View File
@@ -472,7 +472,7 @@ const SilentSendSync = {
if (!keyInfo) return { data: null, decrypted: false, needsAuth: true };
try {
const decrypted = await SilentSendCrypto.decryptWithKey(data.payload, keyInfo.key);
return this._handleDecryptedMeta(decrypted);
return await this._handleDecryptedMeta(decrypted);
} catch {
return { data: null, decrypted: false, needsAuth: true };
}
@@ -484,7 +484,7 @@ const SilentSendSync = {
if (tempKeyStore) {
try {
const decrypted = await SilentSendCrypto.decryptWithKey(data.payload, tempKeyStore.key);
return this._handleDecryptedMeta(decrypted);
return await this._handleDecryptedMeta(decrypted);
} catch { /* wrong key, fall through to prompt */ }
}
+1 -1
View File
@@ -591,7 +591,7 @@
</section>
<footer>
<p>Silent Send v2.0.9</p>
<p>Silent Send v2.0.10</p>
</footer>
</div>
+11 -5
View File
@@ -338,11 +338,17 @@ async function showLockedUI() {
// --- Profiles ---
function renderProfileSelector() {
const select = $('#profileSelect');
safeHTML(select, profiles.map(p =>
`<option value="${p.id}" ${p.id === currentProfileId ? 'selected' : ''}>` +
`${escapeHtml(p.name)}${p.active ? '' : ' (off)'}` +
`</option>`
).join(''));
// Build options via DOM API — safeHTML + DOMParser mangles <option> elements
select.replaceChildren();
for (const p of profiles) {
const opt = new Option(
`${p.name}${p.active ? '' : ' (off)'}`,
p.id,
false,
p.id === currentProfileId
);
select.appendChild(opt);
}
const profile = profiles.find(p => p.id === currentProfileId);
$('#profileActive').checked = profile?.active ?? true;