OverviewFilesCommitsIssuesDocsReleasesPeers

pearbrowser-desktop / test / origin-isolation-smoke-evidence.test.js

218 lines · javascript ref:
import test from 'node:test'
import assert from 'node:assert/strict'
import { spawnSync } from 'node:child_process'
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'

import {
  analyzeOriginIsolationSmokeEvidence,
  FEATURE_FLAG,
  PROOF_KEY
} from '../scripts/check-origin-isolation-smoke-evidence.mjs'

const checkerPath = fileURLToPath(new URL('../scripts/check-origin-isolation-smoke-evidence.mjs', import.meta.url))
const generatorPath = fileURLToPath(new URL('../scripts/generate-origin-isolation-smoke-evidence.mjs', import.meta.url))
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))

function validEvidence () {
  return {
    schemaVersion: 1,
    kind: 'pearbrowser-origin-isolation-smoke-evidence',
    featureFlag: FEATURE_FLAG,
    proofKey: PROOF_KEY,
    apps: [
      {
        label: 'Peerit',
        url: `hyper://${'a'.repeat(64)}/`,
        origin: 'http://127.0.0.1:61001'
      },
      {
        label: 'Poked',
        url: `hyper://${'b'.repeat(64)}/`,
        origin: 'http://127.0.0.1:61002'
      }
    ],
    storage: {
      proofKey: PROOF_KEY,
      writtenValue: 'peerit-proof-value',
      appA: {
        localStorage: 'peerit-proof-value',
        cookie: `${PROOF_KEY}=peerit-proof-value; other=1`,
        indexedDB: 'peerit-proof-value'
      },
      appB: {
        localStorage: null,
        cookie: 'other=1',
        indexedDB: null
      }
    },
    strictCsp: {
      status: 'PASS',
      evidence: 'screenshot: strict-csp-app-loaded.png'
    },
    tabLifecycle: {
      status: 'PASS',
      evidence: 'screen recording: close app A then navigate app B'
    },
    realAppBridge: {
      status: 'PASS',
      evidence: 'Peerit identity/sync smoke log',
      routes: {
        identity: true,
        sync: true,
        swarmTicket: true,
        swarmEvents: true
      }
    },
    artifacts: [
      'origin-split-screenshot.png',
      'peerit-bridge-smoke.json'
    ]
  }
}

test('origin isolation smoke evidence accepts a complete operator artifact', () => {
  const result = analyzeOriginIsolationSmokeEvidence(validEvidence())
  assert.equal(result.ok, true)
  assert.equal(result.status, 'verified')
  assert.equal(result.failures.length, 0)
  assert.ok(result.checks.some((check) => check.id === 'origin-split' && check.ok))
  assert.ok(result.checks.some((check) => check.id === 'app-b-indexeddb-isolated' && check.ok))
})

test('origin isolation smoke evidence fails same-origin and storage-leak artifacts', () => {
  const evidence = validEvidence()
  evidence.apps[1].origin = evidence.apps[0].origin
  evidence.storage.appB.localStorage = evidence.storage.writtenValue
  evidence.realAppBridge.routes.swarmEvents = false
  const result = analyzeOriginIsolationSmokeEvidence(evidence)

  assert.equal(result.ok, false)
  assert.ok(result.failures.some((failure) => failure.id === 'origin-split'))
  assert.ok(result.failures.some((failure) => failure.id === 'app-b-localstorage-isolated'))
  assert.ok(result.failures.some((failure) => failure.id === 'bridge-swarmEvents'))
})

test('origin isolation smoke evidence validates automated verifier checks when present', () => {
  const evidence = validEvidence()
  evidence.automatedVerifier = {
    kind: 'pearbrowser-origin-isolation-automated-verifier',
    checks: [
      { id: 'origin-split', ok: true },
      { id: 'tab-lifecycle-release', ok: false }
    ]
  }
  const result = analyzeOriginIsolationSmokeEvidence(evidence)

  assert.equal(result.ok, false)
  assert.ok(result.failures.some((failure) => failure.id === 'automated-verifier-checks'))
})

