fix: redact nordvpn path metadata by default
This commit is contained in:
@@ -37,9 +37,29 @@ const MAC_WG_HELPER_PATH = path.join(
|
||||
const CLIENT_IPV4 = "10.5.0.2";
|
||||
const DNS_FALLBACK_RESOLVERS = ["1.1.1.1", "8.8.8.8"];
|
||||
const NORDVPN_MAC_DNS_SERVERS = ["103.86.96.100", "103.86.99.100"];
|
||||
const REDACTED_PATH_KEYS = new Set(["cliPath", "appPath", "configPath", "helperPath", "tokenSource"]);
|
||||
|
||||
function printJson(payload, exitCode = 0, errorStream = false) {
|
||||
const body = `${JSON.stringify(payload, null, 2)}\n`;
|
||||
function sanitizeOutputPayload(payload) {
|
||||
if (Array.isArray(payload)) {
|
||||
return payload.map((value) => sanitizeOutputPayload(value));
|
||||
}
|
||||
if (!payload || typeof payload !== "object") {
|
||||
return payload;
|
||||
}
|
||||
|
||||
const sanitized = {};
|
||||
for (const [key, value] of Object.entries(payload)) {
|
||||
if (REDACTED_PATH_KEYS.has(key)) {
|
||||
sanitized[key] = null;
|
||||
continue;
|
||||
}
|
||||
sanitized[key] = sanitizeOutputPayload(value);
|
||||
}
|
||||
return sanitized;
|
||||
}
|
||||
|
||||
function printJson(payload, exitCode = 0, errorStream = false, debugOutput = false) {
|
||||
const body = `${JSON.stringify(debugOutput ? payload : sanitizeOutputPayload(payload), null, 2)}\n`;
|
||||
(errorStream ? process.stderr : process.stdout).write(body);
|
||||
process.exit(exitCode);
|
||||
}
|
||||
@@ -1256,14 +1276,16 @@ async function loginNordvpn(installProbe) {
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
const debugOutput = Boolean(args.debug);
|
||||
const emitJson = (payload, exitCode = 0, errorStream = false) => printJson(payload, exitCode, errorStream, debugOutput);
|
||||
const action = args._[0];
|
||||
if (!action || args.help) {
|
||||
printJson(usage(), action ? 0 : 1, !action);
|
||||
emitJson(usage(), action ? 0 : 1, !action);
|
||||
}
|
||||
|
||||
const platform = detectPlatform();
|
||||
if (!["darwin", "linux"].includes(platform)) {
|
||||
printJson({ error: `Unsupported platform: ${platform}` }, 1, true);
|
||||
emitJson({ error: `Unsupported platform: ${platform}` }, 1, true);
|
||||
}
|
||||
|
||||
const installProbe = await probeInstallation(platform);
|
||||
@@ -1271,13 +1293,13 @@ async function main() {
|
||||
try {
|
||||
if (action === "status") {
|
||||
const ipInfo = await getPublicIpInfo();
|
||||
printJson(buildStateSummary(installProbe, ipInfo));
|
||||
emitJson(buildStateSummary(installProbe, ipInfo));
|
||||
}
|
||||
|
||||
if (action === "install") {
|
||||
const result = await installNordvpn(installProbe);
|
||||
const refreshed = await probeInstallation(platform);
|
||||
printJson({
|
||||
emitJson({
|
||||
action,
|
||||
...result,
|
||||
state: buildStateSummary(refreshed, await getPublicIpInfo()),
|
||||
@@ -1287,7 +1309,7 @@ async function main() {
|
||||
if (action === "login") {
|
||||
const result = await loginNordvpn(installProbe);
|
||||
const refreshed = await probeInstallation(platform);
|
||||
printJson({
|
||||
emitJson({
|
||||
action,
|
||||
...result,
|
||||
state: buildStateSummary(refreshed, await getPublicIpInfo()),
|
||||
@@ -1298,7 +1320,7 @@ async function main() {
|
||||
const target = args.country || args.city ? buildConnectTarget(args) : null;
|
||||
const verified = await verifyConnectionWithRetry(target);
|
||||
const refreshed = await probeInstallation(platform);
|
||||
printJson(
|
||||
emitJson(
|
||||
{
|
||||
action,
|
||||
requestedTarget: target,
|
||||
@@ -1332,13 +1354,13 @@ async function main() {
|
||||
}
|
||||
|
||||
if (connectResult.manualActionRequired) {
|
||||
printJson({ action, requestedTarget: target, ...connectResult });
|
||||
emitJson({ action, requestedTarget: target, ...connectResult });
|
||||
}
|
||||
|
||||
const verified = await verifyConnectionWithRetry(target, { attempts: 6, delayMs: 2000 });
|
||||
const refreshed = await probeInstallation(platform);
|
||||
const connectState = normalizeSuccessfulConnectState(buildStateSummary(refreshed, verified.ipInfo), connectResult, verified);
|
||||
printJson(
|
||||
emitJson(
|
||||
{
|
||||
action,
|
||||
requestedTarget: target,
|
||||
@@ -1355,16 +1377,16 @@ async function main() {
|
||||
if (action === "disconnect") {
|
||||
const result = await disconnectNordvpn(installProbe);
|
||||
const refreshed = await probeInstallation(platform);
|
||||
printJson({
|
||||
emitJson({
|
||||
action,
|
||||
...result,
|
||||
state: buildStateSummary(refreshed, await getPublicIpInfo()),
|
||||
});
|
||||
}
|
||||
|
||||
printJson({ error: `Unknown action: ${action}`, ...usage() }, 1, true);
|
||||
emitJson({ error: `Unknown action: ${action}`, ...usage() }, 1, true);
|
||||
} catch (error) {
|
||||
printJson({ error: error.message || String(error), action }, 1, true);
|
||||
emitJson({ error: error.message || String(error), action }, 1, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ module.exports = {
|
||||
typeof isMacTailscaleActive === "function" ? isMacTailscaleActive : undefined,
|
||||
normalizeSuccessfulConnectState:
|
||||
typeof normalizeSuccessfulConnectState === "function" ? normalizeSuccessfulConnectState : undefined,
|
||||
sanitizeOutputPayload:
|
||||
typeof sanitizeOutputPayload === "function" ? sanitizeOutputPayload : undefined,
|
||||
shouldAttemptMacWireguardDisconnect:
|
||||
typeof shouldAttemptMacWireguardDisconnect === "function" ? shouldAttemptMacWireguardDisconnect : undefined,
|
||||
detectMacWireguardActiveFromIfconfig:
|
||||
@@ -202,6 +204,31 @@ test("normalizeSuccessfulConnectState marks the connect snapshot active after ve
|
||||
assert.equal(state.wireguard.endpoint, "de1227.nordvpn.com:51820");
|
||||
});
|
||||
|
||||
test("sanitizeOutputPayload redacts local path metadata from normal JSON output", () => {
|
||||
const { sanitizeOutputPayload } = loadInternals();
|
||||
assert.equal(typeof sanitizeOutputPayload, "function");
|
||||
|
||||
const sanitized = sanitizeOutputPayload({
|
||||
cliPath: "/opt/homebrew/bin/nordvpn",
|
||||
appPath: "/Applications/NordVPN.app",
|
||||
wireguard: {
|
||||
configPath: "/Users/stefano/.nordvpn-client/wireguard/nordvpnctl.conf",
|
||||
helperPath: "/Users/stefano/.openclaw/workspace/skills/nordvpn-client/scripts/nordvpn-wireguard-helper.sh",
|
||||
authCache: {
|
||||
tokenSource: "default:/Users/stefano/.openclaw/workspace/.clawdbot/credentials/nordvpn/token.txt",
|
||||
},
|
||||
endpoint: "jp454.nordvpn.com:51820",
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(sanitized.cliPath, null);
|
||||
assert.equal(sanitized.appPath, null);
|
||||
assert.equal(sanitized.wireguard.configPath, null);
|
||||
assert.equal(sanitized.wireguard.helperPath, null);
|
||||
assert.equal(sanitized.wireguard.authCache.tokenSource, null);
|
||||
assert.equal(sanitized.wireguard.endpoint, "jp454.nordvpn.com:51820");
|
||||
});
|
||||
|
||||
test("isMacTailscaleActive treats Running backend as active", () => {
|
||||
const { isMacTailscaleActive } = loadInternals();
|
||||
assert.equal(typeof isMacTailscaleActive, "function");
|
||||
|
||||
Reference in New Issue
Block a user