OverviewFilesCommitsIssuesDocsReleasesPeers

P2P-Hiverelay / test / unit / api-network-state.test.js

284 lines · javascript ref:
import test from 'brittle'
import {
  buildNetworkStatePayload,
  buildNetworkStateRoutePayload,
  buildNetworkStateRouteContext,
  detailedNetworkState,
  isDetailedNetworkStateQuery,
  publicNetworkRelay,
  publicNetworkState,
  resolveNetworkStateRoute
} from '../../packages/core/core/relay-node/api-network-state.js'

function detailedState () {
  return {
    timestamp: 12345,
    summary: {
      totalRelays: 1,
      onlineRelays: 1,
      totalConnections: 4,
      totalStorage: 100,
      totalStorageMax: 200
    },
    relays: [{
      publicKey: 'a'.repeat(64),
      name: 'Relay aaaaaaaa',
      host: '203.0.113.10',
      apiPort: 9100,
      region: 'NA',
      online: true,
      lastSeen: 999,
      uptime: { ms: 1000 },
      connections: 4,
      seededApps: 2,
      storage: { used: 100, max: 200 },
      relay: { totalBytesRelayed: 50 },
      seeder: { coresSeeded: 2 },
      memory: { heapUsed: 123, rss: 456 },
      tor: { running: true, onionAddress: 'relay-secret.onion' },
      holesailKey: 'holesail-secret-key',
      holesailConnected: true,
      errors: 1
    }]
  }
}

test('api network state: detailed query parser is explicit', (t) => {
  t.ok(isDetailedNetworkStateQuery('1'))
  t.ok(isDetailedNetworkStateQuery('true'))
  t.absent(isDetailedNetworkStateQuery('TRUE'))
  t.absent(isDetailedNetworkStateQuery('0'))
  t.absent(isDetailedNetworkStateQuery(null))
})

test('api network state: route helper maps exact network read', (t) => {
  t.alike(resolveNetworkStateRoute('GET', '/api/network'), {
    kind: 'network-state'
  })

  t.is(resolveNetworkStateRoute('POST', '/api/network'), null)
  t.is(resolveNetworkStateRoute('GET', '/api/network/extra'), null)
  t.is(resolveNetworkStateRoute('GET', '/api/networks'), null)
})

test('api network state: route context owns detailed auth decision', (t) => {
  t.alike(buildNetworkStateRouteContext(new URL('http://127.0.0.1/api/network?detailed=1')), {
    detailed: true,
    requiresAuth: true
  })

  t.alike(buildNetworkStateRouteContext(new URL('http://127.0.0.1/api/network?detailed=true')), {
    detailed: true,
    requiresAuth: true
  })

  t.alike(buildNetworkStateRouteContext(new URL('http://127.0.0.1/api/network?detailed=TRUE')), {
    detailed: false,
    requiresAuth: false
  })

  t.alike(buildNetworkStateRouteContext(null), {
    detailed: false,
    requiresAuth: false
  })
})

test('api network state: route payload helper dispatches redacted and detailed state', (t) => {
  const networkDiscovery = {
    getNetworkState: detailedState
  }

  const pub = buildNetworkStateRoutePayload({
    route: { kind: 'network-state' },
    url: new URL('http://127.0.0.1/api/network'),
    networkDiscovery
  })
  const detailed = buildNetworkStateRoutePayload({
    route: { kind: 'network-state' },
    url: new URL('http://127.0.0.1/api/network?detailed=true'),
    networkDiscovery
  })
  const contextOverride = buildNetworkStateRoutePayload({
    route: { kind: 'network-state' },
    context: { detailed: false },
    url: new URL('http://127.0.0.1/api/network?detailed=true'),
    networkDiscovery
  })
  const unknown = buildNetworkStateRoutePayload({
    route: { kind: 'unknown' },
    networkDiscovery
  })

  t.is(pub.ok, true)
  t.absent(pub.payload.relays[0].host)
  t.absent(JSON.stringify(pub.payload).includes('holesail-secret-key'))
  t.is(detailed.ok, true)
  t.is(detailed.payload.relays[0].host, '203.0.113.10')
  t.is(detailed.payload.relays[0].holesailKey, 'holesail-secret-key')
  t.absent(contextOverride.payload.relays[0].host)
  t.is(unknown.status, 404)
  t.is(unknown.payload.error, 'unknown network state route')
})

test('api network state: unavailable discovery returns stable 503 payload', (t) => {
  t.alike(buildNetworkStatePayload(), {
    ok: false,
    status: 503,
    payload: { error: 'Network discovery not running' }
  })
  t.alike(buildNetworkStatePayload({ networkDiscovery: {} }), {
    ok: false,
    status: 503,
    payload: { error: 'Network discovery not running' }
  })
})