test('origin isolation smoke evidence accepts passing automated verifier checks', () => {
  const evidence = validEvidence()
  evidence.automatedVerifier = {
    kind: 'pearbrowser-origin-isolation-automated-verifier',
    checks: [
      { id: 'origin-split', ok: true },
      { id: 'tab-lifecycle-release', ok: true }
    ]
  }
  const result = analyzeOriginIsolationSmokeEvidence(evidence)

  assert.equal(result.ok, true)
  assert.ok(result.checks.some((check) => check.id === 'automated-verifier-kind' && check.ok))
  assert.ok(result.checks.some((check) => check.id === 'automated-verifier-checks' && check.ok))
})

test('origin isolation smoke evidence CLI exits non-zero until the proof is complete', () => {
  assert.equal(pkg.scripts?.['check:origin-isolation-smoke-evidence'], 'node scripts/check-origin-isolation-smoke-evidence.mjs')

  const fixture = mkdtempSync(join(tmpdir(), 'pear-origin-smoke-evidence-'))
  try {
    const goodPath = join(fixture, 'good.json')
    const badPath = join(fixture, 'bad.json')
    writeFileSync(goodPath, JSON.stringify(validEvidence(), null, 2))
    const bad = validEvidence()
    bad.strictCsp.evidence = ''
    writeFileSync(badPath, JSON.stringify(bad, null, 2))

    const good = spawnSync(process.execPath, [checkerPath, '--file', goodPath, '--json'], {
      encoding: 'utf8'
    })
    assert.equal(good.status, 0, good.stderr || good.stdout)
    assert.equal(JSON.parse(good.stdout).status, 'verified')

    const blocked = spawnSync(process.execPath, [checkerPath, '--file', badPath, '--json'], {
      encoding: 'utf8'
    })
    assert.equal(blocked.status, 1)
    const report = JSON.parse(blocked.stdout)
    assert.equal(report.status, 'blocked')
    assert.ok(report.failures.some((failure) => failure.id === 'strict-csp'))
  } finally {
    rmSync(fixture, { recursive: true, force: true })
  }
})

test('origin isolation automated verifier emits checker-compatible evidence from a plan', () => {
  assert.equal(pkg.scripts?.['generate:origin-isolation-smoke-evidence'], 'node scripts/generate-origin-isolation-smoke-evidence.mjs')

  const fixture = mkdtempSync(join(tmpdir(), 'pear-origin-smoke-verifier-'))
  try {
    const planPath = join(fixture, 'plan.json')
    const outPath = join(fixture, 'evidence.json')
    writeFileSync(planPath, JSON.stringify({
      kind: 'pearbrowser-origin-isolation-smoke-plan',
      apps: [
        {
          label: 'Peerit',
          url: `hyper://${'a'.repeat(64)}/`,
          driveKey: 'a'.repeat(64)
        },
        {
          label: 'Pearfeed',
          url: `hyper://${'b'.repeat(64)}/`,
          driveKey: 'b'.repeat(64)
        }
      ]
    }, null, 2))

    const generated = spawnSync(process.execPath, [
      generatorPath,
      '--plan',
      planPath,
      '--proof-value',
      'automated-origin-proof',
      '--out',
      outPath,
      '--json'
    ], {
      encoding: 'utf8'
    })
    assert.equal(generated.status, 0, generated.stderr || generated.stdout)
    const evidence = JSON.parse(readFileSync(outPath, 'utf8'))
    const stdoutEvidence = JSON.parse(generated.stdout)
    assert.equal(evidence.kind, 'pearbrowser-origin-isolation-smoke-evidence')
    assert.equal(stdoutEvidence.storage.writtenValue, 'automated-origin-proof')
    assert.notEqual(evidence.apps[0].origin, evidence.apps[1].origin)
    assert.equal(evidence.storage.appA.localStorage, 'automated-origin-proof')
    assert.equal(evidence.storage.appB.localStorage, null)
    assert.equal(evidence.realAppBridge.routes.swarmEvents, true)
    assert.equal(evidence.automatedVerifier.mode, 'local-hyperproxy-httpbridge-fixture')
    assert.ok(evidence.automatedVerifier.checks.some((check) => check.id === 'tab-lifecycle-release' && check.ok))

    const analyzed = analyzeOriginIsolationSmokeEvidence(evidence)
    assert.equal(analyzed.ok, true)

    const checked = spawnSync(process.execPath, [checkerPath, '--file', outPath, '--json'], {
      encoding: 'utf8'
    })
    assert.equal(checked.status, 0, checked.stderr || checked.stdout)
    assert.equal(JSON.parse(checked.stdout).status, 'verified')
  } finally {
    rmSync(fixture, { recursive: true, force: true })
  }
})