const assert = require('node:assert/strict') const { execFileSync } = require('node:child_process') const fs = require('node:fs') const os = require('node:os') const path = require('node:path') const test = require('node:test') const { setTimeout: delay } = require('node:timers/promises') const frontend = path.resolve(__dirname, '../frontend') const source = fs.readFileSync(path.join(frontend, 'nginx.conf'), 'utf8') test('ingress access format exposes only approved fields and preserves nginx variables', () => { const format = source.match(/log_format ingress_timing escape=json\s+([\s\S]*?);/) assert.ok(format, 'escaped timing format exists') const variables = [...new Set(format[1].match(/\$[a-z][a-z0-9_]*/g))].sort() assert.deepEqual(variables, [ '$ingress_cf_ray', '$ingress_route_class', '$ingress_upstream_connect_time', '$ingress_upstream_header_time', '$ingress_upstream_response_time', '$request_time', '$status', '$time_iso8601', ].sort()) const active = source.split('\n').filter(line => !line.trim().startsWith('#')).join('\n') assert.deepEqual([...active.matchAll(/access_log\s+([^;]+);/g)].map(match => match[1]), [ '/var/log/nginx/access.log ingress_timing', ]) const entrypoint = fs.readFileSync(path.join(frontend, 'docker-entrypoint.sh'), 'utf8') const substituted = entrypoint.match(/envsubst '([^']+)'/) assert.ok(substituted) for (const variable of variables) assert.ok(!substituted[1].includes(variable)) }) const dockerContext = process.env.NGINX_TEST_DOCKER_CONTEXT const docker = (...args) => execFileSync('docker', [ ...(dockerContext ? ['--context', dockerContext] : []), ...args, ], { encoding: 'utf8', timeout: 30000, stdio: ['ignore', 'pipe', 'pipe'] }).trim() // Enable only after the coordinated runtime freeze. No production network is used. test('isolated nginx classifies requests and records timings without request secrets', { skip: process.env.NGINX_TEST_RUN_INGRESS !== '1', timeout: 90000, }, async () => { const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'weknora-ingress-test-')) const container = `weknora-ingress-test-${process.pid}-${Date.now()}` const seedContainer = `${container}-seed` const volume = `${container}-fixtures` const image = process.env.NGINX_TEST_IMAGE || fs.readFileSync(path.join(frontend, 'Dockerfile'), 'utf8').match(/^FROM (\S+)/m)[1] let created = false let seedCreated = false let volumeCreated = false try { const site = path.join(temp, 'html') fs.mkdirSync(site) for (const [filename, body] of Object.entries({ 'index.html': 'timing fixture', 'embed.html': 'embed fixture', 'config.js': 'window.fixture = true;', 'release.json': '{"release":"fixture"}', })) fs.writeFileSync(path.join(site, filename), body) const replacements = { APP_SCHEME: 'http', APP_HOST: '127.0.0.1', APP_PORT: '81', MAX_FILE_SIZE: '50M', MAX_SKILL_BUNDLE_SIZE: '256M' } const config = source.replace(/\$\{(APP_SCHEME|APP_HOST|APP_PORT|MAX_FILE_SIZE|MAX_SKILL_BUNDLE_SIZE)\}/g, (_, key) => replacements[key]) .replaceAll('/usr/share/nginx/html', '/fixtures/html') .replaceAll('/etc/nginx/api-proxy.conf', '/fixtures/api-proxy.conf') .replaceAll('/var/log/nginx/access.log ingress_timing', '/proc/1/fd/1 ingress_timing') .replaceAll('/var/log/nginx/error.log warn', '/proc/1/fd/2 warn') fs.writeFileSync(path.join(temp, 'nginx.conf'), 'worker_processes 1; error_log /dev/stderr warn; pid /var/run/nginx.pid;\n' + 'events { worker_connections 128; }\nhttp { include /etc/nginx/mime.types;\n' + config + '\nserver { listen 127.0.0.1:81; access_log off; location / { return 204; } }\n}\n') fs.copyFileSync(path.join(frontend, 'nginx-api-proxy.conf'), path.join(temp, 'api-proxy.conf')) // Stage through Docker copy so a remote context needs no host-path sharing. docker('volume', 'create', volume) volumeCreated = true docker('create', '--pull', 'never', '--read-only', '--network', 'none', '--name', seedContainer, '--mount', `type=volume,src=${volume},dst=/fixtures`, '--entrypoint', 'true', image) seedCreated = true docker('cp', `${temp}/.`, `${seedContainer}:/fixtures`) docker('rm', seedContainer) seedCreated = false docker('create', '--rm', '--pull', 'never', '--read-only', '--network', 'none', '--name', container, '--tmpfs', '/var/cache/nginx:rw,noexec,nosuid,size=16m', '--tmpfs', '/var/run:rw,noexec,nosuid,size=1m', '--mount', `type=volume,src=${volume},dst=/fixtures,readonly`, '--entrypoint', 'nginx', image, '-c', '/fixtures/nginx.conf', '-g', 'daemon off;') created = true docker('start', container) docker('exec', container, 'nginx', '-t', '-c', '/fixtures/nginx.conf') const fetchFixture = (url, extra = []) => docker('exec', container, 'wget', '-q', '-O', '/dev/null', ...extra, `http://127.0.0.1${url}`) for (let attempt = 0; ; attempt += 1) { try { fetchFixture('/release.json'); break } catch (error) { if (attempt >= 19) throw error await delay(250) } } const secret = 'ingress-secret-sentinel' const cases = [ ['/admin', 'shell', 200], ['/platform/workbench/content', 'shell', 200], ['/api/v1/auth/me', 'auth_me', 204], ['/api/v1/access/me', 'access_me', 204], ['/api/v1/system/capabilities', 'capabilities', 204], ['/api/v1/workbench/resources/training-maps', 'resource_list', 204], [`/api/v1/workbench/resources/training-maps/${secret}`, 'resource_detail', 204], [`/api/v1/workbench/resources/training-maps/${secret}/jobs`, 'api', 204], [`/r/${secret}`, 'resource_link', 204], [`/files/${secret}`, 'file', 200], [`/embed/${secret}`, 'embed', 200], ['/config.js', 'config', 200], ['/release.json', 'release', 200], [`/unknown/${secret}`, 'other', 200], ] const rays = cases.map((_, index) => `${(index + 1).toString(16).padStart(16, '0')}-AMS`) for (const [index, [url]] of cases.entries()) fetchFixture(`${url}?token=${secret}`, [ `--header=CF-Ray: ${rays[index]}`, `--header=Authorization: Bearer ${secret}`, `--header=Cookie: session=${secret}`, `--header=Referer: https://example.test/${secret}`, `--header=User-Agent: ${secret}`, ]) fetchFixture('/api/body', [`--header=CF-Ray: ${secret}`, `--post-data=${secret}`]) const lines = docker('logs', container).split('\n').filter(line => line.startsWith('{"time":')) assert.ok(lines.length >= cases.length + 2) assert.ok(!lines.join('\n').includes(secret), 'no path, query, header, or body secret appears in access records') const records = lines.map(line => JSON.parse(line)) const fields = ['time', 'route', 'status', 'request_time', 'upstream_connect_time', 'upstream_header_time', 'upstream_response_time', 'cf_ray'].sort() for (const [index, [, route, status]] of cases.entries()) { const matches = records.filter(record => record.cf_ray === rays[index]) assert.equal(matches.length, 1) const record = matches[0] assert.deepEqual(Object.keys(record).sort(), fields) assert.equal(record.route, route) assert.equal(record.status, status, `${route} ${record.cf_ray}`) assert.match(record.request_time, /^\d+\.\d{3}$/) for (const field of ['upstream_connect_time', 'upstream_header_time', 'upstream_response_time']) { assert.match(record[field], status === 204 ? /^\d+\.\d{3}$/ : /^-$/) } } assert.ok(records.some(record => record.route === 'api' && record.cf_ray === '-' && record.status === 204)) } finally { try { if (created) docker('rm', '--force', container) } finally { try { if (seedCreated) docker('rm', '--force', seedContainer) } finally { try { if (volumeCreated) docker('volume', 'rm', volume) } finally { fs.rmSync(temp, { recursive: true, force: true }) } } } } })