Merge pull request #74 from outis1one/claude/review-silent-send-gHgCS
Claude/review silent send g hg cs
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Silent Send",
|
"name": "Silent Send",
|
||||||
"version": "0.9.42",
|
"version": "0.9.43",
|
||||||
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
|
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Silent Send",
|
"name": "Silent Send",
|
||||||
"version": "0.9.42",
|
"version": "0.9.43",
|
||||||
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
|
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"storage",
|
"storage",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "silent-send",
|
"name": "silent-send",
|
||||||
"version": "0.9.42",
|
"version": "0.9.43",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "Browser extension that substitutes personal data before sending to AI services",
|
"description": "Browser extension that substitutes personal data before sending to AI services",
|
||||||
|
|||||||
+69
-61
@@ -50,7 +50,7 @@
|
|||||||
const sorted = [...maps].sort((a, b) => b.real.length - a.real.length);
|
const sorted = [...maps].sort((a, b) => b.real.length - a.real.length);
|
||||||
|
|
||||||
for (const m of sorted) {
|
for (const m of sorted) {
|
||||||
if (!m.enabled || !m.real || !m.substitute) continue;
|
if (!m.enabled || !m.real?.trim() || !m.substitute?.trim()) continue;
|
||||||
const escaped = esc(m.real);
|
const escaped = esc(m.real);
|
||||||
const regex = new RegExp(escaped, m.caseSensitive ? 'g' : 'gi');
|
const regex = new RegExp(escaped, m.caseSensitive ? 'g' : 'gi');
|
||||||
let match;
|
let match;
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
let result = text;
|
let result = text;
|
||||||
const sorted = [...maps].sort((a, b) => b.substitute.length - a.substitute.length);
|
const sorted = [...maps].sort((a, b) => b.substitute.length - a.substitute.length);
|
||||||
for (const m of sorted) {
|
for (const m of sorted) {
|
||||||
if (!m.enabled || !m.real || !m.substitute) continue;
|
if (!m.enabled || !m.real?.trim() || !m.substitute?.trim()) continue;
|
||||||
const escaped = esc(m.substitute);
|
const escaped = esc(m.substitute);
|
||||||
const regex = new RegExp(escaped, m.caseSensitive ? 'g' : 'gi');
|
const regex = new RegExp(escaped, m.caseSensitive ? 'g' : 'gi');
|
||||||
result = result.replace(regex, m.real);
|
result = result.replace(regex, m.real);
|
||||||
@@ -825,67 +825,74 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.__ssInterceptFetch = async function (url, options) {
|
window.__ssInterceptFetch = async function (url, options) {
|
||||||
if (!settings.enabled || !hasSubstitutions()) {
|
// Top-level guard: any unhandled error must never break the original request.
|
||||||
|
try {
|
||||||
|
if (!settings.enabled || !hasSubstitutions()) {
|
||||||
|
return originalFetch.call(this, url, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlStr = typeof url === 'string' ? url : url?.url || '';
|
||||||
|
const method = (options?.method || 'GET').toUpperCase();
|
||||||
|
|
||||||
|
// Only intercept POST/PUT/PATCH
|
||||||
|
if (
|
||||||
|
(method === 'POST' || method === 'PUT' || method === 'PATCH') &&
|
||||||
|
options?.body && !shouldSkipUrl(urlStr)
|
||||||
|
) {
|
||||||
|
// FormData body — scan file uploads via DocumentScanner
|
||||||
|
if (options.body instanceof FormData && typeof globalThis.DocumentScanner !== 'undefined') {
|
||||||
|
try {
|
||||||
|
const scannedForm = await scanFormData(options.body);
|
||||||
|
if (scannedForm) {
|
||||||
|
options = { ...options, body: scannedForm.formData };
|
||||||
|
if (scannedForm.replacements.length > 0) {
|
||||||
|
notifySubstitutions(scannedForm.replacements);
|
||||||
|
console.log(
|
||||||
|
`[Silent Send] Substituted ${scannedForm.replacements.length} value(s) in file upload to ${urlStr}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[Silent Send] FormData scan failed:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String body — JSON or raw text
|
||||||
|
if (typeof options.body === 'string') {
|
||||||
|
try {
|
||||||
|
// Try JSON
|
||||||
|
const body = JSON.parse(options.body);
|
||||||
|
const { modified, replacements } = processBody(body);
|
||||||
|
|
||||||
|
if (modified) {
|
||||||
|
options = { ...options, body: JSON.stringify(body) };
|
||||||
|
notifySubstitutions(replacements);
|
||||||
|
console.log(
|
||||||
|
`[Silent Send] Substituted ${replacements.length} value(s) in ${urlStr}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Not JSON — try raw string substitution (form data, etc.)
|
||||||
|
if (options.body.length > MIN_STRING_LENGTH) {
|
||||||
|
const result = substituteAll(options.body);
|
||||||
|
if (result.modified) {
|
||||||
|
options = { ...options, body: result.text };
|
||||||
|
notifySubstitutions(result.replacements);
|
||||||
|
console.log(
|
||||||
|
`[Silent Send] Substituted ${result.replacements.length} value(s) in form body`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return originalFetch.call(this, url, options);
|
||||||
|
} catch (e) {
|
||||||
|
// Something went wrong in Silent Send — never block the original request
|
||||||
|
console.warn('[Silent Send] Fetch interceptor error, passing through:', e);
|
||||||
return originalFetch.call(this, url, options);
|
return originalFetch.call(this, url, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
const urlStr = typeof url === 'string' ? url : url?.url || '';
|
|
||||||
const method = (options?.method || 'GET').toUpperCase();
|
|
||||||
|
|
||||||
// Only intercept POST/PUT/PATCH
|
|
||||||
if (
|
|
||||||
(method === 'POST' || method === 'PUT' || method === 'PATCH') &&
|
|
||||||
options?.body && !shouldSkipUrl(urlStr)
|
|
||||||
) {
|
|
||||||
// FormData body — scan file uploads via DocumentScanner
|
|
||||||
if (options.body instanceof FormData && typeof globalThis.DocumentScanner !== 'undefined') {
|
|
||||||
try {
|
|
||||||
const scannedForm = await scanFormData(options.body);
|
|
||||||
if (scannedForm) {
|
|
||||||
options = { ...options, body: scannedForm.formData };
|
|
||||||
if (scannedForm.replacements.length > 0) {
|
|
||||||
notifySubstitutions(scannedForm.replacements);
|
|
||||||
console.log(
|
|
||||||
`[Silent Send] Substituted ${scannedForm.replacements.length} value(s) in file upload to ${urlStr}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('[Silent Send] FormData scan failed:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String body — JSON or raw text
|
|
||||||
if (typeof options.body === 'string') {
|
|
||||||
try {
|
|
||||||
// Try JSON
|
|
||||||
const body = JSON.parse(options.body);
|
|
||||||
const { modified, replacements } = processBody(body);
|
|
||||||
|
|
||||||
if (modified) {
|
|
||||||
options = { ...options, body: JSON.stringify(body) };
|
|
||||||
notifySubstitutions(replacements);
|
|
||||||
console.log(
|
|
||||||
`[Silent Send] Substituted ${replacements.length} value(s) in ${urlStr}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Not JSON — try raw string substitution (form data, etc.)
|
|
||||||
if (options.body.length > MIN_STRING_LENGTH) {
|
|
||||||
const result = substituteAll(options.body);
|
|
||||||
if (result.modified) {
|
|
||||||
options = { ...options, body: result.text };
|
|
||||||
notifySubstitutions(result.replacements);
|
|
||||||
console.log(
|
|
||||||
`[Silent Send] Substituted ${result.replacements.length} value(s) in form body`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return originalFetch.call(this, url, options);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Activate the fetch hook. If early-hook.js ran first (world: MAIN, document_start),
|
// Activate the fetch hook. If early-hook.js ran first (world: MAIN, document_start),
|
||||||
@@ -1130,6 +1137,7 @@
|
|||||||
|
|
||||||
for (const p of pairs) {
|
for (const p of pairs) {
|
||||||
const searchTerm = settings.revealMode ? p.to : p.from;
|
const searchTerm = settings.revealMode ? p.to : p.from;
|
||||||
|
if (!searchTerm?.trim()) continue;
|
||||||
const escaped = esc(searchTerm);
|
const escaped = esc(searchTerm);
|
||||||
// Add word boundaries when the term starts/ends with word chars to
|
// Add word boundaries when the term starts/ends with word chars to
|
||||||
// prevent partial-word matches (e.g. "aud" inside "Claude")
|
// prevent partial-word matches (e.g. "aud" inside "Claude")
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const SubstitutionEngine = {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (const mapping of sorted) {
|
for (const mapping of sorted) {
|
||||||
if (!mapping.enabled || !mapping.real || !mapping.substitute) continue;
|
if (!mapping.enabled || !mapping.real?.trim() || !mapping.substitute?.trim()) continue;
|
||||||
|
|
||||||
const escaped = this._escapeRegex(mapping.real);
|
const escaped = this._escapeRegex(mapping.real);
|
||||||
const regex = new RegExp(escaped, mapping.caseSensitive ? 'g' : 'gi');
|
const regex = new RegExp(escaped, mapping.caseSensitive ? 'g' : 'gi');
|
||||||
@@ -54,7 +54,7 @@ const SubstitutionEngine = {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (const mapping of sorted) {
|
for (const mapping of sorted) {
|
||||||
if (!mapping.enabled || !mapping.real || !mapping.substitute) continue;
|
if (!mapping.enabled || !mapping.real?.trim() || !mapping.substitute?.trim()) continue;
|
||||||
|
|
||||||
const escaped = this._escapeRegex(mapping.substitute);
|
const escaped = this._escapeRegex(mapping.substitute);
|
||||||
const regex = new RegExp(escaped, mapping.caseSensitive ? 'g' : 'gi');
|
const regex = new RegExp(escaped, mapping.caseSensitive ? 'g' : 'gi');
|
||||||
@@ -71,7 +71,7 @@ const SubstitutionEngine = {
|
|||||||
const found = [];
|
const found = [];
|
||||||
|
|
||||||
for (const mapping of mappings) {
|
for (const mapping of mappings) {
|
||||||
if (!mapping.enabled || !mapping.real) continue;
|
if (!mapping.enabled || !mapping.real?.trim()) continue;
|
||||||
|
|
||||||
const escaped = this._escapeRegex(mapping.real);
|
const escaped = this._escapeRegex(mapping.real);
|
||||||
const regex = new RegExp(escaped, mapping.caseSensitive ? 'g' : 'gi');
|
const regex = new RegExp(escaped, mapping.caseSensitive ? 'g' : 'gi');
|
||||||
@@ -103,7 +103,7 @@ const SubstitutionEngine = {
|
|||||||
// Collect all match positions in the original text
|
// Collect all match positions in the original text
|
||||||
const matches = [];
|
const matches = [];
|
||||||
for (const mapping of sorted) {
|
for (const mapping of sorted) {
|
||||||
if (!mapping.enabled || !mapping.real || !mapping.substitute) continue;
|
if (!mapping.enabled || !mapping.real?.trim() || !mapping.substitute?.trim()) continue;
|
||||||
|
|
||||||
const escaped = this._escapeRegex(mapping.real);
|
const escaped = this._escapeRegex(mapping.real);
|
||||||
const regex = new RegExp(escaped, mapping.caseSensitive ? 'g' : 'gi');
|
const regex = new RegExp(escaped, mapping.caseSensitive ? 'g' : 'gi');
|
||||||
|
|||||||
@@ -1814,11 +1814,12 @@ async function applyBulkImport() {
|
|||||||
await Storage.updateProfile(profile.id, profile);
|
await Storage.updateProfile(profile.id, profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add mappings
|
// Add mappings — skip any with blank/whitespace-only real values
|
||||||
for (const m of result.mappings) {
|
for (const m of result.mappings) {
|
||||||
|
if (!m.real?.trim()) continue;
|
||||||
await Storage.addMapping({
|
await Storage.addMapping({
|
||||||
real: m.real,
|
real: m.real.trim(),
|
||||||
substitute: m.substitute || '',
|
substitute: m.substitute?.trim() || '',
|
||||||
category: m.category || 'general',
|
category: m.category || 'general',
|
||||||
caseSensitive: false,
|
caseSensitive: false,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user