test('api network state: public relay redacts connection metadata', (t) => {
  const relay = publicNetworkRelay(detailedState().relays[0])

  t.alike(relay, {
    publicKey: 'a'.repeat(64),
    name: 'Relay aaaaaaaa',
    region: 'NA',
    online: true,
    lastSeen: 999,
    uptime: { ms: 1000 },
    connections: 4,
    seededApps: 2,
    storage: { used: 100, max: 200, pct: 0 },
    relay: {
      activeCircuits: 0,
      totalCircuitsServed: 0,
      totalBytesRelayed: 50,
      capacityUsedPct: 0,
      peersWithCircuits: 0
    },
    seeder: {
      coresSeeded: 2,
      totalBytesStored: 0,
      totalBytesServed: 0,
      capacityUsedPct: 0
    },
    apiReachable: true,
    torAvailable: true,
    holesailAvailable: true,
    errors: 1
  })
  t.absent(Object.prototype.hasOwnProperty.call(relay, 'host'))
  t.absent(Object.prototype.hasOwnProperty.call(relay, 'apiPort'))
  t.absent(Object.prototype.hasOwnProperty.call(relay, 'memory'))
  t.absent(Object.prototype.hasOwnProperty.call(relay, 'tor'))
  t.absent(Object.prototype.hasOwnProperty.call(relay, 'holesailKey'))
  t.absent(JSON.stringify(relay).includes('holesail-secret-key'))
  t.absent(JSON.stringify(relay).includes('relay-secret.onion'))
})

test('api network state: public state keeps summary and redacted relays', (t) => {
  const state = publicNetworkState(detailedState())

  t.is(state.timestamp, 12345)
  t.is(state.summary.onlineRelays, 1)
  t.is(state.relays.length, 1)
  t.is(state.relays[0].apiReachable, true)
  t.absent(JSON.stringify(state).includes('203.0.113.10'))
  t.absent(JSON.stringify(state).includes('holesail-secret-key'))
})

test('api network state: build payload preserves detailed state only when requested', (t) => {
  const networkDiscovery = {
    getNetworkState: detailedState
  }

  const publicPayload = buildNetworkStatePayload({ networkDiscovery })
  t.is(publicPayload.status, 200)
  t.absent(publicPayload.payload.relays[0].host)

  const detailedPayload = buildNetworkStatePayload({ networkDiscovery, detailed: true })
  t.is(detailedPayload.status, 200)
  t.is(detailedPayload.payload.relays[0].host, '203.0.113.10')
  t.is(detailedPayload.payload.relays[0].holesailKey, 'holesail-secret-key')
  t.absent(Object.prototype.hasOwnProperty.call(detailedPayload.payload.relays[0], 'unknown'))
})

test('api network state: detailed payload is bounded and drops raw relay internals', (t) => {
  const state = {
    timestamp: -1,
    summary: {
      totalRelays: 1500,
      onlineRelays: 1.9,
      totalConnections: Infinity,
      totalStorage: 100.9,
      totalStorageMax: -1,
      secret: 'should-not-leak'
    },
    relays: []
  }
  for (let i = 0; i < 1005; i++) {
    state.relays.push({
      publicKey: i === 0 ? 'C'.repeat(64) : 'b'.repeat(64),
      name: i === 0 ? ' Relay <one> ' : 'Relay',
      host: i === 0 ? '203.0.113.10' : 'bad\nhost',
      apiPort: i === 0 ? 9100 : 999999,
      region: 'NA',
      online: true,
      lastSeen: 123.9,
      uptime: { ms: 456.8, human: '7m', secret: 'should-not-leak' },
      connections: 4.9,
      seededApps: -1,
      storage: { used: Infinity, max: 200.9, pct: 2, raw: 'should-not-leak' },
      relay: { activeCircuits: 1.8, totalBytesRelayed: 50, circuits: [{ secret: 'should-not-leak' }] },
      seeder: { coresSeeded: 2.9, totalBytesServed: 3, cores: [{ secret: 'should-not-leak' }] },
      memory: { heapUsed: 123.9, rss: -1, external: 'should-not-leak' },
      tor: { running: true, onionAddress: 'relay-secret.onion', socksProxy: 'should-not-leak' },
      holesailKey: 'holesail-secret-key',
      holesailConnected: true,
      errors: '<script>',
      raw: { secret: 'should-not-leak' }
    })
  }

  const payload = detailedNetworkState(state)

  t.is(payload.timestamp, null)
  t.alike(payload.summary, {
    totalRelays: 1500,
    onlineRelays: 1,
    totalConnections: 0,
    totalStorage: 100,
    totalStorageMax: 0
  })
  t.is(payload.relays.length, 1000, 'relay rows are capped')
  t.alike(payload.relays[0], {
    publicKey: 'c'.repeat(64),
    name: 'Relay <one>',
    host: '203.0.113.10',
    apiPort: 9100,
    region: 'NA',
    online: true,
    lastSeen: 123,
    uptime: { ms: 456, human: '7m' },
    connections: 4,
    seededApps: 0,
    storage: { used: 0, max: 200, pct: 2 },
    relay: {
      activeCircuits: 1,
      totalCircuitsServed: 0,
      totalBytesRelayed: 50,
      capacityUsedPct: 0,
      peersWithCircuits: 0
    },
    seeder: {
      coresSeeded: 2,
      totalBytesStored: 0,
      totalBytesServed: 3,
      capacityUsedPct: 0
    },
    memory: { heapUsed: 123, rss: 0 },
    tor: { running: true, onionAddress: 'relay-secret.onion', activeConnections: 0 },
    holesailKey: 'holesail-secret-key',
    holesailConnected: true,
    errors: 0
  })
  t.absent(JSON.stringify(payload).includes('should-not-leak'), 'raw detailed network fields are removed')
})