Files
TORCH/test/dashboard-config-exposure.test.mjs
T
Claude 639ad6877d chore: code quality, CI/CD, and DX improvements
Phase 3 — Code Quality:
- Deduplicate nowUnix() in lock-ops.mjs (import from utils.mjs)
- Promote ESLint no-unused-vars to error, fix all 46 violations
- Remove dead code: unused imports, variables, and catch bindings
- Add /* global marked */ for CDN-loaded library in dashboard

Phase 4 — CI/CD:
- Add Node.js version matrix (22, 23) to CI workflow
- Add npm audit security scan step to CI
- Add innerHTML usage check to CI pipeline
- Enable Dependabot for npm and GitHub Actions dependencies
- Create PR template

Phase 5 — Developer Experience:
- Create .env.example documenting all environment variables
- Expand CONTRIBUTING.md with commit conventions, agent/prompt
  creation guide, architecture overview, and test integrity
- Add system architecture diagram with Mermaid (docs/architecture/)

https://claude.ai/code/session_01GfwUNbgeMgXreFDbsNQMsS
2026-02-19 21:03:30 +00:00

115 lines
4.3 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import http from 'node:http';
import fs from 'node:fs';
import path from 'node:path';
import { cmdDashboard } from '../src/dashboard.mjs';
const tempConfigPath = path.resolve('torch-config-exposure-test.json');
const SENSITIVE_CONFIG = {
nostrLock: {
namespace: 'test-namespace',
relays: ['wss://relay.example.com'],
dailyRoster: ['user1', 'user2'], // Sensitive roster
weeklyRoster: ['user3'],
ttlSeconds: 3600,
queryTimeoutMs: 5000,
publishTimeoutMs: 5000,
minSuccessfulRelayPublishes: 2,
minActiveRelayPool: 1,
},
dashboard: {
auth: 'secret-auth-string', // stripped by auth check
relays: ['wss://dashboard.relay.example.com'],
},
scheduler: {
paused: {
daily: ['user1'],
},
firstPromptByCadence: {
daily: 'some prompt',
},
},
};
test('Dashboard Configuration Exposure Security', async (t) => {
// Setup temp config
fs.writeFileSync(tempConfigPath, JSON.stringify(SENSITIVE_CONFIG, null, 2));
// Override env to use this config
const originalEnvPath = process.env.TORCH_CONFIG_PATH;
process.env.TORCH_CONFIG_PATH = tempConfigPath;
// Disable auth via env so we can access the endpoint without credentials
// The vulnerability is about exposure of *other* fields.
// We need to bypass the basic auth middleware to test the JSON response.
// However, cmdDashboard checks config.dashboard.auth too.
// If we want to test that auth is stripped from JSON, we should enable auth but provide credentials.
// OR we can just test the sanitization logic which runs regardless of how we got there?
// No, the endpoint logic runs after auth check.
// To keep it simple: we want to verify that IF we can access the endpoint, the sensitive data is gone.
// We can simulate an authorized request or disable auth.
// If we disable auth in config, we can access it.
// Let's modify the config for the test to NOT have auth, so we can access it freely.
// We want to verify that things like 'dailyRoster' are stripped.
const configForTest = { ...SENSITIVE_CONFIG };
delete configForTest.dashboard.auth;
fs.writeFileSync(tempConfigPath, JSON.stringify(configForTest, null, 2));
let server;
let serverPort;
// Cleanup function
t.after(() => {
if (server) server.close();
if (fs.existsSync(tempConfigPath)) fs.unlinkSync(tempConfigPath);
if (originalEnvPath) process.env.TORCH_CONFIG_PATH = originalEnvPath;
else delete process.env.TORCH_CONFIG_PATH;
});
// Start server
await new Promise((resolve) => {
cmdDashboard(0).then((srv) => {
server = srv;
serverPort = server.address().port;
resolve();
});
});
// Fetch config
const responseBody = await new Promise((resolve, reject) => {
http.get(`http://localhost:${serverPort}/torch-config.json`, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => resolve(data));
}).on('error', reject);
});
let exposedConfig;
try {
exposedConfig = JSON.parse(responseBody);
} catch (_e) {
assert.fail(`Failed to parse JSON response: ${responseBody}`);
}
// 1. Assert sensitive fields are NOT present
assert.equal(exposedConfig.nostrLock?.dailyRoster, undefined, 'dailyRoster should NOT be exposed');
assert.equal(exposedConfig.nostrLock?.weeklyRoster, undefined, 'weeklyRoster should NOT be exposed');
assert.equal(exposedConfig.nostrLock?.relayFallbacks, undefined, 'relayFallbacks should NOT be exposed');
assert.equal(exposedConfig.scheduler, undefined, 'scheduler config should NOT be exposed');
assert.equal(exposedConfig.raw, undefined, 'raw config should NOT be exposed');
// 2. Assert required fields ARE present
assert.ok(exposedConfig.dashboard, 'dashboard config should be present');
// Note: cmdDashboard loads from file, parseTorchConfig normalizes keys.
// check dashboard.relays
assert.deepEqual(exposedConfig.dashboard.relays, ['wss://dashboard.relay.example.com'], 'dashboard relays should be present');
assert.ok(exposedConfig.nostrLock, 'nostrLock config should be present');
assert.equal(exposedConfig.nostrLock.namespace, 'test-namespace', 'nostrLock namespace should be present');
assert.deepEqual(exposedConfig.nostrLock.relays, ['wss://relay.example.com'], 'nostrLock relays should be present');
});