Merge pull request #29 from outis1one/claude/read-repo-wA3y1
Claude/read repo w a3y1
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"name": "Silent Send",
|
"name": "Silent Send",
|
||||||
"version": "2.0.11",
|
"version": "2.0.13",
|
||||||
"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": "2.0.11",
|
"version": "2.0.13",
|
||||||
"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": "2.0.11",
|
"version": "2.0.13",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "BSL-1.1",
|
"license": "BSL-1.1",
|
||||||
"description": "Browser extension that substitutes personal data before sending to AI services",
|
"description": "Browser extension that substitutes personal data before sending to AI services",
|
||||||
|
|||||||
@@ -201,6 +201,17 @@ const messageHandlers = {
|
|||||||
await setupAutoSyncAlarm();
|
await setupAutoSyncAlarm();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async 'get:decrypted-config'(_message, _sender, sendResponse) {
|
||||||
|
try {
|
||||||
|
const mappings = await Storage.getMappings();
|
||||||
|
const identity = await Storage.getIdentity();
|
||||||
|
const settings = await Storage.getSettings();
|
||||||
|
sendResponse({ mappings, identity, settings });
|
||||||
|
} catch {
|
||||||
|
sendResponse(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async 'org:config-changed'() {
|
async 'org:config-changed'() {
|
||||||
await setupOrgPolicyAlarm();
|
await setupOrgPolicyAlarm();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -905,11 +905,8 @@
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
// Fetch Interception — scans ALL POST requests with a body.
|
// Fetch Interception — scans ALL POST requests with a body.
|
||||||
// Service-agnostic: doesn't depend on URL patterns.
|
// Service-agnostic: doesn't depend on URL patterns.
|
||||||
// Uses __ssOriginalFetch from the early hook (injected synchronously
|
|
||||||
// before any page JS) to ensure we have the real fetch, even if
|
|
||||||
// frameworks like Next.js (ChatGPT) store a reference early.
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
const originalFetch = window.__ssOriginalFetch || window.fetch;
|
const originalFetch = window.fetch;
|
||||||
|
|
||||||
// URLs to never touch (static assets, analytics, etc.)
|
// URLs to never touch (static assets, analytics, etc.)
|
||||||
const SKIP_URL_PATTERNS = [
|
const SKIP_URL_PATTERNS = [
|
||||||
@@ -1029,9 +1026,7 @@
|
|||||||
return originalFetch.call(this, url, options);
|
return originalFetch.call(this, url, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register our fetch interceptor so the early hook proxy can use it
|
|
||||||
window.__ssInterceptFetch = window.fetch;
|
|
||||||
window.__ssReady = true;
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Document Upload Processing
|
// Document Upload Processing
|
||||||
@@ -1397,8 +1392,8 @@
|
|||||||
// ============================================================
|
// ============================================================
|
||||||
// XMLHttpRequest Interception — same aggressive approach
|
// XMLHttpRequest Interception — same aggressive approach
|
||||||
// ============================================================
|
// ============================================================
|
||||||
const origOpen = window.__ssOriginalXHROpen || XMLHttpRequest.prototype.open;
|
const origOpen = XMLHttpRequest.prototype.open;
|
||||||
const origSend = window.__ssOriginalXHRSend || XMLHttpRequest.prototype.send;
|
const origSend = XMLHttpRequest.prototype.send;
|
||||||
|
|
||||||
XMLHttpRequest.prototype.open = function (method, url, ...rest) {
|
XMLHttpRequest.prototype.open = function (method, url, ...rest) {
|
||||||
this._ssUrl = url;
|
this._ssUrl = url;
|
||||||
|
|||||||
+45
-29
@@ -15,24 +15,6 @@
|
|||||||
if (window.__silentSendInjected) return;
|
if (window.__silentSendInjected) return;
|
||||||
window.__silentSendInjected = true;
|
window.__silentSendInjected = true;
|
||||||
|
|
||||||
// Cross-browser API
|
|
||||||
const api =
|
|
||||||
typeof browser !== 'undefined' && browser.runtime
|
|
||||||
? browser
|
|
||||||
: typeof chrome !== 'undefined'
|
|
||||||
? chrome
|
|
||||||
: null;
|
|
||||||
|
|
||||||
// IMMEDIATELY inject the early fetch hook into the page world as an
|
|
||||||
// EXTERNAL file. Must be external (not inline) because sites like
|
|
||||||
// claude.ai have strict CSP that blocks inline scripts. Firefox
|
|
||||||
// enforces this strictly; Chrome is more permissive but external
|
|
||||||
// works everywhere.
|
|
||||||
const earlyHook = document.createElement('script');
|
|
||||||
earlyHook.src = api.runtime.getURL('src/content/early-hook.js');
|
|
||||||
(document.head || document.documentElement).appendChild(earlyHook);
|
|
||||||
earlyHook.onload = () => earlyHook.remove();
|
|
||||||
|
|
||||||
// Merge active profiles into flat identity object
|
// Merge active profiles into flat identity object
|
||||||
function mergeProfiles(data) {
|
function mergeProfiles(data) {
|
||||||
const profiles = data?.profiles || [];
|
const profiles = data?.profiles || [];
|
||||||
@@ -65,23 +47,57 @@
|
|||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cross-browser API
|
||||||
|
const api =
|
||||||
|
typeof browser !== 'undefined' && browser.runtime
|
||||||
|
? browser
|
||||||
|
: typeof chrome !== 'undefined'
|
||||||
|
? chrome
|
||||||
|
: null;
|
||||||
|
|
||||||
// Load mappings and settings, then inject into page
|
// Load mappings and settings, then inject into page
|
||||||
async function init() {
|
async function init() {
|
||||||
|
let mappings, identity, settings;
|
||||||
|
|
||||||
const result = await api.storage.local.get(['ss_mappings', 'ss_identity', 'ss_settings']);
|
const result = await api.storage.local.get(['ss_mappings', 'ss_identity', 'ss_settings']);
|
||||||
const settings = result.ss_settings || { enabled: true };
|
const isEncrypted = result.ss_mappings?._ssLocalEncrypted ||
|
||||||
|
result.ss_identity?._ssLocalEncrypted;
|
||||||
|
|
||||||
// Check if data is encrypted (locked) — pass empty config
|
if (isEncrypted) {
|
||||||
// The background will send decrypted data via vault:unlocked when ready
|
// Data is encrypted — ask the background script for decrypted config.
|
||||||
const isLocked = result.ss_mappings?._ssLocalEncrypted ||
|
// The background has access to the Storage module which can decrypt.
|
||||||
result.ss_identity?._ssLocalEncrypted;
|
try {
|
||||||
const mappings = isLocked ? [] : (result.ss_mappings || []);
|
const response = await api.runtime.sendMessage({ type: 'get:decrypted-config' });
|
||||||
const identityData = isLocked ? {} : (result.ss_identity || {});
|
if (response?.mappings) {
|
||||||
|
mappings = response.mappings;
|
||||||
|
identity = response.identity || {};
|
||||||
|
settings = response.settings || { enabled: true };
|
||||||
|
} else {
|
||||||
|
// Background couldn't decrypt (locked) — inject with empty config
|
||||||
|
// and wait for vault:unlocked message later
|
||||||
|
mappings = [];
|
||||||
|
identity = {};
|
||||||
|
settings = result.ss_settings || { enabled: true };
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
mappings = [];
|
||||||
|
identity = {};
|
||||||
|
settings = result.ss_settings || { enabled: true };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Data is plaintext — read directly
|
||||||
|
mappings = result.ss_mappings || [];
|
||||||
|
const identityData = result.ss_identity || {};
|
||||||
|
identity = mergeProfiles(identityData);
|
||||||
|
settings = result.ss_settings || { enabled: true };
|
||||||
|
}
|
||||||
|
|
||||||
// Merge active profiles into a flat identity object for the content script
|
// Ensure identity is merged if it came from background
|
||||||
const identity = mergeProfiles(identityData);
|
if (identity.profiles) {
|
||||||
|
identity = mergeProfiles(identity);
|
||||||
|
}
|
||||||
|
|
||||||
// Load the full content.js which will use __ssOriginalFetch
|
// Inject the main interception script into the page's world
|
||||||
// (captured by the early hook above) and set __ssReady = true
|
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.setAttribute('data-ss-config', JSON.stringify({ mappings, identity, settings }));
|
script.setAttribute('data-ss-config', JSON.stringify({ mappings, identity, settings }));
|
||||||
script.src = api.runtime.getURL('src/content/content.js');
|
script.src = api.runtime.getURL('src/content/content.js');
|
||||||
|
|||||||
@@ -591,7 +591,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>Silent Send v2.0.11</p>
|
<p>Silent Send v2.0.13</p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user