-
Notifications
You must be signed in to change notification settings - Fork 10
refactor(main): split monolithic ipcHandlers.js into six domain modules under src/main/ipc/ #93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,58 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // src/core/featureFlags.js | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Single source of truth for feature-flag defaults, typed keys, and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // get/set semantics. Falls back to defaults when a key is missing from | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the database, and rejects writes to unknown keys in debug builds. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const DEFAULT_FLAGS = Object.freeze({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| realtimeProtection: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| autoReports: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| scanHistory: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| externalLookups: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| geoLookup: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| networkPerimeterMap: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notificationsEnabled: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| scanNotifications: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| launchAtStartup: false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| folderWatch: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| networkAlerts: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| networkTrafficHistory: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Confirm all featureFlags.getFlag/setFlag call sites use keys present in DEFAULT_FLAGS
rg -nP "featureFlags\.(getFlag|setFlag)\(" -A1
rg -nP "feature\.systemMonitoring"Repository: chrisriv10/Soterios Length of output: 157 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== featureFlags file ==\n'
if [ -f src/core/featureFlags.js ]; then
wc -l src/core/featureFlags.js
cat -n src/core/featureFlags.js | sed -n '1,220p'
else
echo "src/core/featureFlags.js not found"
fi
printf '\n== search autoUpdates/systemMonitoring/featureFlags call sites ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' \
-e 'autoUpdates' -e 'systemMonitoring' -e 'featureFlags\.(getFlag|setFlag)\(' -e 'getFlag\(db, '\''autoUpdates'\''' -e 'feature\.autoUpdates' -e 'feature\.systemMonitoring' .Repository: chrisriv10/Soterios Length of output: 7563 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '== relevant files ==\n'
git ls-files 'src/**' | rg 'featureFlags|main\.js|systemMonitoring|autoUpdates'
printf '\n== feature flag references ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' \
-e 'autoUpdates' -e 'systemMonitoring' -e 'featureFlags' srcRepository: chrisriv10/Soterios Length of output: 5958 🏁 Script executed: #!/bin/bash
set -euo pipefail
sed -n '700,740p' src/main/main.js | nl -baRepository: chrisriv10/Soterios Length of output: 197 Add Suggested change const DEFAULT_FLAGS = Object.freeze({
realtimeProtection: true,
autoReports: true,
scanHistory: true,
externalLookups: true,
geoLookup: true,
networkPerimeterMap: true,
notificationsEnabled: true,
scanNotifications: true,
launchAtStartup: false,
folderWatch: true,
networkAlerts: true,
networkTrafficHistory: true,
+ autoUpdates: true,
});📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const FLAG_KEYS = Object.freeze(Object.keys(DEFAULT_FLAGS)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function isKnownFlag(key) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Object.prototype.hasOwnProperty.call(DEFAULT_FLAGS, key); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function getFlag(db, key, fallback) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isKnownFlag(key)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error(`Unknown feature flag: ${key}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const raw = db.getSetting(key, undefined); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (raw === undefined || raw === null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return typeof fallback === 'undefined' ? DEFAULT_FLAGS[key] : fallback; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return Boolean(raw); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function setFlag(db, key, value) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isKnownFlag(key)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error(`Unknown feature flag: ${key}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const boolValue = Boolean(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| db.setSetting(key, boolValue); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return boolValue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function getDefaults() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { ...DEFAULT_FLAGS }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| module.exports = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DEFAULT_FLAGS, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FLAG_KEYS, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isKnownFlag, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getFlag, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setFlag, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getDefaults, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,14 @@ | ||||||||||||||||||||||||||||||
| // src/core/scanProgress.js | ||||||||||||||||||||||||||||||
| // Centralised progress normalisation used by the scan engine and IPC | ||||||||||||||||||||||||||||||
| // handlers. Guarantees finite integers in the 0-100 range so every | ||||||||||||||||||||||||||||||
| // caller does not have to re-implement the same guards. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| function clampProgress(value) { | ||||||||||||||||||||||||||||||
| const n = Number(value); | ||||||||||||||||||||||||||||||
| if (!Number.isFinite(n)) return 0; | ||||||||||||||||||||||||||||||
| return Math.max(0, Math.min(100, Math.round(n))); | ||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Keep progress normalization non-throwing.
Proposed fix function clampProgress(value) {
- const n = Number(value);
+ let n;
+ try {
+ n = Number(value);
+ } catch (_) {
+ return 0;
+ }
if (!Number.isFinite(n)) return 0;
return Math.max(0, Math.min(100, Math.round(n)));
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| module.exports = { | ||||||||||||||||||||||||||||||
| clampProgress, | ||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| const https = require('https'); | ||
|
|
||
| function requestText(url, options = {}) { | ||
| return new Promise((resolve, reject) => { | ||
| const req = https.request(url, { | ||
| method: 'GET', | ||
| headers: { | ||
| 'User-Agent': 'Soterios', | ||
| ...options.headers, | ||
| }, | ||
| }, (res) => { | ||
| let body = ''; | ||
| res.setEncoding('utf8'); | ||
| res.on('data', chunk => { body += chunk; }); | ||
| res.on('end', () => resolve({ statusCode: res.statusCode, body })); | ||
| }); | ||
| req.on('error', reject); | ||
| req.setTimeout(15000, () => req.destroy(new Error('Request timed out'))); | ||
| req.end(); | ||
| }); | ||
| } | ||
|
|
||
| module.exports = { requestText }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,130 @@ | ||||||||||||||||||||||||||||||||||||
| const { ipcMain, dialog, BrowserWindow } = require('electron'); | ||||||||||||||||||||||||||||||||||||
| const path = require('path'); | ||||||||||||||||||||||||||||||||||||
| const fs = require('fs'); | ||||||||||||||||||||||||||||||||||||
| const { requestText } = require('../ipc/_shared'); | ||||||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||||||
| isPathInScanReportsDir, | ||||||||||||||||||||||||||||||||||||
| } = require('../../security/reportExport'); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const VALID_FIREWALL_PROFILES = ['Domain', 'Private', 'Public']; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| function isValidFirewallProfile(name) { | ||||||||||||||||||||||||||||||||||||
| return typeof name === 'string' && VALID_FIREWALL_PROFILES.includes(name); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| function isValidIp(ip) { | ||||||||||||||||||||||||||||||||||||
| const v4 = /^(\d{1,3}\.){3}\d{1,3}$/; | ||||||||||||||||||||||||||||||||||||
| const v6 = /^[0-9a-fA-F:]+$/; | ||||||||||||||||||||||||||||||||||||
| return v4.test(ip) || (v6.test(ip) && ip.includes(':')); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| function register(mainWindow, { db, firewallManager }) { | ||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:status', async () => { | ||||||||||||||||||||||||||||||||||||
| return firewallManager.getStatus(); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:rules', async () => { | ||||||||||||||||||||||||||||||||||||
| return firewallManager.getRules(); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:listRules', async () => { | ||||||||||||||||||||||||||||||||||||
| return firewallManager.listRules(); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:createRule', async (_event, spec) => { | ||||||||||||||||||||||||||||||||||||
| return firewallManager.createRule(spec); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:deleteRule', async (_event, name) => { | ||||||||||||||||||||||||||||||||||||
| return firewallManager.deleteRule(name); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:setRuleEnabled', async (_event, { name, enabled }) => { | ||||||||||||||||||||||||||||||||||||
| return firewallManager.setRuleEnabled(name, enabled); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+34
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
fd -t f 'FirewallManager' --exec ast-grep outline {} --items all
rg -nP '\b(createRule|deleteRule|setRuleEnabled)\s*\(' -C6 --iglob '*firewall*'Repository: chrisriv10/Soterios Length of output: 715 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- src/main/ipc/firewall.js ---'
cat -n src/main/ipc/firewall.js | sed -n '1,120p'
echo
echo '--- src/security/FirewallManager.js (outline) ---'
ast-grep outline src/security/FirewallManager.js --items all
echo
echo '--- src/security/FirewallManager.js (relevant methods) ---'
rg -n "class FirewallManager|createRule|deleteRule|setRuleEnabled|setProfileEnabled|friendlyFirewallError|_validateImportRule|isValidIp|psEscape" src/security/FirewallManager.js -n -C 6Repository: chrisriv10/Soterios Length of output: 14930 🏁 Script executed: #!/bin/bash
set -euo pipefail
sed -n '1,260p' src/security/FirewallManager.js | cat -nRepository: chrisriv10/Soterios Length of output: 13046 Guard 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:setProfileEnabled', async (_event, { profile, enabled }) => { | ||||||||||||||||||||||||||||||||||||
| if (!isValidFirewallProfile(profile)) throw new Error(`Invalid firewall profile: ${profile}`); | ||||||||||||||||||||||||||||||||||||
| return firewallManager.setProfileEnabled(profile, !!enabled); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:exportRules', async () => { | ||||||||||||||||||||||||||||||||||||
| const data = await firewallManager.exportRules(); | ||||||||||||||||||||||||||||||||||||
| const result = await dialog.showSaveDialog(mainWindow || BrowserWindow.getFocusedWindow(), { | ||||||||||||||||||||||||||||||||||||
| title: 'Export Soterios firewall rules', | ||||||||||||||||||||||||||||||||||||
| defaultPath: 'soterios-firewall-rules.json', | ||||||||||||||||||||||||||||||||||||
| filters: [{ name: 'JSON', extensions: ['json'] }], | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
| if (result.canceled || !result.filePath) return { canceled: true }; | ||||||||||||||||||||||||||||||||||||
| await fs.promises.writeFile(result.filePath, JSON.stringify(data, null, 2), 'utf8'); | ||||||||||||||||||||||||||||||||||||
| return { success: true, path: result.filePath, count: data.rules.length }; | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:importRules', async (_event, options = {}) => { | ||||||||||||||||||||||||||||||||||||
| const onConflict = ['skip', 'overwrite', 'rename'].includes(options && options.onConflict) | ||||||||||||||||||||||||||||||||||||
| ? options.onConflict | ||||||||||||||||||||||||||||||||||||
| : 'skip'; | ||||||||||||||||||||||||||||||||||||
| const result = await dialog.showOpenDialog(mainWindow || BrowserWindow.getFocusedWindow(), { | ||||||||||||||||||||||||||||||||||||
| title: 'Import Soterios firewall rules', | ||||||||||||||||||||||||||||||||||||
| properties: ['openFile'], | ||||||||||||||||||||||||||||||||||||
| filters: [{ name: 'JSON', extensions: ['json'] }], | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
| if (result.canceled || !result.filePaths.length) return { canceled: true }; | ||||||||||||||||||||||||||||||||||||
| const filePath = result.filePaths[0]; | ||||||||||||||||||||||||||||||||||||
| const stat = await fs.promises.stat(filePath); | ||||||||||||||||||||||||||||||||||||
| const MAX_IMPORT_BYTES = 2 * 1024 * 1024; | ||||||||||||||||||||||||||||||||||||
| if (stat.size > MAX_IMPORT_BYTES) { | ||||||||||||||||||||||||||||||||||||
| throw new Error('Import file is too large (limit 2 MB).'); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| let payload; | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| const raw = await fs.promises.readFile(filePath, 'utf8'); | ||||||||||||||||||||||||||||||||||||
| payload = JSON.parse(raw); | ||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||
| throw new Error('Could not parse import file as JSON.'); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| const summary = await firewallManager.importRules(payload, { onConflict }); | ||||||||||||||||||||||||||||||||||||
| return { ...summary, path: filePath }; | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const TRUSTED_IPS_KEY = 'firewall.trustedIps'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:getTrusted', () => { | ||||||||||||||||||||||||||||||||||||
| return db.getSetting(TRUSTED_IPS_KEY, []); | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:trustConnection', (_event, ip) => { | ||||||||||||||||||||||||||||||||||||
| if (!ip || !isValidIp(ip)) throw new Error('Invalid address.'); | ||||||||||||||||||||||||||||||||||||
| const current = db.getSetting(TRUSTED_IPS_KEY, []); | ||||||||||||||||||||||||||||||||||||
| if (!current.includes(ip)) current.push(ip); | ||||||||||||||||||||||||||||||||||||
| db.setSetting(TRUSTED_IPS_KEY, current); | ||||||||||||||||||||||||||||||||||||
| return current; | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+96
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Normalize the stored value to an array.
🛡️ Proposed fix- const current = db.getSetting(TRUSTED_IPS_KEY, []);
+ const stored = db.getSetting(TRUSTED_IPS_KEY, []);
+ const current = Array.isArray(stored) ? [...stored] : [];
if (!current.includes(ip)) current.push(ip);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ipcMain.handle('firewall:untrustConnection', (_event, ip) => { | ||||||||||||||||||||||||||||||||||||
| const current = (db.getSetting(TRUSTED_IPS_KEY, []) || []).filter((x) => x !== ip); | ||||||||||||||||||||||||||||||||||||
| db.setSetting(TRUSTED_IPS_KEY, current); | ||||||||||||||||||||||||||||||||||||
| return current; | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // -- WHOIS lookup (no API key required) -- | ||||||||||||||||||||||||||||||||||||
| ipcMain.handle('network:whois', async (_event, ip) => { | ||||||||||||||||||||||||||||||||||||
| if (!ip || !isValidIp(ip)) throw new Error('Invalid address.'); | ||||||||||||||||||||||||||||||||||||
| const res = await requestText(`https://ipwho.is/${encodeURIComponent(ip)}`); | ||||||||||||||||||||||||||||||||||||
| if (res.statusCode !== 200) throw new Error(`WHOIS lookup failed (${res.statusCode}).`); | ||||||||||||||||||||||||||||||||||||
| const data = JSON.parse(res.body || '{}'); | ||||||||||||||||||||||||||||||||||||
| if (data.success === false) return { found: false }; | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+111
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win Handle non-JSON responses from A 200 response with an HTML error/captcha body makes 🛡️ Proposed fix- const data = JSON.parse(res.body || '{}');
+ let data;
+ try {
+ data = JSON.parse(res.body || '{}');
+ } catch {
+ throw new Error('WHOIS lookup returned an unreadable response.');
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||
| found: true, | ||||||||||||||||||||||||||||||||||||
| ip: data.ip, | ||||||||||||||||||||||||||||||||||||
| country: data.country, | ||||||||||||||||||||||||||||||||||||
| region: data.region, | ||||||||||||||||||||||||||||||||||||
| city: data.city, | ||||||||||||||||||||||||||||||||||||
| org: (data.connection && data.connection.org) || data.org || null, | ||||||||||||||||||||||||||||||||||||
| isp: (data.connection && data.connection.isp) || null, | ||||||||||||||||||||||||||||||||||||
| asn: (data.connection && data.connection.asn) || null, | ||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| module.exports = { register }; | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
Missing
autoUpdatesflag definition breaks the auto-update check.DEFAULT_FLAGSinsrc/core/featureFlags.jsenumerates 12 flags but omitsautoUpdates, whichsrc/main/main.jsrelies on; sincegetFlagthrows for any key absent fromDEFAULT_FLAGS, the consumer call always fails.src/core/featureFlags.js#L6-19: addautoUpdates: true(and confirm whethersystemMonitoring, referenced in the PR description, also needs an entry) toDEFAULT_FLAGS.src/main/main.js#L727-727: no change needed here once the flag is defined — this call site will start working correctly againstfeatureFlags.getFlag(db, 'autoUpdates', true).🤖 Prompt for AI Agents